blob: 9a32e8e0a38739ba2a0cedff1d5da39fa00e1afa [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.codehaus.groovy.ast;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Represents a variable scope. This is primarily used to determine variable sharing
* across method and closure boundaries.
*/
public class VariableScope {
private VariableScope parent;
private ClassNode classScope;
private boolean inStaticContext;
private boolean resolvesDynamic;
private Map<String, Variable> declaredVariables = Collections.emptyMap();
private Map<String, Variable> referencedLocalVariables = Collections.emptyMap();
private Map<String, Variable> referencedClassVariables = Collections.emptyMap();
public VariableScope() {
super();
}
public VariableScope(VariableScope parent) {
this.parent = parent;
}
public VariableScope getParent() {
return parent;
}
public boolean isRoot() {
return parent == null;
}
/**
* Non-null iff this scope corresponds to a class; as opposed to a method, "if" statement, block statement, etc.
*/
public ClassNode getClassScope() {
return classScope;
}
/**
* Returns true iff this scope corresponds to a class; as opposed to a method, "if" statement, block statement, etc.
*/
public boolean isClassScope() {
return classScope != null;
}
public void setClassScope(ClassNode classScope) {
this.classScope = classScope;
}
public boolean isInStaticContext() {
return inStaticContext;
}
public void setInStaticContext(boolean inStaticContext) {
this.inStaticContext = inStaticContext;
}
//
public Variable getDeclaredVariable(String name) {
return declaredVariables.get(name);
}
public Variable getReferencedLocalVariable(String name) {
return referencedLocalVariables.get(name);
}
public Variable getReferencedClassVariable(String name) {
return referencedClassVariables.get(name);
}
public boolean isReferencedLocalVariable(String name) {
return referencedLocalVariables.containsKey(name);
}
public boolean isReferencedClassVariable(String name) {
return referencedClassVariables.containsKey(name);
}
/**
* Gets a map containing the variables declared in this scope. This map cannot be modified.
*
* @return a map containing the declared variable references
*/
public Map<String, Variable> getDeclaredVariables() {
if (declaredVariables == Collections.EMPTY_MAP) {
return declaredVariables;
} else {
return Collections.unmodifiableMap(declaredVariables);
}
}
/**
* Gets a map containing the class variables referenced by this scope. This not can not be modified.
*
* @return a map containing the class variable references
*/
public Map<String, Variable> getReferencedClassVariables() {
if (referencedClassVariables == Collections.EMPTY_MAP) {
return referencedClassVariables;
} else {
return Collections.unmodifiableMap(referencedClassVariables);
}
}
public int getReferencedLocalVariablesCount() {
return referencedLocalVariables.size();
}
/**
* Gets an iterator for the declared class variables. The remove operation is not supported.
*
* @return an iterator for the declared variables
*/
public Iterator<Variable> getDeclaredVariablesIterator() {
return getDeclaredVariables().values().iterator();
}
/**
* Gets an iterator for the referenced local variables. The remove operation *is* supported.
*
* @return an iterator for the referenced local variables
*/
public Iterator<Variable> getReferencedLocalVariablesIterator() {
return referencedLocalVariables.values().iterator();
}
/**
* Gets an iterator for the referenced class variables. The remove operation is not supported.
*
* @return an iterator for the referenced class variables
*/
public Iterator<Variable> getReferencedClassVariablesIterator() {
return getReferencedClassVariables().values().iterator();
}
public void putDeclaredVariable(Variable var) {
if (declaredVariables == Collections.EMPTY_MAP)
declaredVariables = new LinkedHashMap<>();
declaredVariables.put(var.getName(), var);
}
public void putReferencedLocalVariable(Variable var) {
if (referencedLocalVariables == Collections.EMPTY_MAP)
referencedLocalVariables = new LinkedHashMap<>();
referencedLocalVariables.put(var.getName(), var);
}
public void putReferencedClassVariable(Variable var) {
if (referencedClassVariables == Collections.EMPTY_MAP)
referencedClassVariables = new LinkedHashMap<>();
referencedClassVariables.put(var.getName(), var);
}
public Object removeReferencedClassVariable(String name) {
if (referencedClassVariables.isEmpty()) {
return null;
} else {
return referencedClassVariables.remove(name);
}
}
//
// TODO: implement Cloneable and override Object.clone()
public VariableScope copy() {
VariableScope copy = new VariableScope(parent);
copy.classScope = classScope;
copy.inStaticContext = inStaticContext;
copy.resolvesDynamic = resolvesDynamic;
if (!declaredVariables.isEmpty()) {
copy.declaredVariables = new LinkedHashMap<>(declaredVariables);
}
if (!referencedLocalVariables.isEmpty()) {
copy.referencedLocalVariables = new LinkedHashMap<>(referencedLocalVariables);
}
if (!referencedClassVariables.isEmpty()) {
copy.referencedClassVariables = new LinkedHashMap<>(referencedClassVariables);
}
return copy;
}
}