001package org.apache.maven.doxia.module.xhtml; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.io.Writer; 023 024import javax.swing.text.MutableAttributeSet; 025import javax.swing.text.html.HTML.Attribute; 026 027import org.apache.maven.doxia.sink.XhtmlBaseSink; 028import org.apache.maven.doxia.sink.SinkEventAttributeSet; 029import org.apache.maven.doxia.util.HtmlTools; 030 031import org.codehaus.plexus.util.StringUtils; 032 033/** 034 * <a href="http://www.w3.org/TR/xhtml1/">Xhtml 1.0 Transitional</a> sink implementation. 035 * <br/> 036 * It uses the DTD/xhtml1-transitional <a href="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 037 * http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd</a>. 038 * 039 * @author Jason van Zyl 040 * @author ltheussl 041 * @version $Id$ 042 * @since 1.0 043 */ 044public class XhtmlSink 045 extends XhtmlBaseSink 046 implements XhtmlMarkup 047{ 048 // ---------------------------------------------------------------------- 049 // Instance fields 050 // ---------------------------------------------------------------------- 051 052 private String encoding; 053 054 private String languageId; 055 056 /** An indication on if we're inside a head title. */ 057 private boolean headTitleFlag; 058 059 // ---------------------------------------------------------------------- 060 // Constructors 061 // ---------------------------------------------------------------------- 062 063 /** 064 * Constructor, initialize the Writer. 065 * 066 * @param writer not null writer to write the result. 067 */ 068 protected XhtmlSink( Writer writer ) 069 { 070 super( writer ); 071 } 072 073 /** 074 * Constructor, initialize the Writer and tells which encoding is used. 075 * 076 * @param writer not null writer to write the result. 077 * @param encoding the encoding used, that should be written to the generated HTML content 078 * if not <code>null</code>. 079 */ 080 protected XhtmlSink( Writer writer, String encoding ) 081 { 082 super( writer ); 083 084 this.encoding = encoding; 085 } 086 087 /** 088 * Constructor, initialize the Writer and tells which encoding and languageId are used. 089 * 090 * @param writer not null writer to write the result. 091 * @param encoding the encoding used, that should be written to the generated HTML content 092 * if not <code>null</code>. 093 * @param languageId language identifier for the root element as defined by 094 * <a href="ftp://ftp.isi.edu/in-notes/bcp/bcp47.txt">IETF BCP 47</a>, Tags for the Identification of Languages; 095 * in addition, the empty string may be specified. 096 */ 097 protected XhtmlSink( Writer writer, String encoding, String languageId ) 098 { 099 this( writer, encoding ); 100 101 this.languageId = languageId; 102 } 103 104 /** {@inheritDoc} */ 105 public void head() 106 { 107 init(); 108 109 setHeadFlag( true ); 110 111 write( "<!DOCTYPE html PUBLIC \"" + XHTML_TRANSITIONAL_PUBLIC_ID + "\" \"" + XHTML_TRANSITIONAL_SYSTEM_ID 112 + "\">" ); 113 114 MutableAttributeSet atts = new SinkEventAttributeSet(); 115 atts.addAttribute( "xmlns", XHTML_NAMESPACE ); 116 117 if ( languageId != null ) 118 { 119 atts.addAttribute( Attribute.LANG.toString(), languageId ); 120 atts.addAttribute( "xml:lang", languageId ); 121 } 122 123 writeStartTag( HTML, atts ); 124 125 writeStartTag( HEAD ); 126 } 127 128 /** {@inheritDoc} */ 129 public void head_() 130 { 131 if ( !isHeadTitleFlag() ) 132 { 133 // The content of element type "head" must match 134 // "((script|style|meta|link|object|isindex)*, 135 // ((title,(script|style|meta|link|object|isindex)*, 136 // (base,(script|style|meta|link|object|isindex)*)?)|(base,(script|style|meta|link|object|isindex)*, 137 // (title,(script|style|meta|link|object|isindex)*))))" 138 writeStartTag( TITLE ); 139 writeEndTag( TITLE ); 140 } 141 142 setHeadFlag( false ); 143 setHeadTitleFlag( false ); 144 145 if ( encoding != null ) 146 { 147 write( "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=" + encoding + "\"/>" ); 148 } 149 150 writeEndTag( HEAD ); 151 } 152 153 /** 154 * {@inheritDoc} 155 * @see javax.swing.text.html.HTML.Tag#TITLE 156 */ 157 public void title() 158 { 159 setHeadTitleFlag( true ); 160 161 writeStartTag( TITLE ); 162 } 163 164 /** 165 * {@inheritDoc} 166 * @see javax.swing.text.html.HTML.Tag#TITLE 167 */ 168 public void title_() 169 { 170 content( getTextBuffer().toString() ); 171 172 writeEndTag( TITLE ); 173 174 resetTextBuffer(); 175 176 } 177 178 /** 179 * {@inheritDoc} 180 * @see javax.swing.text.html.HTML.Tag#META 181 */ 182 public void author_() 183 { 184 if ( getTextBuffer().length() > 0 ) 185 { 186 MutableAttributeSet att = new SinkEventAttributeSet(); 187 att.addAttribute( Attribute.NAME, "author" ); 188 String text = HtmlTools.escapeHTML( getTextBuffer().toString() ); 189 // hack: un-escape numerical entities that have been escaped above 190 // note that numerical entities should really be added as one unicode character in the first place 191 text = StringUtils.replace( text, "&#", "&#" ); 192 att.addAttribute( Attribute.CONTENT, text ); 193 194 writeSimpleTag( META, att ); 195 196 resetTextBuffer(); 197 } 198 } 199 200 /** 201 * {@inheritDoc} 202 * @see javax.swing.text.html.HTML.Tag#META 203 */ 204 public void date_() 205 { 206 if ( getTextBuffer().length() > 0 ) 207 { 208 MutableAttributeSet att = new SinkEventAttributeSet(); 209 att.addAttribute( Attribute.NAME, "date" ); 210 att.addAttribute( Attribute.CONTENT, getTextBuffer().toString() ); 211 212 writeSimpleTag( META, att ); 213 214 resetTextBuffer(); 215 } 216 } 217 218 /** 219 * {@inheritDoc} 220 * @see javax.swing.text.html.HTML.Tag#BODY 221 */ 222 public void body() 223 { 224 writeStartTag( BODY ); 225 } 226 227 /** 228 * {@inheritDoc} 229 * @see javax.swing.text.html.HTML.Tag#BODY 230 * @see javax.swing.text.html.HTML.Tag#HTML 231 */ 232 public void body_() 233 { 234 writeEndTag( BODY ); 235 236 writeEndTag( HTML ); 237 238 flush(); 239 240 init(); 241 } 242 243 // ---------------------------------------------------------------------- 244 // Public protected methods 245 // ---------------------------------------------------------------------- 246 247 /** 248 * <p>Setter for the field <code>headTitleFlag</code>.</p> 249 * 250 * @param headTitleFlag an header title flag. 251 * @since 1.1 252 */ 253 protected void setHeadTitleFlag( boolean headTitleFlag ) 254 { 255 this.headTitleFlag = headTitleFlag; 256 } 257 258 /** 259 * <p>isHeadTitleFlag.</p> 260 * 261 * @return the current headTitleFlag. 262 * @since 1.1 263 */ 264 protected boolean isHeadTitleFlag() 265 { 266 return this.headTitleFlag ; 267 } 268}