/*******************************************************************************
* Copyright (c) 2007, 2010 Association for Decentralized Information Management
* in Industry THTH ry.
* 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
*
* Contributors:
* VTT Technical Research Centre of Finland - initial API and implementation
*******************************************************************************/
package org.simantics.ui.workbench.editor.input;
import java.util.Arrays;
import org.simantics.db.ReadGraph;
import org.simantics.db.Resource;
import org.simantics.db.common.request.ParametrizedRead;
import org.simantics.db.common.request.Queries;
import org.simantics.db.common.request.UnaryRead;
import org.simantics.db.exception.DatabaseException;
import org.simantics.db.layer0.request.combinations.Combinators;
import org.simantics.db.layer0.request.combinations.Combinators.SynchronizationProcedure;
import org.simantics.db.request.Read;
import org.simantics.ui.workbench.IResourceEditorInput;
/**
* Composable database read operations designed to be used for defining
* validation strategies for resource editor inputs through composition.
*
*
* For example one validation criterion might be that the editor input or one of
* its neighboring resources must have a proper URI.
*
* @author Tuukka Lehtonen
*/
public final class InputValidationCombinators {
/**
* Returns a function:
*
* = subject -> exists(URI(subject))
* | null -> false
*
.
*/
public static ParametrizedRead hasURI() {
return HAS_URI;
}
/**
* Returns a function:
*
* = input -> resource(input)
* | null -> null
*
.
*/
public static ParametrizedRead extractInputResource() {
return EXTRACT_INPUT_RESOURCE;
}
/**
* Returns a function:
*
* = subject -> singleObject(subject, resource(relationURI))
* | null -> null
*
.
*/
public static ParametrizedRead completeFunction(String relationURI) {
return new CompleteFunctionURI(relationURI);
}
/**
* Returns a function:
*
* = subject -> possibleObject(subject, resource(relationURI))
* | null -> null
*
.
*/
public static ParametrizedRead partialFunction(String relationURI) {
return new PartialFunctionURI(relationURI);
}
/**
* Returns a function:
*
* = subject -> true if any of conditions is true for subject, false otherwise
* | null -> true
*
.
*/
@SuppressWarnings("unchecked")
public static ParametrizedRead or(ParametrizedRead c1,
ParametrizedRead c2) {
return new FunctionOr(new ParametrizedRead[] { c1, c2 });
}
// ------------------------------------------------------------------------
private static class Or extends UnaryRead {
ParametrizedRead[] reads;
public Or(Resource resource, ParametrizedRead... reads) {
super(resource);
this.reads = reads;
}
@Override
public Boolean perform(ReadGraph graph) throws DatabaseException {
for (ParametrizedRead r : reads) {
Read read = r.get(parameter);
Boolean value = graph.syncRequest( read );
if (value)
return Boolean.TRUE;
}
return Boolean.FALSE;
}
@Override
public int hashCode() {
return super.hashCode() * 31 + Arrays.hashCode(reads);
}
@Override
public boolean equals(Object object) {
if (this == object) return true;
else if (object == null || getClass() != object.getClass()) return false;
Or other = (Or) object;
return super.equals(object) && Arrays.equals(reads, other.reads);
}
}
// ------------------------------------------------------------------------
private static class FunctionOr implements ParametrizedRead {
ParametrizedRead[] reads;
public FunctionOr(ParametrizedRead... reads) {
this.reads = reads;
}
@Override
public Read get(Resource subject) {
if (subject == null || reads.length == 0)
return Combinators.constant(Boolean.TRUE);
return new Or(subject, reads);
}
@Override
public int hashCode() {
return getClass().hashCode() + 31 * Arrays.hashCode(reads);
}
@Override
public boolean equals(Object obj) {
if(obj == this) return true;
if(obj == null || obj.getClass() != getClass()) return false;
FunctionOr other = (FunctionOr)obj;
return Arrays.equals(reads, other.reads);
}
}
// ------------------------------------------------------------------------
private static class HasURI extends UnaryRead {
public HasURI(Resource resource) {
super(resource);
}
@Override
public Boolean perform(ReadGraph graph) throws DatabaseException {
if (parameter == null)
return Boolean.FALSE;
String uri = graph.syncRequest(Queries.possibleUri(parameter));
//System.out.println("uri(" + parameter + "): " + uri);
if (uri == null)
return Boolean.FALSE;
// FIXME: URI request will return invalid URIs, like
// null/Configuration/MyComposite after deleting the parenting model
// For this reason we try to reverse lookup the URI back into a
// resource which must be equal to the original parameter resource.
Resource reverseLookup = graph.getPossibleResource(uri);
//System.out.println("resource(" + uri + "): " + reverseLookup);
return parameter.equals(reverseLookup);
}
}
public static Read hasURI(Resource resource) {
return new HasURI(resource);
}
// ------------------------------------------------------------------------
private static class ExtractResource extends UnaryRead {
public ExtractResource(IResourceEditorInput input) {
super(input);
}
@Override
public Resource perform(ReadGraph graph) throws DatabaseException {
return parameter != null ? parameter.getResource() : null;
}
}
public static Read extractResource(IResourceEditorInput input) {
return new ExtractResource(input);
}
// ------------------------------------------------------------------------
private static class PossibleObjectURI implements Read {
Resource subject;
String relationURI;
public PossibleObjectURI(Resource subject, String relationURI) {
this.subject = subject;
this.relationURI = relationURI;
}
@Override
public Resource perform(ReadGraph graph) throws DatabaseException {
SynchronizationProcedure procedure = new SynchronizationProcedure();
Resource relation = graph.getResource(relationURI);
graph.forPossibleObject(subject, relation, procedure);
return procedure.getResult();
}
@Override
public int hashCode() {
return subject.hashCode() + 31 * relationURI.hashCode();
}
@Override
public boolean equals(Object object) {
if (this == object) return true;
else if (object == null || getClass() != object.getClass()) return false;
PossibleObjectURI other = (PossibleObjectURI)object;
return subject.equals(other.subject) && relationURI.equals(other.relationURI);
}
}
/**
* Returns a read request that reads an object possibly connected to the subject by the relation.
*/
public static Read possibleObject(Resource subject, String relationURI) {
return new PossibleObjectURI(subject, relationURI);
}
// ------------------------------------------------------------------------
private static class PartialFunctionURI implements ParametrizedRead {
String relationURI;
public PartialFunctionURI(String relationURI) {
this.relationURI = relationURI;
}
@Override
public Read get(Resource subject) {
if (subject == null)
return Combinators.constant(null);
return possibleObject(subject, relationURI);
}
@Override
public int hashCode() {
return getClass().hashCode() + 31 * relationURI.hashCode();
}
@Override
public boolean equals(Object obj) {
if(obj == this) return true;
if(obj == null || obj.getClass() != getClass()) return false;
PartialFunctionURI other = (PartialFunctionURI)obj;
return relationURI.equals(other.relationURI);
}
}
// ------------------------------------------------------------------------
private static class SingleObjectURI implements Read {
Resource subject;
String relationURI;
public SingleObjectURI(Resource subject, String relationURI) {
this.subject = subject;
this.relationURI = relationURI;
}
@Override
public Resource perform(ReadGraph graph) throws DatabaseException {
SynchronizationProcedure procedure = new SynchronizationProcedure();
Resource relation = graph.getResource(relationURI);
graph.forSingleObject(subject, relation, procedure);
return procedure.getResult();
}
@Override
public int hashCode() {
return subject.hashCode() + 31 * relationURI.hashCode();
}
@Override
public boolean equals(Object object) {
if (this == object) return true;
else if (object == null || getClass() != object.getClass()) return false;
SingleObjectURI other = (SingleObjectURI) object;
return subject.equals(other.subject) && relationURI.equals(other.relationURI);
}
}
/**
* Returns a read request that reads a single object connected to the
* subject by the relation.
*/
public static Read singleObject(Resource subject, String relationURI) {
return new SingleObjectURI(subject, relationURI);
}
// ------------------------------------------------------------------------
private static class CompleteFunctionURI implements ParametrizedRead {
String relationURI;
public CompleteFunctionURI(String relationURI) {
this.relationURI = relationURI;
}
@Override
public Read get(Resource subject) {
if (subject == null)
return Combinators.constant(null);
return singleObject(subject, relationURI);
}
@Override
public int hashCode() {
return getClass().hashCode() + 31 * relationURI.hashCode();
}
@Override
public boolean equals(Object obj) {
if(obj == this) return true;
if(obj == null || obj.getClass() != getClass()) return false;
CompleteFunctionURI other = (CompleteFunctionURI)obj;
return relationURI.equals(other.relationURI);
}
}
// ------------------------------------------------------------------------
private static final ParametrizedRead EXTRACT_INPUT_RESOURCE = new ParametrizedRead() {
@Override
public Read get(IResourceEditorInput input) {
return extractResource(input);
}
};
// ------------------------------------------------------------------------
private static final ParametrizedRead HAS_URI = new ParametrizedRead() {
@Override
public Read get(Resource resource) {
return hasURI(resource);
}
};
}