[Yanel-commits] rev 26380 -
public/yanel/trunk/src/core/java/org/wyona/yanel/core/serialization
michi at wyona.com
michi at wyona.com
Mon Jul 30 14:07:46 CEST 2007
Author: michi
Date: 2007-07-30 14:07:46 +0200 (Mon, 30 Jul 2007)
New Revision: 26380
Added:
public/yanel/trunk/src/core/java/org/wyona/yanel/core/serialization/AbstractSerializer.java
public/yanel/trunk/src/core/java/org/wyona/yanel/core/serialization/XMLSerializer.java
Modified:
public/yanel/trunk/src/core/java/org/wyona/yanel/core/serialization/HTMLSerializer.java
public/yanel/trunk/src/core/java/org/wyona/yanel/core/serialization/SerializerFactory.java
Log:
abstract and xml serializer introduced
Added: public/yanel/trunk/src/core/java/org/wyona/yanel/core/serialization/AbstractSerializer.java
===================================================================
--- public/yanel/trunk/src/core/java/org/wyona/yanel/core/serialization/AbstractSerializer.java (rev 0)
+++ public/yanel/trunk/src/core/java/org/wyona/yanel/core/serialization/AbstractSerializer.java 2007-07-30 12:07:46 UTC (rev 26380)
@@ -0,0 +1,262 @@
+package org.wyona.yanel.core.serialization;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.util.Properties;
+import org.apache.log4j.Category;
+import org.apache.xml.serializer.Serializer;
+import org.apache.xml.serializer.DOMSerializer;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.ext.LexicalHandler;
+import org.xml.sax.helpers.DefaultHandler;
+
+public abstract class AbstractSerializer extends DefaultHandler implements Serializer, LexicalHandler {
+
+ private static Category log = Category.getInstance(AbstractSerializer.class);
+ private EntityResolver entityResolver;
+ private String pendingElement = null;
+ private boolean doIndent;
+ private boolean visitedRootElement = false;
+
+ protected static final String[] nonCollapsableElements = { "textarea", "script", "style", "div" };
+
+ public AbstractSerializer() {
+ }
+
+ public void startDocument() throws SAXException {
+ try {
+ String omitXMLDeclaration = this.properties.getProperty("omit-xml-declaration", "no");
+ if (omitXMLDeclaration != null && !omitXMLDeclaration.equals("yes")) {
+ print("<?xml version=\"1.0\"?>\n");
+ }
+ String doctypePublic = this.properties.getProperty("doctype-public");
+ String doctypeSystem = this.properties.getProperty("doctype-system");
+ String method = this.properties.getProperty("method", "xml");
+ if (doctypePublic != null) {
+ print("<!DOCTYPE " + method + " PUBLIC \"" + doctypePublic);
+ if (doctypeSystem != null) {
+ print("\" \"" + doctypeSystem);
+ }
+ print("\">\n");
+ }
+ this.doIndent = this.properties.getProperty("indent", "no").equals("yes");
+ } catch (RuntimeException e) {
+ log.error(e.getMessage(), e);
+ throw e;
+ }
+ }
+
+ public void endDocument() throws SAXException {
+ try {
+ os.flush();
+ os.close();
+ } catch (IOException e) {
+ log.error(e.getMessage(), e);
+ throw new SAXException(e);
+ }
+ }
+
+ public void startElement(String namespaceURI, String localName, String qName, Attributes attrs) throws SAXException {
+ handlePendingElement();
+ String eName = ("".equals(localName)) ? qName : localName;
+
+ StringBuffer element = new StringBuffer();
+ element.append("<" + eName);
+
+ // add xhtml namespace to the root element:
+ if (!this.visitedRootElement) {
+ element.append(" xmlns=\"http://www.w3.org/1999/xhtml\"");
+ this.visitedRootElement = true;
+ }
+ for(int i = 0; i < attrs.getLength(); i++) {
+ String aLocalName = attrs.getLocalName(i);
+ String aQName = attrs.getQName(i);
+ String aName = ("".equals(aLocalName)) ? aQName : aLocalName;
+ // don't copy namespace attributes
+ if (!(aLocalName.startsWith("xmlns") || aQName.startsWith("xmlns"))) {
+ String aValue = replaceEntities(attrs.getValue(i));
+ element.append(" " + aName + "=\"" + aValue + "\"");
+ }
+ }
+ // NOTE: the element will not be closed yet because we don't know if the
+ // element has to be collapsed.
+
+ this.pendingElement = element.toString();
+ }
+
+ public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
+ String eName = ("".equals(localName)) ? qName : localName;
+ if (this.pendingElement != null) {
+ if (isCollapsableElement(eName)) {
+ print(this.pendingElement + "/>");
+ } else {
+ print(this.pendingElement + "></" + eName + ">");
+ }
+ this.pendingElement = null;
+ } else {
+ print("</" + eName + ">");
+ }
+ if (this.doIndent) {
+ // Not a real indent yet, just add line breaks.
+ // Don't add line breaks after inline-elements because it might break the
+ // layout. see http://www.w3.org/TR/CSS21/text.html#q8
+ print("\n");
+ }
+ }
+
+ /**
+ * Writes the pending element if there is one.
+ * This method is called when we know that the element is either non-empty
+ * or non-collapsable.
+ * @throws SAXException
+ */
+ protected void handlePendingElement() throws SAXException {
+ if (this.pendingElement != null) {
+ print(this.pendingElement + ">");
+ this.pendingElement = null;
+ }
+ }
+
+ /**
+ * Indicates whether an element may be collapsed in the output if it is empty.
+ * Collapsing means to write e.g. <textarea/> instead of <textarea></textarea>.
+ * Some browsers (e.g. IE) have problems with </textarea>.
+ * @param elementName
+ * @return
+ */
+ private boolean isCollapsableElement(String elementName) {
+ for (int i=0; i< this.nonCollapsableElements.length; i++) {
+ if (nonCollapsableElements[i].equals(elementName)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public void characters(char[] buf, int offset, int len) throws SAXException {
+ handlePendingElement();
+ String s = replaceEntities(new String(buf, offset, len));
+ print(s);
+ }
+
+
+ public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
+ try {
+ if (this.entityResolver != null) {
+ return this.entityResolver.resolveEntity(publicId, systemId);
+ } else {
+ return super.resolveEntity(publicId, systemId);
+ }
+ } catch (IOException e) {
+ log.error(e.getMessage(), e);
+ throw new SAXException(e);
+ }
+ }
+
+ public void setEntityResolver(EntityResolver entityResolver) {
+ this.entityResolver = entityResolver;
+ }
+
+ public ContentHandler asContentHandler() throws IOException {
+ return this;
+ }
+
+ public DOMSerializer asDOMSerializer() throws IOException {
+ return null;
+ }
+
+
+ protected OutputStream os;
+
+ public void setOutputStream(OutputStream os) {
+ this.os = os;
+ }
+
+ public OutputStream getOutputStream() {
+ return this.os;
+ }
+
+ protected void print(String s) throws SAXException {
+ try {
+ this.os.write(s.getBytes("UTF-8"));
+ } catch (IOException e) {
+ log.error(e.getMessage(), e);
+ throw new SAXException(e);
+ }
+ }
+
+ /**
+ * Replaces some characters by their corresponding xml entities.
+ * @param str
+ * @return
+ */
+ public String replaceEntities(String str) {
+ // there may be some & and some & mixed in the input, so first transform all
+ // & to & and then transform all & back to &
+ // this way we don't get double escaped &amp;
+ str = str.replaceAll("&", "&");
+ str = str.replaceAll("&", "&");
+ str = str.replaceAll("<", "<");
+ str = str.replaceAll(">", ">");
+ str = str.replaceAll("'", "'");
+ str = str.replaceAll("\"", """);
+ return str;
+ }
+
+ public void setWriter(Writer writer) {
+ // TODO Auto-generated method stub
+ }
+
+ public Writer getWriter() {
+ return null;
+ }
+
+ protected Properties properties;
+
+ public void setOutputFormat(Properties properties) {
+ this.properties = properties;
+ }
+
+ public Properties getOutputFormat() {
+ return properties;
+ }
+
+ public boolean reset() {
+ return true;
+ }
+
+ public void comment(char[] buf, int offset, int length) throws SAXException {
+ handlePendingElement();
+ String s = new String(buf, offset, length);
+ print("<!-- " + s + " -->");
+ }
+
+ public void endCDATA() throws SAXException {
+ // TODO Auto-generated method stub
+ }
+
+ public void endDTD() throws SAXException {
+ // TODO Auto-generated method stub
+ }
+
+ public void endEntity(String name) throws SAXException {
+ // TODO Auto-generated method stub
+ }
+
+ public void startCDATA() throws SAXException {
+ // TODO Auto-generated method stub
+ }
+
+ public void startDTD(String arg0, String arg1, String arg2) throws SAXException {
+ // TODO Auto-generated method stub
+ }
+
+ public void startEntity(String name) throws SAXException {
+ // TODO Auto-generated method stub
+ }
+}
Modified: public/yanel/trunk/src/core/java/org/wyona/yanel/core/serialization/HTMLSerializer.java
===================================================================
--- public/yanel/trunk/src/core/java/org/wyona/yanel/core/serialization/HTMLSerializer.java 2007-07-30 11:47:45 UTC (rev 26379)
+++ public/yanel/trunk/src/core/java/org/wyona/yanel/core/serialization/HTMLSerializer.java 2007-07-30 12:07:46 UTC (rev 26380)
@@ -15,7 +15,7 @@
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.DefaultHandler;
-public class HTMLSerializer extends DefaultHandler implements Serializer, LexicalHandler {
+public class HTMLSerializer extends AbstractSerializer implements Serializer, LexicalHandler {
private static Category log = Category.getInstance(HTMLSerializer.class);
private EntityResolver entityResolver;
@@ -209,28 +209,6 @@
}
}
- /**
- * Replaces some characters by their corresponding xml entities.
- * @param str
- * @return
- */
- private String replaceEntities(String str) {
- // there may be some & and some & mixed in the input, so first transform all
- // & to & and then transform all & back to &
- // this way we don't get double escaped &amp;
- str = str.replaceAll("&", "&");
- str = str.replaceAll("&", "&");
- str = str.replaceAll("<", "<");
- str = str.replaceAll(">", ">");
- str = str.replaceAll("'", "'");
- str = str.replaceAll("\"", """);
- return str;
- }
-
- public void setWriter(Writer writer) {
- // TODO Auto-generated method stub
- }
-
public Writer getWriter() {
return null;
}
@@ -254,28 +232,4 @@
String s = new String(buf, offset, length);
print("<!-- " + s + " -->");
}
-
- public void endCDATA() throws SAXException {
- // TODO Auto-generated method stub
- }
-
- public void endDTD() throws SAXException {
- // TODO Auto-generated method stub
- }
-
- public void endEntity(String name) throws SAXException {
- // TODO Auto-generated method stub
- }
-
- public void startCDATA() throws SAXException {
- // TODO Auto-generated method stub
- }
-
- public void startDTD(String arg0, String arg1, String arg2) throws SAXException {
- // TODO Auto-generated method stub
- }
-
- public void startEntity(String name) throws SAXException {
- // TODO Auto-generated method stub
- }
}
Modified: public/yanel/trunk/src/core/java/org/wyona/yanel/core/serialization/SerializerFactory.java
===================================================================
--- public/yanel/trunk/src/core/java/org/wyona/yanel/core/serialization/SerializerFactory.java 2007-07-30 11:47:45 UTC (rev 26379)
+++ public/yanel/trunk/src/core/java/org/wyona/yanel/core/serialization/SerializerFactory.java 2007-07-30 12:07:46 UTC (rev 26380)
@@ -15,6 +15,7 @@
public static final int XHTML_STRICT = 1;
public static final int HTML_TRANSITIONAL = 2;
+ public static final int XML = 3;
public static Serializer getSerializer(Properties format) {
Serializer serializer = new HTMLSerializer();
@@ -23,8 +24,9 @@
}
public static Serializer getSerializer(int key) {
- Serializer serializer = new HTMLSerializer();
+ Serializer serializer = null;
if (key == XHTML_STRICT) {
+ serializer = new HTMLSerializer();
Properties format = OutputPropertiesFactory.getDefaultMethodProperties("html");
format.setProperty("indent", "yes");
format.setProperty("doctype-public", "-//W3C//DTD XHTML 1.0 Strict//EN");
@@ -32,12 +34,19 @@
serializer.setOutputFormat(format);
}
if (key == HTML_TRANSITIONAL) {
+ serializer = new HTMLSerializer();
Properties format = OutputPropertiesFactory.getDefaultMethodProperties("html");
format.setProperty("indent", "yes");
format.setProperty("doctype-public", "-//W3C//DTD HTML 4.01 Transitional//EN");
format.setProperty("doctype-system", "http://www.w3.org/TR/html4/loose.dtd");
serializer.setOutputFormat(format);
}
+ if (key == XML) {
+ serializer = new XMLSerializer();
+ Properties format = OutputPropertiesFactory.getDefaultMethodProperties("xml");
+ format.setProperty("indent", "yes");
+ serializer.setOutputFormat(format);
+ }
return serializer;
}
}
Added: public/yanel/trunk/src/core/java/org/wyona/yanel/core/serialization/XMLSerializer.java
===================================================================
--- public/yanel/trunk/src/core/java/org/wyona/yanel/core/serialization/XMLSerializer.java (rev 0)
+++ public/yanel/trunk/src/core/java/org/wyona/yanel/core/serialization/XMLSerializer.java 2007-07-30 12:07:46 UTC (rev 26380)
@@ -0,0 +1,220 @@
+package org.wyona.yanel.core.serialization;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.util.Properties;
+import org.apache.log4j.Category;
+import org.apache.xml.serializer.Serializer;
+import org.apache.xml.serializer.DOMSerializer;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.ext.LexicalHandler;
+import org.xml.sax.helpers.DefaultHandler;
+
+public class XMLSerializer extends AbstractSerializer implements Serializer, LexicalHandler {
+
+ private static Category log = Category.getInstance(XMLSerializer.class);
+ private EntityResolver entityResolver;
+ private String pendingElement = null;
+ private boolean doIndent;
+ private boolean visitedRootElement = false;
+
+ protected static final String[] nonCollapsableElements = { "textarea", "script", "style", "div" };
+
+ protected static final String[] inlineElements = {"a", "abbr", "acronym", "b", "basefont",
+ "bdo", "big", "br", "cite", "code", "dfn", "em", "font", "i", "img", "input", "kbd",
+ "label", "q", "s", "samp", "select", "small", "span", "strike", "strong", "sub", "sup",
+ "textarea", "tt", "u", "var"};
+
+ public XMLSerializer() {
+ }
+
+ public void startDocument() throws SAXException {
+ try {
+ String omitXMLDeclaration = this.properties.getProperty("omit-xml-declaration", "no");
+ if (omitXMLDeclaration != null && !omitXMLDeclaration.equals("yes")) {
+ print("<?xml version=\"1.0\"?>\n");
+ }
+ this.doIndent = this.properties.getProperty("indent", "no").equals("yes");
+ } catch (RuntimeException e) {
+ log.error(e.getMessage(), e);
+ throw e;
+ }
+ }
+
+ public void endDocument() throws SAXException {
+ try {
+ os.flush();
+ os.close();
+ } catch (IOException e) {
+ log.error(e.getMessage(), e);
+ throw new SAXException(e);
+ }
+ }
+
+ public void startElement(String namespaceURI, String localName, String qName, Attributes attrs) throws SAXException {
+ handlePendingElement();
+ String eName = ("".equals(localName)) ? qName : localName;
+
+ StringBuffer element = new StringBuffer();
+ element.append("<" + eName);
+
+ for(int i = 0; i < attrs.getLength(); i++) {
+ String aLocalName = attrs.getLocalName(i);
+ String aQName = attrs.getQName(i);
+ String aName = ("".equals(aLocalName)) ? aQName : aLocalName;
+ // don't copy namespace attributes
+ if (!(aLocalName.startsWith("xmlns") || aQName.startsWith("xmlns"))) {
+ String aValue = replaceEntities(attrs.getValue(i));
+ element.append(" " + aName + "=\"" + aValue + "\"");
+ }
+ }
+ // NOTE: the element will not be closed yet because we don't know if the
+ // element has to be collapsed.
+
+ this.pendingElement = element.toString();
+ }
+
+ public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
+ String eName = ("".equals(localName)) ? qName : localName;
+ if (this.pendingElement != null) {
+ if (isCollapsableElement(eName)) {
+ print(this.pendingElement + "/>");
+ } else {
+ print(this.pendingElement + "></" + eName + ">");
+ }
+ this.pendingElement = null;
+ } else {
+ print("</" + eName + ">");
+ }
+ if (this.doIndent && !isInlineElement(eName)) {
+ // Not a real indent yet, just add line breaks.
+ // Don't add line breaks after inline-elements because it might break the
+ // layout. see http://www.w3.org/TR/CSS21/text.html#q8
+ print("\n");
+ }
+ }
+
+ /**
+ * Indicates whether an html element is a inline element or not.
+ * @param element element name
+ * @return true if it's an inline element, false otherwise.
+ */
+ protected boolean isInlineElement(String element) {
+ for (int i = 0; i < inlineElements.length; i++) {
+ if (element.equals(inlineElements[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Writes the pending element if there is one.
+ * This method is called when we know that the element is either non-empty
+ * or non-collapsable.
+ * @throws SAXException
+ */
+ protected void handlePendingElement() throws SAXException {
+ if (this.pendingElement != null) {
+ print(this.pendingElement + ">");
+ this.pendingElement = null;
+ }
+ }
+
+ /**
+ * Indicates whether an element may be collapsed in the output if it is empty.
+ * Collapsing means to write e.g. <textarea/> instead of <textarea></textarea>.
+ * Some browsers (e.g. IE) have problems with </textarea>.
+ * @param elementName
+ * @return
+ */
+ private boolean isCollapsableElement(String elementName) {
+ for (int i=0; i< this.nonCollapsableElements.length; i++) {
+ if (nonCollapsableElements[i].equals(elementName)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public void characters(char[] buf, int offset, int len) throws SAXException {
+ handlePendingElement();
+ String s = replaceEntities(new String(buf, offset, len));
+ print(s);
+ }
+
+
+ public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
+ try {
+ if (this.entityResolver != null) {
+ return this.entityResolver.resolveEntity(publicId, systemId);
+ } else {
+ return super.resolveEntity(publicId, systemId);
+ }
+ } catch (IOException e) {
+ log.error(e.getMessage(), e);
+ throw new SAXException(e);
+ }
+ }
+
+ public void setEntityResolver(EntityResolver entityResolver) {
+ this.entityResolver = entityResolver;
+ }
+
+ public ContentHandler asContentHandler() throws IOException {
+ return this;
+ }
+
+ public DOMSerializer asDOMSerializer() throws IOException {
+ return null;
+ }
+
+
+ protected OutputStream os;
+
+ public void setOutputStream(OutputStream os) {
+ this.os = os;
+ }
+
+ public OutputStream getOutputStream() {
+ return this.os;
+ }
+
+ protected void print(String s) throws SAXException {
+ try {
+ this.os.write(s.getBytes("UTF-8"));
+ } catch (IOException e) {
+ log.error(e.getMessage(), e);
+ throw new SAXException(e);
+ }
+ }
+
+ public Writer getWriter() {
+ return null;
+ }
+
+ protected Properties properties;
+
+ public void setOutputFormat(Properties properties) {
+ this.properties = properties;
+ }
+
+ public Properties getOutputFormat() {
+ return properties;
+ }
+
+ public boolean reset() {
+ return true;
+ }
+
+ public void comment(char[] buf, int offset, int length) throws SAXException {
+ handlePendingElement();
+ String s = new String(buf, offset, length);
+ print("<!-- " + s + " -->");
+ }
+}
More information about the Yanel-commits
mailing list