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.biojavax.bio.db.biosql; 023import java.lang.reflect.InvocationTargetException; 024import java.lang.reflect.Method; 025import java.util.Collection; 026import java.util.Collections; 027import java.util.HashMap; 028import java.util.Iterator; 029import java.util.Map; 030import java.util.NoSuchElementException; 031 032import org.biojava.bio.BioRuntimeException; 033import org.biojava.bio.seq.Feature; 034import org.biojava.bio.seq.FeatureFilter; 035import org.biojava.bio.seq.FilterUtils; 036import org.biojava.utils.walker.WalkerFactory; 037import org.biojavax.Note; 038import org.biojavax.RichAnnotation; 039import org.biojavax.RichObjectFactory; 040import org.biojavax.SimpleNote; 041import org.biojavax.bio.seq.RichFeature; 042import org.biojavax.bio.seq.RichLocation; 043import org.biojavax.bio.seq.RichLocation.Strand; 044import org.biojavax.ontology.ComparableTerm; 045 046 047 048/** 049 * A filter for accepting or rejecting a feature. 050 * 051 * <p> 052 * It is possible to write custom <code>FeatureFilter</code>s by implementing this 053 * interface. There are also a wide range of built-in features, and it is possible 054 * to build complex queries using <code>FeatureFilter.And</code>, <code>FeatureFilter.Or</code>, 055 * and <code>FeatureFilter.Not</code>. Where possible, use of the built-in filters 056 * is preferable to writing new filters, since the methods in the <code>FilterUtils</code> 057 * class have access to special knowledge about the built-in filter types and how they 058 * relate to one another. 059 * </p> 060 * 061 * <p> 062 * If the filter is to be used in a remote process, it is recognized that it may 063 * be serialized and sent over to run remotely, rather than each feature being 064 * retrieved locally. 065 * </p> 066 * 067 * <p> 068 * This class requires the Hibernate JAR files to be on your classpath at runtime. It is 069 * designed ONLY for use with BioSQLRichSequenceDB and BioSQLBioEntryDB. 070 * </p> 071 * 072 * @author Matthew Pocock 073 * @author Thomas Down 074 * @author Richard Holland 075 * @since 1.5 076 * @since 1.5 077 */ 078 079public interface BioSQLFeatureFilter extends FeatureFilter { 080 081 /** 082 * This method returns a Hibernate Criterion object that can be used to 083 * query the database. 084 * @return a Hibernate Criterion object representing this filter. 085 */ 086 public Object asCriterion(); 087 088 /** 089 * Returns a map of property names (keys) to aliases (values), if the criterion 090 * returned by asCriterion() uses aliases at all. If not, then it must at least 091 * return the empty map else you'll get NullPointerExceptions thrown elsewhere. 092 * @return Map a map of property names to aliases used in the criterion. 093 */ 094 public Map criterionAliasMap(); 095 096 /** 097 * A class representing some useful stuff you can do with BioSQLFeatureFilters, 098 * for instance converting plain FeatureFilters into a their BioSQLFeatureFilter 099 * equivalents (where possible). 100 */ 101 public static class Tools { 102 /** 103 * Convert a non-BioSQL FeatureFilter into a BioSQL one. We do this 104 * by walking through it, converting any ones we recognise into their 105 * BioSQLFeatureFilter equivalents. If we don't recognise them, we take 106 * special action. For the child of an And, we can just ignore the missing 107 * side and replace the And itself with the remaining side. For everything else, 108 * the entire FeatureFilter is replaced by BioSQLFeatureFilter.all else we 109 * run the risk of missing out potential candidates. 110 * The end result is a filter that can be applied to the 111 * database to filter out potential candidates for more rigorous selection 112 * in-memory by the default filter() method in AbstractRichSequenceDB. Whether or 113 * not the filter picks out everything correctly depends entirely on whether it 114 * is made up of BioSQLFeatureFilter elements, or can be converted into them. 115 */ 116 public static BioSQLFeatureFilter convert(FeatureFilter ff) { 117 // The easy case first. 118 if (ff instanceof BioSQLFeatureFilter) return (BioSQLFeatureFilter)ff; 119 else { 120 BioSQLFeatureFilter bff = attemptConversion(ff); 121 if (bff!=null) return bff; 122 else return BioSQLFeatureFilter.all; // catch-all case. 123 } 124 } 125 126 private static BioSQLFeatureFilter attemptConversion(FeatureFilter ff) { 127 // AND - convert both children. If both are convertible, return the And 128 // of them. If only one is, return just that child. If neither are, 129 // return null. 130 if (ff instanceof FeatureFilter.And) { 131 FeatureFilter.And ffand = (FeatureFilter.And)ff; 132 BioSQLFeatureFilter child1 = attemptConversion(ffand.getChild1()); 133 BioSQLFeatureFilter child2 = attemptConversion(ffand.getChild2()); 134 if (child1==null && child2==null) return null; 135 else if (child1==null && child2!=null) return child2; 136 else if (child1!=null && child2==null) return child1; 137 else return new BioSQLFeatureFilter.And(child1,child2); 138 } 139 // OR - convert both children. If both are convertible, return the Or 140 // of them. Otherwise, return null. 141 else if (ff instanceof FeatureFilter.Or) { 142 FeatureFilter.Or ffor = (FeatureFilter.Or)ff; 143 BioSQLFeatureFilter child1 = attemptConversion(ffor.getChild1()); 144 BioSQLFeatureFilter child2 = attemptConversion(ffor.getChild2()); 145 if (child1==null || child2==null) return null; 146 else return new BioSQLFeatureFilter.Or(child1,child2); 147 } 148 // NOT - convert the child. If convertible, return the Not of it. Else, 149 // return null. 150 else if (ff instanceof FeatureFilter.Not) { 151 FeatureFilter.Not ffnot = (FeatureFilter.Not)ff; 152 BioSQLFeatureFilter child = attemptConversion(ffnot.getChild()); 153 if (child==null) return null; 154 else return new BioSQLFeatureFilter.Not(child); 155 } 156 // BySource - convert the term to a Term from the default ontology then 157 // try BySourceTerm. 158 else if (ff instanceof FeatureFilter.BySource) { 159 FeatureFilter.BySource ffsrc = (FeatureFilter.BySource)ff; 160 String name = ffsrc.getSource(); 161 return new BioSQLFeatureFilter.BySourceTermName(name); 162 } 163 // ByType - convert the term to a Term from the default ontology then 164 // try ByTypeTerm. 165 else if (ff instanceof FeatureFilter.ByType) { 166 FeatureFilter.ByType ffsrc = (FeatureFilter.ByType)ff; 167 String name = ffsrc.getType(); 168 return new BioSQLFeatureFilter.ByTypeTermName(name); 169 } 170 // ContainedByLocation - simple pass-through 171 else if (ff instanceof FeatureFilter.ContainedByLocation) { 172 FeatureFilter.ContainedByLocation ffloc = (FeatureFilter.ContainedByLocation)ff; 173 return new BioSQLFeatureFilter.ContainedByRichLocation(RichLocation.Tools.enrich(ffloc.getLocation())); 174 } 175 // BySequenceName - simple pass-through 176 else if (ff instanceof FeatureFilter.BySequenceName) { 177 FeatureFilter.BySequenceName ffsn = (FeatureFilter.BySequenceName)ff; 178 return new BioSQLFeatureFilter.BySequenceName(ffsn.getSequenceName()); 179 } 180 // ShadowOverlapsLocation - simple pass-through to OverlapsRichLocation, as we have no concept 181 // of shadows within BioSQL so they are effectively the same thing. 182 else if (ff instanceof FeatureFilter.ShadowOverlapsLocation) { 183 FeatureFilter.ShadowOverlapsLocation ffloc = (FeatureFilter.ShadowOverlapsLocation)ff; 184 return new BioSQLFeatureFilter.OverlapsRichLocation(RichLocation.Tools.enrich(ffloc.getLocation())); 185 } 186 // AnnotationContains - attempt to convert the key to a ComparableTerm, and the value to a string (retrieve the 187 // sole member if it is a collection), then wrap the whole thing in a Note with rank 0 and try using 188 // ByNoteWithValue to retrieve. 189 else if (ff instanceof FeatureFilter.AnnotationContains) { 190 FeatureFilter.AnnotationContains ffann = (FeatureFilter.AnnotationContains)ff; 191 if (!(ffann.getValue() instanceof String)) return null; 192 String noteValue = (String)ffann.getValue(); 193 ComparableTerm noteTerm; 194 Object key = ffann.getKey(); 195 if (key instanceof Collection) { 196 Collection coll = (Collection)key; 197 if (coll.size()<1) return null; 198 else key = coll.toArray()[0]; 199 } 200 if (key instanceof ComparableTerm) noteTerm = (ComparableTerm)key; 201 else if (key instanceof String) noteTerm = RichObjectFactory.getDefaultOntology().getOrCreateTerm((String)key); 202 else return null; 203 return new BioSQLFeatureFilter.ByNote(new SimpleNote(noteTerm,noteValue,0)); 204 } 205 // StrandFilter - attempt to convert the StrandedFeature.Strand to a RichLocation.Strand then pass through 206 // to ByStrand. 207 else if (ff instanceof FeatureFilter.StrandFilter) { 208 FeatureFilter.StrandFilter ffstr = (FeatureFilter.StrandFilter)ff; 209 Strand strand = RichLocation.Strand.forName(""+ffstr.getStrand().getToken()); 210 return new BioSQLFeatureFilter.ByStrand(strand); 211 } 212 // HasAnnotation - attempt to convert the term into a ComparableTerm, then use ByNoteTermOnly. 213 else if (ff instanceof FeatureFilter.HasAnnotation) { 214 FeatureFilter.HasAnnotation ffann = (FeatureFilter.HasAnnotation)ff; 215 ComparableTerm noteTerm; 216 if (ffann.getKey() instanceof ComparableTerm) noteTerm = (ComparableTerm)ffann.getKey(); 217 else if (ffann.getKey() instanceof String) noteTerm = RichObjectFactory.getDefaultOntology().getOrCreateTerm((String)ffann.getKey()); 218 else return null; 219 return new BioSQLFeatureFilter.ByNoteTermOnly(noteTerm); 220 } 221 // ByAnnotation - attempt to convert the key to a ComparableTerm, and the value to a string, then wrap the 222 // whole thing in a Note with rank 0 and try using ByNoteWithValue to retrieve. 223 else if (ff instanceof FeatureFilter.ByAnnotation) { 224 FeatureFilter.ByAnnotation ffann = (FeatureFilter.ByAnnotation)ff; 225 if (!(ffann.getValue() instanceof String)) return null; 226 String noteValue = (String)ffann.getValue(); 227 ComparableTerm noteTerm; 228 Object key = ffann.getKey(); 229 if (key instanceof ComparableTerm) noteTerm = (ComparableTerm)key; 230 else if (key instanceof String) noteTerm = RichObjectFactory.getDefaultOntology().getOrCreateTerm((String)key); 231 else return null; 232 return new BioSQLFeatureFilter.ByNote(new SimpleNote(noteTerm,noteValue,0)); 233 } 234 // OverlapsLocation - simple pass-through to OverlapsRichLocation. 235 else if (ff instanceof FeatureFilter.OverlapsLocation) { 236 FeatureFilter.OverlapsLocation ffloc = (FeatureFilter.OverlapsLocation)ff; 237 return new BioSQLFeatureFilter.OverlapsRichLocation(RichLocation.Tools.enrich(ffloc.getLocation())); 238 } 239 // ShadowContainedByLocation - simple pass-through to ContainedByRichLocation, as we have no concept 240 // of shadows within BioSQL so they are effectively the same thing. 241 else if (ff instanceof FeatureFilter.ShadowContainedByLocation) { 242 FeatureFilter.ShadowContainedByLocation ffloc = (FeatureFilter.ShadowContainedByLocation)ff; 243 return new BioSQLFeatureFilter.ContainedByRichLocation(RichLocation.Tools.enrich(ffloc.getLocation())); 244 } 245 // Anything else we don't recognise? Return null! 246 else { 247 return null; 248 } 249 } 250 } 251 252 // Now for some useful filters. 253 254 /** 255 * All features are selected by this filter. 256 */ 257 static final public BioSQLFeatureFilter all = new BioSQLAcceptAllFilter(); 258 259 /** 260 * No features are selected by this filter. 261 */ 262 static final public BioSQLFeatureFilter none = new BioSQLAcceptNoneFilter(); 263 264 /** 265 * A filter for Hibernate-BioSQL filters to extend. 266 */ 267 public abstract static class HibernateFeatureFilter implements BioSQLFeatureFilter { 268 protected Method not; 269 protected Method and; 270 protected Method or; 271 protected Method eq; 272 protected Method le; 273 protected Method ge; 274 protected Method conjunction; 275 protected Method disjunction; 276 protected Method conjunctAdd; 277 protected Method disjunctAdd; 278 279 public HibernateFeatureFilter() { 280 try { 281 // Lazy load the Restrictions class from Hibernate. 282 Class restrictions = Class.forName("org.hibernate.criterion.Restrictions"); 283 // Lazy load the Criterion class from Hibernate. 284 Class criterion = Class.forName("org.hibernate.criterion.Criterion"); 285 // Lookup the methods 286 this.not = restrictions.getMethod("not", new Class[]{criterion}); 287 this.and = restrictions.getMethod("and", new Class[]{criterion,criterion}); 288 this.or = restrictions.getMethod("or", new Class[]{criterion,criterion}); 289 this.eq = restrictions.getMethod("eq", new Class[]{String.class,Object.class}); 290 this.le = restrictions.getMethod("le", new Class[]{String.class,Object.class}); 291 this.ge = restrictions.getMethod("ge", new Class[]{String.class,Object.class}); 292 this.conjunction = restrictions.getMethod("conjunction", new Class[]{}); 293 this.disjunction = restrictions.getMethod("disjunction", new Class[]{}); 294 // Lazy load the Conjunction(Or)+Disjunction(And) class from Hibernate. 295 Class conjunctClass = Class.forName("org.hibernate.criterion.Conjunction"); 296 Class disjunctClass = Class.forName("org.hibernate.criterion.Disjunction"); 297 // Lookup the methods 298 this.conjunctAdd = conjunctClass.getMethod("add", new Class[]{criterion}); 299 this.disjunctAdd = disjunctClass.getMethod("add", new Class[]{criterion}); 300 } catch (ClassNotFoundException e) { 301 throw new RuntimeException(e); 302 } catch (NoSuchMethodException e) { 303 throw new RuntimeException(e); 304 } 305 } 306 307 public Map criterionAliasMap() { 308 return Collections.EMPTY_MAP; 309 } 310 } 311 312 /** 313 * A filter that returns all features not accepted by a child filter. 314 * 315 * @author Thomas Down 316 * @author Matthew Pocock 317 * @author Richard Holland 318 * @since 1.5 319 */ 320 public final static class Not extends HibernateFeatureFilter { 321 static { WalkerFactory.getInstance().addTypeWithParent(Not.class); } 322 323 BioSQLFeatureFilter child; 324 325 public BioSQLFeatureFilter getChild() { 326 return child; 327 } 328 329 public Not(BioSQLFeatureFilter child) { 330 super(); 331 if (!(child instanceof BioSQLFeatureFilter)) 332 throw new BioRuntimeException("Cannot use non-BioSQLFeatureFilter instances with this class"); 333 this.child = child; 334 } 335 336 public boolean accept(Feature f) { 337 return !(child.accept(f)); 338 } 339 340 public Object asCriterion() { 341 try { 342 return this.not.invoke(null,new Object[]{child.asCriterion()}); 343 } catch (InvocationTargetException e) { 344 throw new RuntimeException(e); 345 } catch (IllegalAccessException e) { 346 throw new RuntimeException(e); 347 } 348 } 349 350 public Map criterionAliasMap() { 351 return child.criterionAliasMap(); 352 } 353 354 public boolean equals(Object o) { 355 return 356 (o instanceof Not) && 357 (((Not) o).getChild().equals(this.getChild())); 358 } 359 360 public int hashCode() { 361 return getChild().hashCode(); 362 } 363 364 public String toString() { 365 return "Not(" + child + ")"; 366 } 367 } 368 369 370 /** 371 * A filter that returns all features accepted by both child filter. 372 * 373 * @author Thomas Down 374 * @author Matthew Pocock 375 * @author Richard Holland 376 * @since 1.5 377 */ 378 public final static class And extends HibernateFeatureFilter { 379 static { WalkerFactory.getInstance().addTypeWithParent(And.class); } 380 381 BioSQLFeatureFilter c1, c2; 382 383 public BioSQLFeatureFilter getChild1() { 384 return c1; 385 } 386 387 public BioSQLFeatureFilter getChild2() { 388 return c2; 389 } 390 391 public And(BioSQLFeatureFilter c1, BioSQLFeatureFilter c2) { 392 super(); 393 if (!(c1 instanceof BioSQLFeatureFilter) || !(c2 instanceof BioSQLFeatureFilter)) 394 throw new BioRuntimeException("Cannot use non-BioSQLFeatureFilter instances with this class"); 395 this.c1 = c1; 396 this.c2 = c2; 397 } 398 399 public boolean accept(Feature f) { 400 return (c1.accept(f) && c2.accept(f)); 401 } 402 403 public Object asCriterion() { 404 try { 405 return this.and.invoke(null,new Object[]{c1.asCriterion(),c2.asCriterion()}); 406 } catch (InvocationTargetException e) { 407 throw new RuntimeException(e); 408 } catch (IllegalAccessException e) { 409 throw new RuntimeException(e); 410 } 411 } 412 413 public Map criterionAliasMap() { 414 Map results = new HashMap(); 415 results.putAll(c1.criterionAliasMap()); 416 results.putAll(c2.criterionAliasMap()); 417 return results; 418 } 419 420 public boolean equals(Object o) { 421 if(o instanceof BioSQLFeatureFilter) { 422 return FilterUtils.areEqual(this, (FeatureFilter) o); 423 } else { 424 return false; 425 } 426 } 427 428 public int hashCode() { 429 return getChild1().hashCode() ^ getChild2().hashCode(); 430 } 431 432 public String toString() { 433 return "And(" + c1 + " , " + c2 + ")"; 434 } 435 } 436 437 /** 438 * A filter that returns all features accepted by at least one child filter. 439 * 440 * @author Thomas Down 441 * @author Matthew Pocock 442 * @author Richard Holland 443 * @since 1.5 444 */ 445 public final static class Or extends HibernateFeatureFilter { 446 static { WalkerFactory.getInstance().addTypeWithParent(Or.class); } 447 448 BioSQLFeatureFilter c1, c2; 449 450 public BioSQLFeatureFilter getChild1() { 451 return c1; 452 } 453 454 public BioSQLFeatureFilter getChild2() { 455 return c2; 456 } 457 458 public Or(BioSQLFeatureFilter c1, BioSQLFeatureFilter c2) { 459 super(); 460 if (!(c1 instanceof BioSQLFeatureFilter) || !(c2 instanceof BioSQLFeatureFilter)) 461 throw new BioRuntimeException("Cannot use non-BioSQLFeatureFilter instances with this class"); 462 this.c1 = c1; 463 this.c2 = c2; 464 } 465 466 public boolean accept(Feature f) { 467 return (c1.accept(f) || c2.accept(f)); 468 } 469 470 public Object asCriterion() { 471 try { 472 return this.or.invoke(null,new Object[]{c1.asCriterion(),c2.asCriterion()}); 473 } catch (InvocationTargetException e) { 474 throw new RuntimeException(e); 475 } catch (IllegalAccessException e) { 476 throw new RuntimeException(e); 477 } 478 } 479 480 public Map criterionAliasMap() { 481 Map results = new HashMap(); 482 results.putAll(c1.criterionAliasMap()); 483 results.putAll(c2.criterionAliasMap()); 484 return results; 485 } 486 487 public boolean equals(Object o) { 488 if(o instanceof BioSQLFeatureFilter) { 489 return FilterUtils.areEqual(this, (FeatureFilter) o); 490 } else { 491 return false; 492 } 493 } 494 495 public int hashCode() { 496 return getChild1().hashCode() ^ getChild2().hashCode(); 497 } 498 499 public String toString() { 500 return "Or(" + c1 + " , " + c2 + ")"; 501 } 502 } 503 504 /** 505 * Construct one of these to filter features by display name. 506 * 507 * @author Richard Holland 508 * @since 1.5 509 */ 510 final public static class ByName extends HibernateFeatureFilter { 511 private String name; 512 513 public String getName() { 514 return name; 515 } 516 517 /** 518 * Create a ByType filter that filters in all features with type fields 519 * equal to type. 520 * 521 * @param name the String to match type fields against 522 */ 523 public ByName(String name) { 524 super(); 525 if (name == null) { 526 throw new NullPointerException("Name may not be null"); 527 } 528 this.name = name; 529 } 530 531 /** 532 * Returns true if the feature has a matching type property. 533 */ 534 public boolean accept(Feature f) { 535 if (f instanceof RichFeature) { 536 return name.equals(((RichFeature)f).getName()); 537 } 538 return false; 539 } 540 541 public Object asCriterion() { 542 try { 543 return this.eq.invoke(null,new Object[]{"name",name}); 544 } catch (InvocationTargetException e) { 545 throw new RuntimeException(e); 546 } catch (IllegalAccessException e) { 547 throw new RuntimeException(e); 548 } 549 } 550 551 public boolean equals(Object o) { 552 return 553 (o instanceof ByName) && 554 (((ByName) o).getName().equals(this.getName())); 555 } 556 557 public int hashCode() { 558 return getName().hashCode(); 559 } 560 561 public String toString() { 562 return "ByName(" + name + ")"; 563 } 564 } 565 566 /** 567 * Construct one of these to filter features by rank. 568 * 569 * @author Richard Holland 570 * @since 1.5 571 */ 572 final public static class ByRank extends HibernateFeatureFilter { 573 private int rank; 574 575 public int getRank() { 576 return rank; 577 } 578 579 /** 580 * Create a Rank filter that filters in all features with rank fields 581 * equal to rank. 582 * 583 * @param rank the rank to match type fields against 584 */ 585 public ByRank(int rank) { 586 super(); 587 this.rank = rank; 588 } 589 590 /** 591 * Returns true if the feature has a matching type property. 592 */ 593 public boolean accept(Feature f) { 594 if (f instanceof RichFeature) { 595 return rank==((RichFeature)f).getRank(); 596 } 597 return false; 598 } 599 600 public Object asCriterion() { 601 try { 602 return this.eq.invoke(null,new Object[]{"rank",new Integer(rank)}); 603 } catch (InvocationTargetException e) { 604 throw new RuntimeException(e); 605 } catch (IllegalAccessException e) { 606 throw new RuntimeException(e); 607 } 608 } 609 610 public boolean equals(Object o) { 611 return 612 (o instanceof ByRank) && 613 (((ByRank) o).getRank() == this.getRank()); 614 } 615 616 public int hashCode() { 617 return rank; 618 } 619 620 public String toString() { 621 return "ByRank(" + rank + ")"; 622 } 623 } 624 625 /** 626 * Construct one of these to filter features by type. 627 * 628 * @author Matthew Pocock 629 * @author Richard Holland 630 * @since 1.5 631 */ 632 final public static class ByTypeTerm extends HibernateFeatureFilter { 633 private ComparableTerm typeTerm; 634 635 public ComparableTerm getTypeTerm() { 636 return typeTerm; 637 } 638 639 /** 640 * Create a ByTypeTerm filter that filters in all features with typeTerm fields 641 * equal to typeTerm. 642 * 643 * @param typeTerm the Term to match typeTerm fields against 644 */ 645 public ByTypeTerm(ComparableTerm typeTerm) { 646 super(); 647 if (typeTerm == null) { 648 throw new NullPointerException("Type may not be null"); 649 } 650 this.typeTerm = typeTerm; 651 } 652 653 /** 654 * Returns true if the feature has a matching type property. 655 */ 656 public boolean accept(Feature f) { 657 return typeTerm.equals(f.getTypeTerm()); 658 } 659 660 public Object asCriterion() { 661 try { 662 return this.eq.invoke(null,new Object[]{"typeTerm",typeTerm}); 663 } catch (InvocationTargetException e) { 664 throw new RuntimeException(e); 665 } catch (IllegalAccessException e) { 666 throw new RuntimeException(e); 667 } 668 } 669 670 public boolean equals(Object o) { 671 return 672 (o instanceof ByTypeTerm) && 673 (((ByTypeTerm) o).getTypeTerm().equals(this.getTypeTerm())); 674 } 675 676 public int hashCode() { 677 return getTypeTerm().hashCode(); 678 } 679 680 public String toString() { 681 return "ByTypeTerm(" + typeTerm + ")"; 682 } 683 } 684 685 686 /** 687 * Construct one of these to filter features by source. 688 * 689 * @author Matthew Pocock 690 * @author Richard Holland 691 * @since 1.5 692 */ 693 final public static class BySourceTerm extends HibernateFeatureFilter { 694 private ComparableTerm sourceTerm; 695 696 public ComparableTerm getSourceTerm() { 697 return sourceTerm; 698 } 699 700 /** 701 * Create a BySourceTerm filter that filters in all features with sourceTerm fields 702 * equal to source. 703 * 704 * @param sourceTerm the Term to match sourceTerm fields against 705 */ 706 public BySourceTerm(ComparableTerm sourceTerm) { 707 super(); 708 if (sourceTerm == null) { 709 throw new NullPointerException("Source may not be null"); 710 } 711 this.sourceTerm = sourceTerm; 712 } 713 714 /** 715 * Returns true if the feature has a matching source property. 716 */ 717 public boolean accept(Feature f) { 718 return sourceTerm.equals(f.getSourceTerm()); 719 } 720 721 public Object asCriterion() { 722 try { 723 return this.eq.invoke(null,new Object[]{"sourceTerm",sourceTerm}); 724 } catch (InvocationTargetException e) { 725 throw new RuntimeException(e); 726 } catch (IllegalAccessException e) { 727 throw new RuntimeException(e); 728 } 729 } 730 731 public boolean equals(Object o) { 732 return 733 (o instanceof BySourceTerm) && 734 (((BySourceTerm) o).getSourceTerm().equals(this.getSourceTerm())); 735 } 736 737 public int hashCode() { 738 return getSourceTerm().hashCode(); 739 } 740 741 public String toString() { 742 return "BySourceTerm(" + sourceTerm + ")"; 743 } 744 } 745 746 /** 747 * Construct one of these to filter features by type (name only - parent ontology 748 * is ignored). 749 * 750 * @author Richard Holland 751 * @since 1.5 752 */ 753 final public static class ByTypeTermName extends HibernateFeatureFilter { 754 private String typeTermName; 755 756 public String getTypeTermName() { 757 return typeTermName; 758 } 759 760 /** 761 * Create a ByTypeTermName filter that filters in all features with typeTerm fields 762 * having name equal to typeTermName. 763 * 764 * @param typeTermName the Term to match typeTermName fields against 765 */ 766 public ByTypeTermName(String typeTermName) { 767 super(); 768 if (typeTermName == null) { 769 throw new NullPointerException("Type name may not be null"); 770 } 771 this.typeTermName = typeTermName; 772 } 773 774 /** 775 * Returns true if the feature has a matching type property. 776 */ 777 public boolean accept(Feature f) { 778 return typeTermName.equals(f.getTypeTerm().getName()); 779 } 780 781 public Object asCriterion() { 782 try { 783 return this.eq.invoke(null,new Object[]{"tt.name",typeTermName}); 784 } catch (InvocationTargetException e) { 785 throw new RuntimeException(e); 786 } catch (IllegalAccessException e) { 787 throw new RuntimeException(e); 788 } 789 } 790 791 public Map criterionAliasMap() { 792 Map results = new HashMap(); 793 results.put("typeTerm","tt"); 794 return results; 795 } 796 797 public boolean equals(Object o) { 798 return 799 (o instanceof ByTypeTermName) && 800 (((ByTypeTermName) o).getTypeTermName().equals(this.getTypeTermName())); 801 } 802 803 public int hashCode() { 804 return getTypeTermName().hashCode(); 805 } 806 807 public String toString() { 808 return "ByTypeTermName(" + typeTermName + ")"; 809 } 810 } 811 812 813 /** 814 * Construct one of these to filter features by source (name only - parent ontology 815 * is ignored). 816 * 817 * @author Richard Holland 818 * @since 1.5 819 */ 820 final public static class BySourceTermName extends HibernateFeatureFilter { 821 private String sourceTermName; 822 823 public String getSourceTermName() { 824 return sourceTermName; 825 } 826 827 /** 828 * Create a BySourceTerm filter that filters in all features with sourceTerm fields 829 * having name equal to sourceTermName. 830 * 831 * @param sourceTermName the name of the Term to match sourceTerm fields against 832 */ 833 public BySourceTermName(String sourceTermName) { 834 super(); 835 if (sourceTermName == null) { 836 throw new NullPointerException("Source name may not be null"); 837 } 838 this.sourceTermName = sourceTermName; 839 } 840 841 /** 842 * Returns true if the feature has a matching source property. 843 */ 844 public boolean accept(Feature f) { 845 return sourceTermName.equals(f.getSourceTerm().getName()); 846 } 847 848 public Object asCriterion() { 849 try { 850 return this.eq.invoke(null,new Object[]{"st.name",sourceTermName}); 851 } catch (InvocationTargetException e) { 852 throw new RuntimeException(e); 853 } catch (IllegalAccessException e) { 854 throw new RuntimeException(e); 855 } 856 } 857 858 public Map criterionAliasMap() { 859 Map results = new HashMap(); 860 results.put("sourceTerm","st"); 861 return results; 862 } 863 864 public boolean equals(Object o) { 865 return 866 (o instanceof BySourceTermName) && 867 (((BySourceTermName) o).getSourceTermName().equals(this.getSourceTermName())); 868 } 869 870 public int hashCode() { 871 return getSourceTermName().hashCode(); 872 } 873 874 public String toString() { 875 return "BySourceTermName(" + sourceTermName + ")"; 876 } 877 } 878 879 /** 880 * Accept features that reside on a sequence with a particular name. 881 * 882 * @author Matthew Pocock 883 * @author Richard Holland 884 * @since 1.5 885 */ 886 public final static class BySequenceName extends HibernateFeatureFilter { 887 private String seqName; 888 889 public BySequenceName(String seqName) { 890 super(); 891 this.seqName = seqName; 892 } 893 894 public String getSequenceName() { 895 return seqName; 896 } 897 898 public boolean accept(Feature f) { 899 return f.getSequence().getName().equals(seqName); 900 } 901 902 public Object asCriterion() { 903 try { 904 return this.eq.invoke(null,new Object[]{"p.name",seqName}); 905 } catch (InvocationTargetException e) { 906 throw new RuntimeException(e); 907 } catch (IllegalAccessException e) { 908 throw new RuntimeException(e); 909 } 910 } 911 912 public Map criterionAliasMap() { 913 Map results = new HashMap(); 914 results.put("parent","p"); 915 return results; 916 } 917 918 public boolean equals(Object o) { 919 return 920 (o instanceof BySequenceName) && 921 ((BySequenceName) o).getSequenceName().equals(seqName); 922 } 923 924 public int hashCode() { 925 return seqName.hashCode(); 926 } 927 } 928 929 /** 930 * A filter that returns all features contained within a location. Contained means 931 * that a feature is entirely within, on the same strand and on the same sequence 932 * as any single member of the flattened query location. 933 * 934 * @author Matthew Pocock 935 * @author Richard Holland 936 * @since 1.5 937 */ 938 public final static class ContainedByRichLocation extends HibernateFeatureFilter { 939 private RichLocation loc; 940 941 public RichLocation getRichLocation() { 942 return loc; 943 } 944 945 /** 946 * Creates a filter that returns everything contained within loc. 947 * 948 * @param loc the location that will contain the accepted features 949 */ 950 public ContainedByRichLocation(RichLocation loc) { 951 super(); 952 if (loc == null) { 953 throw new NullPointerException("Loc may not be null"); 954 } 955 this.loc = loc; 956 } 957 958 /** 959 * Returns true if the feature is within this filter's location. 960 */ 961 public boolean accept(Feature f) { 962 return loc.contains(f.getLocation()); 963 } 964 965 public Object asCriterion() { 966 try { 967 // Conjunction of criteria for each member of the query location. 968 Collection members = RichLocation.Tools.flatten(loc); 969 // some combo of Tools.flatten(loc), min(loc.start,feat.start) and min(loc.end,feat.end) 970 Object parentConjunct = this.conjunction.invoke((Object[])null,(Object[])null); 971 for (Iterator i = members.iterator(); i.hasNext(); ) { 972 RichLocation loc = (RichLocation)i.next(); 973 Object childDisjunct = this.disjunction.invoke((Object[])null,(Object[])null); 974 // for each member, find features that have start>=member.start, 975 // end<=member.end and strand=member.strand and crossref=member.crossref 976 this.disjunctAdd.invoke(childDisjunct,new Object[]{this.eq.invoke(null, new Object[]{"l.strandNum",new Integer(loc.getStrand().intValue())})}); 977 this.disjunctAdd.invoke(childDisjunct,new Object[]{this.eq.invoke(null, new Object[]{"l.crossRef",loc.getCrossRef()})}); 978 this.disjunctAdd.invoke(childDisjunct,new Object[]{this.ge.invoke(null, new Object[]{"l.min",new Integer(loc.getMin())})}); 979 this.disjunctAdd.invoke(childDisjunct,new Object[]{this.le.invoke(null, new Object[]{"l.max",new Integer(loc.getMax())})}); 980 // add the member to the set of restrictions 981 this.conjunctAdd.invoke(parentConjunct,new Object[]{childDisjunct}); 982 } 983 return parentConjunct; 984 } catch (InvocationTargetException e) { 985 throw new RuntimeException(e); 986 } catch (IllegalAccessException e) { 987 throw new RuntimeException(e); 988 } 989 } 990 991 public Map criterionAliasMap() { 992 Map results = new HashMap(); 993 results.put("locationSet","l"); 994 return results; 995 } 996 997 public boolean equals(Object o) { 998 return 999 (o instanceof ContainedByRichLocation) && 1000 (((ContainedByRichLocation) o).getRichLocation().equals(this.getRichLocation())); 1001 } 1002 1003 public int hashCode() { 1004 return getRichLocation().hashCode(); 1005 } 1006 1007 public String toString() { 1008 return "ContainedBy(" + loc + ")"; 1009 } 1010 } 1011 1012 /** 1013 * A filter that returns all features having locations on a given strand. They 1014 * may actually have features on other strands too, of course. 1015 * 1016 * @author Richard Holland 1017 * @since 1.5 1018 */ 1019 public final static class ByStrand extends HibernateFeatureFilter { 1020 private Strand str; 1021 1022 public Strand getStrand() { 1023 return str; 1024 } 1025 1026 /** 1027 * Creates a filter that returns everything on strand str. 1028 * 1029 * @param str the strand that will contain the accepted features 1030 */ 1031 public ByStrand(Strand str) { 1032 super(); 1033 if (str == null) { 1034 throw new NullPointerException("Strand may not be null"); 1035 } 1036 this.str = str; 1037 } 1038 1039 /** 1040 * Returns true if the feature overlaps this filter's location. 1041 */ 1042 public boolean accept(Feature f) { 1043 if (f instanceof RichFeature) { 1044 RichFeature rf = (RichFeature)f; 1045 for (Iterator i = rf.getLocation().blockIterator(); i.hasNext(); ) { 1046 RichLocation l = (RichLocation)i.next(); 1047 if (l.getStrand().equals(str)) return true; 1048 } 1049 } 1050 return false; 1051 } 1052 1053 public Object asCriterion() { 1054 try { 1055 // any location on the feature with a matching strand? 1056 return this.eq.invoke(null,new Object[]{"l.strandNum",new Integer(str.intValue())}); 1057 } catch (InvocationTargetException e) { 1058 throw new RuntimeException(e); 1059 } catch (IllegalAccessException e) { 1060 throw new RuntimeException(e); 1061 } 1062 } 1063 1064 public Map criterionAliasMap() { 1065 Map results = new HashMap(); 1066 results.put("locationSet","l"); 1067 return results; 1068 } 1069 1070 public boolean equals(Object o) { 1071 return 1072 (o instanceof ByStrand) && 1073 (((ByStrand) o).getStrand().equals(this.getStrand())); 1074 } 1075 1076 public int hashCode() { 1077 return getStrand().hashCode(); 1078 } 1079 1080 public String toString() { 1081 return "ByStrand(" + str + ")"; 1082 } 1083 } 1084 1085 /** 1086 * A filter that returns all features overlapping a location. Overlaps means 1087 * that a feature includes part of, on the same strand and on the same sequence 1088 * any single member of the flattened query location. 1089 * 1090 * @author Matthew Pocock 1091 * @author Richard Holland 1092 * @since 1.5 1093 */ 1094 public final static class OverlapsRichLocation extends HibernateFeatureFilter { 1095 private RichLocation loc; 1096 1097 public RichLocation getRichLocation() { 1098 return loc; 1099 } 1100 1101 /** 1102 * Creates a filter that returns everything overlapping loc. 1103 * 1104 * @param loc the location that will overlap the accepted features 1105 */ 1106 public OverlapsRichLocation(RichLocation loc) { 1107 super(); 1108 if (loc == null) { 1109 throw new NullPointerException("Loc may not be null"); 1110 } 1111 this.loc = loc; 1112 } 1113 1114 /** 1115 * Returns true if the feature overlaps this filter's location. 1116 */ 1117 public boolean accept(Feature f) { 1118 return loc.overlaps(f.getLocation()); 1119 } 1120 1121 public Object asCriterion() { 1122 try { 1123 // Conjunction of criteria for each member of the query location. 1124 Collection members = RichLocation.Tools.flatten(loc); 1125 // some combo of Tools.flatten(loc), min(loc.start,feat.start) and min(loc.end,feat.end) 1126 Object parentConjunct = this.conjunction.invoke((Object[])null,(Object[])null); 1127 for (Iterator i = members.iterator(); i.hasNext(); ) { 1128 RichLocation loc = (RichLocation)i.next(); 1129 Object childDisjunct = this.disjunction.invoke((Object[])null,(Object[])null); 1130 // for each member, find features that have start<=member.end, end>=member.start, 1131 // strand=member.strand and crossref=member.crossref 1132 this.disjunctAdd.invoke(childDisjunct,new Object[]{this.eq.invoke(null, new Object[]{"l.strandNum",new Integer(loc.getStrand().intValue())})}); 1133 this.disjunctAdd.invoke(childDisjunct,new Object[]{this.eq.invoke(null, new Object[]{"l.crossRef",loc.getCrossRef()})}); 1134 this.disjunctAdd.invoke(childDisjunct,new Object[]{this.ge.invoke(null, new Object[]{"l.max",new Integer(loc.getMin())})}); 1135 this.disjunctAdd.invoke(childDisjunct,new Object[]{this.le.invoke(null, new Object[]{"l.min",new Integer(loc.getMax())})}); 1136 // add the member to the set of restrictions 1137 this.conjunctAdd.invoke(parentConjunct,new Object[]{childDisjunct}); 1138 } 1139 return parentConjunct; 1140 } catch (InvocationTargetException e) { 1141 throw new RuntimeException(e); 1142 } catch (IllegalAccessException e) { 1143 throw new RuntimeException(e); 1144 } 1145 } 1146 1147 public Map criterionAliasMap() { 1148 Map results = new HashMap(); 1149 results.put("locationSet","l"); 1150 return results; 1151 } 1152 1153 public boolean equals(Object o) { 1154 return 1155 (o instanceof OverlapsRichLocation) && 1156 (((OverlapsRichLocation) o).getRichLocation().equals(this.getRichLocation())); 1157 } 1158 1159 public int hashCode() { 1160 return getRichLocation().hashCode(); 1161 } 1162 1163 public String toString() { 1164 return "Overlaps(" + loc + ")"; 1165 } 1166 } 1167 1168 /** 1169 * A filter that returns all features that have the given note, and 1170 * the value and rank is checked as well. 1171 * 1172 * @author Richard Holland 1173 * @author George Waldon 1174 * @since 1.5 1175 */ 1176 public final static class ByNote extends HibernateFeatureFilter { 1177 private Note note; 1178 1179 public ByNote(Note note) { 1180 super(); 1181 this.note = note; 1182 } 1183 1184 public Note getNote() { 1185 return note; 1186 } 1187 1188 public boolean accept(Feature f) { 1189 if (f instanceof RichFeature) { 1190 RichAnnotation ra = ((RichFeature)f).getRichAnnotation(); 1191 try { 1192 Note n = ra.getNote(note); 1193 return (n.getValue()==note.getValue()) || (n.getValue()!=null && note.getValue()!=null && n.getValue().equals(note.getValue())); 1194 } catch (NoSuchElementException e) { 1195 return false; 1196 } 1197 } 1198 return false; 1199 } 1200 1201 public Object asCriterion() { 1202 try { 1203 Object conjunct = this.conjunction.invoke((Object[])null,(Object[])null); 1204 this.conjunctAdd.invoke(conjunct,new Object[]{this.eq.invoke(null, new Object[]{"n.term",note.getTerm()})}); 1205 this.conjunctAdd.invoke(conjunct,new Object[]{this.eq.invoke(null, new Object[]{"n.value",note.getValue()})}); 1206 this.conjunctAdd.invoke(conjunct,new Object[]{this.eq.invoke(null, new Object[]{"n.rank",new Integer(note.getRank())})}); 1207 return conjunct; 1208 } catch (InvocationTargetException e) { 1209 throw new RuntimeException(e); 1210 } catch (IllegalAccessException e) { 1211 throw new RuntimeException(e); 1212 } 1213 } 1214 1215 public Map criterionAliasMap() { 1216 Map results = new HashMap(); 1217 results.put("noteSet","n"); 1218 return results; 1219 } 1220 1221 public boolean equals(Object o) { 1222 if(o instanceof ByNote) { 1223 ByNote that = (ByNote) o; 1224 return this.getNote() == that.getNote(); 1225 } 1226 1227 return false; 1228 } 1229 1230 public int hashCode() { 1231 return getNote().hashCode(); 1232 } 1233 1234 public String toString() { 1235 return "ByNote {" + note + "}"; 1236 } 1237 } 1238 1239 /** 1240 * A filter that returns all features that have a note with the given term. The value 1241 * and rank is not checked. 1242 * 1243 * @author Richard Holland 1244 * @author George Waldon 1245 * @since 1.5 1246 */ 1247 public final static class ByNoteTermOnly extends HibernateFeatureFilter { 1248 private ComparableTerm term; 1249 1250 public ByNoteTermOnly(ComparableTerm term) { 1251 super(); 1252 this.term = term; 1253 } 1254 1255 public ComparableTerm getTerm() { 1256 return term; 1257 } 1258 1259 public boolean accept(Feature f) { 1260 if (f instanceof RichFeature) { 1261 RichAnnotation ra = ((RichFeature)f).getRichAnnotation(); 1262 try { 1263 for (Iterator i = ra.getNoteSet().iterator(); i.hasNext(); ) if (((Note)i.next()).getTerm().equals(term)) return true; 1264 } catch (NoSuchElementException e) { 1265 return false; 1266 } 1267 } 1268 return false; 1269 } 1270 1271 public Object asCriterion() { 1272 try { 1273 return this.eq.invoke(null, new Object[]{"n.term",term}); 1274 } catch (InvocationTargetException e) { 1275 throw new RuntimeException(e); 1276 } catch (IllegalAccessException e) { 1277 throw new RuntimeException(e); 1278 } 1279 } 1280 1281 public Map criterionAliasMap() { 1282 Map results = new HashMap(); 1283 results.put("noteSet","n"); 1284 return results; 1285 } 1286 1287 public boolean equals(Object o) { 1288 if(o instanceof ByNoteTermOnly) { 1289 ByNoteTermOnly that = (ByNoteTermOnly) o; 1290 return this.getTerm() == that.getTerm(); 1291 } 1292 1293 return false; 1294 } 1295 1296 public int hashCode() { 1297 return getTerm().hashCode(); 1298 } 1299 1300 public String toString() { 1301 return "ByNoteTermOnly {" + term + "}"; 1302 } 1303 } 1304} 1305