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.marshall.html; 018 019import static org.apache.juneau.commons.utils.StringUtils.*; 020 021import java.io.*; 022 023import org.apache.juneau.marshall.*; 024import org.apache.juneau.marshall.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/HtmlSupport">HTML</a> 031 032 * </ul> 033 */ 034@SuppressWarnings({ 035 "java:S119", // 'SELF' (CRTP self-type) is intentional and clearer than a single-letter name. 036 "resource" // Writer resource managed by calling code 037}) 038public abstract class HtmlWriter<SELF extends HtmlWriter<SELF>> extends XmlWriter<SELF> { 039 040 /** 041 * Copy constructor. 042 * 043 * @param w Writer being copied. 044 */ 045 protected HtmlWriter(HtmlWriter<?> w) { 046 super(w); 047 } 048 049 /** 050 * Constructor. 051 * 052 * @param out The writer being wrapped. 053 * @param useWhitespace If <jk>true</jk>, tabs will be used in output. 054 * @param maxIndent The maximum indentation level. 055 * @param trimStrings If <jk>true</jk>, strings should be trimmed before they're serialized. 056 * @param quoteChar The quote character to use (i.e. <js>'\''</js> or <js>'"'</js>) 057 * @param uriResolver The URI resolver for resolving URIs to absolute or root-relative form. 058 */ 059 protected HtmlWriter(Writer out, boolean useWhitespace, int maxIndent, boolean trimStrings, char quoteChar, UriResolver uriResolver) { 060 super(out, useWhitespace, maxIndent, trimStrings, quoteChar, uriResolver, false, null); 061 } 062 063 @Override /* Overridden from SerializerWriter */ 064 public SELF cr(int depth) { 065 if (depth > 0) 066 super.cr(depth); 067 return self(); 068 } 069 070 @Override /* Overridden from SerializerWriter */ 071 public SELF cre(int depth) { 072 if (depth > 0) 073 super.cre(depth); 074 return self(); 075 } 076 077 @Override /* Overridden from XmlSerializerWriter */ 078 @SuppressWarnings({ 079 "java:S3776" // Cognitive complexity acceptable for text serialization with whitespace handling 080 }) 081 public SELF text(Object o, boolean preserveWhitespace) { 082 083 if (o == null) { 084 append("<null/>"); 085 return self(); 086 } 087 var s = o.toString(); 088 if (s.isEmpty()) { 089 append("<sp/>"); 090 return self(); 091 } 092 093 for (var i = 0; i < s.length(); i++) { 094 var test = s.charAt(i); 095 if (test == '&') 096 append("&"); 097 else if (test == '<') 098 append("<"); 099 else if (test == '>') 100 append(">"); 101 else if (test == '\n') 102 append(preserveWhitespace ? "\n" : "<br/>"); 103 else if (test == '\f') // XML 1.0 doesn't support form feeds or backslashes, so we have to invent something. 104 append(preserveWhitespace ? "\f" : "<ff/>"); 105 else if (test == '\b') 106 append(preserveWhitespace ? "\b" : "<bs/>"); 107 else if (test == '\t') 108 append(preserveWhitespace ? "\t" : "<sp> </sp>"); 109 else if ((i == 0 || i == s.length() - 1) && Character.isWhitespace(test)) { 110 if (preserveWhitespace) 111 w(test); 112 else if (test == ' ') 113 append("<sp> </sp>"); 114 else 115 append("<sp>&#x").append(toHex4(test)).append(";</sp>"); 116 } else if (Character.isISOControl(test)) 117 append("&#" + (int)test + ";"); 118 else 119 w(test); 120 } 121 122 return self(); 123 } 124}