package com.redhat.ceylon.eclipse.code.imports; import static com.redhat.ceylon.compiler.typechecker.tree.TreeUtil.formatPath; import static com.redhat.ceylon.eclipse.util.EditorUtil.getCurrentEditor; import static com.redhat.ceylon.eclipse.java2ceylon.Java2CeylonProxies.utilJ2C; import static com.redhat.ceylon.eclipse.java2ceylon.Java2CeylonProxies.importsJ2C; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.TreeMap; import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.ExecutionException; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.window.Window; import org.eclipse.ltk.core.refactoring.DocumentChange; import org.eclipse.swt.widgets.Shell; import org.eclipse.text.edits.ReplaceEdit; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IFileEditorInput; import com.redhat.ceylon.compiler.typechecker.analyzer.AnalysisError; import com.redhat.ceylon.compiler.typechecker.tree.Message; import com.redhat.ceylon.compiler.typechecker.tree.Node; import com.redhat.ceylon.compiler.typechecker.tree.Tree; import com.redhat.ceylon.compiler.typechecker.tree.Tree.ImportPath; import com.redhat.ceylon.eclipse.code.editor.CeylonEditor; import com.redhat.ceylon.eclipse.code.parse.CeylonParseController; import com.redhat.ceylon.eclipse.code.parse.TreeLifecycleListener.Stage; import com.redhat.ceylon.eclipse.util.EditorUtil; import com.redhat.ceylon.ide.common.util.escaping_; import com.redhat.ceylon.model.typechecker.model.Declaration; import com.redhat.ceylon.model.typechecker.model.Module; import com.redhat.ceylon.model.typechecker.model.Package; import com.redhat.ceylon.model.typechecker.model.Referenceable; public class CleanImportsHandler extends AbstractHandler { @Override public Object execute(ExecutionEvent event) throws ExecutionException { CeylonEditor editor = (CeylonEditor) getCurrentEditor(); IDocument doc = editor.getCeylonSourceViewer() .getDocument(); importsJ2C().cleanImports(editor.getParseController(), doc); return null; } @Deprecated public static void cleanImports( CeylonParseController controller, IDocument doc) { if (!isEnabled(controller)) return; Tree.CompilationUnit rootNode = controller.getTypecheckedRootNode(); if (rootNode!=null) { String imports = imports(rootNode, doc); Tree.ImportList importList = rootNode.getImportList(); if (imports!=null && !(imports.trim().isEmpty() && importList.getImports().isEmpty())) { int start; int length; String extra; Tree.ImportList il = importList; if (il==null || il.getImports().isEmpty()) { start=0; length=0; extra=utilJ2C().indents().getDefaultLineDelimiter(doc); } else { start = il.getStartIndex(); length = il.getDistance(); extra=""; } try { if (!doc.get(start, length) .equals(imports+extra)) { DocumentChange change = new DocumentChange( "Organize Imports", doc); change.setEdit(new ReplaceEdit(start, length, imports+extra)); EditorUtil.performChange(change); } } catch (BadLocationException e) { e.printStackTrace(); } } } } @Deprecated // Never used public static String imports(Node node, Tree.ImportList til, IDocument doc) { List<Declaration> unused = new ArrayList<Declaration>(); DetectUnusedImportsVisitor duiv = new DetectUnusedImportsVisitor(unused); til.visit(duiv); node.visit(duiv); return reorganizeImports(til, unused, Collections.<Declaration>emptyList(), doc); } @Deprecated private static String imports(Tree.CompilationUnit cu, IDocument doc) { List<Declaration> proposals = new ArrayList<Declaration>(); List<Declaration> unused = new ArrayList<Declaration>(); new ImportProposalsVisitor(cu, proposals).visit(cu); new DetectUnusedImportsVisitor(unused).visit(cu); return reorganizeImports(cu.getImportList(), unused, proposals, doc); } public static String imports(List<Declaration> proposed, IDocument doc) { return reorganizeImports(null, Collections.<Declaration>emptyList(), proposed, doc); } @Deprecated public static String reorganizeImports(Tree.ImportList til, List<Declaration> unused, List<Declaration> proposed, IDocument doc) { Map<String,List<Tree.Import>> packages = new TreeMap<String,List<Tree.Import>>(); if (til!=null) { for (Tree.Import i: til.getImports()) { String pn = packageName(i); if (pn!=null) { List<Tree.Import> is = packages.get(pn); if (is==null) { is = new ArrayList<Tree.Import>(); packages.put(pn, is); } is.add(i); } } } for (Declaration d: proposed) { Package p = d.getUnit().getPackage(); String pn = p.getNameAsString(); if (!packages.containsKey(pn)) { packages.put(pn, Collections.<Tree.Import>emptyList()); } } StringBuilder builder = new StringBuilder(); String lastToplevel=null; String delim = utilJ2C().indents().getDefaultLineDelimiter(doc); for (Map.Entry<String, List<Tree.Import>> pack: packages.entrySet()) { String packageName = pack.getKey(); List<Tree.Import> imports = pack.getValue(); boolean hasWildcard = hasWildcard(imports); List<Tree.ImportMemberOrType> list = getUsedImportElements(imports, unused, hasWildcard, packages); if (hasWildcard || !list.isEmpty() || imports.isEmpty()) { //in this last case there is no existing import, but imports are proposed lastToplevel = appendBreakIfNecessary(lastToplevel, packageName, builder, doc); Referenceable packageModel = imports.isEmpty() ? null : //TODO: what to do in this case? look up the Package where? imports.get(0).getImportPath() .getModel(); String escapedPackageName; if (packageModel instanceof Package) { Package p = (Package) packageModel; escapedPackageName = escaping_.get_().escapePackageName(p); } else { escapedPackageName = packageName; } if (builder.length()!=0) { builder.append(delim); } builder.append("import ") .append(escapedPackageName) .append(" {"); appendImportElements(packageName, list, unused, proposed, hasWildcard, builder, doc); builder.append(delim).append("}"); } } return builder.toString(); } @Deprecated private static boolean hasWildcard(List<Tree.Import> imports) { boolean hasWildcard = false; for (Tree.Import i: imports) { hasWildcard = hasWildcard || i!=null && i.getImportMemberOrTypeList() .getImportWildcard()!=null; } return hasWildcard; } @Deprecated private static String appendBreakIfNecessary(String lastToplevel, String currentPackage, StringBuilder builder, IDocument doc) { int index = currentPackage.indexOf('.'); String topLevel = index<0 ? currentPackage : currentPackage.substring(0, index); if (lastToplevel!=null && !topLevel.equals(lastToplevel)) { builder.append(utilJ2C().indents().getDefaultLineDelimiter(doc)); } return topLevel; } @Deprecated private static void appendImportElements(String packageName, List<Tree.ImportMemberOrType> elements, List<Declaration> unused, List<Declaration> proposed, boolean hasWildcard, StringBuilder builder, IDocument doc) { String indent = utilJ2C().indents().getDefaultIndent(); String delim = utilJ2C().indents().getDefaultLineDelimiter(doc); for (Tree.ImportMemberOrType i: elements) { Declaration d = i.getDeclarationModel(); if (d!=null && isErrorFree(i)) { builder.append(delim) .append(indent); String alias = i.getImportModel().getAlias(); if (!alias.equals(d.getName())) { String escapedAlias = escaping_.get_().escapeAliasedName(d, new ceylon.language.String(alias)); builder.append(escapedAlias) .append("="); } builder.append(escaping_.get_().escapeName(d)); appendNestedImportElements(i, unused, builder, doc); builder.append(","); } } for (Declaration d: proposed) { Package pack = d.getUnit().getPackage(); if (pack.getNameAsString() .equals(packageName)) { builder.append(delim) .append(indent); builder.append(escaping_.get_().escapeName(d)).append(","); } } if (hasWildcard) { builder.append(delim) .append(indent) .append("..."); } else { // remove trailing , builder.setLength(builder.length()-1); } } @Deprecated private static void appendNestedImportElements( Tree.ImportMemberOrType imt, List<Declaration> unused, StringBuilder builder, IDocument doc) { String indent = utilJ2C().indents().getDefaultIndent(); String delim = utilJ2C().indents().getDefaultLineDelimiter(doc); if (imt.getImportMemberOrTypeList()!=null) { builder.append(" {"); boolean found=false; for (Tree.ImportMemberOrType nimt: imt.getImportMemberOrTypeList() .getImportMemberOrTypes()) { Declaration d = nimt.getDeclarationModel(); if (d!=null && isErrorFree(nimt)) { if (!unused.contains(d)) { found=true; builder.append(delim) .append(indent) .append(indent); String alias = nimt.getImportModel().getAlias(); if (!alias.equals(d.getName())) { String escapedAlias = escaping_.get_().escapeAliasedName(d, new ceylon.language.String(alias)); builder.append(escapedAlias) .append("="); } builder.append(escaping_.get_().escapeName(d)) .append(","); } } } if (imt.getImportMemberOrTypeList() .getImportWildcard()!=null) { found=true; builder.append(delim) .append(indent).append(indent) .append("...,"); } if (found) { // remove trailing "," builder.setLength(builder.length()-1); builder.append(delim) .append(indent) .append('}'); } else { // remove the " {" builder.setLength(builder.length()-2); } } } @Deprecated private static boolean hasRealErrors(Node node) { for (Message m: node.getErrors()) { if (m instanceof AnalysisError) { return true; } } return false; } @Deprecated private static List<Tree.ImportMemberOrType> getUsedImportElements( List<Tree.Import> imports, List<Declaration> unused, boolean hasWildcard, Map<String, List<Tree.Import>> packages) { List<Tree.ImportMemberOrType> list = new ArrayList<Tree.ImportMemberOrType>(); for (Tree.Import ti: imports) { for (Tree.ImportMemberOrType imt: ti.getImportMemberOrTypeList() .getImportMemberOrTypes()) { Declaration dm = imt.getDeclarationModel(); if (dm!=null && isErrorFree(imt)) { Tree.ImportMemberOrTypeList nimtl = imt.getImportMemberOrTypeList(); if (unused.contains(dm)) { if (nimtl!=null) { for (Tree.ImportMemberOrType nimt: nimtl.getImportMemberOrTypes()) { Declaration ndm = nimt.getDeclarationModel(); if (ndm!=null && isErrorFree(nimt)) { if (!unused.contains(ndm)) { list.add(imt); break; } } } if (nimtl.getImportWildcard()!=null) { list.add(imt); } } } else { if (!hasWildcard || imt.getAlias()!=null || nimtl!=null || preventAmbiguityDueWildcards(dm, packages)) { list.add(imt); } } } } } return list; } @Deprecated private static boolean isErrorFree(Tree.ImportMemberOrType imt) { return !hasRealErrors(imt.getIdentifier()) && !hasRealErrors(imt); } @Deprecated private static boolean preventAmbiguityDueWildcards(Declaration d, Map<String, List<Tree.Import>> importsMap) { Module module = d.getUnit().getPackage().getModule(); String containerName = d.getContainer().getQualifiedNameString(); for (Map.Entry<String, List<Tree.Import>> importEntry: importsMap.entrySet()) { String packageName = importEntry.getKey(); List<Tree.Import> importList = importEntry.getValue(); if (!packageName.equals(containerName) && hasWildcard(importList)) { Package p2 = module.getPackage(packageName); if (p2 != null) { Declaration d2 = p2.getMember(d.getName(), null, false); if (d2!=null && d2.isToplevel() && d2.isShared() && !d2.isAnonymous() && !isImportedWithAlias(d2, importList)) { return true; } } } } return false; } @Deprecated private static boolean isImportedWithAlias(Declaration d, List<Tree.Import> importList) { for (Tree.Import i: importList) { for (Tree.ImportMemberOrType imt: i.getImportMemberOrTypeList() .getImportMemberOrTypes()) { String name = imt.getIdentifier().getText(); if (d.getName().equals(name) && imt.getAlias() != null) { return true; } } } return false; } @Deprecated private static String packageName(Tree.Import i) { ImportPath path = i.getImportPath(); if (path!=null) { return formatPath(path.getIdentifiers()); } else { return null; } } @Override public boolean isEnabled() { IEditorPart editor = getCurrentEditor(); if (super.isEnabled() && editor instanceof CeylonEditor && editor.getEditorInput() instanceof IFileEditorInput) { CeylonEditor ce = (CeylonEditor) editor; CeylonParseController cpc = ce.getParseController(); return isEnabled(cpc); } else { return false; } } public static boolean isEnabled(CeylonParseController cpc) { return cpc!=null && cpc.getStage().ordinal() >= Stage.TYPE_ANALYSIS.ordinal() && cpc.getTypecheckedRootNode()!=null; } @Deprecated public static Declaration select(List<Declaration> proposals) { CeylonEditor editor = (CeylonEditor) getCurrentEditor(); Shell shell = editor.getSite().getShell(); ImportSelectionDialog fid = new ImportSelectionDialog(shell, proposals); if (fid.open() == Window.OK) { return (Declaration) fid.getFirstResult(); } else { return null; } } }