/*
 * 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.
/** * 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.
/** * Represents what a piece of a variable does. */
public static enum Type {
A member of the previous context (ie "dot" access).
/** * A member of the previous context (ie "dot" access). */
MEMBER,
Brackets containing a string as the "index".
/** * Brackets containing a string as the "index". */
STR_INDEX,
Brackets containing an integer index (ie an array).
/** * Brackets containing an integer index (ie an array). */
INT_INDEX,
Parenthesis represent a member method to be called.
/** * Parenthesis represent a member method to be called. */
METHOD }
The type of this piece of a variable.
/** * The type of this piece of a variable. */
public final Type type;
The text of this piece of the variable. Used for Type.MEMBER and Type.STR_INDEX types.
/** * 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 Type.INT_INDEX.
/** * 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).
/** * 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 ; } }