001 package ezvcard.io.xml; 002 003 import static ezvcard.util.IOUtils.utf8Writer; 004 005 import java.io.File; 006 import java.io.FileInputStream; 007 import java.io.IOException; 008 import java.io.InputStream; 009 import java.io.OutputStream; 010 import java.io.Reader; 011 import java.io.StringWriter; 012 import java.io.Writer; 013 import java.util.ArrayList; 014 import java.util.Collections; 015 import java.util.HashMap; 016 import java.util.List; 017 import java.util.Map; 018 019 import javax.xml.namespace.QName; 020 import javax.xml.transform.OutputKeys; 021 import javax.xml.transform.TransformerException; 022 import javax.xml.xpath.XPath; 023 import javax.xml.xpath.XPathConstants; 024 import javax.xml.xpath.XPathExpressionException; 025 import javax.xml.xpath.XPathFactory; 026 027 import org.w3c.dom.Document; 028 import org.w3c.dom.Element; 029 import org.xml.sax.SAXException; 030 031 import ezvcard.Ezvcard; 032 import ezvcard.Messages; 033 import ezvcard.VCard; 034 import ezvcard.VCardDataType; 035 import ezvcard.VCardVersion; 036 import ezvcard.io.CannotParseException; 037 import ezvcard.io.EmbeddedVCardException; 038 import ezvcard.io.SkipMeException; 039 import ezvcard.io.scribe.ScribeIndex; 040 import ezvcard.io.scribe.VCardPropertyScribe; 041 import ezvcard.io.scribe.VCardPropertyScribe.Result; 042 import ezvcard.parameter.VCardParameters; 043 import ezvcard.property.ProductId; 044 import ezvcard.property.VCardProperty; 045 import ezvcard.property.Xml; 046 import ezvcard.util.IOUtils; 047 import ezvcard.util.ListMultimap; 048 import ezvcard.util.XmlUtils; 049 050 /* 051 Copyright (c) 2013, Michael Angstadt 052 All rights reserved. 053 054 Redistribution and use in source and binary forms, with or without 055 modification, are permitted provided that the following conditions are met: 056 057 1. Redistributions of source code must retain the above copyright notice, this 058 list of conditions and the following disclaimer. 059 2. Redistributions in binary form must reproduce the above copyright notice, 060 this list of conditions and the following disclaimer in the documentation 061 and/or other materials provided with the distribution. 062 063 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 064 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 065 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 066 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 067 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 068 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 069 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 070 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 071 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 072 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 073 074 The views and conclusions contained in the software and documentation are those 075 of the authors and should not be interpreted as representing official policies, 076 either expressed or implied, of the FreeBSD Project. 077 */ 078 079 //@formatter:off 080 /** 081 * <p> 082 * Represents an XML document that contains vCard objects ("xCard" standard). 083 * This class can be used to read and write xCard documents. 084 * </p> 085 * <p> 086 * <b>Examples:</b> 087 * 088 * <pre class="brush:java"> 089 * String xml = 090 * "<vcards xmlns=\"urn:ietf:params:xml:ns:vcard-4.0\">" + 091 * "<vcard>" + 092 * "<fn>" + 093 * "<text>John Doe</text>" + 094 * "</fn>" + 095 * "<n>" + 096 * "<surname>Doe</surname>" + 097 * "<given>Johnathan</given>" + 098 * "<additional>Jonny</additional>" + 099 * "<additional>John</additional>" + 100 * "<prefix>Mr.</prefix>" + 101 * "<suffix />" + 102 * "</n>" + 103 * "</vcard>" + 104 * "</vcards>"; 105 * 106 * //parsing an existing xCard document 107 * XCardDocument xcard = new XCardDocument(xml); 108 * List<VCard> vcards = xcard.parseAll(); 109 * 110 * //creating an empty xCard document 111 * XCardDocument xcard = new XCardDocument(); 112 * 113 * //VCard objects can be added at any time 114 * VCard vcard = ... 115 * xcard.add(vcard); 116 * 117 * //retrieving the raw XML DOM 118 * Document document = xcard.getDocument(); 119 * 120 * //call one of the "write()" methods to output the xCard document 121 * File file = new File("johndoe.xml"); 122 * xcard.write(file); 123 * </pre> 124 * 125 * </p> 126 * @author Michael Angstadt 127 * @see <a href="http://tools.ietf.org/html/rfc6351">RFC 6351</a> 128 */ 129 //@formatter:on 130 public class XCardDocument { 131 private static final VCardVersion version4 = VCardVersion.V4_0; //xCard only supports 4.0 132 private static final XCardNamespaceContext nsContext = new XCardNamespaceContext(version4, "v"); 133 134 /** 135 * Defines the names of the XML elements that are used to hold each 136 * parameter's value. 137 */ 138 private final Map<String, VCardDataType> parameterDataTypes = new HashMap<String, VCardDataType>(); 139 { 140 registerParameterDataType(VCardParameters.ALTID, VCardDataType.TEXT); 141 registerParameterDataType(VCardParameters.CALSCALE, VCardDataType.TEXT); 142 registerParameterDataType(VCardParameters.GEO, VCardDataType.URI); 143 registerParameterDataType(VCardParameters.LABEL, VCardDataType.TEXT); 144 registerParameterDataType(VCardParameters.LANGUAGE, VCardDataType.LANGUAGE_TAG); 145 registerParameterDataType(VCardParameters.MEDIATYPE, VCardDataType.TEXT); 146 registerParameterDataType(VCardParameters.PID, VCardDataType.TEXT); 147 registerParameterDataType(VCardParameters.PREF, VCardDataType.INTEGER); 148 registerParameterDataType(VCardParameters.SORT_AS, VCardDataType.TEXT); 149 registerParameterDataType(VCardParameters.TYPE, VCardDataType.TEXT); 150 registerParameterDataType(VCardParameters.TZ, VCardDataType.URI); 151 } 152 153 private ScribeIndex index = new ScribeIndex(); 154 private boolean addProdId = true; 155 private boolean versionStrict = true; 156 private final List<List<String>> parseWarnings = new ArrayList<List<String>>(); 157 private Document document; 158 private Element root; 159 160 /** 161 * Creates an xCard document. 162 */ 163 public XCardDocument() { 164 document = XmlUtils.createDocument(); 165 root = createElement("vcards"); 166 document.appendChild(root); 167 } 168 169 /** 170 * Parses an xCard document from a string. 171 * @param xml the XML string to read the vCards from 172 * @throws SAXException if there's a problem parsing the XML 173 */ 174 public XCardDocument(String xml) throws SAXException { 175 this(XmlUtils.toDocument(xml)); 176 } 177 178 /** 179 * Parses an xCard document from an input stream. 180 * @param in the input stream to read the vCards from 181 * @throws IOException if there's a problem reading from the input stream 182 * @throws SAXException if there's a problem parsing the XML 183 */ 184 public XCardDocument(InputStream in) throws SAXException, IOException { 185 this(XmlUtils.toDocument(in)); 186 } 187 188 /** 189 * Parses an xCard document from a file. 190 * @param file the file to read the vCards from 191 * @throws IOException if there's a problem reading from the file 192 * @throws SAXException if there's a problem parsing the XML 193 */ 194 public XCardDocument(File file) throws SAXException, IOException { 195 InputStream in = new FileInputStream(file); 196 try { 197 init(XmlUtils.toDocument(in)); 198 } finally { 199 IOUtils.closeQuietly(in); 200 } 201 } 202 203 /** 204 * <p> 205 * Parses an xCard document from a reader. 206 * </p> 207 * <p> 208 * Note that use of this constructor is discouraged. It ignores the 209 * character encoding that is defined within the XML document itself, and 210 * should only be used if the encoding is undefined or if the encoding needs 211 * to be ignored for whatever reason. The 212 * {@link #XCardDocument(InputStream)} constructor should be used instead, 213 * since it takes the XML document's character encoding into account when 214 * parsing. 215 * </p> 216 * @param reader the reader to read the vCards from 217 * @throws IOException if there's a problem reading from the reader 218 * @throws SAXException if there's a problem parsing the XML 219 */ 220 public XCardDocument(Reader reader) throws SAXException, IOException { 221 this(XmlUtils.toDocument(reader)); 222 } 223 224 /** 225 * Wraps an existing XML DOM object. 226 * @param document the XML DOM that contains the xCard document 227 */ 228 public XCardDocument(Document document) { 229 init(document); 230 } 231 232 private void init(Document document) { 233 XPath xpath = XPathFactory.newInstance().newXPath(); 234 xpath.setNamespaceContext(nsContext); 235 236 try { 237 //find the <vcards> element 238 String prefix = nsContext.getPrefix(); 239 root = (Element) xpath.evaluate("//" + prefix + ":vcards", document, XPathConstants.NODE); 240 } catch (XPathExpressionException e) { 241 //never thrown, xpath expression is hard coded 242 } 243 } 244 245 /** 246 * Gets whether or not a "PRODID" property will be added to each vCard, 247 * saying that the vCard was generated by this library. 248 * @return true if the property will be added, false if not (defaults to 249 * true) 250 */ 251 public boolean isAddProdId() { 252 return addProdId; 253 } 254 255 /** 256 * Sets whether or not to add a "PRODID" property to each vCard, saying that 257 * the vCard was generated by this library. 258 * @param addProdId true to add this property, false not to (defaults to 259 * true) 260 */ 261 public void setAddProdId(boolean addProdId) { 262 this.addProdId = addProdId; 263 } 264 265 /** 266 * Gets whether properties that do not support xCard (vCard version 4.0) 267 * will be excluded from the written vCard. 268 * @return true to exclude properties that do not support xCard, false to 269 * include them anyway (defaults to true) 270 */ 271 public boolean isVersionStrict() { 272 return versionStrict; 273 } 274 275 /** 276 * Sets whether properties that do not support xCard (vCard version 4.0) 277 * will be excluded from the written vCard. 278 * @param versionStrict true to exclude properties that do not support 279 * xCard, false to include them anyway (defaults to true) 280 */ 281 public void setVersionStrict(boolean versionStrict) { 282 this.versionStrict = versionStrict; 283 } 284 285 /** 286 * Gets the warnings from the last parse operation. 287 * @return the warnings (it is a "list of lists"--each parsed {@link VCard} 288 * object has its own warnings list) 289 * @see #parseAll 290 * @see #parseFirst 291 */ 292 public List<List<String>> getParseWarnings() { 293 return parseWarnings; 294 } 295 296 /** 297 * Registers the data type of an experimental parameter. Experimental 298 * parameters use the "unknown" data type by default. 299 * @param parameterName the parameter name (e.g. "x-foo") 300 * @param dataType the data type or null to remove 301 */ 302 public void registerParameterDataType(String parameterName, VCardDataType dataType) { 303 parameterName = parameterName.toLowerCase(); 304 if (dataType == null) { 305 parameterDataTypes.remove(parameterName); 306 } else { 307 parameterDataTypes.put(parameterName, dataType); 308 } 309 } 310 311 /** 312 * <p> 313 * Registers a property scribe. This is the same as calling: 314 * </p> 315 * <p> 316 * {@code getScribeIndex().register(scribe)} 317 * </p> 318 * @param scribe the scribe to register 319 */ 320 public void registerScribe(VCardPropertyScribe<? extends VCardProperty> scribe) { 321 index.register(scribe); 322 } 323 324 /** 325 * Gets the scribe index. 326 * @return the scribe index 327 */ 328 public ScribeIndex getScribeIndex() { 329 return index; 330 } 331 332 /** 333 * Sets the scribe index. 334 * @param index the scribe index 335 */ 336 public void setScribeIndex(ScribeIndex index) { 337 this.index = index; 338 } 339 340 /** 341 * Gets the XML document that was generated. 342 * @return the XML document 343 */ 344 public Document getDocument() { 345 return document; 346 } 347 348 /** 349 * Parses all the {@link VCard} objects from the xCard document. 350 * @return the vCard objects 351 */ 352 public List<VCard> parseAll() { 353 parseWarnings.clear(); 354 355 if (root == null) { 356 return Collections.emptyList(); 357 } 358 359 List<VCard> vcards = new ArrayList<VCard>(); 360 for (Element vcardElement : getVCardElements()) { 361 List<String> warnings = new ArrayList<String>(); 362 parseWarnings.add(warnings); 363 VCard vcard = parseVCardElement(vcardElement, warnings); 364 vcards.add(vcard); 365 } 366 367 return vcards; 368 } 369 370 /** 371 * Parses the first the {@link VCard} object from the xCard document. 372 * @return the vCard object 373 */ 374 public VCard parseFirst() { 375 parseWarnings.clear(); 376 377 if (root == null) { 378 return null; 379 } 380 381 List<Element> vcardElements = getVCardElements(); 382 if (vcardElements.isEmpty()) { 383 return null; 384 } 385 386 List<String> warnings = new ArrayList<String>(); 387 parseWarnings.add(warnings); 388 return parseVCardElement(vcardElements.get(0), warnings); 389 } 390 391 private VCard parseVCardElement(Element vcardElement, List<String> warnings) { 392 VCard vcard = new VCard(); 393 vcard.setVersion(version4); 394 395 String ns = version4.getXmlNamespace(); 396 List<Element> children = XmlUtils.toElementList(vcardElement.getChildNodes()); 397 for (Element child : children) { 398 if ("group".equals(child.getLocalName()) && ns.equals(child.getNamespaceURI())) { 399 String group = child.getAttribute("name"); 400 if (group.length() == 0) { 401 group = null; 402 } 403 List<Element> propElements = XmlUtils.toElementList(child.getChildNodes()); 404 for (Element propElement : propElements) { 405 parseAndAddElement(propElement, group, vcard, warnings); 406 } 407 } else { 408 parseAndAddElement(child, null, vcard, warnings); 409 } 410 } 411 412 return vcard; 413 } 414 415 /** 416 * Parses a property element from the XML document and adds the property to 417 * the vCard. 418 * @param element the element to parse 419 * @param group the group name or null if the property does not belong to a 420 * group 421 * @param vcard the vCard object 422 * @param warningsBuf the list to add the warnings to 423 */ 424 private void parseAndAddElement(Element element, String group, VCard vcard, List<String> warnings) { 425 VCardParameters parameters = parseSubTypes(element); 426 427 VCardProperty property; 428 String propertyName = element.getLocalName(); 429 String ns = element.getNamespaceURI(); 430 VCardPropertyScribe<? extends VCardProperty> scribe = index.getPropertyScribe(new QName(ns, propertyName)); 431 try { 432 Result<? extends VCardProperty> result = scribe.parseXml(element, parameters); 433 434 property = result.getProperty(); 435 property.setGroup(group); 436 437 for (String warning : result.getWarnings()) { 438 addWarning(propertyName, warning, warnings); 439 } 440 } catch (SkipMeException e) { 441 addWarning(propertyName, warnings, 22, e.getMessage()); 442 return; 443 } catch (CannotParseException e) { 444 String xml = XmlUtils.toString(element); 445 addWarning(propertyName, warnings, 33, xml, e.getMessage()); 446 447 scribe = index.getPropertyScribe(Xml.class); 448 Result<? extends VCardProperty> result = scribe.parseXml(element, parameters); 449 property = result.getProperty(); 450 property.setGroup(group); 451 } catch (EmbeddedVCardException e) { 452 addWarning(propertyName, warnings, 34); 453 return; 454 } 455 456 vcard.addProperty(property); 457 } 458 459 /** 460 * Parses the property parameters (aka "sub types"). 461 * @param element the property's XML element 462 * @return the parsed parameters 463 */ 464 private VCardParameters parseSubTypes(Element element) { 465 VCardParameters subTypes = new VCardParameters(); 466 467 List<Element> parametersElements = XmlUtils.toElementList(element.getElementsByTagNameNS(version4.getXmlNamespace(), "parameters")); 468 for (Element parametersElement : parametersElements) { // foreach "<parameters>" element (there should only be 1 though) 469 List<Element> paramElements = XmlUtils.toElementList(parametersElement.getChildNodes()); 470 for (Element paramElement : paramElements) { 471 String name = paramElement.getLocalName().toUpperCase(); 472 List<Element> valueElements = XmlUtils.toElementList(paramElement.getChildNodes()); 473 if (valueElements.isEmpty()) { 474 String value = paramElement.getTextContent(); 475 subTypes.put(name, value); 476 } else { 477 for (Element valueElement : valueElements) { 478 String value = valueElement.getTextContent(); 479 subTypes.put(name, value); 480 } 481 } 482 } 483 } 484 485 return subTypes; 486 } 487 488 /** 489 * Writes the XML document to a string without pretty-printing it. 490 * @return the XML string 491 */ 492 public String write() { 493 return write(-1); 494 } 495 496 /** 497 * Writes the XML document to a string and pretty-prints it. 498 * @param indent the number of indent spaces to use for pretty-printing 499 * @return the XML string 500 */ 501 public String write(int indent) { 502 StringWriter sw = new StringWriter(); 503 try { 504 write(sw, indent); 505 } catch (TransformerException e) { 506 //writing to string 507 } 508 return sw.toString(); 509 } 510 511 /** 512 * Writes the XML document to an output stream without pretty-printing it. 513 * @param out the output stream 514 * @throws TransformerException if there's a problem writing to the output 515 * stream 516 */ 517 public void write(OutputStream out) throws TransformerException { 518 write(out, -1); 519 } 520 521 /** 522 * Writes the XML document to an output stream and pretty-prints it. 523 * @param out the output stream 524 * @param indent the number of indent spaces to use for pretty-printing 525 * @throws TransformerException if there's a problem writing to the output 526 * stream 527 */ 528 public void write(OutputStream out, int indent) throws TransformerException { 529 write(utf8Writer(out), indent); 530 } 531 532 /** 533 * Writes the XML document to a file without pretty-printing it. 534 * @param file the file 535 * @throws TransformerException if there's a problem writing to the file 536 * @throws IOException if there's a problem writing to the file 537 */ 538 public void write(File file) throws TransformerException, IOException { 539 write(file, -1); 540 } 541 542 /** 543 * Writes the XML document to a file and pretty-prints it. 544 * @param file the file stream 545 * @param indent the number of indent spaces to use for pretty-printing 546 * @throws TransformerException if there's a problem writing to the file 547 * @throws IOException if there's a problem writing to the file 548 */ 549 public void write(File file, int indent) throws TransformerException, IOException { 550 Writer writer = utf8Writer(file); 551 try { 552 write(writer, indent); 553 } finally { 554 IOUtils.closeQuietly(writer); 555 } 556 } 557 558 /** 559 * Writes the XML document to a writer without pretty-printing it. 560 * @param writer the writer 561 * @throws TransformerException if there's a problem writing to the writer 562 */ 563 public void write(Writer writer) throws TransformerException { 564 write(writer, -1); 565 } 566 567 /** 568 * Writes the XML document to a writer and pretty-prints it. 569 * @param writer the writer 570 * @param indent the number of indent spaces to use for pretty-printing 571 * @throws TransformerException if there's a problem writing to the writer 572 */ 573 public void write(Writer writer, int indent) throws TransformerException { 574 Map<String, String> properties = new HashMap<String, String>(); 575 if (indent >= 0) { 576 properties.put(OutputKeys.INDENT, "yes"); 577 properties.put("{http://xml.apache.org/xslt}indent-amount", indent + ""); 578 } 579 XmlUtils.toWriter(document, writer, properties); 580 } 581 582 /** 583 * Adds a vCard to the XML document. 584 * @param vcard the vCard to add 585 * @throws IllegalArgumentException if a scribe hasn't been registered for a 586 * custom property class (see: {@link #registerScribe}) 587 */ 588 public void add(VCard vcard) { 589 ListMultimap<String, VCardProperty> typesToAdd = new ListMultimap<String, VCardProperty>(); //group the types by group name (null = no group name) 590 591 for (VCardProperty type : vcard) { 592 if (addProdId && type instanceof ProductId) { 593 //do not add the PRODID in the vCard if "addProdId" is true 594 continue; 595 } 596 597 if (versionStrict && !type.getSupportedVersions().contains(version4)) { 598 //do not add the property to the vCard if it is not supported by the target version 599 continue; 600 } 601 602 //check for scribes before writing anything to the stream 603 if (index.getPropertyScribe(type) == null) { 604 throw new IllegalArgumentException("No scribe found for property class \"" + type.getClass().getName() + "\"."); 605 } 606 607 typesToAdd.put(type.getGroup(), type); 608 } 609 610 //add an extended type saying it was generated by this library 611 if (addProdId) { 612 ProductId prodId = new ProductId("ez-vcard " + Ezvcard.VERSION); 613 typesToAdd.put(prodId.getGroup(), prodId); 614 } 615 616 //marshal each type object 617 Element vcardElement = createElement("vcard"); 618 for (String groupName : typesToAdd.keySet()) { 619 Element parent; 620 if (groupName != null) { 621 Element groupElement = createElement("group"); 622 groupElement.setAttribute("name", groupName); 623 vcardElement.appendChild(groupElement); 624 parent = groupElement; 625 } else { 626 parent = vcardElement; 627 } 628 629 for (VCardProperty type : typesToAdd.get(groupName)) { 630 try { 631 Element typeElement = marshalType(type, vcard); 632 parent.appendChild(typeElement); 633 } catch (SkipMeException e) { 634 //skip property 635 } catch (EmbeddedVCardException e) { 636 //skip property 637 } 638 } 639 } 640 root.appendChild(vcardElement); 641 } 642 643 /** 644 * Marshals a type object to an XML element. 645 * @param type the type object to marshal 646 * @param vcard the vcard the type belongs to 647 * @return the XML element 648 */ 649 @SuppressWarnings({ "rawtypes", "unchecked" }) 650 private Element marshalType(VCardProperty type, VCard vcard) { 651 VCardPropertyScribe scribe = index.getPropertyScribe(type); 652 if (scribe == null) { 653 throw new IllegalArgumentException("No marshaller found for property class \"" + type.getClass().getName() + "\"."); 654 } 655 656 VCardParameters parameters = scribe.prepareParameters(type, version4, vcard); 657 658 QName qname = scribe.getQName(); 659 Element typeElement = createElement(qname.getLocalPart(), qname.getNamespaceURI()); 660 661 //marshal the sub types 662 if (!parameters.isEmpty()) { 663 Element parametersElement = marshalSubTypes(parameters); 664 typeElement.appendChild(parametersElement); 665 } 666 667 //marshal the value 668 scribe.writeXml(type, typeElement); 669 670 return typeElement; 671 } 672 673 private Element marshalSubTypes(VCardParameters parameters) { 674 Element parametersElement = createElement("parameters"); 675 676 for (Map.Entry<String, List<String>> param : parameters) { 677 String parameterName = param.getKey().toLowerCase(); 678 Element parameterElement = createElement(parameterName); 679 680 for (String paramValue : param.getValue()) { 681 VCardDataType dataType = parameterDataTypes.get(parameterName); 682 String dataTypeElementName = (dataType == null) ? "unknown" : dataType.getName().toLowerCase(); 683 Element dataTypeElement = createElement(dataTypeElementName); 684 dataTypeElement.setTextContent(paramValue); 685 parameterElement.appendChild(dataTypeElement); 686 } 687 688 parametersElement.appendChild(parameterElement); 689 } 690 691 return parametersElement; 692 } 693 694 private List<Element> getVCardElements() { 695 return getChildElements(root, "vcard"); 696 } 697 698 private List<Element> getChildElements(Element parent, String localName) { 699 List<Element> elements = new ArrayList<Element>(); 700 for (Element child : XmlUtils.toElementList(parent.getChildNodes())) { 701 if (localName.equals(child.getLocalName()) && version4.getXmlNamespace().equals(child.getNamespaceURI())) { 702 elements.add(child); 703 } 704 } 705 return elements; 706 } 707 708 /** 709 * Creates a new XML element. 710 * @param name the name of the XML element 711 * @return the new XML element 712 */ 713 private Element createElement(String name) { 714 return createElement(name, version4.getXmlNamespace()); 715 } 716 717 /** 718 * Creates a new XML element. 719 * @param name the name of the XML element 720 * @param ns the namespace of the XML element 721 * @return the new XML element 722 */ 723 private Element createElement(String name, String ns) { 724 return document.createElementNS(ns, name); 725 } 726 727 private void addWarning(String propertyName, List<String> warnings, int code, Object... args) { 728 String message = Messages.INSTANCE.getParseMessage(code, args); 729 addWarning(propertyName, message, warnings); 730 } 731 732 private void addWarning(String propertyName, String message, List<String> warnings) { 733 String warning = Messages.INSTANCE.getParseMessage(35, propertyName, message); 734 warnings.add(warning); 735 } 736 }