/*
* Copyright 2005 The Apache Software Foundation
*
* Licensed 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.ws.jaxme.impl;
import javax.xml.bind.DatatypeConverterInterface;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.bind.ValidationEventLocator;
import javax.xml.bind.helpers.PrintConversionEventImpl;
import javax.xml.bind.helpers.ValidationEventLocatorImpl;
import org.apache.ws.jaxme.XMLConstants;
import org.apache.ws.jaxme.util.NamespaceSupport;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
/** The controller is created by the
* {@link org.apache.ws.jaxme.JMMarshaller} for
* marshalling a given element.
*/
public class JMSAXDriverController {
private static final Attributes ZERO_ATTRIBUTES = new AttributesImpl();
private final JMMarshallerImpl marshaller;
private final DatatypeConverterInterface converter;
private final ContentHandler target;
private final NamespaceSupport nss = new NamespaceSupport();
private int cnt;
/** Returns the Marshaller, which created the controller.
*/
public JMMarshallerImpl getJMMarshaller() {
return marshaller;
}
/** Returns the target handler, to which SAX events are
* being fired.
*/
public ContentHandler getTarget() {
return target;
}
/** Returns an instance of NamespaceSupport.
*/
public NamespaceSupport getNamespaceContext() {
return nss;
}
/** Creates a new instance with the given marshaller and target.
*/
public JMSAXDriverController(JMMarshallerImpl pMarshaller, ContentHandler pTarget) throws SAXException {
marshaller = pMarshaller;
target = pTarget;
converter = marshaller.getDatatypeConverter();
}
/** Returns the {@link DatatypeConverterInterface} being used for
* conversion of atomic values.
*/
public DatatypeConverterInterface getDatatypeConverter() {
return converter;
}
protected String getNewPrefix(String pURI, String pSuggestedPrefix) {
if (pSuggestedPrefix == null || pSuggestedPrefix.length() == 0) {
if (!nss.isPrefixDeclared("")) {
nss.declarePrefix("", pURI);
return "";
}
pSuggestedPrefix = "p";
}
String pc = pSuggestedPrefix;
for (;;) {
if (!nss.isPrefixDeclared(pc)) {
nss.declarePrefix(pc, pURI);
return pc;
}
pc = pSuggestedPrefix + ++cnt;
}
}
protected String getPreferredPrefix(JMSAXDriver pDriver, String pURI) {
if (pDriver != null) {
String prefix = pDriver.getPreferredPrefix(pURI);
if (prefix != null) {
return prefix;
}
}
if (XMLConstants.XML_SCHEMA_URI.equals(pURI)) {
return "xsi";
} else {
return null;
}
}
protected String getElementQName(JMSAXDriver pDriver, String pPrefix,
String pNamespaceURI, String pLocalName) throws SAXException {
if (pNamespaceURI == null) {
pNamespaceURI = "";
}
String prefix = nss.getPrefix(pNamespaceURI);
if (prefix == null) {
if (pPrefix != null) {
prefix = pPrefix;
} else {
prefix = getNewPrefix(pNamespaceURI, getPreferredPrefix(pDriver, pNamespaceURI));
}
nss.declarePrefix(prefix, pNamespaceURI);
getTarget().startPrefixMapping(prefix, pNamespaceURI);
}
if (prefix == null || "".equals(prefix)) {
return pLocalName;
} else {
return prefix + ":" + pLocalName;
}
}
/** Returns the qualified name of the element <code>pLocalName</code>
* in namespace <code>pNamespaceURI</code>. In other words, it attachs
* a prefix, if required.
*/
public String getElementQName(JMSAXDriver pDriver, String pNamespaceURI,
String pLocalName)
throws SAXException {
if (pNamespaceURI == null) {
pNamespaceURI = "";
}
String prefix = nss.getPrefix(pNamespaceURI);
if (prefix == null) {
prefix = getNewPrefix(pNamespaceURI, getPreferredPrefix(pDriver, pNamespaceURI));
nss.declarePrefix(prefix, pNamespaceURI);
getTarget().startPrefixMapping(prefix, pNamespaceURI);
}
if (prefix == null || "".equals(prefix)) {
return pLocalName;
} else {
return prefix + ":" + pLocalName;
}
}
/** Returns the qualified name of the attribute <code>pLocalName</code>.
* In other words, attachs a prefix, if required.
*/
public String getAttrQName(JMSAXDriver pDriver, String pNamespaceURI,
String pLocalName) throws SAXException {
if (pNamespaceURI == null) {
pNamespaceURI = "";
}
String prefix = nss.getAttributePrefix(pNamespaceURI);
if (prefix == null) {
prefix = getPreferredPrefix(pDriver, pNamespaceURI);
if (prefix == null) {
prefix = getNewPrefix(pNamespaceURI, "p");
}
getTarget().startPrefixMapping(prefix, pNamespaceURI);
}
if (prefix == null || "".equals(prefix)) {
return pLocalName;
} else {
return prefix + ":" + pLocalName;
}
}
protected void addSchemaLocationAttributes(JMSAXDriver pDriver,
AttributesImpl pAttrs) throws SAXException {
JMMarshallerImpl m = getJMMarshaller();
String schemaLocation = m.getSchemaLocation();
String schemaLocationAttribute;
if (schemaLocation != null) {
schemaLocationAttribute = XMLConstants.XML_SCHEMA_NS_ATTR;
} else {
schemaLocation = m.getNoNamespaceSchemaLocation();
if (schemaLocation != null) {
schemaLocationAttribute = XMLConstants.XML_SCHEMA_NO_NS_ATTR;
} else {
schemaLocationAttribute = null;
}
}
if (schemaLocation != null) {
String qName = getAttrQName(pDriver, XMLConstants.XML_SCHEMA_URI, schemaLocationAttribute);
pAttrs.addAttribute(XMLConstants.XML_SCHEMA_URI, schemaLocationAttribute,
qName, "CDATA", schemaLocation);
}
}
/** Marshals the given object, creating a root element with
* the given namespace URI and local name.
* @param pElement The element being marshalled. It must be
* an instance of the class associated to this specific
* JMXmlSerializer.
*/
public void marshal(JMSAXDriver pDriver, String pPrefix,
String pNamespaceURI, String pLocalName,
Object pElement) throws SAXException {
int context = nss.getContext();
String qName = getElementQName(pDriver, pPrefix, pNamespaceURI, pLocalName);
AttributesImpl attrs = pDriver.getAttributes(this, pElement);
addSchemaLocationAttributes(pDriver, attrs);
ContentHandler h = getTarget();
h.startElement(pNamespaceURI, pLocalName, qName, attrs);
pDriver.marshalChilds(this, h, pElement);
h.endElement(pNamespaceURI, pLocalName, qName);
restoreContext(context);
}
/** Marshals the given object, creating an element with
* the given namespace URI and local name.
* @param pElement The element being marshalled. It must be
* an instance of the class associated to this specific
* JMXmlSerializer.
*/
public void marshal(JMSAXDriver pDriver, String pNamespaceURI,
String pLocalName, Object pElement) throws SAXException {
int context = nss.getContext();
String qName = getElementQName(pDriver, pNamespaceURI, pLocalName);
AttributesImpl attrs = pDriver.getAttributes(this, pElement);
ContentHandler h = getTarget();
h.startElement(pNamespaceURI, pLocalName, qName, attrs);
pDriver.marshalChilds(this, h, pElement);
h.endElement(pNamespaceURI, pLocalName, qName);
restoreContext(context);
}
/** Called by the driver for creating a simple child.
*/
public void marshalSimpleChild(JMSAXDriver pDriver, String pNamespaceURI,
String pLocalName, String pValue)
throws SAXException {
int context = nss.getContext();
String qName = getElementQName(pDriver, pNamespaceURI, pLocalName);
ContentHandler h = getTarget();
h.startElement(pNamespaceURI, pLocalName, qName, ZERO_ATTRIBUTES);
if (pValue != null && pValue.length() > 0) {
h.characters(pValue.toCharArray(), 0, pValue.length());
}
h.endElement(pNamespaceURI, pLocalName, qName);
restoreContext(context);
}
private void restoreContext(int pContext) throws SAXException {
NamespaceSupport nss = getNamespaceContext();
ContentHandler h = getTarget();
for (;;) {
String prefix = nss.checkContext(pContext);
if (prefix == null) {
return;
}
h.endPrefixMapping(prefix);
}
}
public void printConversionEvent(Object pObject, String pMsg, Exception pException) throws SAXException {
ValidationEventHandler handler = getJMMarshaller().getEventHandler();
if (handler != null) {
ValidationEventLocator locator = new ValidationEventLocatorImpl(pObject);
PrintConversionEventImpl event = new PrintConversionEventImpl(ValidationEvent.FATAL_ERROR, pMsg, locator);
if (handler.handleEvent(event)) {
return;
}
}
throw new SAXException(pMsg, pException);
}
}