/* * #%L * BroadleafCommerce Common Libraries * %% * Copyright (C) 2009 - 2015 Broadleaf Commerce * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * #L% */ package org.broadleafcommerce.common.util; import org.apache.log4j.Logger; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.TreeSet; /** * An API for w3c.Nodes manipulation * @author gdiaz * */ public class NodeUtil { static Logger LOG = Logger.getLogger(NodeUtil.class.getName()); /** * a simple implementation of the Comparator interface, (applied to the Node class) that uses the value of a given * node attribute as comparison criterion. * Nodes not having the required attribute (or not having attributes at all) bypass this comparator, i.e., they are considered arbitrarily different * as far as this comparator is concerned. * * * @author gdiaz * */ public static class NodeComparatorBySingleAttribute implements Comparator<Node> { /** * the name of the unique attribute whose value will be compared */ private String attributeName; public NodeComparatorBySingleAttribute(String attributeName) { this.attributeName = attributeName; } @Override public int compare(Node o1, Node o2) { NamedNodeMap attributes1 = o1.getAttributes(); NamedNodeMap attributes2 = o2.getAttributes(); if (attributes1 == null || attributes2 == null) { return -1; } Node id1 = attributes1.getNamedItem(attributeName); Node id2 = attributes2.getNamedItem(attributeName); if (id1 == null || id2 == null) { return -1; } String idVal1 = id1.getNodeValue(); String idVal2 = id2.getNodeValue(); return idVal1.compareTo(idVal2); } } /** * given an array of nodes, returns a subarray containing only those nodes having a non-null specified attribute * @param primaryNodes the original array of nodes. All nodes are assumed to at least have attributes * @param attributeName the attribute name * @return */ public static Node[] filterByAttribute(Node[] primaryNodes, String attributeName) { //filter out primary nodes that don't have the attribute ArrayList<Node> filterList = new ArrayList<Node>(); for (int j = 0; j < primaryNodes.length; j++) { if (primaryNodes[j].getAttributes().getNamedItem(attributeName) != null) { filterList.add(primaryNodes[j]); } } Node[] filtered = filterList.toArray(new Node[] {}); return filtered; } /** * tries to find a test Node within an array of nodes * The array is assumed sorted according to a custom comparator by single attribute, but if can be optionally sorted inside the method * @param arrNodes the haystack * @param testNode the needle * @param attribute the attribute used for comparison * @param sortArray true if the array needs to be sorted, false if it comes already sorted * @return */ public static int findNode(Node[] arrNodes, Node testNode, String attributeName, boolean sortArray) { NodeComparatorBySingleAttribute comparator = new NodeComparatorBySingleAttribute(attributeName); if (sortArray) { Arrays.sort(arrNodes, comparator); } int position = Arrays.binarySearch(arrNodes, testNode, comparator); return position; } /** * creates a sorted list of nodes, with the merged nodes of 2 NodeLists * The comparison criteria is a single-attribute comparator, whose attribute name is also given as a parameter * The original NodeLists are not modified. They can be null. They are not assumed to be sorted. * @param Node the target node (assumed childless, and within the same document) to which the merged children will be appended * @param list1 the original list to merge * @param list2 the second list to merge which will overwrite values from <b>list1</b> * @param attribute */ public static void mergeNodeLists(Node targetNode, org.w3c.dom.NodeList list1, org.w3c.dom.NodeList list2, String attributeName) { NodeComparatorBySingleAttribute comparator = new NodeComparatorBySingleAttribute(attributeName); TreeSet<Node> resultSet = new TreeSet<Node>(comparator); if (list1 != null) { for (int i = 0; i < list1.getLength(); i++) { resultSet.add(list1.item(i)); } } if (list2 != null) { for (int i = 0; i < list2.getLength(); i++) { resultSet.add(list2.item(i)); } } for (Node node : resultSet) { targetNode.appendChild(node); } } }