MtasCQLParserSentenceCondition.java

package mtas.parser.cql.util;

import java.util.ArrayList;
import java.util.List;

import mtas.parser.cql.ParseException;
import mtas.search.spans.MtasSpanOrQuery;
import mtas.search.spans.MtasSpanRecurrenceQuery;
import mtas.search.spans.MtasSpanSequenceItem;
import mtas.search.spans.MtasSpanSequenceQuery;
import mtas.search.spans.util.MtasSpanQuery;

/**
 * The Class MtasCQLParserSentenceCondition.
 */
public class MtasCQLParserSentenceCondition {

  /** The sequence list. */
  // parent list: multiple items for OR
  // child list: sequence
  private List<List<MtasCQLParserSentenceCondition>> sequenceList;

  /** The basic sentence. */
  private MtasCQLParserBasicSentenceCondition basicSentence = null;

  /** The minimum occurence. */
  private int minimumOccurence;

  /** The maximum occurence. */
  private int maximumOccurence;

  /** The basic. */
  private boolean basic;

  /** The simplified. */
  private boolean simplified;

  /** The optional. */
  private boolean optional;

  /** The ignore. */
  private MtasSpanQuery ignore;

  /** The maximum ignore length. */
  private Integer maximumIgnoreLength;

  /**
   * Instantiates a new mtas CQL parser sentence condition.
   *
   * @param s the s
   * @param ignore the ignore
   * @param maximumIgnoreLength the maximum ignore length
   * @throws ParseException the parse exception
   */
  public MtasCQLParserSentenceCondition(MtasCQLParserBasicSentenceCondition s,
      MtasSpanQuery ignore, Integer maximumIgnoreLength) throws ParseException {
    sequenceList = new ArrayList<List<MtasCQLParserSentenceCondition>>();
    basicSentence = s;
    minimumOccurence = 1;
    maximumOccurence = 1;
    simplified = false;
    basic = true;
    optional = false;
    this.ignore = ignore;
    this.maximumIgnoreLength = maximumIgnoreLength;
  }

  /**
   * Instantiates a new mtas CQL parser sentence condition.
   *
   * @param sp the sp
   * @param ignore the ignore
   * @param maximumIgnoreLength the maximum ignore length
   * @throws ParseException the parse exception
   */
  public MtasCQLParserSentenceCondition(MtasCQLParserSentenceCondition sp,
      MtasSpanQuery ignore, Integer maximumIgnoreLength) throws ParseException {
    sequenceList = new ArrayList<List<MtasCQLParserSentenceCondition>>();
    addSentenceToEndLatestSequence(sp);
    minimumOccurence = 1;
    maximumOccurence = 1;
    simplified = false;
    basic = false;
    optional = false;
    this.ignore = ignore;
    this.maximumIgnoreLength = maximumIgnoreLength;
  }

  /**
   * Adds the basic sentence to end latest sequence.
   *
   * @param s the s
   * @throws ParseException the parse exception
   */
  public void addBasicSentenceToEndLatestSequence(
      MtasCQLParserBasicSentenceCondition s) throws ParseException {
    if (!simplified) {
      if (isBasic()) {
        if (basicSentence == null) {
          basicSentence = s;
        } else {
          basicSentence.addBasicSentence(s);
        }
      } else {
        MtasCQLParserSentenceCondition sentenceCurrent = new MtasCQLParserSentenceCondition(
            s, ignore, maximumIgnoreLength);
        if (sequenceList.size() == 0) {
          sequenceList.add(new ArrayList<MtasCQLParserSentenceCondition>());
        }
        sequenceList.get(sequenceList.size() - 1).add(sentenceCurrent);
      }
    } else {
      throw new ParseException("already simplified");
    }
  }

  /**
   * Adds the sentence to end latest sequence.
   *
   * @param s the s
   * @throws ParseException the parse exception
   */
  // public void addBasicSentenceAsOption(MtasCQLParserBasicSentenceCondition s)
  // throws ParseException {
  // if (!simplified) {
  // MtasCQLParserSentenceCondition sentenceCurrent;
  // List<MtasCQLParserSentenceCondition> sentenceSequence;
  // if (isBasic()) {
  // if (basicSentence == null) {
  // basicSentence = s;
  // } else {
  // // add previous basic sentence as first option
  // sentenceSequence = new ArrayList<MtasCQLParserSentenceCondition>();
  // sentenceCurrent = new MtasCQLParserSentenceCondition(basicSentence,
  // ignore, maximumIgnoreLength);
  // sentenceSequence.add(sentenceCurrent);
  // sequenceList.add(sentenceSequence);
  // basicSentence = null;
  // // create new option for current basic sentence
  // sentenceSequence = new ArrayList<MtasCQLParserSentenceCondition>();
  // sentenceCurrent = new MtasCQLParserSentenceCondition(s, ignore,
  // maximumIgnoreLength);
  // sentenceSequence.add(sentenceCurrent);
  // sequenceList.add(sentenceSequence);
  // // not basic anymore
  // basic = false;
  // }
  // } else {
  // sentenceSequence = new ArrayList<MtasCQLParserSentenceCondition>();
  // sentenceCurrent = new MtasCQLParserSentenceCondition(s, ignore,
  // maximumIgnoreLength);
  // sentenceSequence.add(sentenceCurrent);
  // sequenceList.add(sentenceSequence);
  // }
  // } else {
  // throw new ParseException("already simplified");
  // }
  // }

  /**
   * Adds the sentence to start first sequence.
   *
   * @param s
   *          the s
   * @throws ParseException
   *           the parse exception
   */
  // public void addSentenceToStartFirstSequence(MtasCQLParserSentenceCondition
  // s)
  // throws ParseException {
  // if (!simplified) {
  // MtasCQLParserSentenceCondition sentenceCurrent;
  // List<MtasCQLParserSentenceCondition> sentenceSequence;
  // if (isBasic()) {
  // if (basicSentence == null) {
  // sentenceSequence = new ArrayList<MtasCQLParserSentenceCondition>();
  // sentenceCurrent = s;
  // sentenceSequence.add(sentenceCurrent);
  // sequenceList.add(sentenceSequence);
  // // not basic anymore
  // basic = false;
  // } else {
  // // add sentence as first item in new sequence
  // sentenceSequence = new ArrayList<MtasCQLParserSentenceCondition>();
  // // add sentence to first option
  // sentenceCurrent = s;
  // sentenceSequence.add(sentenceCurrent);
  // // add basic sentence as second item
  // sentenceCurrent = new MtasCQLParserSentenceCondition(basicSentence,
  // ignore, maximumIgnoreLength);
  // sentenceSequence.add(sentenceCurrent);
  // sequenceList.add(sentenceSequence);
  // basicSentence = null;
  // // not simple anymore
  // basic = false;
  // }
  // } else {
  // sentenceSequence = new ArrayList<MtasCQLParserSentenceCondition>();
  // sentenceSequence.add(s);
  // sentenceSequence.addAll(sequenceList.get(0));
  // sequenceList.set(0, sentenceSequence);
  // sentenceSequence = sequenceList.get((sequenceList.size() - 1));
  // sentenceCurrent = sentenceSequence.get((sentenceSequence.size() - 1));
  // }
  // } else {
  // throw new ParseException("already simplified");
  // }
  // }

  /**
   * Adds the sentence to end latest sequence.
   *
   * @param s
   *          the s
   * @throws ParseException
   *           the parse exception
   */
  public void addSentenceToEndLatestSequence(MtasCQLParserSentenceCondition s)
      throws ParseException {
    if (!simplified) {
      MtasCQLParserSentenceCondition sentenceCurrent;
      List<MtasCQLParserSentenceCondition> sentenceSequence;
      if (isBasic()) {
        if (basicSentence == null) {
          sentenceSequence = new ArrayList<MtasCQLParserSentenceCondition>();
          sentenceCurrent = s;
          sentenceSequence.add(sentenceCurrent);
          sequenceList.add(sentenceSequence);
          // not simple anymore
          basic = false;
        } else {
          // add previous basic sentence as first option
          sentenceSequence = new ArrayList<MtasCQLParserSentenceCondition>();
          sentenceCurrent = new MtasCQLParserSentenceCondition(basicSentence,
              ignore, maximumIgnoreLength);
          sentenceSequence.add(sentenceCurrent);
          sequenceList.add(sentenceSequence);
          basicSentence = null;
          // add sentence to first option
          sentenceCurrent = s;
          sentenceSequence.add(sentenceCurrent);
          // not simple anymore
          basic = false;
        }
      } else {
        sentenceCurrent = s;
        if (sequenceList.size() == 0) {
          sequenceList.add(new ArrayList<MtasCQLParserSentenceCondition>());
        }
        sequenceList.get(sequenceList.size() - 1).add(sentenceCurrent);
      }
    } else {
      throw new ParseException("already simplified");
    }
  }

  /**
   * Adds the sentence as first option.
   *
   * @param s the s
   * @throws ParseException the parse exception
   */
  public void addSentenceAsFirstOption(MtasCQLParserSentenceCondition s)
      throws ParseException {
    if (!simplified) {
      MtasCQLParserSentenceCondition sentenceCurrent;
      List<MtasCQLParserSentenceCondition> sentenceSequence;
      if (isBasic()) {
        if (basicSentence == null) {
          sentenceSequence = new ArrayList<MtasCQLParserSentenceCondition>();
          sentenceCurrent = s;
          sentenceSequence.add(sentenceCurrent);
          sequenceList.add(sentenceSequence);
          // not simple anymore
          basic = false;
        } else {
          // add sentence as first option
          sentenceSequence = new ArrayList<MtasCQLParserSentenceCondition>();
          sentenceCurrent = s;
          sentenceSequence.add(sentenceCurrent);
          sequenceList.add(sentenceSequence);
          // add previous basic sentence as new option
          sentenceSequence = new ArrayList<MtasCQLParserSentenceCondition>();
          sentenceCurrent = new MtasCQLParserSentenceCondition(basicSentence,
              ignore, maximumIgnoreLength);
          sentenceSequence.add(sentenceCurrent);
          sequenceList.add(sentenceSequence);
          basicSentence = null;
          // not simple anymore
          basic = false;
        }
      } else {
        sentenceSequence = new ArrayList<MtasCQLParserSentenceCondition>();
        sentenceCurrent = s;
        sentenceSequence.add(sentenceCurrent);
        List<List<MtasCQLParserSentenceCondition>> newsequenceList = new ArrayList<List<MtasCQLParserSentenceCondition>>();
        newsequenceList.add(sentenceSequence);
        newsequenceList.addAll(sequenceList);
        sequenceList = newsequenceList;
      }
    } else {
      throw new ParseException("already simplified");
    }
  }

  /**
   * Checks if is basic.
   *
   * @return true, if is basic
   */
  // public void addSentenceAsOption(MtasCQLParserSentenceCondition s)
  // throws ParseException {
  // if (!simplified) {
  // MtasCQLParserSentenceCondition sentenceCurrent;
  // List<MtasCQLParserSentenceCondition> sentenceSequence;
  // if (isBasic()) {
  // if (basicSentence == null) {
  // sentenceSequence = new ArrayList<MtasCQLParserSentenceCondition>();
  // sentenceCurrent = s;
  // sentenceSequence.add(sentenceCurrent);
  // sequenceList.add(sentenceSequence);
  // // not simple anymore
  // basic = false;
  // } else {
  // // add previous basic sentence as first option
  // sentenceSequence = new ArrayList<MtasCQLParserSentenceCondition>();
  // sentenceCurrent = new MtasCQLParserSentenceCondition(basicSentence,
  // ignore, maximumIgnoreLength);
  // sentenceSequence.add(sentenceCurrent);
  // sequenceList.add(sentenceSequence);
  // basicSentence = null;
  // // add sentence as new option
  // sentenceSequence = new ArrayList<MtasCQLParserSentenceCondition>();
  // sentenceCurrent = s;
  // sentenceSequence.add(sentenceCurrent);
  // sequenceList.add(sentenceSequence);
  // // not simple anymore
  // basic = false;
  // }
  // } else {
  // sentenceSequence = new ArrayList<MtasCQLParserSentenceCondition>();
  // sentenceCurrent = s;
  // sentenceSequence.add(sentenceCurrent);
  // sequenceList.add(sentenceSequence);
  // }
  // } else {
  // throw new ParseException("already simplified");
  // }
  // }

  /**
   * Checks if is basic.
   *
   * @return true, if is basic
   */
  private boolean isBasic() {
    return basic;
  }

  /**
   * Checks if is single.
   *
   * @return true, if is single
   */
  private boolean isSingle() {
    return basic ? true : ((sequenceList.size() > 1) ? false : true);
  }

  /**
   * Simplify.
   *
   * @throws ParseException the parse exception
   */
  public void simplify() throws ParseException {
    if (!simplified) {
      if (!isBasic()) {
        for (List<MtasCQLParserSentenceCondition> sequence : sequenceList) {
          simplifySequence(sequence);
        }
        // flatten
        if (sequenceList.size() > 1) {
          List<List<MtasCQLParserSentenceCondition>> newSequenceList = new ArrayList<List<MtasCQLParserSentenceCondition>>();
          for (List<MtasCQLParserSentenceCondition> sequence : sequenceList) {
            if (sequence.size() == 1) {
              MtasCQLParserSentenceCondition subSentence = sequence.get(0);
              if (subSentence.isBasic()) {
                newSequenceList.add(sequence);
              } else {
                newSequenceList.addAll(subSentence.sequenceList);
              }
            }
          }
          sequenceList = newSequenceList;
        }
      }
      simplified = true;
    }
  }

  /**
   * Simplify sequence.
   *
   * @param sequence the sequence
   * @throws ParseException the parse exception
   */
  private void simplifySequence(List<MtasCQLParserSentenceCondition> sequence)
      throws ParseException {
    List<MtasCQLParserSentenceCondition> newSequence = new ArrayList<MtasCQLParserSentenceCondition>();
    MtasCQLParserSentenceCondition lastSentence = null;
    for (MtasCQLParserSentenceCondition sentence : sequence) {
      sentence.simplify();
      if (lastSentence == null) {
        lastSentence = sentence;
      } else if (lastSentence.isBasic() && sentence.isBasic()) {
        if (!lastSentence.isOptional() && !sentence.isOptional()
            && sentence.getMaximumOccurence() == 1
            && lastSentence.getMaximumOccurence() == 1) {
          lastSentence.basicSentence.addBasicSentence(sentence.basicSentence);
        } else {
          newSequence.add(lastSentence);
          lastSentence = sentence;
        }
      } else if (lastSentence.isBasic() && !sentence.isBasic()) {
        if (sentence.isSingle() && !sentence.isOptional()
            && sentence.getMaximumOccurence() == 1
            && lastSentence.getMaximumOccurence() == 1) {
          // add all items from (first) sequenceList potentially to the new
          // sequence
          for (MtasCQLParserSentenceCondition subSentence : sentence.sequenceList
              .get(0)) {
            newSequence.add(lastSentence);
            lastSentence = subSentence;
          }
        } else {
          // add sentence potentially to the new sequence
          newSequence.add(lastSentence);
          lastSentence = sentence;
        }
      } else if (!lastSentence.isBasic() && sentence.isBasic()) {
        if (lastSentence.isSingle() && !lastSentence.isOptional()
            && sentence.getMaximumOccurence() == 1
            && lastSentence.getMaximumOccurence() == 1) {
          // add basic sentence to end latest sequence
          lastSentence
              .addBasicSentenceToEndLatestSequence(sentence.basicSentence);
        } else {
          // add sentence potentially to the new sequence
          newSequence.add(lastSentence);
          lastSentence = sentence;
        }
      } else {
        if (sentence.isSingle() && !sentence.isOptional()
            && lastSentence.isSingle() && !lastSentence.isOptional()
            && sentence.getMaximumOccurence() == 1
            && lastSentence.getMaximumOccurence() == 1) {
          // combine sentences
          for (MtasCQLParserSentenceCondition subSentence : sentence.sequenceList
              .get(0)) {
            lastSentence.sequenceList.get(0).add(subSentence);
          }
        } else {
          // add sentence potentially to the new sequence (both not basic)
          newSequence.add(lastSentence);
          lastSentence = sentence;
        }
      }
    }
    // add last to newSequence
    if (lastSentence != null) {
      newSequence.add(lastSentence);
    }
    // replace content sequence with newSequence
    sequence.clear();
    sequence.addAll(newSequence);
  }

  /**
   * Gets the minimum occurence.
   *
   * @return the minimum occurence
   */
  public int getMinimumOccurence() {
    return minimumOccurence;
  }

  /**
   * Gets the maximum occurence.
   *
   * @return the maximum occurence
   */
  public int getMaximumOccurence() {
    return maximumOccurence;
  }

  /**
   * Sets the occurence.
   *
   * @param min the min
   * @param max the max
   * @throws ParseException the parse exception
   */
  public void setOccurence(int min, int max) throws ParseException {
    if ((min < 0) || (min > max) || (max < 1)) {
      throw new ParseException("Illegal number {" + min + "," + max + "}");
    }
    if (min == 0) {
      optional = true;
    }
    minimumOccurence = Math.max(1, min);
    maximumOccurence = max;
  }

  /**
   * Checks if is optional.
   *
   * @return true, if is optional
   */
  public boolean isOptional() {
    return optional;
  }

  /**
   * Sets the optional.
   *
   * @param status the new optional
   */
  // public boolean hasOptionalParts() throws ParseException {
  // if (simplified) {
  // return optionalParts;
  // } else {
  // throw new ParseException("can't be called when not simplified");
  // }
  // }

  /**
   * Sets the optional.
   *
   * @param status
   *          the new optional
   */
  public void setOptional(boolean status) {
    optional = status;
  }

  /**
   * Creates the query.
   *
   * @param sentenceSequence the sentence sequence
   * @return the mtas span query
   * @throws ParseException the parse exception
   */
  private MtasSpanQuery createQuery(
      List<MtasCQLParserSentenceCondition> sentenceSequence)
      throws ParseException {
    if (sentenceSequence.size() == 1) {
      if (maximumOccurence > 1) {
        return new MtasSpanRecurrenceQuery(sentenceSequence.get(0).getQuery(),
            minimumOccurence, maximumOccurence, ignore, maximumIgnoreLength);
      } else {
        return sentenceSequence.get(0).getQuery();
      }
    } else {
      List<MtasSpanSequenceItem> clauses = new ArrayList<MtasSpanSequenceItem>();
      for (MtasCQLParserSentenceCondition sentence : sentenceSequence) {
        clauses.add(
            new MtasSpanSequenceItem(sentence.getQuery(), sentence.optional));
      }
      if (maximumOccurence > 1) {
        return new MtasSpanRecurrenceQuery(
            new MtasSpanSequenceQuery(clauses, ignore, maximumIgnoreLength),
            minimumOccurence, maximumOccurence, ignore, maximumIgnoreLength);
      } else {
        return new MtasSpanSequenceQuery(clauses, ignore, maximumIgnoreLength);
      }
    }
  }

  /**
   * Gets the query.
   *
   * @return the query
   * @throws ParseException the parse exception
   */
  public MtasSpanQuery getQuery() throws ParseException {
    simplify();
    if (isBasic()) {
      MtasSpanQuery query;
      if (basicSentence == null) {
        throw new ParseException("no condition");
      } else if (basicSentence.isOptional()) {
        List<MtasSpanSequenceItem> clauses = new ArrayList<MtasSpanSequenceItem>();
        clauses.add(new MtasSpanSequenceItem(basicSentence.getQuery(),
            basicSentence.isOptional()));
        query = new MtasSpanSequenceQuery(clauses, ignore, maximumIgnoreLength);
        if (maximumOccurence > 1) {
          query = new MtasSpanRecurrenceQuery(query, minimumOccurence,
              maximumOccurence, ignore, maximumIgnoreLength);
        }
      } else {
        query = basicSentence.getQuery();
        if (maximumOccurence > 1) {
          query = new MtasSpanRecurrenceQuery(query, minimumOccurence,
              maximumOccurence, ignore, maximumIgnoreLength);
        }
      }
      return query;
    } else if (sequenceList.isEmpty()) {
      throw new ParseException("no condition");
    } else if (isSingle()) {
      return createQuery(sequenceList.get(0));
    } else {
      List<MtasSpanQuery> clauses = new ArrayList<MtasSpanQuery>();
      for (List<MtasCQLParserSentenceCondition> sentenceSequence : sequenceList) {
        clauses.add(createQuery(sentenceSequence));
      }
      return new MtasSpanOrQuery(
          clauses.toArray(new MtasSpanQuery[clauses.size()]));
    }
  }

  /*
   * (non-Javadoc)
   * 
   * @see java.lang.Object#toString()
   */
  @Override
  public String toString() {
    return toString("", "");
  }

  /**
   * To string.
   *
   * @param firstIndent the first indent
   * @param indent the indent
   * @return the string
   */
  public String toString(String firstIndent, String indent) {
    String text = "";
    if (isBasic()) {
      try {
        text += firstIndent + "BASIC SENTENCE" + (optional ? " OPTIONAL" : "")
            + ": " + basicSentence.getQuery()
            + (basicSentence.isOptional() ? " OPTIONAL" : "") + "\n";
      } catch (ParseException e) {
        text += firstIndent + "BASIC SENTENCE" + (optional ? " OPTIONAL" : "")
            + ": " + e.getMessage() + "\n";
      }
    } else {
      text += firstIndent + "SENTENCE" + (optional ? " OPTIONAL" : "") + "\n";
      if (simplified) {
        try {
          text += indent + "- Query: "
              + getQuery().toString(getQuery().getField());
        } catch (ParseException e) {
          text += indent + "- Query: " + e.getMessage();
        }
        text += "\n";
      } else {
        for (List<MtasCQLParserSentenceCondition> sentenceSequence : sequenceList) {
          text += indent + "- Sequence :\n";
          for (MtasCQLParserSentenceCondition sentence : sentenceSequence) {
            text += sentence.toString(indent + "  - ", indent + "    ");
          }
        }
        text += "\n";
      }
    }
    return text;
  }

}