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