@Override
 public TextPattern combineWithPrecedingPart(TextPattern previousPart) {
   if (previousPart.hasConstantLength()) {
     // We "gobble up" the previous part and adjust our left matching edge.
     // This should make filtering more efficient, since we will likely have fewer hits to filter.
     TextPatternPositionFilter result = (TextPatternPositionFilter) clone();
     result.clauses.set(0, new TextPatternSequence(previousPart, clauses.get(0)));
     result.adjustLeft(previousPart.getMinLength());
     return result;
   }
   return super.combineWithPrecedingPart(previousPart);
 }
  @Override
  public TextPattern rewrite() {
    TextPattern producer = clauses.get(0).rewrite();
    TextPattern filter = clauses.get(1).rewrite();

    if (!invert
        && op != Operation.STARTS_AT
        && op != Operation.ENDS_AT
        && producer instanceof TextPatternAnyToken) {
      // We're filtering "all n-grams of length min-max".
      // Use the special optimized TextPatternFilterNGrams.
      TextPatternAnyToken tp = (TextPatternAnyToken) producer;
      return new TextPatternFilterNGrams(filter, op, tp.getMinLength(), tp.getMaxLength());
    }

    if (producer != clauses.get(0) || filter != clauses.get(1)) {
      TextPatternPositionFilter result =
          new TextPatternPositionFilter(producer, filter, op, invert);
      result.leftAdjust = leftAdjust;
      result.rightAdjust = rightAdjust;
      return result;
    }
    return this;
  }