package org.eclipse.dltk.tcl.internal.parser; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Stack; import org.eclipse.dltk.ast.ASTNode; import org.eclipse.dltk.ast.Modifiers; import org.eclipse.dltk.ast.declarations.Argument; import org.eclipse.dltk.ast.declarations.Declaration; import org.eclipse.dltk.ast.declarations.FieldDeclaration; import org.eclipse.dltk.ast.declarations.MethodDeclaration; import org.eclipse.dltk.ast.declarations.ModuleDeclaration; import org.eclipse.dltk.ast.declarations.TypeDeclaration; import org.eclipse.dltk.ast.expressions.Expression; import org.eclipse.dltk.ast.expressions.StringLiteral; import org.eclipse.dltk.ast.references.Reference; import org.eclipse.dltk.ast.references.SimpleReference; import org.eclipse.dltk.ast.statements.Statement; import org.eclipse.dltk.compiler.IElementRequestor; import org.eclipse.dltk.compiler.ISourceElementRequestor; import org.eclipse.dltk.compiler.SourceElementRequestVisitor; import org.eclipse.dltk.compiler.IElementRequestor.ImportInfo; import org.eclipse.dltk.compiler.problem.IProblemReporter; import org.eclipse.dltk.compiler.util.Util; import org.eclipse.dltk.tcl.ast.TclConstants; import org.eclipse.dltk.tcl.ast.TclStatement; import org.eclipse.dltk.tcl.ast.expressions.TclBlockExpression; import org.eclipse.dltk.tcl.ast.expressions.TclExecuteExpression; import org.eclipse.dltk.tcl.core.TclKeywordsManager; import org.eclipse.dltk.tcl.core.TclParseUtil; import org.eclipse.dltk.tcl.core.ast.TclAdvancedExecuteExpression; import org.eclipse.dltk.tcl.core.ast.TclPackageDeclaration; import org.eclipse.dltk.tcl.core.extensions.ISourceElementRequestVisitorExtension; import org.eclipse.dltk.tcl.internal.core.TclExtensionManager; public class TclSourceElementRequestVisitor extends SourceElementRequestVisitor { protected Stack<ExitFromType> exitStack = new Stack<ExitFromType>(); protected IProblemReporter fReporter; protected ISourceElementRequestVisitorExtension[] extensions = TclExtensionManager .getDefault().getSourceElementRequestoVisitorExtensions(); public TclSourceElementRequestVisitor(ISourceElementRequestor requestor, IProblemReporter reporter) { super(requestor); this.fReporter = reporter; } protected String removeLastSegment(String s, String delimeter) { if (s.indexOf("::") == -1) { return Util.EMPTY_STRING; } int pos = s.length() - 1; while (s.charAt(pos) != ':') { pos--; } if (pos > 1) { return s.substring(0, pos - 1); } else { return "::"; } } public static class ExitFromType { private int level; private int end; private boolean exitFromModule; private String namespace; public boolean created = false; /** * @since 2.0 */ public ExitFromType(int level, int declEnd, boolean mod, String pop) { this(level, declEnd, mod, pop, false); } /** * @since 2.0 */ public ExitFromType(int level, int declEnd, boolean mod, String pop, boolean created) { this.level = level; this.end = declEnd; this.exitFromModule = mod; this.namespace = pop; this.created = created; } /** * @since 2.0 */ public void go(IElementRequestor requestor) { for (int i = 0; i < this.level; i++) { requestor.exitType(this.end); } if (this.exitFromModule) { requestor.exitModuleRoot(); } } } protected String getEnclosingNamespace() { for (int head = exitStack.size() - 1;; --head) { final ExitFromType exit = exitStack.get(head); if (exit.namespace != null) { return exit.namespace; } } } /** * Enters into required type (if type doesn't exists, creates it). If name * is fully-qualified (starting with a "::") then it is always resolved * globally. Else search are done first in current namespace, than in * global. Flags <code>onlyCurrent</code> allows to search * <em>not qualified</em> names only in current namespace. If type doesn't * exists, it will be created. If name is qualified, it will be created * globally, else in current namespace.s * * @param decl * expression containing typedeclaration correct source ranges * setup * @param name * name containing a type * @param onlyCurrent * @return ExitFromType object, that should be called to exit */ public ExitFromType resolveType(Declaration decl, String name, boolean onlyCurrent) { String type = removeLastSegment(name, "::"); while (type.length() > 2 && type.endsWith("::")) { type = type.substring(0, type.length() - 2); } if (type.length() == 0) { return new ExitFromType(0, 0, false, null); } if (type.equals("::")) { this.fRequestor.enterModuleRoot(); return new ExitFromType(0, decl.sourceEnd(), true, "::"); } boolean fqn = type.startsWith("::"); String fullyQualified = type; if (!fqn) { // make name fully-qualified String e = this.getEnclosingNamespace(); if (e == null) { throw new AssertionError("there are no enclosing namespace!"); } if (!e.endsWith("::")) { e += "::"; } fullyQualified = e + type; } // first, try existent if (((ISourceElementRequestor) this.fRequestor).enterTypeAppend(type, "::")) { return new ExitFromType(1/* split.length */, decl.sourceEnd(), false, fullyQualified); } // create it // Lets add warning in any case. int needEnterLeave = 0; String[] split = null; String e = this.getEnclosingNamespace(); if (e == null) { throw new AssertionError("there are no enclosing namespace!"); } boolean entered = false; boolean exitFromModule = false; if (e.length() > 0 && !fqn) { // We need to report warning here. entered = ((ISourceElementRequestor) this.fRequestor) .enterTypeAppend(e, "::"); } if (fqn || !entered) { split = TclParseUtil.tclSplit(fullyQualified.substring(2)); this.fRequestor.enterModuleRoot(); exitFromModule = true; } else { if (!entered) { throw new AssertionError("can't enter to enclosing namespace!"); } needEnterLeave++; split = TclParseUtil.tclSplit(type); } for (int i = 0; i < split.length; ++i) { if (split[i].length() > 0) { needEnterLeave++; if (!((ISourceElementRequestor) this.fRequestor) .enterTypeAppend(split[i], "::")) { ISourceElementRequestor.TypeInfo ti = new ISourceElementRequestor.TypeInfo(); if (decl instanceof TypeDeclaration) { ti.modifiers = getModifiers(decl); } else { ti.modifiers = Modifiers.AccNameSpace; } ti.name = split[i]; ti.nameSourceStart = decl.getNameStart(); ti.nameSourceEnd = decl.getNameEnd() - 1; ti.declarationStart = decl.sourceStart(); if (decl instanceof TypeDeclaration) { ti.superclasses = processSuperClasses((TypeDeclaration) decl); } this.fRequestor.enterType(ti); } } } return new ExitFromType(needEnterLeave, decl.sourceEnd(), exitFromModule, fullyQualified, true); } public boolean visit(TypeDeclaration s) throws Exception { this.fNodes.push(s); ISourceElementRequestor.TypeInfo info = new ISourceElementRequestor.TypeInfo(); info.modifiers = this.getModifiers(s); String fullName = s.getName(); String[] split = TclParseUtil.tclSplit(fullName); if (split.length != 0) { info.name = split[split.length - 1]; } else { info.name = ""; } info.nameSourceStart = s.getNameStart(); info.nameSourceEnd = s.getNameEnd(); info.declarationStart = s.sourceStart(); info.superclasses = this.processSuperClasses(s); info.modifiers = this.getModifiers(s); ExitFromType exit = this.resolveType(s, fullName + "::dummy", true); this.exitStack.push(exit); this.fInClass = true; return true; } protected int getModifiers(Declaration s) { int flags = 0; flags = s.getModifiers(); for (int i = 0; i < this.extensions.length; i++) { flags |= this.extensions[i].getModifiers(s); } return flags; } private boolean isConstructor(MethodDeclaration s) { for (int i = 0; i < this.extensions.length; i++) { if (this.extensions[i].isConstructor(s)) { return true; } } return false; } public boolean endvisit(TypeDeclaration typeDeclaration) throws Exception { ExitFromType exit = this.exitStack.pop(); exit.go(fRequestor); this.fInClass = false; this.onEndVisitClass(typeDeclaration); this.fNodes.pop(); return true; } private static Map<String, Boolean> kwMap = new HashMap<String, Boolean>(); static { String[] kw = TclKeywordsManager.getKeywords(); for (int q = 0; q < kw.length; ++q) { kwMap.put(kw[q], Boolean.TRUE); } } public boolean visit(Statement statement) throws Exception { this.fNodes.push(statement); if (statement instanceof TclPackageDeclaration) { this.processPackage(statement); this.fNodes.pop(); return false; } else if (statement instanceof TclStatement) { processReferences((TclStatement) statement); // it can contain nested scripts/substitutions return true; } else if (statement instanceof FieldDeclaration) { this.processField(statement); return false; } for (int i = 0; i < extensions.length; i++) { if (extensions[i].visit(statement, this)) { return true; } } return true; } private void processReferences(TclStatement statement) { Expression commandId = statement.getAt(0); if (commandId != null && commandId instanceof SimpleReference) { String name = ((SimpleReference) commandId).getName(); if (name.startsWith("::")) { name = name.substring(2); } if ("source".equals(name)) { if (statement.getCount() > 1) { Expression sourceFile = statement.getAt(1); if (sourceFile instanceof Reference) { ImportInfo importInfo = new ImportInfo(); importInfo.sourceStart = statement.sourceStart(); importInfo.sourceEnd = statement.sourceEnd(); importInfo.containerName = org.eclipse.dltk.tcl.core.TclConstants.SOURCE_CONTAINER; importInfo.name = ((Reference) sourceFile) .getStringRepresentation(); this.fRequestor.acceptImport(importInfo); } } } if (!kwMap.containsKey(name)) { int argCount = statement.getCount() - 1; if (name.length() > 0) { if (name.charAt(0) != '$') { this.fRequestor.acceptMethodReference(name, argCount, commandId.sourceStart(), commandId.sourceEnd()); } } } } for (int j = 1; j < statement.getCount(); ++j) { Expression st = statement.getAt(j); if (st instanceof TclExecuteExpression) { TclExecuteExpression expr = (TclExecuteExpression) st; List exprs = expr.parseExpression(); for (int i = 0; i < exprs.size(); ++i) { if (exprs.get(i) instanceof TclStatement) { this.processReferences((TclStatement) exprs.get(i)); } else if (exprs.get(i) instanceof TclPackageDeclaration) { processPackage((Statement) exprs.get(i)); } } } else if (st instanceof TclAdvancedExecuteExpression) { TclAdvancedExecuteExpression expr = (TclAdvancedExecuteExpression) st; List exprs = expr.getStatements(); for (int i = 0; i < exprs.size(); ++i) { if (exprs.get(i) instanceof TclStatement) { this.processReferences((TclStatement) exprs.get(i)); } else if (exprs.get(i) instanceof TclPackageDeclaration) { processPackage((Statement) exprs.get(i)); } } } else if (st instanceof StringLiteral) { int pos = 0; StringLiteral literal = (StringLiteral) st; String value = literal.getValue(); pos = value.indexOf("$"); while (pos != -1) { SimpleReference ref = OldTclParserUtils .findVariableFromString(literal, pos); if (ref != null) { this.fRequestor.acceptFieldReference(ref.getName() .substring(1), ref.sourceStart()); pos = pos + ref.getName().length(); } pos = value.indexOf("$", pos + 1); } } else if (st instanceof SimpleReference) { SimpleReference ref = (SimpleReference) st; String name = ref.getName(); if (name.startsWith("$")) { // This is variable usage. this.fRequestor.acceptFieldReference(ref.getName() .substring(1), ref.sourceStart()); } } } } private void processPackage(Statement statement) { TclPackageDeclaration pack = (TclPackageDeclaration) statement; ASTNode version = pack.getVersion(); if (pack.getStyle() == TclPackageDeclaration.STYLE_PROVIDE) { if (version != null && version instanceof SimpleReference) { this.fRequestor.acceptPackage(pack.getNameStart(), pack .getNameEnd() - 1, pack.getName() + " (" + ((SimpleReference) version).getName() + ")"); } else { this.fRequestor.acceptPackage(pack.getNameStart(), pack .getNameEnd() - 1, pack.getName()); } } else if (pack.getStyle() == TclPackageDeclaration.STYLE_IFNEEDED) { if (version != null && version instanceof SimpleReference) { this.fRequestor.acceptPackage(pack.getNameStart(), pack .getNameEnd() - 1, pack.getName() + " (" + ((SimpleReference) version).getName() + ")*"); } else { this.fRequestor.acceptPackage(pack.getNameStart(), pack .getNameEnd() - 1, pack.getName() + "*"); } } else if (pack.getStyle() == TclPackageDeclaration.STYLE_REQUIRE) { ImportInfo importInfo = new ImportInfo(); importInfo.sourceStart = pack.getNameStart(); importInfo.sourceEnd = pack.getNameEnd() - 1; importInfo.containerName = org.eclipse.dltk.tcl.core.TclConstants.REQUIRE_CONTAINER; importInfo.name = pack.getName(); importInfo.version = version instanceof SimpleReference ? ((SimpleReference) version) .getName() : null; this.fRequestor.acceptImport(importInfo); } } protected boolean processField(Statement statement) { FieldDeclaration decl = (FieldDeclaration) statement; ISourceElementRequestor.FieldInfo fi = new ISourceElementRequestor.FieldInfo(); fi.nameSourceStart = decl.getNameStart(); fi.nameSourceEnd = decl.getNameEnd() - 1; fi.declarationStart = decl.sourceStart(); fi.modifiers = this.getModifiers(decl); boolean needExit = false; String arrayName = null; String arrayIndex = null; String name = decl.getName(); if (TclParseUtil.isArrayVariable(name)) { arrayName = TclParseUtil.extractArrayName(name); arrayIndex = TclParseUtil.extractArrayIndex(name); } if (arrayName != null) { name = arrayName; } fi.name = name; String fullName = TclParseUtil.escapeName(name); ExitFromType exit = null;// this.resolveType(decl, fullName, false); for (int i = 0; i < extensions.length; i++) { if ((exit = extensions[i].processField(decl, this)) != null) { continue; } } if (exit == null) { exit = this.resolveType(decl, fullName, false); } needExit = ((ISourceElementRequestor) this.fRequestor) .enterFieldCheckDuplicates(fi); int end = decl.sourceEnd(); if (needExit) { if (arrayName != null) { ISourceElementRequestor.FieldInfo fiIndex = new ISourceElementRequestor.FieldInfo(); fiIndex.name = arrayName + "(" + arrayIndex + ")"; fiIndex.nameSourceStart = decl.getNameStart(); fiIndex.nameSourceEnd = decl.getNameEnd() - 1; fiIndex.declarationStart = decl.sourceStart(); fiIndex.modifiers = TclConstants.TCL_FIELD_TYPE_INDEX | this.getModifiers(decl); if (((ISourceElementRequestor) this.fRequestor) .enterFieldCheckDuplicates(fiIndex)) { this.fRequestor.exitField(end); } } this.fRequestor.exitField(end); } exit.go(fRequestor); return false; } public boolean visit(ModuleDeclaration declaration) throws Exception { this.exitStack.push(new ExitFromType(0, 0, false, "::")); return super.visit(declaration); } protected ExitFromType getExitExtended(MethodDeclaration method) { for (int i = 0; i < extensions.length; i++) { if (extensions[i].extendedExitRequired(method, this)) { return extensions[i].getExitExtended(method, this); } } return null; } protected boolean extendedExitRequired(MethodDeclaration method) { for (int i = 0; i < extensions.length; i++) { if (extensions[i].extendedExitRequired(method, this)) { return true; } } return false; } public boolean visit(MethodDeclaration method) throws Exception { this.fNodes.push(method); this.fInMethod = true; this.fCurrentMethod = method; for (int i = 0; i < extensions.length; i++) { if (extensions[i].skipMethod(method, this)) { return true; } } String[] parameter = null; String[] parameterInitializers = null; List arguments = method.getArguments(); if (arguments != null) { parameter = new String[arguments.size()]; parameterInitializers = new String[arguments.size()]; for (int a = 0; a < arguments.size(); a++) { Object node = arguments.get(a); parameterInitializers[a] = null; if (node instanceof Argument) { Argument ref = (Argument) node; parameter[a] = ref.getName(); Statement e = (Statement) ref.getInitialization(); if (e != null) { if (e instanceof SimpleReference) { parameterInitializers[a] = ((SimpleReference) e) .getName(); } else if (e instanceof TclBlockExpression) { String name = ((TclBlockExpression) e).getBlock(); parameterInitializers[a] = name; } else if (e instanceof StringLiteral) { String name = ((StringLiteral) e).getValue(); parameterInitializers[a] = name; } else if (e instanceof TclExecuteExpression) { String name = ((TclExecuteExpression) e) .getExpression(); parameterInitializers[a] = name; } } // if( parameterInitializers[a] == null ) { // parameterInitializers[a] = ""; // } } else if (node instanceof String) { parameter[a] = (String) node; } } } ISourceElementRequestor.MethodInfo mi = new ISourceElementRequestor.MethodInfo(); String sName = method.getName(); sName = TclParseUtil.escapeName(sName); String fullName = sName; if (fullName.indexOf("::") != -1) { String[] split = TclParseUtil.tclSplit(fullName); sName = split[split.length - 1]; } mi.parameterNames = parameter; mi.parameterInitializers = parameterInitializers; mi.name = sName; mi.modifiers = this.getModifiers(method); mi.isConstructor = this.isConstructor(method); mi.nameSourceStart = method.getNameStart(); mi.nameSourceEnd = method.getNameEnd() - 1; mi.declarationStart = method.sourceStart(); ExitFromType exit = null; // boolean requireFieldExit = false; if (extendedExitRequired(method)) { exit = getExitExtended(method); } else { exit = this.resolveType(method, fullName, false); } // if (exit.created) { // if (this.fReporter != null) { // try { // this.fReporter.reportProblem(new DefaultProblem("", // "Namespace not found.", 0, null, // ProblemSeverities.Warning, method.getNameStart(), // method.getNameEnd(), -1)); // } catch (CoreException e1) { // if (DLTKCore.DEBUG) { // e1.printStackTrace(); // } // } // } // } ((ISourceElementRequestor) this.fRequestor).enterMethodRemoveSame(mi); this.exitStack.push(exit); return true; } public boolean endvisit(MethodDeclaration method) throws Exception { for (int i = 0; i < extensions.length; i++) { if (extensions[i].skipMethod(method, this)) { this.fNodes.pop(); return true; } } super.endvisit(method); ExitFromType exit = this.exitStack.pop(); exit.go(fRequestor); return true; } public ISourceElementRequestor getRequestor() { return ((ISourceElementRequestor) this.fRequestor); } }