001package biweekly.io.xml;
002
003import static biweekly.io.xml.XCalNamespaceContext.XCAL_NS;
004
005import java.util.ArrayList;
006import java.util.Collection;
007import java.util.List;
008
009import org.w3c.dom.Document;
010import org.w3c.dom.Element;
011
012import biweekly.ICalDataType;
013import biweekly.util.XmlUtils;
014
015/*
016 Copyright (c) 2013-2015, Michael Angstadt
017 All rights reserved.
018
019 Redistribution and use in source and binary forms, with or without
020 modification, are permitted provided that the following conditions are met: 
021
022 1. Redistributions of source code must retain the above copyright notice, this
023 list of conditions and the following disclaimer. 
024 2. Redistributions in binary form must reproduce the above copyright notice,
025 this list of conditions and the following disclaimer in the documentation
026 and/or other materials provided with the distribution. 
027
028 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
029 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
030 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
031 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
032 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
033 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
034 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
035 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
036 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
037 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
038 */
039
040/**
041 * Wraps xCal functionality around an XML {@link Element} object.
042 * @author Michael Angstadt
043 */
044public class XCalElement {
045        private final Element element;
046        private final Document document;
047
048        /**
049         * Creates a new xCal element.
050         * @param element the XML element to wrap
051         */
052        public XCalElement(Element element) {
053                this.element = element;
054                document = element.getOwnerDocument();
055        }
056
057        /**
058         * Gets the first value of the given data type.
059         * @param dataType the data type to look for or null for the "unknown" data
060         * type
061         * @return the value or null if not found
062         */
063        public String first(ICalDataType dataType) {
064                String dataTypeStr = toLocalName(dataType);
065                return first(dataTypeStr);
066        }
067
068        /**
069         * Gets the value of the first child element with the given name.
070         * @param localName the name of the element
071         * @return the element's text or null if not found
072         */
073        public String first(String localName) {
074                for (Element child : children()) {
075                        if (localName.equals(child.getLocalName()) && XCAL_NS.equals(child.getNamespaceURI())) {
076                                return child.getTextContent();
077                        }
078                }
079                return null;
080        }
081
082        /**
083         * Gets all the values of a given data type.
084         * @param dataType the data type to look for or null for the "unknown" data
085         * type
086         * @return the values
087         */
088        public List<String> all(ICalDataType dataType) {
089                String dataTypeStr = toLocalName(dataType);
090                return all(dataTypeStr);
091        }
092
093        /**
094         * Gets the values of all child elements that have the given name.
095         * @param localName the element name
096         * @return the values of the child elements
097         */
098        public List<String> all(String localName) {
099                List<String> childrenText = new ArrayList<String>();
100                for (Element child : children()) {
101                        if (localName.equals(child.getLocalName()) && XCAL_NS.equals(child.getNamespaceURI())) {
102                                String text = child.getTextContent();
103                                childrenText.add(text);
104                        }
105                }
106                return childrenText;
107        }
108
109        /**
110         * Adds a value.
111         * @param dataType the data type or null for the "unknown" data type
112         * @param value the value
113         * @return the created element
114         */
115        public Element append(ICalDataType dataType, String value) {
116                String dataTypeStr = toLocalName(dataType);
117                return append(dataTypeStr, value);
118        }
119
120        /**
121         * Adds a child element.
122         * @param name the name of the child element
123         * @param value the value of the child element.
124         * @return the created element
125         */
126        public Element append(String name, String value) {
127                Element child = document.createElementNS(XCAL_NS, name);
128                child.setTextContent(value);
129                element.appendChild(child);
130                return child;
131        }
132
133        /**
134         * Adds a child element.
135         * @param name the name of the child element
136         * @return the created element
137         */
138        public XCalElement append(String name) {
139                return new XCalElement(append(name, (String) null));
140        }
141
142        /**
143         * Adds an empty value.
144         * @param dataType the data type
145         * @return the created element
146         */
147        public XCalElement append(ICalDataType dataType) {
148                return append(dataType.getName().toLowerCase());
149        }
150
151        /**
152         * Adds multiple child elements, each with the same name.
153         * @param name the name for all the child elements
154         * @param values the values of each child element
155         * @return the created elements
156         */
157        public List<Element> append(String name, Collection<String> values) {
158                List<Element> elements = new ArrayList<Element>(values.size());
159                for (String value : values) {
160                        elements.add(append(name, value));
161                }
162                return elements;
163        }
164
165        /**
166         * Gets the owner document.
167         * @return the owner document
168         */
169        public Document document() {
170                return document;
171        }
172
173        /**
174         * Gets the wrapped XML element.
175         * @return the wrapped XML element
176         */
177        public Element getElement() {
178                return element;
179        }
180
181        /**
182         * Gets the child elements of the wrapped XML element.
183         * @return the child elements
184         */
185        private List<Element> children() {
186                return XmlUtils.toElementList(element.getChildNodes());
187        }
188
189        /**
190         * Gets all child elements with the given data type.
191         * @param dataType the data type
192         * @return the child elements
193         */
194        public List<XCalElement> children(ICalDataType dataType) {
195                String localName = dataType.getName().toLowerCase();
196                List<XCalElement> children = new ArrayList<XCalElement>();
197                for (Element child : children()) {
198                        if (localName.equals(child.getLocalName()) && XCAL_NS.equals(child.getNamespaceURI())) {
199                                children.add(new XCalElement(child));
200                        }
201                }
202                return children;
203        }
204
205        /**
206         * Gets the first child element with the given data type.
207         * @param dataType the data type
208         * @return the child element or null if not found
209         */
210        public XCalElement child(ICalDataType dataType) {
211                String localName = dataType.getName().toLowerCase();
212                for (Element child : children()) {
213                        if (localName.equals(child.getLocalName()) && XCAL_NS.equals(child.getNamespaceURI())) {
214                                return new XCalElement(child);
215                        }
216                }
217                return null;
218        }
219
220        private String toLocalName(ICalDataType dataType) {
221                return (dataType == null) ? "unknown" : dataType.getName().toLowerCase();
222        }
223}