
org.biojava.bio.PropertyConstraint Maven / Gradle / Ivy
/*
* BioJava development code
*
* This code may be freely distributed and modified under the
* terms of the GNU Lesser General Public Licence. This should
* be distributed with the code. If you do not have a copy,
* see:
*
* http://www.gnu.org/copyleft/lesser.html
*
* Copyright for this code is held jointly by the individual
* authors. These should be listed in @author doc comments.
*
* For more information on the BioJava project and its aims,
* or to join the biojava-l mailing list, visit the home page
* at:
*
* http://www.biojava.org/
*
*/
package org.biojava.bio;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* PropertyConstraint
s describes a constraint applied
* to the members of an annotation bundle.
*
* PropertyConstraint
s are usually used in conjunction
* with the AnnotationType
interface to describe a class
* of annotations by the values of their properties. In this way, you
* can generate controlled vocabularies over Java objects.
*
* The constraints accept or reject individual objects.
* In general, it is not possible to get back a set of all items
* that would be accepted by a particular constraint.
* Instantiate PropertyConstraint classes when populating an
* AnnotationType instance
* Implement PropertyContraint to provide meta-data about a new
* type of object relationship. For example, if there was a
* data-structure representing an inheritance hierachy, then an
* implementation of PropertyConstraint could be written that allowed
* a propertie's value to be constrained to be a child of a
* particular node in the hierachy
* @since 1.3
* @author Matthew Pocock
* @author Keith James
* @author Thomas Down
*
*
*/
public interface PropertyConstraint {
/**
* accept
returns true if the value fulfills the
* constraint.
*
* Manually compare items with the PropertyConstraint. Node:
* this will ususaly be done for you in an AnnotationType instance
*
* Use for implementing accept() on AnnotatoinType
*
* @param value an Object
to check.
* @return a boolean
.
*/
boolean accept(Object value);
/**
* subConstraintOf
returns true if the constraint
* is a sub-constraint.
*
*
A pair of constraints super and sub are in a
* superConstraint/subConstraint relationship if every object
* accepted by sub is also accepted by super. To put it another
* way, if instanceOf was used as a set-membership indicator
* function over some set of objects, then the set produced by
* super would be a superset of that produced by sub.
*
* It is not expected that constraints will neccesarily
* maintain references to super/sub types. It will be more usual
* to infer this relationship by introspecting the constraints
* themselves. For example,
* PropertyConstraint.ByClass
will infer
* subConstraintOf by looking at the possible class of all items
* matching subConstraint.
*
* Useful when attempting to compare two constraints to see
* if it is necisary to retain both. You may want to check the more
* general or the more specific constraint only.
*
* @param subConstraint a PropertyConstraint
to check.
* @return a boolean
.
*
*/
boolean subConstraintOf(PropertyConstraint subConstraint);
/**
* ANY
is a constraint which accepts a property for
* addition under all conditions.
*
* Whenever a PropertyConstraint is needed and you want to allow
* any value there
*/
PropertyConstraint ANY = new AnyPropertyConstraint();
/**
* NONE
is a constraint which accepts no value for a property
* under any condition.
*
* Whenever a PropertyConstraint is needed and you want to
* dissalow all values there e.g. when marking a property as having to be unset
*/
PropertyConstraint NONE = new NonePropertyConstraint();
/**
* ByClass
accepts a property value if it is an
* instance of a specific Java class.
*
* @since 1.3
* @author Matthew Pocock
* Constrain a property to containing values of a particular class
* e.g. new ByClass(String.class)
or
* new ByClass(Double)
will ensure
* that the property is a String or a Double respecitvely.
*/
class ByClass implements PropertyConstraint {
private Class cl;
/**
* Create a new ByClass instance.
*
* @param cl the Class that all properties must be assignable to
*/
public ByClass(Class cl) {
this.cl = cl;
}
/**
* Get the Class used as the constraint.
*
* @return the Class all properties must be instances of
*/
public Class getPropertyClass() {
return cl;
}
public boolean accept(Object value) {
return cl.isInstance(value);
}
public boolean subConstraintOf(PropertyConstraint subConstraint) {
if (subConstraint instanceof ByClass) {
ByClass sc = (ByClass) subConstraint;
return cl.isAssignableFrom(sc.getPropertyClass());
} else if(subConstraint instanceof Enumeration) {
Set values = ((Enumeration) subConstraint).getValues();
for(Iterator i = values.iterator(); i.hasNext(); ) {
if(!accept(i.next())) {
return false;
}
}
return true;
} else if(subConstraint instanceof ExactValue) {
return accept(((ExactValue) subConstraint).getValue());
}
return false;
}
public String toString() {
return "Class:" + cl.toString();
}
}
/**
* ByAnnotationType
accepts a property value if it
* belongs to type defined by AnnotationType.
*
* If you had an Embl AnnotationType then you could say that the REF
* property must contain annotations that fits your reference AnnotationType
* (with author list, title, optinal medline ID etc.).
* If you wish to build a tree of Annotations so that a
* property in one is guaranteed to be itself an Annotation of a
* particular type. Effectively this lets you build your own
* type system using AnnotationType and PropertyConstraint.
* @since 1.3
* @author Matthew Pocock
*
*/
class ByAnnotationType implements PropertyConstraint {
private AnnotationType annType;
/**
* Create a new constraint by type.
*
* @param annType the AnnotationType to constrain to
*/
public ByAnnotationType(AnnotationType annType) {
this.annType = annType;
}
/**
* Get the AnnotationType used as a constraint.
*
* @return the AnnotationType constraint
*/
public AnnotationType getAnnotationType() {
return annType;
}
public boolean accept(Object value) {
if (value instanceof Annotation) {
return annType.instanceOf((Annotation) value);
}
return false;
}
public boolean subConstraintOf(PropertyConstraint subConstraint) {
if (subConstraint instanceof ByAnnotationType) {
ByAnnotationType at = (ByAnnotationType) subConstraint;
return annType.subTypeOf(at.getAnnotationType());
}
return false;
}
public String toString() {
return "AnnotationType:" + annType.getProperties();
}
}
/**
* Matches properties if they have exactly this one value.
*
* This is like the extreme case of an Enumeration which has just one
* member. It is most usefull for selecting annotations with a particular
* property set to a particular value e.g. ID="01234".
*
*
* If you want to declare that a property must have a single value
*
* In conjunction with CardinalityConstraint.ZERO_OR_ONE you
* could make a property that is potional but if present must have this
* value
*
* Use with FilterUtils.byAnnotation() to search for features
* with properties set to specific values
* @author Matthew Pocock
*/
class ExactValue implements PropertyConstraint {
private Object value;
/**
* Get a PropertyConstraint that matches this object and all those that
* are equal to it (by the Object.equals() method).
*
* @param value the Object to match against
*/
public ExactValue(Object value) {
this.value = value;
}
/**
* Get the value that all properties must match.
*
* @return the value Object
*/
public Object getValue() {
return value;
}
public boolean accept(Object obj) {
return value.equals(obj);
}
public boolean subConstraintOf(PropertyConstraint pc) {
if(pc instanceof ExactValue) {
return value.equals(((ExactValue) pc).getValue());
} else if(pc instanceof Enumeration) {
Enumeration e = (Enumeration) pc;
return e.getValues().size() == 1 && e.accept(value);
}
return false;
}
public String toString() {
return "ExactValue: " + value;
}
}
/**
* Enumeration
accepts a property if it is present
* in the specified set of values.
*
*
*
* If you want to declare that a property must be within a range
* of values, for example PRIMARY_COLOR is one of "RED, YELLOW, BLUE"
*
* Use with FilterUtils.byAnnotation() to search for features
* with properties set to a range of values
* @since 1.3
* @author Matthew Pocock
*/
class Enumeration implements PropertyConstraint {
private Set values;
/**
* Creates a new Enumeration
using the members of
* the specified set as a constraint.
*
* @param values a Set
of all possible values
*
*/
public Enumeration(Set values) {
this.values = values;
}
/**
* Creates a new Enumeration
using the elements of the
* specified array as a constraint.
*
* @param values an Array
of all possible values
*
*/
public Enumeration(Object[] values) {
this.values = new HashSet();
for(int i = 0; i < values.length; i++) {
this.values.add(values[i]);
}
}
/**
* getValues
returns the set of values which
* constrain the property.
*
* @return a Set
.
*/
public Set getValues() {
return values;
}
public boolean accept(Object value) {
return values.contains(value);
}
public boolean subConstraintOf(PropertyConstraint subConstraint) {
if (subConstraint instanceof Enumeration) {
Enumeration subE = (Enumeration) subConstraint;
return values.containsAll(subE.getValues());
} else if(subConstraint instanceof ExactValue) {
return accept(((ExactValue) subConstraint).getValue());
}
return false;
}
public String toString() {
return "Enumeration:" + values;
}
}
/**
* A property constraint that accpepts items iff they are accepted by both
* child constraints. This effectively matches the intersection of the items
* matched by the two constraints.
*
*
* Use this to combine multiple constraints. You can make one
* or both of the children And instances if you need a tighter
* intersection.
* @author Matthew Pocock
*/
class And implements PropertyConstraint {
private PropertyConstraint c1;
private PropertyConstraint c2;
/**
* Create a new And
from two child constraints.
*
* @param c1 the first child
* @param c2 the seccond child
*/
public And(PropertyConstraint c1, PropertyConstraint c2) {
this.c1 = c1;
this.c2 = c2;
}
/**
* Get the first child PropertyConstraint.
*
* @return the first child PropertyConstraint
*
*/
public PropertyConstraint getChild1() {
return c1;
}
/**
* Get the seccond child PropertyConstraint.
*
* @return the seccond child PropertyConstraint
*
*
*/
public PropertyConstraint getChild2() {
return c2;
}
public boolean accept(Object object) {
return c1.accept(object) && c2.accept(object);
}
public boolean subConstraintOf(PropertyConstraint pc) {
return c1.subConstraintOf(pc) && c2.subConstraintOf(pc);
}
public String toString() {
return "And(" + c1 + ", " + c2 + ")";
}
}
/**
* A property constraint that accepts items iff they are accepted by either
* child constraints. This effectively matches the union of the items
* matched by the two constraints. Use this to combine multiple constraints. You can make one
* or both of the children Or instances if you need a wider
* union.
*
* @author Matthew Pocock
*/
class Or implements PropertyConstraint {
private PropertyConstraint c1;
private PropertyConstraint c2;
/**
* Create a new Or
from two child constraints.
*
* @param c1 the first child
* @param c2 the seccond child
*/
public Or(PropertyConstraint c1, PropertyConstraint c2) {
this.c1 = c1;
this.c2 = c2;
}
/**
* Get the first child PropertyConstraint.
*
* @return the first child PropertyConstraint
*
*/
public PropertyConstraint getChild1() {
return c1;
}
/**
* Get the seccond child PropertyConstraint.
*
* @return the seccond child PropertyConstraint
*
*/
public PropertyConstraint getChild2() {
return c2;
}
public boolean accept(Object object) {
return c1.accept(object) || c2.accept(object);
}
public boolean subConstraintOf(PropertyConstraint pc) {
return c1.subConstraintOf(pc) || c2.subConstraintOf(pc);
}
public String toString() {
return "Or(" + c1 + ", " + c2 + ")";
}
}
}