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