/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-2008, Open Source Geospatial Foundation (OSGeo)
*
* This library 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;
* version 2.1 of the License.
*
* This library 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.
*/
package org.geotools.xml.impl;
import org.eclipse.xsd.XSDComplexTypeDefinition;
import org.eclipse.xsd.XSDElementDeclaration;
import org.eclipse.xsd.XSDFeature;
import org.eclipse.xsd.XSDNamedComponent;
import org.eclipse.xsd.XSDParticle;
import org.eclipse.xsd.XSDSimpleTypeDefinition;
import org.eclipse.xsd.XSDTypeDefinition;
import org.picocontainer.MutablePicoContainer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
import javax.xml.namespace.QName;
import org.geotools.util.SoftValueHashMap;
import org.geotools.xml.Binding;
import org.geotools.xml.Schemas;
import org.geotools.xs.XS;
public class BindingWalker implements TypeWalker.Visitor {
BindingLoader loader;
SoftValueHashMap /*<XSDFeature,BindingExecutionChain>*/ chains;
TypeWalker typeWalker;
MutablePicoContainer context;
ArrayList bindings;
XSDFeature component;
XSDTypeDefinition container;
public BindingWalker(BindingLoader factory) {
this.loader = factory;
chains = new SoftValueHashMap(100);
typeWalker = new TypeWalker();
}
public Binding getAnyTypeBinding() {
return loader.loadBinding(XS.ANYTYPE, context);
}
public boolean visit(XSDTypeDefinition type) {
//look up the associated binding object for this type
QName bindingName = null;
if (type.getName() != null) {
bindingName = new QName(type.getTargetNamespace(), type.getName());
} else {
//anonymous type, does it belong to a global element
for (Iterator e = type.getSchema().getElementDeclarations().iterator(); e.hasNext();) {
XSDElementDeclaration element = (XSDElementDeclaration) e.next();
if (type.equals(element.getAnonymousTypeDefinition())) {
//TODO: this naming convention for anonymous types could conflict with
// other types in the schema
bindingName = new QName(type.getTargetNamespace(), "_" + element.getName());
break;
}
}
if (bindingName == null) {
//do we have a containing type?
if (container != null) {
XSDNamedComponent base = container;
//the container itself could be an anonymous type, check for
// a named containing element
if ( container.getName() == null ) {
if ( container.getContainer() instanceof XSDElementDeclaration ) {
XSDElementDeclaration e = (XSDElementDeclaration) container.getContainer();
//only do this if the containing element is global
if ( e.isGlobal() ) {
base = e;
}
}
}
//get the anonymous element, and look it up in the container type
if (type.getContainer() instanceof XSDElementDeclaration) {
XSDElementDeclaration anonymous = (XSDElementDeclaration) type.getContainer();
XSDParticle particle = Schemas.getChildElementParticle(container,
anonymous.getName(), true);
if (particle != null) {
bindingName = new QName(base.getTargetNamespace(),
base.getName() + "_" + anonymous.getName());
}
}
}
}
if ((bindingName == null) || (loader.getBinding(bindingName) == null)) {
//special case check, look for an anonymous complex type
// with simple content
if (type instanceof XSDComplexTypeDefinition
&& type.getBaseType() instanceof XSDSimpleTypeDefinition) {
//we assign the default complex binding instread of
// delegating to parent, because if we dont, any attributes
// defined by the type will not be parsed because simple
// types cannot have attributes.
//TODO: put this somewhere else, perahps in teh factories
// that create the bindings
bindingName = XS.ANYTYPE;
}
}
}
//load the binding into the current context
Binding binding = loader.loadBinding(bindingName, context);
if (binding != null) {
//add the binding
bindings.add(binding);
//check execution mode, if override break out
if (binding.getExecutionMode() == Binding.OVERRIDE) {
return false;
}
}
return true;
}
public void walk(XSDFeature component, Visitor visitor, XSDTypeDefinition container,
MutablePicoContainer context) {
BindingExecutionChain chain = (BindingExecutionChain) chains.get(component);
if (chain == null) {
this.container = container;
this.component = component;
this.context = context;
this.bindings = new ArrayList();
//first walk the type hierarchy to get the binding objects
typeWalker.walk(component.getType(), this);
//also look up a binding to teh instance itself, if found it will go
// at the bottom of the binding hierarchy
if (component.getName() != null) {
QName qName = new QName(component.getTargetNamespace(), component.getName());
Binding binding = loader.loadBinding(qName, context);
if (binding != null) {
//check for override
if (binding.getExecutionMode() == Binding.OVERRIDE) {
//override, clear the binding list
bindings.clear();
bindings.add(binding);
} else {
//not override, add as first
bindings.add(0, binding);
}
}
}
chain = new BindingExecutionChain(bindings);
chains.put(component, chain);
}
chain.execute(visitor);
}
public void walk(XSDFeature component, Visitor visitor, MutablePicoContainer context) {
walk(component, visitor, null, context);
}
public static interface Visitor {
void visit(Binding binding);
}
public static class BindingExecutionChain {
List bindings;
public BindingExecutionChain(List bindings) {
this.bindings = bindings;
}
public void execute(Visitor visitor) {
//simulated call stack
Stack stack = new Stack();
//visit from bottom to top
for (int i = 0; i < bindings.size(); i++) {
Binding binding = (Binding) bindings.get(i);
if (binding.getExecutionMode() == Binding.AFTER) {
//put on stack to execute after parent
stack.push(binding);
continue;
}
//execute the strategy
visitor.visit(binding);
}
//unwind the call stack
while (!stack.isEmpty()) {
Binding binding = (Binding) stack.pop();
visitor.visit(binding);
}
}
}
}