/******************************************************************************* * Copyright (c) 2010 xored software, Inc. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * xored software, Inc. - initial API and Implementation (Vladislav Kuzkokov) *******************************************************************************/ package org.eclipse.dltk.javascript.core.dom.rewrite; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.dltk.internal.javascript.corext.refactoring.code.flow.VariableBinding; import org.eclipse.dltk.javascript.core.dom.AccessorAssignment; import org.eclipse.dltk.javascript.core.dom.CatchClause; import org.eclipse.dltk.javascript.core.dom.DomPackage; import org.eclipse.dltk.javascript.core.dom.ExpressionStatement; import org.eclipse.dltk.javascript.core.dom.FunctionExpression; import org.eclipse.dltk.javascript.core.dom.GetterAssignment; import org.eclipse.dltk.javascript.core.dom.Identifier; import org.eclipse.dltk.javascript.core.dom.Node; import org.eclipse.dltk.javascript.core.dom.Parameter; import org.eclipse.dltk.javascript.core.dom.SetterAssignment; import org.eclipse.dltk.javascript.core.dom.Source; import org.eclipse.dltk.javascript.core.dom.VariableDeclaration; import org.eclipse.dltk.javascript.core.dom.VariableReference; import org.eclipse.dltk.javascript.core.dom.util.DomSwitch; import org.eclipse.emf.ecore.EObject; public abstract class VariableLookup extends DomSwitch<Boolean> { private Map<String, List<Identifier>> decls = new HashMap<String, List<Identifier>>(); private Set<String> scope = new HashSet<String>(); protected abstract void reportDeclaration(Identifier decl); protected abstract void reportReference(Identifier ref, Identifier decl); public final void traverse(Node node) { if (doSwitch(node) == null) for (EObject obj : node.eContents()) traverse((Node) obj); } private void addDeclaration(Identifier id) { if (scope.contains(id.getName())) { List<Identifier> list = decls.get(id.getName()); reportReference(id, list.get(list.size() - 1)); return; } reportDeclaration(id); List<Identifier> list = decls.get(id.getName()); if (list == null) { list = new ArrayList<Identifier>(); decls.put(id.getName(), list); } list.add(id); scope.add(id.getName()); } private void popScope(Set<String> outerScope) { for (String str : scope) { List<Identifier> list = decls.get(str); list.remove(list.size() - 1); } scope = outerScope; } protected final void findDeclarations(Node node) { switch (node.eClass().getClassifierID()) { case DomPackage.EXPRESSION_STATEMENT: ExpressionStatement stmt = (ExpressionStatement) node; if (stmt.getExpression() instanceof FunctionExpression) { Identifier id = ((FunctionExpression) stmt.getExpression()) .getIdentifier(); if (id != null) addDeclaration(id); return; } break; case DomPackage.FUNCTION_EXPRESSION: return; case DomPackage.VARIABLE_DECLARATION: addDeclaration(((VariableDeclaration) node).getIdentifier()); break; } for (EObject obj : node.eContents()) findDeclarations((Node) obj); } private Set<String> pushScope() { Set<String> outerScope = scope; scope = new HashSet<String>(); return outerScope; } @Override public Boolean caseVariableReference(VariableReference node) { Identifier id = node.getVariable(); List<Identifier> list = decls.get(id.getName()); if (list == null || list.size() == 0) reportReference(id, null); else reportReference(id, list.get(list.size() - 1)); return true; } @Override public Boolean caseGetterAssignment(GetterAssignment node) { Set<String> outerScope = pushScope(); findDeclarations(node.getBody()); traverse(node.getBody()); popScope(outerScope); return true; } @Override public Boolean caseSetterAssignment(SetterAssignment node) { Set<String> outerScope = pushScope(); addDeclaration(node.getParameter()); findDeclarations(node.getBody()); traverse(node.getBody()); popScope(outerScope); return true; } @Override public Boolean caseCatchClause(CatchClause node) { Set<String> outerScope = pushScope(); addDeclaration(node.getException()); traverse(node.getBody()); popScope(outerScope); return true; } @Override public Boolean caseFunctionExpression(FunctionExpression node) { Set<String> outerScope = pushScope(); if (node.getIdentifier() != null && !(node.eContainer() instanceof ExpressionStatement)) addDeclaration(node.getIdentifier()); for (Parameter param : node.getParameters()) addDeclaration(param.getName()); findDeclarations(node.getBody()); traverse(node.getBody()); popScope(outerScope); return true; } @Override public Boolean caseSource(Source node) { Set<String> outerScope = pushScope(); findDeclarations(node); for (EObject obj : node.getStatements()) traverse((Node) obj); popScope(outerScope); return true; } public static Set<String> getVisibleNames(Node node) { final Set<String> result = new HashSet<String>(); final boolean[] reportDecls = new boolean[] { true }; VariableLookup lookup = new VariableLookup() { @Override protected void reportDeclaration(Identifier decl) { if (reportDecls[0]) result.add(decl.getName()); } @Override protected void reportReference(Identifier ref, Identifier decl) { if (decl == null) result.add(ref.getName()); } }; Node body = null; while (body == null) { node = (Node) node.eContainer(); switch (node.eClass().getClassifierID()) { case DomPackage.FUNCTION_EXPRESSION: body = ((FunctionExpression) node).getBody(); break; case DomPackage.GETTER_ASSIGNMENT: case DomPackage.SETTER_ASSIGNMENT: body = ((AccessorAssignment) node).getBody(); case DomPackage.SOURCE: body = node; break; } } lookup.findDeclarations(body); reportDecls[0] = false; lookup.traverse(node); return result; } public static List<Identifier> findReferences(Node root, Set<String> names) { return findReferences(root, names, false); } public static List<Identifier> findReferences(Node root, Set<String> names, final boolean firstOnly) { final Set<String> wanted; if (firstOnly) { wanted = new HashSet<String>(); wanted.addAll(names); } else wanted = names; final List<Identifier> refs = new ArrayList<Identifier>(); VariableLookup lookup = new VariableLookup() { @Override protected void reportDeclaration(Identifier decl) { // do nothing } @Override protected void reportReference(Identifier ref, Identifier decl) { if (decl != null) return; String str = ref.getName(); if (wanted.contains(str)) { refs.add(ref); if (firstOnly) wanted.remove(str); } } }; lookup.traverse(root); return refs; } public static Map<Identifier, VariableBinding> findBindings(Node node) { final Map<Identifier, VariableBinding> bindings = new HashMap<Identifier, VariableBinding>(); VariableLookup lookup = new VariableLookup() { @Override protected void reportDeclaration(Identifier decl) { Node parent = (Node) decl.eContainer(); // Type type = null; switch (parent.eClass().getClassifierID()) { case DomPackage.VARIABLE_DECLARATION: // type = ((VariableDeclaration)parent).getType(); break; case DomPackage.PARAMETER: // type = ((Parameter)parent).getType(); break; } String typeName = /* type == null ? */null /* : type.getName() */; bindings.put(decl, new VariableBinding(decl.getName(), bindings.size(), decl, typeName)); } @Override protected void reportReference(Identifier ref, Identifier decl) { if (decl != null) bindings.put(ref, bindings.get(decl)); } }; lookup.traverse(node); return bindings; } }