package org.etk.orm.plugins.common.jcr;
class Parser {
static void parsePath(PathVisitor visitor, String path, int start, int end) throws PathException {
int length = end - start;
if (length > 0) {
char c = path.charAt(start);
if (c == '/') {
if (start + 1 < end) {
parseRelativePath(visitor, path, start + 1, end);
}
} else if (c == '[') {
throw new UnsupportedOperationException("todo");
} else {
parseRelativePath(visitor, path, start, end);
}
} else {
parseRelativePath(visitor, path, start, end);
}
}
static void parseAbsolutePath(PathVisitor visitor, String path, int start, int end) throws PathException {
int length = end - start;
if (length == 0) {
throw new PathException("Invalid absolute empty path");
}
char c = path.charAt(start);
if (c == '/') {
if (start + 1 < end) {
parseRelativePath(visitor, path, start + 1, end);
}
} else {
throw new PathException("Invalid absolute path" + path.substring(start, end));
}
}
static void parseRelativePath(PathVisitor visitor, String s, int start, int end) throws PathException {
if (start == end) {
parsePathSegment(visitor, s, start, end);
} else {
int pos = lastIndexOf(s, '/', start, end);
if (pos == start) {
throw new PathException("Cannot parse absolute path" + s.substring(start, end));
} else if (pos == end - 1) {
parseRelativePath(visitor, s, start, end - 1);
} else if (pos == -1) {
if (s.charAt(end -1) == '/') {
parsePathSegment(visitor, s, start, end - 1);
} else {
parsePathSegment(visitor, s, start, end);
}
} else {
parseRelativePath(visitor, s, start, pos);
if (s.charAt(end -1) == '/') {
parsePathSegment(visitor, s, pos + 1, end - 1);
} else {
parsePathSegment(visitor, s, pos + 1, end);
}
}
}
}
static void parsePathSegment(PathVisitor visitor, String s, int start, int end) throws PathException {
int length = end - start;
if (length == 1) {
if (s.charAt(start) == '.') {
visitor.onSelf();
return;
}
} else if (length == 2) {
if (s.charAt(start) == '.' && s.charAt(start + 1) == '.') {
visitor.onParent();
return;
}
}
int pos = indexOf(s, '[', start, end);
if (pos != -1) {
if (pos == end -1) {
throw new PathException("Malformed expression " + s.substring(start, end));
}
if (s.charAt(end - 1) != ']') {
throw new PathException("Missing ending ] in expression " + s.substring(start, end));
}
String number = s.substring(pos + 1, end - 1);
Integer numberValue;
try {
numberValue = Integer.parseInt(number);
if (numberValue < 0) {
throw new PathException("No negative index allowed in expression " + s.substring(start, end));
}
}
catch (NumberFormatException e) {
throw new PathException("Invalid index in expression " + s.substring(start, end));
}
parseName(visitor, s, start, pos, numberValue);
} else {
parseName(visitor, s, start, end, null);
}
}
private static void parseName(PathVisitor visitor, String s, int start, int end, Integer number) throws PathException {
/*
PathSegment ::= ExpandedName [Index] | QualifiedName [Index] | SelfOrParent
Index ::= '[' Number ']'
Number ::= An integer > 0
ExpandedName ::= '{' Namespace '}' LocalName
Namespace ::= EmptyString | Uri
Uri ::= A URI, as defined in Section 3 in http://tools.ietf.org/html/rfc3986#section-3
QualifiedName ::= [ Prefix ':' ] LocalName
Prefix ::= Any string that matches the NCName production in http://www.w3.org/TR/REC-xml-names
LocalName ::= ValidString � SelfOrParent
ValidString ::= XmlChar � InvalidChar
InvalidChar ::= '/' | ':' | '[' | ']' | '|' | '*'
XmlChar ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
XmlChar ::= Any character that matches the Char production at http://www.w3.org/TR/xml/#NT-Char
SelfOrParent ::= '.' | '..'
*/
//
int length = end - start;
if (length > 0 &&s.charAt(start) == '{') {
int curlyBraceIndex = indexOf(s, '}', start + 1, end);
if (curlyBraceIndex == -1) {
throw new PathException("Uri not closed in name value " + s.substring(start, end));
}
// Should validate URI ...
validateLocalName(s, curlyBraceIndex + 1, end);
visitor.onURIPathSegment(s, start + 1, curlyBraceIndex, curlyBraceIndex + 1, end, number);
} else {
// Maybe there is an optional prefix
int colonIndex = indexOf(s, ':', start, end);
if (colonIndex != -1) {
String prefix = s.substring(start, colonIndex);
// Should validate prefix
validateLocalName(s, colonIndex + 1, end);
visitor.onPrefixPathSegment(s, start, colonIndex, colonIndex + 1, end, number);
} else {
validateLocalName(s, start, end);
visitor.onPathSegment(s, start, end, number);
}
}
}
private static void validateLocalName(String s, int start, int end) throws PathException {
int length = end - start;
// Now validate as a name
if (length - start == 1) {
if (s.charAt(start) == '.') {
throw new PathException("'.' is not a valid name");
}
} else if (length - start == 2) {
if (s.charAt(start) == '.' && s.charAt(start + 1) == '.') {
throw new PathException("'..' is not a valid name");
}
}
// #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
while (start < end) {
char c = s.charAt(start);
if (c == 0x9
|| c == 0xA
|| c == 0xD
|| (c >= 0x20 && c <= 0xD7FF)
|| (c >= 0xE000 && c <= 0xFFFD)
|| (c >= 0x10000 && c <= 0x10FFFF)) {
if (c == '/' || c == ':' || c == '[' || c == ']' || c == '|' || c == '*') {
throw new PathException("Illegal path value " + s.substring(start, end) + " (char " + c + " at position " + start + " not accepted)");
}
start++;
continue;
}
throw new PathException("Illegal path value " + s.substring(start, end) + " (char " + c + " at position " + start + " not accepted)");
}
}
private static int indexOf(String s, char c, int start, int end) {
while (start < end) {
if (s.charAt(start) == c) {
return start;
}
start++;
}
return -1;
}
private static int lastIndexOf(String s, char c, int start, int end) {
while (start < end) {
int next = end - 1;
if (s.charAt(next) == c) {
return next;
}
end = next;
}
return -1;
}
}