001package ezvcard.property;
002
003import java.util.LinkedHashMap;
004import java.util.List;
005import java.util.Map;
006
007import org.w3c.dom.Document;
008import org.w3c.dom.Element;
009import org.w3c.dom.Node;
010import org.xml.sax.SAXException;
011
012import ezvcard.SupportedVersions;
013import ezvcard.VCard;
014import ezvcard.VCardVersion;
015import ezvcard.ValidationWarning;
016import ezvcard.util.XmlUtils;
017
018/*
019 Copyright (c) 2012-2023, Michael Angstadt
020 All rights reserved.
021
022 Redistribution and use in source and binary forms, with or without
023 modification, are permitted provided that the following conditions are met: 
024
025 1. Redistributions of source code must retain the above copyright notice, this
026 list of conditions and the following disclaimer. 
027 2. Redistributions in binary form must reproduce the above copyright notice,
028 this list of conditions and the following disclaimer in the documentation
029 and/or other materials provided with the distribution. 
030
031 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
032 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
033 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
034 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
035 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
036 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
037 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
038 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
039 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
040 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
041
042 The views and conclusions contained in the software and documentation are those
043 of the authors and should not be interpreted as representing official policies, 
044 either expressed or implied, of the FreeBSD Project.
045 */
046
047/**
048 * <p>
049 * Contains an XML element that was not recognized when parsing an xCard
050 * (XML-formatted vCard).
051 * </p>
052 * 
053 * <p>
054 * <b>Code sample</b>
055 * </p>
056 * 
057 * <pre class="brush:java">
058 * VCard vcard = new VCard();
059 * 
060 * Xml xml = new Xml("&lt;b&gt;Some xml&lt;/b&gt;");
061 * vcard.addXml(xml);
062 * </pre>
063 * 
064 * <p>
065 * <b>Property name:</b> {@code XML}
066 * </p>
067 * <p>
068 * <b>Supported versions:</b> {@code 4.0}
069 * </p>
070 * @author Michael Angstadt
071 * @see <a href="http://tools.ietf.org/html/rfc6350#page-27">RFC 6350 p.27</a>
072 */
073/*
074 * Note: This class does not extend SimpleProperty because of issues
075 * implementing "equals". SimpleProperty's "equals" method calls the "equals"
076 * method on the "value" field. However, equals method for the "Document" class
077 * does not check for true equality.
078 */
079@SupportedVersions(VCardVersion.V4_0)
080public class Xml extends VCardProperty implements HasAltId {
081        private Document value;
082
083        /**
084         * Creates an XML property.
085         * @param xml the XML to use as the property's value
086         * @throws SAXException if the XML cannot be parsed
087         */
088        public Xml(String xml) throws SAXException {
089                this((xml == null) ? null : XmlUtils.toDocument(xml));
090        }
091
092        /**
093         * Creates an XML property.
094         * @param element the XML element to use as the property's value (the
095         * element is imported into an empty {@link Document} object)
096         */
097        public Xml(Element element) {
098                this((element == null) ? null : detachElement(element));
099        }
100
101        private static Document detachElement(Element element) {
102                Document document = XmlUtils.createDocument();
103                Node imported = document.importNode(element, true);
104                document.appendChild(imported);
105                return document;
106        }
107
108        /**
109         * Creates an XML property.
110         * @param document the XML document to use as the property's value
111         */
112        public Xml(Document document) {
113                this.value = document;
114        }
115
116        /**
117         * Copy constructor.
118         * @param original the property to make a copy of
119         */
120        public Xml(Xml original) {
121                super(original);
122                if (original.value != null) {
123                        Element root = original.value.getDocumentElement();
124                        value = (root == null) ? XmlUtils.createDocument() : detachElement(root);
125                }
126        }
127
128        /**
129         * Gets the value of this property.
130         * @return the value or null if not set
131         */
132        public Document getValue() {
133                return value;
134        }
135
136        /**
137         * Sets the value of this property.
138         * @param value the value
139         */
140        public void setValue(Document value) {
141                this.value = value;
142        }
143
144        //@Override
145        public String getAltId() {
146                return parameters.getAltId();
147        }
148
149        //@Override
150        public void setAltId(String altId) {
151                parameters.setAltId(altId);
152        }
153
154        @Override
155        protected void _validate(List<ValidationWarning> warnings, VCardVersion version, VCard vcard) {
156                if (value == null) {
157                        warnings.add(new ValidationWarning(8));
158                }
159        }
160
161        @Override
162        protected Map<String, Object> toStringValues() {
163                Map<String, Object> values = new LinkedHashMap<>();
164                values.put("value", (value == null) ? "null" : XmlUtils.toString(value));
165                return values;
166        }
167
168        @Override
169        public Xml copy() {
170                return new Xml(this);
171        }
172
173        @Override
174        public int hashCode() {
175                final int prime = 31;
176                int result = super.hashCode();
177                result = prime * result + ((value == null) ? 0 : XmlUtils.toString(value).hashCode());
178                return result;
179        }
180
181        @Override
182        public boolean equals(Object obj) {
183                if (this == obj) return true;
184                if (!super.equals(obj)) return false;
185                Xml other = (Xml) obj;
186                if (value == null) {
187                        if (other.value != null) return false;
188                } else {
189                        if (other.value == null) return false;
190                        if (!XmlUtils.toString(value).equals(XmlUtils.toString(other.value))) return false;
191                }
192                return true;
193        }
194}