[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Local copy of SiRPAC



Hi Sergey and Dan

Please find attached my local copy of SiRPAC.java. By running
a diff -c against your own copy you will probably find quickest
the modifications I have made w.r.t your changes.

I'm sorry but I've been too busy to follow this up with Sergey
Hopefully this puts me off the critical path.. 

Cheers,
Janne
--
Janne Saarela <js@pro-solutions.com>         Pro Solutions Ltd.
Phone/Finland: +358 (0)40 508 4767                   P.O.Box 34
Phone/France :  +33 (0)6.07.45.36.67         FIN-00131 Helsinki
Fax          : +358 (0)42 508 4767                      Finland
/**
 * SiRPAC - Simple RDF Parser & Compiler
 *
 * Copyright ) World Wide Web Consortium, (Massachusetts Institute of
 * Technology, Institut National de Recherche en Informatique et en
 * Automatique, Keio University).
 *
 * All Rights Reserved.
 *
 * Please see the full Copyright clause at
 * <http://www.w3.org/Consortium/Legal/copyright-software.html>
 *
 * This program translates RDF descriptions into corresponding
 * triple representation.
 * This version uses SAX V1.0 available at <http://www.microstar.com/XML/SAX/>
 *
 * $Log: SiRPAC.java,v $
 * Revision 1.16  1999/05/21 13:02:32  jsaarela
 * Distribution release V1.14 on 21-May-99.
 *
 * Revision 1.21  1999/05/06 12:20:18  jsaarela
 * Fixed bug related to resource="#id" management.
 *
 * Revision 1.20  1999/05/04 14:52:43  jsaarela
 * Literal value now tells if it is well-formed XML.
 * Improved entity management in Data nodes.
 *
 * Revision 1.19  1999/04/26 14:51:27  jsaarela
 * URI resolution improved.
 *
 * Revision 1.18  1999/04/01 09:32:48  jsaarela
 * SiRPAC distribution release V1.11 on 1-Apr-99
 *
 * Revision 1.17  1999/03/10 08:54:40  jsaarela
 * Management of parseType="Literal" and "Resource" now equally
 * tested.
 *
 * Revision 1.16  1999/01/13 15:00:30  jsaarela
 * Finished conformance testing with PR-rdf-syntax-19990105 version
 * of the RDF M&S spec.
 *
 *
 * @author	Janne Saarela <jsaarela@w3.org>
 */
package org.w3c.rdf;

import org.xml.sax.HandlerBase;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.AttributeList;
import org.xml.sax.EntityResolver;
import org.xml.sax.DTDHandler;
import org.xml.sax.DocumentHandler;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXParseException;
import org.xml.sax.Parser;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import org.xml.sax.helpers.*;

import java.net.URL;
import java.util.*;
import java.io.*;

public class SiRPAC implements EntityResolver, DTDHandler, DocumentHandler,
			       ErrorHandler, DataSource {
    final static public String	REVISION = "$Id: SiRPAC.java,v 1.16 1999/05/21 13:02:32 jsaarela Exp $";
    public final static String	RDFMS = new String ("http://www.w3.org/1999/02/22-rdf-syntax-ns#");
    public final static String	RDFSCHEMA = new String ("http://www.w3.org/TR/1999/PR-rdf-schema-19990303#");
    public final static String	XMLSCHEMA = new String ("xml");

    private Stack		m_namespaceStack = new Stack ();
    private Stack		m_elementStack = new Stack ();
    private Element		m_root = null;
    private Vector		m_triples = new Vector ();
    private String		m_sErrorMsg = new String ();
    private String		m_sWarningMsg = new String ();
    private String		m_sSource = null;

    /**
     * The walk-through of RDF schemas requires two lists
     * shared by all SiRPAC instances
     * 1. s_vNStodo - list of all namespaces SiRPAC should still vist
     * 2. s_vNSdone - list of all namespaces SiRPAC has gone through
     */

    static private Vector	s_vNStodo = new Vector ();
    static private Vector	s_vNSdone = new Vector ();

    /**
     * The following two variables may be changed on the fly
     * to change the behaviour of the parser
     */
    private boolean		m_bCreateBags = false;
    private boolean		m_bFetchSchemas = false;

    /**
     * The following flag indicates whether the XML markup
     * should be stored into a string as a literal value
     * for RDF
     */
    private Stack		m_parseTypeStack = new Stack ();
    private Stack		m_parseElementStack = new Stack ();
    private String		m_sLiteral = new String ();

    /**
     * Support for multiple RDFConsumer objects that can
     * receive notifications about new triples
     */
    private Vector		m_consumers = new Vector ();

    /**
     * The current source of data
     */
    private RDFSource		m_RDFsource = null;

    /**
     * Which XML parser to use
     */
    private String		m_sXMLParser = null;


    public SiRPAC () {
	// You may wish to un-comment the following line
	// in case you don't have the facility to set
	// sax.parser property in your execution environment
	// m_sXMLParser = "com.ibm.xml.parser.SAXDriver";
    }

    public SiRPAC (String sXMLParser) {
	m_sXMLParser = sXMLParser;
    }

    /**
     * Methods to determine whether we are parsing
     * parseType="Literal" or parseType="Resource"
     */
    public boolean parseLiteral() {
	if (!m_elementStack.empty()) {
	    for (int x = m_elementStack.size()-1; x >= 0; x--) {
		Element e = (Element)m_elementStack.elementAt(x);
		String sParseType = e.getAttribute(RDFMS+"parseType");
		if (sParseType != null) {
		    if (!sParseType.equals ("Resource")) {
			return true;
		    }
		}
	    }
	}
	return false;
    }

    public boolean parseResource() {
	if (!m_elementStack.empty()) {
	    for (int x = m_elementStack.size()-1; x >= 0; x--) {
		Element e = (Element)m_elementStack.elementAt(x);
		String sParseType = e.getAttribute(RDFMS+"parseType");
		if (sParseType != null) {
		    if (sParseType.equals ("Resource"))
			return true;
		}
	    }
	}
	return false;
    }

    /**
     * createBags method allows one to determine whether SiRPAC
     * produces Bag instances for each Description block.
     * The default setting is to generate them.
     */
    public void createBags (boolean b) {
	m_bCreateBags = b;
    }

    /**
     * Set whether parser recursively fetches and parses
     * every RDF schema it finds in the namespace declarations
     */
    public void fetchSchemas (boolean b) {
	m_bFetchSchemas = b;
    }

    /**
     * setSource methods saves the name of the source document for
     * later inspection if needed
     */
    public void setSource (String sSource) {
	m_sSource = sSource;
    }

    public String source () {
	return m_sSource;
    }

    /**
     * Return all non-RDF namespace URIs recognized by the parser
     */
    public Enumeration listNamespaces () {
	return SiRPAC.s_vNSdone.elements();
    }

    /**
     * Return the full namespace URI for a given prefix <i>sPrefix</i>.
     * The default namespace is identified with <i>xmlns</i> prefix.
     * The namespace of <i>xmlns</i> attribute is an empty string.
     */
    public String namespace (String sPrefix) {
	if (sPrefix == null) {
	    sPrefix = new String ("xmlns");
	}
	for (int x = m_namespaceStack.size()-1; x >=0; x--) {
	    Hashtable ht = (Hashtable)m_namespaceStack.elementAt (x);
	    String sURI = (String)ht.get (sPrefix);
	    if (sURI != null)
		return sURI;
	}
	/**
	 * Give error only if
	 * 1. the prefix is not from the reserved xml namespace
	 * 2. the prefix is not xmlns which is to look for the default
	 *    namespace
	 */
	if (sPrefix.equals (XMLSCHEMA)) {
	    return XMLSCHEMA;
	} else if (sPrefix.equals ("xmlns")) {
	    return "";
	} else {
	    addError ("Unresolved namespace prefix "+sPrefix);
	}
	return "";
    }

    /**
     * One can register multiple RDFConsumer objects which will
     * receive notifications about new triples
     */
    public void register (RDFConsumer c) {
	m_consumers.addElement (c);
    }

    /**
     * Remove an RDFConsumer object from SiRPAC
     */
    public void unregister (RDFConsumer c) {
	m_consumers.removeElement(c);
    }

    /**
     * Notify all registered consumers that were
     * are about to start parsing
     */
    public void startConsumers () {
	for (int x = 0; x < m_consumers.size(); x++) {
	    RDFConsumer consumer = (RDFConsumer)m_consumers.elementAt(x);
	    consumer.start (this);
	}
    }

    /**
     * Notify all registered consumers that were
     * are at the end of the parsing process
     */
    public void endConsumers () {
	for (int x = 0; x < m_consumers.size(); x++) {
	    RDFConsumer consumer = (RDFConsumer)m_consumers.elementAt(x);
	    consumer.end (this);
	}
    }

    public void setRDFSource (RDFSource source) {
	m_RDFsource = source;
    }

    public RDFSource getRDFSource () {
	return m_RDFsource;
    }

    public void fetchRDF () throws Exception {
	startConsumers ();

	try {
	    // Create a new parser.
	    Parser p = null;

	    if (m_sXMLParser == null)
		p = ParserFactory.makeParser();
	    else
		p = ParserFactory.makeParser(m_sXMLParser);

	    // Register the handlers
	    p.setEntityResolver(this);
	    p.setDTDHandler (this);
	    p.setDocumentHandler(this);
	    p.setErrorHandler (this);
	    
	    StringReader sr = new StringReader (m_RDFsource.content());
	    InputSource source = new InputSource (sr);
	    
	    source.setSystemId(m_RDFsource.url());
	    setSource (m_RDFsource.url());

	    p.parse (source);
	    resolve ();
	    // createBags (true);
	    processXML (root());
	    // root().linearize (0, System.out);
	} catch (SAXException e) {
	    addError ("\n<br>Internal error "+e.getMessage());
	} catch (Exception e) {
	    addError ("\n<br>Internal error "+e);
	}

	endConsumers ();
    }

    public InputSource resolveEntity (String publicId, String systemId) {
	return null;
    }

    public void notationDecl (String name, String publicId, String systemId) {
    }

    /**
     * Display unparsed entity declarations as they are reported.
     *
     * @see org.xml.sax.DTDHandler#unparsedEntityDecl
     */
    public void unparsedEntityDecl (String name,
				    String publicId,
				    String systemId,
				    String notationName) {
    }

    public void setDocumentLocator (Locator locator) {
    }

    public void startDocument () {
	m_sErrorMsg = "";
    }

    public void endDocument () throws SAXException {
    }

    public void doctype (String name, String publicID, String systemID) {
    }

    /**
     * Called for each new element.
     * Build up the document tree using an element stack
     *
     * @exception SAXException Passed on since we don't handle it.
     */
    public void startElement (String name, AttributeList al) throws SAXException {
	Hashtable namespaces = new Hashtable ();

	/**
	 * The following loop tries to identify special xmlns prefix
	 * attributes and update the namespace stack accordingly.
	 * While doing all this, it builds another AttributeList instance
	 * which will hold the expanded names of the attributes
	 * (I think this approach is only useful for RDF which uses
	 * attributes as an abbreviated syntax for element names)
	 */
	AttributeListImpl newAL = new AttributeListImpl ();

	int iLength = al.getLength ();
	if (iLength == 0) {
	    // ohwell, no attributes
	} else for (int x = 0; x < iLength; x++) {
	    String aName = al.getName (x);
	    if (aName.equals ("xmlns")) {
		String aValue = al.getValue (aName);
		if (aValue != null && aValue.length() == 0 && source() != null)
		    aValue = source();
		namespaces.put (aName, aValue);

		// save all non-RDF schema addresses
		if (!hasString (SiRPAC.s_vNStodo, aValue) &&
		    !hasString (SiRPAC.s_vNSdone, aValue) &&
		    !aValue.startsWith (RDFMS) &&
		    !aValue.startsWith (RDFSCHEMA)) {
		    SiRPAC.s_vNStodo.addElement (aValue);
		} 
	    } else if (aName.startsWith ("xmlns:")) {
		String aValue = al.getValue (aName);
		if (aValue != null && aValue.length() == 0 && source() != null)
		    aValue = source();
		aName = aName.substring (6);
		namespaces.put (aName, aValue);

		// save all non-RDF schema addresses
		if (!hasString (SiRPAC.s_vNStodo, aValue) &&
		    !hasString (SiRPAC.s_vNSdone, aValue) &&
		    !aValue.startsWith (RDFMS) &&
		    !aValue.startsWith (RDFSCHEMA)) {
		    SiRPAC.s_vNStodo.addElement (aValue);
		}
	    }
	}
	/**
	 * Place new namespace declarations into the stack
	 * (Yes, I could optimize this a bit, not it wastes space
	 * if there are no xmlns definitions)
	 */
	m_namespaceStack.push (namespaces);

	/**
	 * Figure out the prefix part if it exists and
	 * determine the namespace of the element accordingly
	 */
	String sNamespace = null;
	String sElementName = null;
	Element newElement = null;
	int i = name.indexOf (':');
	String sPrefix2 = null;
	if (i > 0) {
	    sPrefix2 = name.substring (0, i);
	    sNamespace = namespace (sPrefix2);
	    sElementName = name.substring (i+1);
	} else {
	    sNamespace = namespace ("xmlns");
	    sElementName = name;
	}

	/**
	 * Finally look for attributes other than the special xmlns,
	 * expand them, and place to the new AttributeListImpl
	 */
	for (int x = 0; x < iLength; x++) {
	    String sAttributeNamespace = null;
	    String aName = al.getName (x);
	    if (!aName.startsWith ("xmlns")) {
		String aValue = al.getValue (aName);
		String aType = al.getType (aName);

		int iIndex = aName.indexOf (':');
		String sPrefix = null;
		if (iIndex > 0) {
		    sPrefix = aName.substring (0, iIndex);
		    sAttributeNamespace = namespace (sPrefix);
		    aName = aName.substring (iIndex+1);

		} else {
		    if (sNamespace == null)
			sAttributeNamespace = namespace ("xmlns");
		    else 
			sAttributeNamespace = sNamespace;
		}
		if (parseLiteral()) {
		    if (sPrefix == null) {
			sPrefix = "gen" + x; // x is a handy counter
		    }
		    newAL.addAttribute (sPrefix + ":" + aName,
					aType,
					aValue);
		    newAL.addAttribute ("xmlns:"+sPrefix,
					aType,
					sAttributeNamespace);
		} else {
		    newAL.addAttribute (sAttributeNamespace+aName,
					aType,
					aValue);
		}
		/**
		 * This call will try to see if the user is using
		 * RDF look-alike elements from another namespace
		 *
		 * Note: you can remove the call if you wish
		 */
		likeRDF (sAttributeNamespace, aName);
	    }
	}

	/**
	 * If we have parseType="Literal" set earlier, this element
	 * needs some additional attributes to make it stand-alone
	 * piece of XML
	 */
	if (parseLiteral()) {
	    if (sPrefix2 == null) {
		// default namespace coming in
		if (sNamespace != null) {
		    newAL.addAttribute ("xmlns:gen",
					"CDATA",
					sNamespace);
		}
		newElement = new Element ("gen:" + sElementName,
					  newAL);
		newElement.prefix ("gen");
	    } else {
		String sAttributeNamespace = namespace (sPrefix2);
		if (sAttributeNamespace != null)
		    newAL.addAttribute ("xmlns:"+sPrefix2,
					"CDATA",
					sAttributeNamespace);
		newElement = new Element (sPrefix2 + ":" + sElementName,
					  newAL);
	    }
	} else {
	    newElement = new Element (sNamespace + sElementName,
				      newAL);
	    likeRDF (sNamespace, sElementName);
	}

	checkAttributes (newElement);

	/**
	 * Check parseType
	 */
	String sLiteralValue = newElement.getAttribute(RDFMS+"parseType");
	if (sLiteralValue != null && !sLiteralValue.equals ("Resource")) {
	    /**
	     * This is the management of the element where
	     * parseType="Literal" appears
	     *
	     * You should notice RDF V1.0 conforming implementations
	     * must treat other values than Literal and Resource as
	     * Literal. This is why the condition is !equals("Resource")
	     */
	    m_parseTypeStack.push (sLiteralValue);
	    
	    if (!m_elementStack.empty()) {
		Element e = (Element)m_elementStack.peek ();
		e.addChild (newElement);
	    }
	    
	    m_elementStack.push (newElement);
	    m_parseElementStack.push (newElement);
	    m_sLiteral = "";
	    return;
	}

	if (parseLiteral()) {
	    /**
	     * This is the management of any element nested within
	     * a parseType="Literal" declaration
	     */
	    makeMarkupST (newElement);
	    m_elementStack.push (newElement);
	    return;
	}

	/**
	 * Update the containment hierarchy
	 * with the stack.
	 */
	if (!m_elementStack.empty()) {
	    Element e = (Element)m_elementStack.peek ();
	    e.addChild (newElement);
	}

	/**
	 * Place the new element into the stack
	 */
	m_elementStack.push (newElement);

	if (sLiteralValue != null && sLiteralValue.equals ("Resource")) {
	    m_parseTypeStack.push (sLiteralValue);
	    m_parseElementStack.push (newElement);
	    m_sLiteral = "";

	    /**
	     * Since parseType="Resource" implies the following
	     * production must match Description, let's create
	     * an additional Description node here in the document tree.
	     */
	    Element desc = new Element (RDFMS+"Description", new AttributeListImpl());
	    
	    if (!m_elementStack.empty()) {
		Element e = (Element)m_elementStack.peek ();
		e.addChild (desc);
	    }

	    m_elementStack.push (desc);
	}
    }

    /**
     * Helper function to determine if a String is already within a
     * Vector
     *
     * @param	v	Vector to be inspected
     * @param	s	String to be searched
     */
    private boolean hasString (Vector v, String s) {
	for (int x = 0; x < v.size(); x++) {
	    String s2 = (String)v.elementAt(x);
	    if (s.equals (s2)) {
		return true;
	    }
	}
	return false;
    }

    /** 
     * For each end of an element scope step back in the 
     * element and namespace stack
     *
     * @exception SAXException Passed on since we don't handle it.
     */
    public void endElement (String name) throws SAXException {
	boolean bParseLiteral = parseLiteral();
	m_root = (Element)m_elementStack.pop ();
	m_namespaceStack.pop ();

	if (bParseLiteral) {
	    Element pe = (Element)m_parseElementStack.peek ();
	    if (pe != m_root) {
		makeMarkupET (m_root.prefix()+name);
	    } else {
		m_root.addChild (new Data (m_sLiteral, true));
		m_sLiteral = "";
		m_parseElementStack.pop();
		m_parseTypeStack.pop ();
	    }
	} else if (parseResource()) {
	    /**
	     * If we are doing parseType="Resource"
	     * we need to explore whether the next element in
	     * the stack is the closing element in which case
	     * we remove it as well (remember, there's an
	     * extra Description element to be removed)
	     */
	    if (!m_elementStack.empty()) {
		Element pe = (Element)m_parseElementStack.peek ();
		if (m_elementStack.peek() == pe) {
		    Element e = (Element)m_elementStack.pop ();
		    m_parseElementStack.pop();
		    m_parseTypeStack.pop ();
		}
	    }
	}
    }

    /**
     * Return the root element pointer. This requires the parsing
     * has been already done.
     */
    public Element root () {
	return m_root;
    }

    public void characters (char ch[], int start, int length)
	throws SAXException {

	/**
	 * Place all characters as Data instance to the containment
	 * hierarchy with the help of the stack.
	 */
	Element e = (Element)m_elementStack.peek ();
	String s = new String (ch, start, length);

	if (parseLiteral()) {
	    makeMarkupChar (s);
	    return;
	}

	/**
	 * Determine whether the previous event was for
	 * characters. If so, update the Data node contents.
	 * A&amp;B would otherwise result in three
	 * separate Data nodes in the parse tree
	 */
	boolean bHasData = false;
	Data dataNode = null;
	Enumeration enum = e.children();
	while (enum.hasMoreElements()) {
	    Element e2 = (Element)enum.nextElement();
	    if (e2 instanceof Data) {
		bHasData = true;
		dataNode = (Data)e2;
		break;
	    }
	}

	/**
	 * Warning: this is not correct procedure according to XML spec.
	 * All whitespace matters!
	 */
	String sTrimmed = s.trim();
	if (sTrimmed.length() > 0) {
	    if (!bHasData) {
		e.addChild (new Data (s));
	    } else {
		dataNode.set (dataNode.data() + s);
	    }
	}
    }

    public void ignorableWhitespace (char ch[], int start, int length) {
    }

    public void processingInstruction (String target, String data) {
    }

    /**
     * Report all warnings, and continue parsing.
     *
     * @see org.xml.sax.ErrorHandler#warning
     */
    public void warning (SAXParseException exception)
    {
	m_sWarningMsg += exception.getMessage() +
	    " (" +
	    exception.getSystemId() +

	    "line <a href=\"#" +
	    exception.getLineNumber() +
	    "\">" +
	    exception.getLineNumber() +
	    "</a>, column " +
	    exception.getColumnNumber() +
	    ")";
    }


    /**
     * Report all recoverable errors, and try to continue parsing.
     *
     * @see org.xml.sax.ErrorHandler#error
     */
    public void error (SAXParseException exception)
    {
	m_sErrorMsg += "Recoverable Error: " +
	    exception.getMessage() +
	    " (" +
	    //	  exception.getSystemId() +
	  
	    "line <a href=\"#" +
	    exception.getLineNumber() +
	    "\">" +
	    exception.getLineNumber() +
	    "</a>, column " +
	    exception.getColumnNumber() +
	    ")";
    }


    /**
     * Report all fatal errors, and try to continue parsing.
     *
     * <p>Note: results are no longer reliable once a fatal error has
     * been reported.</p>
     *
     * @see org.xml.sax.ErrorHandler#fatalError
     */
    public void fatalError (SAXParseException exception)
    {
	m_sErrorMsg = "Fatal Error: " +
	    exception.getMessage() +
	    " (" +
	    //	  exception.getSystemId() +
	    "line <a href=\"#" +
	    exception.getLineNumber() +
	    "\">" +
	    exception.getLineNumber() +
	    "</a>, column " +
	    exception.getColumnNumber() +
	    ")";
    }

    /**
     * Generate an error message as a string
     */
    public void addError (String sMsg) {
	m_sErrorMsg += sMsg + "\n";
    }

    public String errors () {
	return m_sErrorMsg;
    }

    /**
     * Generate a warning message as a string
     */
    public void addWarning (String sMsg) {
	m_sWarningMsg += sMsg + "\n";
    }

    public String warnings () {
	return m_sWarningMsg;
    }

    public static Parser createParser (String className) {
	Parser parser = null;

	try {
	    // Get the named class.
	    Class c = Class.forName(className);
	    // Instantiate the parser.
	    parser = (Parser)(c.newInstance());
	} catch (ClassNotFoundException e) {
	    System.err.println("SAX parser class " + className +
			       "cannot be loaded.");
	    System.exit(1);
	} catch (IllegalAccessException e) {
	    System.err.println("SAX parser class " + className +
			       " does not have a zero-argument constructor.");
	    System.exit(1);
	} catch (InstantiationException e) {
	    System.err.println("SAX parser class " + className +
			       " cannot be instantiated.");
	    System.exit(1);
	}

	// Check the the parser object
	// actually implements the Parser interface.
	if (!(parser instanceof org.xml.sax.Parser)) {
	    System.err.println("Class " + className +
			       " does not implement org.xml.sax.Parser.");
	    System.exit(1);
	}

	return parser;
    }

    /**
     * If a URL is relative, make it absolute against the current directory.
     *
     * @exception java.net.MalformedURLException
     */
    private static String makeAbsoluteURL (String url)
	throws java.net.MalformedURLException {
	URL baseURL;

	String currentDirectory = System.getProperty("user.dir");
	String fileSep = System.getProperty("file.separator");
	String file = currentDirectory.replace(fileSep.charAt(0), '/') + '/';

	if (file.charAt(0) != '/') {
	    file = "/" + file;
	}
	baseURL = new URL("file", null, file);

	return new URL(baseURL, url).toString();
    }


    /**
     * Escape special characters for display.
     */
    private static String escapeCharacters(char ch[], int start, int length) {
	StringBuffer out = new StringBuffer();

	for (int i = start; i < start+length; i++) {
	    if (ch[i] >= 0x20 && ch[i] < 0x7f) {
		out.append(ch[i]);
	    } else {
		out.append("&#" + (int)ch[i] + ';');
	    }
	}

	return out.toString();
    }

    /**
     * Given an XML document (well-formed HTML, for example),
     * look for a suitable element to start parsing from
     *
     * @exception SAXException Passed on since we don't handle it.
     */
    public void processXML (Element ele) throws SAXException {
	if (isRDF(ele)) {
	    if (isRDFroot (ele)) {
		processRDF(ele);
	    } else if (isDescription (ele)) {
		processDescription (ele, false, m_bCreateBags, m_bCreateBags);
	    }
	} else {
	    Enumeration e = ele.children();
	    while (e.hasMoreElements()) {
		Element child = (Element)e.nextElement();
		processXML (child);
	    }
	}

	/**
	 * Call fetchSchema for all namespaces met during parsing
	 */
	if (m_bFetchSchemas) {
	    while (SiRPAC.s_vNStodo.size() > 0) {
		String sURI = (String)SiRPAC.s_vNStodo.elementAt(0);
		SiRPAC.s_vNStodo.removeElementAt(0);
		SiRPAC.s_vNSdone.addElement (sURI);

		fetchSchema (sURI);
	    }
	}
    }

    /**
     * fetchSchema fetches an RDF schema from the given <i>sURI</i>
     *
     */
    public void fetchSchema (String sURI) {
	setSource (sURI);
	try {
	    URL url = new URL (sURI);
	    String sContentType = url.openConnection().getContentType();
	    // DEBUG -> remove false later on
	    if (false &&
		!sContentType.startsWith ("text/xml") &&
		!sContentType.startsWith ("text/html")) {
		addError ("The RDF schema at "+sURI+" is of wrong content type '"+sContentType+"'\n(should have been 'text/xml' or 'text/html')");
	    } else {
			
		InputStream is = url.openStream ();
		InputSource source = new InputSource (is);
			
		// Create a new parser.
		Parser p = null;

		if (m_sXMLParser == null)
		    p = ParserFactory.makeParser();
		else
		    p = ParserFactory.makeParser(m_sXMLParser);
			
		// Register the handlers
		p.setEntityResolver(this);
		p.setDTDHandler (this);
		p.setDocumentHandler(this);
		p.setErrorHandler (this);

		p.parse(source);
		resolve ();
		processXML (root());
	    }
	} catch (Exception ex) {
	    addError ("Could not load RDF schema from "+sURI+". Problem: "+ex);
	}
    }

    /**
     * Start processing an RDF/XML document instance from the
     * root element <i>rdf</i>.
     *
     * @exception SAXException Passed on since we don't handle it.
     */
    public void processRDF (Element rdf) throws SAXException {
	Enumeration e = rdf.children();
	if (!e.hasMoreElements()) {
	    addError ("Empty RDF element");
	    return;
	}
	while (e.hasMoreElements()) {
	    Element ele = (Element)e.nextElement();

	    if (isDescription (ele)) {
		processDescription (ele, false, m_bCreateBags, m_bCreateBags);
	    } else if (isContainer (ele)) {
		processContainer (ele);
	    } else if (isTypedPredicate (ele)) {
		processTypedNode (ele);
	    }
	}
    }

    /**
     * Manage the typedNode production in the RDF grammar.
     *
     * @exception SAXException Passed on since we don't handle it.
     */
    public String processTypedNode (Element typedNode) throws SAXException {
	String sID              = typedNode.ID();
	String sBagID           = typedNode.bagID();
	String sAbout           = typedNode.about();
	String sAboutEach       = typedNode.aboutEach();
	String sAboutEachPrefix = typedNode.aboutEachPrefix();

	if (typedNode.resource() != null) {
	    addError ("'resource' attribute not allowed for a typedNode "+typedNode.name()+" - see <a href=\"http://www.w3.org/TR/REC-rdf-syntax/#typedNode\">[6.13]</a>");
	}

	/**
	 * We are going to manage this typedNode using the processDescription
	 * routine later on. Before that, place all properties encoded as
	 * attributes to separate child nodes.
	 */
	Enumeration e = typedNode.attributes ();
	while (e.hasMoreElements()) {
	    String sAttribute = (String)e.nextElement();
	    String sValue = typedNode.getAttribute (sAttribute);
	    sValue = sValue.trim ();

	    if (!sAttribute.startsWith (RDFMS) &&
		!sAttribute.startsWith (XMLSCHEMA)) {
		if (sValue.length() > 0) {
		    Element newPredicate = new Element (sAttribute,
						       new AttributeListImpl ());
		    newPredicate.addAttribute (RDFMS + "ID", (sAbout != null ? sAbout : sID));
		    newPredicate.addAttribute (RDFMS + "bagID", sBagID);

		    Data newData = new Data (sValue);
		    newPredicate.addChild (newData);
		    typedNode.addChild (newPredicate);
		    typedNode.removeAttribute (sAttribute);
		}
	    }
	}

	String sObject = new String ();
	if (sAbout != null)
	    sObject = sAbout;
	else if (sID != null)
	    sObject = sID;
	else
	    sObject = newReificationID(source());

	typedNode.ID (sObject, source());

	// special case: should the typedNode have aboutEach attribute,
	// the type predicate should distribute to pointed
	// collection also -> create a child node to the typedNode
	Enumeration eTargets = typedNode.targets ();
	if (sAboutEach != null &&
	    eTargets.hasMoreElements()) {
	    Element newPredicate = new Element (RDFMS + "type",
					       new AttributeListImpl());
	    newPredicate.resource (typedNode.name());

	    typedNode.addChild (newPredicate);
	} else {
	    addTriple (new Property(RDFMS + "type"),
		       new Resource(sObject),
		       new Resource(typedNode.name()));
	}

	String sDesc = processDescription (typedNode, false, false, false);

	return sObject;
    }

    /**
     * processDescription manages Description elements
     *
     * @param description	The Description element itself
     * @param inPredicate	Is this is a nested description
     * @param reificate		Do we need to reificate
     * @param createBag		Do we create a bag container
     *
     * @return		An ID for the description
     *
     * @exception SAXException Passed on since we don't handle it.
     */
    public String processDescription (Element description,
				      boolean inPredicate,
				      boolean reificate,
				      boolean createBag) throws SAXException {
	/**
	 * Return immediately if the description has already been managed
	 */
	if (description.done())
	    return description.ID();

	int	iChildCount = 1;
	boolean	bOnce = true;

	/**
	 * Determine first all relevant values
	 */
	String sID              = description.ID();
	String sBagid           = description.bagID();
	String sAbout           = description.about();
	String sAboutEach       = description.aboutEach();
	String sAboutEachPrefix = description.aboutEachPrefix();

	Element target = description.target();

	boolean hasTarget = description.targets().hasMoreElements();

	boolean targetIsContainer = false;
	String sTargetAbout = null;
	String sTargetBagid = null;
	String sTargetID = null;

	/**
	 * Determine what the target of the Description reference is
	 */
	if (hasTarget) {
	    sTargetAbout = target.about();
	    sTargetID    = target.ID();
	    sTargetBagid = target.bagID();

	    /**
	     * Target is collection if
	     * 1. it is identified with bagID attribute
	     * 2. it is identified with ID attribute and is a collection
	     */
	    if (sTargetBagid != null && sAbout != null) {
		targetIsContainer = (sAbout.substring(1).equals (sTargetBagid));
	    } else {
		if (sTargetID != null &&
		    sAbout != null &&
		    sAbout.substring(1).equals (sTargetID) &&
		    isContainer (target)) {
		    targetIsContainer = true;
		}
	    }
	}

	/**
	 * Check if there are properties encoded using the abbreviated
	 * syntax
	 */
	expandAttributes (description, description);

	/**
	 * Manage the aboutEach attribute here
	 */
	if (sAboutEach != null && hasTarget) {
	    if (isContainer(target)) {
		Enumeration e = target.children ();
		while (e.hasMoreElements()) {
		    Element ele = (Element)e.nextElement ();
		    if (isListItem (ele)) {
			String sResource = ele.resource();
			/**
			 * Manage <li resource="..." /> case
			 */
			if (sResource != null) {
			    Element newDescription = null;
			    newDescription = new Element (RDFMS + "Description",
							  new AttributeListImpl ());
			    newDescription.about (sResource);
			    Enumeration e2 = description.children();
			    while (e2.hasMoreElements()) {
				Element ele2 = (Element)e2.nextElement ();
				if (newDescription != null) {
				    newDescription.addChild (ele2);
				}
			    }
			    if (newDescription != null)
				processDescription (newDescription,
						    false, false, false);
			} else {
			    /**
			     * Otherwise we have a structured value inside <li>
			     */
			    
			    // loop through the children of <li>
			    // (can be only one)
			    Enumeration e2 = ele.children ();
			    while (e2.hasMoreElements()) {
				Element ele2 = (Element)e2.nextElement ();
				
				// loop through the items in the
				// description with aboutEach
				// and add them to the target
				Element newNode = new Element (RDFMS + "Description",
							       new AttributeListImpl());
				Enumeration e3 = description.children();
				while (e3.hasMoreElements()) {
				    Element ele3 = (Element)e3.nextElement ();
				    newNode.addChild (ele3);
				}
				newNode.addTarget (ele2);
				
				processDescription (newNode,
						    true, false, false);
			    }
			}
		    } else if (isTypedPredicate (ele)) {
			Element newNode = new Element (RDFMS + "Description",
						       new AttributeListImpl());
			Enumeration e2 = description.children();
			while (e2.hasMoreElements()) {
			    Element ele2 = (Element)e2.nextElement ();
			    newNode.addChild (ele2);
			}
			newNode.addTarget (ele);
			
			processDescription (newNode,
					    true, false, false);
		    }
		}
	    } else if (isDescription(target)) {
		processDescription (target,
				    false, reificate, createBag);

		Enumeration e = target.children ();
		while (e.hasMoreElements()) {
		    Element ele = (Element)e.nextElement ();
		    Element newNode = new Element (RDFMS + "Description",
						   new AttributeListImpl());
		    Enumeration e2 = description.children();
		    while (e2.hasMoreElements()) {
			Element ele2 = (Element)e2.nextElement ();
			newNode.addChild (ele2);
		    }
		    newNode.addTarget (ele);

		    processDescription (newNode,
					true, false, false);
		}
	    }
	    return null;
	}

	/**
	 * Manage the aboutEachPrefix attribute
	 */
	if (sAboutEachPrefix != null) {
	    if (hasTarget) {
		Enumeration e = description.targets();
		while (e.hasMoreElements()) {
		    target = (Element)e.nextElement ();
		    sTargetAbout = target.about();
		    Element newDescription = new Element (RDFMS + "Description",
							  new AttributeListImpl ());
		    newDescription.about (sTargetAbout);
		    Enumeration e2 = description.children();
		    while (e2.hasMoreElements()) {
			Element ele2 = (Element)e2.nextElement ();
			newDescription.addChild (ele2);
		    }
		    processDescription (newDescription,
					false, false, false);
		}
	    }
	    return null;
	}

	/**
	 * Enumerate through the children
	 */
	Enumeration e = description.children();

	while (e.hasMoreElements()) {
	    Element n = (Element)e.nextElement();

	    if (isDescription (n)) {
		addError ("Cannot nest a Description inside another Description");
	    } else if (isContainer (n)) {
		addError ("Cannot nest a container (Bag/Alt/Seq) inside a Description");
	    } else if (isTypedPredicate(n)) {


		String sChildID = null;

		if (hasTarget && targetIsContainer) {
		    sChildID = processPredicate (n, description,
						(target.bagID() != null ? target.bagID() : target.ID()),
						false);
		    description.ID (sChildID, source());
		    createBag = false;
		} else if (hasTarget) {
		    sChildID = processPredicate (n, description,
						(target.bagID() != null ? target.bagID() : target.ID()),
						reificate);
		    description.ID (sChildID, source());
		} else if (!hasTarget && !inPredicate) {
		    // if (description.ID() == null)
		    //description.ID (newReificationID(source()));
		    if (sAbout == null)
			if (sID != null)
			    sAbout = sID;
			else
			    sAbout = description.ID();
		    sChildID = processPredicate (n, description,
						 sAbout,
						 ( sBagid != null ? true : reificate));
		    //description.ID (sChildID, source());
		} else if (!hasTarget && inPredicate) {
		    if (sAbout == null) {
			if (sID != null) {
			    description.ID (sID, source());
			    sAbout = sID;
			} else {
			    if (description.ID() == null)
				description.ID (newReificationID(source()));
			    sAbout = description.ID();
			}
		    } else {
			description.ID (sAbout);
		    }
		    sChildID = processPredicate (n, description,
						sAbout,
						false);
		}

		/**
		 * Each Description block creates also a Bag node which
		 * has links to all properties within the block IF
		 * the m_bCreateBags variable is true
		 */
		if (sBagid != null || (m_bCreateBags && createBag)) {
		    String sNamespace = RDFMS;
		    // do only once and only if there is a child
		    if (bOnce && sChildID != null) {
			bOnce = false;
			if (description.bagID() == null)
			    description.bagID (newReificationID(source()));
			if (description.ID() == null)
			    description.ID (description.bagID());

			addTriple (new Property(sNamespace + "type"),
				   new Resource(description.bagID()),
				   new Resource(sNamespace + "Bag"));
		    }
		    if (sChildID != null) {
			addTriple (new Property(sNamespace + "_" + iChildCount),
				   new Resource(description.bagID()),
				   new Resource(sChildID));
			iChildCount++;
		    }
		}
	    }
	}

	description.done (true);

	return description.ID();
    }

    /**
     * processPredicate handles all elements not defined as special
     * RDF elements.
     *
     * @param predicate	The predicate element itself
     * @param description Context for the predicate
     * @param sTarget	The target resource
     * @param reificate	Should this predicate be reificated
     *
     * @return the new ID which can be used to identify the predicate
     *
     * @exception SAXException Passed on since we don't handle it.
     */
    private String processPredicate (Element predicate,
				     Element description,
				     String sTarget,
				     boolean reificate) throws SAXException {
	String sStatementID = predicate.ID();
	String sBagID       = predicate.bagID();
	String sResource    = predicate.resource();

	/**
	 * If a predicate has other attributes than rdf:ID, rdf:bagID,
	 * or xmlns... -> generate new triples according to the spec.
	 * (See end of Section 6)
	 */

	// this new element may not be needed
	Element d = new Element (RDFMS + "Description",
				 new AttributeListImpl());
	if (expandAttributes (d, predicate)) {
	    // error checking

	    // determine the 'about' part for the new statements
	    if (sStatementID != null) {
		d.about (sStatementID);
		// make rdf:ID the value of the predicate
		predicate.addChild (new Data (sStatementID));
	    } else if (sResource != null) {
		d.about (sResource);
	    } else {
		sStatementID = newReificationID(source());
		d.ID (sStatementID);
	    }

	    if (sBagID != null) {
		d.addAttribute (RDFMS + "bagID", sBagID);
		d.bagID (sBagID);
	    }

	    processDescription (d, true, false, m_bCreateBags);
	    predicate.addChild (d);
	}

	/**
	 * Tricky part: if the resource attribute is present for a predicate
	 * AND there are no children, the value of the predicate is either
	 * 1. the URI in the resource attribute OR
	 * 2. the node ID of the resolved #resource attribute
	 */
	if (sResource != null && !predicate.children().hasMoreElements()) {
	    if (predicate.target() == null) {
		if (reificate) {
		    sStatementID = reificate (new Property(predicate.name()),
					      new Resource(sTarget),
					      new Resource(sResource),
					      predicate.ID());
		    predicate.ID (sStatementID, source());
		} else {
		    addTriple (new Property(predicate.name()),
			       new Resource(sTarget),
			       new Resource(sResource));
		}
	    } else {
		if (reificate) {
		    sStatementID = reificate (new Property(predicate.name()),
					      new Resource(sTarget),
					      new Resource(predicate.target().ID()),
					      predicate.ID());
		    predicate.ID (sStatementID, source());
		} else {
		    addTriple (new Property(predicate.name()),
			       new Resource(sTarget),
			       new Resource(predicate.target().ID()));
		}
	    }
	    return predicate.ID();
	}

	/**
	 * Does this predicate make a reference somewhere using the
	 * <i>sResource</i> attribute
	 */
	if (sResource != null && predicate.target() != null) {
	    sStatementID = processDescription (predicate.target(),
					       true, false, false);
	    if (reificate) {
		sStatementID = reificate (new Property(predicate.name()),
					  new Resource(sTarget),
					  new Resource(sStatementID),
					  predicate.ID());
		predicate.ID (sStatementID, source());
	    } else {
		addTriple (new Property(predicate.name()),
			   new Resource(sTarget),
			   new Resource(sStatementID));
	    }

	    return sStatementID;
	}

	/**
	 * Before looping through the children, let's check
	 * if there are any. If not, the value of the predicate is
	 * an anonymous node
	 */
	Enumeration e2 = predicate.children();
	if (!(e2.hasMoreElements())) {
	    if (reificate) {
		sStatementID = reificate (new Property(predicate.name()),
					  new Resource(sTarget),
					  new Resource(newReificationID(source())),
					  predicate.ID());
	    } else {
		addTriple (new Property(predicate.name()),
			   new Resource(sTarget),
			   new Resource(newReificationID(source())));
	    }
	}
	boolean	bUsedTypedNodeProduction = false;

	while (e2.hasMoreElements()) {
	    Element n2 = (Element)e2.nextElement();

	    if (isDescription (n2)) {
		Element d2 = n2;

		sStatementID = processDescription (d2, true, false, false);

		d2.ID (sStatementID, source());

		if (reificate) {
		    sStatementID = reificate (new Property(predicate.name()),
					      new Resource(sTarget),
					      new Resource(sStatementID),
					      predicate.ID());
		} else {
		    addTriple (new Property(predicate.name()),
			       new Resource(sTarget),
			       new Resource(sStatementID));
		}

	    } else if (n2 instanceof Data) {
		/**
		 * We've got real data
		 */
		String	sValue = ((Data)n2).data();
		boolean	isXML = ((Data)n2).isXML();

		/**
		 * Only if the content is not empty PCDATA (whitespace that is),
		 * print the triple
		 */
		sValue = sValue.trim();
		if (sValue.length() > 0) {
		    if (reificate) {
			sStatementID = reificate (new Property(predicate.name()),
						  new Resource(sTarget),
						  new Literal(sValue, isXML),
						  predicate.ID());
			predicate.ID (sStatementID, source());
		    } else {
			addTriple (new Property(predicate.name()),
				   new Resource(sTarget),
				   new Literal(sValue, isXML));
		    }
		}
	    } else if (isContainer (n2)) {

		String sCollectionID = processContainer (n2);
		sStatementID = sCollectionID;

		/**
		 * Attach the collection to the current predicate
		 */
		if (description.target() != null) {
		    if (reificate) {
			sStatementID = reificate (new Property(predicate.name()),
						  new Resource(description.target().about()),
						  new Resource(sCollectionID),
						 predicate.ID());
			predicate.ID (sStatementID, source());
		    } else {
			addTriple (new Property(predicate.name()),
				   new Resource(description.target().about()),
				   new Resource(sCollectionID));
		    }
		} else {
		    if (reificate) {
			sStatementID = reificate (new Property(predicate.name()),
						  new Resource(sTarget),
						  new Resource(sCollectionID),
						  predicate.ID());
			predicate.ID (sStatementID, source());
		    } else {
			addTriple (new Property(predicate.name()),
				   new Resource(sTarget),
				   new Resource(sCollectionID));
		    }
		}
	    } else if (isTypedPredicate (n2)) {
		if (bUsedTypedNodeProduction) {
		    addError ("Only one typedNode allowed inside a predicate (Extra typedNode is "+n2.name()+") - see <a href=\"http://www.w3.org/TR/REC-rdf-syntax/#propertyElt\">[6.12]</a>");
		} else {
		    bUsedTypedNodeProduction = true;
		}
		sStatementID = processTypedNode (n2);
		addTriple (new Property(predicate.name()),
			   new Resource(sTarget),
			   new Resource(sStatementID));
	    }
	}

	return sStatementID;
    }

    private String processContainer (Element n) throws SAXException {
	String sID = n.ID();
	if (sID == null)
	    sID = newReificationID(source());

	/**
	 * Do the instantiation only once
	 */
	if (!n.done()) {
	    String sNamespace = RDFMS;
	    if (isSequence (n)) {
		addTriple (new Property(sNamespace+"type"),
			   new Resource(sID),
			   new Resource(sNamespace+"Seq"));
	    } else if (isAlternative (n)) {
		addTriple (new Property(sNamespace+"type"),
			   new Resource(sID),
			   new Resource(sNamespace+"Alt"));
	    } else if (isBag (n)) {
		addTriple (new Property(sNamespace+"type"),
			   new Resource(sID),
			   new Resource(sNamespace+"Bag"));
	    }
	    n.done (true);
	}

	expandAttributes (n, n);

	Enumeration e = ((Element)n).children();

	if (!e.hasMoreElements() &&
	    isAlternative (n)) {
	    addError ("An RDF:Alt container must have at least one nested listitem");
	}

	int iCounter = 1;
	while (e.hasMoreElements()) {
	    Element n2 = (Element)e.nextElement();
	    if (isListItem (n2)) {
		processListItem (sID, n2, iCounter);
		iCounter++;
	    } else {
		addError ("Cannot nest "+n2.name()+" inside a container (Bag/Alt/Seq)");
	    }
	}

	return sID;
    }

    private void processListItem (String sID, Element listitem, int iCounter)
	throws SAXException {
	/**
	 * Two different cases for
	 * 1. LI element without content (resource available)
	 * 2. LI element with content (resource unavailable)
	 */
	String sResource = listitem.resource();
	if (listitem.target() != null) {
	    sResource = listitem.target().ID();
	}
	if (sResource != null) {
	    addTriple (new Property(RDFMS+"_"+iCounter),
		       new Resource(sID),
		       new Resource(sResource));
	    // validity checking
	    if (listitem.children().hasMoreElements()) {
		addError ("Listitem with 'resource' attribute cannot have child nodes - see <a href=\"http://www.w3.org/TR/REC-rdf-syntax/#referencedItem\">[6.29]</a>");
	    }
	    listitem.ID (sResource, source());
	} else {
	    Enumeration e = listitem.children();
	    while (e.hasMoreElements()) {
		Element n = (Element)e.nextElement();
		if (n instanceof Data) {
		    addTriple (new Property(RDFMS+"_"+iCounter),
			       new Resource(sID),
			       new Literal(((Data)n).data()));
		} else if (isDescription (n)) {
		    String sNodeID = processDescription (n, false, true, false);
		    addTriple (new Property(RDFMS+"_"+iCounter),
			       new Resource(sID),
			       new Resource(sNodeID));
		    listitem.ID (sNodeID, source());
		} else if (isListItem (n)) {
		    addError ("Cannot nest a listitem inside another listitem");
		} else if (isContainer (n)) {
		    processContainer (n);
		    addTriple (new Property(RDFMS+"_"+iCounter),
			       new Resource(sID),
			       new Resource(n.ID()));
		} else if (isTypedPredicate (n)) {
		    String sNodeID = processTypedNode (n); // 
		    addTriple (new Property(RDFMS+"_"+iCounter),
			       new Resource(sID),
			       new Resource(sNodeID));
		}
	    }
	}
    }

    /**
     * checkAttributes goes through the attributes of element <i>e</i>
     * to see 
     * 1. if there are symbolic references to other nodes in the data model.
     *    in which case they must be stored for later resolving with
     *    <i>resolveLater</i> method.
     * 2. if there is an identity attribute, it is registered using
     *    <i>registerResource</i> or <i>registerID</i> method.
     *
     * @see	resolveLater
     * @see	registerResource
     * @see	registerID
     */
    private void checkAttributes (Element e) {

	String sResource = e.getAttribute (RDFMS, "resource");
	if (sResource != null) {
	    if (sResource.startsWith("#")) {
		resolveLater (e);
		e.resource (sResource);
	    } else {
		e.resource (sResource, source());
	    }
	}

	String sAboutEach = e.getAttribute (RDFMS, "aboutEach");
	if (sAboutEach != null &&
	    sAboutEach.startsWith("#")) {
	    resolveLater (e);
	    e.aboutEach (sAboutEach);
	}

	String sAboutEachPrefix = e.getAttribute (RDFMS, "aboutEachPrefix");
	if (sAboutEachPrefix != null) {
	    resolveLater (e);
	    e.aboutEachPrefix (sAboutEachPrefix);
	}

	String sAbout = e.getAttribute (RDFMS, "about");
	if (sAbout != null) {
	    if (sAbout.startsWith("#")) {
		resolveLater (e);
	    } else {
		registerID (sAbout, e);
		registerResource (e);
		e.about (sAbout, source());
	    }
	}

	String sBagID = e.getAttribute (RDFMS, "bagID");
	if (sBagID != null) {
	    e.bagID (sBagID, source());
	    sBagID = e.bagID();
	    registerID (sBagID, e);
	}

	String sID = e.getAttribute (RDFMS, "ID");
	if (sID != null) {
	    e.ID (sID, source());
	    sID = e.ID();
	    registerID (sID, e);
	}

	if (sID != null && sAbout != null) {
	    addError ("A description block cannot use both 'ID' and 'about' attributes - see <a href=\"http://www.w3.org/TR/REC-rdf-syntax/#idAboutAttr\">[6.5]</a>");
	}
    }

    /**
     * Take an element <i>ele</i> with its parent element <i>parent</i>
     * and evaluate all its attributes to see if they are non-RDF specific
     * and non-XML specific in which case they must become children of
     * the <i>ele</i> node.
     *
     * @exception SAXException Passed on since we don't handle it.
     */
    private boolean expandAttributes (Element parent, Element ele)
	throws SAXException {
	boolean foundAbbreviation = false;
	Enumeration e = ele.attributes ();
	while (e.hasMoreElements()) {
	    String sAttribute = (String)e.nextElement();
	    String sValue = ele.getAttribute (sAttribute).trim();

	    if (sAttribute.startsWith (XMLSCHEMA))
		continue;
	    // exception: expand rdf:value
	    if (sAttribute.startsWith (RDFMS) && 
		!sAttribute.startsWith (RDFMS+"_") &&
		!sAttribute.endsWith ("value") &&
		!sAttribute.endsWith ("type"))
		continue;
	    if (sValue.length() > 0) {
		foundAbbreviation = true;
		Element newElement = new Element (sAttribute,
						  new AttributeListImpl());
		Data newData = new Data (sValue);
		newElement.addChild (newData);
		parent.addChild (newElement);
	    }
	}
	return foundAbbreviation;
    }

    /**
     * reificate creates one new node and four new triples
     * and returns the ID of the new node
     */
    private String reificate (Property predicate,
			      Resource subject,
			      RDFnode object,
			      String sNodeID) {
	String sNamespace = RDFMS;
	if (sNodeID == null)
	    sNodeID = newReificationID(source());

	/**
	 * The original statement must remain in the data model
	 */
	addTriple (predicate, subject, object);

	/**
	 * Do not reificate reificated properties
	 */
	if (predicate.equals (sNamespace+"subject") ||
	    predicate.equals (sNamespace+"predicate") ||
	    predicate.equals (sNamespace+"object") ||
	    predicate.equals (sNamespace+"type")) {
	    return null;
	}

	/**
	 * Reificate by creating 4 new triples
	 */
	addTriple (new Property(sNamespace + "predicate"),
		   new Resource(sNodeID),
		   predicate);

	addTriple (new Property(sNamespace + "subject"),
		   new Resource(sNodeID),
		   new Resource(( subject.toString().length() == 0 ? source() : subject.toString())));

	addTriple (new Property(sNamespace + "object"),
		   new Resource(sNodeID),
		   object);

	addTriple (new Property(sNamespace + "type"),
		   new Resource(sNodeID),
		   new Resource(sNamespace + "Statement"));

	return sNodeID;
    }

    /**
     * Create a new triple and add it to the <i>m_triples</i> Vector
     */
    public void addTriple (Property predicate,
			   Resource subject,
			   RDFnode object) {
	/**
	 * If there is no subject (about=""), then use the URI/filename where
	 * the RDF description came from
	 */
	if (predicate == null) {
	    addWarning ("Predicate null when subject="+subject+" and object="+object);
	    return;
	}

	if (subject == null) {
	    addWarning ("Subject null when predicate="+predicate+" and object="+object);
	    return;
	}

	if (object == null) {
	    addWarning ("Object null when predicate="+predicate+" and subject="+subject);
	    return;
	}

	if (subject.toString() == null ||
	    subject.toString().length() == 0) {
	    subject = new Resource(source());
	}

	Triple t = new Triple (predicate, subject, object);
	m_triples.addElement (t);

	/**
	 * Notify RDFConsumer objects if any
	 */
	for (int x = 0; x < m_consumers.size(); x++) {
	    RDFConsumer consumer = (RDFConsumer)m_consumers.elementAt(x);
	    consumer.assert (this, predicate, subject, object);
	}
    }

    /**
     * Print all triples to the <i>ps</i> PrintStream
     */
    public void printTriples (PrintStream ps) {
	for (int x = 0; x < m_triples.size(); x++) {
	    Triple t = (Triple)m_triples.elementAt (x);
	    ps.println ("triple(\""+t.predicate()+"\",\""+t.subject()+"\",\""+t.object()+"\").");
	}
    }

    /**
     * Return all created triples in an Enumeration instance
     */
    public Enumeration triples () {
	return m_triples.elements ();
    }

    /**
     * Is the element a Description
     */
    public boolean isDescription (Element e) {
	return isRDF(e) &&
	    e.name().endsWith ("Description");
    }

    /**
     * Is the element a ListItem
     */
    public boolean isListItem (Element e) {
	return isRDF(e) &&
	    ( e.name().endsWith ("li") ||
	      e.name().indexOf ("_") > -1);
    }

    /**
     * Is the element a Container
     *
     * @see	isSequence
     * @see	isAlternative
     * @see	isBag
     */
    public boolean isContainer (Element e) {
	return (isSequence (e) ||
		isAlternative (e) ||
		isBag (e));
    }

    /**
     * Is the element a Sequence
     */
    public boolean isSequence (Element e) {
	return isRDF(e) &&
	    e.name().endsWith ("Seq");
    }

    /**
     * Is the element an Alternative
     */
    public boolean isAlternative (Element e) {
	return isRDF(e) &&
	    e.name().endsWith ("Alt");
    }

    /**
     * Is the element a Bag
     */
    public boolean isBag (Element e) {
	return isRDF(e) &&
	    e.name().endsWith ("Bag");
    }

    /**
     * This method matches all properties but those from RDF namespace
     */
    public boolean isTypedPredicate (Element e) {
	if (isRDF(e)) {
	    // list all RDF predicates known by the RDF specification
	    if (e.name().endsWith ("predicate") ||
		e.name().endsWith ("subject") ||
		e.name().endsWith ("object") ||
		e.name().endsWith ("type") ||
		e.name().endsWith ("value") ||
		e.name().indexOf("_") >= 0 ||
		e.name().endsWith ("Property") ||
		e.name().endsWith ("Statement")) {
		return true;
	    }
	    return false;
	}
	if (e.name().length() > 0)
	    return true;
	else
	    return false;
    }

    public boolean isRDFroot (Element e) {
	return isRDF(e) &&
	    e.name().endsWith ("RDF");
    }

    /**
     * Check if the element <i>e</i> is from the namespace
     * of the RDF schema by comparing only the beginning of
     * the expanded element name with the canonical RDFMS
     * URI
     */
    public boolean isRDF (Element e) {
	if (e != null && e.name() != null)
	    return e.name().startsWith (RDFMS);
	else
	    return false;
    }

    /**
     * Methods for node reference management
     */
    private Vector	m_vResources = new Vector ();
    private Vector	m_vResolveQueue = new Vector ();
    private Hashtable	m_hIDtable = new Hashtable ();
    private int		m_iReificationCounter = 0;

    /**
     * Add the element <i>e</i> to the <i>m_vResolveQueue</i>
     * to be resolved later.
     */
    public void resolveLater (Element e) {
	m_vResolveQueue.addElement (e);
    }

    /**
     * Go through the <i>m_vResolveQueue</i> and assign
     * direct object reference for each symbolic reference
     */
    public void resolve () {
	for (int x = 0; x < m_vResolveQueue.size(); x++) {
	    Element e = (Element)m_vResolveQueue.elementAt(x);

	    String sAbout = e.about();
	    if (sAbout != null) {
		if (sAbout.startsWith ("#"))
		    sAbout = sAbout.substring(1);
		Element e2 = (Element)lookforNode(sAbout);
		if (e2 != null) {
		    e.addTarget (e2);
		} else {
		    addError ("Unresolved internal reference to "+sAbout);
		}
	    }

	    String sResource = e.getAttribute (RDFMS, "resource");
	    if (sResource != null) {
		if (sResource.startsWith ("#"))
		    sResource = sResource.substring(1);
		Element e2 = (Element)lookforNode(sResource);
		if (e2 != null) {
		    e.resource (e2.ID());
		    e.addTarget (e2);
		} else {
		    addError ("Unresolved internal reference to "+sResource);
		}
	    }

	    String sAboutEach = e.getAttribute (RDFMS, "aboutEach");
	    if (sAboutEach != null) {
		sAboutEach = sAboutEach.substring(1);
		Element e2 = (Element)lookforNode(sAboutEach);
		if (e2 != null) {
		    e.addTarget (e2);
		}	
	    }

	    String sAboutEachPrefix = e.getAttribute (RDFMS, "aboutEachPrefix");
	    if (sAboutEachPrefix != null) {
		for (int y = 0; y < m_vResources.size(); y++) {
		    Element ele = (Element)m_vResources.elementAt(y);
		    String sA = ele.about();
		    if (sA.startsWith (sAboutEachPrefix)) {
			e.addTarget (ele);
		    }
		}
	    }
	}
	m_vResolveQueue.removeAllElements();
    }

    /**
     * Look for a node by name <i>sID</i> from the Hashtable
     * <i>m_hIDtable</i> of all registered IDs.
     */
    public Element lookforNode (String sID) {
	if (sID == null) {
	    return null;
 	} else {
	    return (Element)m_hIDtable.get (source()+"#"+sID);
	}
    }

    /**
     * Add an element <i>e</i> to the Hashtable <i>m_hIDtable</i>
     * which stores all nodes with an ID
     */
    public void registerID (String sID, Element e) {
	if (m_hIDtable.get (sID) != null)
	    addError("Node ID '"+sID+"' redefined.");
	m_hIDtable.put (sID, e);
    }

    /**
     * Create a new reification ID by using a name part and an
     * incremental counter <i>m_iReificationCounter</i>.
     */
    public String newReificationID (String sSource) {
	m_iReificationCounter++;

	return Element.makeAbsolute(new String ("genid" + m_iReificationCounter),
						sSource);
    }

    /**
     * Add an element <i>e</i> to the Vector <i>m_vResources</i>
     * which stores all nodes with an URI
     */
    public void registerResource (Element e) {
	m_vResources.addElement (e);
    }

    public void makeMarkupST (Element ele) {
	m_sLiteral += "<" + ele.name();

	Enumeration e = ele.attributes();
	while (e.hasMoreElements()) {
	    String sAttribute = (String)e.nextElement();
	    String sAttributeValue = (String)ele.getAttribute (sAttribute);
	    m_sLiteral += " " + sAttribute + "='" + sAttributeValue + "'";
	}
	m_sLiteral += ">";
    }

    public void makeMarkupET (String name) {
	m_sLiteral += "</" + name + ">";
    }

    public void makeMarkupChar (String s) {
	m_sLiteral += s;
    }

    /**
     * This method adds a warning for each name (element & attribute)
     * which looks like it is from RDF but it is not.
     *
     * Note: this method is useful for interactive use but can be
     * omitted from embedded applications.
     */

    public void likeRDF (String sNamespace, String sElement) {
	if (!sNamespace.equals (RDFMS)) {
	    if (sElement.equals ("RDF") ||
		sElement.equals ("Description") ||
		sElement.equals ("Bag") ||
		sElement.equals ("Alt") ||
		sElement.equals ("Seq") ||
		sElement.equals ("li") ||
		sElement.equals ("_1") ||
		sElement.equals ("ID") ||
		sElement.equals ("parseType") ||
		sElement.equals ("resource") ||
		sElement.equals ("about") ||
		sElement.equals ("value") ||
		sElement.equals ("subject") ||
		sElement.equals ("predicate") ||
		sElement.equals ("object") ||
		sElement.equals ("type")) {
		addWarning ("Name '"+sElement+"' looks like it is from RDF but it has namespace "+sNamespace+"\n");
	    }
	}
    }

    /**
     * main method for running SiRPAC as an application
     */
    public static void main (String args[]) throws Exception {
	if (args.length == 0) {
	    System.err.println("Usage: java -Dorg.xml.sax.parser=<classname> org.w3c.rdf.SiRPAC [ URI or filename ] [-fetch_schemas | -f]");
	    System.err.println ("This is revision "+REVISION);
	    System.exit(1);
	}

	SiRPAC compiler = new SiRPAC ();
	if (args.length == 2 &&
	    args[1].startsWith ("-f")) {
	    compiler.fetchSchemas (true);
	}

	URL url = null;
	try {
	    url = new URL (args[0]);
	} catch (Exception e) {
	    url = new URL ("file", null, args[0]);
	}

	compiler.setRDFSource (new RDFSource(url));
	compiler.fetchRDF ();

	String sErrors = compiler.errors ();
	if (sErrors != null && sErrors.length() > 0) {
	    System.out.println ("Errors during parsing:\n"+sErrors);
	} else {
	    compiler.printTriples (System.out);
	}
	/*
	String sWarnings = compiler.warnings ();
	if (sWarnings != null && sWarnings.length() > 0) {
	    System.out.println ("Warnings during parsing:\n"+sWarnings);
	}
	*/
    }
}