001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.maven.doxia.sink.impl;
020
021import javax.swing.text.MutableAttributeSet;
022import javax.swing.text.html.HTML.Tag;
023
024import java.util.Objects;
025
026import org.apache.maven.doxia.markup.HtmlTag;
027import org.apache.maven.doxia.markup.XmlMarkup;
028
029/**
030 * An abstract <code>Sink</code> for xml markup syntax.
031 *
032 * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
033 * @since 1.0
034 */
035public abstract class AbstractXmlSink extends SinkAdapter implements XmlMarkup {
036    /** Default namespace prepended to all tags */
037    private String nameSpace;
038
039    private boolean firstTag = true;
040
041    private boolean insertNewline = true;
042
043    /**
044     * <p>Setter for the field <code>insertNewline</code>.</p>
045     *
046     * @param insertNewline a boolean.
047     */
048    public void setInsertNewline(boolean insertNewline) {
049        this.insertNewline = insertNewline;
050    }
051
052    /**
053     * Sets the default namespace that is prepended to all tags written by this sink.
054     *
055     * @param ns the default namespace.
056     * @since 1.1
057     */
058    public void setNameSpace(String ns) {
059        this.nameSpace = ns;
060    }
061
062    /**
063     * Return the default namespace that is prepended to all tags written by this sink.
064     *
065     * @return the current default namespace.
066     * @since 1.1
067     */
068    public String getNameSpace() {
069        return this.nameSpace;
070    }
071
072    /**
073     * Starts a Tag. For instance:
074     * <pre>
075     * &lt;tag&gt;
076     * </pre>
077     *
078     * @param t a non null tag
079     * @see #writeStartTag(HtmlTag, javax.swing.text.MutableAttributeSet)
080     * @deprecated use {@link #writeStartTag(HtmlTag)}; the {@code javax.swing} tag type goes in 2.3.0.
081     */
082    @Deprecated
083    protected void writeStartTag(Tag t) {
084        writeStartTag(t, null);
085    }
086
087    /**
088     * Starts a Tag with attributes. For instance:
089     * <pre>
090     * &lt;tag attName="attValue"&gt;
091     * </pre>
092     *
093     * @param t a non null tag.
094     * @param att a set of attributes. May be null.
095     * @see #writeStartTag(HtmlTag, javax.swing.text.MutableAttributeSet, boolean)
096     * @deprecated use {@link #writeStartTag(HtmlTag, javax.swing.text.MutableAttributeSet)}; the
097     *      {@code javax.swing} tag type goes in 2.3.0.
098     */
099    @Deprecated
100    protected void writeStartTag(Tag t, MutableAttributeSet att) {
101        writeStartTag(t, att, false);
102    }
103
104    /**
105     * Starts a Tag with attributes. For instance:
106     * <pre>
107     * &lt;tag attName="attValue"&gt;
108     * </pre>
109     *
110     * @param t a non null tag.
111     * @param att a set of attributes. May be null.
112     * @param isEmptyElement boolean to write as tag for an <a href="https://www.w3.org/TR/xml/#d0e2480">empty element</a>.
113     * @deprecated use {@link #writeStartTag(HtmlTag, javax.swing.text.MutableAttributeSet, boolean)}; the
114     *      {@code javax.swing} tag type goes in 2.3.0.
115     */
116    @Deprecated
117    protected void writeStartTag(Tag t, MutableAttributeSet att, boolean isEmptyElement) {
118        writeStartTag(toHtmlTag(t), att, isEmptyElement);
119    }
120
121    /**
122     * Starts a Tag. For instance:
123     * <pre>
124     * &lt;tag&gt;
125     * </pre>
126     *
127     * @param t a non null tag
128     * @see #writeStartTag(HtmlTag, javax.swing.text.MutableAttributeSet)
129     * @since 2.2.0
130     */
131    protected void writeStartTag(HtmlTag t) {
132        writeStartTag(t, null);
133    }
134
135    /**
136     * Starts a Tag with attributes. For instance:
137     * <pre>
138     * &lt;tag attName="attValue"&gt;
139     * </pre>
140     *
141     * @param t a non null tag.
142     * @param att a set of attributes. May be null.
143     * @see #writeStartTag(HtmlTag, javax.swing.text.MutableAttributeSet, boolean)
144     * @since 2.2.0
145     */
146    protected void writeStartTag(HtmlTag t, MutableAttributeSet att) {
147        writeStartTag(t, att, false);
148    }
149
150    /**
151     * Starts a Tag with attributes. For instance:
152     * <pre>
153     * &lt;tag attName="attValue"&gt;
154     * </pre>
155     *
156     * @param t a non null tag.
157     * @param att a set of attributes. May be null.
158     * @param isEmptyElement boolean to write as tag for an <a href="https://www.w3.org/TR/xml/#d0e2480">empty element</a>.
159     * @since 2.2.0
160     */
161    protected void writeStartTag(HtmlTag t, MutableAttributeSet att, boolean isEmptyElement) {
162        Objects.requireNonNull(t, "t cannot be null");
163
164        StringBuilder sb = new StringBuilder();
165
166        if (insertNewline && t.isBlock() && !firstTag) {
167            sb.append(EOL);
168        }
169        firstTag = false;
170
171        sb.append(LESS_THAN);
172
173        if (nameSpace != null) {
174            sb.append(nameSpace).append(':');
175        }
176
177        sb.append(t.toString());
178
179        sb.append(SinkUtils.getAttributeString(att));
180
181        if (isEmptyElement) {
182            sb.append(SPACE).append(SLASH);
183        }
184
185        sb.append(GREATER_THAN);
186
187        write(sb.toString());
188    }
189
190    /**
191     * Writes a system EOL.
192     *
193     * @since 1.1
194     */
195    protected void writeEOL() {
196        write(EOL);
197    }
198
199    /**
200     * Ends a Tag without writing an EOL. For instance: <pre>&lt;/tag&gt;</pre>.
201     *
202     * @param t a tag.
203     * @deprecated use {@link #writeEndTag(HtmlTag)}; the {@code javax.swing} tag type goes in 2.3.0.
204     */
205    @Deprecated
206    protected void writeEndTag(Tag t) {
207        writeEndTag(toHtmlTag(t));
208    }
209
210    /**
211     * Ends a Tag without writing an EOL. For instance: <pre>&lt;/tag&gt;</pre>.
212     *
213     * @param t a tag.
214     * @since 2.2.0
215     */
216    protected void writeEndTag(HtmlTag t) {
217        Objects.requireNonNull(t, "t cannot be null");
218
219        StringBuilder sb = new StringBuilder();
220        sb.append(LESS_THAN);
221        sb.append(SLASH);
222
223        if (nameSpace != null) {
224            sb.append(nameSpace).append(':');
225        }
226
227        sb.append(t.toString());
228        sb.append(GREATER_THAN);
229
230        write(sb.toString());
231    }
232
233    /**
234     * Starts a simple Tag. For instance:
235     * <pre>
236     * &lt;tag /&gt;
237     * </pre>
238     *
239     * @param t a non null tag
240     * @see #writeSimpleTag(HtmlTag, javax.swing.text.MutableAttributeSet)
241     * @deprecated use {@link #writeSimpleTag(HtmlTag)}; the {@code javax.swing} tag type goes in 2.3.0.
242     */
243    @Deprecated
244    protected void writeSimpleTag(Tag t) {
245        writeSimpleTag(t, null);
246    }
247
248    /**
249     * Starts a simple Tag with attributes. For instance:
250     * <pre>
251     * &lt;tag attName="attValue" /&gt;
252     * </pre>
253     *
254     * @param t a non null tag.
255     * @param att a set of attributes. May be null.
256     * @see #writeStartTag(HtmlTag, javax.swing.text.MutableAttributeSet, boolean)
257     * @deprecated use {@link #writeSimpleTag(HtmlTag, javax.swing.text.MutableAttributeSet)}; the
258     *      {@code javax.swing} tag type goes in 2.3.0.
259     */
260    @Deprecated
261    protected void writeSimpleTag(Tag t, MutableAttributeSet att) {
262        writeStartTag(t, att, true);
263    }
264
265    /**
266     * Starts a simple Tag. For instance:
267     * <pre>
268     * &lt;tag /&gt;
269     * </pre>
270     *
271     * @param t a non null tag
272     * @see #writeSimpleTag(HtmlTag, javax.swing.text.MutableAttributeSet)
273     * @since 2.2.0
274     */
275    protected void writeSimpleTag(HtmlTag t) {
276        writeSimpleTag(t, null);
277    }
278
279    /**
280     * Starts a simple Tag with attributes. For instance:
281     * <pre>
282     * &lt;tag attName="attValue" /&gt;
283     * </pre>
284     *
285     * @param t a non null tag.
286     * @param att a set of attributes. May be null.
287     * @see #writeStartTag(HtmlTag, javax.swing.text.MutableAttributeSet, boolean)
288     * @since 2.2.0
289     */
290    protected void writeSimpleTag(HtmlTag t, MutableAttributeSet att) {
291        writeStartTag(t, att, true);
292    }
293
294    /**
295     * Returns the Doxia tag of the same name, falling back to a tag that answers exactly as the
296     * given one does, so that a tag Doxia does not declare keeps its own block value.
297     */
298    private static HtmlTag toHtmlTag(Tag t) {
299        Objects.requireNonNull(t, "t cannot be null");
300
301        HtmlTag known = HtmlTag.valueOf(t.toString());
302
303        return known != null ? known : new HtmlTag(t.toString(), t.isBlock());
304    }
305
306    /**
307     * Write a text to the sink.
308     *
309     * @param text the given text to write
310     */
311    protected abstract void write(String text);
312}