/*******************************************************************************
* Copyright (c) 2004, 2007 IBM Corporation and Cambridge Semantics Incorporated.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* File: $Source: /cvsroot/slrp/boca/com.ibm.adtech.boca.core/src/com/ibm/adtech/boca/utils/StatementUtils.java,v $
* Created by: Matthew Roy ( <a href="mailto:mroy@us.ibm.com">mroy@us.ibm.com </a>)
* Created on: Dec 19, 2006
* Revision: $Id: StatementUtils.java 168 2007-07-31 14:11:14Z mroy $
*
* Contributors:
* IBM Corporation - initial API and implementation
* Cambridge Semantics Incorporated - Fork to Anzo
*******************************************************************************/
package org.openanzo.rdf.utils;
import info.aduna.collections.iterators.Iterators;
import java.util.ArrayList;
import java.util.Collection;
import javax.xml.datatype.DatatypeConstants;
import javax.xml.datatype.XMLGregorianCalendar;
import org.apache.commons.collections15.MultiMap;
import org.apache.commons.collections15.multimap.MultiHashMap;
import org.openanzo.exceptions.AnzoException;
import org.openanzo.exceptions.ExceptionConstants;
import org.openanzo.exceptions.LogUtils;
import org.openanzo.rdf.Constants;
import org.openanzo.rdf.IDataset;
import org.openanzo.rdf.INamedGraph;
import org.openanzo.rdf.Literal;
import org.openanzo.rdf.PlainLiteral;
import org.openanzo.rdf.Resource;
import org.openanzo.rdf.Statement;
import org.openanzo.rdf.TypedLiteral;
import org.openanzo.rdf.URI;
import org.openanzo.rdf.Value;
import org.openanzo.rdf.query.QueryEncoder;
import org.openanzo.rdf.vocabulary.RDF;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Basic utilities for Statements and Literal
*
* @author Matthew Roy ( <a href="mailto:mroy@cambridgesemantics.com">mroy@cambridgesemantics.com </a>)
*
*/
public class StatementUtils {
private static final Logger log = LoggerFactory.getLogger(StatementUtils.class);
private static final String containerItemPrefix = RDF.NAMESPACE + "_";
/**
* Determine if given statement matches given subject,prop,obj,namedgraph pattern
*
* @param statement
* Statement to compare against match parameters
* @param subj
* Subject to match, or null for any
* @param prop
* Predicate to match, or null for any
* @param obj
* Object to match, or null for any
* @param context
* NamedGraph to match, or null for any
* @return true if statement provided matches the 4 parameters passed
*/
public static boolean match(Statement statement, Resource subj, org.openanzo.rdf.URI prop, Value obj, org.openanzo.rdf.URI context) {
if (subj == null && prop == null && obj == null && context == null) {
return true;
} else {
if (subj != null && !subj.equals(Constants.ANY_URI) && !subj.equals(statement.getSubject())) {
return false;
}
if (prop != null && !prop.equals(Constants.ANY_URI) && !prop.equals(statement.getPredicate())) {
return false;
}
if (obj != null && !obj.equals(Constants.ANY_URI) && !obj.equals(statement.getObject())) {
return false;
}
if (context != null && !context.equals(Constants.ANY_URI) && !context.equals(statement.getNamedGraphUri())) {
return false;
}
return true;
}
}
/**
* Try to convert Literal to native object based on datatype
*
* @param literal
* Literal to convert
* @return native value of object based on datatype, or a string containing the literal's label if no conversion exists.
*/
static public Object getNativeValue(Literal literal) {
if (literal instanceof PlainLiteral) {
PlainLiteral pl = (PlainLiteral) literal;
return pl.getLabel();
} else {
TypedLiteral pl = (TypedLiteral) literal;
return pl.getNativeValue();
}
}
/**
* Convert an xsd:dateTime Literal to its long representation. Note that xsd:dateTime allows the time zone to be omitted. However, to convert a literal to a
* millisecond value, requires a time zone. That's because the millisecond long value is supposed to represent an absolute time. that is, milliseconds since
* January 1, 1970, 00:00:00 GMT. Without a time zone in the xsd:dateTime literal, that number cannot be derived.
*
* Note that this may cause information loss. xsd:dateTime support arbitrary precision fractional seconds. This method reduces the precision to
* milliseconds.
*
* @param dateTime
* xsd:dateTime Literal to convert
* @return long representation of literal. null if the literal couldn't be parsed into a long such as for lack of a time zone being specified in the
* literal.
*/
protected static Long convertToMilliseconds(TypedLiteral dateTime) {
Long ret = null;
Object nativeValue = dateTime.getNativeValue();
if (nativeValue instanceof XMLGregorianCalendar) {
XMLGregorianCalendar xmlCal = (XMLGregorianCalendar) nativeValue;
try {
if (xmlCal.getXMLSchemaType().equals(DatatypeConstants.DATETIME) && xmlCal.getTimezone() != DatatypeConstants.FIELD_UNDEFINED) {
ret = xmlCal.toGregorianCalendar().getTimeInMillis();
}
} catch (IllegalStateException e) {
// XMLGregorianCalendar#getXMLSchemaType() will throw an IllegalStateException if the XMLGregorianCalendar is invalid.
log.debug(LogUtils.GLITTER_MARKER, "Error parsing dateTime literal into millisecond representation: {}", dateTime.toString());
}
}
return ret;
}
/**
* Convert {@link Constants#ANY_URI} to a null
*
* @param node
* Value to convert to null if Constants.ANY_URI
* @return value or null if equals {@link Constants#ANY_URI}
*/
public static Value convertUriToAny(Value node) {
if (node != null && node.equals(Constants.ANY_URI)) {
return null;
}
return node;
}
/**
* Build a string representing this find operation
*
* @param subject
* @param property
* @param object
* @param namedGraphUri
* @return a string representing this find operation
*/
public static String buildQueryString(Resource subject, org.openanzo.rdf.URI property, Value object, URI... namedGraphUri) {
String subjectNode = createQueryNodeString(subject, "subject");
String predicateNode = createQueryNodeString(property, "predicate");
String objectNode = createQueryNodeString(object, "object");
StringBuilder query = new StringBuilder("SELECT ");
query.append("?graph ");
if (subjectNode.startsWith("?")) {
query.append(subjectNode + " ");
}
if (predicateNode.startsWith("?")) {
query.append(predicateNode + " ");
}
if (objectNode.startsWith("?")) {
query.append(objectNode + " ");
}
if (namedGraphUri != null) {
for (URI uri : namedGraphUri) {
query.append(" FROM NAMED " + QueryEncoder.encodeForQuery(uri));
}
}
query.append(" WHERE { GRAPH ");
query.append(" ?graph ");
query.append(" {");
query.append(subjectNode);
query.append(" ");
query.append(predicateNode);
query.append(" ");
query.append(objectNode);
query.append(" }}");
return query.toString();
}
private static String createQueryNodeString(Value value, String name) {
if (value == null || value.equals(Constants.ANY_URI)) {
return "?" + name + " ";
}
return QueryEncoder.encodeForQuery(value);
}
/**
* Get an Collection for the members of the RDFContainer construct provided
*
* @param container
* Resource of RDFContainer object
* @param graph
* Graph containing data
* @return Collection containing data contained within RDFContainer
* @throws AnzoException
*/
public static Collection<Value> getContainerMembers(Resource container, INamedGraph graph) throws AnzoException {
ArrayList<Value> statements = new ArrayList<Value>();
if (graph.contains(container, RDF.TYPE, RDF.ALT) || graph.contains(container, RDF.TYPE, RDF.Bag) || graph.contains(container, RDF.TYPE, RDF.Seq)) {
Collection<Statement> containedStatements = graph.find(container, null, null);
for (Statement stmt : containedStatements) {
String predicate = stmt.getPredicate().toString();
if (predicate.startsWith(containerItemPrefix)) {
String val = predicate.substring(containerItemPrefix.length());
try {
int li = Integer.parseInt(val);
statements.add(li, stmt.getObject());
} catch (NumberFormatException e) {
throw new AnzoException(ExceptionConstants.IO.USER_ENCODE_ERROR, e);
}
}
}
}
return statements;
}
/**
* Get an Collection for the members of the RDFContainer construct provided
*
* @param container
* Resource of RDFContainer object
* @param dataset
* Dataset containing data
* @return Collection containing data contained within RDFContainer
* @throws AnzoException
*/
public static Collection<Statement> getContainerMembers(Resource container, IDataset dataset) throws AnzoException { // NO_UCD
ArrayList<Statement> statements = new ArrayList<Statement>();
if (dataset.contains(container, RDF.TYPE, RDF.ALT) || dataset.contains(container, RDF.TYPE, RDF.Bag) || dataset.contains(container, RDF.TYPE, RDF.Seq)) {
Collection<Statement> containedStatements = dataset.find(container, null, null);
for (Statement stmt : containedStatements) {
String predicate = stmt.getPredicate().toString();
if (predicate.startsWith(containerItemPrefix)) {
String val = predicate.substring(containerItemPrefix.length());
try {
int li = Integer.parseInt(val);
statements.add(li, stmt);
} catch (NumberFormatException e) {
throw new AnzoException(ExceptionConstants.IO.USER_ENCODE_ERROR, e);
}
}
}
}
return statements;
}
/**
* Get an Collection for the members of the RDFList construct provided
*
* @param list
* Resource of RDFList object
* @param graph
* Graph containing data
* @return Collection containing data contained within RDFContainer
*/
public static Collection<Value> getCollectionMembers(Resource list, INamedGraph graph) {
ArrayList<Value> statements = new ArrayList<Value>();
if (graph.contains(list, RDF.first, null) && graph.contains(list, RDF.rest, null)) {
Collection<Statement> firstStatements = graph.find(list, RDF.first, null);
if (!firstStatements.isEmpty()) {
Statement first = firstStatements.iterator().next();
if (!statements.contains(first.getObject())) {
statements.add(first.getObject());
}
}
Collection<Statement> restStatements = graph.find(list, RDF.rest, null);
if (!restStatements.isEmpty()) {
Statement rest = restStatements.iterator().next();
if (!statements.contains(rest.getObject()) && rest.getObject() instanceof Resource) {
Iterators.addAll(getCollectionMembers((Resource) rest.getObject(), graph).iterator(), statements);
}
}
}
return statements;
}
/**
* Get an Collection for the members of the RDFList construct provided
*
* @param list
* Resource of RDFList object
* @param dataset
* Dataset containing data
* @return Collection containing data contained within RDFContainer
*/
public static MultiMap<URI, Value> getCollectionMembers(Resource list, IDataset dataset) { // NO_UCD
MultiHashMap<URI, Value> statements = new MultiHashMap<URI, Value>();
if (dataset.contains(list, RDF.first, null) && dataset.contains(list, RDF.rest, null)) {
Collection<Statement> firstStatements = dataset.find(list, RDF.first, null);
if (!firstStatements.isEmpty()) {
Statement first = firstStatements.iterator().next();
if (!statements.containsValue(first.getNamedGraphUri(), first.getObject())) {
statements.put(first.getNamedGraphUri(), first.getObject());
}
}
Collection<Statement> restStatements = dataset.find(list, RDF.rest, null);
if (!restStatements.isEmpty()) {
Statement rest = restStatements.iterator().next();
if (!statements.containsValue(rest.getNamedGraphUri(), rest.getObject()) && rest.getObject() instanceof Resource) {
statements.putAll(getCollectionMembers((Resource) rest.getObject(), dataset));
}
}
}
return statements;
}
}