0001: //==============================================================================
0002: //===
0003: //=== SchemaLoader
0004: //===
0005: //==============================================================================
0006: //=== Copyright (C) 2001-2007 Food and Agriculture Organization of the
0007: //=== United Nations (FAO-UN), United Nations World Food Programme (WFP)
0008: //=== and United Nations Environment Programme (UNEP)
0009: //===
0010: //=== This program is free software; you can redistribute it and/or modify
0011: //=== it under the terms of the GNU General Public License as published by
0012: //=== the Free Software Foundation; either version 2 of the License, or (at
0013: //=== your option) any later version.
0014: //===
0015: //=== This program is distributed in the hope that it will be useful, but
0016: //=== WITHOUT ANY WARRANTY; without even the implied warranty of
0017: //=== MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0018: //=== General Public License for more details.
0019: //===
0020: //=== You should have received a copy of the GNU General Public License
0021: //=== along with this program; if not, write to the Free Software
0022: //=== Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
0023: //===
0024: //=== Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2,
0025: //=== Rome - Italy. email: geonetwork@osgeo.org
0026: //==============================================================================
0027:
0028: package org.fao.geonet.kernel.schema;
0029:
0030: import java.util.*;
0031: import org.jdom.*;
0032:
0033: import java.io.File;
0034: import jeeves.utils.Xml;
0035: import org.fao.geonet.constants.Edit;
0036:
0037: //==============================================================================
0038:
0039: public class SchemaLoader {
0040: private Element elRoot;
0041: private HashMap hmElements = new HashMap();
0042: private HashMap hmTypes = new HashMap();
0043: private HashMap hmAttrGrp = new HashMap();
0044: private HashMap hmAttrGpEn = new HashMap();
0045: private HashMap hmAbsElems = new HashMap();
0046: private HashMap hmSubsGrp = new HashMap();
0047: private HashMap hmSubsLink = new HashMap();
0048: private HashMap hmSubsNames = new HashMap();
0049: private HashMap hmAttribs = new HashMap();
0050: private HashMap hmAllAttrs = new HashMap();
0051: private HashMap hmGroups = new HashMap();
0052: private HashMap hmNameSpaces = new HashMap();
0053:
0054: private SchemaSubstitutions ssOverRides;
0055:
0056: private String targetNS;
0057: private String targetNSPrefix;
0058:
0059: /** Restrictions for simple types (element restriction) */
0060: private HashMap hmElemRestr = new HashMap();
0061:
0062: /** Restrictions for simple types (type restriction) */
0063: private HashMap hmTypeRestr = new HashMap();
0064:
0065: //---------------------------------------------------------------------------
0066: //---
0067: //--- Constructor
0068: //---
0069: //---------------------------------------------------------------------------
0070:
0071: public SchemaLoader() {
0072: }
0073:
0074: //---------------------------------------------------------------------------
0075: //---
0076: //--- API methods
0077: //---
0078: //---------------------------------------------------------------------------
0079:
0080: public MetadataSchema load(String xmlSchemaFile, String schemaId,
0081: String xmlSubstitutionsFile) throws Exception {
0082: ssOverRides = new SchemaSubstitutions(xmlSubstitutionsFile);
0083:
0084: if (xmlSchemaFile.startsWith("_"))
0085: return new MetadataSchema(new Element("root"));
0086:
0087: //--- PHASE 1 : pre-processing
0088: //---
0089: //--- the xml file is parsed and simplified. Xml schema subtrees are
0090: //--- wrapped in some classes
0091:
0092: ArrayList alElementFiles = loadFile(xmlSchemaFile,
0093: new HashSet());
0094:
0095: parseElements(alElementFiles);
0096:
0097: //--- PHASE 2 : resolve abstract elements
0098:
0099: for (Iterator i = hmSubsGrp.keySet().iterator(); i.hasNext();) {
0100: String elem = (String) i.next();
0101:
0102: ArrayList elements = (ArrayList) hmSubsGrp.get(elem);
0103: ArrayList subsNames = new ArrayList();
0104: hmSubsNames.put(elem, subsNames);
0105: for (int j = 0; j < elements.size(); j++) {
0106: ElementEntry ee = (ElementEntry) elements.get(j);
0107: if (ee.type == null) {
0108: ee.type = (String) hmAbsElems.get(elem);
0109:
0110: if (ee.type == null) {
0111: // If we don't have a type then insert with null and fix
0112: // when all elements have been added to hmElements
0113: Logger
0114: .log("Type is null for 'element' : "
0115: + ee.name
0116: + " which is part of substitution group with head element "
0117: + elem);
0118: }
0119: }
0120:
0121: hmElements.put(ee.name, ee.type);
0122: subsNames.add(ee.name);
0123: }
0124: }
0125:
0126: //--- PHASE 3 : add namespaces and elements
0127:
0128: MetadataSchema mds = new MetadataSchema(elRoot);
0129: for (int j = 0; j < alElementFiles.size(); j++) {
0130: ElementInfo ei = (ElementInfo) alElementFiles.get(j);
0131: mds.addNS(ei.targetNSPrefix, ei.targetNS);
0132: }
0133:
0134: for (Iterator i = hmElements.keySet().iterator(); i.hasNext();) {
0135: String elem = (String) i.next();
0136: String type = (String) hmElements.get(elem);
0137:
0138: // fix any null types by back tracking through substitution links
0139: // until we get a concrete type or die trying :-)
0140: if (type == null) {
0141: Logger.log("Searching for type for element " + elem);
0142: type = recurseOnSubstitutionLinks(elem);
0143: if (type == null) {
0144: System.out.println("WARNING: Cannot find type for "
0145: + elem + ": assuming string");
0146: type = "string";
0147: } else {
0148: Logger.log("-- Recursive search returned " + type
0149: + " for element " + elem);
0150: }
0151: }
0152:
0153: ArrayList elemRestr = (ArrayList) hmElemRestr.get(elem);
0154: ArrayList typeRestr = (ArrayList) hmTypeRestr.get(type);
0155:
0156: if (elemRestr == null)
0157: elemRestr = new ArrayList();
0158:
0159: if (typeRestr != null)
0160: elemRestr.addAll(typeRestr);
0161:
0162: ArrayList elemSubs = (ArrayList) hmSubsNames.get(elem);
0163: if (elemSubs == null)
0164: elemSubs = new ArrayList();
0165: String elemSubsLink = (String) hmSubsLink.get(elem);
0166: if (elemSubsLink == null)
0167: elemSubsLink = "";
0168: mds.addElement(elem, type, elemRestr, elemSubs,
0169: elemSubsLink);
0170: }
0171:
0172: //--- PHASE 4 : resolve references in attribute groups
0173:
0174: for (Iterator i = hmAttrGpEn.values().iterator(); i.hasNext();) {
0175: AttributeGroupEntry age = (AttributeGroupEntry) i.next();
0176: for (int k = 0; k < age.alAttrs.size(); k++) {
0177: AttributeEntry attr = (AttributeEntry) age.alAttrs
0178: .get(k);
0179: if (attr.name != null)
0180: hmAllAttrs.put(attr.name, attr);
0181: }
0182: ArrayList attrs = resolveNestedAttributeGroups(age);
0183: hmAttrGrp.put(age.name, attrs);
0184: }
0185:
0186: //--- PHASE 5 : check attributes to see whether they should be qualified
0187:
0188: HashMap hmAttrChk = new HashMap();
0189: for (Iterator i = hmAllAttrs.values().iterator(); i.hasNext();) {
0190: AttributeEntry attr = (AttributeEntry) i.next();
0191: AttributeEntry attrPrev = (AttributeEntry) hmAttrChk
0192: .get(attr.unqualifiedName);
0193: if (attrPrev != null) {
0194: attr.form = "qualified";
0195: attrPrev.form = "qualified";
0196: } else {
0197: hmAttrChk.put(attr.unqualifiedName, attr);
0198: }
0199: }
0200:
0201: //--- PHASE 6 : post-processing
0202: //---
0203: //--- resolve type inheritance and elements
0204:
0205: ArrayList alTypes = new ArrayList(hmTypes.values());
0206: for (ListIterator i = alTypes.listIterator(); i.hasNext();) {
0207: ComplexTypeEntry cte = (ComplexTypeEntry) i.next();
0208:
0209: MetadataType mdt = new MetadataType();
0210:
0211: mdt.setOrType(cte.isOrType);
0212:
0213: //--- resolve element and attribute inheritance from complexContent
0214:
0215: if (cte.complexContent != null) {
0216:
0217: if (cte.complexContent.base != null) {
0218:
0219: //--- add elements
0220: cte.alElements = resolveInheritance(cte);
0221:
0222: //--- add attribs (if any)
0223: ArrayList complexContentAttribs = resolveAttributeInheritance(cte);
0224: for (int j = 0; j < complexContentAttribs.size(); j++) {
0225: AttributeEntry ae = (AttributeEntry) complexContentAttribs
0226: .get(j);
0227: mdt.addAttribute(buildMetadataAttrib(ae));
0228: }
0229:
0230: //--- if the base type is an ortype then we need to make this an
0231: //--- or type as well
0232: ComplexTypeEntry baseCTE = (ComplexTypeEntry) hmTypes
0233: .get(cte.complexContent.base);
0234: if (baseCTE.isOrType) {
0235: cte.isOrType = true;
0236: mdt.setOrType(true);
0237: Logger.log("Setting " + cte.name
0238: + " to isOrType");
0239: }
0240: } else {
0241: throw new IllegalArgumentException(
0242: "base not defined for complexContent in "
0243: + cte.name);
0244: }
0245:
0246: //--- resolve attribute inheritance from simpleContent
0247:
0248: } else if (cte.simpleContent != null) {
0249: ArrayList simpleContentAttribs = resolveAttributeInheritanceFromSimpleContent(cte);
0250: for (int j = 0; j < simpleContentAttribs.size(); j++) {
0251: AttributeEntry ae = (AttributeEntry) simpleContentAttribs
0252: .get(j);
0253: mdt.addAttribute(buildMetadataAttrib(ae));
0254: }
0255:
0256: //--- otherwise process the attributes and attribute groups for this type
0257:
0258: } else {
0259: for (int j = 0; j < cte.alAttribs.size(); j++) {
0260: AttributeEntry ae = (AttributeEntry) cte.alAttribs
0261: .get(j);
0262: mdt.addAttribute(buildMetadataAttrib(ae));
0263: }
0264: for (int k = 0; k < cte.alAttribGroups.size(); k++) {
0265: String attribGroup = (String) cte.alAttribGroups
0266: .get(k);
0267: ArrayList al = (ArrayList) hmAttrGrp
0268: .get(attribGroup);
0269:
0270: if (al == null)
0271: throw new IllegalArgumentException(
0272: "Attribute group not found : "
0273: + attribGroup);
0274:
0275: for (int j = 0; j < al.size(); j++) {
0276: AttributeEntry ae = (AttributeEntry) al.get(j);
0277: mdt.addAttribute(buildMetadataAttrib(ae));
0278: }
0279: }
0280: }
0281:
0282: //--- now add the elements belonging to this complex type to the mdt
0283:
0284: for (int j = 0; j < cte.alElements.size(); j++) {
0285: ElementEntry ee = (ElementEntry) cte.alElements.get(j);
0286:
0287: // Three situations:
0288: // 1. element is a container element - group, choice or sequence - so recurse
0289: // and get elements from any containers nested inside this container -
0290: // we generate a name to use from the cte.name and element position
0291:
0292: if (ee.groupElem || ee.choiceElem || ee.sequenceElem) {
0293: Integer baseNr = j;
0294: String baseName = cte.name;
0295: String extension;
0296: ArrayList elements;
0297: if (ee.choiceElem) {
0298: extension = Edit.RootChild.CHOICE;
0299: elements = ee.alContainerElems;
0300: } else if (ee.groupElem) {
0301: extension = Edit.RootChild.GROUP;
0302: GroupEntry group = (GroupEntry) hmGroups
0303: .get(ee.ref);
0304: elements = group.alElements;
0305: } else {
0306: extension = Edit.RootChild.SEQUENCE;
0307: elements = ee.alContainerElems;
0308: }
0309: String type = ee.name = baseName + extension
0310: + baseNr;
0311: ArrayList newCtes = createTypeAndResolveNestedContainers(
0312: schemaId, mds, elements, baseName,
0313: extension, baseNr);
0314: if (newCtes.size() != 0) {
0315: for (int ctCntr = 0; ctCntr < newCtes.size(); ctCntr++) {
0316: ComplexTypeEntry newCte = (ComplexTypeEntry) newCtes
0317: .get(ctCntr);
0318: i.add(newCte);
0319: i.previous();
0320: }
0321: }
0322: mds.addElement(ee.name, type, new ArrayList(),
0323: new ArrayList(), "");
0324: mdt.addElementWithType(ee.name, type, ee.min,
0325: ee.max);
0326:
0327: // 2. element is a reference to a global element so check if abstract or
0328: // if the type needs to be turned into a choice ie. it has one element
0329: // which is the head of a substitution group or a new choice type
0330: // is created for the element or just add it if none of
0331: // the above
0332: } else if (ee.ref != null) {
0333: boolean choiceType = (cte.alElements.size() == 1);
0334: handleRefElement(j, schemaId, cte.name, choiceType,
0335: ee, mdt, mds);
0336:
0337: // 3. element is a local element so get type or process local complex/simpleType// and add to the ListIterator if complex
0338: } else if (ee.name != null) {
0339: ComplexTypeEntry newCte = handleLocalElement(j,
0340: schemaId, cte.name, ee, mdt, mds);
0341: if (newCte != null) {
0342: i.add(newCte);
0343: i.previous();
0344: }
0345:
0346: } else {
0347: throw new IllegalArgumentException(
0348: "Unknown element type at position " + j
0349: + " in complexType " + cte.name);
0350: }
0351: }
0352: mds.addType(cte.name, mdt);
0353: }
0354:
0355: return mds;
0356: }
0357:
0358: //---------------------------------------------------------------------------
0359: //---
0360: //--- Recurse on substitution links until we get a type that we can use
0361: //---
0362: //---------------------------------------------------------------------------
0363: private String recurseOnSubstitutionLinks(String elemName) {
0364: String elemLinkName = (String) hmSubsLink.get(elemName);
0365: if (elemLinkName != null) {
0366: String elemLinkType = (String) hmElements.get(elemLinkName);
0367: if (elemLinkType != null)
0368: return elemLinkType; // found concrete type!
0369: else
0370: recurseOnSubstitutionLinks(elemLinkName); // keep trying
0371: }
0372: return null; // Cannot find a type so return null
0373: }
0374:
0375: //---------------------------------------------------------------------------
0376: //---
0377: //--- Build a local element into the MetadataType and Schema
0378: //---
0379: //---------------------------------------------------------------------------
0380: private ComplexTypeEntry handleLocalElement(Integer elementNr,
0381: String schemaId, String baseName, ElementEntry ee,
0382: MetadataType mdt, MetadataSchema mds) {
0383:
0384: ComplexTypeEntry cteInt = null;
0385: ArrayList elemRestr = new ArrayList();
0386:
0387: if (ee.type == null) {
0388: if (ee.complexType != null) {
0389: cteInt = ee.complexType;
0390: ee.type = cteInt.name = ee.name + "HSI" + elementNr
0391: + getUnqualifiedName(baseName);
0392: } else if (ee.simpleType != null) {
0393: ee.type = "string";
0394: if (ee.simpleType.alEnum != null) // add enumerations if any
0395: elemRestr.add(ee.simpleType.alEnum);
0396: } else {
0397: System.out.println("WARNING: Could not find type for "
0398: + ee.name + " - assuming string");
0399: ee.type = "string";
0400: }
0401: }
0402:
0403: mds
0404: .addElement(ee.name, ee.type, elemRestr,
0405: new ArrayList(), "");
0406: mdt.addElementWithType(ee.name, ee.type, ee.min, ee.max);
0407:
0408: return (cteInt);
0409:
0410: }
0411:
0412: //---------------------------------------------------------------------------
0413: //---
0414: //--- Return list of substitutes if we want to override those derived
0415: //--- from the schema XSDs - this is schema dependent and defined in
0416: //--- the schema-substitutes files and is used for elements such as
0417: //--- gco:CharacterString in the iso19139 schema
0418: //---
0419: //--- returns null if there are no user defined substitutes,
0420: //--- OR an empty list if removal of all schema substitutes is required
0421: //--- OR a list of ElementEntry objects to use as substitutes
0422: //---
0423: //---------------------------------------------------------------------------
0424: private ArrayList getOverRideSubstitutes(String elementName) {
0425:
0426: ArrayList subs = (ArrayList) hmSubsGrp.get(elementName);
0427: ArrayList ssOs = ssOverRides.getSubstitutes(elementName);
0428: if (ssOs != null && subs != null) {
0429: ArrayList results = new ArrayList();
0430: ArrayList validSubs = (ArrayList) hmSubsNames
0431: .get(elementName);
0432: for (int i = 0; i < ssOs.size(); i++) {
0433: String altSub = (String) ssOs.get(i);
0434: if (validSubs != null && !validSubs.contains(altSub)) {
0435: System.out
0436: .println("WARNING: schema-substitutions.xml specified "
0437: + altSub
0438: + " for element "
0439: + elementName
0440: + " but the schema does not define this as a valid substitute");
0441: }
0442: for (int k = 0; k < subs.size(); k++) {
0443: ElementEntry ee = (ElementEntry) subs.get(k);
0444: if (ee.name.equals(altSub)) {
0445: results.add(ee);
0446: }
0447: }
0448: }
0449: if (results.size() == 0 && validSubs != null) {
0450: System.out
0451: .println("WARNING: schema-substitutions.xml has wiped out XSD substitution list for "
0452: + elementName);
0453: }
0454: return results;
0455: }
0456: return null;
0457: }
0458:
0459: //---------------------------------------------------------------------------
0460: //---
0461: //--- Build a reference to a global element into the MetadataType and Schema
0462: //---
0463: //---------------------------------------------------------------------------
0464: private void handleRefElement(Integer elementNr, String schemaId,
0465: String baseName, boolean choiceType, ElementEntry ee,
0466: MetadataType mdt, MetadataSchema mds) {
0467:
0468: String type = (String) hmElements.get(ee.ref);
0469: boolean isAbstract = hmAbsElems.containsKey(ee.ref);
0470:
0471: // If we have user specified substitutions then use them otherwise
0472: // use those from the schema
0473: boolean doSubs = true;
0474: ArrayList al = getOverRideSubstitutes(ee.ref);
0475: if (al == null)
0476: al = (ArrayList) hmSubsGrp.get(ee.ref);
0477: else
0478: doSubs = false;
0479:
0480: if ((al != null && al.size() > 0) || isAbstract) {
0481: if (choiceType) {
0482: // The complex type has only one element then make it a choice type if
0483: // there are concrete elements in the substitution group
0484: Integer elementsAdded = assembleChoiceElements(mdt, al,
0485: doSubs);
0486: if (!isAbstract && doSubs) {
0487: /*
0488: * Control of substitution lists is via the schema-substitutions.xml
0489: * file because some profiles do not mandate substitutions of this
0490: * kind eg. wmo
0491: *
0492: if (elementsAdded == 1 &&
0493: (getPrefix(mdt.getElementAt(0)).equals(getProfile(schemaId))) &&
0494: schemaId.startsWith("iso19139")) {
0495: Logger.log("Sticking with "+mdt.toString()+" for "+ee.ref);
0496: } else {
0497: *
0498: */
0499: mdt.addRefElementWithType(ee.ref, type, ee.min,
0500: ee.max);
0501: elementsAdded++;
0502: /*}*/
0503: }
0504: mdt.setOrType(elementsAdded > 1);
0505: } else {
0506: // The complex type has real elements and/or attributes so make a new
0507: // choice element with type and replace this element with it
0508: MetadataType mdtc = new MetadataType();
0509: Integer elementsAdded = assembleChoiceElements(mdtc,
0510: al, doSubs);
0511: if (!isAbstract && doSubs) {
0512: mdtc.addRefElementWithType(ee.ref, ee.type, ee.min,
0513: ee.max);
0514: elementsAdded++;
0515: }
0516: mdtc.setOrType(elementsAdded > 1);
0517: type = ee.ref + Edit.RootChild.CHOICE + elementNr;
0518: String name = type;
0519: mds.addType(type, mdtc);
0520: mds.addElement(name, type, new ArrayList(),
0521: new ArrayList(), "");
0522: mdt.addElementWithType(name, type, ee.min, ee.max);
0523: }
0524: } else if (!isAbstract) {
0525: mdt.addRefElementWithType(ee.ref, type, ee.min, ee.max);
0526: } else {
0527: System.out.println("WARNING: element " + ee.ref + " from "
0528: + baseName
0529: + " has fallen through the logic (abstract: "
0530: + isAbstract + ") - ignoring");
0531: }
0532: }
0533:
0534: //---------------------------------------------------------------------------
0535: //---
0536: //--- Recurse on attributeGroups to build a list of AttributeEntry objects
0537: //---
0538: //---------------------------------------------------------------------------
0539: private ArrayList resolveNestedAttributeGroups(
0540: AttributeGroupEntry age) {
0541: ArrayList attrs = new ArrayList();
0542:
0543: if (age.alAttrGrps.size() > 0) {
0544: for (int i = 0; i < age.alAttrGrps.size(); i++) {
0545: AttributeGroupEntry ageInternal = (AttributeGroupEntry) age.alAttrGrps
0546: .get(i);
0547: AttributeGroupEntry ageRef = (AttributeGroupEntry) hmAttrGpEn
0548: .get(ageInternal.ref);
0549: if (ageRef == null)
0550: throw new IllegalArgumentException(
0551: "ERROR: cannot find attributeGroup with ref "
0552: + ageInternal.ref);
0553: attrs.addAll(resolveNestedAttributeGroups(ageRef));
0554: }
0555: }
0556: attrs.addAll(age.alAttrs);
0557: return attrs;
0558: }
0559:
0560: //---------------------------------------------------------------------------
0561: //---
0562: //--- Descend recursively to deal with nested containers
0563: //---
0564: //---------------------------------------------------------------------------
0565: private ArrayList createTypeAndResolveNestedContainers(
0566: String schemaId, MetadataSchema mds, ArrayList al,
0567: String baseName, String extension, Integer baseNr) {
0568:
0569: ArrayList complexTypes = new ArrayList();
0570:
0571: Integer oldBaseNr = baseNr;
0572: if (al == null)
0573: return complexTypes;
0574: MetadataType mdt = new MetadataType();
0575: if (extension.contains(Edit.RootChild.CHOICE))
0576: mdt.setOrType(true);
0577: for (int k = 0; k < al.size(); k++) {
0578: ElementEntry ee = (ElementEntry) al.get(k);
0579: baseNr++;
0580:
0581: // CHOICE
0582: if (ee.choiceElem) {
0583: String newExtension = Edit.RootChild.CHOICE;
0584: ArrayList newCtes = createTypeAndResolveNestedContainers(
0585: schemaId, mds, ee.alContainerElems, baseName,
0586: newExtension, baseNr);
0587: if (newCtes.size() > 0)
0588: complexTypes.addAll(newCtes);
0589: ee.name = ee.type = baseName + newExtension + baseNr;
0590: mds.addElement(ee.name, ee.type, new ArrayList(),
0591: new ArrayList(), "");
0592: mdt
0593: .addElementWithType(ee.name, ee.type, ee.min,
0594: ee.max);
0595:
0596: // GROUP
0597: } else if (ee.groupElem) {
0598: String newExtension = Edit.RootChild.GROUP;
0599: if (ee.ref != null) {
0600: GroupEntry group = (GroupEntry) hmGroups
0601: .get(ee.ref);
0602: ArrayList alGroupElements = group.alElements;
0603: ArrayList newCtes = createTypeAndResolveNestedContainers(
0604: schemaId, mds, alGroupElements, baseName,
0605: newExtension, baseNr);
0606: if (newCtes.size() > 0)
0607: complexTypes.addAll(newCtes);
0608: ee.name = ee.type = baseName + newExtension
0609: + baseNr;
0610: mds.addElement(ee.name, ee.type, new ArrayList(),
0611: new ArrayList(), "");
0612: mdt.addElementWithType(ee.name, ee.type, ee.min,
0613: ee.max);
0614: } else {
0615: System.out
0616: .println("WARNING: group element ref is NULL in "
0617: + baseName + extension + baseNr);
0618: }
0619:
0620: // SEQUENCE
0621: } else if (ee.sequenceElem) {
0622: String newExtension = Edit.RootChild.SEQUENCE;
0623: ArrayList newCtes = createTypeAndResolveNestedContainers(
0624: schemaId, mds, ee.alContainerElems, baseName,
0625: newExtension, baseNr);
0626: if (newCtes.size() > 0)
0627: complexTypes.addAll(newCtes);
0628: ee.name = ee.type = baseName + newExtension + baseNr;
0629: mds.addElement(ee.name, ee.type, new ArrayList(),
0630: new ArrayList(), "");
0631: mdt
0632: .addElementWithType(ee.name, ee.type, ee.min,
0633: ee.max);
0634:
0635: // ELEMENT
0636: } else {
0637: if (ee.name != null) {
0638: ComplexTypeEntry newCte = handleLocalElement(k,
0639: schemaId, baseName, ee, mdt, mds);
0640: if (newCte != null)
0641: complexTypes.add(newCte);
0642: } else {
0643: handleRefElement(k, schemaId, baseName, false, ee,
0644: mdt, mds);
0645: }
0646: }
0647: }
0648: mds.addType(baseName + extension + oldBaseNr, mdt);
0649: return complexTypes;
0650: }
0651:
0652: //---------------------------------------------------------------------------
0653: //---
0654: //--- Descend recursively to deal with abstract elements
0655: //---
0656: //---------------------------------------------------------------------------
0657: private int assembleChoiceElements(MetadataType mdt, ArrayList al,
0658: boolean doSubs) {
0659:
0660: int number = 0;
0661: if (al == null)
0662: return number;
0663: for (int k = 0; k < al.size(); k++) {
0664: ElementEntry ee = (ElementEntry) al.get(k);
0665: if (ee.abstrElem) {
0666: Integer numberRecursed = assembleChoiceElements(mdt,
0667: (ArrayList) hmSubsGrp.get(ee.name), doSubs);
0668: number = number + numberRecursed;
0669: } else {
0670: number++;
0671: mdt
0672: .addElementWithType(ee.name, ee.type, ee.min,
0673: ee.max);
0674: // Also add any elements that substitute for this one so that we can
0675: // complete the list of choices if required
0676: if (doSubs) {
0677: ArrayList elemSubs = (ArrayList) hmSubsGrp
0678: .get(ee.name);
0679: if (elemSubs != null) {
0680: for (int j = 0; j < elemSubs.size(); j++) {
0681: ElementEntry eeSub = (ElementEntry) elemSubs
0682: .get(j);
0683: mdt.addElementWithType(eeSub.name,
0684: eeSub.type, eeSub.min, eeSub.max);
0685: number++;
0686: }
0687: }
0688: }
0689: }
0690: }
0691: return number;
0692: }
0693:
0694: //---------------------------------------------------------------------------
0695: //---
0696: //--- PHASE 1 : Schema loading
0697: //---
0698: //---------------------------------------------------------------------------
0699:
0700: /** Loads the xml-schema file, removes annotations and resolve imports/includes */
0701:
0702: private ArrayList loadFile(String xmlSchemaFile, HashSet loadedFiles)
0703: throws Exception {
0704: loadedFiles.add(new File(xmlSchemaFile).getCanonicalPath());
0705:
0706: String path = new File(xmlSchemaFile).getParent() + "/";
0707:
0708: //--- load xml-schema
0709:
0710: elRoot = Xml.loadFile(xmlSchemaFile);
0711:
0712: // change target namespace
0713: String oldtargetNS = targetNS;
0714: String oldtargetNSPrefix = targetNSPrefix;
0715: targetNS = elRoot.getAttributeValue("targetNamespace");
0716: targetNSPrefix = null;
0717:
0718: if (targetNS != null) {
0719: for (Iterator i = elRoot.getAdditionalNamespaces()
0720: .iterator(); i.hasNext();) {
0721: Namespace ns = (Namespace) i.next();
0722: if (targetNS.equals(ns.getURI())) {
0723: targetNSPrefix = ns.getPrefix();
0724: break;
0725: }
0726: }
0727: if ("".equals(targetNSPrefix))
0728: targetNSPrefix = null;
0729: }
0730: // This is a bug in jdom - seems that if the namespace prefix is xml: and
0731: // namespace is as shown in the if statement then getAdditionalNamespaces
0732: // doesn't return the namespaces and we can't get a prefix - this fix gets
0733: // around that bug
0734: if (xmlSchemaFile.contains("xml.xsd")
0735: && targetNS
0736: .equals("http://www.w3.org/XML/1998/namespace"))
0737: targetNSPrefix = "xml";
0738:
0739: List children = elRoot.getChildren();
0740:
0741: //--- collect elements into an array because we have to add elements
0742: //--- when we encounter the "import" element
0743:
0744: ArrayList alElementFiles = new ArrayList();
0745:
0746: for (int i = 0; i < children.size(); i++) {
0747: Element elChild = (Element) children.get(i);
0748: String name = elChild.getName();
0749:
0750: if (name.equals("annotation"))
0751: ;
0752:
0753: else if (name.equals("import") || name.equals("include")) {
0754: String schemaLoc = elChild
0755: .getAttributeValue("schemaLocation");
0756:
0757: //--- we must prevent imports from the web
0758:
0759: if (schemaLoc.startsWith("http:")) {
0760: int lastSlash = schemaLoc.lastIndexOf("/");
0761: schemaLoc = schemaLoc.substring(lastSlash + 1);
0762: }
0763: if (!loadedFiles.contains(new File(path + schemaLoc)
0764: .getCanonicalPath()))
0765: alElementFiles.addAll(loadFile(path + schemaLoc,
0766: loadedFiles));
0767: } else
0768: alElementFiles.add(new ElementInfo(elChild,
0769: xmlSchemaFile, targetNS, targetNSPrefix));
0770: }
0771: // restore target namespace
0772: targetNS = oldtargetNS;
0773: targetNSPrefix = oldtargetNSPrefix;
0774:
0775: return alElementFiles;
0776: }
0777:
0778: //---------------------------------------------------------------------------
0779: //---
0780: //--- PHASE 2 : Parse elements building intermediate data structures
0781: //---
0782: //---------------------------------------------------------------------------
0783:
0784: private void parseElements(ArrayList alElementFiles)
0785: throws JDOMException {
0786: //--- clear some structures
0787:
0788: hmElements.clear();
0789: hmTypes.clear();
0790: hmAttrGrp.clear();
0791: hmAbsElems.clear();
0792: hmSubsGrp.clear();
0793: hmSubsLink.clear();
0794: hmElemRestr.clear();
0795: hmTypeRestr.clear();
0796: hmAttribs.clear();
0797: hmAllAttrs.clear();
0798: hmGroups.clear();
0799:
0800: for (int i = 0; i < alElementFiles.size(); i++) {
0801: ElementInfo ei = (ElementInfo) alElementFiles.get(i);
0802:
0803: Element elChild = ei.element;
0804: String name = elChild.getName();
0805:
0806: if (name.equals("element"))
0807: buildGlobalElement(ei);
0808:
0809: else if (name.equals("complexType"))
0810: buildComplexType(ei);
0811:
0812: else if (name.equals("simpleType"))
0813: buildSimpleType(ei);
0814:
0815: else if (name.equals("attribute"))
0816: buildGlobalAttrib(ei);
0817:
0818: else if (name.equals("group"))
0819: buildGlobalGroup(ei);
0820:
0821: else if (name.equals("attributeGroup"))
0822: buildGlobalAttributeGroup(ei);
0823:
0824: else
0825: Logger.log("Unknown global element : "
0826: + elChild.getName(), ei);
0827: }
0828: }
0829:
0830: //---------------------------------------------------------------------------
0831:
0832: private void buildGlobalElement(ElementInfo ei) {
0833: ElementEntry ee = new ElementEntry(ei);
0834:
0835: if (ee.name == null)
0836: throw new IllegalArgumentException(
0837: "Name is null for element : " + ee.name);
0838:
0839: if (ee.substGroup != null) {
0840: ArrayList al = (ArrayList) hmSubsGrp.get(ee.substGroup);
0841:
0842: if (al == null) {
0843: al = new ArrayList();
0844: hmSubsGrp.put(ee.substGroup, al);
0845: }
0846: al.add(ee);
0847:
0848: if (hmSubsLink.get(ee.name) != null) {
0849: throw new IllegalArgumentException(
0850: "Substitution link collision for : " + ee.name
0851: + " link to " + ee.substGroup);
0852: } else {
0853: hmSubsLink.put(ee.name, ee.substGroup);
0854: }
0855: }
0856: if (ee.abstrElem) {
0857: if (hmAbsElems.containsKey(ee.name))
0858: throw new IllegalArgumentException(
0859: "Namespace collision for : " + ee.name);
0860: hmAbsElems.put(ee.name, ee.type);
0861:
0862: return;
0863: }
0864: if (ee.complexType != null) {
0865: String type = ee.name + "HSI";
0866: ee.complexType.name = type;
0867: ee.type = type;
0868: if (hmElements.containsKey(ee.name))
0869: throw new IllegalArgumentException(
0870: "Namespace collision for : " + ee.name);
0871:
0872: hmElements.put(ee.name, type);
0873: hmTypes.put(type, ee.complexType);
0874:
0875: } else if (ee.simpleType != null) {
0876: String type = ee.name;
0877: if (hmElements.containsKey(ee.name))
0878: throw new IllegalArgumentException(
0879: "Namespace collision for : " + ee.name);
0880: ee.type = "string";
0881: hmElements.put(ee.name, ee.type);
0882: hmElemRestr.put(ee.name, ee.simpleType.alEnum);
0883:
0884: } else {
0885: if (ee.type == null && ee.substGroup == null) {
0886: System.out
0887: .println("WARNING: "
0888: + ee.name
0889: + " is a global element without a type - assuming a string");
0890: ee.type = "string";
0891: }
0892: hmElements.put(ee.name, ee.type);
0893:
0894: }
0895: if (ee.name.contains("SensorML")) {
0896: Logger.log("SensorML element detected " + ee.name + " "
0897: + ee.complexType.name);
0898: }
0899: }
0900:
0901: //---------------------------------------------------------------------------
0902:
0903: private void buildComplexType(ElementInfo ei) {
0904: ComplexTypeEntry ct = new ComplexTypeEntry(ei);
0905: if (hmTypes.containsKey(ct.name))
0906: throw new IllegalArgumentException(
0907: "Namespace collision for : " + ct.name);
0908:
0909: hmTypes.put(ct.name, ct);
0910: }
0911:
0912: //---------------------------------------------------------------------------
0913:
0914: private void buildSimpleType(ElementInfo ei) {
0915: SimpleTypeEntry st = new SimpleTypeEntry(ei);
0916: if (hmTypeRestr.containsKey(st.name))
0917: throw new IllegalArgumentException(
0918: "Namespace collision for : " + st.name);
0919:
0920: hmTypeRestr.put(st.name, st.alEnum);
0921: }
0922:
0923: //---------------------------------------------------------------------------
0924:
0925: private void buildGlobalAttrib(ElementInfo ei) {
0926: AttributeEntry at = new AttributeEntry(ei);
0927: if (hmAttribs.containsKey(at.name))
0928: throw new IllegalArgumentException(
0929: "Namespace collision for : " + at.name);
0930:
0931: hmAttribs.put(at.name, at);
0932: hmAllAttrs.put(at.name, at);
0933: }
0934:
0935: //---------------------------------------------------------------------------
0936:
0937: private void buildGlobalGroup(ElementInfo ei) {
0938: GroupEntry ge = new GroupEntry(ei);
0939: if (hmGroups.containsKey(ge.name))
0940: throw new IllegalArgumentException(
0941: "Namespace collision for : " + ge.name);
0942:
0943: hmGroups.put(ge.name, ge);
0944: }
0945:
0946: //---------------------------------------------------------------------------
0947:
0948: private void buildGlobalAttributeGroup(ElementInfo ei) {
0949:
0950: AttributeGroupEntry age = new AttributeGroupEntry(ei);
0951: if (hmAttrGpEn.containsKey(age.name))
0952: throw new IllegalArgumentException(
0953: "Namespace collision for : " + age.name);
0954: hmAttrGpEn.put(age.name, age);
0955:
0956: }
0957:
0958: //---------------------------------------------------------------------------
0959: //---
0960: //--- Add in attributes from complexType with SimpleContent that restricts
0961: //--- or extends a base type (if any)
0962: //---
0963: //---------------------------------------------------------------------------
0964:
0965: private ArrayList resolveAttributeInheritanceFromSimpleContent(
0966: ComplexTypeEntry cte) {
0967: ArrayList result = new ArrayList();
0968:
0969: if (cte.simpleContent == null) {
0970: throw new IllegalArgumentException(
0971: "SimpleContent must be present in base type of the SimpleContent in "
0972: + cte.name);
0973: } else {
0974:
0975: // recurse if we need to follow the base type
0976:
0977: String baseType = cte.simpleContent.base;
0978: ComplexTypeEntry baseCTE = (ComplexTypeEntry) hmTypes
0979: .get(baseType);
0980: if (baseCTE != null)
0981: result = new ArrayList(
0982: resolveAttributeInheritanceFromSimpleContent(baseCTE));
0983:
0984: // if the base type was a restriction then replace the attributes we got
0985: // from the restriction with these
0986: if (cte.simpleContent.restriction) {
0987: ArrayList adds = (ArrayList) cte.simpleContent.alAttribs
0988: .clone();
0989: for (int i = 0; i < result.size(); i++) {
0990: AttributeEntry attrib = (AttributeEntry) result
0991: .get(i);
0992: for (int j = 0; j < adds.size(); j++) {
0993: AttributeEntry attribOther = (AttributeEntry) adds
0994: .get(j);
0995: boolean eqAttrib = eqAttribs(attribOther,
0996: attrib);
0997: if (eqAttrib) {
0998: result.set(i, attribOther);
0999: }
1000: }
1001: }
1002: }
1003: // otherwise base type was an extension so add the attributes we got
1004: // from the extension to these
1005: else
1006: result.addAll((ArrayList) cte.simpleContent.alAttribs
1007: .clone());
1008:
1009: // No one seems clear on what to do with attributeGroups so treat them
1010: // as an extension
1011: if (cte.simpleContent.alAttribGroups != null) {
1012: for (int k = 0; k < cte.simpleContent.alAttribGroups
1013: .size(); k++) {
1014: String attribGroup = (String) cte.simpleContent.alAttribGroups
1015: .get(k);
1016: ArrayList al = (ArrayList) hmAttrGrp
1017: .get(attribGroup);
1018:
1019: if (al == null)
1020: throw new IllegalArgumentException(
1021: "Attribute group not found : "
1022: + attribGroup);
1023:
1024: for (int j = 0; j < al.size(); j++)
1025: result.add(al.get(j));
1026: }
1027: }
1028: }
1029:
1030: return result;
1031: }
1032:
1033: /** function to test whether two AttributeEntry objects have the same name
1034: */
1035: boolean eqAttribs(AttributeEntry attribOther, AttributeEntry attrib) {
1036: if (attribOther.name != null) {
1037: if (attrib.name != null) {
1038: if (attribOther.name.equals(attrib.name))
1039: return true;
1040: } else {
1041: if (attribOther.name.equals(attrib.reference))
1042: return true;
1043: }
1044: } else {
1045: if (attrib.name != null) {
1046: if (attribOther.reference.equals(attrib.name))
1047: return true;
1048: } else {
1049: if (attribOther.reference.equals(attrib.reference))
1050: return true;
1051: }
1052: }
1053: return false;
1054: }
1055:
1056: //---------------------------------------------------------------------------
1057: //---
1058: //--- Add in attributes from complexType with ComplexContent that restricts
1059: //--- or extends a base type (if any)
1060: //---
1061: //---------------------------------------------------------------------------
1062:
1063: private ArrayList resolveAttributeInheritance(ComplexTypeEntry cte) {
1064:
1065: if (cte.complexContent == null)
1066: return cte.alAttribs;
1067:
1068: String baseType = cte.complexContent.base;
1069: ComplexTypeEntry baseCTE = (ComplexTypeEntry) hmTypes
1070: .get(baseType);
1071: if (baseCTE == null)
1072: throw new IllegalArgumentException(
1073: "Base type not found for : " + baseType);
1074:
1075: ArrayList result = new ArrayList(
1076: resolveAttributeInheritance(baseCTE));
1077:
1078: // if the base type was a restriction then replace the attributes we got
1079: // from the restriction with these
1080:
1081: if (cte.complexContent.restriction) {
1082: ArrayList adds = (ArrayList) cte.complexContent.alAttribs;
1083: for (int i = 0; i < result.size(); i++) {
1084: AttributeEntry attrib = (AttributeEntry) result.get(i);
1085: for (int j = 0; j < adds.size(); j++) {
1086: AttributeEntry attribOther = (AttributeEntry) adds
1087: .get(j);
1088: boolean eqAttrib = eqAttribs(attribOther, attrib);
1089: if (eqAttrib) {
1090: result.set(i, attribOther);
1091: }
1092: }
1093: }
1094: }
1095: // otherwise base type was an extension so add the attributes we got
1096: // from the extension to these
1097: else {
1098: result.addAll((ArrayList) cte.complexContent.alAttribs);
1099: if (cte.complexContent.alAttribGroups != null) {
1100: for (int k = 0; k < cte.complexContent.alAttribGroups
1101: .size(); k++) {
1102: String attribGroup = (String) cte.complexContent.alAttribGroups
1103: .get(k);
1104: ArrayList al = (ArrayList) hmAttrGrp
1105: .get(attribGroup);
1106: if (al == null)
1107: throw new IllegalArgumentException(
1108: "Attribute group not found : "
1109: + attribGroup);
1110: for (int j = 0; j < al.size(); j++)
1111: result.add(al.get(j));
1112: }
1113: }
1114: }
1115:
1116: // No one seems clear on what to do with attributeGroups so treat them
1117: // as an extension
1118: if (baseCTE.alAttribGroups != null) {
1119: for (int k = 0; k < baseCTE.alAttribGroups.size(); k++) {
1120: String attribGroup = (String) baseCTE.alAttribGroups
1121: .get(k);
1122: ArrayList al = (ArrayList) hmAttrGrp.get(attribGroup);
1123:
1124: if (al == null)
1125: throw new IllegalArgumentException(
1126: "Attribute group not found : "
1127: + attribGroup);
1128:
1129: for (int j = 0; j < al.size(); j++)
1130: result.add(al.get(j));
1131: }
1132: }
1133:
1134: return result;
1135: }
1136:
1137: //---------------------------------------------------------------------------
1138: //---
1139: //--- Add in elements to complexType that come from base type (if any)
1140: //---
1141: //---------------------------------------------------------------------------
1142:
1143: private ArrayList resolveInheritance(ComplexTypeEntry cte) {
1144: if (cte == null || cte.complexContent == null)
1145: return cte.alElements;
1146:
1147: String baseType = cte.complexContent.base;
1148: ComplexTypeEntry baseCTE = (ComplexTypeEntry) hmTypes
1149: .get(baseType);
1150: if (baseCTE == null)
1151: throw new IllegalArgumentException(
1152: "Base type not found for : " + baseType);
1153:
1154: // skip over the elements in the base type of a restricted complex type
1155: // by ending the recursion
1156: ArrayList result = new ArrayList();
1157: if (!cte.complexContent.restriction)
1158: result = new ArrayList(resolveInheritance(baseCTE));
1159:
1160: result.addAll((ArrayList) cte.complexContent.alElements);
1161:
1162: return result;
1163: }
1164:
1165: //---------------------------------------------------------------------------
1166:
1167: private MetadataAttribute buildMetadataAttrib(AttributeEntry ae) {
1168: String name = ae.name;
1169: String ref = ae.reference;
1170: String value = ae.defValue;
1171: boolean overRequired = ae.required;
1172:
1173: MetadataAttribute ma = new MetadataAttribute();
1174:
1175: if (ref != null) {
1176: ae = (AttributeEntry) hmAttribs.get(ref);
1177: if (ae == null)
1178: throw new IllegalArgumentException("Reference '" + ref
1179: + "' not found for attrib : " + name);
1180: }
1181:
1182: if (ref != null && ref.contains(":"))
1183: ma.name = ref;
1184: else
1185: ma.name = ae.unqualifiedName;
1186:
1187: if (value != null)
1188: ma.defValue = value;
1189: else
1190: ma.defValue = ae.defValue;
1191:
1192: ma.required = overRequired;
1193:
1194: for (int k = 0; k < ae.alValues.size(); k++)
1195: ma.values.add(ae.alValues.get(k));
1196:
1197: return ma;
1198: }
1199:
1200: //---------------------------------------------------------------------------
1201:
1202: private String getPrefix(String qname) {
1203: int pos = qname.indexOf(":");
1204: if (pos < 0)
1205: return "";
1206: else
1207: return qname.substring(0, pos);
1208: }
1209:
1210: //--------------------------------------------------------------------------
1211:
1212: public String getUnqualifiedName(String qname) {
1213: int pos = qname.indexOf(":");
1214: if (pos < 0)
1215: return qname;
1216: else
1217: return qname.substring(pos + 1);
1218: }
1219:
1220: //---------------------------------------------------------------------------
1221:
1222: private String getProfile(String name) {
1223: int pos = name.indexOf(".");
1224: if (pos < 0)
1225: return "";
1226: else
1227: return name.substring(pos + 1);
1228: }
1229:
1230: }
1231:
1232: //==============================================================================
1233:
1234: class ElementInfo {
1235: public Element element;
1236: public String file;
1237: public String targetNS;
1238: public String targetNSPrefix;
1239:
1240: //---------------------------------------------------------------------------
1241:
1242: public ElementInfo(Element e, String f, String tns, String tnsp) {
1243: element = e;
1244: file = f;
1245: targetNS = tns;
1246: targetNSPrefix = tnsp;
1247: }
1248: }
1249:
1250: //==============================================================================
|