001    package ezvcard.util;
002    
003    import java.util.ArrayList;
004    import java.util.Arrays;
005    import java.util.Collection;
006    import java.util.List;
007    
008    import org.w3c.dom.Document;
009    import org.w3c.dom.Element;
010    
011    import ezvcard.VCardVersion;
012    
013    /*
014     Copyright (c) 2013, Michael Angstadt
015     All rights reserved.
016    
017     Redistribution and use in source and binary forms, with or without
018     modification, are permitted provided that the following conditions are met: 
019    
020     1. Redistributions of source code must retain the above copyright notice, this
021     list of conditions and the following disclaimer. 
022     2. Redistributions in binary form must reproduce the above copyright notice,
023     this list of conditions and the following disclaimer in the documentation
024     and/or other materials provided with the distribution. 
025    
026     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
027     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
028     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
029     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
030     ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
031     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
032     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
033     ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
034     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
035     SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
036    
037     The views and conclusions contained in the software and documentation are those
038     of the authors and should not be interpreted as representing official policies, 
039     either expressed or implied, of the FreeBSD Project.
040     */
041    
042    /**
043     * Wraps xCard functionality around an XML {@link Element}.
044     * @author Michael Angstadt
045     */
046    public class XCardElement {
047            private final Document document;
048            private final Element element;
049            private List<Element> children;
050            private final VCardVersion version;
051            private final String namespace;
052    
053            /**
054             * Creates a new XML element under its own XML document.
055             * @param propertyName the property name (e.g. "adr")
056             */
057            public XCardElement(String propertyName) {
058                    this(propertyName, VCardVersion.V4_0);
059            }
060    
061            /**
062             * Creates a new XML element under its own XML document.
063             * @param propertyName the property name (e.g. "adr")
064             * @param version the vCard version
065             */
066            public XCardElement(String propertyName, VCardVersion version) {
067                    this.version = version;
068                    namespace = version.getXmlNamespace();
069                    document = XmlUtils.createDocument();
070                    element = document.createElementNS(namespace, propertyName);
071                    document.appendChild(element);
072            }
073    
074            /**
075             * Wraps an existing XML element.
076             * @param element the XML element
077             */
078            public XCardElement(Element element) {
079                    this(element, VCardVersion.V4_0);
080            }
081    
082            /**
083             * Wraps an existing XML element.
084             * @param element the XML element
085             * @param version the vCard version
086             */
087            public XCardElement(Element element, VCardVersion version) {
088                    this.document = element.getOwnerDocument();
089                    this.element = element;
090                    this.version = version;
091                    namespace = version.getXmlNamespace();
092            }
093    
094            /**
095             * Gets the value of the first <code>&lt;text&gt;</code> child element.
096             * @return the text value or null if not found
097             */
098            public String text() {
099                    return get("text");
100            }
101    
102            /**
103             * Gets the value of the first <code>&lt;uri&gt;</code> child element.
104             * @return the URI or null if not found
105             */
106            public String uri() {
107                    return get("uri");
108            }
109    
110            /**
111             * Gets the value of the first <code>&lt;date-and-or-time&gt;</code> child
112             * element.
113             * @return the value or null if not found
114             */
115            public String dateAndOrTime() {
116                    return get("date-and-or-time");
117            }
118    
119            /**
120             * Gets the value of the first <code>&lt;timestamp&gt;</code> child element.
121             * @return the timestamp or null if not found
122             */
123            public String timestamp() {
124                    return get("timestamp");
125            }
126    
127            /**
128             * Gets the value of the first child element with one of the given names.
129             * @param names the possible names of the element
130             * @return the element's text or null if not found
131             */
132            public String get(String... names) {
133                    List<String> localNamesList = Arrays.asList(names);
134                    for (Element child : children()) {
135                            if (localNamesList.contains(child.getLocalName()) && namespace.equals(child.getNamespaceURI())) {
136                                    return child.getTextContent();
137                            }
138                    }
139                    return null;
140            }
141    
142            /**
143             * Gets the value of all non-empty child elements that have the given name.
144             * @param localName the element name
145             * @return the values of the child elements
146             */
147            public List<String> getAll(String localName) {
148                    List<String> childrenText = new ArrayList<String>();
149                    for (Element child : children()) {
150                            if (localName.equals(child.getLocalName()) && namespace.equals(child.getNamespaceURI())) {
151                                    String text = child.getTextContent();
152                                    if (text.length() > 0) {
153                                            childrenText.add(child.getTextContent());
154                                    }
155                            }
156                    }
157                    return childrenText;
158            }
159    
160            /**
161             * Adds a <code>&lt;text&gt;</code> child element.
162             * @param text the text
163             * @return the created element
164             */
165            public Element text(String text) {
166                    return append("text", text);
167            }
168    
169            /**
170             * Adds a <code>&lt;uri&gt;</code> child element.
171             * @param uri the URI
172             * @return the created element
173             */
174            public Element uri(String uri) {
175                    return append("uri", uri);
176            }
177    
178            /**
179             * Adds a <code>&lt;date-and-or-time&gt;</code> child element.
180             * @param dateAndOrTime the date/time
181             * @return the created element
182             */
183            public Element dateAndOrTime(String dateAndOrTime) {
184                    return append("date-and-or-time", dateAndOrTime);
185            }
186    
187            /**
188             * Adds a <code>&lt;timestamp&gt;</code> child element.
189             * @param timestamp the timestamp
190             * @return the created element
191             */
192            public Element timestamp(String timestamp) {
193                    return append("timestamp", timestamp);
194            }
195    
196            /**
197             * Adds a child element.
198             * @param name the name of the child element
199             * @param value the value of the child element.
200             * @return the created element
201             */
202            public Element append(String name, String value) {
203                    Element child = document.createElementNS(namespace, name);
204                    child.setTextContent(value);
205                    element.appendChild(child);
206    
207                    if (children != null) {
208                            children.add(child);
209                    }
210    
211                    return child;
212            }
213    
214            /**
215             * Adds multiple child elements, each with the same name.
216             * @param name the name for all the child elements
217             * @param values the values of each child element
218             * @return the created elements
219             */
220            public List<Element> append(String name, Collection<String> values) {
221                    List<Element> elements = new ArrayList<Element>(values.size());
222                    for (String value : values) {
223                            elements.add(append(name, value));
224                    }
225                    return elements;
226            }
227    
228            /**
229             * Gets the owner document.
230             * @return the owner document
231             */
232            public Document document() {
233                    return document;
234            }
235    
236            /**
237             * Gets the wrapped XML element.
238             * @return the wrapped XML element
239             */
240            public Element element() {
241                    return element;
242            }
243    
244            /**
245             * Gets the vCard version.
246             * @return the vCard version
247             */
248            public VCardVersion version() {
249                    return version;
250            }
251    
252            /**
253             * Gets the child elements of the XML element.
254             * @return the child elements
255             */
256            private List<Element> children() {
257                    if (children == null) {
258                            children = XmlUtils.toElementList(element.getChildNodes());
259                    }
260                    return children;
261            }
262    }