package org.ebayopensource.turmeric.eclipse.typelibrary.utils.importtypes;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
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.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* This class is used to cut XSD schema from WSDL. It uses
* ImportTypesFromXSDParser to handle XSD content.
*
* @author mzang
*
*/
public class ImportTypesFromWSDLParser extends DefaultHandler {
String path = null;
/**
* cut XSD files from WSDL
*
* @param wsdlPath
* @throws SAXException
* @throws IOException
* @throws ParserConfigurationException
*/
public void cutWSDL(String wsdlPath) throws SAXException, IOException,
ParserConfigurationException {
path = wsdlPath;
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);
}
// variables for all types in current wsdl.
private Map<String, TypeModel> xsds = new HashMap<String, TypeModel>();
private Map<String, TypeModel> referedTLTypes = new HashMap<String, TypeModel>();
private Map<String, String> nsMappingWSDL = new HashMap<String, String>();
private List<NodeQName> xmlPath = new ArrayList<NodeQName>();
// node QNames
private static final String WSDL_NS = "http://schemas.xmlsoap.org/wsdl[/]{0,}";
private static final Pattern WSDL_NS_PATTERN = Pattern.compile(WSDL_NS,
Pattern.CASE_INSENSITIVE);
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 XML_NS = "xmlns:";
private static String WSDL_DEF_NAME = "definitions";
private static String WSDL_TYPE_DEF_NAME = "types";
private static String SCHEMA_DEF_NAME = "schema";
/**
* get Type Models
*
* @return
*/
public Collection<TypeModel> getTypeModels() {
return this.xsds.values();
}
private static boolean isWSDLNS(String namespace) {
Matcher matcher = WSDL_NS_PATTERN.matcher(namespace);
return matcher.matches();
}
private boolean wsdlDef() {
if (xmlPath.size() < 1) {
return false;
}
NodeQName qName = xmlPath.get(0);
return (qName.ns == NS.WSDL) && WSDL_DEF_NAME.equals(qName.localName);
}
private boolean wsdlTypesDef() {
if (xmlPath.size() < 2) {
return false;
}
NodeQName qName = xmlPath.get(1);
return (qName.ns == NS.WSDL)
&& WSDL_TYPE_DEF_NAME.equals(qName.localName);
}
private boolean schemaDef() {
if (xmlPath.size() < 3) {
return false;
}
NodeQName qName = xmlPath.get(2);
return (qName.ns == NS.SCHEMA)
&& SCHEMA_DEF_NAME.equals(qName.localName);
}
private boolean isWSDLNode() {
if (xmlPath.size() != 1) {
return false;
}
return wsdlDef();
}
private boolean isSchemaNodeStart() {
if (xmlPath.size() != 3) {
return false;
}
return wsdlDef() && wsdlTypesDef() && schemaDef();
}
private boolean isSchemaNodeEnd(String uri, String localName) {
if (xmlPath.size() != 3) {
return false;
}
return (wsdlDef() && wsdlTypesDef() && schemaDef())
&& (isSchemaNS(uri) && SCHEMA_DEF_NAME.equals(localName));
}
/**
* 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.
*/
private void postProcessTypes() throws SAXException {
referedTLTypes = ImportTypesFromXSDParser.postProcessTypes(xsds);
}
/**
* get referred types
*
* @return
*/
public Map<String, TypeModel> getReferedTLTypes() {
return referedTLTypes;
}
/**
* document end, post process
*/
@Override
public void endDocument() throws SAXException {
postProcessTypes();
ImportTypesFromXSDParser.clearNoTypeNameCounter();
// test();
}
/**
* this is for test purpose only.
*
* @throws SAXException
*/
public void test() 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 static boolean isSchemaNS(String namespace) {
Matcher matcher = SCHEMA_NS_PATTERN.matcher(namespace);
return matcher.matches();
}
private ImportTypesFromXSDParser schmaHandler = null;
/**
* start to handle an element
*/
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
if (schmaHandler != null) {
schmaHandler.startElement(uri, localName, qName, attributes);
return;
}
NS ns = NS.OTHER;
if (isSchemaNS(uri)) {
ns = NS.SCHEMA;
} else if (isWSDLNS(uri)) {
ns = NS.WSDL;
}
NodeQName currNode = new NodeQName(uri, ns, localName);
xmlPath.add(currNode);
// current node is wsdl:definitions
if (isWSDLNode() == true) {
// store the namespace attributes in the nsMappingWSDL
for (int index = 0; index < attributes.getLength(); index++) {
String attrName = attributes.getQName(index);
if (attrName.toLowerCase().startsWith((XML_NS))) {
nsMappingWSDL.put(attrName, attributes.getValue(index));
}
}
return;
}
// current node is xs:schema
if (isSchemaNodeStart() == true) {
schmaHandler = new ImportTypesFromXSDParser();
schmaHandler.setOuterNSMappint(nsMappingWSDL);
schmaHandler.startElement(uri, localName, qName, attributes);
}
}
/**
* element end
*/
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
if (schmaHandler != null) {
schmaHandler.endElement(uri, localName, qName);
if (isSchemaNodeEnd(uri, localName) == true) {
xsds.putAll(schmaHandler.getTypeModelMap());
schmaHandler = null;
}
}
if (schmaHandler == null) {
xmlPath.remove(xmlPath.size() - 1);
}
}
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
if (schmaHandler != null) {
schmaHandler.characters(ch, start, length);
}
}
/**
* for test only
* @param args
* @throws SAXException
* @throws IOException
* @throws ParserConfigurationException
*/
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\\aaaaa");
for (File wsdl : f.listFiles()) {
if (isWSDL(wsdl) == false) {
continue;
}
try {
ImportTypesFromWSDLParser instance = new ImportTypesFromWSDLParser();
instance.cutWSDL(wsdl.toString());
} catch (Throwable ex) {
ex.printStackTrace();
}
}
}
private static boolean isWSDL(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("wsdl") == false) {
return false;
}
return true;
}
}