package org.ebayopensource.turmeric.eclipse.typelibrary.utils.importtypes; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.apache.commons.lang.StringUtils; import org.ebayopensource.turmeric.common.config.LibraryType; import org.ebayopensource.turmeric.eclipse.repositorysystem.core.SOAGlobalRegistryAdapter; import org.ebayopensource.turmeric.eclipse.typelibrary.utils.importtypes.xsdpiece.IXSDPiece; import org.ebayopensource.turmeric.eclipse.typelibrary.utils.importtypes.xsdpiece.SchemaTypePiece; import org.ebayopensource.turmeric.eclipse.typelibrary.utils.importtypes.xsdpiece.StringPiece; import org.ebayopensource.turmeric.eclipse.typelibrary.utils.importtypes.xsdpiece.TypeDocumentationPiece; import org.ebayopensource.turmeric.eclipse.typelibrary.utils.importtypes.xsdpiece.TypeLibraryNamePiece; import org.ebayopensource.turmeric.eclipse.typelibrary.utils.importtypes.xsdpiece.TypeNamePiece; import org.ebayopensource.turmeric.eclipse.typelibrary.utils.importtypes.xsdpiece.TypeNamespacePiece; import org.ebayopensource.turmeric.eclipse.typelibrary.utils.importtypes.xsdpiece.TypeQNamePiece; import org.ebayopensource.turmeric.eclipse.typelibrary.utils.importtypes.xsdpiece.TypeRelatedContent; import org.ebayopensource.turmeric.eclipse.typelibrary.utils.importtypes.xsdpiece.XSDContentList; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; /** * This class is used to parse XSD content and cut XSD content from it. Also it * will handle dependencies, add document node to type. * * @author mzang * */ public class ImportTypesFromXSDParser extends DefaultHandler { public void cutXSD(String wsdlPath) throws SAXException, IOException, ParserConfigurationException { SAXParserFactory saxfac = SAXParserFactory.newInstance(); saxfac.setFeature("http://xml.org/sax/features/namespace-prefixes", true); saxfac.setNamespaceAware(true); saxfac.setXIncludeAware(true); saxfac.setValidating(false); SAXParser saxParser = saxfac.newSAXParser(); saxParser.parse(wsdlPath, this); } private static final String REPLACE = "${IMPORT_TYPE_REPLACEMENT}"; private static final String REPLACE_SPLITER = "\\$\\{IMPORT_TYPE_REPLACEMENT\\}"; // variables that need to be clear up once a type is processed. private XSDContentList content = new XSDContentList(); private String typeName = null; private String tlName = null; private String tlNamespace = null; private String documentation = null; private int documentIndex = -1; private int documentAnnotationIndex = -1; private boolean acceptContent = false; private List<String> errors = new ArrayList<String>(); private List<String> warnings = new ArrayList<String>(); // variables for all types in the same schema node. private Map<String, String> nsMappingSchema = new HashMap<String, String>(); private String targetNamespace = null; private String schemaXMLNS = null; private String schemaQName = null; private Map<String, String> tpyeAttrs = new HashMap<String, String>(); // variables for all types in current wsdl. private Map<String, TypeModel> xsds = new HashMap<String, TypeModel>(); private Map<String, TypeModel> refferedTLTypes = null; private Map<String, String> outerNSMapping = new HashMap<String, String>(); private List<NodeQName> xmlPath = new ArrayList<NodeQName>(); private static int noTypeNameCounter = 0; public static void clearNoTypeNameCounter() { noTypeNameCounter = 0; } public void setOuterNSMappint(Map<String, String> outerNSMapping) { this.outerNSMapping = outerNSMapping; } // node QNames private static final String TYPE_NS = "http://www.w3.org/[0-9]{4}/XMLSchema[/]{0,}"; private static final Pattern SCHEMA_NS_PATTERN = Pattern.compile(TYPE_NS, Pattern.CASE_INSENSITIVE); private static String SCHEMA_DEF_NAME = "schema"; private static String COMPLEX_TYPE_NODE = "complexType"; private static String SIMPLE_TYPE_NODE = "simpleType"; private static String ANNOTATION_NODE = "annotation"; private static String TYPE_DOCUMENTATION_NODE = "documentation"; private static String APPINFO_NODE = "appinfo"; private static String TL_SOURCE_NODE = "typeLibrarySource"; private static String ELEMENT_NODE = "element"; private static String ATTRIBUTE_NODE = "attribute"; private static String UNION_NODE = "union"; private static String SEQUENCE_NODE = "sequence"; private static String CHOICE_NODE = "choice"; private static String XS_ANY_NODE = "any"; // attribute names private static String ATTR_TARGET_NS_LB = "targetNamespace"; private static String TYPE_LIBRARY_ATTR = "library"; private static String TYPE_NS_ATTR = "namespace"; private static String TYPE_NAME_ATTR = "name"; private static String TYPE_BASE_ATTR = "base"; private static String TYPE_TYPE_ATTR = "type"; private static String ELEMENT_REF = "ref"; // namespace in attribute value private static String XS_NS = "xs:"; private static String XML_NS = "xmlns:"; private static String XML_NS_ATTR = "xmlns"; // import statement and included statement. private static final String TYPE_INCLUDE_NODE = "\r\n\t<xs:include schemaLocation=\"typelib://" + REPLACE + "//" + REPLACE + ".xsd\" />"; private static final String TYPE_IMPORT_NODE = "\r\n\t<xs:import namespace=\"{0}\" \r\n" + "\t\tschemaLocation=\"typelib://{1}//{2}.xsd\" />"; // document node private static final String ANNOTATION_DOC_NODE = "\r\n\t<xs:annotation>\r\n" + "\t\t<xs:documentation>\r\n" + "\t\t\t" + REPLACE + "\r\n\t\t</xs:documentation>\r\n" + "\t</xs:annotation>\r\n"; private static final String DOC_NODE = "\r\n\t\t<xs:documentation>\r\n" + "\t\t\t" + REPLACE + "\r\n\t\t</xs:documentation>\r\n"; /* * fill with all external type namespace. and real type library namespace */ private static final String TYPE_SCHEMA_NODE_1 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + "<xs:schema "; private static final String TYPE_SCHEMA_NODE_2 = "\r\n" + "\t attributeFormDefault=\"unqualified\" elementFormDefault=\"qualified\" " + "targetNamespace=\""; private static final String TYPE_SCHEMA_NODE_3 = "\" version=\"1.0.0\">\r\n"; private static final String TYPE_SCHEMA_NODE_END = "\r\n</xs:schema>"; private int schemaStartIndex = 0; public void useWSDLModel() { this.schemaStartIndex = 2; } public void setSchemaStartIndex(int schemaStartIndex) { this.schemaStartIndex = schemaStartIndex; } private String getNameForNoTypeWithoutName() { noTypeNameCounter++; return "NoNameType" + noTypeNameCounter; } private static Map<String, LibraryType> LIB_TYPE_MAPPING = new HashMap<String, LibraryType>(); static { List<LibraryType> allTypesList; try { allTypesList = SOAGlobalRegistryAdapter.getInstance() .getGlobalRegistry().getAllTypes(); for (LibraryType libType : allTypesList) { String namespace = libType.getNamespace(); String typeName = libType.getName(); LIB_TYPE_MAPPING.put(namespace + ":" + typeName, libType); } } catch (Exception e) { e.printStackTrace(); } } public Collection<TypeModel> getTypeModels() { return this.xsds.values(); } public Map<String, TypeModel> getTypeModelMap() { return this.xsds; } private static boolean isSchemaNS(String namespace) { Matcher matcher = SCHEMA_NS_PATTERN.matcher(namespace); return matcher.matches(); } private boolean schemaDef() { if (xmlPath.size() < schemaStartIndex + 1) { return false; } NodeQName qName = xmlPath.get(schemaStartIndex); return (qName.ns == NS.SCHEMA) && SCHEMA_DEF_NAME.equals(qName.localName); } private boolean typeDef() { if (xmlPath.size() < schemaStartIndex + 2) { return false; } NodeQName qName = xmlPath.get(schemaStartIndex + 1); return (qName.ns == NS.SCHEMA) && (COMPLEX_TYPE_NODE.equals(qName.localName) || SIMPLE_TYPE_NODE .equals(qName.localName)); } private boolean typeAnotationDef() { if (xmlPath.size() < schemaStartIndex + 3) { return false; } NodeQName qName = xmlPath.get(schemaStartIndex + 2); return (qName.ns == NS.SCHEMA) && (ANNOTATION_NODE.equals(qName.localName)); } private boolean typeAnotationDocDef() { if (xmlPath.size() < schemaStartIndex + 4) { return false; } NodeQName qName = xmlPath.get(schemaStartIndex + 3); return (qName.ns == NS.SCHEMA) && (TYPE_DOCUMENTATION_NODE.equals(qName.localName)); } private boolean typeAnotationAppInfoDef() { if (xmlPath.size() < schemaStartIndex + 4) { return false; } NodeQName qName = xmlPath.get(schemaStartIndex + 3); return (qName.ns == NS.SCHEMA) && (APPINFO_NODE.equals(qName.localName)); } private boolean typeAnotationTLSourceDef() { if (xmlPath.size() < schemaStartIndex + 5) { return false; } NodeQName qName = xmlPath.get(schemaStartIndex + 4); return (TL_SOURCE_NODE.equals(qName.localName)); } // private boolean typeElementDef(NodeQName qName) { // if (qName == null) { // return false; // } // return (qName.ns == NS.SCHEMA) // && (ELEMENT_NODE.equals(qName.localName)); // } private boolean typeAnyEleDef(NodeQName qName) { if (qName == null) { return false; } return (qName.ns == NS.SCHEMA) && (XS_ANY_NODE.equals(qName.localName)); } private boolean isSchemaNode() { if (xmlPath.size() != schemaStartIndex + 1) { return false; } return schemaDef(); } // private boolean isInSchemaNode() { // if (xmlPath.size() <= schemaStartIndex + 2) { // return false; // } // return wsdlDef() && schemaDef(); // } private boolean isTypeNode() { if (xmlPath.size() != schemaStartIndex + 2) { return false; } return schemaDef() && typeDef(); } private boolean isInTypeNode() { if (xmlPath.size() < schemaStartIndex + 3) { return false; } return schemaDef() && typeDef(); } private boolean isTypeLibrarySourceNode() { if (xmlPath.size() != schemaStartIndex + 5) { return false; } return schemaDef() && typeDef() && typeAnotationDef() && typeAnotationAppInfoDef() && typeAnotationTLSourceDef(); } private boolean isTypeDocumentationNode() { if (xmlPath.size() != schemaStartIndex + 4) { return false; } return schemaDef() && typeDef() && typeAnotationDef() && typeAnotationDocDef(); } private boolean isTypeAnnotationNode() { if (xmlPath.size() != schemaStartIndex + 3) { return false; } return schemaDef() && typeDef() && typeAnotationDef(); } private boolean isTypeDependencyRelated(String attrName, String attrValue) { if (TYPE_BASE_ATTR.equals(attrName) == false && TYPE_TYPE_ATTR.equals(attrName) == false) { return false; } return true; } private static boolean isBasicTypeSchemaNamespace(String namespace) { if (namespace == null) { return false; } Matcher mather = SCHEMA_NS_PATTERN.matcher(namespace); return mather.matches(); } private boolean isInW3CBasicSchemaTypeNamespace(String uri, String typeQName) throws SAXException { String[] qName = typeQName.split(":"); if (qName.length == 1) { return isBasicTypeSchemaNamespace(uri); } if (qName.length != 2) { throw new SAXException( "Invalidated type. Type QName is \"namespacepart:typename\":" + typeQName); } String namespace = nsMappingSchema.get(XML_NS + qName[0]); return isBasicTypeSchemaNamespace(namespace); } private static LibraryType findTypeInTypeLibrary(String namespace, String typeName) { return LIB_TYPE_MAPPING.get(namespace + ":" + typeName); } /** * key part of this method is to handle dependency. there are four kinds of * dependencies. 1) Using a standard type from W3C schema type. How to check * - check the namespace value is W3C_TYPE_NS or not. How to handle - For * this kind of dependency, just adding a xmlns definition attribute to the * xs:schema node. 2) Using a type library type. How to check - using the * namespace and type name to try to find such a type in type library. If * found, then it is a dependency to a type library type. Otherwise, it is * not a type library dependency type. How to handle - for a TL type * dependency, need to add a xs:import statement. TYPE_IMPORT is provided to * use. just provide the type library name and the type name. also need to * adding a xmlns definition attribute to the xs:schema node. 3) An internal * type dependency. How to check - if 2) not match and the namespace of that * dependency type is the same as the namespace of current type, so it must * be an internal dependency. How to handle - for internal dependency, there * are two more thing to handle. First, the name of the dependency type may * be changed. Second, the namespace of the dependency type may changed (in * fact, must change). So for the xsd content, the attribute where used the * dependency type, must use "${}" and waiting to be replaced (already use * it when adding content to XSD). For the XSD header, also should use "${}" * and waiting to be replaced. 4) An internal dependency which in fact is * and external type library dependency. How to check - find the dependency * from xsds map and method isTypeLibraryType returns true. How to handle - * first, it is a type library dependency, so there is no need to using free * marker because the type name will never change. Set the type name value * to be it self. Then need to handle the namespace part because the * namespace part is incorrect. * * * Notice that we need to check 2) first because a type may have the same * namespace with an existing type library. just like a class named MyClass * in java.lang package. * * @param xsds * TypeModels to be processed * @return TypeModel that no need to be imported. * */ public static Map<String, TypeModel> postProcessTypes( Map<String, TypeModel> xsds) throws SAXException { Set<String> keys = xsds.keySet(); // a map to store types that refers to type library type Map<String, TypeModel> referToTLType = new HashMap<String, TypeModel>(); Set<String> typeLibTypeKey = new HashSet<String>(); for (String key : keys) { TypeModel model = xsds.get(key); // type library name and namespace are both validated if (model.isNeedToImport() == false) { typeLibTypeKey.add(key); referToTLType.put(key, model); continue; } Map<String, String> typeNSMapping = model.getNsMappingSchema(); /* * the following Collection instance using set to avoid duplication * all xmlns attributes for current type. The list is used to store * contents to be added to XSD. */ // all xmlns mappings for current type. Set<String> xmlnsNames = new HashSet<String>(); List<IXSDPiece> xmlns = new ArrayList<IXSDPiece>(); // all include node for current type Set<String> includeTypeNames = new HashSet<String>(); List<IXSDPiece> includes = new ArrayList<IXSDPiece>(); // all importNode for current type Set<String> importTLTypeNames = new HashSet<String>(); List<IXSDPiece> imports = new ArrayList<IXSDPiece>(); // key-value for namespace and xmlns, just for case 4) Map<String, String> namespaceToXMLNS = new HashMap<String, String>(); /* * adding xmlns to XMLNS if needed. so that the current xsd will * just use node name without prefix such as "xs:" or "xsd:". It * doesn't have a name. So no need to add an element to * "xmlnsNames". */ if (model.getSchemaXMLNS() != null) { xmlns.add(new StringPiece("\r\n\txmlns=\"" + model.getSchemaXMLNS() + "\"")); } /* * except schema basic type, the "xs:schema" is also kind of needing * "xmlns:xs". Add it if needed. */ { String schemaQName = model.getSchemaQName(); String[] qNames = schemaQName.split(":"); if (qNames.length == 2) { xmlns.add(new StringPiece("\r\n\txmlns:" + qNames[0] + "=\"http://www.w3.org/2001/XMLSchema\"")); xmlnsNames.add(qNames[0]); } } /* * go though all pieces, handle dependencies. */ for (IXSDPiece piece : model.getTypeContent().getContentList()) { if (piece instanceof StringPiece) { continue; } if (piece instanceof TypeRelatedContent) { TypeRelatedContent tp = (TypeRelatedContent) piece; if (tp.getQName() == null) { // null means current type. used for type name. tp.setModel(model); continue; } } // basic type if (piece instanceof SchemaTypePiece) { SchemaTypePiece sp = (SchemaTypePiece) piece; String dep = sp.getContent(); String[] qName = dep.split(":"); if (qName.length != 2) { throw new SAXException( "Invalidated type. Type QName should be format" + " \"namespacepart:typename\":" + dep); } String nsShort = qName[0]; String shortNSDefinition = XML_NS + nsShort; if (xmlnsNames.contains(nsShort)) { /* xmlns:xs is written to schema header by default. */ continue; } xmlns.add(new StringPiece("\r\n\t" + shortNSDefinition + "=\"" + sp.getNamespace() + "\"")); xmlnsNames.add(nsShort); continue; } // if not dependency related here, jump to next. if ((piece instanceof TypeQNamePiece) == false) { continue; } TypeQNamePiece depPiece = (TypeQNamePiece) piece; String dep = depPiece.getQName(); String[] qName = dep.split(":"); // in fact, before an dependency is added ,this is already // checked. if (qName.length != 2) { throw new SAXException( "Invalidated type. Type QName should be in format " + "\"namespacepart:typename\":" + dep); } // such as "tns" String nsShort = qName[0]; // such as "xml:tns" String shortNSDefinition = XML_NS + nsShort; // full namespace, such as http://www.example.com/namespace String depNamespace = typeNSMapping.get(shortNSDefinition); // type name, such as ServiceRequest String depTypeName = qName[1]; // try to find the type in type library using current namespace // and type name. LibraryType typeLibratyType = findTypeInTypeLibrary( depNamespace, depTypeName); TypeModel depInternalType = xsds.get(depNamespace + ":" + depTypeName); depPiece.setXmlns(nsShort); depPiece.setModel(depInternalType); /* * if this type refer to a type in type library, use the new * namespace and get it. */ if (depInternalType != null && depInternalType.isTypeLibraryType()) { typeLibratyType = findTypeInTypeLibrary( depInternalType.getTypelibRefNamespace(), depTypeName); } /* * if the dependency is found in current schema file and it is * also need to be imported (not refer to a TL type). then it is * a common internal dependency. */ if ((depInternalType != null && depInternalType .isNeedToImport() == true)) { // use type in wsdl depPiece.setModel(depInternalType); if (xmlnsNames.contains(nsShort) == false) { xmlnsNames.add(nsShort); xmlns.add(new StringPiece("\r\n\t" + shortNSDefinition + "=\"")); xmlns.add(new TypeNamespacePiece(dep, depInternalType)); xmlns.add(new StringPiece("\"")); } if (includeTypeNames.contains(depTypeName) == false && StringUtils.equals(depTypeName, model.getTypeName()) == false) { includeTypeNames.add(depTypeName); TypeLibraryNamePiece libName = new TypeLibraryNamePiece( dep, depInternalType); TypeNamePiece typeName = new TypeNamePiece(dep, depInternalType); String include = syncXSDPrefix(TYPE_INCLUDE_NODE, model.getSchemaQName()); includes.addAll(addToXSDContentListBatch(include, libName, typeName)); } model.addInternalDependency(depInternalType); } /* * if the dependency is NOT found in current schema file but * found in type library. Then it is a common Type Library Type * dependency. */ else if ((typeLibratyType != null) && (depInternalType == null)) { // use type library type if (xmlnsNames.contains(nsShort) == false) { xmlnsNames.add(nsShort); xmlns.add(new StringPiece("\r\n\t" + shortNSDefinition + "=\"" + depNamespace + "\"")); } String importStr = syncXSDPrefix(TYPE_IMPORT_NODE, model.getSchemaQName()); String importNode = MessageFormat.format(importStr, depNamespace, typeLibratyType.getLibraryInfo() .getLibraryName(), depTypeName); depPiece.setLiteralTypeName(depTypeName); if (importTLTypeNames.contains(importNode) == false) { importTLTypeNames.add(importNode); imports.add(new StringPiece(importNode)); } } /* * if the dependency is not only found in current schema file * but also found in type library, AND it has validated TL name * and Namespace attribute. Then it is a referred Type Library * Type dependency. */ else if ((typeLibratyType != null) && (depInternalType != null && depInternalType .isTypeLibraryType() == true)) { // this a a wsdl type refer to typle library. use type // library type // namespace of the TL type String typelibNamespace = depInternalType .getTypelibRefNamespace(); // tl name of the found type String typeLibraryName = typeLibratyType.getLibraryInfo() .getLibraryName(); // tl name defined in the typeLibrarySource node. String refTypeLibraryName = depInternalType .getTypelibRefName(); // tl type name; String typeLibTypeName = depInternalType.getTypeName(); /* * the type library name found in typeLibrarySource is not * the same as the actual type library name found in type * registry, add a warning about this. */ if (typeLibraryName.equals(refTypeLibraryName) == false) { String tlNameWarning = "It is declared in typeLibrarySource node that dependency type \"" + typeLibTypeName + "\" is in type library \"" + refTypeLibraryName + "\". But this type is found in type library \"" + typeLibraryName + "\""; model.addWarning(tlNameWarning); } String xmlnsTL = namespaceToXMLNS.get(typelibNamespace); if (xmlnsTL == null) { xmlnsTL = createNewXMLNS(xmlnsNames); xmlnsNames.add(xmlnsTL); namespaceToXMLNS.put(typelibNamespace, xmlnsTL); xmlns.add(new StringPiece("\r\n\t" + XML_NS + xmlnsTL + "=\"" + typelibNamespace + "\"")); } depPiece.setXmlns(xmlnsTL); depPiece.setLiteralTypeName(typeLibTypeName); String importNode = MessageFormat.format(TYPE_IMPORT_NODE, typelibNamespace, typeLibraryName, typeLibTypeName); if (importTLTypeNames.contains(importNode) == false) { importTLTypeNames.add(importNode); imports.add(new StringPiece(importNode)); } } else if ((typeLibratyType == null) && (depInternalType == null)) { model.addWarning("Dependency \"" + dep + "\" can't be resolved. It is not " + "found in source file or type library."); } else if ((typeLibratyType == null) && (depInternalType != null && depInternalType .isTypeLibraryType() == true)) { model.addWarning("Dependency \"" + dep + "\" can't be resolved. Its definition is found in current " + "schema file. It is declared" + " as a type library type. " + " But it is not found in Type Library."); } else if (depInternalType != null && depInternalType.isTypeLibrarySourceInvalidated() == false) { model.addWarning("Dependency \"" + dep + "\" is not validated. Its definition is found in current " + "schema file. But its typeLibrarySource node, library " + "or namespace attribute is missed."); } } List<IXSDPiece> extraContent = new ArrayList<IXSDPiece>(); String typeSchemaNode1 = syncXSDPrefix(TYPE_SCHEMA_NODE_1, model.getSchemaQName()); extraContent.add(new StringPiece(typeSchemaNode1)); extraContent.addAll(xmlns); String typeSchemaNode2 = syncXSDPrefix(TYPE_SCHEMA_NODE_2, model.getSchemaQName()); extraContent.add(new StringPiece(typeSchemaNode2)); extraContent .add(new TypeNamespacePiece(model.getNamespace(), model)); String typeSchemaNode3 = syncXSDPrefix(TYPE_SCHEMA_NODE_3, model.getSchemaQName()); extraContent.add(new StringPiece(typeSchemaNode3)); extraContent.addAll(imports); extraContent.addAll(includes); extraContent.add(new StringPiece("\r\n")); model.getTypeContent().insert(0, extraContent); String schemaNodeEnd = syncXSDPrefix(TYPE_SCHEMA_NODE_END, model.getSchemaQName()); model.getTypeContent().add(schemaNodeEnd); } for (String removeKey : typeLibTypeKey) { xsds.remove(removeKey); } return referToTLType; } private static String createNewXMLNS(Collection<String> existingXMLNS) { int counter = 1; String startXMLNS = "tns"; while (true) { String xmlns = startXMLNS + counter; if (existingXMLNS.contains(xmlns) == false) { return startXMLNS + counter; } counter++; } } @Override public void endDocument() throws SAXException { refferedTLTypes = postProcessTypes(xsds); // test(); } public Map<String, TypeModel> getReferedTLTypes() { return refferedTLTypes; } public void test(String path) throws SAXException { File file = new File(path); File parentFile = file.getParentFile(); String wsdlFileName = file.getName(); wsdlFileName = wsdlFileName.substring(0, wsdlFileName.lastIndexOf('.')); File xsdFolder = new File(parentFile, wsdlFileName); if (xsdFolder.exists() == false || xsdFolder.isDirectory() == false) { boolean createFolder = xsdFolder.mkdir(); if (createFolder == false) { throw new SAXException("Unable to create folder:" + xsdFolder); } } Set<String> keys = xsds.keySet(); for (String key : keys) { String fileName = key.substring(key.lastIndexOf(':') + 1); TypeModel model = xsds.get(key); if (model.isNeedToImport() == false) { continue; } model.setTypeLibName("TEMPLIBNAME"); try { String xsdRealContent = null; File xsdFile = new File(xsdFolder, fileName + ".xsd"); try { xsdRealContent = model.getTypeContent().toString(); } catch (Exception ex) { ex.printStackTrace(); } if (xsdFile.exists() == true && xsdFile.isFile() == true) { xsdFile.delete(); } xsdFile.createNewFile(); PrintWriter pw = new PrintWriter(xsdFile); pw.write(xsdRealContent); pw.flush(); pw.close(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } if (xsdFolder.list() == null || xsdFolder.list().length == 0) { System.err.println("Parse failed:\t" + xsdFolder.toString()); } else { System.out.println(xsdFolder.toString() + "\t" + xsdFolder.list().length); } } private boolean hasTypeInElement() { if (xmlPath.size() < 2) { return false; } NodeQName eleAttrNode = xmlPath.get(xmlPath.size() - 2); NodeQName typeNode = xmlPath.get(xmlPath.size() - 1); return (ELEMENT_NODE.equals(eleAttrNode.localName) || ATTRIBUTE_NODE .equals(eleAttrNode.localName)) && (COMPLEX_TYPE_NODE.equals(typeNode.localName) || SIMPLE_TYPE_NODE .equals(typeNode.localName)); } private boolean hasUnionInType() { if (xmlPath.size() < 2) { return false; } NodeQName typeNode = xmlPath.get(xmlPath.size() - 2); NodeQName unionNode = xmlPath.get(xmlPath.size() - 1); return UNION_NODE.endsWith(unionNode.localName) && (COMPLEX_TYPE_NODE.equals(typeNode.localName) || SIMPLE_TYPE_NODE .equals(typeNode.localName)); } private boolean hasChoiceInType() { if (xmlPath.size() < 3) { return false; } NodeQName typeNode = xmlPath.get(xmlPath.size() - 3); NodeQName seqNode = xmlPath.get(xmlPath.size() - 2); NodeQName choiceNode = xmlPath.get(xmlPath.size() - 1); return CHOICE_NODE.endsWith(choiceNode.localName) && SEQUENCE_NODE.equals(seqNode.localName) && (COMPLEX_TYPE_NODE.equals(typeNode.localName) || SIMPLE_TYPE_NODE .equals(typeNode.localName)); } private boolean isElementNode() { if (xmlPath.size() < 0) { return false; } NodeQName eleNode = xmlPath.get(xmlPath.size() - 1); return ELEMENT_NODE.endsWith(eleNode.localName); } /** * start to parse an element */ @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { NS ns = NS.OTHER; if (isSchemaNS(uri)) { ns = NS.SCHEMA; } NodeQName currNode = new NodeQName(uri, ns, localName); xmlPath.add(currNode); // current node is xs:schema if (isSchemaNode() == true) { // first, put all NS mappings of WSDL to schema NS mapping. nsMappingSchema.putAll(outerNSMapping); // set schemaQName schemaQName = qName; for (int index = 0; index < attributes.getLength(); index++) { String attrName = attributes.getQName(index); if (attrName.toLowerCase().startsWith((XML_NS))) { /* * store the namespace attributes in the nsMappingSchema. * this is after putting ns attrs into nsMappingWSDL. so if * there are any duplicate ns definitions, the one in type * node will be used. */ nsMappingSchema.put(attrName, attributes.getValue(index)); } else { // put other attributes into tpyeAttrs map. tpyeAttrs.put(attrName, attributes.getValue(index)); } // get xmlns attribute value if (XML_NS_ATTR.equals(attrName)) { schemaXMLNS = attributes.getValue(index); } } targetNamespace = tpyeAttrs.get(ATTR_TARGET_NS_LB); return; } // current node is typeLibrarySource node, get the library value and // namespace value if (isTypeLibrarySourceNode() == true) { tlName = attributes.getValue(TYPE_LIBRARY_ATTR); tlNamespace = attributes.getValue(TYPE_NS_ATTR); } if (isTypeDocumentationNode() == true) { documentation = ""; } boolean inTypeNode = isInTypeNode(); boolean isTypeNode = isTypeNode(); boolean isElementNode = isElementNode(); boolean hasTypeInElement = this.hasTypeInElement(); boolean hasUnionInType = this.hasUnionInType(); boolean hasChoiceInType = this.hasChoiceInType(); if (hasTypeInElement == true) { warnings.add("Codegen doesn't support to " + "define a type in element/attribute node."); } if (hasUnionInType == true) { warnings.add("Codegen doesn't support \"union\" in type."); } if (hasChoiceInType == true) { warnings.add("Codegen doesn't support \"choice\" in type."); } // current node is a type definition node if (isTypeNode == true) { // begin to add whatever content to xsdContent. this is used in // character method. acceptContent = true; // extract type name from attribute for (int index = 0; index < attributes.getLength(); index++) { String attrName = attributes.getQName(index); String attrValue = attributes.getValue(index); if (TYPE_NAME_ATTR.equals(attrName)) { // extract type name attribute value and store it to // typeName typeName = attrValue; } } // if there is no type name, just use a default type name; } if (inTypeNode == true) { if (typeAnyEleDef(currNode) == true) { warnings.add("This type uses \"xs:any\". " + "Please consider to remove it."); } } // current node is in a type definition node or type definition node if (inTypeNode == true || isTypeNode == true) { // write whatever into xsdContent, content.add("<" + qName); for (int index = 0; index < attributes.getLength(); index++) { // trim name and value, just for sure. String attrName = attributes.getQName(index).trim(); String attrValue = attributes.getValue(index).trim(); if ((isTypeNode == true) && (TYPE_NAME_ATTR.equals(attrName) == true)) { // extract type name attribute value and store it to // typeName typeName = attrValue; /* * There might be no type name attribute.write type name to * content outside the loop. */ continue; } if (isElementNode == true && ELEMENT_REF.equals(attrName)) { warnings.add("Codegen doesn't " + "support using \"ref\" in element."); } content.add(" " + attrName + "=\""); /* * if this attribute is dependency related, using a marker to * replace it later. */ if (isTypeDependencyRelated(attrName, attrValue) == true) { if (isInW3CBasicSchemaTypeNamespace(uri, attrValue) == true) { // it is a standard type String[] basicQName = attrValue.split(":"); if (basicQName.length == 2) { SchemaTypePiece piece = new SchemaTypePiece( attrValue, nsMappingSchema.get(XML_NS + basicQName[0])); content.add(piece); } else { content.add(attrValue); } } else { /* * then it is an typle lib dependency or internal * dependency type, using free marker for replacement */ content.add(new TypeQNamePiece(attrValue)); } } else { content.add(attrValue); } content.add("\""); } if (isTypeNode == true) { // no type name attribute, add this attribute if (typeName == null) { typeName = this.getNameForNoTypeWithoutName(); warnings.add("Type name is empty. Using \"" + typeName + "\" as type name."); } content.add(" " + TYPE_NAME_ATTR + "=\""); content.add(new TypeNamePiece(null)); content.add("\""); } content.add(">"); /* * set documentAnnotationIndex, use it when there is no * annotation>document node. */ if (isTypeNode() == true) { documentAnnotationIndex = content.size(); } /* * set documentIndex. now it is in annotation, use it when * documentation node is not included. */ if (isTypeAnnotationNode() == true) { documentIndex = content.size(); } } } /** * due to the String.spliter method. if two replacement mark next to each * other, it will fail. this is a private method and there is no such a * usecase. * * @param strContentBuilder * @param pieces */ private static List<IXSDPiece> addToXSDContentListBatch(String strContent, IXSDPiece... pieces) { List<IXSDPiece> listPieces = new ArrayList<IXSDPiece>(); String[] strPieces = strContent.split(REPLACE_SPLITER); if (strPieces.length != pieces.length + 1) { throw new IllegalArgumentException( "XSD piece not match. Piece size is " + pieces.length + ". Conent piece size is " + strPieces.length + "Content is : \r\n" + strContent); } listPieces.add(new StringPiece(strPieces[0])); for (int i = 1; i < strPieces.length; i++) { listPieces.add(pieces[i - 1]); listPieces.add(new StringPiece(strPieces[i])); } return listPieces; } /** * end of an element */ @Override public void endElement(String uri, String localName, String qName) throws SAXException { if (isInTypeNode() == true || isTypeNode() == true) { /* * write whatever into XSD content if it is type node or in type * node. */ content.add("</" + qName + ">"); } if (isTypeNode() == true) { // fist, insert documentation if needed. if (this.documentation == null) { /* * it means there is no documentation node. but there are to * possibilities: no annotation document and no document */ if (documentIndex > 0) { // has annotation, no document, just insert document node String docNode = syncXSDPrefix(DOC_NODE, schemaQName); content.insert( documentAnnotationIndex, addToXSDContentListBatch(docNode, new TypeDocumentationPiece(null))); } else if (documentAnnotationIndex > 0) { /* * has no annotation, no document, insert annotation * document node */ String annotationDocNode = syncXSDPrefix( ANNOTATION_DOC_NODE, schemaQName); content.insert( documentAnnotationIndex, addToXSDContentListBatch(annotationDocNode, new TypeDocumentationPiece(null))); } documentation = "Documents goes here"; } // end of a type node. create a TypeModel2 instance for current type TypeModel type = null; if ((tlName == null) == (tlNamespace == null)) { type = new TypeModel(typeName, targetNamespace, nsMappingSchema, content, documentation, tlName, tlNamespace); } else { type = new TypeModel(typeName, targetNamespace, nsMappingSchema, content, documentation, tlName, tlNamespace); type.addWarning("Type library source is invalidate!"); } type.addErrors(this.errors); type.addWarnings(this.warnings); type.setSchemaQName(schemaQName); type.setSchemaXMLNS(schemaXMLNS); // put current type to map, key is the NS:TypeName String key = targetNamespace + ":" + typeName; if (xsds.get(key) != null) { throw new SAXException("Duplicated type found!" + key); } xsds.put(key, type); // clear type content, type name, documentation and two doc idx. typeName = null; content = new XSDContentList(); documentation = null; documentIndex = -1; documentAnnotationIndex = -1; // clear tlName and tlNamespace tlName = null; tlNamespace = null; // clear warning and error errors.clear(); warnings.clear(); // no longer accept content until meet another type acceptContent = false; } else if (isSchemaNode() == true) { // end of a type schema block. clear schema node related variables - // schema NS and namespace. nsMappingSchema = new HashMap<String, String>(); targetNamespace = null; schemaXMLNS = null; } xmlPath.remove(xmlPath.size() - 1); } /** * Some schema use there own prefix rather than the common "xs:". some use * xmlns="namespace" and there is no prefix. this method is used to replace * the redefined "xs:" to expected value: empty or their own prefix. the * target prefix comes from schemaQName prefix. * * @param str * @param schemaQName * @return */ private static String syncXSDPrefix(String str, String schemaQName) { if (schemaQName == null) { return str; } String[] qName = schemaQName.split(":"); String replacement = ""; if (qName.length == 2) { replacement = qName[0] + ":"; if (XS_NS.equals(replacement)) { return str; } str = str.replace("<" + XS_NS, "<" + replacement); str = str.replace("</" + XS_NS, "</" + replacement); } else { str = str.replace("<" + XS_NS, "<"); str = str.replace("</" + XS_NS, "</"); } return str; } /** * handle content. */ @Override public void characters(char[] ch, int start, int length) throws SAXException { if (acceptContent == false) { return; } String contentStr = new String(ch, start, length); contentStr = contentStr.replace("&", "&"); contentStr = contentStr.replace("<", "<"); contentStr = contentStr.replace(">", ">"); contentStr = contentStr.replace("\"", """); contentStr = contentStr.replace("'", "'"); if (isTypeDocumentationNode() == true) { documentation += contentStr; if (content.getContentList().get(content.size() - 1) instanceof TypeDocumentationPiece == false) { content.add(new TypeDocumentationPiece(null)); } } else { content.add(contentStr); } } public static void main(String[] args) throws SAXException, IOException, ParserConfigurationException { // File f = new File(args[0]); File f = new File( "C:\\Documents and Settings\\mzang\\Desktop\\aaaaaxsd"); for (File wsdl : f.listFiles()) { if (isXSD(wsdl) == false) { continue; } try { System.out.println("======file=======" + wsdl.toString()); ImportTypesFromXSDParser instance = new ImportTypesFromXSDParser(); instance.cutXSD(wsdl.toString()); } catch (Throwable ex) { ex.printStackTrace(); } } } private static boolean isXSD(File f) { if (f.isFile() == false) { return false; } String uri = f.toString(); int tail = uri.lastIndexOf('.'); if (tail == -1) { return false; } String end = uri.substring(tail + 1); if (end.equalsIgnoreCase("xsd") == false) { return false; } return true; } }