001 package biweekly.property.marshaller;
002
003 import java.util.HashMap;
004 import java.util.List;
005 import java.util.Map;
006
007 import javax.xml.transform.OutputKeys;
008
009 import org.w3c.dom.Document;
010 import org.w3c.dom.Element;
011 import org.xml.sax.SAXException;
012
013 import biweekly.ICalDataType;
014 import biweekly.io.CannotParseException;
015 import biweekly.io.json.JCalValue;
016 import biweekly.io.xml.XCalElement;
017 import biweekly.io.xml.XCalNamespaceContext;
018 import biweekly.parameter.ICalParameters;
019 import biweekly.property.Xml;
020 import biweekly.util.XmlUtils;
021
022 /*
023 Copyright (c) 2013, Michael Angstadt
024 All rights reserved.
025
026 Redistribution and use in source and binary forms, with or without
027 modification, are permitted provided that the following conditions are met:
028
029 1. Redistributions of source code must retain the above copyright notice, this
030 list of conditions and the following disclaimer.
031 2. Redistributions in binary form must reproduce the above copyright notice,
032 this list of conditions and the following disclaimer in the documentation
033 and/or other materials provided with the distribution.
034
035 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
036 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
037 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
039 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
040 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
041 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
042 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
043 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
044 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
045 */
046
047 /**
048 * Marshals {@link Xml} properties.
049 * @author Michael Angstadt
050 */
051 public class XmlMarshaller extends ICalPropertyMarshaller<Xml> {
052 //TODO on writing to plain text: convert to base64 if the string contains values that are illegal within a plain text value (p.17)
053 public XmlMarshaller() {
054 super(Xml.class, "XML", ICalDataType.TEXT);
055 }
056
057 @Override
058 protected String _writeText(Xml property) {
059 Document value = property.getValue();
060 if (value != null) {
061 String xml = valueToString(value);
062 return escape(xml);
063 }
064
065 return "";
066 }
067
068 @Override
069 protected Xml _parseText(String value, ICalDataType dataType, ICalParameters parameters, List<String> warnings) {
070 value = unescape(value);
071 try {
072 return new Xml(value);
073 } catch (SAXException e) {
074 throw new CannotParseException("Cannot parse value as XML: " + value);
075 }
076 }
077
078 @Override
079 protected void _writeXml(Xml property, XCalElement element) {
080 super._writeXml(property, element);
081 //Xml properties are handled as a special case when writing xCal documents, so this method should never get called (see: "XCalDocument" class)
082 }
083
084 @Override
085 protected Xml _parseXml(XCalElement element, ICalParameters parameters, List<String> warnings) {
086 Xml xml = new Xml(element.getElement());
087
088 //remove the <parameters> element
089 Element root = XmlUtils.getRootElement(xml.getValue());
090 for (Element child : XmlUtils.toElementList(root.getChildNodes())) {
091 if ("parameters".equals(child.getLocalName()) && XCalNamespaceContext.XCAL_NS.equals(child.getNamespaceURI())) {
092 root.removeChild(child);
093 }
094 }
095
096 return xml;
097 }
098
099 @Override
100 protected JCalValue _writeJson(Xml property) {
101 Document value = property.getValue();
102 if (value != null) {
103 String xml = valueToString(value);
104 return JCalValue.single(xml);
105 }
106
107 return JCalValue.single("");
108 }
109
110 @Override
111 protected Xml _parseJson(JCalValue value, ICalDataType dataType, ICalParameters parameters, List<String> warnings) {
112 try {
113 String xml = value.asSingle();
114 return (xml == null) ? new Xml((Document) null) : new Xml(xml);
115 } catch (SAXException e) {
116 throw new CannotParseException("Cannot parse value as XML: " + value);
117 }
118 }
119
120 private String valueToString(Document document) {
121 Map<String, String> props = new HashMap<String, String>();
122 props.put(OutputKeys.OMIT_XML_DECLARATION, "yes");
123 return XmlUtils.toString(document, props);
124 }
125 }