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