blob: 344bea363666ebe45f948cf41455b050e3ab4848 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.solr.search;
import java.util.Locale;
/**
* Simple class to help with parsing a string.
* <b>Note: This API is experimental and may change in non backward-compatible ways in the future</b>
*/
public class StrParser {
public String val;
public int pos;
public int end;
public StrParser(String val) {
this(val, 0, val.length());
}
public StrParser(String val, int start, int end) {
this.val = val;
this.pos = start;
this.end = end;
}
public void eatws() {
while (pos < end && Character.isWhitespace(val.charAt(pos))) pos++;
}
public char ch() {
return pos < end ? val.charAt(pos) : 0;
}
public void skip(int nChars) {
pos = Math.max(pos + nChars, end);
}
public boolean opt(String s) {
eatws();
int slen = s.length();
if (val.regionMatches(pos, s, 0, slen)) {
pos += slen;
return true;
}
return false;
}
public boolean opt(char ch) {
eatws();
if (pos < end && val.charAt(pos) == ch) {
pos++;
return true;
}
return false;
}
public void expect(String s) throws SyntaxError {
eatws();
int slen = s.length();
if (val.regionMatches(pos, s, 0, slen)) {
pos += slen;
} else {
throw new SyntaxError("Expected '" + s + "' at position " + pos + " in '" + val + "'");
}
}
public float getFloat() {
eatws();
char[] arr = new char[end - pos];
int i;
for (i = 0; i < arr.length; i++) {
char ch = val.charAt(pos);
if ((ch >= '0' && ch <= '9')
|| ch == '+' || ch == '-'
|| ch == '.' || ch == 'e' || ch == 'E'
) {
pos++;
arr[i] = ch;
} else {
break;
}
}
return Float.parseFloat(new String(arr, 0, i));
}
public Number getNumber() {
eatws();
int start = pos;
boolean flt = false;
while (pos < end) {
char ch = val.charAt(pos);
if ((ch >= '0' && ch <= '9') || ch == '+' || ch == '-') {
pos++;
} else if (ch == '.' || ch =='e' || ch=='E') {
flt = true;
pos++;
} else {
break;
}
}
String v = val.substring(start,pos);
if (flt) {
return Double.parseDouble(v);
} else {
return Long.parseLong(v);
}
}
public double getDouble() {
eatws();
char[] arr = new char[end - pos];
int i;
for (i = 0; i < arr.length; i++) {
char ch = val.charAt(pos);
if ((ch >= '0' && ch <= '9')
|| ch == '+' || ch == '-'
|| ch == '.' || ch == 'e' || ch == 'E'
) {
pos++;
arr[i] = ch;
} else {
break;
}
}
return Double.parseDouble(new String(arr, 0, i));
}
public int getInt() {
eatws();
char[] arr = new char[end - pos];
int i;
for (i = 0; i < arr.length; i++) {
char ch = val.charAt(pos);
if ((ch >= '0' && ch <= '9')
|| ch == '+' || ch == '-'
) {
pos++;
arr[i] = ch;
} else {
break;
}
}
return Integer.parseInt(new String(arr, 0, i));
}
public String getId() throws SyntaxError {
return getId("Expected identifier");
}
public String getId(String errMessage) throws SyntaxError {
eatws();
int id_start = pos;
char ch;
if (pos < end && (ch = val.charAt(pos)) != '$' && Character.isJavaIdentifierStart(ch)) {
pos++;
while (pos < end) {
ch = val.charAt(pos);
// if (!Character.isJavaIdentifierPart(ch) && ch != '.' && ch != ':') {
if (!Character.isJavaIdentifierPart(ch) && ch != '.') {
break;
}
pos++;
}
return val.substring(id_start, pos);
}
if (errMessage != null) {
throw new SyntaxError(errMessage + " at pos " + pos + " str='" + val + "'");
}
return null;
}
public String getGlobbedId(String errMessage) throws SyntaxError {
eatws();
int id_start = pos;
char ch;
if (pos < end && (ch = val.charAt(pos)) != '$' && (Character.isJavaIdentifierStart(ch) || ch=='?' || ch=='*')) {
pos++;
while (pos < end) {
ch = val.charAt(pos);
if (!(Character.isJavaIdentifierPart(ch) || ch=='?' || ch=='*') && ch != '.') {
break;
}
pos++;
}
return val.substring(id_start, pos);
}
if (errMessage != null) {
throw new SyntaxError(errMessage + " at pos " + pos + " str='" + val + "'");
}
return null;
}
/**
* Skips leading whitespace and returns whatever sequence of non
* whitespace it can find (or hte empty string)
*/
public String getSimpleString() {
eatws();
int startPos = pos;
char ch;
while (pos < end) {
ch = val.charAt(pos);
if (Character.isWhitespace(ch)) break;
pos++;
}
return val.substring(startPos, pos);
}
/**
* Sort direction or null if current position does not indicate a
* sort direction. (True is desc, False is asc).
* Position is advanced to after the comma (or end) when result is non null
*/
public Boolean getSortDirection() throws SyntaxError {
final int startPos = pos;
final String order = getId(null);
Boolean top = null;
if (null != order) {
final String orderLowerCase = order.toLowerCase(Locale.ROOT);
if ("desc".equals(orderLowerCase) || "top".equals(orderLowerCase)) {
top = true;
} else if ("asc".equals(orderLowerCase) || "bottom".equals(orderLowerCase)) {
top = false;
}
// it's not a legal direction if more stuff comes after it
eatws();
final char c = ch();
if (0 == c) {
// :NOOP
} else if (',' == c) {
pos++;
} else {
top = null;
}
}
if (null == top) pos = startPos; // no direction, reset
return top;
}
// return null if not a string
public String getQuotedString() throws SyntaxError {
eatws();
char delim = peekChar();
if (!(delim == '\"' || delim == '\'')) {
return null;
}
int val_start = ++pos;
StringBuilder sb = new StringBuilder(); // needed for escaping
for (; ;) {
if (pos >= end) {
throw new SyntaxError("Missing end quote for string at pos " + (val_start - 1) + " str='" + val + "'");
}
char ch = val.charAt(pos);
if (ch == '\\') {
pos++;
if (pos >= end) break;
ch = val.charAt(pos);
switch (ch) {
case 'n':
ch = '\n';
break;
case 't':
ch = '\t';
break;
case 'r':
ch = '\r';
break;
case 'b':
ch = '\b';
break;
case 'f':
ch = '\f';
break;
case 'u':
if (pos + 4 >= end) {
throw new SyntaxError("bad unicode escape \\uxxxx at pos" + (val_start - 1) + " str='" + val + "'");
}
ch = (char) Integer.parseInt(val.substring(pos + 1, pos + 5), 16);
pos += 4;
break;
}
} else if (ch == delim) {
pos++; // skip over the quote
break;
}
sb.append(ch);
pos++;
}
return sb.toString();
}
// next non-whitespace char
public char peek() {
eatws();
return pos < end ? val.charAt(pos) : 0;
}
// next char
public char peekChar() {
return pos < end ? val.charAt(pos) : 0;
}
@Override
public String toString() {
return "'" + val + "'" + ", pos=" + pos;
}
}