org.apache.marmotta.kiwi.sail.KiWiValueFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.marmotta.kiwi.sail.KiWiValueFactory.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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.
 */
package org.apache.marmotta.kiwi.sail;

import org.apache.marmotta.commons.sesame.model.LiteralCommons;
import org.apache.marmotta.commons.sesame.model.Namespaces;
import org.apache.marmotta.commons.sesame.tripletable.IntArray;
import org.apache.marmotta.kiwi.model.rdf.*;
import org.apache.marmotta.kiwi.persistence.KiWiConnection;
import org.apache.marmotta.kiwi.persistence.registry.CacheTripleRegistry;
import org.apache.marmotta.kiwi.persistence.registry.DBTripleRegistry;
import org.apache.marmotta.kiwi.persistence.registry.KiWiTripleRegistry;
import org.apache.marmotta.kiwi.persistence.registry.LocalTripleRegistry;
import org.joda.time.DateTime;
import org.joda.time.format.ISODateTimeFormat;
import org.openrdf.model.*;
import org.openrdf.model.impl.ContextStatementImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.datatype.XMLGregorianCalendar;
import java.sql.SQLException;
import java.util.*;

/**
 * Add file description here!
 * <p/>
 * Author: Sebastian Schaffert
 */
public class KiWiValueFactory implements ValueFactory {

    private static Logger log = LoggerFactory.getLogger(KiWiValueFactory.class);

    private Random anonIdGenerator;

    private KiWiStore store;

    private KiWiTripleRegistry registry;

    private String defaultContext;

    public KiWiValueFactory(KiWiStore store, String defaultContext) {
        anonIdGenerator = new Random();

        switch (store.getPersistence().getConfiguration().getRegistryStrategy()) {
        case DATABASE:
            log.info("KiWi Value Factory: database registry");
            registry = new DBTripleRegistry(store);
            break;
        case CACHE:
            log.info("KiWi Value Factory: cache registry");
            registry = new CacheTripleRegistry(store.getPersistence().getCacheManager());
            break;
        case LOCAL:
            log.info("KiWi Value Factory: in-memory registry");
            registry = new LocalTripleRegistry();
            break;
        default:
            log.info("KiWi Value Factory: in-memory registry");
            registry = new LocalTripleRegistry();
        }

        this.store = store;
        this.defaultContext = defaultContext;
    }

    protected KiWiConnection aqcuireConnection() {
        try {
            KiWiConnection connection = store.getPersistence().getConnection();
            connection.setAutoCommit(true);
            return connection;
        } catch (SQLException ex) {
            log.error("could not acquire database connection", ex);
            throw new RuntimeException(ex);
        }
    }

    protected void releaseConnection(KiWiConnection con) {
        try {
            con.close();
        } catch (SQLException ex) {
            log.error("could not release database connection", ex);
            throw new RuntimeException(ex);
        }
    }

    /**
     * Creates a new bNode.
     *
     * @return An object representing the bNode.
     */
    @Override
    public BNode createBNode() {
        return createBNode(
                Long.toHexString(System.currentTimeMillis()) + Integer.toHexString(anonIdGenerator.nextInt(1000)));
    }

    /**
     * Creates a new URI from the supplied string-representation.
     *
     * @param uri A string-representation of a URI.
     * @return An object representing the URI.
     */
    @Override
    public URI createURI(String uri) {
        KiWiConnection connection = aqcuireConnection();
        try {
            // first look in the registry for newly created resources if the resource has already been created and
            // is still volatile
            KiWiUriResource result = connection.loadUriResource(uri);

            if (result == null) {
                result = new KiWiUriResource(uri);

                connection.storeNode(result);

            }
            if (result.getId() < 0) {
                log.error("node ID is null!");
            }

            return result;
        } catch (SQLException e) {
            log.error("database error, could not load URI resource", e);
            throw new IllegalStateException("database error, could not load URI resource", e);
        } finally {
            releaseConnection(connection);
        }

    }

    /**
     * Creates a new URI from the supplied namespace and local name. Calling this
     * method is funtionally equivalent to calling
     * {@link #createURI(String) createURI(namespace+localName)}, but allows the
     * ValueFactory to reuse supplied namespace and local name strings whenever
     * possible. Note that the values returned by {@link org.openrdf.model.URI#getNamespace()} and
     * {@link org.openrdf.model.URI#getLocalName()} are not necessarily the same as the values that
     * are supplied to this method.
     *
     * @param namespace The URI's namespace.
     * @param localName The URI's local name.
     * @throws IllegalArgumentException If the supplied namespace and localname do not resolve to a legal
     *                                  (absolute) URI.
     */
    @Override
    public URI createURI(String namespace, String localName) {
        return createURI(namespace + localName);
    }

    /**
     * Creates a new blank node with the given node identifier.
     *
     * @param nodeID The blank node identifier.
     * @return An object representing the blank node.
     */
    @Override
    public BNode createBNode(String nodeID) {
        KiWiConnection connection = aqcuireConnection();
        try {
            // first look in the registry for newly created resources if the resource has already been created and
            // is still volatile
            KiWiAnonResource result = connection.loadAnonResource(nodeID);

            if (result == null) {
                result = new KiWiAnonResource(nodeID);

                connection.storeNode(result);
            }
            if (result.getId() < 0) {
                log.error("node ID is null!");
            }

            return result;
        } catch (SQLException e) {
            log.error("database error, could not load anonymous resource", e);
            throw new IllegalStateException("database error, could not load anonymous resource", e);
        } finally {
            releaseConnection(connection);
        }
    }

    /**
     * Creates a new literal representing the specified date that is typed using
     * the appropriate XML Schema date/time datatype.
     *
     * @since 2.7.0
     */
    @Override
    public Literal createLiteral(Date date) {
        return createLiteral(date, null, LiteralCommons.getXSDType(Date.class));
    }

    /**
     * Creates a typed {@link org.openrdf.model.Literal} out of the supplied object, mapping the
     * runtime type of the object to the appropriate XML Schema type. If no
     * mapping is available, the method returns a literal with the string
     * representation of the supplied object as the value, and
     * {@link org.openrdf.model.vocabulary.XMLSchema#STRING} as the datatype. Recognized types are
     * {@link Boolean}, {@link Byte}, {@link Double}, {@link Float},
     * {@link Integer}, {@link Long}, {@link Short}, {@link javax.xml.datatype.XMLGregorianCalendar }
     * , and {@link java.util.Date}.
     *
     * @param object an object to be converted to a typed literal.
     * @return a typed literal representation of the supplied object.
     * @since 2.7.0
     */
    public Literal createLiteral(Object object) {
        if (object instanceof XMLGregorianCalendar) {
            return createLiteral((XMLGregorianCalendar) object);
        } else {
            return createLiteral(object, null, LiteralCommons.getXSDType(object.getClass()));
        }
    }

    /**
     * Creates a new literal with the supplied label.
     *
     * @param label The literal's label.
     */
    @Override
    public Literal createLiteral(String label) {
        // FIXME: MARMOTTA-39 (no default datatype before RDF-1.1)
        // return createLiteral(label, null, LiteralCommons.getXSDType(String.class));
        return createLiteral(label, null, null);
    }

    /**
     * Creates a new literal with the supplied label and language attribute.
     *
     * @param label    The literal's label.
     * @param language The literal's language attribute, or <tt>null</tt> if the literal
     *                 doesn't have a language.
     */
    @Override
    public Literal createLiteral(String label, String language) {
        // FIXME: MARMOTTA-39 (no rdf:langString before RDF-1.1)
        // return createLiteral(label,language,LiteralCommons.getRDFLangStringType());
        return createLiteral(label, language, null);
    }

    /**
     * Creates a new literal with the supplied label and datatype.
     *
     * @param label    The literal's label.
     * @param datatype The literal's datatype, or <tt>null</tt> if the literal doesn't
     *                 have a datatype.
     */
    @Override
    public Literal createLiteral(String label, URI datatype) {
        return createLiteral(label, null, datatype.stringValue());
    }

    /**
     * Internal createLiteral method for different datatypes. This method distinguishes based on the Java class
     * type and the type argument passed as argument how to load and possibly create the new literal.
     *
     * @param value
     * @param lang
     * @param type
     * @param <T>
     * @return
     */
    private <T> KiWiLiteral createLiteral(T value, String lang, String type) {
        Locale locale;
        if (lang != null) {
            try {
                Locale.Builder builder = new Locale.Builder();
                builder.setLanguageTag(lang);
                locale = builder.build();
            } catch (IllformedLocaleException ex) {
                log.warn("malformed language literal (language: {})", lang);
                locale = null;
                lang = null;
            }
        } else {
            locale = null;
        }

        KiWiLiteral result;

        final KiWiUriResource rtype = type == null ? null : (KiWiUriResource) createURI(type);

        final KiWiConnection connection = aqcuireConnection();
        try {

            try {
                // differentiate between the different types of the value
                if (type == null) {
                    // FIXME: MARMOTTA-39 (this is to avoid a NullPointerException in the following if-clauses)
                    result = connection.loadLiteral(value.toString(), lang, rtype);

                    if (result == null) {
                        result = new KiWiStringLiteral(value.toString(), locale, rtype);
                    }
                } else if (value instanceof Date || value instanceof DateTime
                        || type.equals(Namespaces.NS_XSD + "dateTime") || type.equals(Namespaces.NS_XSD + "date")
                        || type.equals(Namespaces.NS_XSD + "time")) {
                    // parse if necessary
                    final DateTime dvalue;
                    if (value instanceof DateTime) {
                        dvalue = ((DateTime) value).withMillisOfDay(0);
                    } else if (value instanceof Date || value instanceof Calendar) {
                        dvalue = new DateTime(value);
                    } else {
                        dvalue = ISODateTimeFormat.dateTimeParser().withOffsetParsed()
                                .parseDateTime(value.toString()).withMillisOfSecond(0);
                    }

                    result = connection.loadLiteral(dvalue);

                    if (result == null) {
                        result = new KiWiDateLiteral(dvalue, rtype);
                    }
                } else if (Integer.class.equals(value.getClass()) || int.class.equals(value.getClass())
                        || Long.class.equals(value.getClass()) || long.class.equals(value.getClass())
                        || type.equals(Namespaces.NS_XSD + "integer") || type.equals(Namespaces.NS_XSD + "long")) {
                    long ivalue = 0;
                    if (Integer.class.equals(value.getClass()) || int.class.equals(value.getClass())) {
                        ivalue = (Integer) value;
                    } else if (Long.class.equals(value.getClass()) || long.class.equals(value.getClass())) {
                        ivalue = (Long) value;
                    } else {
                        ivalue = Long.parseLong(value.toString());
                    }

                    result = connection.loadLiteral(ivalue);

                    if (result == null) {
                        result = new KiWiIntLiteral(ivalue, rtype);
                    }
                } else if (Double.class.equals(value.getClass()) || double.class.equals(value.getClass())
                        || Float.class.equals(value.getClass()) || float.class.equals(value.getClass())
                        || type.equals(Namespaces.NS_XSD + "double") || type.equals(Namespaces.NS_XSD + "float")
                        || type.equals(Namespaces.NS_XSD + "decimal")) {
                    double dvalue = 0.0;
                    if (Float.class.equals(value.getClass()) || float.class.equals(value.getClass())) {
                        dvalue = (Float) value;
                    } else if (Double.class.equals(value.getClass()) || double.class.equals(value.getClass())) {
                        dvalue = (Double) value;
                    } else {
                        dvalue = Double.parseDouble(value.toString());
                    }

                    result = connection.loadLiteral(dvalue);

                    if (result == null) {
                        result = new KiWiDoubleLiteral(dvalue, rtype);
                    }
                } else if (Boolean.class.equals(value.getClass()) || boolean.class.equals(value.getClass())
                        || type.equals(Namespaces.NS_XSD + "boolean")) {
                    boolean bvalue = false;
                    if (Boolean.class.equals(value.getClass()) || boolean.class.equals(value.getClass())) {
                        bvalue = (Boolean) value;
                    } else {
                        bvalue = Boolean.parseBoolean(value.toString());
                    }

                    result = connection.loadLiteral(bvalue);

                    if (result == null) {
                        result = new KiWiBooleanLiteral(bvalue, rtype);
                    }
                } else {
                    result = connection.loadLiteral(value.toString(), lang, rtype);

                    if (result == null) {
                        result = new KiWiStringLiteral(value.toString(), locale, rtype);
                    }
                }
            } catch (IllegalArgumentException ex) {
                // malformed number or date
                log.warn("malformed argument for typed literal of type {}: {}", rtype.stringValue(), value);
                KiWiUriResource mytype = (KiWiUriResource) createURI(Namespaces.NS_XSD + "string");

                result = connection.loadLiteral(value.toString(), lang, mytype);

                if (result == null) {
                    result = new KiWiStringLiteral(value.toString(), locale, mytype);
                }

            }

            if (result.getId() < 0) {
                connection.storeNode(result);
            }

            return result;

        } catch (SQLException e) {
            log.error("database error, could not load literal", e);
            throw new IllegalStateException("database error, could not load literal", e);
        } finally {
            releaseConnection(connection);
        }
    }

    /**
     * Creates a new <tt>xsd:boolean</tt>-typed literal representing the
     * specified value.
     *
     * @param value The value for the literal.
     * @return An <tt>xsd:boolean</tt>-typed literal for the specified value.
     */
    @Override
    public Literal createLiteral(boolean value) {
        return createLiteral(value, null, LiteralCommons.getXSDType(Boolean.class));
    }

    /**
     * Creates a new <tt>xsd:byte</tt>-typed literal representing the
     * specified value.
     *
     * @param value The value for the literal.
     * @return An <tt>xsd:byte</tt>-typed literal for the specified value.
     */
    @Override
    public Literal createLiteral(byte value) {
        return createLiteral((int) value, null, LiteralCommons.getXSDType(Byte.class));
    }

    /**
     * Creates a new <tt>xsd:short</tt>-typed literal representing the
     * specified value.
     *
     * @param value The value for the literal.
     * @return An <tt>xsd:short</tt>-typed literal for the specified value.
     */
    @Override
    public Literal createLiteral(short value) {
        return createLiteral((int) value, null, LiteralCommons.getXSDType(Short.class));
    }

    /**
     * Creates a new <tt>xsd:int</tt>-typed literal representing the specified
     * value.
     *
     * @param value The value for the literal.
     * @return An <tt>xsd:int</tt>-typed literal for the specified value.
     */
    @Override
    public Literal createLiteral(int value) {
        return createLiteral(value, null, LiteralCommons.getXSDType(Integer.class));
    }

    /**
     * Creates a new <tt>xsd:long</tt>-typed literal representing the
     * specified value.
     *
     * @param value The value for the literal.
     * @return An <tt>xsd:long</tt>-typed literal for the specified value.
     */
    @Override
    public Literal createLiteral(long value) {
        return createLiteral(value, null, LiteralCommons.getXSDType(Long.class));
    }

    /**
     * Creates a new <tt>xsd:float</tt>-typed literal representing the
     * specified value.
     *
     * @param value The value for the literal.
     * @return An <tt>xsd:float</tt>-typed literal for the specified value.
     */
    @Override
    public Literal createLiteral(float value) {
        return createLiteral(value, null, LiteralCommons.getXSDType(Float.class));
    }

    /**
     * Creates a new <tt>xsd:double</tt>-typed literal representing the
     * specified value.
     *
     * @param value The value for the literal.
     * @return An <tt>xsd:double</tt>-typed literal for the specified value.
     */
    @Override
    public Literal createLiteral(double value) {
        return createLiteral(value, null, LiteralCommons.getXSDType(Double.class));
    }

    /**
     * Creates a new literal representing the specified calendar that is typed
     * using the appropriate XML Schema date/time datatype.
     *
     * @param calendar The value for the literal.
     * @return An typed literal for the specified calendar.
     */
    @Override
    public Literal createLiteral(XMLGregorianCalendar calendar) {
        Date value = calendar.toGregorianCalendar().getTime();

        return createLiteral(value, null, LiteralCommons.getXSDType(Date.class));
    }

    /**
     * Creates a new statement with the supplied subject, predicate and object.
     *
     * @param subject   The statement's subject.
     * @param predicate The statement's predicate.
     * @param object    The statement's object.
     * @return The created statement.
     */
    @Override
    public Statement createStatement(Resource subject, URI predicate, Value object) {
        if (defaultContext != null) {
            return createStatement(subject, predicate, object, createURI(defaultContext));
        } else {
            return createStatement(subject, predicate, object, null);
        }
    }

    /**
     * Creates a new statement with the supplied subject, predicate and object
     * and associated context.
     *
     * @param subject   The statement's subject.
     * @param predicate The statement's predicate.
     * @param object    The statement's object.
     * @param context   The statement's context.
     * @return The created statement.
     */
    @Override
    public Statement createStatement(Resource subject, URI predicate, Value object, Resource context) {
        return new ContextStatementImpl(subject, predicate, object, context);
    }

    /**
     * Creates a new statement with the supplied subject, predicate and object and associated context. This is a
     * specialised form of createStatement that allows the existance check for a triple to run in the same connection
     * as the rest of the repository operations.
     *
     * @param subject   The statement's subject.
     * @param predicate The statement's predicate.
     * @param object    The statement's object.
     * @param context   The statement's context.
     * @return The created statement.
     */
    public Statement createStatement(Resource subject, URI predicate, Value object, Resource context,
            KiWiConnection connection) {
        try {

            IntArray cacheKey = IntArray.createSPOCKey(subject, predicate, object, context);

            KiWiResource ksubject = convert(subject);
            KiWiUriResource kpredicate = convert(predicate);
            KiWiNode kobject = convert(object);
            KiWiResource kcontext = convert(context);

            KiWiTriple result = new KiWiTriple(ksubject, kpredicate, kobject, kcontext);

            synchronized (registry) {
                long tripleId = registry.lookupKey(cacheKey);

                if (tripleId >= 0) {
                    // try getting id from registry
                    result.setId(tripleId);

                    registry.registerKey(cacheKey, connection.getTransactionId(), result.getId());
                } else {
                    // not found in registry, try loading from database
                    result.setId(connection.getTripleId(ksubject, kpredicate, kobject, kcontext));
                }

                // triple has no id from registry or database, so we create one and flag it for reasoning
                if (result.getId() < 0) {
                    result.setId(connection.getNextSequence());
                    result.setNewTriple(true);

                    registry.registerKey(cacheKey, connection.getTransactionId(), result.getId());
                }
            }

            return result;

        } catch (SQLException e) {
            log.error("database error, could not load triple", e);
            throw new IllegalStateException("database error, could not load triple", e);
        }
    }

    /**
     * Remove a statement from the triple registry. Called when the statement is deleted and the transaction commits.
     * @param triple
     */
    protected void removeStatement(KiWiTriple triple) {
        if (triple.getId() >= 0) {
            IntArray cacheKey = IntArray.createSPOCKey(triple.getSubject(), triple.getPredicate(),
                    triple.getObject(), triple.getContext());

            synchronized (registry) {
                registry.deleteKey(cacheKey);
            }
        }
        triple.setDeleted(true);
    }

    protected void releaseRegistry(KiWiConnection connection) {
        registry.releaseTransaction(connection.getTransactionId());
    }

    public KiWiResource convert(Resource r) {
        return (KiWiResource) convert((Value) r);
    }

    public KiWiUriResource convert(URI r) {
        return (KiWiUriResource) convert((Value) r);
    }

    public KiWiNode convert(Value value) {
        if (value == null) {
            return null;
        } else if (value instanceof KiWiNode) {
            return (KiWiNode) value;
        } else if (value instanceof URI) {
            return (KiWiUriResource) createURI(value.stringValue());
        } else if (value instanceof BNode) {
            return (KiWiAnonResource) createBNode(value.stringValue());
        } else if (value instanceof Literal) {
            Literal l = (Literal) value;
            return createLiteral(l.getLabel(), l.getLanguage(),
                    l.getDatatype() != null ? l.getDatatype().stringValue() : null);
        } else {
            throw new IllegalArgumentException("the value passed as argument does not have the correct type");
        }

    }

    public void close() {

    }

}