/** * Copyright 2004-2016 Riccardo Solmi. All rights reserved. * This file is part of the Whole Platform. * * The Whole Platform is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The Whole Platform is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the Whole Platform. If not, see <http://www.gnu.org/licenses/>. */ package org.whole.lang.xml.util; import java.util.regex.Pattern; import org.whole.lang.bindings.BindingManagerFactory; import org.whole.lang.bindings.IBindingManager; import org.whole.lang.builders.GenericIdentityBuilder; import org.whole.lang.xml.builders.IXmlBuilder; import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; import org.xml.sax.ext.LexicalHandler; import org.xml.sax.ext.Locator2Impl; import org.xml.sax.helpers.AttributesImpl; /** * @author Enrico Persiani */ public class SaxHandlerBuilder extends GenericIdentityBuilder implements IXmlBuilder { private static final String DEFAULT_NS = "xmlns"; private static final Pattern NS_ATTRNAME_PATTERN = Pattern.compile(DEFAULT_NS+"(:[^:]+)?"); protected ContentHandler contentHandler; protected LexicalHandler lexicalHandler; protected String defaultNamespaceUri; protected IBindingManager bindingManager; public SaxHandlerBuilder(ContentHandler contentHandler, LexicalHandler lexicalHandler, String defaultNamespaceUri) { this.contentHandler = contentHandler; this.lexicalHandler = lexicalHandler; this.defaultNamespaceUri = defaultNamespaceUri; this.bindingManager = BindingManagerFactory.instance.createBindingManager(); Locator2Impl locator = new Locator2Impl(); locator.setColumnNumber(-1); locator.setLineNumber(-1); locator.setXMLVersion("1.0"); this.bindingManager.wDefValue("documentStarted", false); this.bindingManager.wDefValue("locator", locator); this.bindingManager.wDefValue("builderStrategy", new BuilderStrategy()); } protected boolean isDocumentStarted() { return this.bindingManager.wBooleanValue("documentStarted"); } protected void setDocumentStarted() { this.bindingManager.wSetValue("documentStarted", true); } protected void conditionalStartDocument() { try { if (!isDocumentStarted()) { contentHandler.setDocumentLocator(getLocator()); contentHandler.startDocument(); setDocumentStarted(); } } catch (SAXException e) { throw new IllegalArgumentException(e); } } protected Locator2Impl getLocator() { return (Locator2Impl) bindingManager.wGetValue("locator"); } protected BuilderStrategy getBuilderStrategy() { return (BuilderStrategy) bindingManager.wGetValue("builderStrategy"); } protected void pushBuilderStrategy() { bindingManager.wEnterScope(); bindingManager.wDefValue("builderStrategy", new BuilderStrategy()); } protected void popBuilderStrategy() { bindingManager.wExitScope(); } protected String createNamepaceId(String prefix, String name) { StringBuilder qName = new StringBuilder(); qName.append(prefix == null ? DEFAULT_NS : prefix); qName.append(':'); qName.append(name); return qName.toString(); } protected void bindNamespaceUri(String namespace, String name, String uri) { bindingManager.wDefValue(createNamepaceId(namespace, name), uri); } protected String getNamespaceUri(String namespace) { String namepaceId = createNamepaceId(null, namespace == null ? DEFAULT_NS : namespace); return bindingManager.wIsSet(namepaceId) ? bindingManager.wStringValue(namepaceId) : defaultNamespaceUri; } public void visit() { } public void nameSpace() { } public void value() { } public void content() { } public void attributes() { } public void tag() { } public void text() { } public void instruction() { } public void pubidLiteral() { } public void systemLiteral() { } public void externalId() { } public void name() { } public void standalone() { } public void encoding() { } public void version() { } public void misc() { } public void docTypeDecl() { } public void xmlDecl() { } public void element() { } public void prolog() { } public void Document() { } public void Document_() { } public void _Document() { try { contentHandler.endDocument(); } catch (SAXException e) { throw new IllegalArgumentException(e); } } public void Prolog() { } public void Prolog_() { } public void _Prolog() { conditionalStartDocument(); } public void XMLDecl() { } public void XMLDecl_() { } public void _XMLDecl() { conditionalStartDocument(); } public void Version() { } public void Version(String value) { getLocator().setXMLVersion(value); } public void Encoding() { } public void Encoding(String value) { getLocator().setEncoding(value); } public void Standalone() { } public void Standalone(boolean value) { } public void DocTypeDecl() { } public void DocTypeDecl_() { getBuilderStrategy().reset(); } public void _DocTypeDecl() { try { lexicalHandler.startDTD(getBuilderStrategy().asQName(), getLocator().getPublicId(), getLocator().getSystemId()); } catch (SAXException e) { throw new IllegalArgumentException(e); } } public void SystemId() { } public void SystemId_() { } public void _SystemId() { } public void SystemLiteral() { } public void SystemLiteral(String value) { getLocator().setSystemId(value.length() == 0 ? null : value); } public void PublicId() { } public void PublicId_() { } public void _PublicId() { } public void PubidLiteral() { } public void PubidLiteral(String value) { getLocator().setPublicId(value.length() == 0 ? null : value); } public void Misc() { } public void Misc_() { } public void Misc_(int initialCapacity) { } public void _Misc() { } public void PI() { } public void PI_() { conditionalStartDocument(); try { getBuilderStrategy().pendingStartElement(); getBuilderStrategy().reset(); } catch (SAXException e) { throw new IllegalArgumentException(e); } } public void _PI() { } public void Instruction() { } public void Instruction(String value) { try { String target = getBuilderStrategy().asQName(); contentHandler.processingInstruction(target, value); } catch (SAXException e) { throw new IllegalArgumentException(e); } } public void Comment() { } public void Comment_() { conditionalStartDocument(); try { getBuilderStrategy().pendingStartElement(); } catch (SAXException e) { throw new IllegalArgumentException(e); } } public void _Comment() { } public void CommentText() { } public void CommentText(String value) { try { char[] comment = value.toCharArray(); lexicalHandler.comment(comment, 0, comment.length); } catch (SAXException e) { throw new IllegalArgumentException(e); } } public void Element() { } public void Element_() { try { getBuilderStrategy().pendingStartElement(); pushBuilderStrategy(); } catch (SAXException e) { throw new IllegalArgumentException(e); } } public void _Element() { try { getBuilderStrategy().pendingStartElement(); getBuilderStrategy().endElement(); } catch (SAXException e) { throw new IllegalArgumentException(e); } finally { popBuilderStrategy(); } } public void Attributes() { } public void Attributes_(int initialCapacity) { Attributes_(); } public void Attributes_() { getBuilderStrategy().Attributes_(); } public void _Attributes() { } public void Attribute() { } public void Attribute_() { getBuilderStrategy().Attribute_(); } public void _Attribute() { getBuilderStrategy()._Attribute(); } public void Name() { } public void Name(String value) { getBuilderStrategy().Name(value); } public void QualifiedName() { } public void QualifiedName_() { } public void _QualifiedName() { } public void NameSpace() { } public void NameSpace(String value) { getBuilderStrategy().NameSpace(value); } public void Value() { } public void Value(String value) { getBuilderStrategy().Value(value); } public void Content() { Content_(); _Content(); } public void Content_() { try { getBuilderStrategy().pendingStartElement(); } catch (SAXException e) { throw new IllegalArgumentException(e); } } public void Content_(int initialCapacity) { } public void _Content() { } public void CharData() { } public void CharData(String value) { try { getBuilderStrategy().pendingStartElement(); char[] chars = value.toCharArray(); contentHandler.characters(chars, 0, chars.length); } catch (SAXException e) { throw new IllegalArgumentException(e); } } public void CDataSect() { } public void CDataSect_(int initialCapacity) { CDataSect_(); } public void CDataSect_() { try { getBuilderStrategy().pendingStartElement(); lexicalHandler.startCDATA(); } catch (SAXException e) { throw new IllegalArgumentException(e); } } public void _CDataSect() { try { lexicalHandler.endCDATA(); } catch (SAXException e) { throw new IllegalArgumentException(e); } } public void CDataSectData() { } public void CDataSectData(String value) { try { char[] chars = value.toCharArray(); contentHandler.characters(chars, 0, chars.length); } catch (SAXException e) { throw new IllegalArgumentException(e); } } public static class IdentityLexicalHandler implements LexicalHandler { public void startDTD(String name, String publicId, String systemId) { } public void endDTD() { } public void startEntity(String name) { } public void endEntity(String name) { } public void startCDATA() { } public void endCDATA() { } public void comment(char[] ch, int start, int length) { } } public class NameStrategy { protected String namespace; protected String name; public NameStrategy() { } public String getNamespace() { return namespace; } public String getName() { return name; } public String asQName() { if (name == null) throw new IllegalStateException("name never set"); return namespace != null ? namespace + ':' + name : name; } public void reset() { this.name = null; this.namespace = null; } public void Name(String name) { if (this.name != null) throw new IllegalStateException("name overwritten"); this.name = name; } public void NameSpace(String namespace) { if (this.namespace != null) throw new IllegalStateException("namespace overwritten"); this.namespace = namespace; } } public class BuilderStrategy extends NameStrategy { protected AttributesImpl attributes; protected boolean pending; protected String elementNamespace; protected String elementName; protected String elementQName; protected String value; public BuilderStrategy() { this.attributes = new AttributesImpl(); } public void Attributes_() { this.elementNamespace = getNamespace(); this.elementName = getName(); this.elementQName = asQName(); this.pending = true; reset(); } public void _Attributes() { } public void Attribute_() { } public void _Attribute() { String uri = ""; if (NS_ATTRNAME_PATTERN.matcher(asQName()).matches()) bindNamespaceUri(getNamespace(), getName(), value); else if (getNamespace() != null) uri = getNamespaceUri(getNamespace()); this.attributes.addAttribute(uri, getName(), asQName(), "CDATA", value); reset(); } public void Value(String value) { this.value = value; } public void pendingStartElement() throws SAXException { if (pending) { String uri = getNamespaceUri(elementNamespace); contentHandler.startElement(uri, elementName, elementQName, attributes); pending = false; } } public void endElement() throws SAXException { String uri = getNamespaceUri(elementNamespace); contentHandler.endElement(uri, elementName, elementQName); } } }