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