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

Re: quick reply



Hi Dan

> would love to see new version of SiRPAC; let me know how best to help.

I attach here my local copy of SiRPAC.java. Please run your test
files with it and tell me how (s)he it doing. You wouldn't be
participating in the xml meetings in Sophia this week, would you?

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);
	}
	*/
    }
}