/*
* 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.apache.commons.collections.OrderedMap;
import org.apache.commons.collections.map.ListOrderedMap;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.xsd.XSDAttributeDeclaration;
import org.eclipse.xsd.XSDAttributeGroupDefinition;
import org.eclipse.xsd.XSDComplexTypeDefinition;
import org.eclipse.xsd.XSDElementDeclaration;
import org.eclipse.xsd.XSDImport;
import org.eclipse.xsd.XSDInclude;
import org.eclipse.xsd.XSDNamedComponent;
import org.eclipse.xsd.XSDPackage;
import org.eclipse.xsd.XSDParticle;
import org.eclipse.xsd.XSDSchema;
import org.eclipse.xsd.XSDSimpleTypeDefinition;
import org.eclipse.xsd.XSDTypeDefinition;
import org.eclipse.xsd.util.XSDSchemaBuildingTools;
import org.eclipse.xsd.util.XSDUtil;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.xml.namespace.QName;
import org.geotools.util.SoftValueHashMap;
import org.geotools.xml.SchemaIndex;
import org.geotools.xml.Schemas;
public class SchemaIndexImpl implements SchemaIndex {
/**
* The schemas
*/
XSDSchema[] schemas;
/**
* Indexes
*/
HashMap elementIndex;
HashMap attributeIndex;
HashMap attributeGroupIndex;
HashMap complexTypeIndex;
HashMap simpleTypeIndex;
/**
* Cache of elements to children
*/
SoftValueHashMap /*<XSDElementDeclaration,OrderedMap>*/ element2children = new SoftValueHashMap(1000);
/**
* Cache of elemnets to attributes
*/
HashMap /*<XSDElementDeclaratoin,List>*/ element2attributes = new HashMap();
/**
* Adapter for tracking changes to schemas.
*/
SchemaAdapter adapter;
public SchemaIndexImpl(XSDSchema[] schemas) {
this.schemas = new XSDSchema[schemas.length + 1];
adapter = new SchemaAdapter();
//set the schemas passed in
for (int i = 0; i < schemas.length; i++) {
this.schemas[i] = schemas[i];
this.schemas[i].eAdapters().add(adapter);
}
//add the schema for xml schema itself
this.schemas[schemas.length] = schemas[0].getSchemaForSchema();
}
public void destroy() {
//remove the adapter from the schemas
for (int i = 0; i < schemas.length; i++) {
this.schemas[i].eAdapters().remove(adapter);
}
schemas = null;
}
public XSDSchema[] getSchemas() {
return schemas;
}
public XSDImport[] getImports() {
Collection imports = find(XSDImport.class);
return (XSDImport[]) imports.toArray(new XSDImport[imports.size()]);
}
public XSDInclude[] getIncludes() {
Collection includes = find(XSDInclude.class);
return (XSDInclude[]) includes.toArray(new XSDInclude[includes.size()]);
}
public XSDElementDeclaration getElementDeclaration(QName qName) {
return (XSDElementDeclaration) lookup(getElementIndex(), qName);
//return (XSDElementDeclaration) getElementIndex().get(qName);
}
public XSDAttributeDeclaration getAttributeDeclaration(QName qName) {
return (XSDAttributeDeclaration) lookup(getAttributeIndex(), qName);
//return (XSDAttributeDeclaration) getAttributeIndex().get(qName);
}
public XSDAttributeGroupDefinition getAttributeGroupDefinition(QName qName) {
return (XSDAttributeGroupDefinition) lookup(getAttributeGroupIndex(), qName);
//return (XSDAttributeGroupDefinition) getAttributeGroupIndex().get(qName);
}
public XSDComplexTypeDefinition getComplexTypeDefinition(QName qName) {
return (XSDComplexTypeDefinition) lookup(getComplexTypeIndex(), qName);
//return (XSDComplexTypeDefinition) getComplexTypeIndex().get(qName);
}
public XSDSimpleTypeDefinition getSimpleTypeDefinition(QName qName) {
return (XSDSimpleTypeDefinition) lookup(getSimpleTypeIndex(), qName);
//return (XSDSimpleTypeDefinition) getSimpleTypeIndex().get(qName);
}
public XSDTypeDefinition getTypeDefinition(QName qName) {
XSDTypeDefinition type = getComplexTypeDefinition(qName);
if (type == null) {
type = getSimpleTypeDefinition(qName);
}
return type;
}
protected XSDNamedComponent lookup(Map index, QName qName) {
XSDNamedComponent component = (XSDNamedComponent) index.get(qName);
if (component != null) {
return component;
}
//check for namespace wildcard
if ("*".equals(qName.getNamespaceURI())) {
ArrayList matches = new ArrayList();
for (Iterator e = index.entrySet().iterator(); e.hasNext();) {
Map.Entry entry = (Map.Entry) e.next();
QName name = (QName) entry.getKey();
if (name.getLocalPart().equals(qName.getLocalPart())) {
matches.add(entry.getValue());
}
}
if (matches.size() == 1) {
return (XSDNamedComponent) matches.get(0);
}
}
return null;
}
protected OrderedMap children(XSDElementDeclaration parent) {
OrderedMap children = (OrderedMap) element2children.get(parent);
if (children == null) {
synchronized (this) {
if (children == null) {
children = new ListOrderedMap();
for (Iterator i = Schemas.getChildElementParticles(parent.getType(), true)
.iterator(); i.hasNext();) {
XSDParticle particle = (XSDParticle) i.next();
XSDElementDeclaration child = (XSDElementDeclaration) particle.getContent();
if (child.isElementDeclarationReference()) {
child = child.getResolvedElementDeclaration();
}
QName childName = null;
if (child.getTargetNamespace() != null) {
childName = new QName(child.getTargetNamespace(), child.getName());
} else if (parent.getTargetNamespace() != null) {
childName = new QName(parent.getTargetNamespace(), child.getName());
} else if (parent.getType().getTargetNamespace() != null) {
childName = new QName(parent.getType().getTargetNamespace(),
child.getName());
} else {
childName = new QName(null, child.getName());
}
children.put(childName, particle);
}
element2children.put(parent, children);
}
}
}
return children;
}
public XSDElementDeclaration getChildElement(XSDElementDeclaration parent, QName childName) {
OrderedMap children = (OrderedMap) children(parent);
XSDParticle particle = (XSDParticle) children.get(childName);
if (particle != null) {
XSDElementDeclaration child = (XSDElementDeclaration) particle.getContent();
if (child.isElementDeclarationReference()) {
child = child.getResolvedElementDeclaration();
}
return child;
}
if ("*".equals(childName.getNamespaceURI())) {
//do a check just on local name
ArrayList matches = new ArrayList();
for (Iterator e = children.entrySet().iterator(); e.hasNext();) {
Map.Entry entry = (Map.Entry) e.next();
QName name = (QName) entry.getKey();
if (name.getLocalPart().equals(childName.getLocalPart())) {
matches.add(entry.getValue());
}
}
if (matches.size() == 1) {
particle = (XSDParticle) matches.get(0);
XSDElementDeclaration child = (XSDElementDeclaration) particle.getContent();
if (child.isElementDeclarationReference()) {
child = child.getResolvedElementDeclaration();
}
return child;
}
}
return null;
}
public List getChildElementParticles(XSDElementDeclaration parent) {
return new ArrayList(children(parent).values());
}
public List getAttributes(XSDElementDeclaration element) {
List attributes = (List) element2attributes.get(element);
if (attributes == null) {
attributes = Schemas.getAttributeDeclarations(element);
element2attributes.put(element, attributes);
}
return Collections.unmodifiableList(attributes);
}
protected Collection find(Class c) {
ArrayList found = new ArrayList();
for (int i = 0; i < schemas.length; i++) {
XSDSchema schema = schemas[i];
List content = schema.getContents();
for (Iterator itr = content.iterator(); itr.hasNext();) {
Object o = itr.next();
if (c.isAssignableFrom(o.getClass())) {
found.add(o);
}
}
}
return found;
}
protected HashMap getElementIndex() {
if (elementIndex == null) {
synchronized (this) {
if (elementIndex == null) {
buildElementIndex();
}
}
}
return elementIndex;
}
protected HashMap getAttributeIndex() {
if (attributeIndex == null) {
synchronized (this) {
if (attributeIndex == null) {
buildAttriubuteIndex();
}
}
}
return attributeIndex;
}
protected HashMap getAttributeGroupIndex() {
if (attributeGroupIndex == null) {
synchronized (this) {
if (attributeGroupIndex == null) {
buildAttributeGroupIndex();
}
}
}
return attributeGroupIndex;
}
protected HashMap getComplexTypeIndex() {
if (complexTypeIndex == null) {
synchronized (this) {
if (complexTypeIndex == null) {
buildComplexTypeIndex();
}
}
}
return complexTypeIndex;
}
protected HashMap getSimpleTypeIndex() {
if (simpleTypeIndex == null) {
synchronized (this) {
if (simpleTypeIndex == null) {
buildSimpleTypeIndex();
}
}
}
return simpleTypeIndex;
}
protected void buildElementIndex() {
elementIndex = new HashMap();
for (int i = 0; i < schemas.length; i++) {
XSDSchema schema = schemas[i];
for (Iterator e = schema.getElementDeclarations().iterator(); e.hasNext();) {
XSDElementDeclaration element = (XSDElementDeclaration) e.next();
QName qName = new QName(element.getTargetNamespace(), element.getName());
elementIndex.put(qName, element);
}
}
}
protected void buildAttriubuteIndex() {
attributeIndex = new HashMap();
for (int i = 0; i < schemas.length; i++) {
XSDSchema schema = schemas[i];
for (Iterator a = schema.getAttributeDeclarations().iterator(); a.hasNext();) {
XSDAttributeDeclaration attribute = (XSDAttributeDeclaration) a.next();
QName qName = new QName(attribute.getTargetNamespace(), attribute.getName());
attributeIndex.put(qName, attribute);
}
}
}
protected void buildAttributeGroupIndex() {
attributeGroupIndex = new HashMap();
for (int i = 0; i < schemas.length; i++) {
XSDSchema schema = schemas[i];
for (Iterator g = schema.getAttributeGroupDefinitions().iterator(); g.hasNext();) {
XSDAttributeGroupDefinition group = (XSDAttributeGroupDefinition) g.next();
QName qName = new QName(group.getTargetNamespace(), group.getName());
attributeGroupIndex.put(qName, group);
}
}
}
protected void buildComplexTypeIndex() {
complexTypeIndex = new HashMap();
for (int i = 0; i < schemas.length; i++) {
XSDSchema schema = schemas[i];
for (Iterator t = schema.getTypeDefinitions().iterator(); t.hasNext();) {
XSDTypeDefinition type = (XSDTypeDefinition) t.next();
if (type instanceof XSDComplexTypeDefinition) {
QName qName = new QName(type.getTargetNamespace(), type.getName());
complexTypeIndex.put(qName, type);
}
}
}
}
protected void buildSimpleTypeIndex() {
simpleTypeIndex = new HashMap();
for (int i = 0; i < schemas.length; i++) {
XSDSchema schema = schemas[i];
for (Iterator t = schema.getTypeDefinitions().iterator(); t.hasNext();) {
XSDTypeDefinition type = (XSDTypeDefinition) t.next();
if (type instanceof XSDSimpleTypeDefinition) {
QName qName = new QName(type.getTargetNamespace(), type.getName());
simpleTypeIndex.put(qName, type);
}
}
}
}
class SchemaAdapter implements Adapter {
Notifier target;
Notification last;
public Notifier getTarget() {
return target;
}
public void setTarget(Notifier target) {
this.target = target;
}
public boolean isAdapterForType(Object object) {
return object instanceof XSDSchema;
}
public void notifyChanged(Notification notification) {
if (notification.getEventType() == Notification.ADD) {
switch (notification.getFeatureID(XSDSchema.class)) {
case XSDPackage.XSD_SCHEMA__ATTRIBUTE_DECLARATIONS:
synchronized (SchemaIndexImpl.this) {
attributeIndex = null;
}
break;
case XSDPackage.XSD_SCHEMA__ELEMENT_DECLARATIONS:
synchronized (SchemaIndexImpl.this) {
elementIndex = null;
}
break;
case XSDPackage.XSD_SCHEMA__TYPE_DEFINITIONS:
synchronized (SchemaIndexImpl.this) {
complexTypeIndex = null;
simpleTypeIndex = null;
}
break;
case XSDPackage.XSD_SCHEMA__ATTRIBUTE_GROUP_DEFINITIONS:
synchronized (SchemaIndexImpl.this) {
attributeGroupIndex = null;
}
break;
}
}
}
}
}