All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.semanticweb.owlapi.rdf.syntax.RDFParser Maven / Gradle / Ivy

There is a newer version: 3.4.9.2-ansell
Show newest version
/*
 * This file is part of the OWL API.
 *
 * The contents of this file are subject to the LGPL License, Version 3.0.
 *
 * Copyright (C) 2011, The University of Manchester
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see http://www.gnu.org/licenses/.
 *
 *
 * Alternatively, the contents of this file may be used under the terms of the Apache License, Version 2.0
 * in which case, the provisions of the Apache License Version 2.0 are applicable instead of those above.
 *
 * Copyright 2011, University of Manchester
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *
 * Original code by Boris Motik.  The original code formed part of KAON
 * which is licensed under the LGPL License. The original package
 * name was edu.unika.aifb.rdf.api
 *
 */
package org.semanticweb.owlapi.rdf.syntax;

import java.io.IOException;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.NodeID;
import org.semanticweb.owlapi.rdf.util.RDFConstants;
import org.xml.sax.Attributes;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.LocatorImpl;

/** This class parses the RDF according to the syntax specified in http://www.w3.org/TR/rdf-syntax-grammar/. */
@SuppressWarnings({ "unused", "javadoc" })
public class RDFParser extends DefaultHandler implements RDFConstants {
    protected static final Locator s_nullDocumentLocator = new LocatorImpl();
    protected static final SAXParserFactory s_parserFactory = SAXParserFactory
            .newInstance();
    private Map resolvedIRIs = new HashMap();
    protected Map uriCache = new HashMap();
    static {
        s_parserFactory.setNamespaceAware(true);
    }
    /** Registered error handler. */
    protected ErrorHandler m_errorHandler;
    /** Stack of base IRIs. */
    protected LinkedList m_baseIRIs;
    private Map m_baseURICache;
    /** IRI of the document being parsed. */
    protected IRI m_baseIRI;
    /** The stack of languages. */
    protected LinkedList m_languages;
    /** The current language. */
    protected String m_language;
    /** Consumer receiving notifications about parsing events. */
    protected RDFConsumer m_consumer;
    /** Current parser's state. */
    protected State m_state;
    /** Stack of parser states. */
    protected List m_states;
    /** Document locator. */
    protected Locator m_documentLocator;

    /** Creates a RDF parser. */
    public RDFParser() {
        m_states = new ArrayList();
        m_baseIRIs = new LinkedList();
        m_languages = new LinkedList();
        m_baseURICache = new HashMap();
    }

    /** Parses RDF from given input source.
     * 
     * @param source
     *            specifies where RDF comes from
     * @param consumer
     *            receives notifications about RDF parsing events */
    public void parse(InputSource source, RDFConsumer consumer) throws SAXException,
            IOException {
        String systemID = source.getSystemId();
        try {
            m_documentLocator = s_nullDocumentLocator;
            if (systemID != null) {
                m_baseIRI = IRI.create(new URI(source.getSystemId()));
            } else {
                throw new SAXException(
                        "Supplied InputSource object myst have systemId property set, which is needed for IRI resolution.");
            }
            m_consumer = consumer;
            m_consumer.startModel(m_baseIRI.toString());
            SAXParser parser = s_parserFactory.newSAXParser();
            parser.parse(source, this);
            m_consumer.endModel();
        } catch (ParserConfigurationException e) {
            throw new SAXException("Parser coniguration exception", e);
        } catch (URISyntaxException e) {
            throw new SAXException("Invalid SystemID '" + systemID
                    + "'of the supplied input source.");
        } finally {
            m_state = null;
            m_states.clear();
            m_documentLocator = null;
            m_baseIRIs.clear();
        }
    }

    @Override
    public void setDocumentLocator(Locator locator) {
        m_documentLocator = locator;
    }

    /** Sets the error handler.
     * 
     * @param errorHandler
     *            the error handler */
    public void setErrorHandler(ErrorHandler errorHandler) {
        m_errorHandler = errorHandler;
    }

    @Override
    public void warning(SAXParseException e) throws SAXException {
        if (m_errorHandler == null) {
            super.warning(e);
        } else {
            m_errorHandler.warning(e);
        }
    }

    @Override
    public void error(SAXParseException e) throws SAXException {
        if (m_errorHandler == null) {
            super.error(e);
        } else {
            m_errorHandler.error(e);
        }
    }

    @Override
    public void fatalError(SAXParseException e) throws SAXException {
        if (m_errorHandler == null) {
            super.fatalError(e);
        } else {
            m_errorHandler.fatalError(e);
        }
    }

    @Override
    public void startDocument() {
        m_states.clear();
        pushState(new StartRDF());
    }

    @Override
    public void endDocument() throws SAXException {
        if (m_state != null) {
            throw new RDFParserException("RDF content not finished.", m_documentLocator);
        }
    }

    @Override
    public void startElement(String namespaceIRI, String localName, String qName,
            Attributes atts) throws SAXException {
        processXMLBase(atts);
        processXMLLanguage(atts);
        m_state.startElement(namespaceIRI, localName, qName, atts);
    }

    @Override
    public void endElement(String namespaceIRI, String localName, String qName)
            throws SAXException {
        m_state.endElement(namespaceIRI, localName, qName);
        m_baseIRI = m_baseIRIs.remove(0);
        m_language = m_languages.remove(0);
    }

    @Override
    public void characters(char[] data, int start, int length) throws SAXException {
        m_state.characters(data, start, length);
    }

    @Override
    public void processingInstruction(String target, String data) throws SAXException {
        if ("include-rdf".equals(target)) {
            Map arguments = parseStringArguments(data);
            if (arguments.size() > 2) {
                throw new RDFParserException(
                        "Incorrect number of arguments for 'include-rdf' processing instruction.",
                        m_documentLocator);
            }
            String logicalIRI = arguments.get("logicalIRI");
            String physicalIRI = arguments.get("physicalIRI");
            if (physicalIRI != null) {
                physicalIRI = resolveIRI(physicalIRI);
            }
            m_consumer.includeModel(logicalIRI, physicalIRI);
        } else if ("model-attribute".equals(target)) {
            Map arguments = parseStringArguments(data);
            if (arguments.size() != 2) {
                throw new RDFParserException(
                        "Incorrect number of arguments for 'model-attribute' processing instruction.",
                        m_documentLocator);
            }
            String key = arguments.get("key");
            if (key == null) {
                throw new RDFParserException(
                        "Mising the 'key' argument for 'model-attribute' processing instruction.",
                        m_documentLocator);
            }
            String value = arguments.get("value");
            if (value == null) {
                throw new RDFParserException(
                        "Mising the 'value' argument for 'model-attribute' processing instruction.",
                        m_documentLocator);
            }
            m_consumer.addModelAttribte(key, value);
        }
    }

    /** Pushes a new state on the state stack.
     * 
     * @param state
     *            new state */
    protected void pushState(State state) {
        m_states.add(state);
        m_state = state;
    }

    /** Pops a state from the stack. */
    protected void popState() throws SAXException {
        int size = m_states.size();
        if (size == 0) {
            throw new RDFParserException("Internal exception: state stack is empty.",
                    m_documentLocator);
        }
        if (size == 1) {
            m_state = null;
        } else {
            m_state = m_states.get(size - 2);
        }
        m_states.remove(size - 1);
    }

    /** Checks if attribute list contains some of the unsupported attributes.
     * 
     * @param atts
     *            the attributes */
    protected void checkUnsupportedAttributes(Attributes atts) throws SAXException {
        if (atts.getIndex(RDFNS, ATTR_ABOUT_EACH) != -1) {
            throw new RDFParserException("rdf:aboutEach attribute is not supported.",
                    m_documentLocator);
        }
        if (atts.getIndex(RDFNS, ATTR_ABOUT_EACH_PREFIX) != -1) {
            throw new RDFParserException(
                    "rdf:aboutEachPrefix attribute is not supported.", m_documentLocator);
        }
    }

    private IRI resolveFromDelegate(IRI iri, String value) {
        if (NodeID.isAnonymousNodeIRI(value)) {
            return IRI.create(value, null);
        }
        // cache the delegate URI if not there already
        if (!m_baseURICache.containsKey(m_baseIRI)) {
            m_baseURICache.put(m_baseIRI, m_baseIRI.toURI());
        }
        // get hold of the delegate URI
        URI delegateURI = m_baseURICache.get(m_baseIRI);
        // resolve against delegate
        return IRI.create(delegateURI.resolve(value));
    }

    /** Processes xml:base reference if there is one.
     * 
     * @param atts
     *            the attributes potentially containing xml:base declaration */
    protected void processXMLBase(Attributes atts) throws SAXException {
        m_baseIRIs.add(0, m_baseIRI);
        String value = atts.getValue(XMLNS, "base");
        if (value != null) {
            try {
                m_baseIRI = resolveFromDelegate(m_baseIRI, value);
                resolvedIRIs.clear();
            } catch (IllegalArgumentException e) {
                RDFParserException exception = new RDFParserException("New base IRI '"
                        + value + "' cannot be resolved against curent base IRI "
                        + m_baseIRI.toString(), m_documentLocator);
                exception.initCause(e);
                throw exception;
            }
        }
    }

    /** Processes xml:language reference is there is one.
     * 
     * @param atts
     *            the attributes potentially containing xml:language declaration */
    protected void processXMLLanguage(Attributes atts) {
        m_languages.add(0, m_language);
        String value = atts.getValue(XMLLANG);
        if (value != null) {
            m_language = value;
        }
    }

    private int cacheHits = 0;

    /** Resolves an IRI with the current base.
     * 
     * @param uri
     *            the IRI being resolved
     * @return the resolved IRI */
    protected String resolveIRI(String uri) throws SAXException {
        if (uri.length() == 0) {
            // MH - Fix for resolving a "This document" reference against base
            // IRIs.
            // XXX namespace?
            String base = m_baseIRI.toString();
            int hashIndex = base.indexOf("#");
            if (hashIndex != -1) {
                return base.substring(0, hashIndex);
            } else {
                return base;
            }
        } else {
            try {
                String resolved = resolvedIRIs.get(uri);
                if (resolved != null) {
                    return resolved;
                } else {
                    IRI theIRI = resolveFromDelegate(m_baseIRI, uri);
                    String u = theIRI.toString();
                    uriCache.put(u, theIRI);
                    resolvedIRIs.put(uri, u);
                    return u;
                }
            } catch (IllegalArgumentException e) {
                RDFParserException exception = new RDFParserException("IRI '" + uri
                        + "' cannot be resolved against curent base IRI "
                        + m_baseIRI.toString(), m_documentLocator);
                exception.initCause(e);
                throw exception;
            }
        }
    }

    /** Returns an absolute IRI from an ID. */
    protected String getIRIFromID(String id) throws SAXException {
        return resolveIRI("#" + id);
    }

    /** Returns an absolute IRI from an about attribute. */
    protected String getIRIFromAbout(String about) throws SAXException {
        return resolveIRI(about);
    }

    /** Returns an absolute IRI from a resource attribute. */
    protected String getIRIFromResource(String resource) throws SAXException {
        return resolveIRI(resource);
    }

    /** Extracts the IRI of the resource from rdf:ID, rdf:nodeID or rdf:about
     * attribute. If no attribute is found, an IRI is generated. */
    protected String getIDNodeIDAboutResourceIRI(Attributes atts) throws SAXException {
        String result = null;
        String value = atts.getValue(RDFNS, ATTR_ID);
        if (value != null) {
            result = getIRIFromID(value);
        }
        value = atts.getValue(RDFNS, ATTR_ABOUT);
        if (value != null) {
            if (result != null) {
                throw new RDFParserException(
                        "Element cannot specify both rdf:ID and rdf:about attributes.",
                        m_documentLocator);
            }
            result = getIRIFromAbout(value);
        }
        value = atts.getValue(RDFNS, ATTR_NODE_ID);
        if (value != null) {
            if (result != null) {
                throw new RDFParserException(
                        "Element cannot specify both rdf:nodeID and rdf:ID or rdf:about attributes.",
                        m_documentLocator);
            }
            result = NodeID.getIRIFromNodeID(value);
        }
        if (result == null) {
            result = NodeID.nextAnonymousIRI();
        }
        return result;
    }

    /** Extracts the IRI of the resource from rdf:resource or rdf:nodeID
     * attribute. If no attribute is found, null is returned.
     * 
     * @param atts
     *            the attributes
     * @return the IRI of the resource or null */
    protected String getNodeIDResourceResourceIRI(Attributes atts) throws SAXException {
        String value = atts.getValue(RDFNS, ATTR_RESOURCE);
        if (value != null) {
            return getIRIFromResource(value);
        } else {
            value = atts.getValue(RDFNS, ATTR_NODE_ID);
            if (value != null) {
                return NodeID.getIRIFromNodeID(value);
            } else {
                return null;
            }
        }
    }

    /** Called when a statement with resource value is added to the model.
     * 
     * @param subject
     *            IRI of the subject resource
     * @param predicate
     *            IRI of the predicate resource
     * @param object
     *            IRI of the object resource
     * @param reificationID
     *            if not null, contains IRI of the resource that
     *            will wold the reified statement */
    protected void statementWithResourceValue(String subject, String predicate,
            String object, String reificationID) throws SAXException {
        m_consumer.statementWithResourceValue(subject, predicate, object);
        if (reificationID != null) {
            m_consumer.statementWithResourceValue(reificationID, RDF_TYPE, RDF_STATEMENT);
            m_consumer.statementWithResourceValue(reificationID, RDF_SUBJECT, subject);
            m_consumer
                    .statementWithResourceValue(reificationID, RDF_PREDICATE, predicate);
            m_consumer.statementWithResourceValue(reificationID, RDF_OBJECT, object);
        }
    }

    /** Called when a statement with literal value is added to the model.
     * 
     * @param subject
     *            IRI of the subject resource
     * @param predicate
     *            IRI of the predicate resource
     * @param object
     *            literal object value
     * @param dataType
     *            the IRI of the literal's datatype (may be null)
     * @param reificationID
     *            if not null, contains IRI of the resource that
     *            will wold the reified statement */
    protected void statementWithLiteralValue(String subject, String predicate,
            String object, String dataType, String reificationID) throws SAXException {
        m_consumer.statementWithLiteralValue(subject, predicate, object, m_language,
                dataType);
        if (reificationID != null) {
            m_consumer.statementWithResourceValue(reificationID, RDF_TYPE, RDF_STATEMENT);
            m_consumer.statementWithResourceValue(reificationID, RDF_SUBJECT, subject);
            m_consumer
                    .statementWithResourceValue(reificationID, RDF_PREDICATE, predicate);
            m_consumer.statementWithLiteralValue(reificationID, RDF_OBJECT, object,
                    m_language, dataType);
        }
    }

    /** Parses the propertyAttributes production.
     * 
     * @param subjectIRI
     *            IRI of the resource whose properties are being parsed
     * @param atts
     *            attributes
     * @param reificationManager
     *            the reification manager */
    protected void propertyAttributes(String subjectIRI, Attributes atts,
            ReificationManager reificationManager) throws SAXException {
        int length = atts.getLength();
        for (int i = 0; i < length; i++) {
            String nsIRI = atts.getURI(i);
            String localName = atts.getLocalName(i);
            if (!XMLNS.equals(nsIRI)
                    && !XMLLANG.equals(localName)
                    && !(RDFNS.equals(nsIRI) && (ATTR_ID.equals(localName)
                            || ATTR_NODE_ID.equals(localName)
                            || ATTR_ABOUT.equals(localName) || ELT_TYPE.equals(localName)
                            || ATTR_RESOURCE.equals(localName)
                            || ATTR_PARSE_TYPE.equals(localName)
                            || ATTR_ABOUT_EACH.equals(localName)
                            || ATTR_ABOUT_EACH_PREFIX.equals(localName) || ATTR_BAG_ID
                                .equals(localName)))) {
                String value = atts.getValue(i);
                String reificationID = reificationManager.getReificationID(null);
                statementWithLiteralValue(subjectIRI, nsIRI + localName, value, null,
                        reificationID);
            } else if (RDFNS.equals(nsIRI) && ELT_TYPE.equals(localName)) {
                String value = resolveIRI(atts.getValue(i));
                String reificationID = reificationManager.getReificationID(null);
                statementWithResourceValue(subjectIRI, nsIRI + localName, value,
                        reificationID);
            }
        }
    }

    /** Checks whether given characters contain only whitespace.
     * 
     * @param data
     *            the data being checked
     * @param start
     *            the start index (inclusive)
     * @param length
     *            the end index (non-inclusive)
     * @return true if characters contain whitespace */
    protected boolean isWhitespaceOnly(char[] data, int start, int length) {
        int end = start + length;
        for (int i = start; i < end; i++) {
            char c = data[i];
            if (c != '\n' && c != '\r' && c != '\t' && c != ' ') {
                return false;
            }
        }
        return true;
    }

    /** Checks whether given characters contain only whitespace.
     * 
     * @param buffer
     *            the data being checked
     * @return true if characters contain whitespace */
    protected boolean isWhitespaceOnly(StringBuilder buffer) {
        for (int i = 0; i < buffer.length(); i++) {
            char c = buffer.charAt(i);
            if (c != '\n' && c != '\r' && c != '\t' && c != ' ') {
                return false;
            }
        }
        return true;
    }

    /** Returns the reification manager for given attributes.
     * 
     * @param atts
     *            the attributes
     * @return the reification manager */
    protected ReificationManager getReificationManager(Attributes atts)
            throws SAXException {
        String bagIDAttr = atts.getValue(RDFNS, ATTR_BAG_ID);
        if (bagIDAttr == null) {
            return ReificationManager.INSTANCE;
        } else {
            String bagID = getIRIFromID(bagIDAttr);
            return new ReifiedStatementBag(bagID);
        }
    }

    /** Parses the string into a map of name-value pairs.
     * 
     * @param string
     *            string to be parsed
     * @return map of name-value pairs
     * @throws SAXException
     *             if there was an IOException this will be wrapped in a parse
     *             exception */
    protected Map parseStringArguments(String string) throws SAXException {
        try {
            StreamTokenizer tokenizer = new StreamTokenizer(new StringReader(string));
            Map result = new HashMap();
            tokenizer.nextToken();
            while (tokenizer.ttype != StreamTokenizer.TT_EOF) {
                if (tokenizer.ttype != StreamTokenizer.TT_WORD) {
                    throw new RDFParserException(
                            "Invalid processing instruction argument.", m_documentLocator);
                }
                String name = tokenizer.sval;
                if ('=' != tokenizer.nextToken()) {
                    throw new RDFParserException("Expecting token =", m_documentLocator);
                }
                tokenizer.nextToken();
                if (tokenizer.ttype != '\"' && tokenizer.ttype != '\'') {
                    throw new RDFParserException(
                            "Invalid processing instruction argument.", m_documentLocator);
                }
                String value = tokenizer.sval;
                result.put(name, value);
                tokenizer.nextToken();
            }
            return result;
        } catch (IOException e) {
            RDFParserException exception = new RDFParserException("I/O error",
                    m_documentLocator);
            exception.initCause(e);
            throw exception;
        }
    }

    public IRI getIRI(String s) {
        return uriCache.get(s);
    }

    /** Base class for all parser states. */
    protected static class State {
        public void startElement(String namespaceIRI, String localName, String qName,
                Attributes atts) throws SAXException {}

        public void endElement(String namespaceIRI, String localName, String qName)
                throws SAXException {}

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

    /** State expecting start of RDF text. */
    protected class StartRDF extends State {
        @Override
        public void startElement(String namespaceIRI, String localName, String qName,
                Attributes atts) throws SAXException {
            if (!RDFNS.equals(namespaceIRI) || !ELT_RDF.equals(localName)) {
                throw new RDFParserException("Expecting rdf:RDF element.",
                        m_documentLocator);
            }
            // the logical IRI is the current IRI that we have as the base IRI
            // at this point
            m_consumer.logicalURI(m_baseIRI.toString());
            pushState(new NodeElementList());
        }

        @Override
        public void endElement(String namespaceIRI, String localName, String qName)
                throws SAXException {
            popState();
        }

        @Override
        public void characters(char[] data, int start, int length) throws SAXException {
            if (!isWhitespaceOnly(data, start, length)) {
                throw new RDFParserException(
                        "Expecting rdf:rdf element instead of character content.",
                        m_documentLocator);
            }
        }
    }

    /** Parses the nodeElementList production. */
    protected class NodeElementList extends State {
        @Override
        public void startElement(String namespaceIRI, String localName, String qName,
                Attributes atts) throws SAXException {
            pushState(new NodeElement());
            m_state.startElement(namespaceIRI, localName, qName, atts);
        }

        @Override
        public void endElement(String namespaceIRI, String localName, String qName)
                throws SAXException {
            popState();
            m_state.endElement(namespaceIRI, localName, qName);
        }

        @Override
        public void characters(char[] data, int start, int length) throws SAXException {
            if (!isWhitespaceOnly(data, start, length)) {
                throw new RDFParserException(
                        "Expecting an object element instead of character content.",
                        m_documentLocator);
            }
        }
    }

    /** Parses the nodeElement production. */
    protected class NodeElement extends State {
        protected String m_subjectIRI;
        protected ReificationManager m_reificationManager;
        protected int m_nextLi = 1;

        public void startDummyElement(Attributes atts) throws SAXException {
            m_subjectIRI = NodeID.nextAnonymousIRI();
            m_reificationManager = getReificationManager(atts);
        }

        public String getSubjectIRI() {
            return m_subjectIRI;
        }

        public String getReificationID(Attributes atts) throws SAXException {
            String rdfID = atts.getValue(RDFNS, ATTR_ID);
            if (rdfID != null) {
                rdfID = getIRIFromID(rdfID);
            }
            return m_reificationManager.getReificationID(rdfID);
        }

        public String getNextLi() {
            return RDFNS + "_" + m_nextLi++;
        }

        public String getPropertyIRI(String uri) {
            if (RDF_LI.equals(uri)) {
                return getNextLi();
            } else {
                return uri;
            }
        }

        @Override
        public void startElement(String namespaceIRI, String localName, String qName,
                Attributes atts) throws SAXException {
            m_subjectIRI = getIDNodeIDAboutResourceIRI(atts);
            boolean isRDFNS = RDFNS.equals(namespaceIRI);
            m_reificationManager = getReificationManager(atts);
            if (!isRDFNS || !ELT_DESCRIPTION.equals(localName)) {
                statementWithResourceValue(m_subjectIRI, RDF_TYPE, namespaceIRI
                        + localName, m_reificationManager.getReificationID(null));
            }
            checkUnsupportedAttributes(atts);
            propertyAttributes(m_subjectIRI, atts, m_reificationManager);
            pushState(new PropertyElementList(this));
        }

        @Override
        public void endElement(String namespaceIRI, String localName, String qName)
                throws SAXException {
            popState();
        }

        @Override
        public void characters(char[] data, int start, int length) throws SAXException {
            if (!isWhitespaceOnly(data, start, length)) {
                throw new RDFParserException(
                        "Cannot answer characters when node is excepted.",
                        m_documentLocator);
            }
        }
    }

    /** Parses the propertyEltList production. The contents of the startElement
     * method implements also the propertyElt production. */
    protected class PropertyElementList extends State {
        protected NodeElement m_nodeElement;

        public PropertyElementList(NodeElement nodeElement) {
            m_nodeElement = nodeElement;
        }

        @Override
        public void startElement(String namespaceIRI, String localName, String qName,
                Attributes atts) throws SAXException {
            String parseType = atts.getValue(RDFNS, ATTR_PARSE_TYPE);
            if (PARSE_TYPE_LITERAL.equals(parseType)) {
                pushState(new ParseTypeLiteralPropertyElement(m_nodeElement));
            } else if (PARSE_TYPE_RESOURCE.equals(parseType)) {
                pushState(new ParseTypeResourcePropertyElement(m_nodeElement));
            } else if (PARSE_TYPE_COLLECTION.equals(parseType)) {
                pushState(new ParseTypeCollectionPropertyElement(m_nodeElement));
            } else if (parseType != null) {
                pushState(new ParseTypeLiteralPropertyElement(m_nodeElement));
            } else {
                String objectIRI = getNodeIDResourceResourceIRI(atts);
                if (objectIRI != null) {
                    pushState(new EmptyPropertyElement(m_nodeElement));
                } else {
                    pushState(new ResourceOrLiteralPropertyElement(m_nodeElement));
                }
            }
            m_state.startElement(namespaceIRI, localName, qName, atts);
        }

        @Override
        public void endElement(String namespaceIRI, String localName, String qName)
                throws SAXException {
            popState();
            m_state.endElement(namespaceIRI, localName, qName);
        }

        @Override
        public void characters(char[] data, int start, int length) throws SAXException {
            if (!isWhitespaceOnly(data, start, length)) {
                throw new RDFParserException(
                        "Cannot answer characters when object properties are excepted.",
                        m_documentLocator);
            }
        }
    }

    /** Parses resourcePropertyElt or literalPropertyElt productions. m_text is
     * null when startElement is expected on the actual property
     * element. */
    protected class ResourceOrLiteralPropertyElement extends State {
        protected NodeElement m_nodeElement;
        protected String m_propertyIRI;
        protected String m_reificationID;
        protected String m_datatype;
        protected StringBuilder m_text;
        protected NodeElement m_innerNode;

        public ResourceOrLiteralPropertyElement(NodeElement nodeElement) {
            m_nodeElement = nodeElement;
        }

        @Override
        public void startElement(String namespaceIRI, String localName, String qName,
                Attributes atts) throws SAXException {
            if (m_text == null) {
                // this is the invocation on the outer element
                m_propertyIRI = m_nodeElement.getPropertyIRI(namespaceIRI + localName);
                m_reificationID = m_nodeElement.getReificationID(atts);
                m_datatype = atts.getValue(RDFNS, ATTR_DATATYPE);
                m_text = new StringBuilder();
            } else {
                if (!isWhitespaceOnly(m_text)) {
                    throw new RDFParserException(
                            "Text was seen and new node is started.", m_documentLocator);
                }
                if (m_datatype != null) {
                    throw new RDFParserException(
                            "rdf:datatype specified on a node with resource value.",
                            m_documentLocator);
                }
                m_innerNode = new NodeElement();
                pushState(m_innerNode);
                m_state.startElement(namespaceIRI, localName, qName, atts);
            }
        }

        @Override
        public void endElement(String namespaceIRI, String localName, String qName)
                throws SAXException {
            if (m_innerNode != null) {
                statementWithResourceValue(m_nodeElement.getSubjectIRI(), m_propertyIRI,
                        m_innerNode.getSubjectIRI(), m_reificationID);
            } else {
                statementWithLiteralValue(m_nodeElement.getSubjectIRI(), m_propertyIRI,
                        m_text.toString(), m_datatype, m_reificationID);
            }
            popState();
        }

        @Override
        public void characters(char[] data, int start, int length) throws SAXException {
            if (m_innerNode != null) {
                if (!isWhitespaceOnly(data, start, length)) {
                    throw new RDFParserException(
                            "Cannot answer characters when object properties are excepted.",
                            m_documentLocator);
                }
            } else {
                m_text.append(data, start, length);
            }
        }
    }

    /** Parses emptyPropertyElt production. */
    protected class EmptyPropertyElement extends State {
        protected NodeElement m_nodeElement;
        protected String m_propertyIRI;

        public EmptyPropertyElement(NodeElement nodeElement) {
            m_nodeElement = nodeElement;
        }

        @Override
        public void startElement(String namespaceIRI, String localName, String qName,
                Attributes atts) throws SAXException {
            if (m_propertyIRI == null) {
                // this is the invocation on the outer element
                m_propertyIRI = m_nodeElement.getPropertyIRI(namespaceIRI + localName);
                String reificationID = m_nodeElement.getReificationID(atts);
                String objectIRI = getNodeIDResourceResourceIRI(atts);
                if (objectIRI == null) {
                    objectIRI = NodeID.nextAnonymousIRI();
                }
                statementWithResourceValue(m_nodeElement.getSubjectIRI(), m_propertyIRI,
                        objectIRI, reificationID);
                ReificationManager reificationManager = getReificationManager(atts);
                propertyAttributes(objectIRI, atts, reificationManager);
            } else {
                throw new RDFParserException("incorrect element start encountered.",
                        m_documentLocator);
            }
        }

        @Override
        public void endElement(String namespaceIRI, String localName, String qName)
                throws SAXException {
            popState();
        }

        @Override
        public void characters(char[] data, int start, int length) throws SAXException {
            throw new RDFParserException("Characters were not excepted.",
                    m_documentLocator);
        }
    }

    /** Parses parseTypeCollectionPropertyElt production. */
    protected class ParseTypeCollectionPropertyElement extends State {
        protected NodeElement m_nodeElement;
        protected String m_propertyIRI;
        protected String m_reificationID;
        protected String m_lastCellIRI;

        public ParseTypeCollectionPropertyElement(NodeElement nodeElement) {
            m_nodeElement = nodeElement;
        }

        @Override
        public void startElement(String namespaceIRI, String localName, String qName,
                Attributes atts) throws SAXException {
            if (m_propertyIRI == null) {
                m_propertyIRI = m_nodeElement.getPropertyIRI(namespaceIRI + localName);
                m_reificationID = m_nodeElement.getReificationID(atts);
            } else {
                NodeElement collectionNode = new NodeElement();
                pushState(collectionNode);
                m_state.startElement(namespaceIRI, localName, qName, atts);
                String newListCellIRI = listCell(collectionNode.getSubjectIRI());
                if (m_lastCellIRI == null) {
                    statementWithResourceValue(m_nodeElement.getSubjectIRI(),
                            m_propertyIRI, newListCellIRI, m_reificationID);
                } else {
                    statementWithResourceValue(m_lastCellIRI, RDF_REST, newListCellIRI,
                            null);
                }
                m_lastCellIRI = newListCellIRI;
            }
        }

        protected String listCell(String valueIRI) throws SAXException {
            String listCellIRI = NodeID.nextAnonymousIRI();
            statementWithResourceValue(listCellIRI, RDF_FIRST, valueIRI, null);
            statementWithResourceValue(listCellIRI, RDF_TYPE, RDF_LIST, null);
            return listCellIRI;
        }

        @Override
        public void endElement(String namespaceIRI, String localName, String qName)
                throws SAXException {
            if (m_lastCellIRI == null) {
                statementWithResourceValue(m_nodeElement.getSubjectIRI(), m_propertyIRI,
                        RDF_NIL, m_reificationID);
            } else {
                statementWithResourceValue(m_lastCellIRI, RDF_REST, RDF_NIL, null);
            }
            popState();
        }

        @Override
        public void characters(char[] data, int start, int length) throws SAXException {
            if (!isWhitespaceOnly(data, start, length)) {
                throw new RDFParserException(
                        "Expecting an object element instead of character content.",
                        m_documentLocator);
            }
        }
    }

    /** Parses parseTypeLiteralPropertyElt production. */
    protected class ParseTypeLiteralPropertyElement extends State {
        protected NodeElement m_nodeElement;
        protected String m_propertyIRI;
        protected String m_reificationID;
        protected int m_depth;
        protected StringBuilder m_content;

        public ParseTypeLiteralPropertyElement(NodeElement nodeElement) {
            m_nodeElement = nodeElement;
        }

        @Override
        public void startElement(String namespaceIRI, String localName, String qName,
                Attributes atts) throws SAXException {
            if (m_depth == 0) {
                m_propertyIRI = m_nodeElement.getPropertyIRI(namespaceIRI + localName);
                m_reificationID = m_nodeElement.getReificationID(atts);
                m_content = new StringBuilder();
            } else {
                m_content.append('<');
                m_content.append(qName);
                int length = atts.getLength();
                for (int i = 0; i < length; i++) {
                    m_content.append(' ');
                    m_content.append(atts.getQName(i));
                    m_content.append("=\"");
                    m_content.append(atts.getValue(i));
                    m_content.append("\"");
                }
                m_content.append(">");
            }
            m_depth++;
        }

        @Override
        public void endElement(String namespaceIRI, String localName, String qName)
                throws SAXException {
            if (m_depth == 1) {
                statementWithLiteralValue(m_nodeElement.getSubjectIRI(), m_propertyIRI,
                        m_content.toString(), RDF_XMLLITERAL, m_reificationID);
                popState();
            } else {
                m_content.append("");
            }
            m_depth--;
        }

        @Override
        public void characters(char[] data, int start, int length) {
            m_content.append(data, start, length);
        }
    }

    /** Parses parseTypeResourcePropertyElt production. */
    protected class ParseTypeResourcePropertyElement extends State {
        protected NodeElement m_nodeElement;
        protected String m_propertyIRI;
        protected String m_reificationID;

        public ParseTypeResourcePropertyElement(NodeElement nodeElement) {
            m_nodeElement = nodeElement;
        }

        @Override
        public void startElement(String namespaceIRI, String localName, String qName,
                Attributes atts) throws SAXException {
            m_propertyIRI = m_nodeElement.getPropertyIRI(namespaceIRI + localName);
            m_reificationID = m_nodeElement.getReificationID(atts);
            NodeElement anonymousNodeElement = new NodeElement();
            anonymousNodeElement.startDummyElement(atts);
            statementWithResourceValue(m_nodeElement.getSubjectIRI(), m_propertyIRI,
                    anonymousNodeElement.getSubjectIRI(), m_reificationID);
            pushState(new PropertyElementList(anonymousNodeElement));
        }

        @Override
        public void endElement(String namespaceIRI, String localName, String qName)
                throws SAXException {
            popState();
        }

        @Override
        public void characters(char[] data, int start, int length) throws SAXException {
            if (!isWhitespaceOnly(data, start, length)) {
                throw new RDFParserException(
                        "Cannot answer characters when object properties are excepted.",
                        m_documentLocator);
            }
        }
    }

    protected static class ReificationManager {
        public static final ReificationManager INSTANCE = new ReificationManager();

        public String getReificationID(String reificationID) throws SAXException {
            return reificationID;
        }
    }

    protected class ReifiedStatementBag extends ReificationManager {
        protected String m_uri;
        protected int m_elements;

        public ReifiedStatementBag(String uri) throws SAXException {
            m_uri = uri;
            m_elements = 0;
            statementWithResourceValue(m_uri, RDF_TYPE, RDF_BAG, null);
        }

        @Override
        public String getReificationID(String reificationID) throws SAXException {
            String resultIRI;
            if (reificationID == null) {
                resultIRI = NodeID.nextAnonymousIRI();
            } else {
                resultIRI = reificationID;
            }
            statementWithResourceValue(m_uri, RDFNS + "_" + ++m_elements, resultIRI, null);
            return resultIRI;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy