package org.basex.io.serial; import static org.basex.data.DataText.*; import static org.basex.query.util.Err.SERILL; import static org.basex.query.util.Err.SERPI; import static org.basex.util.Token.*; import org.basex.util.hash.TokenSet; import org.basex.util.list.TokenList; import java.io.IOException; import java.io.OutputStream; /** * This class serializes data as HTML. * * @author BaseX Team 2005-12, BSD License * @author Christian Gruen */ public class HTMLSerializer extends OutputSerializer { /** HTML: script elements. */ private static final TokenList SCRIPTS = new TokenList(); /** HTML: boolean attributes. */ private static final TokenSet BOOLEAN = new TokenSet(); /** * Constructor, specifying serialization options. * @param os output stream reference * @param p serialization properties * @throws IOException I/O exception */ HTMLSerializer(final OutputStream os, final SerializerProp p) throws IOException { super(os, p, V40, V401); } @Override public void attribute(final byte[] n, final byte[] v) throws IOException { // don't append value for boolean attributes final byte[] tagatt = concat(lc(tag), COLON, lc(n)); if(BOOLEAN.id(tagatt) != 0 && eq(n, v)) return; // escape URI attributes final byte[] val = escape && URIS.id(tagatt) != 0 ? escape(v) : v; print(' '); print(n); print(ATT1); for(int k = 0; k < val.length; k += cl(val, k)) { final int ch = cp(val, k); if(ch == '<' || ch == '&' && val[Math.min(k + 1, val.length - 1)] == '{') { print(ch); } else if(ch == '"') { print(E_QU); } else if(ch == 0x9 || ch == 0xA) { hex(ch); } else { code(ch); } } print(ATT2); } @Override public void finishComment(final byte[] n) throws IOException { if(ind) indent(); print(COMM_O); print(n); print(COMM_C); } @Override public void finishPi(final byte[] n, final byte[] v) throws IOException { if(ind) indent(); if(contains(v, '>')) SERPI.thrwSerial(); print(PI_O); print(n); print(' '); print(v); print(ELEM_C); } @Override protected void code(final int ch) throws IOException { if(script) printChar(ch); else if(ch > 0x7F && ch < 0xA0) SERILL.thrwSerial(Integer.toHexString(ch)); else if(ch == 0xA0) print(E_NBSP); else super.code(ch); } @Override protected void startOpen(final byte[] t) throws IOException { doctype(null); if(ind) indent(); print(ELEM_O); print(t); ind = indent; script = SCRIPTS.contains(lc(t)); if(content && eq(lc(tag), HEAD)) ct++; } @Override protected void finishOpen() throws IOException { super.finishOpen(); ct(false, true); } @Override protected void finishEmpty() throws IOException { if(ct(true, true)) return; print(ELEM_C); if(EMPTIES.contains(lc(tag))) return; ind = false; finishClose(); } @Override protected void finishClose() throws IOException { super.finishClose(); script = script && !SCRIPTS.contains(lc(tag)); } // HTML Serializer: cache elements static { // script elements SCRIPTS.add(token("script")); SCRIPTS.add(token("style")); // boolean attributes BOOLEAN.add(token("area:nohref")); BOOLEAN.add(token("button:disabled")); BOOLEAN.add(token("dir:compact")); BOOLEAN.add(token("dl:compact")); BOOLEAN.add(token("frame:noresize")); BOOLEAN.add(token("hr:noshade")); BOOLEAN.add(token("img:ismap")); BOOLEAN.add(token("input:checked")); BOOLEAN.add(token("input:disabled")); BOOLEAN.add(token("input:readonly")); BOOLEAN.add(token("menu:compact")); BOOLEAN.add(token("object:declare")); BOOLEAN.add(token("ol:compact")); BOOLEAN.add(token("optgroup:disabled")); BOOLEAN.add(token("option:selected")); BOOLEAN.add(token("option:disabled")); BOOLEAN.add(token("script:defer")); BOOLEAN.add(token("select:multiple")); BOOLEAN.add(token("select:disabled")); BOOLEAN.add(token("td:nowrap")); BOOLEAN.add(token("textarea:disabled")); BOOLEAN.add(token("textarea:readonly")); BOOLEAN.add(token("th:nowrap")); BOOLEAN.add(token("ul:compact")); } }