/** * provides and registers a fresh variable in the current scope. It takes parent scopes into * account and only reuses names of synthetic variables from parent scopes. Pseudo scopes are * treated as if they were part of their parent scope. */ @NonNull public String declareVariable( @NonNull Object key, @NonNull String proposedName, boolean synthetic) { if (scopes.isEmpty()) throw new IllegalArgumentException("No scope has been opened yet."); Scope currentScope = scopes.peek(); if (get(proposedName) == null) { currentScope.addVariable(proposedName, synthetic, key); return proposedName; } final Set<String> names = newHashSet(); boolean scopeClosed = false; // add only the non-synthetic variables, since they could be referenced from nested scopes. for (Scope scope : reverse(newArrayList(scopes))) { for (Variable variable : scope.variables()) { if (!scopeClosed || !variable.synthetic) names.add(variable.name); } scopeClosed = scopeClosed || !scope.pseudoScope; // if we left the current scope (incl. pseudo scopes) and the variable is not synthetic, we // can stop collecting names. // Overriding names from outside is ok in that case. if (scopeClosed && !synthetic) break; } String newName = findNewName(names, proposedName); currentScope.addVariable(newName, synthetic, key); return newName; }
@Nullable public String getName(Object referenced) { if (referenced == null) throw new NullPointerException("referenced"); if (scopes.isEmpty()) return null; int size = scopes.size(); int i = size - 1; while (i >= 0) { Scope currentScope = scopes.get(i--); for (Variable v : currentScope.variables()) { if (v.referenced.equals(referenced)) return v.name; } } return null; }