blob: 11574864e1c3f632cf086802b54dc9d0447133a6 [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.sling.scripting.sightly.compiler.util;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
/**
* Tracks information related to HTL variables, at different levels of the compiler.
*/
public final class VariableTracker<T> {
private final Map<String, Stack<T>> variableData = new HashMap<>();
private final Stack<String> declarationStack = new Stack<>();
/**
* Checks if the variable identified by {@code name} is tracked by this tracker or not.
*
* @param name the name of the variable
* @return {@code true} if the variable is declared, {@code false} otherwise
*/
public boolean isDeclared(String name) {
name = name.toLowerCase();
Stack<T> dataStack = variableData.get(name);
return dataStack != null;
}
/**
* Pushes a variable to this tracker. Call this method whenever a variable is declared.
*
* @param name the name of the variable
* @param data the data associated with the variable
*/
public void pushVariable(String name, T data) {
if (name != null) {
name = name.toLowerCase();
Stack<T> dataStack = variableData.get(name);
if (dataStack == null) {
dataStack = new Stack<>();
variableData.put(name, dataStack);
}
dataStack.push(data);
declarationStack.push(name);
}
}
/**
* Pops a variable from this tracker. Call this method whenever a variable goes out of scope.
*
* @return the name of the popped variable
* @throws java.util.NoSuchElementException if there are no declared variables in the scope
*/
public String popVariable() {
String variable = declarationStack.pop();
Stack<T> dataStack = variableData.get(variable);
assert dataStack != null;
dataStack.pop();
if (dataStack.isEmpty()) {
variableData.remove(variable);
}
return variable;
}
/**
* Peeks at the top of the declaration stack.
*
* @return the most recently declared variable and its associated data
* @throws java.util.NoSuchElementException if there are no variables in scope
*/
public Map.Entry<String, T> peek() {
String variable = declarationStack.peek();
Stack<T> dataStack = variableData.get(variable);
assert dataStack != null;
T data = dataStack.peek();
return new AbstractMap.SimpleImmutableEntry<>(variable, data);
}
/**
* Checks if the declaration stack is empty.
*
* @return {@code true} if the stack is empty, {@code false} otherwise
*/
public boolean isEmpty() {
return declarationStack.isEmpty();
}
/**
* Get the data associated with the given variable.
*
* @param name the name of the variable
* @return the associated data or {@code null} if that variable is not in scope
*/
public T get(String name) {
name = name.toLowerCase();
Stack<T> dataStack = variableData.get(name);
if (dataStack == null) {
return null;
}
assert !dataStack.isEmpty();
return dataStack.peek();
}
/**
* Check whether a variable was declared and is visible in the current scope.
*
* @param name the name of the variable
* @return {@code true} if the variable is visible in the current scope, {@code false} otherwise
*/
public boolean isInScope(String name) {
name = name.toLowerCase();
return variableData.get(name) != null;
}
/**
* Get an immutable view of all the data items associated with the specified variable.
*
* @param name the name of the variable
* @return a list of all the data associated with this variable name. If the variable is not declared in the current scope then that
* list will be empty. Otherwise it will contain all the data associated for the current scope starting with the data associated at
* the topmost scope and ending with the most recently associated data
*/
public List<T> getAll(String name) {
name = name.toLowerCase();
Stack<T> dataStack = variableData.get(name);
if (dataStack == null) {
return Collections.emptyList();
}
return Collections.unmodifiableList(dataStack);
}
/**
* Get how many times a variable was declared in the current scope.
*
* @param name the name of the variable
* @return the number of declarations for a variable in the current scope
*/
public int getOccurrenceCount(String name) {
name = name.toLowerCase();
Stack<T> dataStack = variableData.get(name);
if (dataStack == null) {
return 0;
}
return dataStack.size();
}
}