001 package ezvcard.io.xml;
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.VCardDataType;
012 import ezvcard.VCardVersion;
013 import ezvcard.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 The views and conclusions contained in the software and documentation are those
040 of the authors and should not be interpreted as representing official policies,
041 either expressed or implied, of the FreeBSD Project.
042 */
043
044 /**
045 * Wraps xCard functionality around an XML {@link Element}.
046 * @author Michael Angstadt
047 */
048 public class XCardElement {
049 private final Document document;
050 private final Element element;
051 private final VCardVersion version;
052 private final String namespace;
053
054 /**
055 * Creates a new XML element under its own XML document.
056 * @param propertyName the property name (e.g. "adr")
057 */
058 public XCardElement(String propertyName) {
059 this(propertyName, VCardVersion.V4_0);
060 }
061
062 /**
063 * Creates a new XML element under its own XML document.
064 * @param propertyName the property name (e.g. "adr")
065 * @param version the vCard version
066 */
067 public XCardElement(String propertyName, VCardVersion version) {
068 this.version = version;
069 namespace = version.getXmlNamespace();
070 document = XmlUtils.createDocument();
071 element = document.createElementNS(namespace, propertyName);
072 document.appendChild(element);
073 }
074
075 /**
076 * Wraps an existing XML element.
077 * @param element the XML element
078 */
079 public XCardElement(Element element) {
080 this(element, VCardVersion.V4_0);
081 }
082
083 /**
084 * Wraps an existing XML element.
085 * @param element the XML element
086 * @param version the vCard version
087 */
088 public XCardElement(Element element, VCardVersion version) {
089 this.document = element.getOwnerDocument();
090 this.element = element;
091 this.version = version;
092 namespace = version.getXmlNamespace();
093 }
094
095 /**
096 * Gets the first value with one of the given data types.
097 * @param dataTypes the data type(s) to look for (null signifies the
098 * "unknown" data type)
099 * @return the value or null if not found
100 */
101 public String first(VCardDataType... dataTypes) {
102 String names[] = new String[dataTypes.length];
103 for (int i = 0; i < dataTypes.length; i++) {
104 VCardDataType dataType = dataTypes[i];
105 names[i] = toLocalName(dataType);
106 }
107 return first(names);
108 }
109
110 /**
111 * Gets the value of the first child element with one of the given names.
112 * @param names the possible names of the element
113 * @return the element's text or null if not found
114 */
115 public String first(String... names) {
116 List<String> localNamesList = Arrays.asList(names);
117 for (Element child : children()) {
118 if (localNamesList.contains(child.getLocalName()) && namespace.equals(child.getNamespaceURI())) {
119 return child.getTextContent();
120 }
121 }
122 return null;
123 }
124
125 /**
126 * Gets all the values of a given data type.
127 * @param dataType the data type to look for
128 * @return the values
129 */
130 public List<String> all(VCardDataType dataType) {
131 String dataTypeStr = toLocalName(dataType);
132 return all(dataTypeStr);
133 }
134
135 /**
136 * Gets the value of all non-empty child elements that have the given name.
137 * @param localName the element name
138 * @return the values of the child elements
139 */
140 public List<String> all(String localName) {
141 List<String> childrenText = new ArrayList<String>();
142 for (Element child : children()) {
143 if (localName.equals(child.getLocalName()) && namespace.equals(child.getNamespaceURI())) {
144 String text = child.getTextContent();
145 if (text.length() > 0) {
146 childrenText.add(child.getTextContent());
147 }
148 }
149 }
150 return childrenText;
151 }
152
153 /**
154 * Adds a value.
155 * @param dataType the data type or null for the "unknown" data type
156 * @param value the value
157 * @return the created element
158 */
159 public Element append(VCardDataType dataType, String value) {
160 String dataTypeStr = toLocalName(dataType);
161 return append(dataTypeStr, value);
162 }
163
164 /**
165 * Adds a child element.
166 * @param name the name of the child element
167 * @param value the value of the child element.
168 * @return the created element
169 */
170 public Element append(String name, String value) {
171 Element child = document.createElementNS(namespace, name);
172 child.setTextContent(value);
173 element.appendChild(child);
174 return child;
175 }
176
177 /**
178 * Adds multiple child elements, each with the same name.
179 * @param name the name for all the child elements
180 * @param values the values of each child element
181 * @return the created elements
182 */
183 public List<Element> append(String name, Collection<String> values) {
184 if (values.isEmpty()) {
185 Element element = append(name, (String) null);
186 return Arrays.asList(element);
187 }
188
189 List<Element> elements = new ArrayList<Element>(values.size());
190 for (String value : values) {
191 elements.add(append(name, value));
192 }
193 return elements;
194 }
195
196 /**
197 * Gets the owner document.
198 * @return the owner document
199 */
200 public Document document() {
201 return document;
202 }
203
204 /**
205 * Gets the wrapped XML element.
206 * @return the wrapped XML element
207 */
208 public Element element() {
209 return element;
210 }
211
212 /**
213 * Gets the vCard version.
214 * @return the vCard version
215 */
216 public VCardVersion version() {
217 return version;
218 }
219
220 /**
221 * Gets the child elements of the XML element.
222 * @return the child elements
223 */
224 private List<Element> children() {
225 return XmlUtils.toElementList(element.getChildNodes());
226 }
227
228 private String toLocalName(VCardDataType dataType) {
229 return (dataType == null) ? "unknown" : dataType.getName().toLowerCase();
230 }
231 }