blob: 4b0fbfc4b95fe4adbd476a7031e16434b4ba0e39 [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;
/**
* Records declared and referenced variabes for a given scope. Helps determine
* variable sharing across closure and method boundaries.
*/
public class VariableScope implements Cloneable {
private VariableScope parent;
private ClassNode classScope;
private boolean inStaticContext;
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(final 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(final ClassNode classScope) {
this.classScope = classScope;
}
public boolean isInStaticContext() {
return inStaticContext;
}
public void setInStaticContext(final boolean inStaticContext) {
this.inStaticContext = inStaticContext;
}
//
public Variable getDeclaredVariable(final String name) {
return declaredVariables.get(name);
}
public Variable getReferencedLocalVariable(final String name) {
return referencedLocalVariables.get(name);
}
public Variable getReferencedClassVariable(final String name) {
return referencedClassVariables.get(name);
}
public boolean isReferencedLocalVariable(final String name) {
return referencedLocalVariables.containsKey(name);
}
public boolean isReferencedClassVariable(final 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(final Variable var) {
if (declaredVariables == Collections.EMPTY_MAP)
declaredVariables = new LinkedHashMap<>();
declaredVariables.put(var.getName(), var);
}
public void putReferencedLocalVariable(final Variable var) {
if (referencedLocalVariables == Collections.EMPTY_MAP)
referencedLocalVariables = new LinkedHashMap<>();
referencedLocalVariables.put(var.getName(), var);
}
public void putReferencedClassVariable(final Variable var) {
if (referencedClassVariables == Collections.EMPTY_MAP)
referencedClassVariables = new LinkedHashMap<>();
referencedClassVariables.put(var.getName(), var);
}
public Object removeReferencedClassVariable(final String name) {
if (referencedClassVariables.isEmpty()) {
return null;
} else {
return referencedClassVariables.remove(name);
}
}
//
// TODO: implement Cloneable and override Object.clone()
public VariableScope copy() {
VariableScope that = new VariableScope(parent);
that.classScope = this.classScope;
that.inStaticContext = this.inStaticContext;
if (!this.declaredVariables.isEmpty()) {
that.declaredVariables = new LinkedHashMap<>(this.declaredVariables);
}
if (!this.referencedLocalVariables.isEmpty()) {
that.referencedLocalVariables = new LinkedHashMap<>(this.referencedLocalVariables);
}
if (!this.referencedClassVariables.isEmpty()) {
that.referencedClassVariables = new LinkedHashMap<>(this.referencedClassVariables);
}
return that;
}
}