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.seq; 023 024import java.io.Serializable; 025import java.util.HashSet; 026import java.util.Iterator; 027import java.util.Set; 028 029import org.biojava.bio.AnnotationTools; 030import org.biojava.bio.AnnotationType; 031import org.biojava.bio.CardinalityConstraint; 032import org.biojava.bio.CollectionConstraint; 033import org.biojava.bio.PropertyConstraint; 034import org.biojava.bio.seq.homol.SimilarityPairFeature; 035import org.biojava.bio.symbol.Location; 036import org.biojava.bio.symbol.RangeLocation; 037import org.biojava.utils.walker.WalkerFactory; 038 039/** 040 * A filter for accepting or rejecting a feature. 041 * 042 * <p> 043 * It is possible to write custom <code>FeatureFilter</code>s by implementing this 044 * interface. There are also a wide range of built-in features, and it is possible 045 * to build complex queries using <code>FeatureFilter.And</code>, <code>FeatureFilter.Or</code>, 046 * and <code>FeatureFilter.Not</code>. Where possible, use of the built-in filters 047 * is preferable to writing new filters, since the methods in the <code>FilterUtils</code> 048 * class have access to special knowledge about the built-in filter types and how they 049 * relate to one another. 050 * </p> 051 * 052 * <p> 053 * If the filter is to be used in a remote process, it is recognized that it may 054 * be serialized and sent over to run remotely, rather than each feature being 055 * retrieved locally. 056 * </p> 057 * 058 * @since 1.0 059 * @author Matthew Pocock 060 * @author Thomas Down 061 */ 062 063public interface FeatureFilter extends Serializable { 064 /** 065 * This method determines whether a feature is to be accepted. 066 * 067 * @param f the Feature to evaluate 068 * @return true if this feature is to be selected in, or false if it is to be ignored 069 */ 070 boolean accept(Feature f); 071 072 /** 073 * All features are selected by this filter. 074 */ 075 static final public FeatureFilter all = new AcceptAllFilter(); 076 077 /** 078 * No features are selected by this filter. 079 */ 080 static final public FeatureFilter none = new AcceptNoneFilter(); 081 082 083 /** 084 * A filter that returns all features not accepted by a child filter. 085 * 086 * @author Thomas Down 087 * @author Matthew Pocock 088 * @since 1.0 089 */ 090 public final static class Not implements FeatureFilter { 091 static { WalkerFactory.getInstance().addTypeWithParent(Not.class); } 092 093 FeatureFilter child; 094 095 public FeatureFilter getChild() { 096 return child; 097 } 098 099 public Not(FeatureFilter child) { 100 this.child = child; 101 } 102 103 public boolean accept(Feature f) { 104 return !(child.accept(f)); 105 } 106 107 public boolean equals(Object o) { 108 return 109 (o instanceof Not) && 110 (((Not) o).getChild().equals(this.getChild())); 111 } 112 113 public int hashCode() { 114 return getChild().hashCode(); 115 } 116 117 public String toString() { 118 return "Not(" + child + ")"; 119 } 120 } 121 122 /** 123 * A filter that returns all features accepted by both child filter. 124 * 125 * @author Thomas Down 126 * @author Matthew Pocock 127 * @since 1.0 128 */ 129 public final static class And implements FeatureFilter { 130 static { WalkerFactory.getInstance().addTypeWithParent(And.class); } 131 132 FeatureFilter c1, c2; 133 134 public FeatureFilter getChild1() { 135 return c1; 136 } 137 138 public FeatureFilter getChild2() { 139 return c2; 140 } 141 142 public And(FeatureFilter c1, FeatureFilter c2) { 143 this.c1 = c1; 144 this.c2 = c2; 145 } 146 147 public boolean accept(Feature f) { 148 return (c1.accept(f) && c2.accept(f)); 149 } 150 151 public boolean equals(Object o) { 152 if(o instanceof FeatureFilter) { 153 return FilterUtils.areEqual(this, (FeatureFilter) o); 154 } else { 155 return false; 156 } 157 } 158 159 public int hashCode() { 160 return getChild1().hashCode() ^ getChild2().hashCode(); 161 } 162 163 public String toString() { 164 return "And(" + c1 + " , " + c2 + ")"; 165 } 166 } 167 168 /** 169 * A filter that returns all features accepted by at least one child filter. 170 * 171 * @author Thomas Down 172 * @author Matthew Pocock 173 * @since 1.0 174 */ 175 public final static class Or implements FeatureFilter { 176 static { WalkerFactory.getInstance().addTypeWithParent(Or.class); } 177 178 FeatureFilter c1, c2; 179 180 public FeatureFilter getChild1() { 181 return c1; 182 } 183 184 public FeatureFilter getChild2() { 185 return c2; 186 } 187 188 public Or(FeatureFilter c1, FeatureFilter c2) { 189 this.c1 = c1; 190 this.c2 = c2; 191 } 192 193 public boolean accept(Feature f) { 194 return (c1.accept(f) || c2.accept(f)); 195 } 196 197 public boolean equals(Object o) { 198 if(o instanceof FeatureFilter) { 199 return FilterUtils.areEqual(this, (FeatureFilter) o); 200 } else { 201 return false; 202 } 203 } 204 205 public int hashCode() { 206 return getChild1().hashCode() ^ getChild2().hashCode(); 207 } 208 209 public String toString() { 210 return "Or(" + c1 + " , " + c2 + ")"; 211 } 212 } 213 214 /** 215 * Construct one of these to filter features by type. 216 * 217 * @author Matthew Pocock 218 * @since 1.0 219 */ 220 final public static class ByType implements OptimizableFilter { 221 private String type; 222 223 public String getType() { 224 return type; 225 } 226 227 /** 228 * Create a ByType filter that filters in all features with type fields 229 * equal to type. 230 * 231 * @param type the String to match type fields against 232 */ 233 public ByType(String type) { 234 if (type == null) 235 throw new NullPointerException("Type may not be null"); 236 this.type = type; 237 } 238 239 /** 240 * Returns true if the feature has a matching type property. 241 */ 242 public boolean accept(Feature f) { 243 return type.equals(f.getType()); 244 } 245 246 public boolean equals(Object o) { 247 return 248 (o instanceof ByType) && 249 (((ByType) o).getType().equals(this.getType())); 250 } 251 252 public int hashCode() { 253 return getType().hashCode(); 254 } 255 256 public boolean isProperSubset(FeatureFilter sup) { 257 return this.equals(sup) || (sup instanceof AcceptAllFilter); 258 } 259 260 public boolean isDisjoint(FeatureFilter filt) { 261 return (filt instanceof AcceptNoneFilter) || ( 262 (filt instanceof ByType) && 263 !getType().equals(((ByType) filt).getType()) 264 ); 265 } 266 267 public String toString() { 268 return "ByType(" + type + ")"; 269 } 270 } 271 272 /** 273 * Construct one of these to filter features by source. 274 * 275 * @author Matthew Pocock 276 * @since 1.0 277 */ 278 public final static class BySource implements OptimizableFilter { 279 private String source; 280 281 public String getSource() { 282 return source; 283 } 284 285 /** 286 * Create a BySource filter that filters in all features which have sources 287 * equal to source. 288 * 289 * @param source the String to match source fields against 290 */ 291 public BySource(String source) { 292 if (source == null) 293 throw new NullPointerException("Source may not be null"); 294 this.source = source; 295 } 296 297 public boolean accept(Feature f) { return source.equals(f.getSource()); } 298 299 public boolean equals(Object o) { 300 return 301 (o instanceof BySource) && 302 (((BySource) o).getSource().equals(this.getSource())); 303 } 304 305 public boolean isProperSubset(FeatureFilter sup) { 306 return this.equals(sup) || (sup instanceof AcceptAllFilter); 307 } 308 309 public int hashCode() { 310 return getSource().hashCode(); 311 } 312 313 public boolean isDisjoint(FeatureFilter filt) { 314 return (filt instanceof AcceptNoneFilter) || ( 315 (filt instanceof BySource) && 316 !getSource().equals(((BySource) filt).getSource()) 317 ); 318 } 319 320 public String toString() { 321 return "BySource(" + source + ")"; 322 } 323 } 324 325 /** 326 * Filter which accepts only those filters which are an instance 327 * of a specific Java class 328 * 329 * @author Thomas Down 330 * @author Matthew Pocock 331 * @since 1.1 332 */ 333 334 public final static class ByClass implements OptimizableFilter { 335 private Class clazz; 336 337 public ByClass(Class clazz) { 338 if (clazz == null) { 339 throw new NullPointerException("Clazz may not be null"); 340 } 341 if(!Feature.class.isAssignableFrom(clazz)) { 342 throw new ClassCastException( 343 "Filters by class must be over Feature classes: " + 344 clazz 345 ); 346 } 347 this.clazz = clazz; 348 } 349 350 public boolean accept(Feature f) { 351 return clazz.isInstance(f); 352 } 353 354 public Class getTestClass() { 355 return clazz; 356 } 357 358 public boolean equals(Object o) { 359 return 360 (o instanceof ByClass) && 361 (((ByClass) o).getTestClass() == this.getTestClass()); 362 } 363 364 public int hashCode() { 365 return getTestClass().hashCode(); 366 } 367 368 public boolean isProperSubset(FeatureFilter sup) { 369 if(sup instanceof ByClass) { 370 Class supC = ((ByClass) sup).getTestClass(); 371 return supC.isAssignableFrom(this.getTestClass()); 372 } 373 return (sup instanceof AcceptAllFilter); 374 } 375 376 public boolean isDisjoint(FeatureFilter feat) { 377 if(feat instanceof ByClass) { 378 Class featC = ((ByClass) feat).getTestClass(); 379 return 380 ! (featC.isAssignableFrom(getTestClass())) && 381 ! (getTestClass().isAssignableFrom(featC)); 382 } else if (feat instanceof ByComponentName) { 383 return !getTestClass().isAssignableFrom(ComponentFeature.class); 384 } 385 386 return (feat instanceof AcceptNoneFilter); 387 } 388 389 public String toString() { 390 return "ByClass(" + clazz.getName() + ")"; 391 } 392 } 393 394 395 /** 396 * Accept features with a given strandedness. 397 * 398 * @author Matthew Pocock 399 * @since 1.1 400 */ 401 public final static class StrandFilter implements OptimizableFilter { 402 private StrandedFeature.Strand strand; 403 404 /** 405 * Build a new filter that matches all features of a given strand. 406 * 407 * @param strand the Strand to match 408 */ 409 public StrandFilter(StrandedFeature.Strand strand) { 410 this.strand = strand; 411 } 412 413 /** 414 * Retrieve the strand this matches. 415 * 416 * @return the Strand matched 417 */ 418 public StrandedFeature.Strand getStrand() { 419 return strand; 420 } 421 422 /** 423 * Accept the Feature if it is an instance of StrandedFeature and matches 424 * the value of getStrand(). 425 * 426 * @param f the Feature to check 427 * @return true if the strand matches, or false otherwise 428 */ 429 public boolean accept(Feature f) { 430 if(f instanceof StrandedFeature) { 431 StrandedFeature sf = (StrandedFeature) f; 432 return sf.getStrand() == strand; 433 } else { 434 return strand == StrandedFeature.UNKNOWN; 435 } 436 } 437 438 public boolean equals(Object o) { 439 return 440 (o instanceof StrandFilter) && 441 (((StrandFilter) o).getStrand() == this.getStrand()); 442 } 443 444 public int hashCode() { 445 return getStrand().hashCode(); 446 } 447 448 public String toString() { 449 return "StrandedFilter(" + strand + ")"; 450 } 451 452 public boolean isProperSubset(FeatureFilter sup) { 453 return this.equals(sup); 454 } 455 456 public boolean isDisjoint(FeatureFilter filt) { 457 return (filt instanceof AcceptNoneFilter) || ( 458 (filt instanceof StrandFilter) && 459 ((StrandFilter) filt).getStrand() == getStrand() 460 ); 461 } 462 } 463 464 /** 465 * Accept features that reside on a sequence with a particular name. 466 * 467 * @author Matthew Pocock 468 * @since 1.3 469 */ 470 public final static class BySequenceName 471 implements OptimizableFilter { 472 private String seqName; 473 474 public BySequenceName(String seqName) { 475 this.seqName = seqName; 476 } 477 478 public String getSequenceName() { 479 return seqName; 480 } 481 482 public boolean accept(Feature f) { 483 return f.getSequence().getName().equals(seqName); 484 } 485 486 public boolean isProperSubset(FeatureFilter sup) { 487 return equals(sup); 488 } 489 490 public boolean isDisjoint(FeatureFilter filt) { 491 if (filt instanceof BySequenceName) { 492 return !equals(this); 493 } else { 494 return false; 495 } 496 } 497 498 public boolean equals(Object o) { 499 return 500 (o instanceof BySequenceName) && 501 ((BySequenceName) o).getSequenceName().equals(seqName); 502 } 503 504 public int hashCode() { 505 return seqName.hashCode(); 506 } 507 } 508 509 /** 510 * A filter that returns all features contained within a location. 511 * 512 * @author Matthew Pocock 513 * @since 1.0 514 */ 515 public final static class ContainedByLocation implements OptimizableFilter { 516 private Location loc; 517 518 public Location getLocation() { 519 return loc; 520 } 521 522 /** 523 * Creates a filter that returns everything contained within loc. 524 * 525 * @param loc the location that will contain the accepted features 526 */ 527 public ContainedByLocation(Location loc) { 528 if (loc == null) { 529 throw new NullPointerException("Loc may not be null"); 530 } 531 this.loc = loc; 532 } 533 534 /** 535 * Returns true if the feature is within this filter's location. 536 */ 537 public boolean accept(Feature f) { 538 return loc.contains(f.getLocation()); 539 } 540 541 public boolean equals(Object o) { 542 return 543 (o instanceof ContainedByLocation) && 544 (((ContainedByLocation) o).getLocation().equals(this.getLocation())); 545 } 546 547 public int hashCode() { 548 return getLocation().hashCode(); 549 } 550 551 public boolean isProperSubset(FeatureFilter sup) { 552 if(sup instanceof ContainedByLocation) { 553 Location supL = ((ContainedByLocation) sup).getLocation(); 554 return supL.contains(this.getLocation()); 555 } else if(sup instanceof OverlapsLocation) { 556 Location supL = ((OverlapsLocation) sup).getLocation(); 557 return supL.contains(this.getLocation()); 558 } else if (sup instanceof ShadowOverlapsLocation) { 559 Location supL = ((ShadowOverlapsLocation) sup).getLocation(); 560 return supL.contains(this.getLocation()); 561 } else if (sup instanceof ShadowContainedByLocation) { 562 Location supL = ((ShadowContainedByLocation) sup).getLocation(); 563 return supL.contains(this.getLocation()); 564 } 565 return (sup instanceof AcceptAllFilter); 566 } 567 568 public boolean isDisjoint(FeatureFilter filt) { 569 if(filt instanceof ContainedByLocation) { 570 Location loc = ((ContainedByLocation) filt).getLocation(); 571 return !getLocation().overlaps(loc); 572 } else if (filt instanceof OverlapsLocation) { 573 Location filtL = ((OverlapsLocation) filt).getLocation(); 574 return !filtL.overlaps(this.getLocation()); 575 } else if (filt instanceof ShadowOverlapsLocation) { 576 Location filtL = ((ShadowOverlapsLocation) filt).getLocation(); 577 return filtL.getMax() < loc.getMin() || filtL.getMin() > loc.getMax(); 578 } else if (filt instanceof ShadowContainedByLocation) { 579 Location filtL = ((ShadowContainedByLocation) filt).getLocation(); 580 return filtL.getMax() < loc.getMin() || filtL.getMin() > loc.getMax(); 581 } 582 583 return (filt instanceof AcceptNoneFilter); 584 } 585 586 public String toString() { 587 return "ContainedBy(" + loc + ")"; 588 } 589 } 590 591 /** 592 * A filter that returns all features overlapping a location. 593 * 594 * @author Matthew Pocock 595 * @since 1.0 596 */ 597 public final static class OverlapsLocation implements OptimizableFilter { 598 private Location loc; 599 600 public Location getLocation() { 601 return loc; 602 } 603 604 /** 605 * Creates a filter that returns everything overlapping loc. 606 * 607 * @param loc the location that will overlap the accepted features 608 */ 609 public OverlapsLocation(Location loc) { 610 if (loc == null) { 611 throw new NullPointerException("Loc may not be null"); 612 } 613 this.loc = loc; 614 } 615 616 /** 617 * Returns true if the feature overlaps this filter's location. 618 */ 619 public boolean accept(Feature f) { 620 return loc.overlaps(f.getLocation()); 621 } 622 623 public boolean equals(Object o) { 624 return 625 (o instanceof OverlapsLocation) && 626 (((OverlapsLocation) o).getLocation().equals(this.getLocation())); 627 } 628 629 public int hashCode() { 630 return getLocation().hashCode(); 631 } 632 633 public boolean isProperSubset(FeatureFilter sup) { 634 if(sup instanceof OverlapsLocation) { 635 Location supL = ((OverlapsLocation) sup).getLocation(); 636 return supL.contains(this.getLocation()); 637 } else if (sup instanceof ShadowOverlapsLocation) { 638 Location supL = ((ShadowOverlapsLocation) sup).getLocation(); 639 return supL.contains(this.getLocation()); 640 } 641 return (sup instanceof AcceptAllFilter); 642 } 643 644 public boolean isDisjoint(FeatureFilter filt) { 645 if (filt instanceof ContainedByLocation) { 646 Location loc = ((ContainedByLocation) filt).getLocation(); 647 return !getLocation().overlaps(loc); 648 } else if (filt instanceof ShadowContainedByLocation) { 649 Location loc = ((ShadowContainedByLocation) filt).getLocation(); 650 return getLocation().getMax() < loc.getMin() || getLocation().getMin() > loc.getMax(); 651 } 652 return (filt instanceof AcceptNoneFilter); 653 } 654 655 public String toString() { 656 return "Overlaps(" + loc + ")"; 657 } 658 } 659 660 /** 661 * A filter that accepts all features whose shadow overlaps a specified 662 * <code>Location</code>. The shadow is defined as the interval between the 663 * minimum and maximum positions of the feature's location. For features 664 * with contiguous locations, this filter is equivalent to 665 * <code>FeatureFilter.OverlapsLocation</code>.. 666 * 667 * <p> 668 * A typical use of this filter is in graphics code where you are rendering 669 * features with non-contiguous locations in a `blocks and connectors' style, 670 * and wish to draw the connector even when no blocks fall within the 671 * selected field of view 672 * </p> 673 * 674 * @author Thomas Down 675 * @since 1.3 676 */ 677 678 public final static class ShadowOverlapsLocation implements OptimizableFilter { 679 private Location loc; 680 681 public Location getLocation() { 682 return loc; 683 } 684 685 /** 686 * Creates a filter that returns everything overlapping loc. 687 * 688 * @param loc the location that will overlap the accepted features 689 */ 690 public ShadowOverlapsLocation(Location loc) { 691 if (loc == null) { 692 throw new NullPointerException("Loc may not be null"); 693 } 694 this.loc = loc; 695 } 696 697 /** 698 * Returns true if the feature overlaps this filter's location. 699 */ 700 public boolean accept(Feature f) { 701 Location floc = f.getLocation(); 702 if (!floc.isContiguous()) { 703 floc = new RangeLocation(floc.getMin(), floc.getMax()); 704 } 705 return loc.overlaps(floc); 706 } 707 708 public boolean equals(Object o) { 709 return 710 (o instanceof ShadowOverlapsLocation) && 711 (((ShadowOverlapsLocation) o).getLocation().equals(this.getLocation())); 712 } 713 714 public int hashCode() { 715 return getLocation().hashCode() +77; 716 } 717 718 public boolean isProperSubset(FeatureFilter sup) { 719 if(sup instanceof ShadowOverlapsLocation) { 720 Location supL = ((ShadowOverlapsLocation) sup).getLocation(); 721 return supL.contains(this.getLocation()); 722 } 723 return (sup instanceof AcceptAllFilter); 724 } 725 726 public boolean isDisjoint(FeatureFilter filt) { 727 if (filt instanceof ShadowContainedByLocation) { 728 Location loc = ((ShadowContainedByLocation) filt).getLocation(); 729 return !getLocation().overlaps(loc); 730 } else if (filt instanceof ContainedByLocation) { 731 Location loc = ((ContainedByLocation) filt).getLocation(); 732 return (loc.getMax() < getLocation().getMin() || loc.getMin() > getLocation().getMax()); 733 } 734 return (filt instanceof AcceptNoneFilter); 735 } 736 737 public String toString() { 738 return "ShadowOverlaps(" + loc + ")"; 739 } 740 } 741 742 /** 743 * A filter that accepts all features whose shadow is contained by a specified 744 * <code>Location</code>. The shadow is defined as the interval between the 745 * minimum and maximum positions of the feature's location. For features 746 * with contiguous locations, this filter is equivalent to 747 * <code>FeatureFilter.ContainedByLocation</code>. 748 * 749 * @author Thomas Down 750 * @since 1.3 751 */ 752 753 public final static class ShadowContainedByLocation implements OptimizableFilter { 754 private Location loc; 755 756 public Location getLocation() { 757 return loc; 758 } 759 760 /** 761 * Creates a filter that returns everything contained within loc. 762 * 763 * @param loc the location that will contain the accepted features 764 */ 765 public ShadowContainedByLocation(Location loc) { 766 if (loc == null) { 767 throw new NullPointerException("Loc may not be null"); 768 } 769 this.loc = loc; 770 } 771 772 /** 773 * Returns true if the feature is within this filter's location. 774 */ 775 public boolean accept(Feature f) { 776 Location floc = f.getLocation(); 777 if (!floc.isContiguous()) { 778 floc = new RangeLocation(floc.getMin(), floc.getMax()); 779 } 780 return loc.contains(floc); 781 } 782 783 public boolean equals(Object o) { 784 return 785 (o instanceof ShadowContainedByLocation) && 786 (((ShadowContainedByLocation) o).getLocation().equals(this.getLocation())); 787 } 788 789 public int hashCode() { 790 return getLocation().hashCode() + 88; 791 } 792 793 public boolean isProperSubset(FeatureFilter sup) { 794 if(sup instanceof ShadowContainedByLocation) { 795 Location supL = ((ShadowContainedByLocation) sup).getLocation(); 796 return supL.contains(this.getLocation()); 797 } else if (sup instanceof ShadowOverlapsLocation) { 798 Location supL = ((ShadowOverlapsLocation) sup).getLocation(); 799 return supL.contains(this.getLocation()); 800 } 801 return (sup instanceof AcceptAllFilter); 802 } 803 804 public boolean isDisjoint(FeatureFilter filt) { 805 if(filt instanceof ContainedByLocation) { 806 Location filtL = ((ShadowContainedByLocation) filt).getLocation(); 807 return filtL.getMax() < loc.getMin() || filtL.getMin() > loc.getMax(); 808 } if(filt instanceof ShadowContainedByLocation) { 809 Location loc = ((ShadowContainedByLocation) filt).getLocation(); 810 return !getLocation().overlaps(loc); 811 } else if (filt instanceof OverlapsLocation) { 812 Location filtL = ((OverlapsLocation) filt).getLocation(); 813 return filtL.getMax() < loc.getMin() || filtL.getMin() > loc.getMax(); 814 } else if (filt instanceof ShadowOverlapsLocation) { 815 Location filtL = ((ShadowOverlapsLocation) filt).getLocation(); 816 return !filtL.overlaps(this.getLocation()); 817 } 818 819 return (filt instanceof AcceptNoneFilter); 820 } 821 822 public String toString() { 823 return "ShadowContainedBy(" + loc + ")"; 824 } 825 } 826 827 /** 828 * A filter that returns all features that have an annotation bundle that is of a given 829 * annotation type. 830 * 831 * @author Matthew Pocock 832 * @author Thomas Down 833 * @since 1.3 834 */ 835 public static class ByAnnotationType 836 implements OptimizableFilter { 837 private AnnotationType type; 838 839 protected ByAnnotationType() { 840 this(AnnotationType.ANY); 841 } 842 843 public ByAnnotationType(AnnotationType type) { 844 this.type = type; 845 } 846 847 public AnnotationType getType() { 848 return type; 849 } 850 851 protected void setType(AnnotationType type) { 852 this.type = type; 853 } 854 855 public boolean accept(Feature f) { 856 return type.instanceOf(f.getAnnotation()); 857 } 858 859 public boolean equals(Object o) { 860 if(o instanceof ByAnnotationType) { 861 ByAnnotationType that = (ByAnnotationType) o; 862 return this.getType() == that.getType(); 863 } 864 865 return false; 866 } 867 868 public int hashCode() { 869 return getType().hashCode(); 870 } 871 872 public boolean isDisjoint(FeatureFilter filter) { 873 if(filter instanceof AcceptNoneFilter) { 874 return true; 875 } else if(filter instanceof ByAnnotationType) { 876 // check for common property names 877 ByAnnotationType that = (ByAnnotationType) filter; 878 Set props = that.getType().getProperties(); 879 Set ourProps = this.getType().getProperties(); 880 Set allProps = new HashSet(props); 881 allProps.addAll(ourProps); 882 for(Iterator i = allProps.iterator(); i.hasNext(); ) { 883 Object prop = i.next(); 884 885 CollectionConstraint thisC = this.getType().getConstraint(prop); 886 CollectionConstraint thatC = that.getType().getConstraint(prop); 887 if (AnnotationTools.intersection(thisC, thatC) == CollectionConstraint.NONE) { 888 return true; 889 } 890 } 891 } 892 893 return false; 894 } 895 896 public boolean isProperSubset(FeatureFilter filter) { 897 if(filter instanceof ByAnnotationType) { 898 ByAnnotationType that = (ByAnnotationType) filter; 899 900 Set thisProps = this.getType().getProperties(); 901 Set thatProps = that.getType().getProperties(); 902 for(Iterator i = thatProps.iterator(); i.hasNext(); ) { 903 Object prop = i.next(); 904 905 if(!thisProps.contains(prop)) { 906 return false; 907 } 908 909 CollectionConstraint thisP = this.getType().getConstraint(prop); 910 CollectionConstraint thatP = that.getType().getConstraint(prop); 911 912 if( 913 !thatP.subConstraintOf(thisP) 914 ) { 915 return false; 916 } 917 } 918 919 return true; 920 } 921 922 return false; 923 } 924 925 public String toString() { 926 return "ByAnnotationType {" + type + "}"; 927 } 928 } 929 930 /** 931 * Retrieve features that contain a given annotation with a given value. 932 * 933 * @author Matthew Pocock 934 * @author Keith James 935 * @since 1.1 936 */ 937 public final static class ByAnnotation 938 extends ByAnnotationType { 939 private Object key; 940 private Object value; 941 942 /** 943 * Make a new ByAnnotation that will accept features with an annotation 944 * bundle containing 'value' associated with 'key'. 945 * 946 * @param key the Object used as a key in the annotation 947 * @param value the Object associated with key in the annotation 948 */ 949 public ByAnnotation(Object key, Object value) { 950 this.key = key; 951 this.value = value; 952 953 AnnotationType.Impl type = new AnnotationType.Impl(); 954 type.setConstraints( 955 key, 956 new PropertyConstraint.ExactValue(value), 957 CardinalityConstraint.ONE 958 ); 959 setType(type); 960 } 961 962 public Object getKey() { 963 return key; 964 } 965 966 public Object getValue() { 967 return value; 968 } 969 } 970 971 /** 972 * Retrieve features that contain a given annotation, and that the set of values 973 * contains the value given. 974 * 975 * @author Thomas Down 976 * @since 1.3 977 */ 978 public final static class AnnotationContains 979 extends ByAnnotationType { 980 private Object key; 981 private Object value; 982 983 /** 984 * Make a new AnnotationContains that will accept features with an annotation 985 * bundle where the value-set assosiated with the property <code>key</code> 986 * contains a member equal to <code>value</code>. 987 * 988 * @param key the Object used as a key in the annotation 989 * @param value the Object associated with key in the annotation 990 */ 991 public AnnotationContains(Object key, Object value) { 992 this.key = key; 993 this.value = value; 994 995 AnnotationType.Impl type = new AnnotationType.Impl(); 996 type.setConstraint( 997 key, 998 new CollectionConstraint.Contains( 999 new PropertyConstraint.ExactValue(value), 1000 CardinalityConstraint.ONE 1001 ) 1002 ); 1003 setType(type); 1004 } 1005 1006 public Object getKey() { 1007 return key; 1008 } 1009 1010 public Object getValue() { 1011 return value; 1012 } 1013 } 1014 1015 /** 1016 * Retrieve features that contain a given annotation with any value. 1017 * 1018 * @author Matthew Pocock 1019 * @author Keith James 1020 * @since 1.1 1021 */ 1022 public final static class HasAnnotation 1023 extends ByAnnotationType { 1024 private Object key; 1025 1026 /** 1027 * Make a new ByAnnotation that will accept features with an annotation 1028 * bundle containing any value associated with 'key'. 1029 * 1030 * @param key the Object used as a key in the annotation 1031 */ 1032 public HasAnnotation(Object key) { 1033 this.key = key; 1034 1035 AnnotationType.Impl type = new AnnotationType.Impl(); 1036 type.setConstraints( 1037 key, 1038 PropertyConstraint.ANY, 1039 CardinalityConstraint.ONE_OR_MORE 1040 ); 1041 setType(type); 1042 } 1043 1044 public Object getKey() { 1045 return key; 1046 } 1047 } 1048 1049 /** 1050 * Filter by applying a nested <code>FeatureFilter</code> to the 1051 * parent feature. Always <code>false</code> if the parent 1052 * is not a feature (e.g. top-level features, where the 1053 * parent is a sequence). 1054 * 1055 * @author Thomas Down 1056 * @since 1.2 1057 */ 1058 1059 public static class ByParent implements OptimizableFilter, Up { 1060 static { WalkerFactory.getInstance().addTypeWithParent(ByParent.class); } 1061 1062 private FeatureFilter filter; 1063 1064 public ByParent(FeatureFilter ff) { 1065 filter = ff; 1066 } 1067 1068 public FeatureFilter getFilter() { 1069 return filter; 1070 } 1071 1072 public boolean accept(Feature f) { 1073 FeatureHolder fh = f.getParent(); 1074 if (fh instanceof Feature) { 1075 return filter.accept((Feature) fh); 1076 } 1077 1078 return false; 1079 } 1080 1081 public int hashCode() { 1082 return filter.hashCode() + 173; 1083 } 1084 1085 public boolean equals(Object o) { 1086 if (! (o instanceof FeatureFilter.ByParent)) { 1087 return false; 1088 } 1089 1090 FeatureFilter.ByParent ffbp = (FeatureFilter.ByParent) o; 1091 return ffbp.getFilter().equals(filter); 1092 } 1093 1094 public boolean isProperSubset(FeatureFilter ff) { 1095 FeatureFilter ancFilter = null; 1096 if (ff instanceof FeatureFilter.ByParent) { 1097 ancFilter = ((FeatureFilter.ByParent) ff).getFilter(); 1098 } else if (ff instanceof FeatureFilter.ByAncestor) { 1099 ancFilter = ((FeatureFilter.ByAncestor) ff).getFilter(); 1100 } 1101 1102 if (ancFilter != null) { 1103 return FilterUtils.areProperSubset(ancFilter, filter); 1104 } else { 1105 return false; 1106 } 1107 } 1108 1109 public boolean isDisjoint(FeatureFilter ff) { 1110 // System.err.println("Disjunction test for " + toString()); 1111 // System.err.println("Against " + ff.toString()); 1112 if (ff instanceof IsTopLevel) { 1113 return true; 1114 } else if (ff instanceof FeatureFilter.ByParent) { 1115 return FilterUtils.areDisjoint( 1116 ((FeatureFilter.ByParent) ff).getFilter(), 1117 getFilter() 1118 ); 1119 } else if (ff instanceof FeatureFilter.ByAncestor) { 1120 return FilterUtils.areDisjoint( 1121 ((FeatureFilter.ByAncestor) ff).getFilter(), 1122 getFilter() 1123 ); 1124 } else { 1125 FeatureFilter childFilter = FilterUtils.getOnlyChildrenFilter(getFilter()); 1126 if (childFilter != null) { 1127 return FilterUtils.areDisjoint( 1128 childFilter, 1129 ff 1130 ); 1131 } 1132 } 1133 1134 return false; 1135 } 1136 } 1137 1138 /** 1139 * Filter by applying a nested <code>FeatureFilter</code> to all 1140 * ancestor features. Returns <code>true</code> if at least one 1141 * of them matches the filter. Always <code>false</code> if the 1142 * parent is not a feature (e.g. top-level features, where the 1143 * parent is a sequence). 1144 * 1145 * @author Thomas Down 1146 * @since 1.2 1147 */ 1148 1149 public static class ByAncestor implements OptimizableFilter, Up { 1150 static { WalkerFactory.getInstance().addTypeWithParent(ByAncestor.class); } 1151 1152 private FeatureFilter filter; 1153 1154 public ByAncestor(FeatureFilter ff) { 1155 filter = ff; 1156 } 1157 1158 public FeatureFilter getFilter() { 1159 return filter; 1160 } 1161 1162 public boolean accept(Feature f) { 1163 do { 1164 FeatureHolder fh = f.getParent(); 1165 if (fh instanceof Feature) { 1166 f = (Feature) fh; 1167 if (filter.accept(f)) { 1168 return true; 1169 } 1170 } else { 1171 return false; 1172 } 1173 } while (true); 1174 } 1175 1176 public int hashCode() { 1177 return filter.hashCode() + 186; 1178 } 1179 1180 public boolean equals(Object o) { 1181 if (! (o instanceof FeatureFilter.ByAncestor)) { 1182 return false; 1183 } 1184 1185 FeatureFilter.ByAncestor ffba = (FeatureFilter.ByAncestor) o; 1186 return ffba.getFilter().equals(filter); 1187 } 1188 1189 public boolean isProperSubset(FeatureFilter ff) { 1190 FeatureFilter ancFilter = null; 1191 if (ff instanceof FeatureFilter.ByAncestor) { 1192 ancFilter = ((FeatureFilter.ByAncestor) ff).getFilter(); 1193 } 1194 1195 if (ancFilter != null) { 1196 return FilterUtils.areProperSubset(ancFilter, filter); 1197 } else { 1198 return false; 1199 } 1200 } 1201 1202 public boolean isDisjoint(FeatureFilter ff) { 1203 // System.err.println("Disjunction test for " + toString()); 1204 // System.err.println("Against " + ff.toString()); 1205 if (ff instanceof IsTopLevel) { 1206 return true; 1207 } 1208 1209 if (ff instanceof FeatureFilter.ByParent) { 1210 return FilterUtils.areDisjoint( 1211 ((FeatureFilter.ByParent) ff).getFilter(), 1212 getFilter() 1213 ); 1214 } else if (ff instanceof FeatureFilter.ByAncestor) { 1215 return FilterUtils.areDisjoint( 1216 ((FeatureFilter.ByAncestor) ff).getFilter(), 1217 getFilter() 1218 ); 1219 } else { 1220 FeatureFilter descFilter = FilterUtils.getOnlyDescendantsFilter(getFilter()); 1221 if (descFilter != null) { 1222 return FilterUtils.areDisjoint( 1223 descFilter, 1224 ff 1225 ); 1226 } 1227 1228 FeatureFilter childFilter = FilterUtils.getOnlyChildrenFilter(getFilter()); 1229 if (childFilter != null) { 1230 // System.err.println("Child filter is: " + childFilter.toString()); 1231 if (FilterUtils.areProperSubset(childFilter, leaf)) { 1232 // System.err.println("Leaf case"); 1233 return FilterUtils.areDisjoint( 1234 childFilter, 1235 ff 1236 ); 1237 } else { 1238 // System.err.println("Tree case"); 1239 return FilterUtils.areDisjoint( 1240 new FeatureFilter.Or( 1241 childFilter, 1242 new FeatureFilter.ByAncestor(childFilter) 1243 ), 1244 ff 1245 ); 1246 } 1247 } 1248 } 1249 1250 return false; 1251 } 1252 1253 public String toString() { 1254 return "ByAncestor(" + getFilter().toString() + ")"; 1255 } 1256 } 1257 1258 /** 1259 * Accepts features where all immediate children meet the supplied filter. This 1260 * will be <code>true</code> in the case where no child features exist. Mainly useful 1261 * for defining schemas of feature-trees. 1262 * 1263 * @author Thomas Down 1264 * @since 1.3 1265 */ 1266 1267 public static class OnlyChildren implements OptimizableFilter, ByHierarchy { 1268 static { WalkerFactory.getInstance().addTypeWithParent(OnlyChildren.class); } 1269 1270 private FeatureFilter filter; 1271 1272 public OnlyChildren(FeatureFilter ff) { 1273 this.filter = ff; 1274 } 1275 1276 public FeatureFilter getFilter() { 1277 return filter; 1278 } 1279 1280 public boolean accept(Feature f) { 1281 for (Iterator i = f.features(); i.hasNext(); ) { 1282 if (!filter.accept((Feature) i.next())) { 1283 return false; 1284 } 1285 } 1286 return true; 1287 } 1288 1289 public int hashCode() { 1290 return filter.hashCode() + 762; 1291 } 1292 1293 public boolean equals(Object o) { 1294 if (! (o instanceof FeatureFilter.OnlyChildren)) { 1295 return false; 1296 } 1297 1298 FeatureFilter.OnlyChildren ffoc = (FeatureFilter.OnlyChildren) o; 1299 return ffoc.getFilter().equals(filter); 1300 } 1301 1302 public boolean isProperSubset(FeatureFilter ff) { 1303 if (ff == FeatureFilter.all) { 1304 return true; 1305 } else if (ff instanceof OnlyChildren) { 1306 return FilterUtils.areProperSubset( 1307 getFilter(), 1308 ((OnlyChildren) ff).getFilter() 1309 ) ; 1310 } else if (ff instanceof OnlyDescendants) { 1311 return FilterUtils.areProperSubset( 1312 getFilter(), 1313 ((OnlyDescendants) ff).getFilter() 1314 ) ; 1315 } else { 1316 return false; 1317 } 1318 } 1319 1320 public boolean isDisjoint(FeatureFilter ff) { 1321 if (ff instanceof ByChild) { 1322 return FilterUtils.areDisjoint( 1323 getFilter(), 1324 ((ByChild) ff).getFilter() 1325 ); 1326 } else { 1327 return false; 1328 } 1329 } 1330 1331 public String toString() { 1332 return "OnlyChildren(" + filter.toString() + ")"; 1333 } 1334 } 1335 1336 /** 1337 * Accepts features where all descendants meet the supplied filter. This 1338 * will be <code>true</code> in the case where no child features exist. Mainly useful 1339 * for defining schemas of feature-trees. 1340 * 1341 * @author Thomas Down 1342 * @since 1.3 1343 */ 1344 1345 public static class OnlyDescendants implements OptimizableFilter, ByHierarchy { 1346 static { WalkerFactory.getInstance().addTypeWithParent(OnlyDescendants.class); } 1347 1348 private FeatureFilter filter; 1349 1350 public OnlyDescendants(FeatureFilter ff) { 1351 this.filter = ff; 1352 } 1353 1354 public FeatureFilter getFilter() { 1355 return filter; 1356 } 1357 1358 public boolean accept(Feature f) { 1359 return f.filter(FeatureFilter.all).countFeatures() == f.filter(filter).countFeatures(); 1360 } 1361 1362 public int hashCode() { 1363 return filter.hashCode() + 763; 1364 } 1365 1366 public boolean equals(Object o) { 1367 if (! (o instanceof FeatureFilter.OnlyDescendants)) { 1368 return false; 1369 } 1370 1371 FeatureFilter.OnlyDescendants ffoc = (FeatureFilter.OnlyDescendants) o; 1372 return ffoc.getFilter().equals(filter); 1373 } 1374 1375 1376 public boolean isProperSubset(FeatureFilter ff) { 1377 if (ff == FeatureFilter.all) { 1378 return true; 1379 } else if (ff instanceof OnlyDescendants) { 1380 return FilterUtils.areProperSubset( 1381 getFilter(), 1382 ((OnlyDescendants) ff).getFilter() 1383 ) ; 1384 } else { 1385 return false; 1386 } 1387 } 1388 1389 public boolean isDisjoint(FeatureFilter ff) { 1390 if (ff instanceof ByChild) { 1391 return FilterUtils.areDisjoint( 1392 getFilter(), 1393 ((ByChild) ff).getFilter() 1394 ); 1395 } else if (ff instanceof ByDescendant) { 1396 return FilterUtils.areDisjoint( 1397 getFilter(), 1398 ((ByDescendant) ff).getFilter() 1399 ); 1400 } else { 1401 return false; 1402 } 1403 } 1404 } 1405 1406 /** 1407 * Filter by applying a nested <code>FeatureFilter</code> to the 1408 * child features. Always <code>false</code> if there are no children. 1409 * 1410 * @author Matthew Pocock 1411 * @author Thomas Down 1412 * @since 1.3 1413 */ 1414 1415 public static class ByChild implements OptimizableFilter, Down { 1416 static { WalkerFactory.getInstance().addTypeWithParent(ByChild.class); } 1417 1418 private FeatureFilter filter; 1419 1420 public ByChild(FeatureFilter ff) { 1421 filter = ff; 1422 } 1423 1424 public FeatureFilter getFilter() { 1425 return filter; 1426 } 1427 1428 public boolean accept(Feature f) { 1429 for(Iterator i = f.features(); i.hasNext(); ) { 1430 if(filter.accept((Feature) i.next())) { 1431 return true; 1432 } 1433 } 1434 1435 return false; 1436 } 1437 1438 public int hashCode() { 1439 return filter.hashCode() + 173; 1440 } 1441 1442 public boolean equals(Object o) { 1443 if (! (o instanceof FeatureFilter.ByChild)) { 1444 return false; 1445 } 1446 1447 FeatureFilter.ByChild ffbc = (FeatureFilter.ByChild) o; 1448 return ffbc.getFilter().equals(filter); 1449 } 1450 1451 public boolean isProperSubset(FeatureFilter ff) { 1452 FeatureFilter descFilter = null; 1453 if (ff instanceof FeatureFilter.ByChild) { 1454 descFilter = ((FeatureFilter.ByChild) ff).getFilter(); 1455 } else if (ff instanceof FeatureFilter.ByDescendant) { 1456 descFilter = ((FeatureFilter.ByDescendant) ff).getFilter(); 1457 } 1458 1459 if (descFilter != null) { 1460 return FilterUtils.areProperSubset(descFilter, filter); 1461 } else { 1462 return false; 1463 } 1464 } 1465 1466 public boolean isDisjoint(FeatureFilter ff) { 1467 if (ff instanceof OnlyChildren) { 1468 return FilterUtils.areDisjoint( 1469 getFilter(), 1470 ((OnlyChildren) ff).getFilter() 1471 ); 1472 } else if (ff instanceof OnlyDescendants) { 1473 return FilterUtils.areDisjoint( 1474 getFilter(), 1475 ((OnlyDescendants) ff).getFilter() 1476 ); 1477 } else { 1478 return false; 1479 } 1480 } 1481 } 1482 1483 1484 /** 1485 * Filter by applying a nested <code>FeatureFilter</code> to all 1486 * descendant features. Returns <code>true</code> if at least one 1487 * of them matches the filter. Always <code>false</code> if the 1488 * feature has no children. 1489 * 1490 * @author Matthew Pocock 1491 * @author Thomas Down 1492 * @since 1.2 1493 */ 1494 1495 public static class ByDescendant implements OptimizableFilter, Down { 1496 static { WalkerFactory.getInstance().addTypeWithParent(ByDescendant.class); } 1497 1498 private FeatureFilter filter; 1499 1500 public ByDescendant(FeatureFilter ff) { 1501 filter = ff; 1502 } 1503 1504 public FeatureFilter getFilter() { 1505 return filter; 1506 } 1507 1508 public boolean accept(Feature f) { 1509 do { 1510 FeatureHolder fh = f.getParent(); 1511 if (fh instanceof Feature) { 1512 f = (Feature) fh; 1513 if (filter.accept(f)) { 1514 return true; 1515 } 1516 } else { 1517 return false; 1518 } 1519 } while (true); 1520 } 1521 1522 public int hashCode() { 1523 return filter.hashCode() + 186; 1524 } 1525 1526 public boolean equals(Object o) { 1527 if (! (o instanceof FeatureFilter.ByDescendant)) { 1528 return false; 1529 } 1530 1531 FeatureFilter.ByDescendant ffba = (FeatureFilter.ByDescendant) o; 1532 return ffba.getFilter().equals(filter); 1533 } 1534 1535 public boolean isProperSubset(FeatureFilter ff) { 1536 FeatureFilter ancFilter = null; 1537 if (ff instanceof FeatureFilter.ByDescendant) { 1538 ancFilter = ((FeatureFilter.ByDescendant) ff).getFilter(); 1539 } 1540 1541 if (ancFilter != null) { 1542 return FilterUtils.areProperSubset(ancFilter, filter); 1543 } else { 1544 return false; 1545 } 1546 } 1547 1548 public boolean isDisjoint(FeatureFilter ff) { 1549 if (ff instanceof OnlyDescendants) { 1550 return FilterUtils.areDisjoint( 1551 getFilter(), 1552 ((OnlyDescendants) ff).getFilter() 1553 ); 1554 } else { 1555 return false; 1556 } 1557 } 1558 } 1559 1560 /** 1561 * Accept features with a given reading frame. 1562 * 1563 * @author Mark Schreiber 1564 * @since 1.2 1565 */ 1566 public final static class FrameFilter implements OptimizableFilter { 1567 private FramedFeature.ReadingFrame frame; 1568 1569 /** 1570 * Build a new filter that matches all features of a reading frame. 1571 * 1572 * @param frame the ReadingFrame to match 1573 */ 1574 public FrameFilter(FramedFeature.ReadingFrame frame) { 1575 this.frame = frame; 1576 } 1577 1578 /** 1579 * Retrieve the reading frame this filter matches. 1580 */ 1581 public FramedFeature.ReadingFrame getFrame(){ 1582 return frame; 1583 } 1584 1585 /** 1586 * Accept the Feature if it is an instance of FramedFeature and matches 1587 * the value of getFrame(). 1588 * 1589 * @param f the Feature to check 1590 * @return true if the frame matches, or false otherwise 1591 */ 1592 public boolean accept(Feature f) { 1593 if(f instanceof FramedFeature) { 1594 FramedFeature ff = (FramedFeature) f; 1595 return ff.getReadingFrame() == frame; 1596 } else { 1597 return false; 1598 } 1599 } 1600 1601 public int hashCode() { 1602 return frame.getFrame() + 99; 1603 } 1604 1605 public boolean equals(Object o) { 1606 return (o instanceof FrameFilter && ((FrameFilter) o).getFrame() == getFrame()); 1607 } 1608 1609 public boolean isProperSubset(FeatureFilter sup) { 1610 return this.equals(sup); 1611 } 1612 1613 public boolean isDisjoint(FeatureFilter filt) { 1614 return (filt instanceof AcceptNoneFilter) || ( 1615 (filt instanceof FrameFilter) && 1616 ((FrameFilter) filt).getFrame() == getFrame() 1617 ); 1618 } 1619 } 1620 1621 /** 1622 * <code>ByPairwiseScore</code> is used to filter 1623 * <code>SimilarityPairFeature</code>s by their score. Features 1624 * are accepted if their score falls between the filter's minimum 1625 * and maximum values, inclusive. Features are rejected if they 1626 * are not <code>SimilarityPairFeature</code>s. The minimum value 1627 * accepted must be less than the maximum value. 1628 * 1629 * @author Keith James 1630 * @since 1.3 1631 */ 1632 public static final class ByPairwiseScore implements OptimizableFilter { 1633 private double minScore; 1634 private double maxScore; 1635 private double score; 1636 private int hashCode; 1637 1638 /** 1639 * Creates a new <code>ByPairwiseScore</code>. 1640 * 1641 * @param minScore a <code>double</code>. 1642 * @param maxScore a <code>double</code>. 1643 */ 1644 public ByPairwiseScore(double minScore, double maxScore) { 1645 if (minScore > maxScore) 1646 throw new IllegalArgumentException("Filter minimum score must be less than maximum score"); 1647 1648 this.minScore = minScore; 1649 this.maxScore = maxScore; 1650 1651 hashCode += (minScore == 0.0 ? 0L : Double.doubleToLongBits(minScore)); 1652 hashCode += (maxScore == 0.0 ? 0L : Double.doubleToLongBits(maxScore)); 1653 } 1654 1655 /** 1656 * Accept a Feature if it is an instance of 1657 * SimilarityPairFeature and its score is <= filter's minimum 1658 * score and >= filter's maximum score. 1659 * 1660 * @param f a <code>Feature</code>. 1661 * @return a <code>boolean</code>. 1662 */ 1663 public boolean accept(Feature f) { 1664 if (! (f instanceof SimilarityPairFeature)) { 1665 return false; 1666 } 1667 1668 score = ((SimilarityPairFeature) f).getScore(); 1669 return (score >= minScore && 1670 score <= maxScore); 1671 } 1672 1673 /** 1674 * <code>getMinScore</code> returns the minimum score 1675 * accepted. 1676 * 1677 * @return a <code>double</code>. 1678 */ 1679 public double getMinScore() { 1680 return minScore; 1681 } 1682 1683 /** 1684 * <code>getMaxScore</code> returns the maximum score 1685 * accepted. 1686 * 1687 * @return a <code>double</code>. 1688 */ 1689 public double getMaxScore() { 1690 return maxScore; 1691 } 1692 1693 public boolean equals(Object o) { 1694 if (o instanceof ByPairwiseScore) { 1695 ByPairwiseScore psf = (ByPairwiseScore) o; 1696 if (psf.getMinScore() == minScore && 1697 psf.getMaxScore() == maxScore) { 1698 return true; 1699 } 1700 } 1701 return false; 1702 } 1703 1704 public int hashCode() { 1705 return hashCode; 1706 } 1707 1708 public boolean isProperSubset(FeatureFilter sup) { 1709 if (sup instanceof ByPairwiseScore) { 1710 ByPairwiseScore psf = (ByPairwiseScore) sup; 1711 return (psf.getMinScore() <= minScore && 1712 psf.getMaxScore() >= maxScore); 1713 } 1714 return false; 1715 } 1716 1717 public boolean isDisjoint(FeatureFilter filt) { 1718 if (filt instanceof AcceptNoneFilter) 1719 return true; 1720 1721 if (filt instanceof ByPairwiseScore) { 1722 ByPairwiseScore psf = (ByPairwiseScore) filt; 1723 return (psf.getMaxScore() < minScore || 1724 psf.getMinScore() > maxScore); 1725 } 1726 return false; 1727 } 1728 1729 public String toString() { 1730 return minScore + " >= score <= " + maxScore; 1731 } 1732 } 1733 1734 /** 1735 * Accepts features which are ComponentFeatures and have a <code>componentSequenceName</code> 1736 * property of the specified value. 1737 * 1738 * @author Thomas Down 1739 * @since 1.3 1740 */ 1741 1742 public final static class ByComponentName implements OptimizableFilter { 1743 private String cname; 1744 1745 public ByComponentName(String cname) { 1746 this.cname = cname; 1747 } 1748 1749 public boolean accept(Feature f) { 1750 if (f instanceof ComponentFeature) { 1751 return cname.equals(((ComponentFeature) f).getComponentSequenceName()); 1752 } else { 1753 return false; 1754 } 1755 } 1756 1757 public String getComponentName() { 1758 return cname; 1759 } 1760 1761 public boolean equals(Object o) { 1762 return (o instanceof ByComponentName) && ((ByComponentName) o).getComponentName().equals(cname); 1763 } 1764 1765 public int hashCode() { 1766 return getComponentName().hashCode(); 1767 } 1768 1769 public boolean isProperSubset(FeatureFilter sup) { 1770 if (sup instanceof ByComponentName) { 1771 return equals(sup); 1772 } else if (sup instanceof ByClass) { 1773 return ((ByClass) sup).getTestClass().isAssignableFrom(ComponentFeature.class); 1774 } else { 1775 return (sup instanceof AcceptAllFilter); 1776 } 1777 } 1778 1779 public boolean isDisjoint(FeatureFilter feat) { 1780 if (feat instanceof ByComponentName) { 1781 return !equals(feat); 1782 } else if (feat instanceof ByClass) { 1783 Class featC = ((ByClass) feat).getTestClass(); 1784 return ! (featC.isAssignableFrom(ComponentFeature.class)); 1785 } else { 1786 return (feat instanceof AcceptNoneFilter); 1787 } 1788 } 1789 1790 public String toString() { 1791 return "ByComponentName(" + cname + ")"; 1792 } 1793 } 1794 1795 /** 1796 * A filter which accepts only top-level Features. This is true 1797 * is <code>getParent()</code> returns a <code>Sequence</code> instance. 1798 * 1799 * @since 1.3 1800 */ 1801 1802 public static final FeatureFilter top_level = new IsTopLevel(); 1803 1804 /** 1805 * A filter which accepts features with no children 1806 * 1807 * @since 1.3 1808 */ 1809 1810 // public static final FeatureFilter leaf = new IsLeaf(); 1811 public static final FeatureFilter leaf = new FeatureFilter.OnlyChildren(FeatureFilter.none); 1812 1813 // Note: this implements OptimizableFilter, but cheats :-). Consequently, 1814 // other optimizablefilters don't know anything about it. The convenience 1815 // methods on FilterUtils give ByFeature a higher precedence to make 1816 // sure this works out. 1817 1818 /** 1819 * Accept only features which are equal to the specified feature 1820 * 1821 * @author Thomas Down 1822 * @since 1.3 1823 */ 1824 1825 public static final class ByFeature implements OptimizableFilter { 1826 private final Feature feature; 1827 1828 public ByFeature(Feature f) { 1829 this.feature = f; 1830 } 1831 1832 public Feature getFeature() { 1833 return feature; 1834 } 1835 1836 public boolean accept(Feature f) { 1837 return f.equals(feature); 1838 } 1839 1840 public boolean isProperSubset(FeatureFilter ff) { 1841 return ff.accept(feature); 1842 } 1843 1844 public boolean isDisjoint(FeatureFilter ff) { 1845 return !ff.accept(feature); 1846 } 1847 1848 public int hashCode() { 1849 return feature.hashCode() + 65; 1850 } 1851 1852 public boolean equals(Object o) { 1853 if (o instanceof FeatureFilter.ByFeature) { 1854 return ((FeatureFilter.ByFeature) o).getFeature().equals(feature); 1855 } else { 1856 return false; 1857 } 1858 } 1859 } 1860} 1861