Subversion Repositories general

Rev

Rev 1005 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

package ak.zpath;

import java.util.List;
import java.util.ArrayList;
import java.text.ParseException;

public class PathCreator
{
  private PathElement element;
  private List        elements;
  private PathParser  pathParser = new PathParser();

  public PathCreator()
  {
  }

  public List createPath(String path)
    throws ParseException
  {
    List  tokens = pathParser.parse(path);
    Token firstToken;
    int   pos    = -1;

    if(tokens.size() == 0)
      throw new PathCreateException("Empty path", null);

    firstToken = (Token)tokens.get(0);
    elements   = new ArrayList();

    if(firstToken.getType() == Token.TOKEN_SLASH) {
      // the path starts with slash
      PathElement slashElement = new PathElement();

      slashElement.setName("/");
      elements.add(slashElement);
      pos = 0;
    }

    element = null;
    stateElementStart(tokens, pos);
    if(element != null) elements.add(element);  // add the last element

    return elements;
  }

  private int stateElementStart(List tokens, int pos)
    throws PathCreateException
  {
    Token token = (Token)tokens.get(++pos);

    if(element != null) elements.add(element);
    element = new PathElement();

    switch(token.getType()) {
      case Token.TOKEN_NAME:
        element.setName(token.getValue());
        pos = stateElementName(tokens, pos);
        break;

      default:
        throw new PathCreateException("Element name expected", token);
    }

    return pos;
  }

  private int stateElementName(List tokens, int pos)
    throws PathCreateException
  {
    if(++pos >= tokens.size()) return pos;

    Token token = (Token)tokens.get(pos);

    switch(token.getType()) {
      case Token.TOKEN_SLASH:
        pos = stateElementStart(tokens, pos);
        break;

      case Token.TOKEN_OPEN_BRACKET:
        pos = stateElementCondition(tokens, pos);
        break;

      case Token.TOKEN_SHARP:
        pos = stateOperandText(tokens, pos);
        break;

      case Token.TOKEN_AT:
        pos = stateOperandAttribute(tokens, pos);
        break;

      default:
        throw new PathCreateException(
          "Element condition or operand expected", token);
    }

    return pos;
  }

  private int stateOperandText(List tokens, int pos)
    throws PathCreateException
  {
    Token token = (Token)tokens.get(++pos);

    element.setFirstOperand(PathElement.OPERAND_TEXT, null);

    switch(token.getType()) {
      case Token.TOKEN_ASSIGN:
        element.setOperation(PathElement.OPERATION_ASSIGN);
        pos = stateSecondOperand(tokens, pos);
        break;

      case Token.TOKEN_APPEND:
        element.setOperation(PathElement.OPERATION_APPEND);
        pos = stateSecondOperand(tokens, pos);
        break;

      default:
        throw new PathCreateException(
          "Assign or append operation expected", token);
    }

    return pos;
  }

  private int stateOperandAttribute(List tokens, int pos)
    throws PathCreateException
  {
    Token token = (Token)tokens.get(++pos);

    switch(token.getType()) {
      case Token.TOKEN_NAME: {
        element.setFirstOperand(PathElement.OPERAND_ATTRIBUTE, token.getValue());
        token = (Token)tokens.get(++pos);
        switch(token.getType()) {
          case Token.TOKEN_ASSIGN:
            element.setOperation(PathElement.OPERATION_ASSIGN);
            pos = stateSecondOperand(tokens, pos);
            break;

          case Token.TOKEN_APPEND:
            element.setOperation(PathElement.OPERATION_APPEND);
            pos = stateSecondOperand(tokens, pos);
            break;

          default:
            throw new PathCreateException(
              "Assign or append operation expected", token);
        }
        break;
      }

      default:
        throw new PathCreateException("Attribute name expected", token);
    }

    return pos;
  }

  private int stateSecondOperand(List tokens, int pos)
    throws PathCreateException
  {
    Token token = (Token)tokens.get(++pos);

    switch(token.getType()) {
      case Token.TOKEN_VARIABLE:
        element.setSecondOperand(PathElement.OPERAND_VARIABLE, token.getValue());
        if(pos < tokens.size() - 1)
          throw new PathCreateException("Path end expected", token);
        break;

      case Token.TOKEN_STRING:
        element.setSecondOperand(PathElement.OPERAND_STRING, token.getValue());
        if(pos < tokens.size() - 1)
          throw new PathCreateException("Path end expected", token);
        break;

      default:
        throw new PathCreateException("Second operand expected", token);
    }

    element.validateOperands(); // must be end of the path

    return pos;
  }

  private int stateElementCondition(List tokens, int pos)
    throws PathCreateException
  {
    Token token = (Token)tokens.get(++pos);

    switch(token.getType()) {
      case Token.TOKEN_NEW:
        pos = stateNew(tokens, pos);
        break;

      case Token.TOKEN_SHARP:
      case Token.TOKEN_AT:
        pos = stateLogicalCondition(tokens, pos);
        break;

      case Token.TOKEN_INDEX:
        pos = stateIndex(tokens, pos);
        break;

      default:
        throw new PathCreateException("Element condition expected", token);
    }

    return pos;
  }

  private int stateLogicalCondition(List tokens, int pos)
    throws PathCreateException
  {
    Token token = (Token)tokens.get(pos); // do not go to the next token

    switch(token.getType()) {
      case Token.TOKEN_SHARP:
        pos = stateConditionText(tokens, pos);
        break;

      case Token.TOKEN_AT:
        pos = stateConditionAttribute(tokens, pos);
        break;

      default:
        throw new PathCreateException("Element condition expected", token);
    }

    return pos;
  }

  private int stateIndex(List tokens, int pos)
    throws PathCreateException
  {
    Token token = (Token)tokens.get(pos);

    try {
      element.setIndex(Integer.parseInt(token.getValue()));
    }
    catch(NumberFormatException ex) {
        throw new PathCreateException(
          "Cannot parse '" + token.getValue() + "' as integer", token);
    }

    token = (Token)tokens.get(++pos);

    switch(token.getType()) {
      case Token.TOKEN_CLOSE_BRACKET:
        pos = stateConditionEnd(tokens, pos);
        break;

      case Token.TOKEN_AND:
        pos++; // go to next token for stateLogicalCondition
        pos = stateLogicalCondition(tokens, pos);
        break;

      default:
        throw new PathCreateException(
          "End of conditions or next condition expected", token);
    }

    return pos;
  }

  private int stateConditionText(List tokens, int pos)
    throws PathCreateException
  {
    Token token = (Token)tokens.get(++pos);

    element.addCondition();
    element.setFirstCondition(PathElement.OPERAND_TEXT, null);

    switch(token.getType()) {
      case Token.TOKEN_EQUAL:
        element.setConditionOperation(PathElement.OPERATION_EQUAL);
        pos = stateConditionSecond(tokens, pos);
        break;

      default:
        throw new PathCreateException("Compare operator expected", token);
    }

    return pos;
  }

  private int stateConditionAttribute(List tokens, int pos)
    throws PathCreateException
  {
    Token token = (Token)tokens.get(++pos);

    element.addCondition();

    switch(token.getType()) {
      case Token.TOKEN_NAME: {
        element.setFirstCondition(
          PathElement.OPERAND_ATTRIBUTE, token.getValue());
        token = (Token)tokens.get(++pos);
        switch(token.getType()) {
          case Token.TOKEN_EQUAL:
            element.setConditionOperation(PathElement.OPERATION_EQUAL);
            pos = stateConditionSecond(tokens, pos);
            break;

          default:
            throw new PathCreateException("Compare operation expected", token);
        }
        break;
      }

      default:
        throw new PathCreateException("Attibute name expected", token);
    }

    return pos;
  }

  private int stateConditionSecond(List tokens, int pos)
    throws PathCreateException
  {
    Token token = (Token)tokens.get(++pos);
    switch(token.getType()) {
      case Token.TOKEN_VARIABLE:
        element.setSecondCondition(
          PathElement.OPERAND_VARIABLE, token.getValue());
        break;

      case Token.TOKEN_STRING:
        element.setSecondCondition(
          PathElement.OPERAND_STRING, token.getValue());
        break;

      default:
        throw new PathCreateException(
          "Second condition operand expected", token);
    }
    element.validateCondition(); // must be end of condition

    token = (Token)tokens.get(++pos);
    switch(token.getType()) {
      case Token.TOKEN_CLOSE_BRACKET:
        pos = stateConditionEnd(tokens, pos);
        break;

      case Token.TOKEN_AND:
        pos++; // go to next token for stateLogicalCondition
        pos = stateLogicalCondition(tokens, pos);
        break;

      default:
        throw new PathCreateException(
          "End of conditions or next condition expected", token);
    }

    return pos;
  }

  private int stateNew(List tokens, int pos)
    throws PathCreateException
  {
    Token token = (Token)tokens.get(++pos);

    element.setNew();

    switch(token.getType()) {
      case Token.TOKEN_CLOSE_BRACKET:
        pos = stateConditionEnd(tokens, pos);
        break;

      default:
        throw new PathCreateException("End of conditions expected", token);
    }

    return pos;
  }

  private int stateConditionEnd(List tokens, int pos)
    throws PathCreateException
  {
    if(++pos >= tokens.size()) return pos;

    Token token = (Token)tokens.get(pos);

    switch(token.getType()) {
      case Token.TOKEN_SLASH:
        stateElementStart(tokens, pos); // start new path element
        break;

      case Token.TOKEN_SHARP:
        pos = stateOperandText(tokens, pos);
        break;

      case Token.TOKEN_AT:
        pos = stateOperandAttribute(tokens, pos);
        break;

      default:
        throw new PathCreateException(
          "End of element description expected", token);
    }

    return pos;
  }
}