blob: 2d21fbb76b2c0b8eae6040cd24d163340ce744fd [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cocoon.components.serializers;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.cocoon.components.serializers.encoding.HTMLEncoder;
import org.apache.cocoon.components.serializers.util.DocType;
import org.apache.cocoon.components.serializers.util.SGMLDocType;
import org.xml.sax.SAXException;
/**
* <p>A serializer converting XHTML into plain old HTML.</p>
*
* <p>For configuration options of this serializer, please look at the
* {@link XHTMLSerializer} and {@link EncodingSerializer}.</p>
*
* <p>Any of the XHTML document type declared or used will be converted into
* its HTML 4.01 counterpart, and in addition to those a "compatible" doctype
* can be supported to exploit a couple of shortcuts into MSIE's rendering
* engine. The values for the <code>doctype-default</code> can then be:</p>
*
* <dl>
* <dt>"<code>none</code>"</dt>
* <dd>Not to emit any dococument type declaration.</dd>
* <dt>"<code>compatible</code>"</dt>
* <dd>The HTML 4.01 Transitional (exploiting MSIE shortcut).</dd>
* <dt>"<code>strict</code>"</dt>
* <dd>The HTML 4.01 Strict document type.</dd>
* <dt>"<code>loose</code>"</dt>
* <dd>The HTML 4.01 Transitional document type.</dd>
* <dt>"<code>frameset</code>"</dt>
* <dd>The HTML 4.01 Frameset document type.</dd>
* </dl>
*
* @version CVS $Id$
*/
public class HTMLSerializer extends XHTMLSerializer {
/** A cross-browser compatible very simple document type declaration. */
public static final DocType HTML401_DOCTYPE_COMPATIBLE = new SGMLDocType(
"HTML", "-//W3C//DTD HTML 4.01 Transitional//EN", null);
/** A representation of the HTML 4.01 strict document type. */
public static final DocType HTML401_DOCTYPE_STRICT = new SGMLDocType(
"HTML", "-//W3C//DTD HTML 4.01//EN",
"http://www.w3.org/TR/html4/strict.dtd");
/** A representation of the HTML 4.01 transitional document type. */
public static final DocType HTML401_DOCTYPE_TRANSITIONAL = new SGMLDocType(
"HTML", "-//W3C//DTD HTML 4.01 Transitional//EN",
"http://www.w3.org/TR/html4/loose.dtd");
/** A representation of the HTML 4.01 frameset document type. */
public static final DocType HTML401_DOCTYPE_FRAMESET = new SGMLDocType(
"HTML", "-//W3C//DTD HTML 4.01 Frameset//EN",
"http://www.w3.org/TR/html4/frameset.dtd");
/* ====================================================================== */
private static final HTMLEncoder HTML_ENCODER = new HTMLEncoder();
/**
* Create a new instance of this <code>HTMLSerializer</code>
*/
public HTMLSerializer() {
super(HTML_ENCODER);
}
/**
* Configure this instance by selecting the default document type to use.
*/
public void configure(Configuration conf)
throws ConfigurationException {
super.configure(conf);
String doctype = conf.getChild("doctype-default").getValue(null);
if ("none".equalsIgnoreCase(doctype)) {
this.doctype_default = null;
} else if ("compatible".equalsIgnoreCase(doctype)) {
this.doctype_default = HTML401_DOCTYPE_COMPATIBLE;
} else if ("strict".equalsIgnoreCase(doctype)) {
this.doctype_default = HTML401_DOCTYPE_STRICT;
} else if ("loose".equalsIgnoreCase(doctype)) {
this.doctype_default = HTML401_DOCTYPE_TRANSITIONAL;
} else if ("frameset".equalsIgnoreCase(doctype)) {
this.doctype_default = HTML401_DOCTYPE_FRAMESET;
} else {
/* Default is compatible (MSIE hack) */
this.doctype_default = HTML401_DOCTYPE_COMPATIBLE;
}
}
/* ====================================================================== */
/** Empty namespaces declaration. */
private static final String NAMESPACES[][] = new String[0][0];
/** Check if the URI is allowed by this serializer. */
private boolean checkNamespace(String nsuri) {
if (nsuri.length() == 0) return(true);
if (XHTML1_NAMESPACE.equals(nsuri)) return(true);
return(false);
}
/* ====================================================================== */
/**
* Write the XML document header.
* <p>
* This method overrides the default <code>XMLSerializer</code>.behaviour.
* </p>
*/
public void head()
throws SAXException {
// NO NOTHING!
}
/**
* Receive notification of the beginning of the document body.
*
* @param nsuri The namespace URI of the root element.
* @param local The local name of the root element.
* @param qual The fully-qualified name of the root element.
*/
public void body(String nsuri, String local, String qual)
throws SAXException {
String name = local.toUpperCase();
if (! this.checkNamespace(nsuri)) {
throw new SAXException("Unsupported namespace \"" + nsuri + "\" "
+ "for HTML root element \"" + qual + "\""
+ this.getLocation());
}
if (this.doctype == null) this.doctype = this.doctype_default;
if (XHTML1_DOCTYPE_STRICT.equals(this.doctype)) {
this.doctype = HTML401_DOCTYPE_STRICT;
} else if (XHTML1_DOCTYPE_TRANSITIONAL.equals(this.doctype)) {
this.doctype = HTML401_DOCTYPE_TRANSITIONAL;
} else if (XHTML1_DOCTYPE_FRAMESET.equals(this.doctype)) {
this.doctype = HTML401_DOCTYPE_FRAMESET;
} else if (this.doctype != null) {
/* The root element is uppercase, always!!! */
this.doctype = new SGMLDocType(this.doctype.getName().toUpperCase(),
this.doctype.getPublicId(),
this.doctype.getSystemId());
}
super.body(XHTML1_NAMESPACE, name, name);
}
/**
* Receive notification of the beginning of an element.
*/
public void startElementImpl(String nsuri, String local, String qual,
String namespaces[][], String attributes[][])
throws SAXException {
String name = local.toUpperCase();
if (! this.checkNamespace(nsuri)) {
throw new SAXException("Unsupported namespace \"" + nsuri + "\" "
+ "for HTML element \"" + qual + "\""
+ this.getLocation());
}
int length = 0;
for (int x = 0; x < attributes.length; x ++) {
if (checkNamespace(attributes[x][ATTRIBUTE_NSURI])) length ++;
}
String at[][] = new String[length][ATTRIBUTE_LENGTH];
length = 0;
for (int x = 0; x < attributes.length; x ++) {
if (!checkNamespace(attributes[x][ATTRIBUTE_NSURI])) continue;
String at_name = attributes[x][ATTRIBUTE_LOCAL].toLowerCase();
at[length][ATTRIBUTE_NSURI] = XHTML1_NAMESPACE;
at[length][ATTRIBUTE_LOCAL] = at_name;
at[length][ATTRIBUTE_QNAME] = at_name;
at[length][ATTRIBUTE_VALUE] = attributes[x][ATTRIBUTE_VALUE];
length++;
}
super.startElementImpl(XHTML1_NAMESPACE, name, name, NAMESPACES, at);
}
/**
* Receive notification of the end of an element.
*/
public void endElementImpl(String nsuri, String local, String qual)
throws SAXException {
this.closeElement(false);
String name = local.toUpperCase();
if (! this.checkNamespace(nsuri)) {
throw new SAXException("Unsupported namespace \"" + nsuri + "\" "
+ "for HTML element \"" + qual + "\""
+ this.getLocation());
}
if (name.equals("AREA")) return;
if (name.equals("BASE")) return;
if (name.equals("BASEFONT")) return;
if (name.equals("BR")) return;
if (name.equals("COL")) return;
if (name.equals("FRAME")) return;
if (name.equals("HR")) return;
if (name.equals("IMG")) return;
if (name.equals("INPUT")) return;
if (name.equals("ISINDEX")) return;
if (name.equals("LINK")) return;
if (name.equals("META")) return;
if (name.equals("PARAM")) return;
super.endElementImpl(XHTML1_NAMESPACE, name, name);
}
}