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.parameter.Value;
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
060             * @return the value or null if not found
061             */
062            public String first(Value dataType) {
063                    return first(dataType.getValue().toLowerCase());
064            }
065    
066            /**
067             * Gets the first value of the "unknown" data type.
068             * @return the value or null if not found
069             */
070            public String firstUnknown() {
071                    return first("unknown");
072            }
073    
074            /**
075             * Gets the value of the first child element with the given name.
076             * @param localName the name of the element
077             * @return the element's text or null if not found
078             */
079            public String first(String localName) {
080                    for (Element child : children()) {
081                            if (localName.equals(child.getLocalName()) && XCAL_NS.equals(child.getNamespaceURI())) {
082                                    return child.getTextContent();
083                            }
084                    }
085                    return null;
086            }
087    
088            /**
089             * Gets all the values of a given data type.
090             * @param dataType the data type to look for
091             * @return the values
092             */
093            public List<String> all(Value dataType) {
094                    return all(dataType.getValue().toLowerCase());
095            }
096    
097            /**
098             * Gets the values of all child elements that have the given name.
099             * @param localName the element name
100             * @return the values of the child elements
101             */
102            public List<String> all(String localName) {
103                    List<String> childrenText = new ArrayList<String>();
104                    for (Element child : children()) {
105                            if (localName.equals(child.getLocalName()) && XCAL_NS.equals(child.getNamespaceURI())) {
106                                    String text = child.getTextContent();
107                                    childrenText.add(text);
108                            }
109                    }
110                    return childrenText;
111            }
112    
113            /**
114             * Adds a value with the "unknown" data type.
115             * @param value the value
116             * @return the created element
117             */
118            public Element appendUnknown(String value) {
119                    return append("unknown", value);
120            }
121    
122            /**
123             * Adds a value.
124             * @param dataType the data type
125             * @param value the value
126             * @return the created element
127             */
128            public Element append(Value dataType, String value) {
129                    return append(dataType.getValue().toLowerCase(), value);
130            }
131    
132            /**
133             * Adds a child element.
134             * @param name the name of the child element
135             * @param value the value of the child element.
136             * @return the created element
137             */
138            public Element append(String name, String value) {
139                    Element child = document.createElementNS(XCAL_NS, name);
140                    child.setTextContent(value);
141                    element.appendChild(child);
142                    return child;
143            }
144    
145            /**
146             * Adds a child element.
147             * @param name the name of the child element
148             * @return the created element
149             */
150            public XCalElement append(String name) {
151                    return new XCalElement(append(name, (String) null));
152            }
153    
154            /**
155             * Adds an empty value.
156             * @param dataType the data type
157             * @return the created element
158             */
159            public XCalElement append(Value dataType) {
160                    return append(dataType.getValue().toLowerCase());
161            }
162    
163            /**
164             * Adds multiple child elements, each with the same name.
165             * @param name the name for all the child elements
166             * @param values the values of each child element
167             * @return the created elements
168             */
169            public List<Element> append(String name, Collection<String> values) {
170                    List<Element> elements = new ArrayList<Element>(values.size());
171                    for (String value : values) {
172                            elements.add(append(name, value));
173                    }
174                    return elements;
175            }
176    
177            /**
178             * Gets the owner document.
179             * @return the owner document
180             */
181            public Document document() {
182                    return document;
183            }
184    
185            /**
186             * Gets the wrapped XML element.
187             * @return the wrapped XML element
188             */
189            public Element getElement() {
190                    return element;
191            }
192    
193            /**
194             * Gets the child elements of the wrapped XML element.
195             * @return the child elements
196             */
197            private List<Element> children() {
198                    return XmlUtils.toElementList(element.getChildNodes());
199            }
200    
201            /**
202             * Gets all child elements with the given data type.
203             * @param dataType the data type
204             * @return the child elements
205             */
206            public List<XCalElement> children(Value dataType) {
207                    String localName = dataType.getValue().toLowerCase();
208                    List<XCalElement> children = new ArrayList<XCalElement>();
209                    for (Element child : children()) {
210                            if (localName.equals(child.getLocalName()) && XCAL_NS.equals(child.getNamespaceURI())) {
211                                    children.add(new XCalElement(child));
212                            }
213                    }
214                    return children;
215            }
216    
217            /**
218             * Gets the first child element with the given data type.
219             * @param dataType the data type
220             * @return the child element or null if not found
221             */
222            public XCalElement child(Value dataType) {
223                    String localName = dataType.getValue().toLowerCase();
224                    for (Element child : children()) {
225                            if (localName.equals(child.getLocalName()) && XCAL_NS.equals(child.getNamespaceURI())) {
226                                    return new XCalElement(child);
227                            }
228                    }
229                    return null;
230            }
231    }