in oak-core-spi/src/main/java/org/apache/jackrabbit/oak/namepath/JcrPathParser.java [82:287]
public static boolean parse(String jcrPath, Listener listener) {
// check for length
int len = jcrPath == null ? 0 : jcrPath.length();
// shortcut for root path
if (len == 1 && jcrPath.charAt(0) == '/') {
listener.root();
return true;
}
// short cut for empty path
if (len == 0) {
return true;
}
// check if absolute path
int pos = 0;
if (jcrPath.charAt(0) == '/') {
if (!listener.root()) {
return false;
}
pos++;
}
// parse the path
int state = STATE_PREFIX_START;
int lastPos = pos;
String name = null;
int index = 0;
boolean wasSlash = false;
final PathAwareListener pathAwareListener = new PathAwareListener(listener, jcrPath);
while (pos <= len) {
char c = pos == len ? EOF : jcrPath.charAt(pos);
pos++;
switch (c) {
case '/':
case EOF:
if (state == STATE_PREFIX_START && c != EOF) {
pathAwareListener.error("Double slash '//' not allowed.");
return false;
}
if (state == STATE_PREFIX
|| state == STATE_NAME
|| state == STATE_INDEX_END) {
// eof path element
if (name == null) {
if (wasSlash) {
pathAwareListener.error("Trailing slashes not allowed in prefixes and names.");
return false;
}
name = jcrPath.substring(lastPos, pos - 1);
}
if (!JcrNameParser.parse(name, pathAwareListener, index)) {
return false;
}
state = STATE_PREFIX_START;
lastPos = pos;
name = null;
index = 0;
} else if (state == STATE_DOT) {
if (!pathAwareListener.current()) {
return false;
}
lastPos = pos;
state = STATE_PREFIX_START;
} else if (state == STATE_DOTDOT) {
if (!pathAwareListener.parent()) {
return false;
}
lastPos = pos;
state = STATE_PREFIX_START;
} else if (state == STATE_NAME_START) {
pathAwareListener.error("Local name must not be empty.");
return false;
} else if (state == STATE_URI && c == EOF) {
// we reached EOF without finding the closing curly brace '}'
pathAwareListener.error("Missing '}'.");
return false;
} else if (state != STATE_URI
&& !(state == STATE_PREFIX_START && c == EOF)) { // ignore trailing slash
pathAwareListener.error("'" + c + "' not allowed in name.");
return false;
}
break;
case '.':
if (state == STATE_PREFIX_START) {
state = STATE_DOT;
} else if (state == STATE_DOT) {
state = STATE_DOTDOT;
} else if (state == STATE_DOTDOT) {
state = STATE_PREFIX;
} else if (state == STATE_INDEX_END) {
pathAwareListener.error("'" + c + "' not valid after index. '/' expected.");
return false;
}
break;
case ':':
if (state == STATE_PREFIX_START) {
pathAwareListener.error("Prefix must not be empty.");
return false;
} else if (state == STATE_PREFIX) {
if (wasSlash) {
pathAwareListener.error("Trailing slashes not allowed in prefixes and names.");
return false;
}
state = STATE_NAME_START;
// don't reset the lastPos/pos since prefix+name are passed together to the NameResolver
} else if (state != STATE_URI) {
pathAwareListener.error("'" + c + "' not allowed in name.");
return false;
}
break;
case ']':
if (state == STATE_INDEX) {
try {
index = Integer.parseInt(jcrPath.substring(lastPos, pos - 1));
} catch (NumberFormatException e) {
pathAwareListener.error("NumberFormatException in index: " +
jcrPath.substring(lastPos, pos - 1));
return false;
}
if (index < 0) {
pathAwareListener.error("Index number invalid: " + index);
return false;
}
state = STATE_INDEX_END;
} else {
pathAwareListener.error("'" + c + "' not allowed in name.");
return false;
}
break;
case '*':
case '|':
pathAwareListener.error("'" + c + "' not allowed in name.");
return false;
case '{':
if (state == STATE_PREFIX_START && lastPos == pos-1) {
// '{' marks the start of a uri enclosed in an expanded name
// instead of the usual namespace prefix, if it is
// located at the beginning of a new segment and a '}' will follow.
state = jcrPath.indexOf('}', pos) == -1 ? STATE_NAME : STATE_URI;
} else if (state == STATE_PREFIX || state == STATE_NAME_START || state == STATE_DOT || state == STATE_DOTDOT) {
// otherwise it's part of the local name
state = STATE_NAME;
}
break;
case '}':
if (state == STATE_URI) {
state = jcrPath.indexOf(':', jcrPath.lastIndexOf('{', pos)) == -1 ?
STATE_NAME : STATE_NAME_START;
} else if (state == STATE_PREFIX_START || state == STATE_DOT || state == STATE_DOTDOT) {
state = STATE_PREFIX;
} else if (state == STATE_NAME_START || state == STATE_PREFIX) {
state = STATE_NAME;
} else if (state == STATE_INDEX_END) {
pathAwareListener.error("'" + c + "' not valid after index. '/' expected.");
return false;
}
break;
case '[':
if (state == STATE_PREFIX || state == STATE_NAME) {
if (wasSlash) {
pathAwareListener.error("Trailing slashes not allowed in prefixes and names.");
return false;
}
state = STATE_INDEX;
name = jcrPath.substring(lastPos, pos - 1);
lastPos = pos;
}
else if (state == STATE_PREFIX_START || state == STATE_DOT || state == STATE_DOTDOT) {
state = STATE_PREFIX;
} else if (state == STATE_NAME_START) {
state = STATE_NAME;
} else if (state == STATE_INDEX_END) {
pathAwareListener.error("'" + c + "' not valid after index. '/' expected.");
return false;
}
break;
default:
if (state == STATE_PREFIX_START || state == STATE_DOT || state == STATE_DOTDOT) {
state = STATE_PREFIX;
} else if (state == STATE_NAME_START) {
state = STATE_NAME;
} else if (state == STATE_INDEX_END) {
pathAwareListener.error("'" + c + "' not valid after index. '/' expected.");
return false;
}
}
wasSlash = c == '/';
}
return true;
}