001package com.hfg.html;
002
003import java.io.*;
004import java.nio.charset.Charset;
005
006import javax.servlet.http.HttpServletResponse;
007
008import com.hfg.util.StringBuilderPlus;
009import com.hfg.xml.XMLBasedDoc;
010import com.hfg.xml.Doctype;
011import com.hfg.util.mime.MimeType;
012import com.hfg.xml.XMLException;
013import com.hfg.xml.XMLNode;
014import com.hfg.xml.XMLTag;
015import com.hfg.xml.parser.SaxyParser;
016import com.hfg.xml.parser.XMLTagReader;
017
018//------------------------------------------------------------------------------
019/**
020 Represents an HTML document.
021
022 @author J. Alex Taylor, hairyfatguy.com
023 */
024//------------------------------------------------------------------------------
025// com.hfg XML/HTML Coding Library
026//
027// This library is free software; you can redistribute it and/or
028// modify it under the terms of the GNU Lesser General Public
029// License as published by the Free Software Foundation; either
030// version 2.1 of the License, or (at your option) any later version.
031//
032// This library is distributed in the hope that it will be useful,
033// but WITHOUT ANY WARRANTY; without even the implied warranty of
034// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
035// Lesser General Public License for more details.
036//
037// You should have received a copy of the GNU Lesser General Public
038// License along with this library; if not, write to the Free Software
039// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
040//
041// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
042// jataylor@hairyfatguy.com
043//------------------------------------------------------------------------------
044
045
046public class HTMLDoc extends XMLBasedDoc<HTMLTag>
047{
048   private Meta mContentTypeMetaTag;
049
050   private static Doctype DEFAULT_DOCTYPE = Doctype.HTML_5;
051
052   //###########################################################################
053   // CONSTRUCTORS
054   //###########################################################################
055
056   //---------------------------------------------------------------------------
057   public HTMLDoc()
058   {
059      super();
060      setRootNode(new HTML());
061      init();
062   }
063
064   //---------------------------------------------------------------------------
065   public HTMLDoc(Doctype inDoctype)
066   {
067      this();
068      setDoctype(inDoctype);
069   }
070
071   //---------------------------------------------------------------------------
072   public HTMLDoc(HTML inRootTag)
073   {
074      super(inRootTag);
075//      init();
076   }
077
078   //---------------------------------------------------------------------------
079   public HTMLDoc(File inFile)
080         throws XMLException, IOException
081   {
082      super(inFile);
083//      init();
084   }
085
086   //---------------------------------------------------------------------------
087   public HTMLDoc(BufferedReader inReader)
088   {
089      super(inReader);
090   }
091
092   //---------------------------------------------------------------------------
093   public HTMLDoc(BufferedInputStream inStream)
094   {
095      super(inStream);
096   }
097
098   //---------------------------------------------------------------------------
099   private void init()
100   {
101      if (null == getDoctype())
102      {
103         setDoctype(DEFAULT_DOCTYPE);
104      }
105   }
106
107
108   //###########################################################################
109   // PUBLIC METHODS
110   //###########################################################################
111
112   //--------------------------------------------------------------------------
113   /**
114    Used by toXML() and toHTML() for content placed at the start of the document.
115    */
116   @Override
117   public String getHeader()
118   {
119      String header = "";
120      if (getDoctype() != null)
121      {
122         if (getDoctype().getMimeType() == MimeType.APPLICATION_XHTML)
123         {
124            header = super.getHeader(); // Adds XML header. ex: "<?xml version='1.0' standalone='yes' ?>"
125         }
126         else
127         {
128            header = getDoctype().toString() + NL;
129         }
130      }
131
132      return header;
133   }
134
135   //---------------------------------------------------------------------------
136   @Override
137   public HTMLDoc setDoctype(Doctype inValue)
138   {
139      super.setDoctype(inValue);
140      return this;
141   }
142
143   //---------------------------------------------------------------------------
144   @Override
145   public HTMLDoc setEncoding(Charset inValue)
146   {
147      super.setEncoding(inValue);
148      return this;
149   }
150
151   //---------------------------------------------------------------------------
152   public String toHTML()
153   {
154      return toXML();
155   }
156
157   //---------------------------------------------------------------------------
158   public void toHTML(OutputStream inStream)
159   {
160      toXML(inStream);
161   }
162
163   //---------------------------------------------------------------------------
164   public void toHTML(PrintWriter inWriter)
165   {
166      toXML(inWriter);
167   }
168
169   //---------------------------------------------------------------------------
170   public void toHTML(File inFile)
171   {
172      toXML(inFile);
173   }
174
175   //---------------------------------------------------------------------------
176   public void toHTML(HttpServletResponse inResponse)
177   {
178      if (getDoctype() != null)
179      {
180         inResponse.setContentType(generateContentTypeString());
181      }
182
183      try
184      {
185         Writer writer = null;
186         try
187         {
188            writer = inResponse.getWriter();
189            toXML(writer);
190         }
191         finally
192         {
193            if (writer != null)
194            {
195               writer.close();
196            }
197         }
198      }
199      catch (IOException e)
200      {
201         throw new XMLException(e);
202      }
203   }
204
205   //--------------------------------------------------------------------------
206   public String toIndentedHTML(int inInitialIndentLevel, int inIndentSize)
207   {
208      return toIndentedXML(inInitialIndentLevel, inIndentSize);
209   }
210
211   //--------------------------------------------------------------------------
212   public void toIndentedHTML(OutputStream inOutputStream, int inInitialIndentLevel, int inIndentSize)
213   {
214      toIndentedXML(inOutputStream, inInitialIndentLevel, inIndentSize);
215   }
216
217   //--------------------------------------------------------------------------
218   public void toIndentedHTML(PrintWriter inWriter, int inInitialIndentLevel, int inIndentSize)
219   {
220      toIndentedXML(inWriter, inInitialIndentLevel, inIndentSize);
221   }
222
223
224   //--------------------------------------------------------------------------
225   public void toIndentedHTML(File inFile, int inInitialIndentLevel, int inIndentSize)
226   {
227      toIndentedXML(inFile, inInitialIndentLevel, inIndentSize);
228   }
229
230   //---------------------------------------------------------------------------
231   public void setRootNode(HTML inValue)
232   {
233      super.setRootNode(inValue);
234   }
235
236   //---------------------------------------------------------------------------
237   @Override
238   public HTML getRootNode()
239   {
240      XMLNode rootNode = super.getRootNode();
241      if (!(rootNode instanceof HTML))
242      {
243         rootNode = new HTML(rootNode);
244         setRootNode((HTML) rootNode);
245      }
246
247      return (HTML) rootNode;
248   }
249
250   //###########################################################################
251   // PROTECTED METHODS
252   //###########################################################################
253
254   //--------------------------------------------------------------------------
255   protected void  prepareForOutput()
256   {
257      // If we read in an HTML document we don't really want to alter its content.
258      // If however we constructed the document, let's be complete and set a meta tag for the content type.
259      if (isConstructedContent())
260      {
261         setMetaContentTypeAndEncoding();
262      }
263   }
264
265   //--------------------------------------------------------------------------
266   protected XMLTagReader getTagReader()
267   {
268      XMLTagReader xmlTagReader = new XMLTagReader();
269      if (xmlTagReader.getParser() instanceof SaxyParser)
270      {
271         ((SaxyParser) xmlTagReader.getParser()).setLenientHTMLParsing(true);
272      }
273
274      return xmlTagReader;
275   }
276
277   //###########################################################################
278   // PRIVATE METHODS
279   //###########################################################################
280
281   //---------------------------------------------------------------------------
282   private void setMetaContentTypeAndEncoding()
283   {
284      if (getRootNode() != null)
285      {
286         if (null == mContentTypeMetaTag
287             && getRootNode().getHead() != null)
288         {
289            mContentTypeMetaTag = new Meta();
290            getRootNode().getHead().addSubtag(mContentTypeMetaTag);
291         }
292
293         if (mContentTypeMetaTag != null)
294         {
295            mContentTypeMetaTag.setHttpEquiv("Content-Type")
296                  .setContentAttribute(generateContentTypeString());
297         }
298      }
299   }
300
301   //---------------------------------------------------------------------------
302   private String generateContentTypeString()
303   {
304      StringBuilderPlus contentType = new StringBuilderPlus().setDelimiter("; ");
305      if (getDoctype() != null)
306      {
307         contentType.append(getDoctype().getMimeType());
308         contentType.delimitedAppend("charset=" + getEncoding());
309      }
310
311      return contentType.toString();
312   }
313}