/* LanguageTool, a natural language style checker 
 * Copyright (C) 2005 Daniel Naber (http://www.danielnaber.de)
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 * USA
 */
package de.danielnaber.languagetool.dev.index;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.regex.Pattern;

import org.apache.lucene.index.Term;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.regex.JavaUtilRegexCapabilities;
import org.apache.lucene.search.spans.SpanQuery;

import de.danielnaber.languagetool.rules.patterns.Element;
import de.danielnaber.languagetool.rules.patterns.PatternRule;

/**
 * A factory class for building a Query from a PatternRule.
 * 
 * @author Tao Lin
 * 
 */
public class PatternRuleQueryBuilder {

  public static final String FIELD_NAME = "field";

  private static final int MAX_SKIP = 1000;

  public static Query buildQuery(PatternRule rule) throws UnsupportedPatternRuleException {
    return next(rule.getElements().iterator());
  }

  // create the next SpanQuery from the top Element in the Iterator.
  private static SpanQuery next(Iterator<Element> it) throws UnsupportedPatternRuleException {

    // no more Element
    if (!it.hasNext()) {
      return null;
    }

    final Element patternElement = it.next();

    checkUnsupportedRule(patternElement);

    final ArrayList<SpanQuery> list = new ArrayList<SpanQuery>();

    int skip = 0;

    final boolean caseSensitive = patternElement.getCaseSensitive();

    final SpanQuery tokenQuery = createTokenQuery(patternElement.getString(),
        patternElement.getNegation(), patternElement.isRegularExpression(), caseSensitive);

    final SpanQuery posQuery = createPOSQuery(patternElement.getPOStag(),
        patternElement.getPOSNegation(), patternElement.isPOStagRegularExpression());

    if (tokenQuery != null && posQuery != null) {
      final RigidSpanNearQuery q = new RigidSpanNearQuery(new SpanQuery[] { tokenQuery, posQuery },
          0, true);
      list.add(q);
    } else if (tokenQuery != null) {
      list.add(tokenQuery);
    } else if (posQuery != null) {
      list.add(posQuery);
    } else {
      skip++;
    }
    if (patternElement.getSkipNext() >= 0) {
      skip += patternElement.getSkipNext();
    } else {
      // skip == -1
      skip = MAX_SKIP;
    }

    // recursion invoke
    final SpanQuery next = next(it);

    if (next != null) {
      list.add(next);
      return new RigidSpanNearQuery(list.toArray(new SpanQuery[list.size()]), skip + 1, true);
    } else if (list.size() > 0) {
      return list.get(0);
    } else {
      return null;
    }

  }

  private static void checkUnsupportedRule(Element patternElement)
      throws UnsupportedPatternRuleException {

    // unsupported rule features
    if (patternElement.hasExceptionList()) {
      throw new UnsupportedPatternRuleException(
          "Pattern rules with token exceptions are not supported.");
    }

    if (patternElement.hasAndGroup()) {
      throw new UnsupportedPatternRuleException(
          "Pattern rules with tokens in \"And Group\" are not supported.");
    }

    if (patternElement.isPartOfPhrase()) {
      throw new UnsupportedPatternRuleException("Pattern rules with phrases are not supported.");
    }

    if (patternElement.isUnified()) {
      throw new UnsupportedPatternRuleException(
          "Pattern rules with unified tokens are not supported.");
    }

    if (patternElement.isInflected()) {
      throw new UnsupportedPatternRuleException(
          "Pattern rules with inflated tokens are not supported.");
    }

    if (patternElement.testWhitespace()) {
      throw new UnsupportedPatternRuleException(
          "Pattern rules with tokens testing \"Whitespace before\" are not supported.");
    }
  }

  private static SpanQuery createTokenQuery(String token, boolean isNegation,
      boolean isRegularExpression, boolean caseSensitive) {
    SpanQuery q = null;
    if (token != null && !token.equals("")) {
      if (!isNegation) {
        String text = isRegularExpression ? token : Pattern.quote(token);
        final Term term = new Term(FIELD_NAME, text);
        q = new POSAwaredSpanRegexQuery(term, false);
        if (!caseSensitive) {
          ((POSAwaredSpanRegexQuery) q).setRegexImplementation(new JavaUtilRegexCapabilities(
              JavaUtilRegexCapabilities.FLAG_CASE_INSENSITIVE));
        }
      } else {
        String text = isRegularExpression ? token : Pattern.quote(token);
        final Term term = new Term(FIELD_NAME, text);
        q = new POSAwaredSpanRegexNotQuery(term, false);
        if (!caseSensitive) {
          ((POSAwaredSpanRegexNotQuery) q).setRegexImplementation(new JavaUtilRegexCapabilities(
              JavaUtilRegexCapabilities.FLAG_CASE_INSENSITIVE));
        }
      }
    }
    return q;
  }

  private static SpanQuery createPOSQuery(String token, boolean isNegation,
      boolean isRegularExpression) {
    SpanQuery q = null;
    if (token != null && !token.equals("")) {
      if (!isNegation) {
        String text = isRegularExpression ? token : Pattern.quote(token);
        final Term term = new Term(FIELD_NAME, text);
        q = new POSAwaredSpanRegexQuery(term, true);
      } else {
        String text = isRegularExpression ? token : Pattern.quote(token);
        final Term term = new Term(FIELD_NAME, text);
        q = new POSAwaredSpanRegexNotQuery(term, true);
      }
    }
    return q;
  }
}

@@ -3561,24 +3561,51 @@
 atures%0D%0A    
+// TODO: comment in%0D%0A    /*
 if (patternE
@@ -3751,32 +3751,34 @@
 orted.%22);%0D%0A    %7D
+*/
 %0D%0A%0D%0A    if (patt
@@ -4425,32 +4425,59 @@
 ;%0D%0A    %7D%0D%0A%0D%0A    
+// TODO: comment in%0D%0A    /*
 if (patternEleme
@@ -4641,24 +4641,26 @@
 d.%22);%0D%0A    %7D
+*/
 %0D%0A  %7D%0D%0A%0D%0A  p

comment out to make code compile again