001/* 002 * Copyright (c) 2010, 2022, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. Oracle designates this 008 * particular file as subject to the "Classpath" exception as provided 009 * by Oracle in the LICENSE file that accompanied this code. 010 * 011 * This code is distributed in the hope that it will be useful, but WITHOUT 012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 014 * version 2 for more details (a copy is included in the LICENSE file that 015 * accompanied this code). 016 * 017 * You should have received a copy of the GNU General Public License version 018 * 2 along with this work; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 020 * 021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 022 * or visit www.oracle.com if you need additional information or have any 023 * questions. 024 */ 025 026package org.jdrupes.mdoclet.internal.doclets.formats.html.markup; 027 028import java.io.IOException; 029import java.io.Writer; 030 031import org.jdrupes.mdoclet.internal.doclets.toolkit.Content; 032 033/** 034 * Class for generating raw HTML content to be added to HTML pages of javadoc output. 035 */ 036public class RawHtml extends Content { 037 038 protected final String rawHtmlContent; 039 040 /** 041 * Creates HTML for an arbitrary string of HTML. 042 * The string is accepted as-is and is not validated in any way. 043 * It should be syntactically well-formed and contain matching {@code <} and {@code >}, 044 * and matching quotes for attributes. 045 * 046 * @param rawHtml the string 047 * @return the HTML 048 */ 049 public static RawHtml of(CharSequence rawHtml) { 050 return new RawHtml(rawHtml) { 051 @Override 052 public int charCount() { 053 return charCount(rawHtmlContent); 054 } 055 }; 056 } 057 058 /** 059 * Creates HTML for the start of an element. 060 * 061 * @param name the name of the element 062 * @param attrs content containing any attributes 063 * @param selfClosing whether this is a self-closing element. 064 * @return the HTML 065 */ 066 public static RawHtml startElement(CharSequence name, Content attrs, 067 boolean selfClosing) { 068 StringBuilder sb = new StringBuilder("<" + name); 069 if (!attrs.isEmpty()) { 070 sb.append(" "); 071 sb.append(attrs); 072 } 073 sb.append(selfClosing ? "/>" : ">"); 074 return new RawHtml(sb); 075 } 076 077 /** 078 * Creates HTML for the end of an element. 079 * 080 * @param name the name of the element 081 * @return the HTML 082 */ 083 public static RawHtml endElement(CharSequence name) { 084 return new RawHtml("</" + name + ">"); 085 } 086 087 /** 088 * Creates HTML for an HTML comment. 089 * 090 * The body will be enclosed in {@code <!--} and {@code -->} if it does not 091 * already begin and end with those sequences. 092 * 093 * @param body the body of the comment 094 * 095 * @return the HTML 096 */ 097 public static RawHtml comment(String body) { 098 return section("<!--", body, "-->"); 099 } 100 101 /** 102 * Creates HTML for an HTML CDATA section. 103 * 104 * The body will be enclosed in {@code <![CDATA]} and {@code ]]>} if it does not 105 * already begin and end with those sequences. 106 * 107 * @param body the body of the CDATA section 108 * 109 * @return the HTML 110 */ 111 public static RawHtml cdata(String body) { 112 return section("<![CDATA[", body, "]]>"); 113 } 114 115 private static RawHtml section(String prefix, String body, String suffix) { 116 return new RawHtml( 117 body.startsWith(prefix) && body.endsWith(suffix) ? body 118 : prefix + body + suffix); 119 } 120 121 /** 122 * Constructor to construct a RawHtml object. 123 * 124 * @param rawHtml raw HTML text to be added 125 */ 126 private RawHtml(CharSequence rawHtml) { 127 assert Text.checkNewlines(rawHtml); 128 rawHtmlContent = rawHtml.toString(); 129 } 130 131 @Override 132 public boolean isEmpty() { 133 return rawHtmlContent.isEmpty(); 134 } 135 136 @Override 137 public String toString() { 138 return rawHtmlContent; 139 } 140 141 private enum State { 142 TEXT, ENTITY, TAG, STRING 143 } 144 145 protected static int charCount(CharSequence htmlText) { 146 State state = State.TEXT; 147 int count = 0; 148 for (int i = 0; i < htmlText.length(); i++) { 149 char c = htmlText.charAt(i); 150 switch (state) { 151 case TEXT: 152 switch (c) { 153 case '<': 154 state = State.TAG; 155 break; 156 case '&': 157 state = State.ENTITY; 158 count++; 159 break; 160 default: 161 count++; 162 } 163 break; 164 165 case ENTITY: 166 if (!Character.isLetterOrDigit(c)) 167 state = State.TEXT; 168 break; 169 170 case TAG: 171 switch (c) { 172 case '"': 173 state = State.STRING; 174 break; 175 case '>': 176 state = State.TEXT; 177 break; 178 } 179 break; 180 181 case STRING: 182 switch (c) { 183 case '"': 184 state = State.TAG; 185 break; 186 } 187 } 188 } 189 return count; 190 } 191 192 @Override 193 public boolean write(Writer out, String newline, boolean atNewline) 194 throws IOException { 195 out.write(rawHtmlContent.replace("\n", newline)); 196 return rawHtmlContent.endsWith("\n"); 197 } 198}