001package biweekly.io.scribe.property;
002
003import java.util.HashMap;
004import java.util.List;
005import java.util.Map;
006
007import javax.xml.transform.OutputKeys;
008
009import org.w3c.dom.Document;
010import org.w3c.dom.Element;
011import org.xml.sax.SAXException;
012
013import biweekly.ICalDataType;
014import biweekly.Warning;
015import biweekly.io.CannotParseException;
016import biweekly.io.json.JCalValue;
017import biweekly.io.xml.XCalElement;
018import biweekly.io.xml.XCalNamespaceContext;
019import biweekly.parameter.ICalParameters;
020import biweekly.property.Xml;
021import 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 */
052public class XmlScribe extends ICalPropertyScribe<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 XmlScribe() {
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 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}