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