/******************************************************************************* * Copyright (c) 2000, 2008 IBM Corporation and others. * 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.wst.jsdt.internal.corext.dom; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.eclipse.wst.jsdt.core.dom.AST; import org.eclipse.wst.jsdt.core.dom.ASTNode; import org.eclipse.wst.jsdt.core.dom.ASTVisitor; import org.eclipse.wst.jsdt.core.dom.Block; import org.eclipse.wst.jsdt.core.dom.BodyDeclaration; import org.eclipse.wst.jsdt.core.dom.CatchClause; import org.eclipse.wst.jsdt.core.dom.Expression; import org.eclipse.wst.jsdt.core.dom.ForInStatement; import org.eclipse.wst.jsdt.core.dom.ForStatement; import org.eclipse.wst.jsdt.core.dom.IBinding; import org.eclipse.wst.jsdt.core.dom.FunctionInvocation; import org.eclipse.wst.jsdt.core.dom.QualifiedName; import org.eclipse.wst.jsdt.core.dom.SimpleName; import org.eclipse.wst.jsdt.core.dom.TypeDeclarationStatement; /** * * Provisional API: This class/interface is part of an interim API that is still under development and expected to * change significantly before reaching stability. It is being made available at this early stage to solicit feedback * from pioneering adopters on the understanding that any code that uses this API will almost certainly be broken * (repeatedly) as the API evolves. */ public class CodeScopeBuilder extends ASTVisitor { public static class Scope { private Scope fParent; private int fStart; private int fLength; private List fNames; private List fChildren; private int fCursorOffset; Scope(Scope parent, int start, int length) { fParent= parent; fStart= start; fLength= length; if (fParent != null) fParent.addChild(this); } public void setCursor(int offset) { fCursorOffset= offset; } private void addChild(Scope child) { if (fChildren == null) fChildren= new ArrayList(2); fChildren.add(child); } private void addName(String name) { if (fNames == null) fNames= new ArrayList(2); fNames.add(name); } public Scope findScope(int start, int length) { if (fStart <= start && start + length <= fStart + fLength) { if (fChildren == null) return this; for (Iterator iter= fChildren.iterator(); iter.hasNext();) { Scope scope= ((Scope)iter.next()).findScope(start, length); if (scope != null) return scope; } return this; } return null; } public String createName(String candidate, boolean add) { int i= 1; String result= candidate; while(isInUse(result)) { result= candidate + i++; } if (add) addName(result); return result; } public boolean isInUse(String name) { if (internalIsInUse(name)) return true; if (fChildren != null) { for (Iterator iter= fChildren.iterator(); iter.hasNext();) { Scope child= (Scope) iter.next(); if (fCursorOffset < child.fStart && child.isInUseDown(name)) { return true; } } } return false; } private boolean internalIsInUse(String name) { if (fNames != null && fNames.contains(name)) return true; if (fParent != null) return fParent.internalIsInUse(name); return false; } private boolean isInUseDown(String name) { if (fNames != null && fNames.contains(name)) return true; if (fChildren == null) return false; for (Iterator iter= fChildren.iterator(); iter.hasNext();) { Scope scope= (Scope) iter.next(); if (scope.isInUseDown(name)) return true; } return false; } } private IBinding fIgnoreBinding; private Selection fIgnoreRange; private Scope fScope; private List fScopes; public static Scope perform(BodyDeclaration node, IBinding ignore) { CodeScopeBuilder collector= new CodeScopeBuilder(node, ignore); node.accept(collector); return collector.fScope; } public static Scope perform(BodyDeclaration node, Selection ignore) { CodeScopeBuilder collector= new CodeScopeBuilder(node, ignore); node.accept(collector); return collector.fScope; } private CodeScopeBuilder(ASTNode node, IBinding ignore) { fScope= new Scope(null, node.getStartPosition(), node.getLength()); fScopes= new ArrayList(); fIgnoreBinding= ignore; } private CodeScopeBuilder(ASTNode node, Selection ignore) { fScope= new Scope(null, node.getStartPosition(), node.getLength()); fScopes= new ArrayList(); fIgnoreRange= ignore; } public boolean visit(CatchClause node) { // open a new scope for the exception declaration. fScopes.add(fScope); fScope= new Scope(fScope, node.getStartPosition(), node.getLength()); return true; } public void endVisit(CatchClause node) { fScope= (Scope)fScopes.remove(fScopes.size() - 1); } public boolean visit(SimpleName node) { if (fIgnoreBinding != null && Bindings.equals(fIgnoreBinding, node.resolveBinding())) return false; if (fIgnoreRange != null && fIgnoreRange.covers(node)) return false; fScope.addName(node.getIdentifier()); return false; } public boolean visit(QualifiedName node) { // only consider the left most identifier. node.getQualifier().accept(this); return false; } public boolean visit(FunctionInvocation node) { Expression receiver= node.getExpression(); if (receiver == null) { SimpleName name= node.getName(); if (fIgnoreBinding == null || (name!=null && !Bindings.equals(fIgnoreBinding, name.resolveBinding())) ) node.getName().accept(this); } else { receiver.accept(this); } accept(node.arguments()); return false; } public boolean visit(TypeDeclarationStatement node) { if (node.getAST().apiLevel() == AST.JLS2) { fScope.addName(node.getTypeDeclaration().getName().getIdentifier()); } else { fScope.addName(node.getDeclaration().getName().getIdentifier()); } return false; } public boolean visit(Block node) { fScopes.add(fScope); fScope= new Scope(fScope, node.getStartPosition(), node.getLength()); return true; } public void endVisit(Block node) { fScope= (Scope)fScopes.remove(fScopes.size() - 1); } public boolean visit(ForStatement node) { fScopes.add(fScope); fScope= new Scope(fScope, node.getStartPosition(), node.getLength()); return true; } public void endVisit(ForStatement node) { fScope= (Scope)fScopes.remove(fScopes.size() - 1); } private void accept(List list) { int size; if (list == null || (size= list.size()) == 0) return; for (int i= 0; i < size; i++) { ((ASTNode)list.get(i)).accept(this); } } public boolean visit(ForInStatement node) { fScopes.add(fScope); fScope= new Scope(fScope, node.getStartPosition(), node.getLength()); return true; } public void endVisit(ForInStatement node) { fScope= (Scope)fScopes.remove(fScopes.size() - 1); } }