/*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is the Kowari Metadata Store.
*
* The Initial Developer of the Original Code is Plugged In Software Pty
* Ltd (http://www.pisoftware.com, mailto:info@pisoftware.com). Portions
* created by Plugged In Software Pty Ltd are Copyright (C) 2001,2002
* Plugged In Software Pty Ltd. All Rights Reserved.
*
* Contributor(s): N/A.
*
* [NOTE: The text of this Exhibit A may differ slightly from the text
* of the notices in the Source Code files of the Original Code. You
* should use the text of this Exhibit A rather than the text found in the
* Original Code Source Code for Your Modifications.]
*
*/
package org.mulgara.resolver.gis;
//Java 2 standard packages
// Log4J
import org.apache.log4j.Logger;
//local packages
import java.net.*;
import org.mulgara.content.*;
import org.mulgara.query.*;
import org.mulgara.query.rdf.*;
import org.mulgara.resolver.spi.*;
import org.mulgara.store.statement.*;
import org.mulgara.store.tuples.*;
import org.mulgara.util.*;
import java.util.*;
/**
* Used for estimating the difference between two points. Infers statements that
* are inserted.
*
* @created 2004-11-17
*
* @author <a href="mailto:robert.turner@tucanatech.com">Robert Turner</a>
*
* @version $Revision: 1.6 $
*
* @modified $Date: 2005/01/05 04:58:29 $
*
* @maintenanceAuthor $Author: newmana $
*
* @company <A href="mailto:info@PIsoftware.com">Plugged In Software</A>
*
* @copyright ©2001 <a href="http://www.pisoftware.com/">Plugged In
* Software Pty Ltd</a>
*
* @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a>
*/
public class WritableGISResolver extends ReadOnlyGISResolver {
/** Logger. */
private final static Logger log = Logger.getLogger(WritableGISResolver.class.getName());
/** Model type */
private static final URI MODEL_TYPE = URI.create(Mulgara.NAMESPACE + "Model");
/**
* Constructor.
*
* @param resolverSession ResolverSession
* @param systemResolver Resolver
* @param contentManager ContentHandlerManager
* @param rdfType long
* @param systemModel long
* @throws ResolverFactoryException
*/
WritableGISResolver(ResolverSession resolverSession, Resolver resolver,
Resolver systemResolver,
ContentHandlerManager contentManager) throws ResolverFactoryException {
super(resolverSession, resolver, systemResolver, contentManager);
}
/**
* {@inheritDoc}
*
* @param model long
* @param statements Statements
* @param occurs boolean Whether to insert (true) or delete (false)
* @throws ResolverException
*/
public void modifyModel(long model, Statements statements,
boolean occurs) throws ResolverException {
//insert/delete statements and inferred statements
if (occurs) {
try {
insertStatements(model, statements);
} catch (Exception exception) {
throw new ResolverException("Failed to insert statements.", exception);
}
} else {
throw new ResolverException("Delete not implemented");
}
}
/**
* Inserts the statements into the model. Also generates and inserts inferred
* statements into the model.
*
* @param model long
* @param modifyStatements Statements
* @throws ResolverException
* @throws TuplesException
*/
private void insertStatements(long model,
Statements modifyStatements) throws ResolverException, TuplesException {
long tempModel = -1;
Resolution xPoints = null;
Resolution xLats = null;
Resolution xLongs = null;
Resolution yPoints = null;
Resolution yLats = null;
Resolution yLongs = null;
Statements inferredStatements = null;
try {
//insert modify statements into a temp model and get all points
tempModel = createTemporaryModel();
super.resolver.modifyModel(tempModel, modifyStatements, true);
Variable x = GISDistanceStatements.X_VAR;
Variable xlat = GISDistanceStatements.XLAT_VAR;
Variable xlong = GISDistanceStatements.XLONG_VAR;
xPoints = getPointTypes(x, tempModel);
xLats = getPointLatitudes(x, xlat, tempModel);
xLongs = getPointLongitudes(x, xlong, tempModel);
// insert modify statements into the store and get all points
super.resolver.modifyModel(model, modifyStatements, true);
Variable y = GISDistanceStatements.Y_VAR;
Variable ylat = GISDistanceStatements.YLAT_VAR;
Variable ylong = GISDistanceStatements.YLONG_VAR;
yPoints = getPointTypes(y, model);
yLats = getPointLatitudes(y, ylat, model);
yLongs = getPointLongitudes(y, ylong, model);
//join all points (generate inferred statements)
List<Resolution> tupleList = new ArrayList<Resolution>();
tupleList.add(xPoints);
tupleList.add(xLats);
tupleList.add(xLongs);
tupleList.add(yPoints);
tupleList.add(yLats);
tupleList.add(yLongs);
Tuples points = TuplesOperations.join(tupleList);
inferredStatements = getInferredStatements(points);
//delete all "duplicate" inferred statements before inserting
deleteDuplicates(model, modifyStatements);
//insert the inferred statements
super.resolver.modifyModel(model, inferredStatements, true);
} finally {
//clean up
if (tempModel != -1) {
dropTemporaryModel(tempModel);
}
close(xPoints);
close(xLats);
close(xLongs);
close(yPoints);
close(yLats);
close(yLongs);
}
}
/**
* Deletes any duplicate inferred statements from the model.
*
* @param model long
* @param inferred Statements
* @throws ResolverException
* @throws TuplesException
* @throws ResolverException
*/
private void deleteDuplicates(long model,
Statements modified) throws ResolverException, TuplesException,
ResolverException {
long tempModel = -1;
Resolution xPoints = null;
Resolution yPoints = null;
Tuples points = null;
Tuples distTypes = null;
Tuples distanceTypes = null;
Tuples projectedDistTypes = null;
Tuples dist = null;
Tuples distances = null;
Tuples all = null;
Statements distTypeStatements = null;
Statements distStatements = null;
Statements distStatements2 = null;
try {
//insert modify statements into a temp model
tempModel = createTemporaryModel();
super.resolver.modifyModel(tempModel, modified, true);
//get all point combinations - cartesian product ($x $y)
Variable x = GISDistanceStatements.X_VAR;
Variable y = GISDistanceStatements.Y_VAR;
xPoints = getPointTypes(x, tempModel);
yPoints = getPointTypes(y, model);
points = TuplesOperations.join(xPoints, yPoints);
//get all distance statements from the existing model ($x $d $y)
Variable d = new Variable("d");
dist = getDistancesStatements(d, model);
all = getAllStatements(x, d, y, model);
distances = TuplesOperations.join(Arrays.asList(new Tuples []{
all, dist, points
}));
if (!isEmpty(distances)) {
//must also delete inverted statements
distStatements = new TuplesWrapperStatements((Tuples) distances.clone(), x, d, y);
distStatements2 = new TuplesWrapperStatements((Tuples) distances.clone(), y, d, x);
}
//get all statements for each of the distances ($d $p $o)
distTypes = getDistances(d, model);
distanceTypes = TuplesOperations.join(distances, distTypes);
//only want $d $p $o
Variable[] vars = new Variable[] {
d,
StatementStore.VARIABLES[1],
StatementStore.VARIABLES[2]
};
projectedDistTypes = TuplesOperations.project(distanceTypes, Arrays.asList(vars), true);
if (!isEmpty(distanceTypes)) {
distTypeStatements = new TuplesWrapperStatements((Tuples)distanceTypes.clone(), d, vars[1], vars[2]);
}
//delete the statements
if (!isEmpty(distStatements)) {
super.resolver.modifyModel(model, distStatements, false);
}
if (!isEmpty(distStatements)) {
super.resolver.modifyModel(model, distStatements2, false);
}
if (!isEmpty(distTypeStatements)) {
super.resolver.modifyModel(model, distTypeStatements, false);
}
} finally {
//clean up
if (tempModel != -1) dropTemporaryModel(tempModel);
close(xPoints);
close(yPoints);
close(points);
close(distTypes);
close(distanceTypes);
close(projectedDistTypes);
close(dist);
close(distances);
close(all);
}
}
/**
* Returns true if the Cursor are empty or null.
*
* @param cursor Cursor
* @return boolean
* @throws TuplesException
*/
private boolean isEmpty(Cursor cursor) throws TuplesException {
return (cursor == null) || cursor.isEmpty() || (cursor.getRowCardinality() == Cursor.ZERO);
}
/**
* Closes the cursor if it is not null.
* @param cursor Cursor
* @throws TuplesException
*/
private void close(Cursor cursor) throws TuplesException {
if (cursor != null) {
cursor.close();
}
}
/**
* Returns all Statements of the form: $x $d $y ($d is type Distance)
*
* @param model long
* @throws ResolverException
* @throws TuplesException
* @return Tuples
*/
private Tuples getDistancesStatements(Variable distVar,
long model) throws ResolverException, TuplesException {
Resolution xPoints = null;
Resolution dist = null;
Resolution yPoints = null;
try {
//delete all statements: $x $d $y
Variable x = GISDistanceStatements.X_VAR;
Variable y = GISDistanceStatements.Y_VAR;
xPoints = getPointTypes(x, model);
yPoints = getPointTypes(y, model);
dist = getDistanceTypes(distVar, model);
//join all points (generate all point combinations)
List<Resolution> tupleList = new ArrayList<Resolution>();
tupleList.add(xPoints);
tupleList.add(dist);
tupleList.add(yPoints);
return TuplesOperations.join(tupleList);
} finally {
//clean up
if (xPoints != null) xPoints.close();
if (dist != null) dist.close();
if (yPoints != null) yPoints.close();
}
}
/**
* Returns all Statements for all Distances
*
* @param model long
* @throws ResolverException
* @throws TuplesException
* @return Tuples
*/
private Tuples getDistances(Variable distVar,
long model) throws ResolverException, TuplesException {
Resolution all = null;
Resolution dist = null;
try {
//delete all statements: $d $p $o
Variable p = StatementStore.VARIABLES[1];
Variable o = StatementStore.VARIABLES[2];
dist = getDistanceTypes(distVar, model);
all = getAllStatements(distVar, p, o, model);
//join all statements for each Distance
List<Resolution> tupleList = new ArrayList<Resolution>();
tupleList.add(all);
tupleList.add(dist);
return TuplesOperations.join(tupleList);
} finally {
//clean up
if (all != null) all.close();
if (dist != null) dist.close();
}
}
/**
* Returns all statements from the model using the given variables.
*
* @param s Variable
* @param p Variable
* @param o Variable
* @param model long
* @throws ResolverException
* @return Resolution
*/
private Resolution getAllStatements(Variable s, Variable p, Variable o,
long model) throws ResolverException {
try {
LocalNode modelNode = new LocalNode(model);
Constraint constraint = new ConstraintImpl(s, p, o, modelNode);
return super.resolver.resolve(constraint);
}
catch (QueryException queryException) {
throw new ResolverException("Could not get all statements.", queryException);
}
}
/**
* Resolves the constraint: $var <rdf:type> <geo:Point> <model>
*
* @param var Variable
* @param model long
* @throws ResolverException
* @return Resolution
*/
private Resolution getPointTypes(Variable var,
long model) throws ResolverException {
try {
LocalNode rdfType = new LocalNode(RDF_TYPE);
LocalNode geoPoint = new LocalNode(GEO_POINT);
LocalNode modelNode = new LocalNode(model);
Constraint constraint = new ConstraintImpl(var, rdfType, geoPoint, modelNode);
return super.resolver.resolve(constraint);
} catch (QueryException queryException) {
throw new ResolverException("Could not get Points.", queryException);
}
}
/**
* Returns all Statements of the form: $d :type Distance
*
* @param var Variable
* @param model long
* @throws ResolverException
* @return Resolution
*/
private Resolution getDistanceTypes(Variable var,
long model) throws ResolverException {
try {
LocalNode rdfType = new LocalNode(RDF_TYPE);
LocalNode distance = new LocalNode(DIS_DISTANCE);
LocalNode modelNode = new LocalNode(model);
Constraint constraint = new ConstraintImpl(var, rdfType, distance, modelNode);
return super.resolver.resolve(constraint);
} catch (QueryException queryException) {
throw new ResolverException("Could not get Distances.", queryException);
}
}
/**
* Resolves the constraint: $var <geo:lat> $lat <model>
*
* @param var Variable
* @param lat Variable
* @param model long
* @throws ResolverException
* @return Resolution
*/
private Resolution getPointLatitudes(Variable var, Variable lat,
long model) throws ResolverException {
try {
LocalNode geoLat = new LocalNode(GEO_LAT);
LocalNode modelNode = new LocalNode(model);
Constraint constraint = new ConstraintImpl(var, geoLat, lat, modelNode);
return super.resolver.resolve(constraint);
} catch (QueryException queryException) {
throw new ResolverException("Could not get Point Latitudes.", queryException);
}
}
/**
* Resolves the constraint: $var <geo:long> $lon <model>
*
* @param var Variable
* @param lon Variable
* @param model long
* @throws ResolverException
* @return Resolution
*/
private Resolution getPointLongitudes(Variable var, Variable lon,
long model) throws ResolverException {
try {
LocalNode geoLong = new LocalNode(GEO_LONG);
LocalNode modelNode = new LocalNode(model);
Constraint constraint = new ConstraintImpl(var, geoLong, lon, modelNode);
return super.resolver.resolve(constraint);
} catch (QueryException queryException) {
throw new ResolverException("Could not get Point Longitudes.", queryException);
}
}
/**
* Creates co-ordinates from the tuples and generates inferred distance
* statements from the co-ordinates 'x' and 'y'.
*
* Tuples will have the variables:
* [[$x] [$xlong] [$xlat] [$y] [$ylat] [$ylong]]
*
* @param points Tuples
* @return Statements
* @throws ResolverException
*/
private Statements getInferredStatements(Tuples points) throws ResolverException {
return new GISDistanceStatements(points, super.gisCalculator, resolverSession);
}
/**
* Creates a Model for storing temporary statements and returns it's Node ID.
*
* @throws ResolverException
* @return long
*/
private long createTemporaryModel() throws ResolverException {
long tempModel = -1;
try {
String modelUID = UIDGenerator.generateUID();
URIReferenceImpl model = new URIReferenceImpl(new URI("temp:" + modelUID));
tempModel = resolverSession.localizePersistent(model);
if (log.isDebugEnabled()) {
log.debug("Creating temp Model: " + model + " with ID: " + tempModel);
}
super.resolver.createModel(tempModel, MODEL_TYPE);
return tempModel;
} catch (Exception exception) {
throw new ResolverException("Failed to create temporary Model.", exception);
}
}
/**
* Drops the model.
*
* @param model long
* @throws ResolverException
*/
private void dropTemporaryModel(long model) throws ResolverException {
if (log.isDebugEnabled()) log.debug("Dropping temp Model with ID: " + model);
super.resolver.removeModel(model);
}
/**
* {@inheritDoc}
*
* @param constraint Constraint
* @return Resolution
* @throws QueryException
*/
public Resolution resolve(Constraint constraint) throws QueryException {
return super.resolver.resolve(constraint);
}
}