0001: /*
0002: * GeoTools - OpenSource mapping toolkit
0003: * http://geotools.org
0004: * (C) 2002-2006, GeoTools Project Managment Committee (PMC)
0005: *
0006: * This library is free software; you can redistribute it and/or
0007: * modify it under the terms of the GNU Lesser General Public
0008: * License as published by the Free Software Foundation;
0009: * version 2.1 of the License.
0010: *
0011: * This library is distributed in the hope that it will be useful,
0012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014: * Lesser General Public License for more details.
0015: */
0016: package org.geotools.xml;
0017:
0018: import java.io.File;
0019: import java.io.IOException;
0020: import java.lang.reflect.InvocationTargetException;
0021: import java.lang.reflect.Method;
0022: import java.net.MalformedURLException;
0023: import java.net.URL;
0024: import java.util.ArrayList;
0025: import java.util.Arrays;
0026: import java.util.HashSet;
0027: import java.util.Iterator;
0028: import java.util.LinkedList;
0029: import java.util.List;
0030: import java.util.Map;
0031: import java.util.Set;
0032: import java.util.logging.Level;
0033: import java.util.logging.Logger;
0034:
0035: import javax.xml.namespace.QName;
0036:
0037: import org.eclipse.emf.common.notify.Adapter;
0038: import org.eclipse.emf.common.notify.AdapterFactory;
0039: import org.eclipse.emf.common.notify.Notifier;
0040: import org.eclipse.emf.common.notify.impl.AdapterFactoryImpl;
0041: import org.eclipse.emf.common.notify.impl.AdapterImpl;
0042: import org.eclipse.emf.common.util.URI;
0043: import org.eclipse.emf.ecore.resource.Resource;
0044: import org.eclipse.emf.ecore.resource.ResourceSet;
0045: import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
0046: import org.eclipse.xsd.XSDAttributeDeclaration;
0047: import org.eclipse.xsd.XSDAttributeGroupContent;
0048: import org.eclipse.xsd.XSDAttributeGroupDefinition;
0049: import org.eclipse.xsd.XSDAttributeUse;
0050: import org.eclipse.xsd.XSDComplexTypeDefinition;
0051: import org.eclipse.xsd.XSDElementDeclaration;
0052: import org.eclipse.xsd.XSDImport;
0053: import org.eclipse.xsd.XSDInclude;
0054: import org.eclipse.xsd.XSDModelGroup;
0055: import org.eclipse.xsd.XSDModelGroupDefinition;
0056: import org.eclipse.xsd.XSDNamedComponent;
0057: import org.eclipse.xsd.XSDParticle;
0058: import org.eclipse.xsd.XSDSchema;
0059: import org.eclipse.xsd.XSDSchemaContent;
0060: import org.eclipse.xsd.XSDSimpleTypeDefinition;
0061: import org.eclipse.xsd.XSDTypeDefinition;
0062: import org.eclipse.xsd.util.XSDConstants;
0063: import org.eclipse.xsd.util.XSDResourceFactoryImpl;
0064: import org.eclipse.xsd.util.XSDResourceImpl;
0065: import org.eclipse.xsd.util.XSDSchemaLocationResolver;
0066: import org.eclipse.xsd.util.XSDSchemaLocator;
0067: import org.eclipse.xsd.util.XSDUtil;
0068:
0069: import org.geotools.resources.Utilities;
0070: import org.geotools.xml.impl.SchemaIndexImpl;
0071: import org.geotools.xml.impl.TypeWalker;
0072: import org.geotools.xml.impl.TypeWalker.Visitor;
0073: import org.picocontainer.ComponentAdapter;
0074: import org.picocontainer.MutablePicoContainer;
0075: import org.picocontainer.Parameter;
0076: import org.picocontainer.PicoContainer;
0077: import org.picocontainer.PicoVisitor;
0078:
0079: /**
0080: * Utility class for performing various opreations.
0081: *
0082: * @author Justin Deoliveira, The Open Planning Project
0083: *
0084: */
0085: public class Schemas {
0086:
0087: private static final Logger LOGGER = org.geotools.util.logging.Logging
0088: .getLogger(Schemas.class.getPackage().getName());
0089:
0090: static {
0091: //need to register custom factory to load schema resources
0092: Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap()
0093: .put("xsd", new XSDResourceFactoryImpl());
0094: }
0095:
0096: /**
0097: * Finds all the XSDSchemas used by the {@link Configuration configuration}
0098: * by looking at the configuration's schema locator and its dependencies.
0099: *
0100: * @param configuration the {@link Configuration} for which to find all its
0101: * related schemas
0102: *
0103: * @return a {@link SchemaIndex} holding the schemas related to
0104: * <code>configuration</code>
0105: */
0106: public static final SchemaIndex findSchemas(
0107: Configuration configuration) {
0108:
0109: Set configurations = new HashSet(configuration
0110: .allDependencies());
0111: configurations.add(configuration);
0112:
0113: List resolvedSchemas = new ArrayList(configurations.size());
0114:
0115: for (Iterator it = configurations.iterator(); it.hasNext();) {
0116: Configuration conf = (Configuration) it.next();
0117: if (LOGGER.isLoggable(Level.FINE))
0118: LOGGER.fine("looking up schema for "
0119: + conf.getNamespaceURI());
0120:
0121: XSDSchemaLocator locator = conf.getSchemaLocator();
0122: if (locator == null) {
0123: if (LOGGER.isLoggable(Level.FINE))
0124: LOGGER.fine("No schema locator for "
0125: + conf.getNamespaceURI());
0126: continue;
0127: }
0128: String namespaceURI = conf.getNamespaceURI();
0129: String schemaLocation = null;
0130: try {
0131: URL location = new URL(conf.getSchemaFileURL());
0132: schemaLocation = location.toExternalForm();
0133: } catch (MalformedURLException e) {
0134: throw new RuntimeException(e);
0135: }
0136: LOGGER.fine("schema location: " + schemaLocation);
0137: XSDSchema schema = locator.locateSchema(null, namespaceURI,
0138: schemaLocation, null);
0139: if (schema != null) {
0140: resolvedSchemas.add(schema);
0141: }
0142: }
0143:
0144: XSDSchema[] schemas = (XSDSchema[]) resolvedSchemas
0145: .toArray(new XSDSchema[resolvedSchemas.size()]);
0146: SchemaIndex index = new SchemaIndexImpl(schemas);
0147:
0148: return index;
0149: }
0150:
0151: /**
0152: * Finds all {@link XSDSchemaLocationResolver}'s used by the configuration.
0153: *
0154: * @param configuration The parser configuration.
0155: *
0156: * @return A list of location resolvers, empty if none found.
0157: */
0158: public static List findSchemaLocationResolvers(
0159: Configuration configuration) {
0160: List all = configuration.allDependencies();
0161: List resolvers = new ArrayList();
0162: for (Iterator c = all.iterator(); c.hasNext();) {
0163: configuration = (Configuration) c.next();
0164: XSDSchemaLocationResolver resolver = configuration
0165: .getSchemaLocationResolver();
0166: if (resolver != null) {
0167: resolvers.add(resolver);
0168: }
0169: }
0170:
0171: return resolvers;
0172: }
0173:
0174: /**
0175: * Parses a schema at the specified location.
0176: *
0177: * @param location A uri pointing to the location of the schema.
0178: *
0179: * @return The parsed schema, or null if the schema could not be parsed.
0180: *
0181: * @throws IOException In the event of a schema parsing error.
0182: */
0183: public static final XSDSchema parse(String location)
0184: throws IOException {
0185: return parse(location, (List) null, (List) null);
0186: }
0187:
0188: /**
0189: * Parses a schema at the specified location.
0190: *
0191: * @param location A uri pointing to the location of the schema.
0192: * @param locators An array of schema locator objects to be used when
0193: * parsing imports/includes of the main schema.
0194: * @param resolvers An array of schema location resolvers used to override
0195: * schema locations encountered in an instance document or an imported
0196: * schema.
0197: *
0198: * @return The parsed schema, or null if the schema could not be parsed.
0199: *
0200: * @throws IOException In the event of a schema parsing error.
0201: */
0202: public static final XSDSchema parse(String location,
0203: XSDSchemaLocator[] locators,
0204: XSDSchemaLocationResolver[] resolvers) throws IOException {
0205:
0206: return parse(location, locators != null ? Arrays
0207: .asList(locators) : (List) null,
0208: resolvers != null ? Arrays.asList(resolvers)
0209: : (List) null);
0210: }
0211:
0212: public static final XSDSchema parse(String location, List locators,
0213: List resolvers) throws IOException {
0214:
0215: //check for case of file url, make sure it is an absolute reference
0216: if (new File(location).exists()) {
0217: location = new File(location).getCanonicalFile().toURI()
0218: .toString();
0219: //location = new File(location).getCanonicalPath();
0220: }
0221:
0222: URI uri = URI.createURI(location);
0223: final ResourceSet resourceSet = new ResourceSetImpl();
0224:
0225: //add the specialized schema location resolvers
0226: if ((resolvers != null) && !resolvers.isEmpty()) {
0227: AdapterFactory adapterFactory = new SchemaLocationResolverAdapterFactory(
0228: resolvers);
0229: resourceSet.getAdapterFactories().add(adapterFactory);
0230: }
0231:
0232: //add the specialized schema locators as adapters
0233: if ((locators != null) && !locators.isEmpty()) {
0234: AdapterFactory adapterFactory = new SchemaLocatorAdapterFactory(
0235: locators);
0236: resourceSet.getAdapterFactories().add(adapterFactory);
0237: }
0238:
0239: XSDResourceImpl xsdMainResource = (XSDResourceImpl) resourceSet
0240: .createResource(URI.createURI(".xsd"));
0241: // LOGGER.info("About to parse xsd schema: " + uri);
0242: xsdMainResource.setURI(uri);
0243: xsdMainResource.load(resourceSet.getLoadOptions());
0244:
0245: return xsdMainResource.getSchema();
0246: }
0247:
0248: /**
0249: * Returns a list of all child element declarations of the specified
0250: * element, no order is guaranteed.
0251: *
0252: * @param element The parent element.
0253: *
0254: * @return A list of @link XSDElementDeclaration objects, one for each
0255: * child element.
0256: *
0257: * @deprecated use {@link #getChildElementDeclarations(XSDTypeDefinition)}
0258: */
0259: public static final List getChildElementDeclarations(
0260: XSDElementDeclaration element) {
0261: return getChildElementDeclarations(element.getType());
0262: }
0263:
0264: /**
0265: * Returns a list of all child element declarations of the specified
0266: * type, no order is guaranteed.
0267: *
0268: * @param type The type.
0269: *
0270: * @return A list of @link XSDElementDeclaration objects, one for each
0271: * child element.
0272: */
0273: public static final List getChildElementDeclarations(
0274: XSDTypeDefinition type) {
0275: return getChildElementDeclarations(type, true);
0276: }
0277:
0278: /**
0279: * Returns a list of all child element declarations of the specified
0280: * element.
0281: * <p>
0282: * The <code>includeParents</code> flag controls if this method should
0283: * returns those elements defined on parent types.
0284: * </p>
0285: * @param element The parent element.
0286: * @param includeParents Flag indicating if parent types should be processed.
0287: * @return A list of @link XSDElementDeclaration objects, one for each
0288: * child element.
0289: *
0290: * @deprecated use {@link #getChildElementDeclarations(XSDTypeDefinition, boolean)}
0291: */
0292: public static final List getChildElementDeclarations(
0293: XSDElementDeclaration element, boolean includeParents) {
0294: return getChildElementDeclarations(element.getType(),
0295: includeParents);
0296: }
0297:
0298: /**
0299: * Returns the particle for an element declaration that is part of a type.
0300: *
0301: * @param type The type definition.
0302: * @param name The naem of the child element declaration.
0303: *
0304: * @param includeParents Flag to control wether parent types are included.
0305: *
0306: * @return The particle representing the element declaration, or <code>null</code> if it could
0307: * not be found.
0308: */
0309: public static final XSDParticle getChildElementParticle(
0310: XSDTypeDefinition type, String name, boolean includeParents) {
0311: List particles = getChildElementParticles(type, includeParents);
0312: for (Iterator p = particles.iterator(); p.hasNext();) {
0313: XSDParticle particle = (XSDParticle) p.next();
0314: XSDElementDeclaration element = (XSDElementDeclaration) particle
0315: .getContent();
0316: if (element.isElementDeclarationReference()) {
0317: element.getResolvedElementDeclaration();
0318: }
0319:
0320: if (name.equals(element.getName())) {
0321: return particle;
0322: }
0323: }
0324:
0325: return null;
0326: }
0327:
0328: /**
0329: * Returns a list of all child element particles that corresponde to element declarations of
0330: * the specified type, no order is guaranteed.
0331: * <p>
0332: * The <code>includeParents</code> flag controls if this method should
0333: * returns those elements defined on parent types.
0334: * </p>
0335: *
0336: * @param type THe type.
0337: * @param includeParents flag indicating if parent types should be processed
0338: *
0339: * @return A list of {@link XSDParticle}.
0340: *
0341: */
0342: public static final List getChildElementParticles(
0343: XSDTypeDefinition type, boolean includeParents) {
0344: final HashSet contents = new HashSet();
0345: final ArrayList particles = new ArrayList();
0346: TypeWalker.Visitor visitor = new TypeWalker.Visitor() {
0347: public boolean visit(XSDTypeDefinition type) {
0348: //simple types dont have children
0349: if (type instanceof XSDSimpleTypeDefinition) {
0350: return true;
0351: }
0352:
0353: XSDComplexTypeDefinition cType = (XSDComplexTypeDefinition) type;
0354:
0355: ElementVisitor visitor = new ElementVisitor() {
0356: public void visit(XSDParticle particle) {
0357: XSDElementDeclaration element = (XSDElementDeclaration) particle
0358: .getContent();
0359: if (element.isElementDeclarationReference()) {
0360: element = element
0361: .getResolvedElementDeclaration();
0362: }
0363:
0364: //make sure unique
0365: if (!contents.contains(element)) {
0366: contents.add(element);
0367: particles.add(particle);
0368: }
0369: }
0370: };
0371:
0372: visitElements(cType, visitor);
0373:
0374: return true;
0375: }
0376: };
0377:
0378: if (includeParents) {
0379: //walk up the type hierarchy of the element to generate a list of
0380: // possible elements
0381: new TypeWalker().rwalk(type, visitor);
0382: } else {
0383: //just visit this type
0384: visitor.visit(type);
0385: }
0386:
0387: return new ArrayList(particles);
0388:
0389: }
0390:
0391: /**
0392: * Returns a list of all child element declarations of the specified
0393: * type, no order is guaranteed.
0394: * <p>
0395: * The <code>includeParents</code> flag controls if this method should
0396: * returns those elements defined on parent types.
0397: * </p>
0398: * @param type The type
0399: * @param includeParents flag indicating if parent types should be processed
0400: *
0401: * @return A list of @link XSDElementDeclaration objects, one for each
0402: * child element.
0403: */
0404: public static final List getChildElementDeclarations(
0405: XSDTypeDefinition type, boolean includeParents) {
0406:
0407: List particles = getChildElementParticles(type, includeParents);
0408: List elements = new ArrayList();
0409: for (Iterator p = particles.iterator(); p.hasNext();) {
0410: XSDParticle particle = (XSDParticle) p.next();
0411: XSDElementDeclaration decl = (XSDElementDeclaration) particle
0412: .getContent();
0413:
0414: if (decl.isElementDeclarationReference()) {
0415: decl = decl.getResolvedElementDeclaration();
0416: }
0417:
0418: elements.add(decl);
0419: }
0420:
0421: return elements;
0422: }
0423:
0424: /**
0425: * Returns the base type defintion of <code>type</code> named <code>parentTypeName<code>.
0426: * <p>
0427: * This method will handle the case in which the <code>parentTypeName == type.getTypeName()</code>.
0428: * If no such parent type is found this method will return <code>null</code>.
0429: * </p>
0430: * @param type The type.
0431: * @param parentTypeName The name of the base type to return.
0432: *
0433: * @return The base type, or null if it could not be found.
0434: */
0435: public static final XSDTypeDefinition getBaseTypeDefinition(
0436: XSDTypeDefinition type, final QName parentTypeName) {
0437:
0438: final List found = new ArrayList();
0439:
0440: TypeWalker.Visitor visitor = new TypeWalker.Visitor() {
0441: public boolean visit(XSDTypeDefinition type) {
0442: if (nameMatches(type, parentTypeName)) {
0443: found.add(type);
0444: return false;
0445: }
0446:
0447: return true;
0448: }
0449: };
0450: new TypeWalker().walk(type, visitor);
0451:
0452: return found.isEmpty() ? null : (XSDTypeDefinition) found
0453: .get(0);
0454: }
0455:
0456: /**
0457: * Returns the minimum number of occurences of an element within a complex
0458: * type.
0459: *
0460: * @param type The type definition containg the declaration <code>element</code>
0461: * @param element The declaration of the element.
0462: *
0463: * @return The minimum number of occurences.
0464: *
0465: * @throws IllegalArgumentException If the element declaration cannot be
0466: * locaated withing the type definition.
0467: */
0468: public static final int getMinOccurs(XSDComplexTypeDefinition type,
0469: XSDElementDeclaration element) {
0470: final XSDElementDeclaration fElement = element;
0471: final ArrayList minOccurs = new ArrayList();
0472:
0473: ElementVisitor visitor = new ElementVisitor() {
0474: public void visit(XSDParticle particle) {
0475: XSDElementDeclaration decl = (XSDElementDeclaration) particle
0476: .getContent();
0477:
0478: if (decl.isElementDeclarationReference()) {
0479: decl = decl.getResolvedElementDeclaration();
0480: }
0481:
0482: if (decl == fElement) {
0483: minOccurs.add(new Integer(particle.getMinOccurs()));
0484: }
0485: }
0486: };
0487:
0488: visitElements(type, visitor, true);
0489:
0490: if (minOccurs.isEmpty()) {
0491: throw new IllegalArgumentException("Element: " + element
0492: + " not found in type: " + type);
0493: }
0494:
0495: return ((Integer) minOccurs.get(0)).intValue();
0496: }
0497:
0498: /**
0499: * Returns the minimum number of occurences of an element within a complex
0500: * type.
0501: *
0502: * @param type The type definition containg the declaration <code>element</code>
0503: * @param element The declaration of the element.
0504: *
0505: * @return The minimum number of occurences.
0506: *
0507: * @throws IllegalArgumentException If the element declaration cannot be
0508: * locaated withing the type definition.
0509: */
0510: public static final int getMaxOccurs(XSDComplexTypeDefinition type,
0511: XSDElementDeclaration element) {
0512: final XSDElementDeclaration fElement = element;
0513: final ArrayList maxOccurs = new ArrayList();
0514:
0515: ElementVisitor visitor = new ElementVisitor() {
0516: public void visit(XSDParticle particle) {
0517: XSDElementDeclaration decl = (XSDElementDeclaration) particle
0518: .getContent();
0519:
0520: if (decl.isElementDeclarationReference()) {
0521: decl = decl.getResolvedElementDeclaration();
0522: }
0523:
0524: if (decl == fElement) {
0525: maxOccurs.add(new Integer(particle.getMaxOccurs()));
0526: }
0527: }
0528: };
0529:
0530: visitElements(type, visitor, true);
0531:
0532: if (maxOccurs.isEmpty()) {
0533: throw new IllegalArgumentException("Element: " + element
0534: + " not found in type: " + type);
0535: }
0536:
0537: return ((Integer) maxOccurs.get(0)).intValue();
0538: }
0539:
0540: private static void visitElements(XSDComplexTypeDefinition cType,
0541: ElementVisitor visitor, boolean includeParents) {
0542: if (includeParents) {
0543:
0544: LinkedList baseTypes = new LinkedList();
0545: XSDTypeDefinition baseType = cType.getBaseType();
0546: while (baseType != null
0547: && baseType != baseType.getBaseType()) {
0548: if (baseType instanceof XSDComplexTypeDefinition) {
0549: baseTypes.addLast(baseType);
0550: }
0551: baseType = baseType.getBaseType();
0552: }
0553:
0554: for (Iterator it = baseTypes.iterator(); it.hasNext();) {
0555: baseType = (XSDTypeDefinition) it.next();
0556: visitElements((XSDComplexTypeDefinition) baseType,
0557: visitor);
0558: }
0559: }
0560: visitElements(cType, visitor);
0561: }
0562:
0563: private static void visitElements(XSDComplexTypeDefinition cType,
0564: ElementVisitor visitor) {
0565: //simple content cant define children
0566: if ((cType.getContent() == null)
0567: || (cType.getContent() instanceof XSDSimpleTypeDefinition)) {
0568: return;
0569: }
0570:
0571: //use a queue to simulate the recursion
0572: LinkedList queue = new LinkedList();
0573: queue.addLast(cType.getContent());
0574:
0575: while (!queue.isEmpty()) {
0576: XSDParticle particle = (XSDParticle) queue.removeFirst();
0577:
0578: //analyze type of particle content
0579: int pType = XSDUtil.nodeType(particle.getElement());
0580:
0581: if (pType == XSDConstants.ELEMENT_ELEMENT) {
0582: visitor.visit(particle);
0583: } else {
0584: //model group
0585: XSDModelGroup grp = null;
0586:
0587: switch (pType) {
0588: case XSDConstants.GROUP_ELEMENT:
0589:
0590: XSDModelGroupDefinition grpDef = (XSDModelGroupDefinition) particle
0591: .getContent();
0592:
0593: if (grpDef.isModelGroupDefinitionReference()) {
0594: grpDef = grpDef
0595: .getResolvedModelGroupDefinition();
0596: }
0597:
0598: grp = grpDef.getModelGroup();
0599:
0600: break;
0601:
0602: case XSDConstants.CHOICE_ELEMENT:
0603: case XSDConstants.ALL_ELEMENT:
0604: case XSDConstants.SEQUENCE_ELEMENT:
0605: grp = (XSDModelGroup) particle.getContent();
0606:
0607: break;
0608: }
0609:
0610: if (grp != null) {
0611: //enque all particles in the group
0612: List parts = grp.getParticles();
0613:
0614: for (Iterator itr = parts.iterator(); itr.hasNext();) {
0615: queue.addLast(itr.next());
0616: }
0617: }
0618: }
0619: } //while
0620: }
0621:
0622: /**
0623: * Returns an element declaration that is contained in the type of another
0624: * element declaration. The following strategy is used to locate the child
0625: * element declaration.
0626: *
0627: * <ol>
0628: * <li>The immediate children of the specified element are examined, if a
0629: * match is found, it is returned.
0630: * </li>If 1. does not match, global elements that derive from the
0631: * immediate children are examined.
0632: * </ol>
0633: *
0634: * @param parent the containing element declaration
0635: * @param qName the qualified name of the contained element
0636: *
0637: * @return The contained element declaration, or false if containment is
0638: * not satisfied.
0639: */
0640: public static final XSDElementDeclaration getChildElementDeclaration(
0641: XSDElementDeclaration parent, QName qName) {
0642: //look for a match in a direct child
0643: List children = getChildElementDeclarations(parent);
0644:
0645: for (Iterator itr = children.iterator(); itr.hasNext();) {
0646: XSDElementDeclaration element = (XSDElementDeclaration) itr
0647: .next();
0648:
0649: if (nameMatches(element, qName)) {
0650: return element;
0651: }
0652: }
0653:
0654: //couldn't find one, look for match in derived elements
0655: ArrayList derived = new ArrayList();
0656:
0657: for (Iterator itr = children.iterator(); itr.hasNext();) {
0658: XSDElementDeclaration child = (XSDElementDeclaration) itr
0659: .next();
0660: derived.addAll(getDerivedElementDeclarations(child));
0661: }
0662:
0663: for (Iterator itr = derived.iterator(); itr.hasNext();) {
0664: XSDElementDeclaration child = (XSDElementDeclaration) itr
0665: .next();
0666:
0667: if (nameMatches(child, qName)) {
0668: return child;
0669: }
0670: }
0671:
0672: return null;
0673: }
0674:
0675: /**
0676: * Returns a list of all top level elements that are of a type derived
0677: * from the type of the specified element.
0678: *
0679: * @param element The element.
0680: *
0681: * @return All elements which are of a type derived from the type of the
0682: * specified element.
0683: */
0684: public static final List getDerivedElementDeclarations(
0685: XSDElementDeclaration element) {
0686: List elements = element.getSchema().getElementDeclarations();
0687: List derived = new ArrayList();
0688:
0689: for (Iterator itr = elements.iterator(); itr.hasNext();) {
0690: XSDElementDeclaration derivee = (XSDElementDeclaration) itr
0691: .next();
0692:
0693: if (derivee.equals(element)) {
0694: continue; //same element
0695: }
0696:
0697: XSDTypeDefinition type = derivee.getType();
0698:
0699: while (true) {
0700: if (type.equals(element.getType())) {
0701: derived.add(derivee);
0702:
0703: break;
0704: }
0705:
0706: if (type.equals(type.getBaseType())) {
0707: break;
0708: }
0709:
0710: type = type.getBaseType();
0711: }
0712: }
0713:
0714: return derived;
0715: }
0716:
0717: /**
0718: * Returns a list of all attribute declarations declared in the type (or
0719: * any base type) of the specified element.
0720: *
0721: * <p>
0722: * This method is just a shortcut for {@link #getAttributeDeclarations(XSDTypeDefinition) getAttributeDeclarations(element.getType()}
0723: * </p>
0724: *
0725: * @param element The element.
0726: *
0727: * @return A list of @link XSDAttributeDeclaration objects, one for each
0728: * attribute of the element.
0729: */
0730: public static final List getAttributeDeclarations(
0731: XSDElementDeclaration element) {
0732: return getAttributeDeclarations(element.getType());
0733: }
0734:
0735: /**
0736: * Returns a list of all attribute declarations declared in the type (or
0737: * any base type) of the specified element.
0738: *
0739: * @param element The element.
0740: *
0741: * @return A list of @link XSDAttributeDeclaration objects, one for each
0742: * attribute of the element.
0743: */
0744: public static final List getAttributeDeclarations(
0745: XSDTypeDefinition type) {
0746: final ArrayList attributes = new ArrayList();
0747:
0748: //walk up the type hierarchy of the element to generate a list of atts
0749: TypeWalker.Visitor visitor = new TypeWalker.Visitor() {
0750: public boolean visit(XSDTypeDefinition type) {
0751: //simple types dont have attributes
0752: if (type instanceof XSDSimpleTypeDefinition) {
0753: return true;
0754: }
0755:
0756: XSDComplexTypeDefinition cType = (XSDComplexTypeDefinition) type;
0757:
0758: //get all the attribute content (groups,or uses) and add to q
0759: List attContent = cType.getAttributeContents();
0760:
0761: for (Iterator itr = attContent.iterator(); itr
0762: .hasNext();) {
0763: XSDAttributeGroupContent content = (XSDAttributeGroupContent) itr
0764: .next();
0765:
0766: if (content instanceof XSDAttributeUse) {
0767: //an attribute, add it to the list
0768: XSDAttributeUse use = (XSDAttributeUse) content;
0769: attributes.add(use.getAttributeDeclaration());
0770: } else if (content instanceof XSDAttributeGroupDefinition) {
0771: //attribute group, add all atts in group to list
0772: XSDAttributeGroupDefinition attGrp = (XSDAttributeGroupDefinition) content;
0773:
0774: if (attGrp
0775: .isAttributeGroupDefinitionReference()) {
0776: attGrp = attGrp
0777: .getResolvedAttributeGroupDefinition();
0778: }
0779:
0780: List uses = attGrp.getAttributeUses();
0781:
0782: for (Iterator aitr = uses.iterator(); aitr
0783: .hasNext();) {
0784: XSDAttributeUse use = (XSDAttributeUse) aitr
0785: .next();
0786: attributes.add(use
0787: .getAttributeDeclaration());
0788: }
0789: }
0790: }
0791:
0792: return true;
0793: }
0794: };
0795:
0796: new TypeWalker().walk(type, visitor);
0797:
0798: return attributes;
0799: }
0800:
0801: /**
0802: * Returns an attribute declaration that is contained in the type of another
0803: * element declaration.
0804: *
0805: * @param element The containing element declaration.
0806: * @param qName The qualified name of the contained attribute
0807: *
0808: * @return The contained attribute declaration, or false if containment is
0809: * not satisfied.
0810: */
0811: public static final XSDAttributeDeclaration getAttributeDeclaration(
0812: XSDElementDeclaration element, QName qName) {
0813: List atts = getAttributeDeclarations(element);
0814:
0815: for (Iterator itr = atts.iterator(); itr.hasNext();) {
0816: XSDAttributeDeclaration att = (XSDAttributeDeclaration) itr
0817: .next();
0818:
0819: if (nameMatches(att, qName)) {
0820: return att;
0821: }
0822: }
0823:
0824: return null;
0825: }
0826:
0827: /**
0828: * Returns a flat list of imports from the specified schema.
0829: * <p>
0830: * The method recurses into imported schemas. The list returned is filtered so that
0831: * duplicate includes are removed. Two includes are considered equal if they have the same
0832: * target namespace.
0833: * </p>
0834: *
0835: * @param schema The top-level schema.
0836: *
0837: * @return A list containing objects of type {@link XSDImport}.
0838: */
0839: public static final List getImports(XSDSchema schema) {
0840: LinkedList queue = new LinkedList();
0841: ArrayList imports = new ArrayList();
0842: HashSet added = new HashSet();
0843:
0844: queue.addLast(schema);
0845:
0846: while (!queue.isEmpty()) {
0847: schema = (XSDSchema) queue.removeFirst();
0848:
0849: List contents = schema.getContents();
0850:
0851: for (Iterator itr = contents.iterator(); itr.hasNext();) {
0852: XSDSchemaContent content = (XSDSchemaContent) itr
0853: .next();
0854:
0855: if (content instanceof XSDImport) {
0856: XSDImport imprt = (XSDImport) content;
0857:
0858: if (!added.contains(imprt.getNamespace())) {
0859: imports.add(imprt);
0860: added.add(imprt.getNamespace());
0861:
0862: queue.addLast(imprt.getResolvedSchema());
0863: }
0864: }
0865: }
0866: }
0867:
0868: return imports;
0869: }
0870:
0871: /**
0872: * Returns a flat list of includes from the specified schema.
0873: * <p>
0874: * The method recurses into included schemas. The list returned is filtered so that
0875: * duplicate includes are removed. Two includes are considered equal if they have the same
0876: * uri location
0877: * </p>
0878: *
0879: * @param schema The top-level schema.
0880: *
0881: * @return A list containing objects of type {@link XSDInclude}.
0882: */
0883: public static final List getIncludes(XSDSchema schema) {
0884: LinkedList queue = new LinkedList();
0885: ArrayList includes = new ArrayList();
0886: HashSet added = new HashSet();
0887:
0888: queue.addLast(schema);
0889:
0890: while (!queue.isEmpty()) {
0891: schema = (XSDSchema) queue.removeFirst();
0892: List contents = schema.getContents();
0893:
0894: for (Iterator itr = contents.iterator(); itr.hasNext();) {
0895: XSDSchemaContent content = (XSDSchemaContent) itr
0896: .next();
0897:
0898: if (content instanceof XSDInclude) {
0899: XSDInclude include = (XSDInclude) content;
0900:
0901: if (!added.contains(include.getSchemaLocation())) {
0902:
0903: XSDSchema incorporated = include
0904: .getIncorporatedSchema();
0905: if (incorporated != null) {
0906: includes.add(include);
0907: added.add(include.getSchemaLocation());
0908: queue.addLast(incorporated);
0909: } else {
0910: String msg = "Could not find included schema: "
0911: + include.getSchemaLocation();
0912: LOGGER.warning(msg);
0913: }
0914: }
0915: }
0916: }
0917: }
0918:
0919: return includes;
0920: }
0921:
0922: /**
0923: * Searches <code>schema</code> for an element which matches <code>name</code>.
0924: *
0925: * @param schema The schema
0926: * @param name The element to search for
0927: *
0928: * @return The element declaration, or null if it could not be found.
0929: */
0930: public static XSDElementDeclaration getElementDeclaration(
0931: XSDSchema schema, QName name) {
0932: for (Iterator e = schema.getElementDeclarations().iterator(); e
0933: .hasNext();) {
0934: XSDElementDeclaration element = (XSDElementDeclaration) e
0935: .next();
0936: if (element.getTargetNamespace().equals(
0937: name.getNamespaceURI())) {
0938: if (element.getName().equals(name.getLocalPart()))
0939: return element;
0940: }
0941: }
0942:
0943: return null;
0944: }
0945:
0946: /**
0947: * Method for comparing the name of a schema component to a qualified name.
0948: * The component name and the qualified name match if both the namespaces
0949: * match, and the local parts match. Prefixes are ignored. Two strings will
0950: * match if one of the following conditions hold.
0951: *
0952: * <ul>
0953: * <li>Both strings are null.
0954: * <li>Both strings are the empty string.
0955: * <li>One string is null, and the other is the empty string.
0956: * <li>Both strings are non-null and non-empty and equals() return true.
0957: * </ul>
0958: *
0959: * @param component The component in question.
0960: * @param qName The qualifined name.
0961: *
0962: */
0963: public static final boolean nameMatches(
0964: XSDNamedComponent component, QName qName) {
0965: String ns1 = component.getTargetNamespace();
0966: String ns2 = qName.getNamespaceURI();
0967: String n1 = component.getName();
0968: String n2 = qName.getLocalPart();
0969:
0970: ns1 = "".equals(ns1) ? null : ns1;
0971: ns2 = "".equals(ns2) ? null : ns2;
0972: n1 = "".equals(n1) ? null : n1;
0973: n2 = "".equals(n2) ? null : n2;
0974:
0975: if (ns1 == null && ns2 != null) {
0976: //try the default namespace
0977: if (component.getSchema() != null) {
0978: ns1 = component.getSchema().getTargetNamespace();
0979: if ("".equals(ns1)) {
0980: ns1 = null;
0981: }
0982: }
0983: }
0984:
0985: return Utilities.equals(ns1, ns2) && Utilities.equals(n1, n2);
0986: //
0987: // //is this the element we are looking for
0988: // if ((component.getTargetNamespace() == null)
0989: // || "".equals(component.getTargetNamespace())) {
0990: // if ((qName.getNamespaceURI() == null)
0991: // || "".equals(qName.getNamespaceURI())) {
0992: // //do a local name match
0993: // String n1 = component.getName();
0994: // if ( "".equals( n1 ) ) {
0995: // n1 = null;
0996: // }
0997: // String n2 = qName.getLocalPart();
0998: // if ( "".equals( n2 ) ) {
0999: // n2 = null;
1000: // }
1001: // return (n1 == null && n2 == null) || n1.equals( n2 );
1002: // }
1003: //
1004: // //assume default namespace
1005: // if (component.getSchema().getTargetNamespace()
1006: // .equals(qName.getNamespaceURI())
1007: // && component.getName().equals(qName.getLocalPart())) {
1008: // return true;
1009: // }
1010: // } else if (component.getTargetNamespace().equals(qName.getNamespaceURI())
1011: // && component.getName().equals(qName.getLocalPart())) {
1012: // return true;
1013: // }
1014:
1015: }
1016:
1017: /**
1018: * Returns the namespace prefix mapped to the targetNamespace of the schema.
1019: *
1020: * @param schema The schema in question.
1021: *
1022: * @return The namesapce prefix, or <code>null</code> if not found.
1023: */
1024: public static String getTargetPrefix(XSDSchema schema) {
1025: String ns = schema.getTargetNamespace();
1026: Map pre2ns = schema.getQNamePrefixToNamespaceMap();
1027:
1028: for (Iterator itr = pre2ns.entrySet().iterator(); itr.hasNext();) {
1029: Map.Entry entry = (Map.Entry) itr.next();
1030: if (entry.getKey() == null)
1031: continue; //default prefix
1032:
1033: if (entry.getValue().equals(ns)) {
1034: return (String) entry.getKey();
1035: }
1036: }
1037:
1038: return null;
1039: }
1040:
1041: /**
1042: * Obtains all instances of a particular class from a container by navigating
1043: * up the container hierachy.
1044: *
1045: * @param container The container.
1046: * @param clazz The class.
1047: *
1048: * @return A list of all instances of <code>clazz</code>, or the empty list if none found.
1049: */
1050: public static List getComponentInstancesOfType(
1051: PicoContainer container, Class clazz) {
1052: List instances = new ArrayList();
1053: while (container != null) {
1054: List l = container.getComponentInstancesOfType(clazz);
1055: instances.addAll(l);
1056: container = container.getParent();
1057: }
1058:
1059: return instances;
1060: }
1061:
1062: /**
1063: * Unregisters a component in the container and all parent containers.
1064: *
1065: * @param container The container.
1066: * @param key The key of the component.
1067: *
1068: */
1069: public static void unregisterComponent(PicoContainer container,
1070: final Object key) {
1071: //go to the top of the hierachy
1072: while (container.getParent() != null) {
1073: container = container.getParent();
1074: }
1075:
1076: container.accept(new PicoVisitor() {
1077: public Object traverse(Object node) {
1078: return null;
1079: }
1080:
1081: public void visitContainer(PicoContainer container) {
1082: if (container instanceof MutablePicoContainer) {
1083: ((MutablePicoContainer) container)
1084: .unregisterComponent(key);
1085: }
1086:
1087: }
1088:
1089: public void visitComponentAdapter(ComponentAdapter adapter) {
1090: }
1091:
1092: public void visitParameter(Parameter parameter) {
1093: }
1094:
1095: });
1096: }
1097:
1098: /**
1099: * Simple visitor interface for visiting elements which are part of
1100: * complex types. This interface is private api because there is probably
1101: * a better way of visiting the contents of a type.
1102: *
1103: * @author Justin Deoliveira, The Open Planning Project, jdeolive@openplans.org
1104: *
1105: */
1106: private static interface ElementVisitor {
1107: /**
1108: * The particle containing the element.
1109: * @param element
1110: */
1111: void visit(XSDParticle element);
1112: }
1113:
1114: static class SchemaLocatorAdapterFactory extends AdapterFactoryImpl {
1115: SchemaLocatorAdapter adapter;
1116:
1117: public SchemaLocatorAdapterFactory(
1118: List/*<XSDSchemaLocator>*/locators) {
1119: adapter = new SchemaLocatorAdapter(locators);
1120: }
1121:
1122: public boolean isFactoryForType(Object type) {
1123: return type == XSDSchemaLocator.class;
1124: }
1125:
1126: public Adapter adaptNew(Notifier notifier, Object type) {
1127: return adapter;
1128: }
1129: }
1130:
1131: static class SchemaLocatorAdapter extends AdapterImpl implements
1132: XSDSchemaLocator {
1133:
1134: List/*<XSDSchemaLocator>*/locators;
1135:
1136: public SchemaLocatorAdapter(List/*<XSDSchemaLocator>*/locators) {
1137: this .locators = locators;
1138: }
1139:
1140: public boolean isAdapterForType(Object type) {
1141: return type == XSDSchemaLocator.class;
1142: }
1143:
1144: public XSDSchema locateSchema(XSDSchema xsdSchema,
1145: String namespaceURI, String rawSchemaLocationURI,
1146: String resolvedSchemaLocationURI) {
1147: for (int i = 0; i < locators.size(); i++) {
1148: XSDSchemaLocator locator = (XSDSchemaLocator) locators
1149: .get(i);
1150: XSDSchema schema = locator.locateSchema(xsdSchema,
1151: namespaceURI, rawSchemaLocationURI,
1152: resolvedSchemaLocationURI);
1153:
1154: if (schema != null) {
1155: return schema;
1156: }
1157: }
1158:
1159: return null;
1160: }
1161: }
1162:
1163: static class SchemaLocationResolverAdapterFactory extends
1164: AdapterFactoryImpl {
1165: SchemaLocationResolverAdapter adapter;
1166:
1167: public SchemaLocationResolverAdapterFactory(
1168: List/*<XSDSchemaLocationResolver>*/resolvers) {
1169: adapter = new SchemaLocationResolverAdapter(resolvers);
1170: }
1171:
1172: public boolean isFactoryForType(Object type) {
1173: return type == XSDSchemaLocationResolver.class;
1174: }
1175:
1176: public Adapter adaptNew(Notifier notifier, Object type) {
1177: return adapter;
1178: }
1179: }
1180:
1181: /**
1182: * An {@link XSDSchemaLocationResolver} composite that uses a list of
1183: * provided resolvers to try and match a given schema URI to a phisical location.
1184: * <p>
1185: * If none of the provided resolvers match, falls back to returning the
1186: * absolute <code>rawSchemaLocationURI</code>, if provided as an asbolute URI,
1187: * so the parser still has a change of getting the contents, though through
1188: * a potentially expensive network call.
1189: * </p>
1190: */
1191: static class SchemaLocationResolverAdapter extends AdapterImpl
1192: implements XSDSchemaLocationResolver {
1193:
1194: List/*<XSDSchemaLocationResolver>*/resolvers;
1195:
1196: public SchemaLocationResolverAdapter(
1197: List/*<XSDSchemaLocationResolver>*/resolvers) {
1198: this .resolvers = resolvers;
1199: }
1200:
1201: public boolean isAdapterForType(Object type) {
1202: return type == XSDSchemaLocationResolver.class;
1203: }
1204:
1205: public String resolveSchemaLocation(XSDSchema schema,
1206: String namespaceURI, String rawSchemaLocationURI) {
1207: for (int i = 0; i < resolvers.size(); i++) {
1208: XSDSchemaLocationResolver resolver = (XSDSchemaLocationResolver) resolvers
1209: .get(i);
1210: String resolved = resolver.resolveSchemaLocation(
1211: schema, namespaceURI, rawSchemaLocationURI);
1212:
1213: if (resolved != null) {
1214: return resolved;
1215: }
1216: }
1217:
1218: if (rawSchemaLocationURI != null
1219: || rawSchemaLocationURI.startsWith("http://")
1220: || rawSchemaLocationURI.startsWith("file:")) {
1221: LOGGER
1222: .warning("Using the provided absolute URI as the location for namespace '"
1223: + namespaceURI
1224: + "', as it couldn't be resolved for the provided location path '"
1225: + rawSchemaLocationURI + "'");
1226: return rawSchemaLocationURI;
1227: }
1228: LOGGER.warning("Could not resolve schema location: "
1229: + rawSchemaLocationURI + " to physical location.");
1230: return null;
1231: }
1232: }
1233: }
|