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    }