package gr.ntua.ivml.mint.xsd; import java.io.File; import java.io.InputStream; import java.io.Reader; import java.math.BigInteger; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import net.sf.json.JSONArray; import net.sf.json.JSONObject; import org.apache.log4j.Logger; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.EntityResolver; import org.xml.sax.ErrorHandler; import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import com.sun.xml.xsom.XSAttributeDecl; import com.sun.xml.xsom.XSAttributeUse; import com.sun.xml.xsom.XSComplexType; import com.sun.xml.xsom.XSContentType; import com.sun.xml.xsom.XSElementDecl; import com.sun.xml.xsom.XSFacet; import com.sun.xml.xsom.XSModelGroup; import com.sun.xml.xsom.XSParticle; import com.sun.xml.xsom.XSRestrictionSimpleType; import com.sun.xml.xsom.XSSchema; import com.sun.xml.xsom.XSSchemaSet; import com.sun.xml.xsom.XSSimpleType; import com.sun.xml.xsom.XSTerm; import com.sun.xml.xsom.XSType; import com.sun.xml.xsom.XmlString; import com.sun.xml.xsom.parser.AnnotationContext; import com.sun.xml.xsom.parser.AnnotationParser; import com.sun.xml.xsom.parser.XSOMParser; import com.sun.xml.xsom.util.DomAnnotationParserFactory; public class XSDParser { private static final Logger log = Logger.getLogger(XSDParser.class); private XSOMParser parser = new XSOMParser(); // private XSSchema schema = null; private XSSchemaSet schemaSet = null; HashMap<String, String> namespaces = new HashMap<String, String>(); private int elementid = 0; private String generateUniqueId() { elementid++; return "" + elementid; } public XSDParser(String xsd) { this.initXSSchema(xsd); } public XSDParser(InputStream is) { this.initXSSchema(is); } public XSDParser(Reader reader) { this.initXSSchema(reader); } public Map<String, String> getNamespaces() { return this.namespaces; } public void setNamespaces(HashMap<String, String> map) { this.namespaces = map; } private void initParser() { log.debug("initParser"); this.parser.setAnnotationParser(new DomAnnotationParserFactory() { public AnnotationParser create() { return new AnnotationParser() { final StringBuffer content = new StringBuffer(); public ContentHandler getContentHandler( AnnotationContext context, String parentElementName, ErrorHandler errorHandler, EntityResolver entityResolver) { return new ContentHandler() { public void characters(char[] ch, int start, int length) throws SAXException { content.append(ch, start, length); } public void endDocument() throws SAXException { } public void endElement(String uri, String localName, String name) throws SAXException { } public void endPrefixMapping(String prefix) throws SAXException { } public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { } public void processingInstruction(String target, String data) throws SAXException { } public void setDocumentLocator(Locator locator) { } public void skippedEntity(String name) throws SAXException { } public void startDocument() throws SAXException { } public void startElement(String uri, String localName, String name, Attributes atts) throws SAXException { } public void startPrefixMapping(String prefix, String uri) throws SAXException { } }; } @Override public Object getResult(Object existing) { return content.toString(); } }; } }); // System.out.println(schemaFileName + ":"); if (this.parser == null) { log.error("schema parser is null!"); } else { ErrorHandler errorHandler = new ErrorHandler() { @Override public void error(SAXParseException arg0) throws SAXException { log.error("error: " + arg0.getMessage()); } @Override public void fatalError(SAXParseException arg0) throws SAXException { log.error("fatal: " + arg0.getMessage()); } @Override public void warning(SAXParseException arg0) throws SAXException { log.error("warning: " + arg0.getMessage()); } }; this.parser.setErrorHandler(errorHandler); } } private void initXSSchema(String schemaFileName) { try { File file = new File(schemaFileName); this.initParser(); this.parser.parse(file); this.schemaSet = this.parser.getResult(); } catch (Exception e) { e.printStackTrace(); } } private void initXSSchema(InputStream is) { try { this.initParser(); this.parser.parse(is); this.schemaSet = this.parser.getResult(); } catch (Exception ex) { ex.printStackTrace(); } } private void initXSSchema(Reader reader) { try { this.initParser(); this.parser.parse(reader); this.schemaSet = this.parser.getResult(); } catch (Exception ex) { ex.printStackTrace(); } } public String getPrefixForNamespace(String namespace) { String prefix = ""; if (this.namespaces.containsKey(namespace)) { prefix = this.namespaces.get(namespace); } else { if (namespace.equals("http://www.w3.org/2001/XMLSchema") || namespace.equals("http://www.w3.org/XML/1998/namespace")) { prefix = "xml"; } else { prefix = "pr" + this.namespaces.keySet().size(); } this.namespaces.put(namespace, prefix); } return prefix; } private ArrayList<String> visitedElements = new ArrayList<String>(); private String elementLabel(XSElementDecl e) { String label = e.getTargetNamespace() + ":" + e.getName(); return e.getTargetNamespace() + ":" + e.getName(); } public JSONObject getElementDescription(XSElementDecl edecl) { // log.debug("getElementDescription for " + edecl.getName()); String namespace = edecl.getTargetNamespace(); //if(namespace.length() == 0) namespace = edecl.getOwnerSchema().getTargetNamespace(); JSONObject result = new JSONObject(); if (edecl != null) { XSComplexType complexType = edecl.getType().asComplexType(); if (complexType != null) { XSContentType contentType = complexType.getContentType(); XSSimpleType simpleType = contentType.asSimpleType(); XSParticle particle = contentType.asParticle(); String name = edecl.getName(); // get root base type XSType xstype = edecl.getType().getBaseType(); while (xstype.getBaseType() != null) { if (xstype.getName().equals(xstype.getBaseType().getName())) break; if (xstype.getName().equalsIgnoreCase("string")) break; xstype = xstype.getBaseType(); } String type = xstype.getName(); result = result.element("name", name).element("id", "") .element("type", type); if(particle != null) { // TODO: possibly problematic, min & max Occurs exist in particle before .getTerm.asElementDecl call // consider removing BigInteger maxOccurs = particle.getMaxOccurs(); BigInteger minOccurs = particle.getMinOccurs(); // System.out.println("name: " + result.getString("name") + " " + maxOccurs + " " + minOccurs); result = result.element("maxOccurs", maxOccurs).element("minOccurs", minOccurs); } if (namespace.length() > 0) { result = result.element("prefix", this .getPrefixForNamespace(namespace)); } // process attributes JSONArray attributes = this .processElementAttributes(complexType); result = result.element("attributes", attributes); // process enumerations if (simpleType != null) { JSONArray enumerations = this .processElementEnumerations(simpleType); if (enumerations.size() > 0) { result = result.element("enumerations", enumerations); } } // process children if (particle != null) { visitedElements.add(this.elementLabel(edecl)); JSONArray elementChildren = new JSONArray(); ArrayList<XSParticle> array = this .getParticleChildren(particle); log.debug("particle: " + edecl.getName() + " " + particle.getTerm()); for (XSParticle p : array) { if (p.getTerm().isElementDecl()) { log.debug(p.getTerm().asElementDecl().getName() + " element decl"); if (!visitedElements.contains(this.elementLabel(p.getTerm() .asElementDecl()))) { elementChildren.add(this .processChildParticle(p)); } } else if (p.getTerm().isModelGroupDecl() || p.getTerm().isModelGroup()) { log.debug("model group"); XSModelGroup group = null; if(p.getTerm().isModelGroupDecl()) { group = p.getTerm().asModelGroupDecl() .getModelGroup(); } else { group = p.getTerm().asModelGroup(); } XSParticle[] groupChildren = group.getChildren(); for (XSParticle gp : groupChildren) { if (!visitedElements.contains(this.elementLabel(gp.getTerm() .asElementDecl()))) { elementChildren.add(this .processChildParticle(gp)); } } } } log.debug("end particles for " + edecl.getName()); result = result.element("children", elementChildren); visitedElements.remove(this.elementLabel(edecl)); } } else { XSSimpleType simpleType = edecl.getType().asSimpleType(); // process enumerations if (simpleType != null) { JSONArray enumerations = this .processElementEnumerations(simpleType); if (enumerations.size() > 0) { result = result.element("enumerations", enumerations); } } result = result.element("name", edecl.getName()).element( "type", "string") .element("id", this.generateUniqueId()); if (namespace.length() > 0) { result = result.element("prefix", this .getPrefixForNamespace(namespace)); } } } else { log.error(edecl + " is null!..."); } result = result.element("mappings", new JSONArray()); if (!result.has("children") || result.getJSONArray("children").size() == 0) { if (result.getString("type").equals("anyType")) { result.put("type", "string"); } } return result; } private JSONObject processChildParticle(XSParticle p) { JSONObject child = this.getElementDescription(p.getTerm() .asElementDecl()); BigInteger maxOccurs = p.getMaxOccurs(); BigInteger minOccurs = p.getMinOccurs(); child = child.element("maxOccurs", maxOccurs).element("minOccurs", minOccurs); return child; } public JSONObject buildTemplate(JSONArray groups, String root) { Iterator<XSSchema> i = this.schemaSet.iterateSchema(); XSElementDecl rootElementDecl = null; while (i.hasNext()) { XSSchema s = i.next(); rootElementDecl = s.getElementDecl(root); if (rootElementDecl != null) { return buildTemplate(groups, rootElementDecl); } } return new JSONObject(); } public JSONObject buildTemplate(JSONArray groups, XSElementDecl rootElementDecl) { JSONObject result = new JSONObject(); String root = rootElementDecl.getName(); String namespace = rootElementDecl.getTargetNamespace(); //if(namespace.length() == 0) namespace = rootElementDecl.getOwnerSchema().getTargetNamespace(); log.debug("init"); result = result.element("mappings", new JSONArray()).element("id", "template_" + root); if (namespace.length() > 0) { String prefix = this.getPrefixForNamespace(namespace); result = result.element("prefix", prefix); } log.debug("check if group"); // check if root is a group element (button) Iterator gi = groups.iterator(); while (gi.hasNext()) { JSONObject group = (JSONObject) gi.next(); if (root.equals(group.getString("element"))) { return result.element("name", root).element("type", "group"); } } log.debug("get base type"); XSType xstype = rootElementDecl.getType().getBaseType(); while (xstype.getBaseType() != null) { if (xstype.getName().equals(xstype.getBaseType().getName())) break; if (xstype.getName().equalsIgnoreCase("string")) break; xstype = xstype.getBaseType(); } visitedElements.add(this.elementLabel(rootElementDecl)); result = result.element("name", rootElementDecl.getName()).element( "type", xstype.getName()); // element types XSComplexType complexType = rootElementDecl.getType().asComplexType(); XSSimpleType simpleType = rootElementDecl.getType().asSimpleType(); if(complexType!=null) { log.debug("is complex type"); XSContentType contentType = complexType.getContentType(); XSParticle particle = contentType.asParticle(); // process element attributes JSONArray attributes = this.processElementAttributes(complexType); result = result.element("attributes", attributes); // process children if (particle != null) { log.debug(particle == null); JSONArray elementChildren = new JSONArray(); ArrayList<XSParticle> array = this.getParticleChildren(particle); for (XSParticle p : array) { if (p.getTerm().isElementDecl()) { JSONObject child; if(!visitedElements.contains(this.elementLabel(p.getTerm().asElementDecl()))) { child = this.buildTemplate(groups, p.getTerm() .asElementDecl()); elementChildren.add(child); } } else if (p.getTerm().isModelGroupDecl()) { XSModelGroup group = p.getTerm().asModelGroupDecl() .getModelGroup(); XSParticle[] groupChildren = group.getChildren(); for (XSParticle gp : groupChildren) { String name = gp.getTerm().asElementDecl().getName(); log.debug(name); JSONObject child; if(!visitedElements.contains(this.elementLabel(gp.getTerm().asElementDecl()))) { child = this.buildTemplate(groups, gp.getTerm() .asElementDecl()); elementChildren.add(child); } } } } result = result.element("children", elementChildren); } } visitedElements.remove(this.elementLabel(rootElementDecl)); // process enumerations if (simpleType != null) { log.debug("is simple type"); JSONArray enumerations = this .processElementEnumerations(simpleType); if (enumerations.size() > 0) { result = result.element("enumerations", enumerations); } result = result.element("type", "string"); } return result; } private JSONArray processElementAttributes(XSComplexType complexType) { JSONArray attributes = new JSONArray(); /* * Collection<? extends XSAttributeUse> acollection = * complexType.getAttributeUses(); Iterator<? extends XSAttributeUse> * aitr = acollection.iterator(); */ Iterator<? extends XSAttributeUse> aitr = complexType .iterateAttributeUses(); while (aitr.hasNext()) { XSAttributeUse attributeUse = aitr.next(); XSAttributeDecl attributeDecl = attributeUse.getDecl(); String namespace = attributeDecl.getTargetNamespace(); //if(namespace.length() == 0) namespace = attributeDecl.getOwnerSchema().getTargetNamespace(); JSONObject attribute = new JSONObject().element("name", "@" + attributeDecl.getName()).element("id", "").element( "mappings", new JSONArray()); if (namespace.length() > 0) { attribute = attribute.element("prefix", this .getPrefixForNamespace(namespace)); } // check if it has a default value and assign it XmlString defaultValue = attributeDecl.getDefaultValue(); if (defaultValue != null && defaultValue.value.length() > 0) { attribute = attribute.element("default", defaultValue.value); } // check if it is required if (attributeUse.isRequired()) { attribute = attribute.element("minOccurs", "1"); } // check for enumerations in attributes XSSimpleType simpleType = attributeDecl.getType(); JSONArray enumerations = this .processElementEnumerations(simpleType); if (enumerations.size() > 0) { attribute = attribute.element("enumerations", enumerations); } attributes.add(attribute); } return attributes; } private JSONArray processElementEnumerations(XSSimpleType simpleType) { JSONArray enumerations = new JSONArray(); XSRestrictionSimpleType restriction = simpleType.asRestriction(); if (restriction != null) { Iterator<? extends XSFacet> i = restriction.getDeclaredFacets() .iterator(); while (i.hasNext()) { XSFacet facet = i.next(); if (facet.getName().equals(XSFacet.FACET_ENUMERATION)) { // log.debug("enumeration: " + facet.getValue().value); enumerations.add(facet.getValue().value); } } } return enumerations; } private String processElementAnnotation(XSElementDecl edecl) { String annotation = ""; if (edecl.getType().getAnnotation() != null && edecl.getType().getAnnotation().getAnnotation() != null) { annotation = (String) edecl.getType().getAnnotation() .getAnnotation(); annotation += "\n"; } if (edecl.getAnnotation() != null && edecl.getAnnotation().getAnnotation() != null) { annotation += (String) edecl.getAnnotation().getAnnotation(); } return annotation; } private String processAttributeAnnotation(XSAttributeDecl edecl) { String annotation = ""; if (edecl.getType().getAnnotation() != null && edecl.getType().getAnnotation().getAnnotation() != null) { annotation = (String) edecl.getType().getAnnotation() .getAnnotation(); annotation += "\n"; } if (edecl.getAnnotation() != null && edecl.getAnnotation().getAnnotation() != null) { annotation += (String) edecl.getAnnotation().getAnnotation(); } return annotation; } private ArrayList<XSParticle> getParticleChildren(XSParticle particle) { ArrayList<XSParticle> children = new ArrayList<XSParticle>(); // process children if (particle != null) { XSTerm term = particle.getTerm(); XSModelGroup group = null; if (term.isModelGroup()) { group = term.asModelGroup(); } else if (term.isModelGroupDecl()) { group = term.asModelGroupDecl().getModelGroup(); } if(group != null) { XSParticle[] particles = group.getChildren(); for (XSParticle p : particles) { if(p.getTerm().isElementDecl()) { children.add(p); } else { ArrayList<XSParticle> particleChildren = this.getParticleChildren(p); children.addAll(particleChildren); } } } } return children; } JSONObject documentation = new JSONObject(); public JSONObject buildDocumentation() { documentation = new JSONObject(); Iterator<XSElementDecl> i = this.schemaSet.iterateElementDecls(); while (i.hasNext()) { XSElementDecl e = i.next(); this.buildDocumentationFor(e); } return documentation; } private void buildDocumentationFor(XSElementDecl edecl) { String annotation = this.processElementAnnotation(edecl); String name = edecl.getName(); String namespace = edecl.getTargetNamespace(); if(namespace.length() == 0) { //namespace = edecl.getOwnerSchema().getTargetNamespace(); } String tag = name; if(namespace.length() != 0) { String prefix = this.getPrefixForNamespace(namespace); tag = prefix + ":" + name; } if (documentation.has(name)) { if (annotation.equals(documentation.getString(name))) { // System.out.println("documentation mismatch for: " + name); // System.out.println("new: " + annotation); // System.out.println("old: " + documentation.getString(name)); } } if (annotation.length() > 0) { documentation.element(tag, annotation); } XSComplexType complexType = edecl.getType().asComplexType(); if (complexType != null) { // proccess children XSContentType contentType = complexType.getContentType(); XSSimpleType simpleType = contentType.asSimpleType(); XSParticle particle = contentType.asParticle(); XSType xstype = edecl.getType().getBaseType(); while (xstype.getBaseType() != null) { if (xstype.getName().equals(xstype.getBaseType().getName())) break; if (xstype.getName().equalsIgnoreCase("string")) break; xstype = xstype.getBaseType(); } // log.debug("name: " + edecl.getName() + " orig: " + etype + // " type: " + type + " namespace: " + namespace); if (particle != null) { visitedElements.add(this.elementLabel(edecl)); // JSONArray elementChildren = new JSONArray(); ArrayList<XSParticle> array = this .getParticleChildren(particle); for (XSParticle p : array) { if (p.getTerm().isElementDecl()) { if (!visitedElements.contains(this.elementLabel(p.getTerm() .asElementDecl()))) { XSElementDecl child = p.getTerm().asElementDecl(); this.buildDocumentationFor(child); } } else if (p.getTerm().isModelGroupDecl()) { XSModelGroup group = p.getTerm().asModelGroupDecl() .getModelGroup(); XSParticle[] groupChildren = group.getChildren(); for (XSParticle gp : groupChildren) { if (!visitedElements.contains(this.elementLabel(gp.getTerm() .asElementDecl()))) { XSElementDecl child = gp.getTerm().asElementDecl(); this.buildDocumentationFor(child); } } } } visitedElements.remove(this.elementLabel(edecl)); } // process attributes Iterator<? extends XSAttributeUse> aitr = complexType .iterateAttributeUses(); while (aitr.hasNext()) { XSAttributeDecl attributeDecl = aitr.next().getDecl(); attributeDecl.getAnnotation(); String nm = attributeDecl.getTargetNamespace(); if(nm.length() == 0) { //nm = attributeDecl.getOwnerSchema().getTargetNamespace(); } tag = "@" + attributeDecl.getName(); if(nm.length() != 0) { String prefix = this.getPrefixForNamespace(nm); tag = "@" + prefix + ":" + attributeDecl.getName(); } String value = this.processAttributeAnnotation(attributeDecl); if (value.length() > 0) { documentation.element(tag, value); } } } } public JSONObject getElementDescription(String element) { JSONObject result = null; Iterator<XSSchema> i = schemaSet.iterateSchema(); while (i.hasNext()) { XSSchema s = i.next(); XSElementDecl edecl = s.getElementDecl(element); if (edecl != null) { // System.out.println("found direct: " + element); return this.getElementDescription(edecl); } else { Map<String, XSElementDecl> map = s.getElementDecls(); Iterator<String> k = map.keySet().iterator(); while (k.hasNext()) { String key = k.next(); edecl = map.get(key); result = getElementDescriptionFromParent(edecl, element); if (result != null) { // System.out.println("found in parent: " + element + " - " + edecl.getName()); return result; } } } } return result; } public JSONObject getElementDescriptionFromParent(XSElementDecl parent, String element) { JSONObject result = null; XSComplexType complexType = parent.getType().asComplexType(); if (complexType != null) { XSContentType contentType = complexType.getContentType(); XSParticle particle = contentType.asParticle(); if (particle != null) { visitedElements.add(this.elementLabel(parent)); ArrayList<XSParticle> array = this .getParticleChildren(particle); for (XSParticle p : array) { if (p.getTerm().isElementDecl()) { String name = p.getTerm().asElementDecl().getName(); if (!visitedElements.contains(p.getTerm().asElementDecl())) { if (name.equals(element)) { result = this.processChildParticle(p); return result; } } } else if (p.getTerm().isModelGroupDecl()) { XSModelGroup group = p.getTerm().asModelGroupDecl() .getModelGroup(); XSParticle[] groupChildren = group.getChildren(); for (XSParticle gp : groupChildren) { String name = gp.getTerm().asElementDecl() .getName(); if (!visitedElements.contains(p.getTerm().asElementDecl())) { if (name.equals(element)) { result = this.processChildParticle(gp); return result; } } } } } visitedElements.remove(this.elementLabel(parent)); } } return result; } public JSONObject getRootElementDescription(String element) { Iterator<XSSchema> i = schemaSet.iterateSchema(); while (i.hasNext()) { XSSchema s = i.next(); XSElementDecl edecl = s.getElementDecl(element); if (edecl != null) { // log.debug("found: " + edecl.getName() + " in " + // s.getTargetNamespace()); JSONObject result = this.getElementDescription(edecl); return result; } } return new JSONObject().element("error", "root element " + element + " not found in any schema set"); } public JSONObject getAnyRootElementDescription() { Iterator<XSSchema> i = schemaSet.iterateSchema(); while (i.hasNext()) { XSSchema s = i.next(); Map<String, XSElementDecl> map = s.getElementDecls(); Iterator<String> k = map.keySet().iterator(); while (k.hasNext()) { String key = k.next(); JSONObject o = this.getRootElementDescription(key); if (o != null) return o; } } return null; } }