001/*
002 *  Copyright 2020 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.core.util.dom;
017
018import java.util.ArrayList;
019import java.util.HashSet;
020import java.util.List;
021import java.util.Set;
022
023import org.apache.commons.lang3.StringUtils;
024import org.w3c.dom.Element;
025import org.w3c.dom.Node;
026import org.w3c.dom.NodeList;
027
028/**
029 * Some useful utility methods for {@link Element} use 
030 */
031public final class DOMUtils
032{
033    private DOMUtils()
034    {
035        // Empty constructor
036    }
037    
038    /**
039     * Retrieves all child elements of the given DOM element.
040     * @param element The DOM element to analyze
041     * @return a {@link List} of child elements instances
042     */
043    public static List<Element> getChildElements(Element element)
044    {
045        if (element == null)
046        {
047            throw new IllegalArgumentException("The element must not be null");
048        }
049        
050        List<Element> elements = new ArrayList<>();
051        
052        NodeList nodes = element.getChildNodes();
053        for (int i = 0; i < nodes.getLength(); i++)
054        {
055            Node node = nodes.item(i);
056            if (node.getNodeType() == Node.ELEMENT_NODE)
057            {
058                elements.add((Element) node);
059            }
060        }
061        
062        return elements;
063    }
064    
065    /**
066     * Get the first child element of the given DOM element.
067     * @param element The DOM element to analyze
068     * @return the first child element or null if there is no child element
069     */
070    public static Element getFirstChildElement(Element element)
071    {
072        if (element == null)
073        {
074            throw new IllegalArgumentException("The element must not be null");
075        }
076        
077        NodeList nodes = element.getChildNodes();
078        for (int i = 0; i < nodes.getLength(); i++)
079        {
080            Node node = nodes.item(i);
081            if (node.getNodeType() == Node.ELEMENT_NODE)
082            {
083                return (Element) node;
084            }
085        }
086        
087        return null;
088    }
089    
090    /**
091     * Retrieves some child elements of the given DOM element
092     * If some elements have the same tag name, only the first of these elements is retrieved
093     * @param element the DOM element to analyze
094     * @return a {@link List} of child elements instances
095     */
096    public static List<Element> getUniqueChildElements(Element element)
097    {
098        if (element == null)
099        {
100            throw new IllegalArgumentException("The element must not be null");
101        }
102        
103        List<Element> elements = new ArrayList<>();
104        Set<String> tagNames = new HashSet<>();
105        
106        NodeList nodes = element.getChildNodes();
107        for (int i = 0; i < nodes.getLength(); i++)
108        {
109            Node node = nodes.item(i);
110            String dataName = node.getNodeName();
111            if (!tagNames.contains(dataName) && node.getNodeType() == Node.ELEMENT_NODE)
112            {
113                elements.add((Element) node);
114                tagNames.add(dataName);
115            }
116        }
117        
118        return elements;
119    }
120    
121    /**
122     * Retrieves all child elements of the given DOM element that match the given element name.
123     * Only look at the direct child level of the given element.
124     * Do not go into further depth (in contrast to the DOM API's getElementsByTagName method).
125     * @param element The DOM element to analyze
126     * @param name The child element name to look for
127     * @return a {@link List} of child elements instances
128     */
129    public static List<Element> getChildElementsByTagName(Element element, String name)
130    {
131        if (element == null)
132        {
133            throw new IllegalArgumentException("The element must not be null");
134        }
135        
136        if (StringUtils.isEmpty(name))
137        {
138            throw new IllegalArgumentException("The name must not be empty");
139        }
140        
141        List<Element> elements = new ArrayList<>();
142        
143        NodeList nodes = element.getChildNodes();
144        for (int i = 0; i < nodes.getLength(); i++)
145        {
146            Node node = nodes.item(i);
147            if (node.getNodeType() == Node.ELEMENT_NODE && name.equals(node.getNodeName()))
148            {
149                elements.add((Element) node);
150            }
151        }
152        
153        return elements;
154    }
155    
156    /**
157     * Retrieves the first child element of the given DOM element that matches the given element name.
158     * Only look at the direct child level of the given element. Do not go into further depth.
159     * @param element The DOM element to analyze
160     * @param name The child element name to look for
161     * @return the first child element instance
162     */
163    public static Element getChildElementByTagName(Element element, String name)
164    {
165        if (element == null)
166        {
167            throw new IllegalArgumentException("The element must not be null");
168        }
169        
170        if (StringUtils.isEmpty(name))
171        {
172            throw new IllegalArgumentException("The name must not be empty");
173        }
174        
175        NodeList nodes = element.getChildNodes();
176        for (int i = 0; i < nodes.getLength(); i++)
177        {
178            Node node = nodes.item(i);
179            if (node.getNodeType() == Node.ELEMENT_NODE && name.equals(node.getNodeName()))
180            {
181                return (Element) node;
182            }
183        }
184        
185        return null;
186    }
187    
188    /**
189     * Checks if the given DOM element has a child element that matches the given element name.
190     * @param element The DOM element to analyze
191     * @param name The child element name to look for
192     * @return <code>true</code> if the given DOM element has a child element that matches the given element name, <code>false</code> otherwise
193     */
194    public static boolean hasChildElement(Element element, String name)
195    {
196        return getChildElementByTagName(element, name) != null;
197    }
198}