001/* 002 * BioJava development code 003 * 004 * This code may be freely distributed and modified under the 005 * terms of the GNU Lesser General Public Licence. This should 006 * be distributed with the code. If you do not have a copy, 007 * see: 008 * 009 * http://www.gnu.org/copyleft/lesser.html 010 * 011 * Copyright for this code is held jointly by the individual 012 * authors. These should be listed in @author doc comments. 013 * 014 * For more information on the BioJava project and its aims, 015 * or to join the biojava-l mailing list, visit the home page 016 * at: 017 * 018 * http://www.biojava.org/ 019 * 020 */ 021 022package org.biojava.bio; 023 024import java.util.HashSet; 025import java.util.Iterator; 026import java.util.Set; 027 028/** 029 * <p><code>PropertyConstraint</code>s describes a constraint applied 030 * to the members of an annotation bundle.</p> 031 * 032 * <p><code>PropertyConstraint</code>s are usually used in conjunction 033 * with the <code>AnnotationType</code> interface to describe a class 034 * of annotations by the values of their properties. In this way, you 035 * can generate controlled vocabularies over Java objects.</p> 036 * 037 * <p>The constraints accept or reject individual objects. 038 * In general, it is not possible to get back a set of all items 039 * that would be accepted by a particular constraint.</p> 040 * Instantiate PropertyConstraint classes when populating an 041 * AnnotationType instance 042 * Implement PropertyContraint to provide meta-data about a new 043 * type of object relationship. For example, if there was a 044 * data-structure representing an inheritance hierachy, then an 045 * implementation of PropertyConstraint could be written that allowed 046 * a propertie's value to be constrained to be a child of a 047 * particular node in the hierachy 048 * @since 1.3 049 * @author Matthew Pocock 050 * @author Keith James 051 * @author Thomas Down 052 * 053 * 054 */ 055public interface PropertyConstraint { 056 /** 057 * <code>accept</code> returns true if the value fulfills the 058 * constraint. 059 * 060 * Manually compare items with the PropertyConstraint. Node: 061 * this will ususaly be done for you in an AnnotationType instance 062 * 063 * Use for implementing accept() on AnnotatoinType 064 * 065 * @param value an <code>Object</code> to check. 066 * @return a <code>boolean</code>. 067 */ 068 boolean accept(Object value); 069 070 /** 071 * <p><code>subConstraintOf</code> returns true if the constraint 072 * is a sub-constraint.<p> 073 * 074 * <p>A pair of constraints super and sub are in a 075 * superConstraint/subConstraint relationship if every object 076 * accepted by sub is also accepted by super. To put it another 077 * way, if instanceOf was used as a set-membership indicator 078 * function over some set of objects, then the set produced by 079 * super would be a superset of that produced by sub.</p> 080 * 081 * <p>It is not expected that constraints will neccesarily 082 * maintain references to super/sub types. It will be more usual 083 * to infer this relationship by introspecting the constraints 084 * themselves. For example, 085 * <code>PropertyConstraint.ByClass</code> will infer 086 * subConstraintOf by looking at the possible class of all items 087 * matching subConstraint.</p> 088 * 089 * Useful when attempting to compare two constraints to see 090 * if it is necisary to retain both. You may want to check the more 091 * general or the more specific constraint only. 092 * 093 * @param subConstraint a <code>PropertyConstraint</code> to check. 094 * @return a <code>boolean</code>. 095 * 096 */ 097 boolean subConstraintOf(PropertyConstraint subConstraint); 098 099 /** 100 * <code>ANY</code> is a constraint which accepts a property for 101 * addition under all conditions. 102 * 103 * Whenever a PropertyConstraint is needed and you want to allow 104 * any value there 105 */ 106 PropertyConstraint ANY = new AnyPropertyConstraint(); 107 108 /** 109 * <code>NONE</code> is a constraint which accepts no value for a property 110 * under any condition. 111 * 112 * Whenever a PropertyConstraint is needed and you want to 113 * dissalow all values there e.g. when marking a property as having to be unset 114 */ 115 PropertyConstraint NONE = new NonePropertyConstraint(); 116 117 /** 118 * <code>ByClass</code> accepts a property value if it is an 119 * instance of a specific Java class. 120 * 121 * @since 1.3 122 * @author Matthew Pocock 123 * Constrain a property to containing values of a particular class 124 * e.g. <code>new ByClass(String.class)</code> or 125 * <code>new ByClass(Double)</code> will ensure 126 * that the property is a String or a Double respecitvely. 127 */ 128 class ByClass implements PropertyConstraint { 129 private Class cl; 130 131 /** 132 * Create a new ByClass instance. 133 * 134 * @param cl the Class that all properties must be assignable to 135 */ 136 public ByClass(Class cl) { 137 this.cl = cl; 138 } 139 140 /** 141 * Get the Class used as the constraint. 142 * 143 * @return the Class all properties must be instances of 144 */ 145 public Class getPropertyClass() { 146 return cl; 147 } 148 149 public boolean accept(Object value) { 150 return cl.isInstance(value); 151 } 152 153 public boolean subConstraintOf(PropertyConstraint subConstraint) { 154 if (subConstraint instanceof ByClass) { 155 ByClass sc = (ByClass) subConstraint; 156 return cl.isAssignableFrom(sc.getPropertyClass()); 157 } else if(subConstraint instanceof Enumeration) { 158 Set values = ((Enumeration) subConstraint).getValues(); 159 for(Iterator i = values.iterator(); i.hasNext(); ) { 160 if(!accept(i.next())) { 161 return false; 162 } 163 } 164 165 return true; 166 } else if(subConstraint instanceof ExactValue) { 167 return accept(((ExactValue) subConstraint).getValue()); 168 } 169 170 return false; 171 } 172 173 public String toString() { 174 return "Class:" + cl.toString(); 175 } 176 } 177 178 /** 179 * <p><code>ByAnnotationType</code> accepts a property value if it 180 * belongs to type defined by AnnotationType.</p> 181 * 182 * <p>If you had an Embl AnnotationType then you could say that the REF 183 * property must contain annotations that fits your reference AnnotationType 184 * (with author list, title, optinal medline ID etc.).</p> 185 * If you wish to build a tree of Annotations so that a 186 * property in one is guaranteed to be itself an Annotation of a 187 * particular type. Effectively this lets you build your own 188 * type system using AnnotationType and PropertyConstraint. 189 * @since 1.3 190 * @author Matthew Pocock 191 * 192 */ 193 class ByAnnotationType implements PropertyConstraint { 194 private AnnotationType annType; 195 196 /** 197 * Create a new constraint by type. 198 * 199 * @param annType the AnnotationType to constrain to 200 */ 201 public ByAnnotationType(AnnotationType annType) { 202 this.annType = annType; 203 } 204 205 /** 206 * Get the AnnotationType used as a constraint. 207 * 208 * @return the AnnotationType constraint 209 */ 210 public AnnotationType getAnnotationType() { 211 return annType; 212 } 213 214 public boolean accept(Object value) { 215 if (value instanceof Annotation) { 216 return annType.instanceOf((Annotation) value); 217 } 218 219 return false; 220 } 221 222 public boolean subConstraintOf(PropertyConstraint subConstraint) { 223 if (subConstraint instanceof ByAnnotationType) { 224 ByAnnotationType at = (ByAnnotationType) subConstraint; 225 return annType.subTypeOf(at.getAnnotationType()); 226 } 227 228 return false; 229 } 230 231 public String toString() { 232 return "AnnotationType:" + annType.getProperties(); 233 } 234 } 235 236 /** 237 * <p>Matches properties if they have exactly this one value.</p> 238 * 239 * <p>This is like the extreme case of an Enumeration which has just one 240 * member. It is most usefull for selecting annotations with a particular 241 * property set to a particular value e.g. ID="01234".</p> 242 * 243 * 244 * If you want to declare that a property must have a single value 245 * 246 * In conjunction with CardinalityConstraint.ZERO_OR_ONE you 247 * could make a property that is potional but if present must have this 248 * value 249 * 250 * Use with FilterUtils.byAnnotation() to search for features 251 * with properties set to specific values 252 * @author Matthew Pocock 253 */ 254 class ExactValue implements PropertyConstraint { 255 private Object value; 256 257 /** 258 * Get a PropertyConstraint that matches this object and all those that 259 * are equal to it (by the Object.equals() method). 260 * 261 * @param value the Object to match against 262 */ 263 public ExactValue(Object value) { 264 this.value = value; 265 } 266 267 /** 268 * Get the value that all properties must match. 269 * 270 * @return the value Object 271 */ 272 public Object getValue() { 273 return value; 274 } 275 276 public boolean accept(Object obj) { 277 return value.equals(obj); 278 } 279 280 public boolean subConstraintOf(PropertyConstraint pc) { 281 if(pc instanceof ExactValue) { 282 return value.equals(((ExactValue) pc).getValue()); 283 } else if(pc instanceof Enumeration) { 284 Enumeration e = (Enumeration) pc; 285 return e.getValues().size() == 1 && e.accept(value); 286 } 287 288 return false; 289 } 290 291 public String toString() { 292 return "ExactValue: " + value; 293 } 294 } 295 296 /** 297 * <code>Enumeration</code> accepts a property if it is present 298 * in the specified set of values. 299 * 300 * 301 * 302 * If you want to declare that a property must be within a range 303 * of values, for example PRIMARY_COLOR is one of "RED, YELLOW, BLUE" 304 * 305 * Use with FilterUtils.byAnnotation() to search for features 306 * with properties set to a range of values 307 * @since 1.3 308 * @author Matthew Pocock 309 */ 310 class Enumeration implements PropertyConstraint { 311 private Set values; 312 313 /** 314 * Creates a new <code>Enumeration</code> using the members of 315 * the specified set as a constraint. 316 * 317 * @param values a <code>Set</code> of all possible values 318 * 319 */ 320 public Enumeration(Set values) { 321 this.values = values; 322 } 323 324 /** 325 * Creates a new <code>Enumeration</code> using the elements of the 326 * specified array as a constraint. 327 * 328 * @param values an <code>Array</code> of all possible values 329 * 330 */ 331 public Enumeration(Object[] values) { 332 this.values = new HashSet(); 333 for(int i = 0; i < values.length; i++) { 334 this.values.add(values[i]); 335 } 336 } 337 338 /** 339 * <code>getValues</code> returns the set of values which 340 * constrain the property. 341 * 342 * @return a <code>Set</code>. 343 */ 344 public Set getValues() { 345 return values; 346 } 347 348 public boolean accept(Object value) { 349 return values.contains(value); 350 } 351 352 public boolean subConstraintOf(PropertyConstraint subConstraint) { 353 if (subConstraint instanceof Enumeration) { 354 Enumeration subE = (Enumeration) subConstraint; 355 return values.containsAll(subE.getValues()); 356 } else if(subConstraint instanceof ExactValue) { 357 return accept(((ExactValue) subConstraint).getValue()); 358 } 359 360 return false; 361 } 362 363 public String toString() { 364 return "Enumeration:" + values; 365 } 366 } 367 368 /** 369 * A property constraint that accpepts items iff they are accepted by both 370 * child constraints. This effectively matches the intersection of the items 371 * matched by the two constraints. 372 * 373 * 374 * Use this to combine multiple constraints. You can make one 375 * or both of the children And instances if you need a tighter 376 * intersection. 377 * @author Matthew Pocock 378 */ 379 class And implements PropertyConstraint { 380 private PropertyConstraint c1; 381 private PropertyConstraint c2; 382 383 /** 384 * Create a new <code>And</code> from two child constraints. 385 * 386 * @param c1 the first child 387 * @param c2 the seccond child 388 */ 389 public And(PropertyConstraint c1, PropertyConstraint c2) { 390 this.c1 = c1; 391 this.c2 = c2; 392 } 393 394 /** 395 * Get the first child PropertyConstraint. 396 * 397 * @return the first child PropertyConstraint 398 * 399 */ 400 public PropertyConstraint getChild1() { 401 return c1; 402 } 403 404 /** 405 * Get the seccond child PropertyConstraint. 406 * 407 * @return the seccond child PropertyConstraint 408 * 409 * 410 */ 411 public PropertyConstraint getChild2() { 412 return c2; 413 } 414 415 public boolean accept(Object object) { 416 return c1.accept(object) && c2.accept(object); 417 } 418 419 public boolean subConstraintOf(PropertyConstraint pc) { 420 return c1.subConstraintOf(pc) && c2.subConstraintOf(pc); 421 } 422 423 public String toString() { 424 return "And(" + c1 + ", " + c2 + ")"; 425 } 426 } 427 428 /** 429 * A property constraint that accepts items iff they are accepted by either 430 * child constraints. This effectively matches the union of the items 431 * matched by the two constraints. Use this to combine multiple constraints. You can make one 432 * or both of the children Or instances if you need a wider 433 * union. 434 * 435 * @author Matthew Pocock 436 */ 437 class Or implements PropertyConstraint { 438 private PropertyConstraint c1; 439 private PropertyConstraint c2; 440 441 /** 442 * Create a new <code>Or</code> from two child constraints. 443 * 444 * @param c1 the first child 445 * @param c2 the seccond child 446 */ 447 public Or(PropertyConstraint c1, PropertyConstraint c2) { 448 this.c1 = c1; 449 this.c2 = c2; 450 } 451 452 /** 453 * Get the first child PropertyConstraint. 454 * 455 * @return the first child PropertyConstraint 456 * 457 */ 458 public PropertyConstraint getChild1() { 459 return c1; 460 } 461 462 /** 463 * Get the seccond child PropertyConstraint. 464 * 465 * @return the seccond child PropertyConstraint 466 * 467 */ 468 public PropertyConstraint getChild2() { 469 return c2; 470 } 471 472 public boolean accept(Object object) { 473 return c1.accept(object) || c2.accept(object); 474 } 475 476 public boolean subConstraintOf(PropertyConstraint pc) { 477 return c1.subConstraintOf(pc) || c2.subConstraintOf(pc); 478 } 479 480 public String toString() { 481 return "Or(" + c1 + ", " + c2 + ")"; 482 } 483 } 484} 485 486