/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This 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 2.1 of
* the License, or (at your option) any later version.
*
* This software 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 this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.xb.binding.sunday.unmarshalling;
import java.util.LinkedList;
import java.util.List;
import javax.xml.namespace.QName;
import org.jboss.xb.binding.JBossXBRuntimeException;
import org.jboss.xb.binding.NamespaceRegistry;
import org.jboss.xb.binding.Util;
import org.jboss.xb.binding.metadata.CharactersMetaData;
import org.jboss.xb.binding.metadata.ValueMetaData;
import org.jboss.xb.binding.parser.JBossXBParser;
import org.jboss.logging.Logger;
import org.jboss.util.StringPropertyReplacer;
import org.xml.sax.Attributes;
import org.apache.xerces.xs.XSTypeDefinition;
/**
* todo: to improve performance, consider gathering all the necessary binding metadata in startElement and
* pushing it instead of ElementBinding into elementStack to free the endElement implementation from
* re-gathering this same metadata again.
*
* @author <a href="mailto:alex@jboss.org">Alexey Loubyansky</a>
* @version <tt>$Revision: 1455 $</tt>
*/
public class SundayContentHandler
implements JBossXBParser.ContentHandler
{
private final static Logger log = Logger.getLogger(SundayContentHandler.class);
private final static Object NIL = new Object();
private final SchemaBinding schema;
private final SchemaBindingResolver schemaResolver;
private final StackImpl elementStack = new StackImpl();
private final StackImpl objectStack = new StackImpl();
private StringBuffer textContent = new StringBuffer();
private Object root;
private NamespaceRegistry nsRegistry = new NamespaceRegistry();
public SundayContentHandler(SchemaBinding schema)
{
this.schema = schema;
this.schemaResolver = null;
}
public SundayContentHandler(SchemaBindingResolver schemaResolver)
{
this.schemaResolver = schemaResolver;
this.schema = null;
}
public void characters(char[] ch, int start, int length)
{
if(elementStack.peek() != null)
{
textContent.append(ch, start, length);
}
}
public void endElement(String namespaceURI, String localName, String qName)
{
ElementBinding elementBinding = (ElementBinding)elementStack.pop();
if(elementBinding != null)
{
QName endName = localName.length() == 0 ? new QName(qName) : new QName(namespaceURI, localName);
Object o = objectStack.pop();
TypeBinding typeBinding = elementBinding.getType();
List elementHandlers = elementBinding.getInterceptors();
if(o != NIL)
{
//
// characters
//
CharactersHandler simpleType = typeBinding.getSimpleType();
if(textContent.length() > 0 || simpleType != null)
{
String dataContent;
SchemaBinding schema = elementBinding.getSchema();
if(textContent.length() == 0)
{
dataContent = null;
}
else
{
dataContent = textContent.toString();
if(schema != null && schema.isReplacePropertyRefs())
{
dataContent = StringPropertyReplacer.replaceProperties(dataContent);
}
textContent.delete(0, textContent.length());
}
Object unmarshalled;
if(simpleType == null)
{
if(schema != null && schema.isStrictSchema())
{
throw new JBossXBRuntimeException("Element " +
endName +
" type binding " +
typeBinding.getQName() +
" does not include text content binding ('" + dataContent
);
}
unmarshalled = dataContent;
}
else
{
ValueMetaData valueMetaData = elementBinding.getValueMetaData();
if(valueMetaData == null)
{
CharactersMetaData charactersMetaData = typeBinding.getCharactersMetaData();
if(charactersMetaData != null)
{
valueMetaData = charactersMetaData.getValue();
}
}
// todo valueMetaData is available from typeBinding
unmarshalled = dataContent == null ?
simpleType.unmarshalEmpty(endName, typeBinding, nsRegistry, valueMetaData) :
simpleType.unmarshal(endName, typeBinding, nsRegistry, valueMetaData, dataContent);
}
if(unmarshalled != null)
{
// if startElement returned null, we use characters as the object for this element
// todo subject to refactoring
if(o == null)
{
o = unmarshalled;
}
else if(simpleType != null)
{
simpleType.setValue(endName, elementBinding, o, unmarshalled);
}
}
// todo interceptors get dataContent?
int i = elementHandlers.size();
while(i-- > 0)
{
ElementInterceptor interceptor = (ElementInterceptor)elementHandlers.get(i);
interceptor.characters(objectStack.peek(elementHandlers.size() - 1 - i),
endName, typeBinding, nsRegistry, dataContent
);
}
}
}
else
{
o = null;
}
//
// endElement
//
Object parent = objectStack.isEmpty() ? null : objectStack.peek();
o = typeBinding.endElement(parent, o, elementBinding, endName);
int i = elementHandlers.size();
while(i-- > 0)
{
ElementInterceptor interceptor = (ElementInterceptor)elementHandlers.get(i);
interceptor.endElement(objectStack.peek(elementHandlers.size() - 1 - i), endName, typeBinding);
}
//
// setParent
//
i = elementHandlers.size();
// todo yack...
if(i == 0)
{
ElementBinding parentElement = elementStack.isEmpty() ? null : (ElementBinding)elementStack.peek();
if(parent != null)
{
typeBinding.getHandler().setParent(parent, o, endName, elementBinding, parentElement);
}
else if(parentElement != null)
{
// todo: review this> the parent has anyType, so it gets the value of its child
if(!objectStack.isEmpty())
{
objectStack.pop();
objectStack.push(o);
if(log.isTraceEnabled())
{
log.trace("Value of " + endName + " " + o + " is promoted as the value of its parent element.");
}
}
}
}
else
{
while(i-- > 0)
{
ElementInterceptor interceptor = (ElementInterceptor)elementHandlers.get(i);
parent = objectStack.pop();
interceptor.add(parent, o, endName);
o = parent;
}
}
if(objectStack.isEmpty())
{
root = o;
}
}
}
public void startElement(String namespaceURI,
String localName,
String qName,
Attributes atts,
XSTypeDefinition type)
{
QName startName = localName.length() == 0 ? new QName(qName) : new QName(namespaceURI, localName);
ElementBinding binding = null;
ElementBinding parentBinding = null;
SchemaBinding schemaBinding = schema;
if(elementStack.isEmpty())
{
if(schemaBinding != null)
{
binding = schemaBinding.getElement(startName);
}
else if(schemaResolver != null)
{
String schemaLocation = atts == null ? null : Util.getSchemaLocation(atts, namespaceURI);
schemaBinding = schemaResolver.resolve(namespaceURI, localName, null, schemaLocation);
if(schemaBinding != null)
{
binding = schemaBinding.getElement(startName);
}
}
else
{
throw new JBossXBRuntimeException("Neither schema binding nor schema binding resolver is not available!");
}
}
else
{
parentBinding = (ElementBinding)elementStack.peek();
if(parentBinding != null)
{
binding = parentBinding.getType().getElement(startName, atts);
schemaBinding = parentBinding.getSchema();
}
}
elementStack.push(binding);
if(binding != null)
{
TypeBinding typeBinding = binding.getType();
Object o = objectStack.isEmpty() ? null : objectStack.peek();
List elementHandlers = binding.getInterceptors();
for(int i = 0; i < elementHandlers.size(); ++i)
{
ElementInterceptor interceptor = (ElementInterceptor)elementHandlers.get(i);
o = interceptor.startElement(o, startName, typeBinding);
objectStack.push(o);
interceptor.attributes(o, startName, typeBinding, atts, nsRegistry);
}
// todo xsi:nil handling
String nil = atts.getValue("xsi:nil");
if(nil == null || !("1".equals(nil) || "true".equals(nil)))
{
o = typeBinding.startElement(o, startName, binding);
}
else
{
o = NIL;
}
objectStack.push(o);
if(o != null && o != NIL)
{
// Expand the attributes list with any missing attrs with defaults
atts = typeBinding.expandWithDefaultAttributes(atts);
typeBinding.attributes(o, startName, binding, atts, nsRegistry);
}
}
else if(schemaBinding != null && schemaBinding.isStrictSchema())
{
throw new JBossXBRuntimeException("Element " +
startName +
" is not bound " +
(parentBinding == null ? "as a global element." : "in type " + parentBinding.getType().getQName())
);
}
else if(log.isTraceEnabled())
{
log.trace("Element " +
startName +
" is not bound " +
(parentBinding == null ? "as a global element." : "in type " + parentBinding.getType().getQName())
);
}
}
public void startPrefixMapping(String prefix, String uri)
{
nsRegistry.addPrefixMapping(prefix, uri);
}
public void endPrefixMapping(String prefix)
{
nsRegistry.removePrefixMapping(prefix);
}
public void processingInstruction(String target, String data)
{
}
public Object getRoot()
{
return root;
}
// Inner
static class StackImpl
{
private LinkedList list = new LinkedList();
public void clear()
{
list.clear();
}
public void push(Object o)
{
list.addLast(o);
}
public Object pop()
{
return list.removeLast();
}
public Object peek()
{
return list.getLast();
}
public Object peek(int i)
{
return list.get(list.size() - 1 - i);
}
public boolean isEmpty()
{
return list.isEmpty();
}
public int size()
{
return list.size();
}
}
}