/*
* LimeXMLSchema.java
*
* Created on April 12, 2001, 4:03 PM
*/
package com.limegroup.gnutella.xml;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
* Stores a XML schema, and provides access to various components
* of schema
* @author asingla
*/
public class LimeXMLSchema {
/**
* List<String> of fields (in canonicalized form to preserve the structural
* information)
*/
private final List /* of SchemaFieldInfo */ _canonicalizedFields;
/**
* The URI for this schema
*/
private final String _schemaURI;
/**
* The description for this schema.
*/
private final String _description;
/**
* The outer-XML name for this schema.
* IE: 'things', for the 'thing' schema.
*/
private final String _rootXMLName;
/**
* Creates new LimeXMLSchema
* @param schemaFile The filefrom where to read the schema definition
* @exception IOException If the specified schemaFile doesnt exist, or isnt
* a valid schema file
*/
public LimeXMLSchema(File schemaFile) throws IOException {
this(LimeXMLUtils.getInputSource(schemaFile));
}
/**
* Creates new LimeXMLSchema
* @param inputSource The source representing the XML schema definition
* to be parsed
* @exception IOException If the specified schemaFile doesnt exist, or isnt
* a valid schema file
*/
public LimeXMLSchema(InputSource inputSource) throws IOException {
//initialize schema
Document document = getDocument(inputSource);
_canonicalizedFields =
(new LimeXMLSchemaFieldExtractor()).getFields(document);
_schemaURI = retrieveSchemaURI(document);
_rootXMLName = getRootXMLName(document);
_description = getDisplayString(_schemaURI);
}
/**
* Initilizes the schema after parsing it from the input source
* @param schemaInputSource The source representing the XML schema definition
* to be parsed
*/
private Document getDocument(InputSource schemaInputSource)
throws IOException {
//get an instance of DocumentBuilderFactory
DocumentBuilderFactory documentBuilderFactory =
DocumentBuilderFactory.newInstance();
//set validating, and namespace awareness
//documentBuilderFactory.setValidating(true);
//documentBuilderFactory.setNamespaceAware(true);
//get the document builder from factory
DocumentBuilder documentBuilder=null;
try {
documentBuilder = documentBuilderFactory.newDocumentBuilder();
} catch(ParserConfigurationException e) {
throw new IOException(e.getMessage());
}
// Set an entity resolver to resolve the schema
documentBuilder.setEntityResolver(new Resolver(schemaInputSource));
// Parse the schema and create a document
Document document=null;
try {
document = documentBuilder.parse(schemaInputSource);
} catch(SAXException e) {
throw new IOException(e.getMessage());
}
return document;
}
/**
* Returns the URI of the schema represented in the passed document
* @param document The document representing the XML Schema whose URI is to
* be retrieved
* @return The schema URI
* @requires The document be a parsed form of valid xml schema
*/
private static String retrieveSchemaURI(Document document) {
//get the root element which should be "xsd:schema" element (provided
//document represents valid schema)
Element root = document.getDocumentElement();
//get attributes
NamedNodeMap nnm = root.getAttributes();
//get the targetNameSpaceAttribute
Node targetNameSpaceAttribute = nnm.getNamedItem("targetNamespace");
if(targetNameSpaceAttribute != null) {
//return the specified target name space as schema URI
return targetNameSpaceAttribute.getNodeValue();
} else {
//return an empty string otherwise
return "";
}
}
/**
* Retrieves the name of the root tag name for XML generated
* with this schema.
*/
private static String getRootXMLName(Document document) {
Element root = document.getDocumentElement();
// Get the children elements.
NodeList children = root.getElementsByTagName("element");
if(children.getLength() == 0)
return "";
Node element = children.item(0);
NamedNodeMap map = element.getAttributes();
Node name = map.getNamedItem("name");
if(name != null)
return name.getNodeValue();
else
return "";
}
/**
* Prints the node, as well as its children (by invoking the method
* recursively on the children)
* @param n The node which has to be printed (along with children)
*/
private void printNode(Node n)
{
//get attributes
if(n.getNodeType() == Node.ELEMENT_NODE)
{
System.out.print("node = " + n.getNodeName() + " ");
NamedNodeMap nnm = n.getAttributes();
Node name = nnm.getNamedItem("name");
if(name != null)
System.out.print(name + "" );
System.out.println("");
NodeList children = n.getChildNodes();
int numChildren = children.getLength();
for(int i=0;i<numChildren; i++)
{
Node child = children.item(i);
printNode(child);
}
}
}
/**
* Returns the unique identifier which identifies this particular schema
* @return the unique identifier which identifies this particular schema
*/
public String getSchemaURI() {
return _schemaURI;
}
/**
* Retrieves the name to use when constructing XML docs under this schema.
*/
public String getRootXMLName() {
return _rootXMLName;
}
/**
* Retrieves the name to use for inner elements when constructing docs under this schema.
*/
public String getInnerXMLName() {
return _description;
}
/**
* Returns all the fields(placeholders) in this schema.
* The field names are canonicalized as mentioned below:
* <p>
* So as to preserve the structure, Structure.Field will be represented as
* Structure__Field (Double Underscore is being used as a delimiter to
* represent the structure).
*<p>
* In case of multiple structured values with same name,
* as might occur while using + or * in the regular expressions in schema,
* those should be represented as using the array index using the __
* notation (withouth the square brackets)
* for e.g. myarray[0].name ==> myarray__0__name
*
* attribute names for an element in the XML schema should be postfixed
* with __ (double underscore).
* So element.attribute ==> element__attribute__
*
* @return unmodifiable list (of SchemaFieldInfo) of all the fields
* in this schema.
*/
public List getCanonicalizedFields()
{
return Collections.unmodifiableList(_canonicalizedFields);
}
/**
* Returns only those fields which are of enumeration type
*/
public List getEnumerationFields()
{
//create a new list
List enumerationFields = new LinkedList();
//iterate over canonicalized fields, and add only those which are
//of enumerative type
Iterator iterator = _canonicalizedFields.iterator();
while(iterator.hasNext())
{
//get next schema field
SchemaFieldInfo schemaFieldInfo = (SchemaFieldInfo)iterator.next();
//if enumerative type, add to the list of enumeration fields
if(schemaFieldInfo.getEnumerationList() != null)
enumerationFields.add(schemaFieldInfo);
}
//return the list of enumeration fields
return enumerationFields;
}
/**
* Returns all the fields(placeholders) names in this schema.
* The field names are canonicalized as mentioned below:
* <p>
* So as to preserve the structure, Structure.Field will be represented as
* Structure__Field (Double Underscore is being used as a delimiter to
* represent the structure).
*<p>
* In case of multiple structured values with same name,
* as might occur while using + or * in the regular expressions in schema,
* those should be represented as using the array index using the __
* notation (withouth the square brackets)
* for e.g. myarray[0].name ==> myarray__0__name
*
* attribute names for an element in the XML schema should be postfixed
* with __ (double underscore).
* So element.attribute ==> element__attribute__
*
* @return list (Strings) of all the field names in this schema.
*/
public String[] getCanonicalizedFieldNames()
{
//get the fields
List canonicalizedFields = this.getCanonicalizedFields();
//extract field names out of those
String[] fieldNames = new String[canonicalizedFields.size()];
Iterator iterator = canonicalizedFields.iterator();
for(int i=0; i < fieldNames.length; i++)
{
fieldNames[i] = ((SchemaFieldInfo)iterator.next())
.getCanonicalizedFieldName();
}
//return the field names
return fieldNames;
}
private static final class Resolver implements EntityResolver
{
private InputSource schema;
public Resolver(InputSource s)
{
schema = s;
}
public InputSource resolveEntity(String publicId, String systemId)
{
return schema;
//String Id = systemId+publicId;
//String schemaId = schema.getSystemId()+schema.getPublicId();
//if (Id.equals(schemaId))
// return schema;
//else
// return null;
}
}//end of private innner class
/**
* Returns the display name of this schema.
*/
public String getDescription() {
return _description;
}
/**
* Utility method to be used in the gui to display schemas
*/
public static String getDisplayString(String schemaURI)
{
int start = schemaURI.lastIndexOf("/");
//TODO3: Are we sure that / is the correct delimiter???
int end = schemaURI.lastIndexOf(".");
String schemaStr;
if(start == -1 || end == -1)
schemaStr = schemaURI;
else
schemaStr= schemaURI.substring(start+1,end);
return schemaStr;
}
public boolean equals(Object o) {
if( o == this )
return true;
if( o == null )
return false;
return _schemaURI.equals(((LimeXMLSchema)o)._schemaURI);
}
public int hashCode() {
return _schemaURI.hashCode();
}
}