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-2026, 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                /*
088                 * Xml properties are handled as a special case when writing xCard
089                 * documents, so this method should never get called (see
090                 * "XCardDocument" class)
091                 */
092                super._writeXml(property, element);
093        }
094
095        @Override
096        protected Xml _parseXml(XCardElement element, VCardParameters parameters, ParseContext context) {
097                Xml xml = new Xml(element.element());
098
099                //remove the <parameters> element
100                Element root = xml.getValue().getDocumentElement();
101
102                //@formatter:off
103                XmlUtils.toElementStream(root.getChildNodes())
104                        .filter(child -> "parameters".equals(child.getLocalName()))
105                        .filter(child -> VCardVersion.V4_0.getXmlNamespace().equals(child.getNamespaceURI()))
106                .forEach(root::removeChild);
107                //@formatter:on
108
109                return xml;
110        }
111
112        @Override
113        protected JCardValue _writeJson(Xml property) {
114                String xml = null;
115                Document value = property.getValue();
116                if (value != null) {
117                        xml = valueToString(value);
118                }
119
120                return JCardValue.single(xml);
121        }
122
123        @Override
124        protected Xml _parseJson(JCardValue value, VCardDataType dataType, VCardParameters parameters, ParseContext context) {
125                try {
126                        String xml = value.asSingle();
127                        return xml.isEmpty() ? new Xml((Document) null) : new Xml(xml);
128                } catch (SAXException e) {
129                        throw new CannotParseException(22);
130                }
131        }
132
133        private String valueToString(Document document) {
134                Map<String, String> props = new HashMap<>();
135                props.put(OutputKeys.OMIT_XML_DECLARATION, "yes");
136                return XmlUtils.toString(document, props);
137        }
138}