001    package biweekly.io.xml;
002    
003    import static biweekly.io.xml.XCalNamespaceContext.XCAL_NS;
004    
005    import java.util.ArrayList;
006    import java.util.Collection;
007    import java.util.List;
008    
009    import org.w3c.dom.Document;
010    import org.w3c.dom.Element;
011    
012    import biweekly.ICalDataType;
013    import biweekly.util.XmlUtils;
014    
015    /*
016     Copyright (c) 2013, Michael Angstadt
017     All rights reserved.
018    
019     Redistribution and use in source and binary forms, with or without
020     modification, are permitted provided that the following conditions are met: 
021    
022     1. Redistributions of source code must retain the above copyright notice, this
023     list of conditions and the following disclaimer. 
024     2. Redistributions in binary form must reproduce the above copyright notice,
025     this list of conditions and the following disclaimer in the documentation
026     and/or other materials provided with the distribution. 
027    
028     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
029     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
030     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
031     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
032     ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
033     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
034     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
035     ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
036     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
037     SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
038     */
039    
040    /**
041     * Wraps xCal functionality around an XML {@link Element} object.
042     * @author Michael Angstadt
043     */
044    public class XCalElement {
045            private final Element element;
046            private final Document document;
047    
048            /**
049             * Creates a new xCal element.
050             * @param element the XML element to wrap
051             */
052            public XCalElement(Element element) {
053                    this.element = element;
054                    document = element.getOwnerDocument();
055            }
056    
057            /**
058             * Gets the first value of the given data type.
059             * @param dataType the data type to look for or null for the "unknown" data
060             * type
061             * @return the value or null if not found
062             */
063            public String first(ICalDataType dataType) {
064                    String dataTypeStr = toLocalName(dataType);
065                    return first(dataTypeStr);
066            }
067    
068            /**
069             * Gets the value of the first child element with the given name.
070             * @param localName the name of the element
071             * @return the element's text or null if not found
072             */
073            public String first(String localName) {
074                    for (Element child : children()) {
075                            if (localName.equals(child.getLocalName()) && XCAL_NS.equals(child.getNamespaceURI())) {
076                                    return child.getTextContent();
077                            }
078                    }
079                    return null;
080            }
081    
082            /**
083             * Gets all the values of a given data type.
084             * @param dataType the data type to look for or null for the "unknown" data
085             * type
086             * @return the values
087             */
088            public List<String> all(ICalDataType dataType) {
089                    String dataTypeStr = toLocalName(dataType);
090                    return all(dataTypeStr);
091            }
092    
093            /**
094             * Gets the values of all child elements that have the given name.
095             * @param localName the element name
096             * @return the values of the child elements
097             */
098            public List<String> all(String localName) {
099                    List<String> childrenText = new ArrayList<String>();
100                    for (Element child : children()) {
101                            if (localName.equals(child.getLocalName()) && XCAL_NS.equals(child.getNamespaceURI())) {
102                                    String text = child.getTextContent();
103                                    childrenText.add(text);
104                            }
105                    }
106                    return childrenText;
107            }
108    
109            /**
110             * Adds a value.
111             * @param dataType the data type or null for the "unknown" data type
112             * @param value the value
113             * @return the created element
114             */
115            public Element append(ICalDataType dataType, String value) {
116                    String dataTypeStr = toLocalName(dataType);
117                    return append(dataTypeStr, value);
118            }
119    
120            /**
121             * Adds a child element.
122             * @param name the name of the child element
123             * @param value the value of the child element.
124             * @return the created element
125             */
126            public Element append(String name, String value) {
127                    Element child = document.createElementNS(XCAL_NS, name);
128                    child.setTextContent(value);
129                    element.appendChild(child);
130                    return child;
131            }
132    
133            /**
134             * Adds a child element.
135             * @param name the name of the child element
136             * @return the created element
137             */
138            public XCalElement append(String name) {
139                    return new XCalElement(append(name, (String) null));
140            }
141    
142            /**
143             * Adds an empty value.
144             * @param dataType the data type
145             * @return the created element
146             */
147            public XCalElement append(ICalDataType dataType) {
148                    return append(dataType.getName().toLowerCase());
149            }
150    
151            /**
152             * Adds multiple child elements, each with the same name.
153             * @param name the name for all the child elements
154             * @param values the values of each child element
155             * @return the created elements
156             */
157            public List<Element> append(String name, Collection<String> values) {
158                    List<Element> elements = new ArrayList<Element>(values.size());
159                    for (String value : values) {
160                            elements.add(append(name, value));
161                    }
162                    return elements;
163            }
164    
165            /**
166             * Gets the owner document.
167             * @return the owner document
168             */
169            public Document document() {
170                    return document;
171            }
172    
173            /**
174             * Gets the wrapped XML element.
175             * @return the wrapped XML element
176             */
177            public Element getElement() {
178                    return element;
179            }
180    
181            /**
182             * Gets the child elements of the wrapped XML element.
183             * @return the child elements
184             */
185            private List<Element> children() {
186                    return XmlUtils.toElementList(element.getChildNodes());
187            }
188    
189            /**
190             * Gets all child elements with the given data type.
191             * @param dataType the data type
192             * @return the child elements
193             */
194            public List<XCalElement> children(ICalDataType dataType) {
195                    String localName = dataType.getName().toLowerCase();
196                    List<XCalElement> children = new ArrayList<XCalElement>();
197                    for (Element child : children()) {
198                            if (localName.equals(child.getLocalName()) && XCAL_NS.equals(child.getNamespaceURI())) {
199                                    children.add(new XCalElement(child));
200                            }
201                    }
202                    return children;
203            }
204    
205            /**
206             * Gets the first child element with the given data type.
207             * @param dataType the data type
208             * @return the child element or null if not found
209             */
210            public XCalElement child(ICalDataType dataType) {
211                    String localName = dataType.getName().toLowerCase();
212                    for (Element child : children()) {
213                            if (localName.equals(child.getLocalName()) && XCAL_NS.equals(child.getNamespaceURI())) {
214                                    return new XCalElement(child);
215                            }
216                    }
217                    return null;
218            }
219    
220            private String toLocalName(ICalDataType dataType) {
221                    return (dataType == null) ? "unknown" : dataType.getName().toLowerCase();
222            }
223    }