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 }