content: /**
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.apache.solr.analysis;
  
  import java.io.IOException;
  
  import org.apache.lucene.analysis.TokenFilter;
  import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
  import org.apache.lucene.analysis.tokenattributes.TypeAttribute;
  
  import static org.apache.solr.analysis.CommonGramsFilter.GRAM_TYPE;
  
  /**
   * Wrap a CommonGramsFilter optimizing phrase queries by only returning single
   * words when they are not a member of a bigram.
   * 
   * Example:
   * <ul>
   * <li>query input to CommonGramsFilter: "the rain in spain falls mainly"
   * <li>output of CommomGramsFilter/input to CommonGramsQueryFilter:
   * |"the, "the-rain"|"rain" "rain-in"|"in, "in-spain"|"spain"|"falls"|"mainly"
   * <li>output of CommonGramsQueryFilter:"the-rain", "rain-in" ,"in-spain",
   * "falls", "mainly"
   * </ul>
   */
  
  /*
   * See:http://hudson.zones.apache.org/hudson/job/Lucene-trunk/javadoc//all/org/apache/lucene/analysis/TokenStream.html and
   * http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/src/java/org/apache/lucene/analysis/package.html?revision=718798
   */
  public final class CommonGramsQueryFilter extends TokenFilter {
  
    private final TypeAttribute typeAttribute = addAttribute(TypeAttribute.class);
    private final PositionIncrementAttribute posIncAttribute = addAttribute(PositionIncrementAttribute.class);
    
    private State previous;
    private String previousType;
    private boolean exhausted;
  
    /**
     * Constructs a new CommonGramsQueryFilter based on the provided CommomGramsFilter 
     * 
     * @param input CommonGramsFilter the QueryFilter will use
     */
    public CommonGramsQueryFilter(CommonGramsFilter input) {
      super(input);
    }
  
    /**
     * {@inheritDoc}
     */
    @Override
    public void reset() throws IOException {
      super.reset();
      previous = null;
      previousType = null;
      exhausted = false;
    }
    
    /**
     * Output bigrams whenever possible to optimize queries. Only output unigrams
     * when they are not a member of a bigram. Example:
     * <ul>
     * <li>input: "the rain in spain falls mainly"
     * <li>output:"the-rain", "rain-in" ,"in-spain", "falls", "mainly"
     * </ul>
     */
    @Override
    public boolean incrementToken() throws IOException {
      while (!exhausted && input.incrementToken()) {
        State current = captureState();
  
        if (previous != null && !isGramType()) {
          restoreState(previous);
          previous = current;
          previousType = typeAttribute.type();
          
          if (isGramType()) {
            posIncAttribute.setPositionIncrement(1);
          }
          return true;
        }
  
        previous = current;
      }
  
      exhausted = true;
  
      if (previous == null || GRAM_TYPE.equals(previousType)) {
        return false;
      }
      
      restoreState(previous);
      previous = null;
      
      if (isGramType()) {
        posIncAttribute.setPositionIncrement(1);
      }
      return true;
    }
  
    // ================================================= Helper Methods ================================================
  
    /**
     * Convenience method to check if the current type is a gram type
     * 
     * @return {@code true} if the current type is a gram type, {@code false} otherwise
     */
    public boolean isGramType() {
      return GRAM_TYPE.equals(typeAttribute.type());
    }
  }
  