001/*-------------------------------------------------------------------------+ 002| | 003| Copyright 2005-2011 The ConQAT Project | 004| | 005| Licensed under the Apache License, Version 2.0 (the "License"); | 006| you may not use this file except in compliance with the License. | 007| 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.conqat.lib.commons.html; 018 019import static org.conqat.lib.commons.html.EHTMLAttribute.SRC; 020import static org.conqat.lib.commons.html.EHTMLAttribute.TYPE; 021import static org.conqat.lib.commons.html.EHTMLElement.SCRIPT; 022 023import java.io.File; 024import java.io.IOException; 025import java.io.OutputStream; 026import java.io.OutputStreamWriter; 027import java.io.PrintStream; 028import java.io.PrintWriter; 029import java.io.UnsupportedEncodingException; 030 031import org.conqat.lib.commons.filesystem.FileSystemUtils; 032import org.conqat.lib.commons.string.StringUtils; 033import org.conqat.lib.commons.xml.IXMLResolver; 034import org.conqat.lib.commons.xml.XMLWriter; 035 036/** 037 * This class is used for writing HTML. 038 */ 039public class HTMLWriter extends XMLWriter<EHTMLElement, EHTMLAttribute> { 040 041 /** The CSS manager class used. */ 042 private final CSSManagerBase cssManager; 043 044 /** 045 * Creates a new writer for HTML documents. 046 * 047 * @param file 048 * the file to write to. 049 * @param cssManager 050 * the CSS manager used. If this is null, the class attributes may be 051 * filled with simple strings. 052 */ 053 public HTMLWriter(File file, CSSManagerBase cssManager) throws IOException { 054 this(new PrintStream(file, FileSystemUtils.UTF8_ENCODING), cssManager); 055 } 056 057 /** 058 * Creates a new writer for HTML documents. 059 * 060 * @param stream 061 * the stream to print to. 062 * @param cssManager 063 * the CSS manager used. If this is null, the class attributes may be 064 * filled with simple strings. 065 */ 066 public HTMLWriter(OutputStream stream, CSSManagerBase cssManager) { 067 super(new PrintWriter(wrapStream(stream)), new HTMLResolver()); 068 this.cssManager = cssManager; 069 } 070 071 // Fix comment finding? 072 /** 073 * Helper method for {@link #HTMLWriter(OutputStream, CSSManagerBase)} to deal 074 * with the exception. 075 */ 076 private static OutputStreamWriter wrapStream(OutputStream stream) { 077 try { 078 return new OutputStreamWriter(stream, FileSystemUtils.UTF8_ENCODING); 079 } catch (UnsupportedEncodingException e) { 080 throw new AssertionError("UTF-8 should be supported!"); 081 } 082 } 083 084 /** 085 * Creates a new writer for HTML documents. 086 * 087 * @param writer 088 * the writer to print to. 089 */ 090 public HTMLWriter(PrintWriter writer, CSSManagerBase cssManager) { 091 super(writer, new HTMLResolver()); 092 this.cssManager = cssManager; 093 } 094 095 /** 096 * This adds a default header for HTML files consisting of the XML header and a 097 * DOCTYPE of the xhtml frameset DTD. 098 * <p> 099 * XML version is set to "1.0", encoding provided by a parameter, and doc type 100 * definition to XHTML 1.0 Frameset. 101 */ 102 public void addStdHeader(String encoding) { 103 addHeader("1.0", encoding); 104 addPublicDocTypeDefintion(EHTMLElement.HTML, "-//W3C//DTD XHTML 1.0 Frameset//EN", 105 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"); 106 } 107 108 /** 109 * This adds a default header for HTML files consisting of the XML header and a 110 * DOCTYPE of the xhtml frameset DTD. 111 * <p> 112 * XML version is set to "1.0", encoding to "UTF-8", and doc type definition to 113 * XHTML 1.0 Frameset. 114 */ 115 public void addStdHeader() { 116 addStdHeader(FileSystemUtils.UTF8_ENCODING); 117 } 118 119 /** 120 * {@inheritDoc} 121 * 122 * Made this public here. 123 */ 124 @Override 125 public void addRawString(String html) { 126 super.addRawString(html); 127 } 128 129 /** 130 * Adds a line separator with closing and open tag (see {@link #addNewLine()}. 131 */ 132 public void addRawNewLine() { 133 addRawString(StringUtils.LINE_SEPARATOR); 134 } 135 136 /** Inserts a script tag that loads JavaScript from a separate file. */ 137 public void addExternalJavaScript(String scriptFilePath) { 138 // this is required, as some browsers choke on a directly closed 139 // script element 140 insertEmptyElement(SCRIPT, SRC, scriptFilePath, TYPE, "text/javascript"); 141 } 142 143 /** 144 * Inserts an empty element but ensures that it is not closed using the 145 * shorthand syntax (e.g. <code><div /></code>). 146 * 147 * This is useful since some browsers choke on some shorthand elements, e.g. 148 * divs or JavaScript tags. 149 * 150 * @see #openElement(EHTMLElement, Object...) 151 */ 152 public void insertEmptyElement(EHTMLElement element, Object... attributes) { 153 openElement(element, attributes); 154 addText(StringUtils.EMPTY_STRING); 155 closeElement(element); 156 } 157 158 /** Inserts a non-breaking space. */ 159 public void addNonBreakingSpace() { 160 addRawString(" "); 161 } 162 163 /** 164 * Adds an attribute to the currently open element but checks in addition if the 165 * attribute may be added at all. 166 * 167 * @throws HTMLWriterException 168 * if the attribute is not allowed for the current element. 169 */ 170 @Override 171 public void addAttribute(EHTMLAttribute attribute, Object value) { 172 if (!getCurrentElement().allowsAttribute(attribute)) { 173 throw new HTMLWriterException("Attribute " + attribute + " not allowed for element " + getCurrentElement()); 174 } 175 176 if (attribute == EHTMLAttribute.STYLE) { 177 assertCssDeclarationBlock(value); 178 value = ((CSSDeclarationBlock) value).toInlineStyle(); 179 } else if (cssManager != null && attribute == EHTMLAttribute.CLASS) { 180 assertCssDeclarationBlock(value); 181 value = cssManager.getCSSClassName((CSSDeclarationBlock) value); 182 } 183 184 super.addAttribute(attribute, value); 185 } 186 187 /** 188 * Asserts that the given value is a {@link CSSDeclarationBlock} and throws an 189 * exception otherwise. 190 */ 191 private static void assertCssDeclarationBlock(Object value) { 192 if (!(value instanceof CSSDeclarationBlock)) { 193 throw new HTMLWriterException("The argument for STYLE and CLASS attributes must be a " 194 + CSSDeclarationBlock.class.getSimpleName() + "!"); 195 } 196 } 197 198 /** The resolver used for the {@link HTMLWriter}. */ 199 public static class HTMLResolver implements IXMLResolver<EHTMLElement, EHTMLAttribute> { 200 201 /** {@inheritDoc} */ 202 @Override 203 public String resolveAttributeName(EHTMLAttribute attribute) { 204 return attribute.toString(); 205 } 206 207 /** {@inheritDoc} */ 208 @Override 209 public String resolveElementName(EHTMLElement element) { 210 return element.toString(); 211 } 212 213 /** {@inheritDoc} */ 214 @Override 215 public Class<EHTMLAttribute> getAttributeClass() { 216 return EHTMLAttribute.class; 217 } 218 } 219}