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.Collection; 025import java.util.Iterator; 026 027import org.biojava.bio.symbol.Location; 028 029/** 030 * Used by <code>AnnotationType</code> to represent the constraint on 031 * the collection of values in a property-slot. 032 * CollectionConstraints usually use a <code>PropertyConstraint</code> 033 * to validate the individual elements. 034 * 035 * 036 * Use one or more of the built-in implementations to build new 037 * <code>AnnotationTypes</code>. 038 * 039 * @since 1.3 040 * @author Thomas Down 041 * @author Matthew Pocock 042 */ 043public interface CollectionConstraint { 044 /** 045 * <code>accept</code> returns true if the value fulfills the 046 * constraint. 047 * 048 * @param values a <code>Collection</code> to check. 049 * @return true if the values are acceptable 050 * 051 * powerUser Manually compare items with the CollectionConstraint. Node: 052 * this will ususaly be done for you in an AnnotationType instance 053 */ 054 public boolean accept(Object values); 055 056 /** 057 * <p><code>subConstraintOf</code> returns true if the constraint 058 * is a sub-constraint.<p> 059 * 060 * <p>A pair of constraints super and sub are in a 061 * superConstraint/subConstraint relationship if every object 062 * accepted by sub is also accepted by super. To put it another 063 * way, if instanceOf was used as a set-membership indicator 064 * function over some set of objects, then the set produced by 065 * super would be a superset of that produced by sub.</p> 066 * 067 * <p>It is not expected that constraints will neccesarily 068 * maintain references to super/sub types. It will be more usual 069 * to infer this relationship by introspecting the constraints 070 * themselves. For example, 071 * <code>CollectionConstraint.ByClass</code> will infer 072 * subConstraintOf by looking at the possible class of all items 073 * matching subConstraint.</p> 074 * 075 * @param subConstraint a <code>CollectionConstraint</code> to check. 076 * @return a <code>boolean</code>. 077 * 078 * 079 * Usefull when attempting to compare two constraints to see 080 * if it is necisary to retain both. You may want to check the more 081 * general or the more specific constraint only. 082 */ 083 public boolean subConstraintOf(CollectionConstraint subConstraint); 084 085 086 /** 087 * Return <code>true</code> iff the Collection formed by adding 088 * <code>newValue</code> to <code>current</code> would be accepted 089 * by this constraint. 090 * 091 * 092 * Implementations may <em>not</em> assume that <code>current</code> 093 * is valid. 094 * 095 * @param current a Collection containing the current values 096 * @param newValue the new value to add 097 * @return true if adding the new value will result in an acceptable 098 * property 099 */ 100 101 public boolean validateAddValue(Collection current, Object newValue); 102 103 /** 104 * Return <code>true</code> iff the Collection formed by removing 105 * <code>newValue</code> from <code>current</code> would be accepted 106 * by this constraint. 107 * 108 * 109 * Implementations may <em>not</em> assume that <code>current</code> 110 * is valid. However, <code>current</code> will already have been 111 * checked to ensure that it contains <code>victim</code>. 112 * 113 * @param current a Collection containing the current values 114 * @param victim the value to remove 115 * @return true if removing the victim will result in an acceptable 116 * property value set 117 */ 118 119 public boolean validateRemoveValue(Collection current, Object victim); 120 121 /** 122 * <code>ANY</code> is a constraint which accepts a property for 123 * addition under all conditions. 124 * 125 * Whenever a CollectionConstraint is needed and you want to allow 126 * any value there 127 */ 128 public static final CollectionConstraint ANY = new AllValuesIn(PropertyConstraint.ANY, CardinalityConstraint.ANY); 129 130 /** 131 * <code>EMPTY</code> is a constraint which only accepts the empty 132 * set. 133 * 134 * Use this to indicate that a property must be undefined 135 */ 136 137 public static final CollectionConstraint EMPTY = new AllValuesIn(PropertyConstraint.NONE, CardinalityConstraint.ZERO); 138 139 /** 140 * <code>NONE</code> is a constraint which accepts no value for a property 141 * under any condition. 142 * 143 * This value indicates an impossible condition. It may be 144 * returned by methods such as <code>AnnotationTools.intersection</code> 145 * to indicate that <code>NO</code> values of a property (include undefined) 146 * are valid. 147 */ 148 public static final CollectionConstraint NONE = new NoneCollectionConstraint(); 149 150 /** 151 * CollectionConstraint which validates all members of a Collection. 152 * All members must be vaild according to the supplied 153 * <code>PropertyConstraint</code>, and the total number of 154 * members must be acceptable by the given cardinality constraint. 155 * 156 * @author Thomas Down 157 * @author Matthew Pocock 158 */ 159 160 public class AllValuesIn implements CollectionConstraint { 161 private PropertyConstraint pc; 162 private Location card; 163 164 /** 165 * Create an AllValuesIn based upon a PropertyConstraint and a 166 * cardinality. 167 * 168 * @param pc the PropertyConstraint to apply to each property value 169 * @param card the cardinality constraint restricting the number of 170 * values 171 */ 172 public AllValuesIn(PropertyConstraint pc, Location card) { 173 this.pc = pc; 174 this.card = card; 175 } 176 177 /** 178 * Get the PropertyConstraint used to validate each property value. 179 * 180 * @return the PropertyConstraint used 181 */ 182 public PropertyConstraint getPropertyConstraint() { 183 return pc; 184 } 185 186 /** 187 * Get the cardinality constraint used to validate the number of property 188 * values. 189 * 190 * @return the cardinality constraint as a Location 191 */ 192 public Location getCardinalityConstraint() { 193 return card; 194 } 195 196 public boolean accept(Object o) { 197 if (o instanceof Collection) { 198 Collection co = (Collection) o; 199 if (!card.contains(co.size())) { 200 return false; 201 } else { 202 for (Iterator i = co.iterator(); i.hasNext(); ) { 203 if (!pc.accept(i.next())) { 204 return false; 205 } 206 } 207 return true; 208 } 209 } else { 210 return card.contains(1) && pc.accept(o); 211 } 212 } 213 214 public boolean validateAddValue(Collection oldcol, Object newValue) { 215 if (!pc.accept(newValue)) { 216 return false; 217 } 218 if (!card.contains(oldcol.size() + 1)) { 219 return false; 220 } 221 for (Iterator i = oldcol.iterator(); i.hasNext(); ) { 222 if (!pc.accept(i.next())) { 223 return false; 224 } 225 } 226 return true; 227 } 228 229 public boolean validateRemoveValue(Collection oldcol, Object victim) { 230 if (!card.contains(oldcol.size() - 1)) { 231 return false; 232 } 233 234 for (Iterator i = oldcol.iterator(); i.hasNext(); ) { 235 Object o = i.next(); 236 if (!o.equals(victim) && !pc.accept(o)) { 237 return false; 238 } 239 } 240 return true; 241 } 242 243 public int hashCode() { 244 return pc.hashCode() + 87; 245 } 246 247 public boolean equals(Object o) { 248 if (o instanceof AllValuesIn) { 249 AllValuesIn avo = (AllValuesIn) o; 250 return avo.getCardinalityConstraint().equals(getCardinalityConstraint()) && 251 avo.getPropertyConstraint().equals(getPropertyConstraint()); 252 } else { 253 return false; 254 } 255 } 256 257 public boolean subConstraintOf(CollectionConstraint cc) { 258 if (cc instanceof NoneCollectionConstraint) { 259 return true; 260 } else if (cc instanceof CollectionConstraint.AllValuesIn) { 261 AllValuesIn ccavi = (AllValuesIn) cc; 262 return getCardinalityConstraint().contains(ccavi.getCardinalityConstraint()) && 263 getPropertyConstraint().subConstraintOf(ccavi.getPropertyConstraint()); 264 } else if (cc instanceof CollectionConstraint.Contains) { 265 if (!getCardinalityConstraint().contains(Integer.MAX_VALUE)) { 266 return false; 267 } else { 268 Contains ccc = (Contains) cc; 269 return getPropertyConstraint().subConstraintOf(ccc.getPropertyConstraint()); 270 } 271 } else if (cc instanceof CollectionConstraint.And) { 272 And cca = (And) cc; 273 return subConstraintOf(cca.getChild1()) || subConstraintOf(cca.getChild2()); 274 } else if (cc instanceof CollectionConstraint.Or) { 275 Or cco = (Or) cc; 276 return subConstraintOf(cco.getChild1()) && subConstraintOf(cco.getChild2()); 277 } 278 return false; 279 } 280 281 public String toString() { 282 return "AllValuesIn: (" + pc.toString() + ", " + card.toString() + ")"; 283 } 284 } 285 286 /** 287 * CollectionConstraint which validates a portion of a Collection. 288 * Accepts only collections where the number of members matching 289 * the <code>PropertyConstraint</code> is in the supplied cardinality. 290 * 291 * <p> 292 * A typical application for this would be with Annotations where 293 * one property can contain a number of synonyms. 294 * <code>CollectionConstraint.Contains</code> could be used as 295 * a query to select instances based on one of these synonyms. 296 * </p> 297 * 298 * @author Thomas Down 299 */ 300 301 public class Contains implements CollectionConstraint { 302 private PropertyConstraint pc; 303 private Location card; 304 305 /** 306 * Create a Contains based upon a PropertyConstraint and a 307 * cardinality. 308 * 309 * @param pc the PropertyConstraint to apply to each property value 310 * @param card the cardinality constraint restricting the number of 311 * values 312 */ 313 public Contains(PropertyConstraint pc, Location card) { 314 this.pc = pc; 315 this.card = card; 316 } 317 318 /** 319 * Get the PropertyConstraint used to validate each property value. 320 * 321 * @return the PropertyConstraint used 322 */ 323 public PropertyConstraint getPropertyConstraint() { 324 return pc; 325 } 326 327 /** 328 * Get the cardinality constraint used to validate the number of property 329 * values. 330 * 331 * @return the cardinality constraint as a Location 332 */ 333 public Location getCardinalityConstraint() { 334 return card; 335 } 336 337 public boolean accept(Object o) { 338 if (o instanceof Collection) { 339 return card.contains(countMembers((Collection) o)); 340 } else { 341 if (pc.accept(o)) { 342 return card.contains(1); 343 } else { 344 return card.contains(0); 345 } 346 } 347 } 348 349 private int countMembers(Collection co) { 350 int members = 0; 351 for (Iterator i = co.iterator(); i.hasNext(); ) { 352 if (pc.accept(i.next())) { 353 ++members; 354 } 355 } 356 return members; 357 } 358 359 public int hashCode() { 360 return pc.hashCode() + 178; 361 } 362 363 public boolean equals(Object o) { 364 if (o instanceof Contains) { 365 Contains avo = (Contains) o; 366 return avo.getCardinalityConstraint().equals(getCardinalityConstraint()) && 367 avo.getPropertyConstraint().equals(getPropertyConstraint()); 368 } else { 369 return false; 370 } 371 } 372 373 public boolean validateAddValue(Collection oldCol, Object newValue) { 374 int members = countMembers(oldCol); 375 if (pc.accept(newValue)) { 376 ++members; 377 } 378 return card.contains(members); 379 } 380 381 public boolean validateRemoveValue(Collection oldCol, Object newValue) { 382 int members = countMembers(oldCol); 383 if (pc.accept(newValue)) { 384 --members; 385 } 386 return card.contains(members); 387 } 388 389 390 public boolean subConstraintOf(CollectionConstraint cc) { 391 if (cc instanceof NoneCollectionConstraint) { 392 return true; 393 } else if (cc instanceof CollectionConstraint.AllValuesIn) { 394 AllValuesIn ccavi = (AllValuesIn) cc; 395 return getCardinalityConstraint().contains(ccavi.getCardinalityConstraint()) && 396 getPropertyConstraint().subConstraintOf(ccavi.getPropertyConstraint()); 397 } else if (cc instanceof CollectionConstraint.Contains) { 398 Contains ccavi = (Contains) cc; 399 return getCardinalityConstraint().contains(ccavi.getCardinalityConstraint()) && 400 getPropertyConstraint().subConstraintOf(ccavi.getPropertyConstraint()); 401 } else if (cc instanceof CollectionConstraint.And) { 402 And cca = (And) cc; 403 return subConstraintOf(cca.getChild1()) || subConstraintOf(cca.getChild2()); 404 } else if (cc instanceof CollectionConstraint.Or) { 405 Or cco = (Or) cc; 406 return subConstraintOf(cco.getChild1()) && subConstraintOf(cco.getChild2()); 407 } 408 return false; 409 } 410 411 public String toString() { 412 return "Contains: (" + pc.toString() + ", " + card.toString() + ")"; 413 } 414 } 415 416 417 /** 418 * A collection constraint that accpepts collections iff they are accepted by both 419 * child constraints. This effectively matches the intersection of the items 420 * matched by the two constraints. 421 * 422 * Use this to combine multiple constraints. You can make one 423 * or both of the children And instances if you need a tighter 424 * intersection. 425 * @author Matthew Pocock 426 * @author Thomas Down 427 */ 428 public class And implements CollectionConstraint { 429 private CollectionConstraint c1; 430 private CollectionConstraint c2; 431 432 /** 433 * Create a new <code>And</code> from two child constraints. 434 * 435 * @param c1 the first child 436 * @param c2 the seccond child 437 */ 438 public And(CollectionConstraint c1, CollectionConstraint c2) { 439 this.c1 = c1; 440 this.c2 = c2; 441 } 442 443 /** 444 * Get the first child CollectionConstraint. 445 * 446 * @return the first child CollectionConstraint 447 * 448 */ 449 public CollectionConstraint getChild1() { 450 return c1; 451 } 452 453 /** 454 * Get the seccond child CollectionConstraint. 455 * 456 * @return the seccond child CollectionConstraint 457 * 458 */ 459 public CollectionConstraint getChild2() { 460 return c2; 461 } 462 463 public boolean accept(Object object) { 464 return c1.accept(object) && c2.accept(object); 465 } 466 467 public boolean subConstraintOf(CollectionConstraint pc) { 468 return c1.subConstraintOf(pc) && c2.subConstraintOf(pc); 469 } 470 471 472 public boolean validateAddValue(Collection oldcoll, Object newvalue) { 473 return c1.validateAddValue(oldcoll, newvalue) && c2.validateAddValue(oldcoll, newvalue); 474 } 475 476 public boolean validateRemoveValue(Collection oldcoll, Object victim) { 477 return c1.validateAddValue(oldcoll, victim) && c2.validateAddValue(oldcoll, victim); 478 } 479 480 public String toString() { 481 return "And(" + c1 + ", " + c2 + ")"; 482 } 483 } 484 485 /** 486 * A collection constraint that accepts items iff they are accepted by either 487 * child constraints. This effectively matches the union of the items 488 * matched by the two constraints. Use this to combine multiple constraints. You can make one 489 * or both of the children Or instances if you need a wider 490 * union. 491 * 492 * @author Matthew Pocock 493 * @author Thomas Down 494 */ 495 public class Or implements CollectionConstraint { 496 private CollectionConstraint c1; 497 private CollectionConstraint c2; 498 499 /** 500 * Create a new <code>Or</code> from two child constraints. 501 * 502 * @param c1 the first child 503 * @param c2 the seccond child 504 */ 505 public Or(CollectionConstraint c1, CollectionConstraint c2) { 506 this.c1 = c1; 507 this.c2 = c2; 508 } 509 510 /** 511 * Get the first child CollectionConstraint. 512 * 513 * @return the first child CollectionConstraint 514 */ 515 public CollectionConstraint getChild1() { 516 return c1; 517 } 518 519 /** 520 * Get the seccond child CollectionConstraint. 521 * 522 * @return the seccond child CollectionConstraint 523 * 524 */ 525 public CollectionConstraint getChild2() { 526 return c2; 527 } 528 529 public boolean accept(Object object) { 530 return c1.accept(object) || c2.accept(object); 531 } 532 533 public boolean subConstraintOf(CollectionConstraint pc) { 534 return c1.subConstraintOf(pc) || c2.subConstraintOf(pc); 535 } 536 537 public boolean validateAddValue(Collection oldcoll, Object newvalue) { 538 return c1.validateAddValue(oldcoll, newvalue) || c2.validateAddValue(oldcoll, newvalue); 539 } 540 541 public boolean validateRemoveValue(Collection oldcoll, Object victim) { 542 return c1.validateAddValue(oldcoll, victim) || c2.validateAddValue(oldcoll, victim); 543 } 544 545 public String toString() { 546 return "Or(" + c1 + ", " + c2 + ")"; 547 } 548 } 549} 550