/*******************************************************************************
* Copyright (c) 2006-2010 eBay Inc. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*******************************************************************************/
package org.ebayopensource.turmeric.eclipse.typelibrary.ui;
/**
* Extract type definitions from XSD file.
*
* @author mzang
*
*/
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.ebayopensource.turmeric.eclipse.typelibrary.ui.model.CommonTypeProp;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
*
*
*
*/
enum ParseStatus {
/**
* Proccesing Type.
*/
ProcessingType, ProcessingCommon, ProcessingIgnore
}
/**
* The Class XSDTypeParser.
*/
public class XSDTypeParser extends DefaultHandler {
/** The Constant typeNameMarker. */
public static final String typeNameMarker = "TypeName";
/** The Constant namespaceMarker. */
public static final String namespaceMarker = "Namespace";
/** The Constant documentMarker. */
public static final String documentMarker = "Document";
private CommonTypeProp current = null;
// contains xml header and import.
private StringBuffer commonHeader = new StringBuffer(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n");
private StringBuffer currTypeContent = new StringBuffer();
private List<CommonTypeProp> allTypes = new ArrayList<CommonTypeProp>();
private static final String COMPLEX_TYPE = "complexType";
private static final String SIMPLE_TYPE = "simpleType";
private static final String IMPORT = "import";
private static final String SCHEMA_NODE_NAME = "schema";
private static final String ANNOTATION_NODE_NAME = "annotation";
private static final String DOCUMENT_NODE_NAME = "documentation";
private static final String NAME_ATTR = "name";
private static final String TARGET_NAMESPACE_ATTR = "targetNamespace";
private static Set<String> LEVEL2_NODE = new HashSet<String>();
static {
LEVEL2_NODE.add(COMPLEX_TYPE);
LEVEL2_NODE.add(SIMPLE_TYPE);
LEVEL2_NODE.add(IMPORT);
}
/** The status. */
ParseStatus status = ParseStatus.ProcessingCommon;
private String targetNamespace = null;
private List<String> nodePath = new ArrayList<String>();
private int documentIndex = -1;
private String prefix = "xs";
private String schemaEnd = null;
/**
* Instantiates a new xSD type parser.
*
* @param xsdStream the xsd stream
*/
public XSDTypeParser(InputStream xsdStream) {
try {
SAXParserFactory saxfac = SAXParserFactory.newInstance();
SAXParser saxParser = saxfac.newSAXParser();
saxParser.parse(xsdStream, this);
String commonHeaderStr = commonHeader.toString();
for (CommonTypeProp type : allTypes) {
type.setSchemaTemplate(commonHeaderStr
+ type.getSchemaTemplate() + schemaEnd);
}
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private static boolean isTypeNode(String nodeName) {
nodeName = getNodeName(nodeName);
return SIMPLE_TYPE.equalsIgnoreCase(nodeName)
|| COMPLEX_TYPE.equalsIgnoreCase(nodeName);
}
private static boolean isSchemaNode(String nodeName) {
return SCHEMA_NODE_NAME.equalsIgnoreCase(getNodeName(nodeName));
}
private static boolean shouldHandleOnLevel2(String qName) {
String nodeName = getNodeName(qName);
return LEVEL2_NODE.contains(nodeName);
}
private static String getNodeName(String qName) {
int index = qName.indexOf(':');
if (index > -1) {
qName = qName.substring(index + 1);
}
return qName;
}
private static String getPrefix(String qName) {
int index = qName.indexOf(':');
if (index > -1) {
return qName.substring(0, index);
}
return "xs";
}
private boolean isTypeDocument() {
if (nodePath.size() == 4 && isSchemaNode(nodePath.get(0))
&& isTypeNode(nodePath.get(1))) {
return ANNOTATION_NODE_NAME.equalsIgnoreCase(getNodeName(nodePath
.get(2)))
&& DOCUMENT_NODE_NAME.equalsIgnoreCase(getNodeName(nodePath
.get(3)));
}
return false;
}
/* (non-Javadoc)
* @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
*/
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
if (status == ParseStatus.ProcessingIgnore) {
return;
}
String content = new String(ch, start, length);
if (isTypeDocument() == true) {
current.setDescription(content);
appendXSD("${" + documentMarker + "}");
} else {
appendXSD(content);
}
}
/* (non-Javadoc)
* @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
*/
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
nodePath.remove(nodePath.size() - 1);
if (status == ParseStatus.ProcessingIgnore) {
return;
}
if (isTypeDocument() == true) {
current.setDescription("");
appendXSD("${" + documentMarker + "}");
}
if (isSchemaNode(qName) == true) {
schemaEnd = "\r\n</" + qName + ">";
} else {
appendXSD("</" + qName + ">");
}
if ((status == ParseStatus.ProcessingType)
&& (isTypeNode(qName) == true)) {
if (current != null) {
status = ParseStatus.ProcessingCommon;
current.setTargetNamespace(targetNamespace);
if (current.getDescription() == null) {
current.setDescription("");
currTypeContent.insert(documentIndex, getDummyDocument());
}
current.setSchemaTemplate(currTypeContent.toString());
currTypeContent.delete(0, currTypeContent.length());
allTypes.add(current);
}
documentIndex = -1;
}
}
private String getDummyDocument() {
return "\r\n<" + prefix + ":annotation>" + "\r\n<" + prefix
+ ":documentation>" + "\r\n${" + documentMarker + "}"
+ "\r\n</" + prefix + ":documentation>" + "\r\n </" + prefix
+ ":annotation>";
}
/* (non-Javadoc)
* @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
*/
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
nodePath.add(qName);
if (nodePath.size() == 2 && (shouldHandleOnLevel2(qName) == false)) {
status = ParseStatus.ProcessingIgnore;
return;
}
boolean inTypeNode = false;
boolean inSchemaNode = false;
if ((status != ParseStatus.ProcessingType)
&& (isTypeNode(qName) == true)) {
status = ParseStatus.ProcessingType;
inTypeNode = true;
current = new CommonTypeProp();
}
inSchemaNode = isSchemaNode(qName);
if (inSchemaNode) {
prefix = getPrefix(qName);
}
appendXSD("<" + qName + "");
for (int index = 0; index < attributes.getLength(); index++) {
String attrName = attributes.getQName(index);
String value = attributes.getValue(index);
if (inTypeNode == true && NAME_ATTR.equalsIgnoreCase(attrName)) {
// replace type name with type name marker
current.setTypeName(value);
appendXSD(" " + attrName + "=\"${" + typeNameMarker + "}\" ");
} else if (inSchemaNode == true
&& TARGET_NAMESPACE_ATTR.equalsIgnoreCase(attrName)) {
targetNamespace = value;
appendXSD(" " + attrName + "=\"${" + namespaceMarker + "}\" ");
} else {
appendXSD(" " + attrName + "=\"" + value + "\" ");
}
}
appendXSD(">");
if (inTypeNode == true) {
documentIndex = currTypeContent.length();
}
}
private void appendXSD(String content) {
if (status == ParseStatus.ProcessingType) {
currTypeContent.append(content);
} else if (status == ParseStatus.ProcessingCommon) {
commonHeader.append(content);
}
}
/**
* Gets the all types.
*
* @return the all types
*/
public List<CommonTypeProp> getAllTypes() {
return allTypes;
}
/**
* Sets the all types.
*
* @param allTypes the new all types
*/
public void setAllTypes(List<CommonTypeProp> allTypes) {
this.allTypes = allTypes;
}
}