/* * 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.XSDAttributeDeclaration; import org.eclipse.xsd.XSDComplexTypeDefinition; import org.eclipse.xsd.XSDElementDeclaration; import org.eclipse.xsd.XSDFactory; import org.eclipse.xsd.XSDSchemaContent; import org.eclipse.xsd.XSDSimpleTypeDefinition; import org.eclipse.xsd.XSDTypeDefinition; import org.eclipse.xsd.util.XSDConstants; import org.eclipse.xsd.util.XSDUtil; import org.picocontainer.defaults.DefaultPicoContainer; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.xml.namespace.QName; import org.geotools.xml.AttributeInstance; import org.geotools.xml.Binding; import org.geotools.xml.ElementInstance; import org.geotools.xml.InstanceComponent; import org.geotools.xml.Node; import org.geotools.xml.Parser; import org.geotools.xml.SchemaIndex; import org.geotools.xml.Schemas; import org.geotools.xml.Text; import org.geotools.xml.TextInstance; public class ElementHandlerImpl extends HandlerImpl implements ElementHandler { /** parent handler **/ Handler parent; /** the element declaration **/ XSDElementDeclaration content; /** the element instance **/ ElementImpl element; /** the running parser */ ParserHandler parser; /** the element type strategy **/ Binding strategy; /** child handlers **/ //ArrayList childHandlers; /** parse tree for the element **/ NodeImpl node; /** parsed value **/ Object value; public ElementHandlerImpl(XSDElementDeclaration content, Handler parent, ParserHandler parser) { this.content = content; this.parent = parent; this.parser = parser; //childHandlers = new ArrayList(); } public void startElement(QName qName, Attributes attributes) throws SAXException { //clear handler list //childHandlers.clear(); //create the attributes List atts = new ArrayList(); for (int i = 0; i < attributes.getLength(); i++) { String rawAttQName = attributes.getQName(i); if (rawAttQName != null) { //ignore namespace declarations if (rawAttQName.startsWith("xmlns:")) { continue; } //ignore xsi:schemaLocation if (rawAttQName.endsWith("schemaLocation")) { String prefix = ""; if (rawAttQName.indexOf(':') != -1) { prefix = rawAttQName.substring(0, rawAttQName.indexOf(':')); } String uri = parser.getNamespaceSupport().getURI(prefix); if ((uri != null) && uri.equals(XSDConstants.SCHEMA_INSTANCE_URI_2001)) { continue; } } } // String qName = attributes.getQName(i); // // // //ignore schema location attribute // if ( attributes.getQName(i) != null && attributes.getQName(index)) // String uri = attributes.getURI(i); String name = attributes.getLocalName(i); QName attQName = new QName(uri, name); XSDAttributeDeclaration decl = Schemas.getAttributeDeclaration(content, attQName); if (decl == null) { //check wether unknown attributes should be parsed if (!parser.isStrict()) { if (parser.getLogger().isLoggable(Level.FINE)) { parser.getLogger().fine("Parsing unknown attribute: " + attQName); } //create a mock attribute and continue decl = XSDFactory.eINSTANCE.createXSDAttributeDeclaration(); decl.setName(attQName.getLocalPart()); decl.setTargetNamespace(attQName.getNamespaceURI()); //set the type to be of string XSDSimpleTypeDefinition type = (XSDSimpleTypeDefinition) XSDUtil.getSchemaForSchema(XSDUtil.SCHEMA_FOR_SCHEMA_URI_2001) .getSimpleTypeIdMap() .get("string"); decl.setTypeDefinition(type); } } //TODO: validate, if there is no declaration for an attribute, then //TODO: make sure no required attributes are missing // validation should fail, this is being side stepped for now until // a good way of handling the namespace attributes on the root // element, for now we just ignore attributes we dont find in the // schema if (decl != null) { AttributeInstance att = new AttributeImpl(decl); att.setNamespace(decl.getTargetNamespace()); att.setName(decl.getName()); att.setText(attributes.getValue(i)); atts.add(att); } else { parser.getLogger().warning("Could not find attribute declaration: " + attQName); } } //create the element element = new ElementImpl(content); element.setNamespace(qName.getNamespaceURI()); element.setName(qName.getLocalPart()); element.setAttributes((AttributeInstance[]) atts.toArray(new AttributeInstance[atts.size()])); //create the parse tree for the node node = new NodeImpl(element); //parse the attributes for (int i = 0; i < element.getAttributes().length; i++) { AttributeInstance attribute = element.getAttributes()[i]; ParseExecutor executor = new ParseExecutor(attribute, null, parent.getContext(), parser); parser.getBindingWalker() .walk(attribute.getAttributeDeclaration(), executor, parent.getContext()); Object parsed = executor.getValue(); node.addAttribute(new NodeImpl(attribute, parsed)); } //create context for children //TODO: this should only be done if the element is complex, this class // needs to be split into two, one for complex, other for simple setContext(new DefaultPicoContainer(parent.getContext())); //set the context on the binding factory ((BindingFactoryImpl) parser.getBindingFactory()).setContext(getContext()); //"start" the child handler parent.startChildHandler(this); // ContextInitializer initer = new ContextInitializer(element, node, // getContext()); // parser.getBindingWalker().walk(element .getElementDeclaration(), initer, getContext() ); } public void characters(char[] ch, int start, int length) throws SAXException { //simply add the text to the element element.addText(ch, start, length); if (isMixed()) { String text = new String(ch, start, length); node.addChild(new NodeImpl(TextInstance.INSTANCE, new Text(text))); } } public void endElement(QName qName) throws SAXException { if (isMixed()) { ((NodeImpl)node).collapseWhitespace(); } //get the containing type (we do this for anonymous complex types) XSDTypeDefinition container = null; if (getParentHandler().getComponent() != null) { container = getParentHandler().getComponent().getTypeDefinition(); } ParseExecutor executor = new ParseExecutor(element, node, getParentHandler().getContext(), parser); parser.getBindingWalker() .walk(element.getElementDeclaration(), executor, container, getParentHandler().getContext()); //cache the parsed value value = executor.getValue(); if (value == null) { //TODO: instead of continuuing, just remove the element from // the parent, or figure out if the element is 'optional' and // remove if (parser.getLogger().isLoggable(Level.FINE)) { parser.getLogger().fine("Binding for " + element.getName() + " returned null"); } } //set the value for this node in the parse tree node.setValue(value); //end this child handler parent.endChildHandler(this); //kill the context parent.getContext().removeChildContainer(getContext()); } public Handler createChildHandler(QName qName) { return getChildHandlerInternal(qName); } private Handler getChildHandlerInternal(QName qName) { SchemaIndex index = parser.getSchemaIndex(); XSDElementDeclaration element = index.getChildElement(content, qName); if (element != null) { //TODO: determine wether the element is complex or simple, and create ElementHandler handler = parser.getHandlerFactory() .createElementHandler(element, this, parser); return handler; } //could not find the element as a direct child of the parent, check // for a global element, and then check its substituation group element = index.getElementDeclaration(qName); if (element != null) { XSDElementDeclaration sub = element.getSubstitutionGroupAffiliation(); if (sub != null) { QName subQName = new QName(sub.getTargetNamespace(), sub.getName()); Handler handler = getChildHandlerInternal(subQName); if (handler != null) { //this means that hte element is substituatable for an // actual child. now we have have choice, do we return // a handler for the actual element, or the element it // substituable for - the answer is to check the bindings //TODO: ask the binding handler = parser.getHandlerFactory().createElementHandler(element, this, parser); return handler; } } } //if return null; } // public List getChildHandlers() { // return childHandlers; // } public void startChildHandler(Handler child) { //childHandlers.add(child); node.addChild(child.getParseNode()); //initialize the context for the handler if (child instanceof ElementHandler) { //get the containing type (we do this for anonymous complex types) XSDTypeDefinition container = null; if (getParentHandler().getComponent() != null) { container = getParentHandler().getComponent().getTypeDefinition(); } ElementInstance childInstance = (ElementInstance) child.getComponent(); ContextInitializer initer = new ContextInitializer(childInstance, node, child.getContext()); parser.getBindingWalker().walk(element.getElementDeclaration(), initer, container, getContext()); } } public void endChildHandler(Handler child) { //add the node to the parse tree //childHandlers.remove(child); } public Handler getParentHandler() { return parent; } public XSDSchemaContent getSchemaContent() { return content; } public Node getParseNode() { return node; } public XSDElementDeclaration getElementDeclaration() { return content; } public InstanceComponent getComponent() { return element; } public void setComponent(ElementImpl element) { this.element = element; } public Object getValue() { return value; } boolean isMixed() { if (!parser.isHandleMixedContent()) { return false; } return content.getType() != null && content.getType() instanceof XSDComplexTypeDefinition && ((XSDComplexTypeDefinition)content.getType()).isMixed(); } public String toString() { return (node != null) ? node.toString() : ""; } }