001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.felix.ipojo.metadata;
020
021import java.util.ArrayList;
022import java.util.Collection;
023import java.util.HashMap;
024import java.util.Iterator;
025import java.util.LinkedHashMap;
026import java.util.List;
027import java.util.Map;
028import java.util.Set;
029
030/**
031 * An element represents an XML Element.
032 * It contains a name, a namepace, {@link Attribute} objects
033 * and sub-elements. This class is used to parse iPOJO metadata.
034 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
035 */
036public class Element {
037
038    /**
039     * The name of the element.
040     */
041    private String m_name;
042
043    /**
044     * The namespace of the element.
045     */
046    private String m_nameSpace;
047
048    /**
049     * The map of attributes of the element (attribute name -> {@link Attribute}).
050     * The map key is the qualified name of the attribute (<code>ns:name</code>)
051     * The value is the attribute object.
052     */
053    private Map m_attributes = new LinkedHashMap();
054
055    /**
056     * The map of the sub-element of the element (element name -> {@link Element}.
057     * The map key is the element qualified name (ns:name).
058     * The value is the array of element of this name.
059     */
060    private Map m_elements = new LinkedHashMap();
061
062    /**
063     * Creates an Element.
064     * @param name the name of the element
065     * @param ns the namespace of the element
066     */
067    public Element(String name, String ns) {
068        m_name = name.toLowerCase();
069        if (ns != null && ns.length() > 0) {
070            m_nameSpace = ns.toLowerCase();
071        }
072    }
073
074    /**
075     * Gets sub-elements.
076     * If no sub-elements, an empty array is returned.
077     * @return the sub elements
078     */
079    public Element[] getElements() {
080        Collection col = m_elements.values();
081        Iterator it = col.iterator();
082        List list = new ArrayList();
083        while (it.hasNext()) {
084            Element[] v = (Element[]) it.next();
085            for (int i = 0; i < v.length; i++) {
086                list.add(v[i]);
087            }
088        }
089        return (Element[]) list.toArray(new Element[list.size()]);
090    }
091
092    /**
093     * Gets element attributes.
094     * If no attributes, an empty array is returned.
095     * @return the attributes
096     */
097    public Attribute[] getAttributes() {
098        return (Attribute[]) m_attributes.values().toArray(new Attribute[0]);
099    }
100
101    /**
102     * Gets element name.
103     * @return the name of the element
104     */
105    public String getName() {
106        return m_name;
107    }
108
109    /**
110     * Gets element namespace.
111     * @return the namespace of the element
112     */
113    public String getNameSpace() {
114        return m_nameSpace;
115    }
116
117    /**
118     * Returns the value of the attribute given in parameter.
119     * @param name the name of the searched attribute
120     * @return the value of the attribute given in parameter,
121     * <code>null</code> if the attribute does not exist
122     */
123    public String getAttribute(String name) {
124        name = name.toLowerCase();
125        Attribute att = (Attribute) m_attributes.get(name);
126        if (att == null) {
127            return null;
128        } else {
129            return att.getValue();
130        }
131    }
132
133    /**
134     * Returns the value of the attribute "name" of the namespace "ns".
135     * @param name the name of the attribute to find
136     * @param ns the namespace of the attribute to find
137     * @return the String value of the attribute, or
138     * <code>null</code> if the attribute is not found.
139     */
140    public String getAttribute(String name, String ns) {
141        name = ns.toLowerCase() + ":" + name.toLowerCase();
142        return getAttribute(name);
143    }
144
145    /**
146     * Gets the qualified name of the current element.
147     * @return the qualified name of the current element.
148     */
149    private String getQualifiedName() {
150        if (m_nameSpace == null) {
151            return m_name;
152        } else {
153            return m_nameSpace + ":" + m_name;
154        }
155    }
156
157    /**
158     * Adds a sub-element.
159     * @param elem the element to add
160     */
161    public void addElement(Element elem) {
162        Element[] array = (Element[]) m_elements.get(elem.getQualifiedName());
163        if (array == null) {
164            m_elements.put(elem.getQualifiedName(), new Element[] {elem});
165        } else {
166            Element[] newElementsList = new Element[array.length + 1];
167            System.arraycopy(array, 0, newElementsList, 0, array.length);
168            newElementsList[array.length] = elem;
169            m_elements.put(elem.getQualifiedName(), newElementsList);
170        }
171    }
172
173    /**
174     * Removes a sub-element.
175     * @param elem the element to remove
176     */
177    public void removeElement(Element elem) {
178        Element[] array = (Element[]) m_elements.get(elem.getQualifiedName());
179        if (array == null) {
180            return;
181        } else {
182            int idx = -1;
183            for (int i = 0; i < array.length; i++) {
184                if (array[i] == elem) {
185                    idx = i;
186                    break;
187                }
188            }
189
190            if (idx >= 0) {
191                if ((array.length - 1) == 0) {
192                    m_elements.remove(elem.getQualifiedName());
193                } else {
194                    Element[] newElementsList = new Element[array.length - 1];
195                    System.arraycopy(array, 0, newElementsList, 0, idx);
196                    if (idx < newElementsList.length) {
197                        System.arraycopy(array, idx + 1, newElementsList, idx, newElementsList.length - idx);
198                    }
199                    m_elements.put(elem.getQualifiedName(), newElementsList); // Update the stored list.
200                }
201            }
202        }
203    }
204
205    /**
206     * Adds a attribute.
207     * @param att the attribute to add
208     */
209    public void addAttribute(Attribute att) {
210        String name = att.getName().toLowerCase();
211        if (att.getNameSpace() != null) {
212            name = att.getNameSpace().toLowerCase() + ":" + name;
213        }
214        m_attributes.put(name, att);
215    }
216
217    /**
218     * Removes an attribute.
219     * @param att the attribute to remove
220     */
221    public void removeAttribute(Attribute att) {
222        String name = att.getName();
223        if (att.getNameSpace() != null) {
224            name = att.getNameSpace() + ":" + name;
225        }
226        m_attributes.remove(name);
227    }
228
229    /**
230     * Gets the elements array of the element type given in parameter.
231     * This method looks for an empty namespace.
232     * @param name the type of the element to find (element name)
233     * @return the resulting element array (<code>null</code> if the search failed)
234     */
235    public Element[] getElements(String name) {
236        Element[] elems = (Element[]) m_elements.get(name.toLowerCase());
237        return elems;
238    }
239
240    /**
241     * Gets the elements array of the element type given in parameter.
242     * @param name the type of the element to find (element name)
243     * @param ns the namespace of the element
244     * @return the resulting element array (<code>null</code> if the search failed)
245     */
246    public Element[] getElements(String name, String ns) {
247        if (ns == null || ns.length() == 0) {
248            return getElements(name);
249        }
250        name = ns + ":" + name;
251        return getElements(name);
252    }
253
254    /**
255     * Does the element contain a sub-element of the type given in parameter.
256     * @param name the type of the element to check.
257     * @return <code>true</code> if the element contains an element of the type "name"
258     */
259    public boolean containsElement(String name) {
260        return m_elements.containsKey(name.toLowerCase());
261    }
262
263    /**
264     * Does the element contain a sub-element of the type given in parameter.
265     * @param name the type of the element to check.
266     * @param ns the namespace of the element to check.
267     * @return <code>true</code> if the element contains an element of the type "name"
268     */
269    public boolean containsElement(String name, String ns) {
270        if (ns != null && ns.length() != 0) {
271            name = ns + ":" + name;
272        }
273        return containsElement(name);
274    }
275
276    /**
277     * Does the element contain an attribute of the name given in parameter.
278     * @param name the name of the element
279     * @return <code>true</code> if the element contains an attribute of the type "name"
280     */
281    public boolean containsAttribute(String name) {
282        return m_attributes.containsKey(name.toLowerCase());
283    }
284
285    /**
286     * Gets the XML form of this element.
287     * @return the XML snippet representing this element.
288     */
289    public String toXMLString() {
290        return toXMLString(0);
291    }
292
293    /**
294     * Internal method to get XML form of an element.
295     * @param indent the indentation to used.
296     * @return the XML snippet representing this element.
297     */
298    private String toXMLString(int indent) {
299        StringBuffer xml = new StringBuffer();
300
301        StringBuffer tabs = new StringBuffer();
302        for (int j = 0; j < indent; j++) {
303            tabs.append("\t");
304        }
305
306        xml.append(tabs);
307        if (m_nameSpace == null) {
308            xml.append("<" + m_name);
309        } else {
310            xml.append("<" + m_nameSpace + ":" + m_name);
311        }
312
313        Set keys = m_attributes.keySet();
314        Iterator it = keys.iterator();
315        while (it.hasNext()) {
316            Attribute current = (Attribute) m_attributes.get(it.next());
317            if (current.getNameSpace() == null) {
318                xml.append(" " + current.getName() + "=\"" + current.getValue() + "\"");
319            } else {
320                xml.append(" " + current.getNameSpace() + ":" + current.getName() + "=\"" + current.getValue() + "\"");
321            }
322        }
323
324
325        if (m_elements.size() == 0) {
326            xml.append("/>");
327            return xml.toString();
328        } else {
329            xml.append(">");
330            keys = m_elements.keySet();
331            it = keys.iterator();
332            while (it.hasNext()) {
333                Element[] e = (Element[]) m_elements.get(it.next());
334                for (int i = 0; i < e.length; i++) {
335                    xml.append("\n");
336                    xml.append(e[i].toXMLString(indent + 1));
337                }
338            }
339            xml.append("\n" + tabs + "</" + m_name + ">");
340            return xml.toString();
341        }
342    }
343
344    /**
345     * To String method.
346     * @return the String form of this element.
347     * @see java.lang.Object#toString()
348     */
349    public String toString() {
350        return toString(0);
351    }
352
353    /**
354     * Internal method to compute the toString method.
355     * @param indent the indentation to use.
356     * @return the String form of this element.
357     */
358    private String toString(int indent) {
359        StringBuffer xml = new StringBuffer();
360
361        StringBuffer tabs = new StringBuffer();
362        for (int j = 0; j < indent; j++) {
363            tabs.append("\t");
364        }
365
366        xml.append(tabs);
367        if (m_nameSpace == null) {
368            xml.append(m_name);
369        } else {
370            xml.append(m_nameSpace + ":" + m_name);
371        }
372
373        Set keys = m_attributes.keySet();
374        Iterator it = keys.iterator();
375        while (it.hasNext()) {
376            Attribute current = (Attribute) m_attributes.get(it.next());
377            if (current.getNameSpace() == null) {
378                xml.append(" " + current.getName() + "=\"" + current.getValue() + "\"");
379            } else {
380                xml.append(" " + current.getNameSpace() + ":" + current.getName() + "=\"" + current.getValue() + "\"");
381            }
382        }
383
384        if (m_elements.size() == 0) {
385            return xml.toString();
386        } else {
387            keys = m_elements.keySet();
388            it = keys.iterator();
389            while (it.hasNext()) {
390                Element[] e = (Element[]) m_elements.get(it.next());
391                for (int i = 0; i < e.length; i++) {
392                    xml.append("\n");
393                    xml.append(e[i].toString(indent + 1));
394                }
395            }
396            return xml.toString();
397        }
398    }
399
400}