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;
}
}