package org.eclipse.dltk.tcl.core; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.eclipse.dltk.ast.ASTNode; import org.eclipse.dltk.ast.ASTVisitor; 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.SimpleReference; import org.eclipse.dltk.ast.statements.Block; import org.eclipse.dltk.ast.statements.Statement; import org.eclipse.dltk.core.DLTKCore; import org.eclipse.dltk.core.IMember; import org.eclipse.dltk.core.IModelElement; 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.ast.ExtendedTclMethodDeclaration; import org.eclipse.dltk.tcl.internal.core.codeassist.TclASTUtil; import org.eclipse.dltk.tcl.internal.parser.raw.BracesSubstitution; import org.eclipse.dltk.tcl.internal.parser.raw.CommandSubstitution; import org.eclipse.dltk.tcl.internal.parser.raw.QuotesSubstitution; import org.eclipse.dltk.tcl.internal.parser.raw.SimpleTclParser; import org.eclipse.dltk.tcl.internal.parser.raw.TclCommand; import org.eclipse.dltk.tcl.internal.parser.raw.TclElement; import org.eclipse.dltk.tcl.internal.parser.raw.TclWord; public class TclParseUtil { public static String extractWord(TclElement element, String content) { return content.substring((element).getStart(), (element).getEnd() + 1); } public static boolean isCommandStart(Object object) { return object != null && object.getClass().equals(TclWord.class); } /** * @since 2.0 */ public static String extractBraces(String s) { if (s != null) { final int len = s.length(); if (len >= 2 && s.charAt(0) == '{' && s.charAt(len - 1) == '}') { return s.substring(1, len - 1); } } return s; } public static String nameFromBlock(String name, char c1, char c2) { int pos = name.indexOf(c1); String nname = name.substring(pos + 1); pos = nname.lastIndexOf(c2); if (nname.length() > pos) { nname = nname.substring(0, pos); } return nname; } public static boolean isArrayVariable(String name) { if (name.length() <= 2) { return false; } if (!name.endsWith(")")) { return false; } if (name.indexOf('(') == -1) { return false; } return true; } public static String escapeName(String name) { // TODO optimize if there are no special chars name = SimpleTclParser.magicSubstitute(name); StringBuilder res = null; int len = name.length(); for (int i = 0; i < len; i++) { final char ch = name.charAt(i); if (Character.isISOControl(ch)) { if (res == null) { res = new StringBuilder(name.length() * 2); res.append(name, 0, i); } res.append("\\u"); String tmp = Integer.toHexString(ch).toUpperCase(); if (tmp.length() == 1) { res.append("0"); } res.append(tmp); } else { if (res != null) { res.append(ch); } } } if (res != null) { if (res.length() == 0 || res.charAt(0) == ' ' || res.charAt(res.length() - 1) == ' ') { res.insert(0, '{'); res.append('}'); } return res.toString(); } else if (name.length() == 0 || name.charAt(0) == ' ' || name.charAt(name.length() - 1) == ' ') { return "{" + name + "}"; } else { return name; } } public static SimpleReference makeVariable(Expression variableName) { String name = null; int start = 0; int end = 0; if (variableName instanceof SimpleReference) { name = ((SimpleReference) variableName).getName(); } else if (variableName instanceof TclBlockExpression) { name = ((TclBlockExpression) variableName).getBlock(); name = nameFromBlock(name, '{', '}'); } else if (variableName instanceof StringLiteral) { name = ((StringLiteral) variableName).getValue(); name = nameFromBlock(name, '"', '"'); } else if (variableName instanceof TclExecuteExpression) { name = ((TclExecuteExpression) variableName).getExpression(); } if (name != null) { String fullName = escapeName(name); start = variableName.sourceStart(); end = variableName.sourceEnd(); return new SimpleReference(start, end, fullName); } return null; } public static TclStatement convertToAST(TclCommand command, String filename, int offset, String content, int startPos) { try { List<ASTNode> exprs = new ArrayList<>(); for (TclWord word : command.getWords()) { // wordText = SimpleTclParser.magicSubstitute(wordText); Object o = word.getContents().get(0); String wordText = getWordText(word); if (o instanceof QuotesSubstitution) { QuotesSubstitution qs = (QuotesSubstitution) o; exprs.add(new StringLiteral( startPos + offset + qs.getStart(), startPos + offset + qs.getEnd() + 1, wordText)); } else if (o instanceof BracesSubstitution) { BracesSubstitution bs = (BracesSubstitution) o; wordText = content.substring(offset + word.getStart(), word.getEnd() + 1 + offset); TclBlockExpression tclBlockExpression = new TclBlockExpression( startPos + offset + bs.getStart(), startPos + offset + bs.getEnd() + 1, wordText); // Advanced content for tcl blocks. tclBlockExpression.setFilename(filename); exprs.add(tclBlockExpression); } else if (o instanceof CommandSubstitution && (word.getContents().size() == 1)) { CommandSubstitution bs = (CommandSubstitution) o; exprs.add(new TclExecuteExpression( startPos + offset + bs.getStart(), startPos + offset + bs.getEnd() + 1, wordText)); } else { exprs.add(new SimpleReference( startPos + offset + word.getStart(), startPos + offset + word.getEnd() + 1, wordText)); } } TclStatement st = new TclStatement(exprs); return st; } catch (StringIndexOutOfBoundsException bounds) { return null; } } private static String getWordText(TclWord word) { StringBuffer buff = new StringBuffer(); List<Object> contents = word.getContents(); for (Object object : contents) { if (object instanceof String) { buff.append((String) object); } } return buff.toString(); } public static void addToDeclaration(ASTNode decl, ASTNode node) { if (decl instanceof ModuleDeclaration && node instanceof Statement) { ((ModuleDeclaration) decl).addStatement(node); } else if (decl instanceof TypeDeclaration) { ((TypeDeclaration) decl).getStatements().add(node); } else if (decl instanceof MethodDeclaration) { ((MethodDeclaration) decl).getStatements().add(node); } else if (decl instanceof Block) { ((Block) decl).addStatement(node); } } public static void removeFromDeclaration(ASTNode decl, ASTNode node) { if (decl instanceof ModuleDeclaration && node instanceof Statement) { ((ModuleDeclaration) decl).removeStatement((Statement) node); } else if (decl instanceof TypeDeclaration) { ((TypeDeclaration) decl).getStatements().remove(node); } else if (decl instanceof MethodDeclaration) { ((MethodDeclaration) decl).getStatements().remove(node); } else if (decl instanceof Block) { ((Block) decl).removeStatement(node); } } public static List<ASTNode> findLevelsTo(ModuleDeclaration module, ASTNode astNodeParent) { List<ASTNode> elements = new ArrayList<>(); if (module != null || astNodeParent instanceof ModuleDeclaration) { if (module == null) { module = (ModuleDeclaration) astNodeParent; } elements.add(module); findElementsTo(TclASTUtil.getStatements(module), astNodeParent, elements); } return elements; } public static void findElementsTo(List statements, ASTNode node, List elements) { if (statements == null) { return; } Iterator i = statements.iterator(); while (i.hasNext()) { ASTNode n = (ASTNode) i.next(); if (n.equals(node)) { elements.add(n); return; } if (n.sourceStart() <= node.sourceStart() && node.sourceEnd() <= n.sourceEnd()) { elements.add(n); findElementsTo(TclASTUtil.getStatements(n), node, elements); return; } } } public static TypeDeclaration findXOTclTypeDeclarationFrom( ModuleDeclaration module, ASTNode parent, String originalName) { return findTclTypeDeclarationFrom(module, parent, originalName, true); } public static TypeDeclaration findTclTypeDeclarationFrom( ModuleDeclaration module, ASTNode node) { String name = getNameFromNode(node); if (name == null) { return null; } if (name.indexOf("::") != -1) { name = name.substring(0, name.lastIndexOf("::")); } List levels = findLevelsTo(module, node); if (levels.size() == 2) { return findTclTypeDeclarationFrom(module, module, name, false); } else if (levels.size() - 2 > 0) { return findTclTypeDeclarationFrom(module, (ASTNode) levels.get(levels.size() - 2), name, false); } return null; } public static String getNameFromNode(ASTNode node) { if (node instanceof Declaration) { return ((Declaration) node).getName(); } else if (node instanceof SimpleReference) { return ((SimpleReference) node).getName(); } return null; } public static TypeDeclaration findTclTypeDeclarationFrom( ModuleDeclaration module, ASTNode parent, String originalName, boolean onlyXOTcl) { String name = originalName; boolean startFromTop = false; if (name.startsWith("::")) { startFromTop = true; name = name.substring(2); } String[] split = TclParseUtil.tclSplit(name); // Set name last name = split[split.length - 1]; if (!startFromTop) { List levels = TclParseUtil.findLevelsTo(module, parent); int len = levels.size(); // We need to observe only previous level. // for (int j = 0; j < len; ++j) { if (len > 0) { ASTNode astNodeParent = (ASTNode) levels.get(len - 1/* - j */); List childs = TclASTUtil.getStatements(astNodeParent); if (childs != null) { TypeDeclaration ty = findTclTypeCheckASTLevel(originalName, split, childs); if (ty != null) { return ty; } } } } else { List childs = TclASTUtil.getStatements(module); if (childs == null) { return null; } TypeDeclaration ty = findTclTypeCheckASTLevel(originalName, split, childs); if (ty != null) { return ty; } } return null; } private static TypeDeclaration findTclTypeCheckASTLevel(String originalName, String[] split, List childs) { for (int i = 0; i < childs.size(); i++) { if (!(childs.get(i) instanceof TypeDeclaration)) { continue; } TypeDeclaration type = (TypeDeclaration) childs.get(i); // if ((type.getModifiers() & Modifiers.AccNameSpace) == 0 // || !onlyXOTcl) { // if (type.getName().equals(originalName)) { // Check for // // original name // return type; // } // Check complex in String cName = split[0]; String tName = type.getName(); if (tName.startsWith("::")) { tName = tName.substring(2); } for (int q = 1; q <= split.length; ++q) { if (tName.equals(cName)) { if (q == split.length) { return type; } else { String nsplit[] = new String[split.length - q]; System.arraycopy(split, q, nsplit, 0, split.length - q); List nchilds = TclASTUtil.getStatements(type); if (childs == null) { continue; } TypeDeclaration ty = findTclTypeCheckASTLevel( originalName, nsplit, nchilds); if (ty != null) { return ty; } } } if (q != split.length) { cName += "::" + split[q]; } } if (tName.equals(split[0]) && split.length == 1) { return type; } else { if (split.length > 1) { List nchilds = TclASTUtil.getStatements(type); if (childs == null) { continue; } String nsplit[] = new String[split.length - 1]; System.arraycopy(split, 1, nsplit, 0, split.length - 1); TypeDeclaration ty = findTclTypeCheckASTLevel(originalName, nsplit, nchilds); if (ty != null) { return ty; } } } } // } return null; } public static TypeDeclaration findTypesFromASTNode(ModuleDeclaration module, ASTNode node, String name) { List levels = findLevelsTo(module, node); String[] split = TclParseUtil.tclSplit(name); for (int i = 0; i < levels.size() - 1; i++) { ASTNode nde = (ASTNode) levels.get(levels.size() - i - 2); if (nde instanceof TypeDeclaration) { TypeDeclaration type = (TypeDeclaration) nde; if (split.length == 2) { if (type.getName().equals(split[0])) { return type; } } TypeDeclaration[] types = type.getTypes(); } } return null; } public static String getElementFQN(ASTNode node, String separator, ModuleDeclaration module) { List<ASTNode> nodes = findLevelsTo(module, node); if (!nodes.contains(node)) { nodes.add(node); } return getElementFQN(nodes, separator, module); } public static String getElementFQN(List<ASTNode> nodes, String separator, ModuleDeclaration module) { StringBuffer prefix = new StringBuffer(); for (ASTNode ns : nodes) { String name = null; if (ns instanceof ModuleDeclaration) { name = ""; // module = (ModuleDeclaration) ns; } else if (ns instanceof TypeDeclaration) { name = ((TypeDeclaration) ns).getName(); if (name.endsWith("::")) { name = name.substring(0, name.length() - 2); } } else if (ns instanceof MethodDeclaration) { if (ns instanceof ExtendedTclMethodDeclaration) { ExtendedTclMethodDeclaration m = (ExtendedTclMethodDeclaration) ns; ASTNode declaringXOTclType = m.getDeclaringType(); if (declaringXOTclType != null) { List ndss = findLevelsTo(module, declaringXOTclType); name = "::" + getElementFQN(ndss, separator, module) + separator + m.getName(); } } else { name = ((MethodDeclaration) ns).getName(); } } else if (ns instanceof FieldDeclaration) { name = ((FieldDeclaration) ns).getName(); } if (name != null) { if (name.startsWith("::")) { prefix.delete(0, prefix.length()); name = name.substring(2); } if (name.length() > 0) { prefix.append(tclNameTo(name, separator) + separator); } } } String result = prefix.toString(); if (result.endsWith(separator)) { return result.substring(0, result.length() - separator.length()); } return result; } public static String tclNameTo(String name, String separator) { if (!separator.equals("::")) { name = name.replaceAll("::", separator); } return name; } public static String extractArrayName(String name) { int t1 = name.indexOf("("); if (t1 > 0 && (name.charAt(t1 - 1) == '\\')) { t1--; } return name.substring(0, t1); } public static String extractArrayIndex(String name) { int t1 = name.indexOf("("); if (t1 > 0 && (name.charAt(t1 - 1) == '\\')) { t1--; } String arrayIndex = name.substring(name.indexOf("(") + 1, name.length() - 1); if (arrayIndex.endsWith("\\")) { arrayIndex = arrayIndex.substring(0, arrayIndex.length() - 1); } return arrayIndex; } public static ASTNode getScopeParent(ModuleDeclaration module, ASTNode node) { List levels = TclParseUtil.findLevelsTo(module, node); for (int i = 0; i < levels.size(); i++) { ASTNode nde = (ASTNode) levels.get(levels.size() - i - 1); if (nde instanceof TypeDeclaration || nde instanceof MethodDeclaration || nde instanceof ModuleDeclaration && nde instanceof Block) { return nde; } } return module; } public static ASTNode getPrevParent(ModuleDeclaration module, ASTNode declaringType) { ASTNode parent = TclParseUtil.getScopeParent(module, declaringType); if (parent instanceof ModuleDeclaration) { return parent; } List levels = TclParseUtil.findLevelsTo(module, parent); return (ASTNode) levels.get(levels.size() - 2); } public static List<ASTNode> findLevelFromModule( final ModuleDeclaration module, final IMember member, final String memberFQN) { final List<ASTNode> levels = new ArrayList<>(); final Set<String> processed = new HashSet<>(); ASTVisitor visitor = new ASTVisitor() { @Override public boolean visitGeneral(ASTNode s) throws Exception { if (s instanceof Declaration) { Declaration d = (Declaration) s; String key = "::" + getElementFQN(s, "::", module); if (key.equals(memberFQN)) { if (processed.add(key)) { levels.add(d); } } } return true; } }; try { module.traverse(visitor); } catch (Exception e) { if (DLTKCore.DEBUG) { e.printStackTrace(); } } return levels; } public static String getNameFromModelElement(IModelElement member) { return getFQNFromModelElement(member, "::"); } public static String getFQNFromModelElement(IModelElement member, String separator) { String buffer = new String(); IModelElement m = member; while (m.getElementType() != IModelElement.SOURCE_MODULE) { buffer = separator + m.getElementName() + buffer; m = m.getParent(); } return buffer; } public static String[] tclSplit(String text) { int len = text.length(); if (len < 2) { return new String[] { text }; } List<String> results = new ArrayList<>(); int pos = 0; for (int i = 0; i < len; ++i) { int c = 0; for (int j = i; j < len; j++) { if (text.charAt(j) == ':') { c++; } else { break; } } if (c > 1) { if (pos <= i) { results.add(text.substring(pos, i)); } pos = i + c; i += (c - 1); } } if (pos < len) { results.add(text.substring(pos, len)); } if (results.isEmpty()) { results.add(""); } return results.toArray(new String[results.size()]); } }