001/**
002 *
003 * Copyright 2014 Florian Schmaus
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.jivesoftware.smack.util;
018
019import java.util.Collection;
020
021import org.jivesoftware.smack.packet.Element;
022import org.jivesoftware.smack.packet.NamedElement;
023import org.jivesoftware.smack.packet.ExtensionElement;
024
025public class XmlStringBuilder implements Appendable, CharSequence {
026    public static final String RIGHT_ANGLE_BRACKET = Character.toString('>');
027
028    private final LazyStringBuilder sb;
029
030    public XmlStringBuilder() {
031        sb = new LazyStringBuilder();
032    }
033
034    public XmlStringBuilder(ExtensionElement pe) {
035        this();
036        prelude(pe);
037    }
038
039    public XmlStringBuilder(NamedElement e) {
040        this();
041        halfOpenElement(e.getElementName());
042    }
043
044    public XmlStringBuilder escapedElement(String name, String escapedContent) {
045        assert escapedContent != null;
046        openElement(name);
047        append(escapedContent);
048        closeElement(name);
049        return this;
050    }
051
052    /**
053     *
054     * @param name
055     * @param content
056     * @return the XmlStringBuilder
057     */
058    public XmlStringBuilder element(String name, String content) {
059        assert content != null;
060        openElement(name);
061        escape(content);
062        closeElement(name);
063        return this;
064    }
065
066    public XmlStringBuilder element(String name, Enum<?> content) {
067        assert content != null;
068        element(name, content.name());
069        return this;
070    }
071
072    public XmlStringBuilder element(Element element) {
073        assert element != null;
074        return append(element.toXML());
075    }
076
077    public XmlStringBuilder optElement(String name, String content) {
078        if (content != null) {
079            element(name, content);
080        }
081        return this;
082    }
083
084    public XmlStringBuilder optElement(Element element) {
085        if (element != null) {
086            append(element.toXML());
087        }
088        return this;
089    }
090
091    public XmlStringBuilder optElement(String name, Enum<?> content) {
092        if (content != null) {
093            element(name, content);
094        }
095        return this;
096    }
097
098    public XmlStringBuilder optIntElement(String name, int value) {
099        if (value >= 0) {
100            element(name, String.valueOf(value));
101        }
102        return this;
103    }
104
105    public XmlStringBuilder halfOpenElement(String name) {
106        assert(StringUtils.isNotEmpty(name));
107        sb.append('<').append(name);
108        return this;
109    }
110
111    public XmlStringBuilder halfOpenElement(NamedElement namedElement) {
112        return halfOpenElement(namedElement.getElementName());
113    }
114
115    public XmlStringBuilder openElement(String name) {
116        halfOpenElement(name).rightAngleBracket();
117        return this;
118    }
119
120    public XmlStringBuilder closeElement(String name) {
121        sb.append("</").append(name);
122        rightAngleBracket();
123        return this;
124    }
125
126    public XmlStringBuilder closeElement(NamedElement e) {
127        closeElement(e.getElementName());
128        return this;
129    }
130
131    public XmlStringBuilder closeEmptyElement() {
132        sb.append("/>");
133        return this;
134    }
135
136    /**
137     * Add a right angle bracket '>'
138     * 
139     * @return a reference to this object.
140     */
141    public XmlStringBuilder rightAngleBracket() {
142        sb.append(RIGHT_ANGLE_BRACKET);
143        return this;
144    }
145
146    /**
147     * 
148     * @return a reference to this object
149     * @deprecated use {@link #rightAngleBracket()} instead
150     */
151    @Deprecated
152    public XmlStringBuilder rightAngelBracket() {
153        return rightAngleBracket();
154    }
155
156    /**
157     * Does nothing if value is null.
158     *
159     * @param name
160     * @param value
161     * @return the XmlStringBuilder
162     */
163    public XmlStringBuilder attribute(String name, String value) {
164        assert value != null;
165        sb.append(' ').append(name).append("='");
166        escape(value);
167        sb.append('\'');
168        return this;
169    }
170
171    public XmlStringBuilder attribute(String name, Enum<?> value) {
172        assert value != null;
173        attribute(name, value.name());
174        return this;
175    }
176
177    public XmlStringBuilder attribute(String name, int value) {
178        assert name != null;
179        return attribute(name, String.valueOf(value));
180    }
181
182    public XmlStringBuilder optAttribute(String name, String value) {
183        if (value != null) {
184            attribute(name, value);
185        }
186        return this;
187    }
188
189    public XmlStringBuilder optAttribute(String name, Enum<?> value) {
190        if (value != null) {
191            attribute(name, value.toString());
192        }
193        return this;
194    }
195
196    /**
197     * Add the given attribute if value => 0
198     *
199     * @param name
200     * @param value
201     * @return a reference to this object
202     */
203    public XmlStringBuilder optIntAttribute(String name, int value) {
204        if (value >= 0) {
205            attribute(name, Integer.toString(value));
206        }
207        return this;
208    }
209
210    /**
211     * Add the given attribute if value not null and value => 0.
212     *
213     * @param name
214     * @param value
215     * @return a reference to this object
216     */
217    public XmlStringBuilder optLongAttribute(String name, Long value) {
218        if (value != null && value >= 0) {
219            attribute(name, Long.toString(value));
220        }
221        return this;
222    }
223
224    public XmlStringBuilder optBooleanAttribute(String name, boolean bool) {
225        if (bool) {
226            sb.append(' ').append(name).append("='true'");
227        }
228        return this;
229    }
230
231    public XmlStringBuilder xmlnsAttribute(String value) {
232        optAttribute("xmlns", value);
233        return this;
234    }
235
236    public XmlStringBuilder xmllangAttribute(String value) {
237        optAttribute("xml:lang", value);
238        return this;
239    }
240 
241    public XmlStringBuilder escape(String text) {
242        assert text != null;
243        sb.append(StringUtils.escapeForXML(text));
244        return this;
245    }
246
247    public XmlStringBuilder prelude(ExtensionElement pe) {
248        return prelude(pe.getElementName(), pe.getNamespace());
249    }
250
251    public XmlStringBuilder prelude(String elementName, String namespace) {
252        halfOpenElement(elementName);
253        xmlnsAttribute(namespace);
254        return this;
255    }
256
257    public XmlStringBuilder optAppend(CharSequence csq) {
258        if (csq != null) {
259            append(csq);
260        }
261        return this;
262    }
263
264    public XmlStringBuilder optAppend(Element element) {
265        if (element != null) {
266            append(element.toXML());
267        }
268        return this;
269    }
270
271    public XmlStringBuilder append(XmlStringBuilder xsb) {
272        assert xsb != null;
273        sb.append(xsb.sb);
274        return this;
275    }
276
277    public XmlStringBuilder append(Collection<? extends Element> elements) {
278        for (Element element : elements) {
279            append(element.toXML());
280        }
281        return this;
282    }
283
284    public XmlStringBuilder emptyElement(Enum<?> element) {
285        return emptyElement(element.name());
286    }
287
288    public XmlStringBuilder emptyElement(String element) {
289        halfOpenElement(element);
290        return closeEmptyElement();
291    }
292
293    public XmlStringBuilder condEmptyElement(boolean condition, String element) {
294        if (condition) {
295            emptyElement(element);
296        }
297        return this;
298    }
299
300    public XmlStringBuilder condAttribute(boolean condition, String name, String value) {
301        if (condition) {
302            attribute(name, value);
303        }
304        return this;
305    }
306
307    @Override
308    public XmlStringBuilder append(CharSequence csq) {
309        assert csq != null;
310        sb.append(csq);
311        return this;
312    }
313
314    @Override
315    public XmlStringBuilder append(CharSequence csq, int start, int end) {
316        assert csq != null;
317        sb.append(csq, start, end);
318        return this;
319    }
320
321    @Override
322    public XmlStringBuilder append(char c) {
323        sb.append(c);
324        return this;
325    }
326
327    @Override
328    public int length() {
329        return sb.length();
330    }
331
332    @Override
333    public char charAt(int index) {
334        return sb.charAt(index);
335    }
336
337    @Override
338    public CharSequence subSequence(int start, int end) {
339        return sb.subSequence(start, end);
340    }
341
342    @Override
343    public String toString() {
344        return sb.toString();
345    }
346
347    @Override
348    public boolean equals(Object other) {
349        if (!(other instanceof CharSequence)) {
350            return false;
351        }
352        CharSequence otherCharSequenceBuilder = (CharSequence) other;
353        return toString().equals(otherCharSequenceBuilder.toString());
354    }
355
356    @Override
357    public int hashCode() {
358        return toString().hashCode();
359    }
360}