package gr.ntua.ivml.athena.xml.xsd; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; 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.ForeignAttributes; 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 Map<String, String> getNamespaces() { return this.namespaces; } public void setNamespaces(HashMap<String, String> map) { this.namespaces = map; } private void initXSSchema(String schemaFileName) { try { 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); this.parser.parse(new File(schemaFileName)); } this.schemaSet = this.parser.getResult(); /* Iterator<XSSchema> s = this.schemaSet.iterateSchema(); while(s.hasNext()) { XSSchema schema = s.next(); Iterator<XSElementDecl> e = schema.iterateElementDecls(); while(e.hasNext()) { XSElementDecl decl = e.next(); } } */ } catch (Exception e) { e.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; } public JSONObject getElementDescription(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(); } private ArrayList<String> visitedElements = new ArrayList<String>(); public JSONObject getElementDescription(XSElementDecl edecl) { //log.debug("getElementDescription for " + edecl.getName()); JSONObject result = new JSONObject(); if(edecl != null) { XSComplexType complexType = edecl.getType().asComplexType(); //log.debug("complexType: " + complexType); if(complexType != null) { XSContentType contentType = complexType.getContentType(); //XSContentType contentType = complexType.getExplicitContent(); 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(); String namespace = edecl.getTargetNamespace(); //log.debug("name: " + name + " type: " + type + " namespace: " + namespace); result = result.element("name", name) .element("id", "") .element("type", type); 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); } } /* deprecated - annotation is loaded externally // process annotation; String annotation = this.processElementAnnotation(edecl); result.element("annotation", annotation); */ // process children if(particle != null){ visitedElements.add(edecl.getName()); JSONArray elementChildren = new JSONArray(); ArrayList<XSParticle> array = this.getParticleChildren(particle); for(XSParticle p: array) { if(p.getTerm().isElementDecl()) { if(!visitedElements.contains(p.getTerm().asElementDecl().getName())) { JSONObject child = this.getElementDescription(p.getTerm().asElementDecl()); int maxOccurs = p.getMaxOccurs(); int minOccurs = p.getMinOccurs(); child = child.element("maxOccurs", maxOccurs).element("minOccurs", minOccurs); elementChildren.add(child); } } } result = result.element("children", elementChildren); visitedElements.remove(edecl.getName()); } } 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); } String namespace = edecl.getTargetNamespace(); if(namespace.length() > 0) { result = result.element("prefix", this.getPrefixForNamespace(namespace)); } } result = result.element("name", edecl.getName()) .element("type", "string") .element("id", this.generateUniqueId()); } } else { log.error(edecl + " is null!..."); } result = result.element("mappings", new JSONArray()); //this.elementCache.put(result.get("id"), result); return result; } public JSONObject buildTemplate(JSONArray groups, String root) { Iterator<XSSchema> i = this.schemaSet.iterateSchema(); while(i.hasNext()) { XSSchema s = i.next(); XSElementDecl 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 nm = rootElementDecl.getTargetNamespace(); String prefix = this.getPrefixForNamespace(nm); result = result.element("mappings", new JSONArray()).element("id", "template_" + root); if(nm.length() > 0) { result = result.element("prefix", prefix); } // 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"); } } 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(); } result = result.element("name", rootElementDecl.getName()).element("type", xstype.getName()); // element types XSComplexType complexType = rootElementDecl.getType().asComplexType(); XSContentType contentType = complexType.getContentType(); XSSimpleType simpleType = contentType.asSimpleType(); XSParticle particle = contentType.asParticle(); // process element 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){ JSONArray elementChildren = new JSONArray(); ArrayList<XSParticle> array = this.getParticleChildren(particle); for(XSParticle p: array) { if(p.getTerm().isElementDecl()) { JSONObject child; child = this.buildTemplate(groups, p.getTerm().asElementDecl()); elementChildren.add(child); } } result = result.element("children", elementChildren); } 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(); 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(); if(term.isModelGroup()){ XSModelGroup xsModelGroup = term.asModelGroup(); XSParticle[] particles = xsModelGroup.getChildren(); for(XSParticle p : particles ){ children.add(p); } } } 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(); 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(name, 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(); } String etype = edecl.getTargetNamespace(); String type = xstype.getName(); String typenamespace = xstype.getTargetNamespace(); String namespace = edecl.getType().getBaseType().getTargetNamespace(); log.debug("name: " + edecl.getName() + " orig: " + etype + " type: " + type + " namespace: " + namespace); if(particle != null){ visitedElements.add(edecl.getName()); // JSONArray elementChildren = new JSONArray(); ArrayList<XSParticle> array = this.getParticleChildren(particle); for(XSParticle p: array) { if(p.getTerm().isElementDecl()) { if(!visitedElements.contains(p.getTerm().asElementDecl().getName())) { XSElementDecl child = p.getTerm().asElementDecl(); this.buildDocumentationFor(child); } } } visitedElements.remove(edecl.getName()); } // process attributes Iterator<? extends XSAttributeUse> aitr = complexType.iterateAttributeUses(); while(aitr.hasNext()){ XSAttributeDecl attributeDecl = aitr.next().getDecl(); attributeDecl.getAnnotation(); String tag = "@" + attributeDecl.getName(); String value = this.processAttributeAnnotation(attributeDecl); if(value.length() > 0) { documentation.element(tag, value); } } } } }