001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  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.apache.juneau.html;
018
019import static org.apache.juneau.commons.utils.StringUtils.*;
020
021import java.io.*;
022
023import org.apache.juneau.*;
024import org.apache.juneau.xml.*;
025
026/**
027 * Specialized writer for serializing HTML.
028 *
029 * <h5 class='section'>See Also:</h5><ul>
030 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/HtmlBasics">HTML Basics</a>
031
032 * </ul>
033 */
034@SuppressWarnings("resource")
035public class HtmlWriter extends XmlWriter {
036
037   /**
038    * Copy constructor.
039    *
040    * @param w Writer being copied.
041    */
042   public HtmlWriter(HtmlWriter w) {
043      super(w);
044   }
045
046   /**
047    * Constructor.
048    *
049    * @param out The writer being wrapped.
050    * @param useWhitespace If <jk>true</jk>, tabs will be used in output.
051    * @param maxIndent The maximum indentation level.
052    * @param trimStrings If <jk>true</jk>, strings should be trimmed before they're serialized.
053    * @param quoteChar The quote character to use (i.e. <js>'\''</js> or <js>'"'</js>)
054    * @param uriResolver The URI resolver for resolving URIs to absolute or root-relative form.
055    */
056   public HtmlWriter(Writer out, boolean useWhitespace, int maxIndent, boolean trimStrings, char quoteChar, UriResolver uriResolver) {
057      super(out, useWhitespace, maxIndent, trimStrings, quoteChar, uriResolver, false, null);
058   }
059
060   @Override /* Overridden from SerializerWriter */
061   public HtmlWriter append(char c) {
062      super.append(c);
063      return this;
064   }
065
066   @Override /* Overridden from XmlWriter */
067   public HtmlWriter append(char[] value) {
068      super.append(value);
069      return this;
070   }
071
072   @Override /* Overridden from SerializerWriter */
073   public HtmlWriter append(int indent, char c) {
074      super.append(indent, c);
075      return this;
076   }
077
078   @Override /* Overridden from SerializerWriter */
079   public HtmlWriter append(int indent, String text) {
080      super.append(indent, text);
081      return this;
082   }
083
084   @Override /* Overridden from SerializerWriter */
085   public HtmlWriter append(Object text) {
086      super.append(text);
087      return this;
088   }
089
090   @Override /* Overridden from SerializerWriter */
091   public HtmlWriter append(String text) {
092      super.append(text);
093      return this;
094   }
095
096   @Override /* Overridden from XmlWriter */
097   public HtmlWriter appendIf(boolean flag, char value) {
098      super.appendIf(flag, value);
099      return this;
100   }
101
102   @Override /* Overridden from XmlWriter */
103   public HtmlWriter appendIf(boolean flag, String value) {
104      super.appendIf(flag, value);
105      return this;
106   }
107
108   @Override /* Overridden from SerializerWriter */
109   public HtmlWriter appendln(int indent, String text) {
110      super.appendln(indent, text);
111      return this;
112   }
113
114   @Override /* Overridden from SerializerWriter */
115   public HtmlWriter appendln(String text) {
116      super.appendln(text);
117      return this;
118   }
119
120   @Override /* Overridden from XmlWriter */
121   public HtmlWriter appendUri(Object value) {
122      super.appendUri(value);
123      return this;
124   }
125
126   @Override /* Overridden from XmlSerializerWriter */
127   public HtmlWriter attr(String name, Object value) {
128      super.attr(name, value);
129      return this;
130   }
131
132   @Override /* Overridden from XmlSerializerWriter */
133   public HtmlWriter attr(String name, Object value, boolean valNeedsEncoding) {
134      super.attr(null, name, value, valNeedsEncoding);
135      return this;
136   }
137
138   @Override /* Overridden from XmlSerializerWriter */
139   public HtmlWriter attr(String ns, String name, Object value) {
140      super.attr(ns, name, value);
141      return this;
142   }
143
144   @Override /* Overridden from XmlSerializerWriter */
145   public HtmlWriter attr(String ns, String name, Object value, boolean valNeedsEncoding) {
146      super.attr(ns, name, value, valNeedsEncoding);
147      return this;
148   }
149
150   @Override /* Overridden from XmlWriter */
151   public HtmlWriter attrUri(String name, Object value) {
152      super.attrUri(name, value);
153      return this;
154   }
155
156   @Override /* Overridden from XmlWriter */
157   public HtmlWriter ceTag() {
158      super.ceTag();
159      return this;
160   }
161
162   @Override /* Overridden from SerializerWriter */
163   public HtmlWriter cr(int depth) {
164      if (depth > 0)
165         super.cr(depth);
166      return this;
167   }
168
169   @Override /* Overridden from SerializerWriter */
170   public HtmlWriter cre(int depth) {
171      if (depth > 0)
172         super.cre(depth);
173      return this;
174   }
175
176   @Override /* Overridden from XmlWriter */
177   public HtmlWriter cTag() {
178      super.cTag();
179      return this;
180   }
181
182   @Override /* Overridden from XmlSerializerWriter */
183   public HtmlWriter eTag(int indent, String name) {
184      super.eTag(indent, name);
185      return this;
186   }
187
188   @Override /* Overridden from XmlSerializerWriter */
189   public HtmlWriter eTag(int indent, String ns, String name) {
190      super.eTag(indent, ns, name);
191      return this;
192   }
193
194   @Override /* Overridden from XmlSerializerWriter */
195   public HtmlWriter eTag(int indent, String ns, String name, boolean needsEncoding) {
196      super.eTag(indent, ns, name, needsEncoding);
197      return this;
198   }
199
200   @Override /* Overridden from XmlSerializerWriter */
201   public HtmlWriter eTag(String name) {
202      super.eTag(name);
203      return this;
204   }
205
206   @Override /* Overridden from XmlSerializerWriter */
207   public HtmlWriter eTag(String ns, String name) {
208      super.eTag(ns, name);
209      return this;
210   }
211
212   @Override /* Overridden from XmlSerializerWriter */
213   public HtmlWriter eTag(String ns, String name, boolean needsEncoding) {
214      super.eTag(ns, name, needsEncoding);
215      return this;
216   }
217
218   @Override /* Overridden from SerializerWriter */
219   public HtmlWriter i(int indent) {
220      super.i(indent);
221      return this;
222   }
223
224   @Override /* Overridden from XmlWriter */
225   public HtmlWriter ie(int indent) {
226      super.ie(indent);
227      return this;
228   }
229
230   @Override /* Overridden from SerializerWriter */
231   public HtmlWriter nl(int indent) {
232      super.nl(indent);
233      return this;
234   }
235
236   @Override /* Overridden from XmlWriter */
237   public HtmlWriter nlIf(boolean flag, int indent) {
238      super.nlIf(flag, indent);
239      return this;
240   }
241
242   @Override /* Overridden from XmlSerializerWriter */
243   public HtmlWriter oAttr(String ns, String name) {
244      super.oAttr(ns, name);
245      return this;
246   }
247
248   @Override /* Overridden from XmlSerializerWriter */
249   public HtmlWriter oTag(int indent, String name) {
250      super.oTag(indent, name);
251      return this;
252   }
253
254   @Override /* Overridden from XmlSerializerWriter */
255   public HtmlWriter oTag(int indent, String ns, String name) {
256      super.oTag(indent, ns, name);
257      return this;
258   }
259
260   @Override /* Overridden from XmlSerializerWriter */
261   public HtmlWriter oTag(int indent, String ns, String name, boolean needsEncoding) {
262      super.oTag(indent, ns, name, needsEncoding);
263      return this;
264   }
265
266   @Override /* Overridden from XmlSerializerWriter */
267   public HtmlWriter oTag(String name) {
268      super.oTag(name);
269      return this;
270   }
271
272   @Override /* Overridden from XmlSerializerWriter */
273   public HtmlWriter oTag(String ns, String name) {
274      super.oTag(ns, name);
275      return this;
276   }
277
278   @Override /* Overridden from XmlSerializerWriter */
279   public HtmlWriter oTag(String ns, String name, boolean needsEncoding) {
280      super.oTag(ns, name, needsEncoding);
281      return this;
282   }
283
284   @Override /* Overridden from SerializerWriter */
285   public HtmlWriter q() {
286      super.q();
287      return this;
288   }
289
290   @Override /* Overridden from SerializerWriter */
291   public HtmlWriter s() {
292      super.s();
293      return this;
294   }
295
296   @Override /* Overridden from XmlWriter */
297   public HtmlWriter sIf(boolean flag) {
298      super.sIf(flag);
299      return this;
300   }
301
302   @Override /* Overridden from XmlSerializerWriter */
303   public HtmlWriter sTag(int indent, String name) {
304      super.sTag(indent, name);
305      return this;
306   }
307
308   @Override /* Overridden from XmlSerializerWriter */
309   public HtmlWriter sTag(int indent, String ns, String name) {
310      super.sTag(indent, ns, name);
311      return this;
312   }
313
314   @Override /* Overridden from XmlSerializerWriter */
315   public HtmlWriter sTag(int indent, String ns, String name, boolean needsEncoding) {
316      super.sTag(indent, ns, name, needsEncoding);
317      return this;
318   }
319
320   @Override /* Overridden from XmlSerializerWriter */
321   public HtmlWriter sTag(String name) {
322      super.sTag(name);
323      return this;
324   }
325
326   @Override /* Overridden from XmlSerializerWriter */
327   public HtmlWriter sTag(String ns, String name) {
328      super.sTag(ns, name);
329      return this;
330   }
331
332   @Override /* Overridden from XmlSerializerWriter */
333   public HtmlWriter sTag(String ns, String name, boolean needsEncoding) {
334      super.sTag(ns, name, needsEncoding);
335      return this;
336   }
337
338   @Override /* Overridden from XmlSerializerWriter */
339   public HtmlWriter tag(int indent, String name) {
340      super.tag(indent, name);
341      return this;
342   }
343
344   @Override /* Overridden from XmlSerializerWriter */
345   public HtmlWriter tag(int indent, String ns, String name) {
346      super.tag(indent, ns, name);
347      return this;
348   }
349
350   @Override /* Overridden from XmlSerializerWriter */
351   public HtmlWriter tag(int indent, String ns, String name, boolean needsEncoding) {
352      super.tag(indent, ns, name, needsEncoding);
353      return this;
354   }
355
356   @Override /* Overridden from XmlSerializerWriter */
357   public HtmlWriter tag(String name) {
358      super.tag(name);
359      return this;
360   }
361
362   @Override /* Overridden from XmlSerializerWriter */
363   public HtmlWriter tag(String ns, String name) {
364      super.tag(ns, name);
365      return this;
366   }
367
368   @Override /* Overridden from XmlSerializerWriter */
369   public HtmlWriter tag(String ns, String name, boolean needsEncoding) {
370      super.tag(ns, name, needsEncoding);
371      return this;
372   }
373
374   @Override /* Overridden from XmlWriter */
375   public HtmlWriter text(Object value) {
376      super.text(value);
377      return this;
378   }
379
380   @Override /* Overridden from XmlSerializerWriter */
381   public HtmlWriter text(Object o, boolean preserveWhitespace) {
382
383      if (o == null) {
384         append("<null/>");
385         return this;
386      }
387      var s = o.toString();
388      if (s.isEmpty()) {
389         append("<sp/>");
390         return this;
391      }
392
393      for (var i = 0; i < s.length(); i++) {
394         var test = s.charAt(i);
395         if (test == '&')
396            append("&amp;");
397         else if (test == '<')
398            append("&lt;");
399         else if (test == '>')
400            append("&gt;");
401         else if (test == '\n')
402            append(preserveWhitespace ? "\n" : "<br/>");
403         else if (test == '\f')  // XML 1.0 doesn't support form feeds or backslashes, so we have to invent something.
404            append(preserveWhitespace ? "\f" : "<ff/>");
405         else if (test == '\b')
406            append(preserveWhitespace ? "\b" : "<bs/>");
407         else if (test == '\t')
408            append(preserveWhitespace ? "\t" : "<sp>&#x2003;</sp>");
409         else if ((i == 0 || i == s.length() - 1) && Character.isWhitespace(test)) {
410            if (preserveWhitespace)
411               w(test);
412            else if (test == ' ')
413               append("<sp> </sp>");
414            else
415               append("<sp>&#x").append(toHex4(test)).append(";</sp>");
416         } else if (Character.isISOControl(test))
417            append("&#" + (int)test + ";");
418         else
419            w(test);
420      }
421
422      return this;
423   }
424
425   @Override /* Overridden from XmlWriter */
426   public HtmlWriter textUri(Object value) {
427      super.textUri(value);
428      return this;
429   }
430
431   @Override /* Overridden from XmlWriter */
432   public HtmlWriter w(char c) {
433      super.w(c);
434      return this;
435   }
436
437   @Override /* Overridden from XmlWriter */
438   public HtmlWriter w(String s) {
439      super.w(s);
440      return this;
441   }
442}