blob: a9b8c929b132e04b6335dff50baefb96ee7eb6a3 [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.lucene.expressions.js;
import java.util.ArrayList;
import java.util.List;
/**
* A helper to parse the context of a variable name, which is the base variable, followed by the
* sequence of array (integer or string indexed) and member accesses.
*/
public class VariableContext {
/**
* Represents what a piece of a variable does.
*/
public static enum Type {
/**
* A member of the previous context (ie "dot" access).
*/
MEMBER,
/**
* Brackets containing a string as the "index".
*/
STR_INDEX,
/**
* Brackets containing an integer index (ie an array).
*/
INT_INDEX,
/**
* Parenthesis represent a member method to be called.
*/
METHOD
}
/**
* The type of this piece of a variable.
*/
public final Type type;
/**
* The text of this piece of the variable. Used for {@link Type#MEMBER} and {@link Type#STR_INDEX} types.
*/
public final String text;
/**
* The integer value for this piece of the variable. Used for {@link Type#INT_INDEX}.
*/
public final int integer;
private VariableContext(Type c, String s, int i) {
type = c;
text = s;
integer = i;
}
/**
* Parses a normalized javascript variable. All strings in the variable should be single quoted,
* and no spaces (except possibly within strings).
*/
public static final VariableContext[] parse(String variable) {
char[] text = variable.toCharArray();
List<VariableContext> contexts = new ArrayList<>();
int i = addMember(text, 0, contexts); // base variable is a "member" of the global namespace
while (i < text.length) {
if (text[i] == '[') {
if (text[++i] == '\'') {
i = addStringIndex(text, i, contexts);
} else {
i = addIntIndex(text, i, contexts);
}
++i; // move past end bracket
} else { // text[i] == '.', ie object member
i = addMember(text, i + 1, contexts);
}
}
return contexts.toArray(new VariableContext[contexts.size()]);
}
// i points to start of member name
private static int addMember(final char[] text, int i, List<VariableContext> contexts) {
int j = i + 1;
while (j < text.length && text[j] != '[' && text[j] != '.' && text[j] != '(') ++j; // find first array, member access, or method call
if (j + 1 < text.length && text[j] == '(' && text[j + 1] == ')') {
contexts.add(new VariableContext(Type.METHOD, new String(text, i, j - i), -1));
j += 2; //move past the parenthesis
} else {
contexts.add(new VariableContext(Type.MEMBER, new String(text, i, j - i), -1));
}
return j;
}
// i points to start of single quoted index
private static int addStringIndex(final char[] text, int i, List<VariableContext> contexts) {
++i; // move past quote
int j = i;
while (text[j] != '\'') { // find end of single quoted string
if (text[j] == '\\') ++j; // skip over escapes
++j;
}
StringBuffer buf = new StringBuffer(j - i); // space for string, without end quote
while (i < j) { // copy string to buffer (without begin/end quotes)
if (text[i] == '\\') ++i; // unescape escapes
buf.append(text[i]);
++i;
}
contexts.add(new VariableContext(Type.STR_INDEX, buf.toString(), -1));
return j + 1; // move past quote, return end bracket location
}
// i points to start of integer index
private static int addIntIndex(final char[] text, int i, List<VariableContext> contexts) {
int j = i + 1;
while (text[j] != ']') ++j; // find end of array access
int index = Integer.parseInt(new String(text, i, j - i));
contexts.add(new VariableContext(Type.INT_INDEX, null, index));
return j ;
}
}