### Eclipse Workspace Patch 1.0 #P org.codehaus.groovy.eclipse.ui Index: src/org/codehaus/groovy/eclipse/ui/utils/GroovyResourceUtil.java =================================================================== --- src/org/codehaus/groovy/eclipse/ui/utils/GroovyResourceUtil.java (revision 0) +++ src/org/codehaus/groovy/eclipse/ui/utils/GroovyResourceUtil.java (revision 0) @@ -0,0 +1,224 @@ +/* + * Copyright 2009, 2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.codehaus.groovy.eclipse.ui.utils; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.codehaus.groovy.eclipse.GroovyPlugin; +import org.codehaus.groovy.eclipse.core.GroovyCore; +import org.codehaus.groovy.eclipse.core.model.GroovyRuntime; +import org.codehaus.groovy.eclipse.editor.actions.RenameToGroovyOrJavaAction; +import org.codehaus.jdt.groovy.model.GroovyNature; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.ltk.core.refactoring.resource.RenameResourceChange; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IEditorReference; +import org.eclipse.ui.IFileEditorInput; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.ide.IDE; +import org.eclipse.ui.progress.UIJob; + +/** + * + * @author Andrew Eisenberg + * @author Nieraj Singh + * @created 2010-11-18 + */ +public class GroovyResourceUtil { + + public static final String GROOVY = ".groovy"; + + public static final String JAVA = ".java"; + + private GroovyResourceUtil() { + // Utility + } + + public static IStatus renameFile(String type, List resources) { + UIJob renameTo = new RenameToGroovyOrJavaJob(type, resources); + renameTo.schedule(); + return Status.OK_STATUS; + } + + /** + * @author Andrew Eisenberg + * @created Oct 17, 2009 + * + */ + public static class RenameToGroovyOrJavaJob extends UIJob { + + private List resources; + + private String javaOrGroovy; + + private RenameToGroovyOrJavaJob(String javaOrGroovy, List resources) { + super(getJobName(javaOrGroovy)); + this.resources = resources; + this.javaOrGroovy = javaOrGroovy; + } + + protected static String getJobName(String javaOrGroovy) { + return "Rename to " + javaOrGroovy; + } + + @Override + public boolean belongsTo(Object family) { + return RenameToGroovyOrJavaAction.class == family; + } + + @Override + public IStatus runInUIThread(IProgressMonitor monitor) { + Set affectedProjects = new HashSet(); + final Set filesAlreadyOpened = new HashSet(); + + for (IResource resource : resources) { + if (resource != null) { + if (resource.getType() != IResource.FILE) { + continue; + } + IDE.saveAllEditors(new IFile[] { (IFile) resource }, true); + String name = convertName(resource); + RenameResourceChange change = new RenameResourceChange(resource.getFullPath(), name); //$NON-NLS-1$ + try { + if (isOpenInEditor(resource)) { + filesAlreadyOpened.add(resource); + } + + change.perform(monitor); + + IProject project = resource.getProject(); + if (!GroovyNature.hasGroovyNature(project)) { + affectedProjects.add(project); + } + } catch (CoreException e) { + String message = "Error converting file extension to " + javaOrGroovy + " for file " + resource.getName(); + GroovyCore.logException(message, e); + return new Status(IStatus.ERROR, GroovyPlugin.PLUGIN_ID, message, e); + } + } + } + + if (!affectedProjects.isEmpty() && javaOrGroovy.equals(GROOVY)) { + askToConvert(affectedProjects, this.getDisplay().getActiveShell()); + } + + reopenFiles(filesAlreadyOpened); + return Status.OK_STATUS; + } + + /** + * @param file + * @return + */ + protected String convertName(IResource file) { + String name = file.getName(); + name = name.substring(0, name.lastIndexOf('.')); + name = name + javaOrGroovy; + return name; + } + + /** + * Reopen files that were closed + * + * @param filesAlreadyOpened + */ + protected void reopenFiles(Set filesAlreadyOpened) { + for (IResource origResource : filesAlreadyOpened) { + String name = convertName(origResource); + IFile newFile = origResource.getParent().getFile(new Path(name)); + try { + IDE.openEditor(getWorkbenchPage(), newFile); + } catch (PartInitException e) { + GroovyCore.logException("Exception thrown when opening " + name + " in an editor", e); + } + } + } + + /** + * @return + */ + private IWorkbenchPage getWorkbenchPage() { + return PlatformUI.getWorkbench().getWorkbenchWindows()[0].getActivePage(); + } + + protected void askToConvert(Set affectedProjects, Shell shell) { + if (affectedProjects.size() == 0) { + return; + } + StringBuilder sb = new StringBuilder(); + if (affectedProjects.size() > 1) { + sb.append("Projects "); + for (IProject project : affectedProjects) { + sb.append(project.getName()).append(", "); + } + sb.replace(sb.length() - 2, 2, " do "); + } else { + sb.append("Project ").append(affectedProjects.iterator().next().getName()).append(" does "); + } + sb.append("have the Groovy nature. Do you want to add it?"); + + boolean yes = MessageDialog.openQuestion(shell, "Convert to Groovy?", sb.toString()); + if (yes) { + for (IProject project : affectedProjects) { + GroovyRuntime.addGroovyRuntime(project); + } + } + } + + /** + * @param file + * @return + */ + protected boolean isOpenInEditor(IResource file) { + try { + IEditorReference[] refs = getWorkbenchPage().getEditorReferences(); + for (IEditorReference ref : refs) { + try { + if (ref.getEditorInput() instanceof IFileEditorInput) { + IFileEditorInput input = (IFileEditorInput) ref.getEditorInput(); + if (input.getFile().equals(file)) { + return true; + } + } + } catch (PartInitException e) { + // Safe to ignore. This can happen when a class file + // editor + // was open, but its underlying resource was deleted. + } + } + } catch (NullPointerException npe) { + // workbench is shutting down, editors cannot be reached + // ok to ignore. + } catch (IndexOutOfBoundsException e) { + // no open workbench windows. OK to ignore + } + return false; + } + } + +} Index: src/org/codehaus/groovy/eclipse/editor/actions/RenameToJavaAction.java =================================================================== --- src/org/codehaus/groovy/eclipse/editor/actions/RenameToJavaAction.java (revision 21246) +++ src/org/codehaus/groovy/eclipse/editor/actions/RenameToJavaAction.java (working copy) @@ -1,5 +1,5 @@ - /* - * Copyright 2003-2009 the original author or authors. +/* + * Copyright 2003-2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ package org.codehaus.groovy.eclipse.editor.actions; +import org.codehaus.groovy.eclipse.ui.utils.GroovyResourceUtil; + /** * @author Andrew Eisenberg * @created Aug 26, 2009 @@ -23,8 +25,9 @@ */ public class RenameToJavaAction extends RenameToGroovyOrJavaAction { public static String COMMAND_ID = "org.codehaus.groovy.eclipse.ui.convertToJava"; + public RenameToJavaAction() { - super(JAVA); + super(GroovyResourceUtil.JAVA); } } Index: src/org/codehaus/groovy/eclipse/editor/actions/RenameToGroovyAction.java =================================================================== --- src/org/codehaus/groovy/eclipse/editor/actions/RenameToGroovyAction.java (revision 21246) +++ src/org/codehaus/groovy/eclipse/editor/actions/RenameToGroovyAction.java (working copy) @@ -1,5 +1,5 @@ - /* - * Copyright 2003-2009 the original author or authors. +/* + * Copyright 2003-2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,7 @@ package org.codehaus.groovy.eclipse.editor.actions; -import java.util.Set; - -import org.codehaus.groovy.eclipse.core.model.GroovyRuntime; -import org.eclipse.core.resources.IProject; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.swt.widgets.Shell; +import org.codehaus.groovy.eclipse.ui.utils.GroovyResourceUtil; /** * @author Andrew Eisenberg @@ -30,31 +25,8 @@ */ public class RenameToGroovyAction extends RenameToGroovyOrJavaAction { public static String COMMAND_ID = "org.codehaus.groovy.eclipse.ui.convertToGroovy"; + public RenameToGroovyAction() { - super(GROOVY); - } - - protected void askToConvert(Set affectedProjects, Shell shell) { - if (affectedProjects.size() == 0) { - return; - } - StringBuilder sb = new StringBuilder(); - if (affectedProjects.size() > 1) { - sb.append("Projects "); - for (IProject project : affectedProjects) { - sb.append(project.getName()).append(", "); - } - sb.replace(sb.length()-2, 2, " do "); - } else { - sb.append("Project ").append(affectedProjects.iterator().next().getName()).append(" does "); - } - sb.append("have the Groovy nature. Do you want to add it?"); - - boolean yes = MessageDialog.openQuestion(shell, "Convert to Groovy?", sb.toString()); - if (yes) { - for (IProject project : affectedProjects) { - GroovyRuntime.addGroovyRuntime(project); - } - } + super(GroovyResourceUtil.GROOVY); } } Index: src/org/codehaus/groovy/eclipse/editor/actions/RenameToGroovyOrJavaAction.java =================================================================== --- src/org/codehaus/groovy/eclipse/editor/actions/RenameToGroovyOrJavaAction.java (revision 21246) +++ src/org/codehaus/groovy/eclipse/editor/actions/RenameToGroovyOrJavaAction.java (working copy) @@ -1,227 +1,94 @@ /********************************************************************** - * Copyright (c) 2004, 2009 IBM Corporation, SpringSource and others. + * Copyright (c) 2004, 2010 IBM Corporation, SpringSource 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: Sian January - initial version * Andrew Eisenberg - conversion to groovy **********************************************************************/ package org.codehaus.groovy.eclipse.editor.actions; - -import java.util.HashSet; +import java.util.ArrayList; import java.util.Iterator; -import java.util.Set; +import java.util.List; -import org.codehaus.groovy.eclipse.GroovyPlugin; -import org.codehaus.groovy.eclipse.core.GroovyCore; -import org.codehaus.jdt.groovy.model.GroovyNature; -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IProject; +import org.codehaus.groovy.eclipse.ui.utils.GroovyResourceUtil; import org.eclipse.core.resources.IResource; -import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Path; -import org.eclipse.core.runtime.Status; import org.eclipse.jface.action.IAction; import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.StructuredSelection; -import org.eclipse.ltk.core.refactoring.resource.RenameResourceChange; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.ui.IEditorReference; -import org.eclipse.ui.IFileEditorInput; -import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.IWorkbenchWindowActionDelegate; -import org.eclipse.ui.PartInitException; -import org.eclipse.ui.PlatformUI; -import org.eclipse.ui.ide.IDE; -import org.eclipse.ui.progress.UIJob; /** * Rename the file extension of a file to groovy or to java */ public abstract class RenameToGroovyOrJavaAction implements IWorkbenchWindowActionDelegate { - /** - * @author Andrew Eisenberg - * @created Oct 17, 2009 - * - */ - private final class RenameToGroovyOrJavaJob extends UIJob { - private RenameToGroovyOrJavaJob(String name) { - super(name); - } - - @Override - public boolean belongsTo(Object family) { - return RenameToGroovyOrJavaAction.class == family; - } - - - - @Override - public IStatus runInUIThread(IProgressMonitor monitor) { - Set affectedProjects = new HashSet(); - final Set filesAlreadyOpened = new HashSet(); - StructuredSelection sel = (StructuredSelection) selection; - for (Iterator iter = sel.iterator(); iter.hasNext();) { - Object object = iter.next(); - if (object instanceof IAdaptable) { - - IResource file = (IResource) ((IAdaptable) object) - .getAdapter(IResource.class); - - if (file != null) { - if (file.getType() != IResource.FILE) { - continue; - } - IDE.saveAllEditors(new IFile[] { (IFile) file }, true); - String name = convertName(file); - RenameResourceChange change = new RenameResourceChange( - file.getFullPath(), name); //$NON-NLS-1$ - try { - if (isOpenInEditor(file)) { - filesAlreadyOpened.add(file); - } - - change.perform(monitor); - - IProject project = file.getProject(); - if (!GroovyNature.hasGroovyNature(project)) { - affectedProjects.add(project); - } - } catch (CoreException e) { - String message = "Error converting file extension to " + - javaOrGroovy + " for file " + file.getName(); - GroovyCore.logException(message, e); - return new Status(IStatus.ERROR, GroovyPlugin.PLUGIN_ID, message, e); - } - } - } - } - - if (! affectedProjects.isEmpty() && javaOrGroovy.equals(GROOVY)) { - askToConvert(affectedProjects, this.getDisplay().getActiveShell()); - } - - reopenFiles(filesAlreadyOpened); - return Status.OK_STATUS; - } - } - - public static final String GROOVY = ".groovy"; - public static final String JAVA = ".java"; private ISelection selection; private String javaOrGroovy; - + public RenameToGroovyOrJavaAction(String javaOrGroovy) { this.javaOrGroovy = javaOrGroovy; } - - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction) - */ - public void run(IAction action) { - if (selection instanceof StructuredSelection) { - UIJob renameTo = new RenameToGroovyOrJavaJob("Rename to " + javaOrGroovy); - renameTo.schedule(); - } - } - /** - * @param file - * @return + /* + * (non-Javadoc) + * + * @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction) */ - protected boolean isOpenInEditor(IResource file) { - try { - IEditorReference[] refs = getWorkbenchPage().getEditorReferences(); - for (IEditorReference ref : refs) { - try { - if (ref.getEditorInput() instanceof IFileEditorInput) { - IFileEditorInput input = (IFileEditorInput) ref.getEditorInput(); - if (input.getFile().equals(file)) { - return true; - } - } - } catch (PartInitException e) { - // Safe to ignore. This can happen when a class file editor - // was open, but its underlying resource was deleted. - } - } - } catch (NullPointerException npe) { - // workbench is shutting down, editors cannot be reached - // ok to ignore. - } catch (IndexOutOfBoundsException e) { - // no open workbench windows. OK to ignore + public void run(IAction action) { + if (selection instanceof IStructuredSelection) { + GroovyResourceUtil.renameFile(javaOrGroovy, getResources((IStructuredSelection) selection)); } - return false; } /** - * @return + * + * @return non-null list of resources in the context selection. May be empty */ - private IWorkbenchPage getWorkbenchPage() { - return PlatformUI.getWorkbench().getWorkbenchWindows()[0].getActivePage(); - } + protected List getResources(IStructuredSelection selection) { + List resources = new ArrayList(); + if (selection != null) { + for (Iterator iter = selection.iterator(); iter.hasNext();) { + Object object = iter.next(); + if (object instanceof IAdaptable) { - /** - * @param file - * @return - */ - protected String convertName(IResource file) { - String name = file.getName(); - name = name.substring(0, name.lastIndexOf('.')); - name = name + javaOrGroovy; - return name; - } + IResource file = (IResource) ((IAdaptable) object).getAdapter(IResource.class); - /** - * Reopen files that were closed - * @param filesAlreadyOpened - */ - protected void reopenFiles(Set filesAlreadyOpened) { - for (IResource origResource : filesAlreadyOpened) { - String name = convertName(origResource); - IFile newFile = origResource.getParent().getFile(new Path(name)); - try { - IDE.openEditor(getWorkbenchPage(), newFile); - } catch (PartInitException e) { - GroovyCore.logException("Exception thrown when opening " + name + " in an editor", e); + if (file != null) { + if (file.getType() != IResource.FILE) { + continue; + } + resources.add(file); + } + } } } + return resources; } - /** - * @param affectedProjects - * @param shell - */ - protected void askToConvert(Set affectedProjects, Shell shell) { - // no op unless converting to groovy - } /* - * (non-Javadoc) - * - * @see org.eclipse.ui.IActionDelegate#selectionChanged(org.eclipse.jface.action.IAction, - * org.eclipse.jface.viewers.ISelection) - */ - public void selectionChanged(IAction action, ISelection selection) { - this.selection = selection; - } + * (non-Javadoc) + * + * @see + * org.eclipse.ui.IActionDelegate#selectionChanged(org.eclipse.jface.action + * .IAction, + * org.eclipse.jface.viewers.ISelection) + */ + public void selectionChanged(IAction action, ISelection selection) { + this.selection = selection; + } public void dispose() { selection = null; } - public void init(IWorkbenchWindow window) { - } + public void init(IWorkbenchWindow window) {} } \ No newline at end of file Index: META-INF/MANIFEST.MF =================================================================== --- META-INF/MANIFEST.MF (revision 21246) +++ META-INF/MANIFEST.MF (working copy) @@ -5,7 +5,8 @@ Bundle-Version: 2.1.1.qualifier Bundle-Activator: org.codehaus.groovy.eclipse.GroovyPlugin Bundle-Vendor: The Codehaus -Export-Package: greclipse.org.eclipse.jdt.internal.ui.javaeditor, +Export-Package: greclipse.org.eclipse.jdt.core.dom.rewrite, + greclipse.org.eclipse.jdt.internal.ui.javaeditor, org.codehaus.groovy.eclipse, org.codehaus.groovy.eclipse.actions, org.codehaus.groovy.eclipse.adapters, @@ -18,6 +19,7 @@ org.codehaus.groovy.eclipse.refactoring.actions, org.codehaus.groovy.eclipse.ui, org.codehaus.groovy.eclipse.ui.cpcontainer, + org.codehaus.groovy.eclipse.ui.utils, org.codehaus.groovy.eclipse.wizards Require-Bundle: org.eclipse.core.resources, org.eclipse.ui, Index: plugin.xml =================================================================== --- plugin.xml (revision 21246) +++ plugin.xml (working copy) @@ -432,6 +432,15 @@ menubarPath="add" id="org.eclipse.ui.texteditor.BookmarkRulerAction"> + + + + Index: src/org/codehaus/groovy/eclipse/editor/GroovyConfiguration.java =================================================================== --- src/org/codehaus/groovy/eclipse/editor/GroovyConfiguration.java (revision 21246) +++ src/org/codehaus/groovy/eclipse/editor/GroovyConfiguration.java (working copy) @@ -172,8 +172,8 @@ @Override public IQuickAssistAssistant getQuickAssistAssistant( ISourceViewer sourceViewer) { - // disable quick assist - return null; + // Enable quick fix assistant for Groovy quick fixes + return super.getQuickAssistAssistant(sourceViewer); } @Override Index: src/org/codehaus/groovy/eclipse/refactoring/actions/TypeSearch.java =================================================================== --- src/org/codehaus/groovy/eclipse/refactoring/actions/TypeSearch.java (revision 0) +++ src/org/codehaus/groovy/eclipse/refactoring/actions/TypeSearch.java (revision 0) @@ -0,0 +1,93 @@ +/* + * Copyright 2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.codehaus.groovy.eclipse.refactoring.actions; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.codehaus.groovy.eclipse.core.GroovyCore; +import org.codehaus.groovy.eclipse.refactoring.actions.OrganizeGroovyImports.UnresolvedTypeData; +import org.codehaus.jdt.groovy.model.GroovyCompilationUnit; +import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.search.IJavaSearchConstants; +import org.eclipse.jdt.core.search.IJavaSearchScope; +import org.eclipse.jdt.core.search.SearchEngine; +import org.eclipse.jdt.core.search.TypeNameMatch; +import org.eclipse.jdt.internal.corext.codemanipulation.OrganizeImportsOperation; +import org.eclipse.jdt.internal.corext.util.TypeNameMatchCollector; + +/** + * Use a SearchEngine to look for the Java types + * This will not find inner types, however + * + * @author Andrew Eisenberg + * @author Nieraj Singh + */ +public class TypeSearch { + + public TypeSearch() { + // + } + + /** + * Use a SearchEngine to look for the types + * This will not find inner types, however + * + * @see OrganizeImportsOperation.TypeReferenceProcessor#process(org.eclipse.core.runtime.IProgressMonitor) + * @param missingType + * @throws JavaModelException + */ + public void searchForTypes(GroovyCompilationUnit unit, Map missingTypes) + throws JavaModelException { + char[][] allTypes = new char[missingTypes.size()][]; + int i = 0; + for (String simpleName : missingTypes.keySet()) { + allTypes[i++] = simpleName.toCharArray(); + } + final List typesFound = new ArrayList(); + TypeNameMatchCollector collector = new TypeNameMatchCollector(typesFound); + IJavaSearchScope scope = SearchEngine.createJavaSearchScope(new IJavaElement[] { unit.getJavaProject() }); + new SearchEngine().searchAllTypeNames(null, allTypes, scope, collector, IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, + null); + + for (TypeNameMatch match : typesFound) { + UnresolvedTypeData data = missingTypes.get(match.getSimpleTypeName()); + if (data == null) { + GroovyCore.logException("GRECLIPSE-735: Match not found in missing types: " + match.getFullyQualifiedName(), + new Exception()); + continue; + } + if (isOfKind(match, data.isAnnotation)) { + data.addInfo(match); + } + } + } + + /** + * If looking for an annotation, then filter out non-annoations, + * otherwise everything is acceptable. + * + * @param match + * @param isAnnotation + * @return + */ + protected boolean isOfKind(TypeNameMatch match, boolean isAnnotation) { + return isAnnotation ? Flags.isAnnotation(match.getModifiers()) : true; + } +} Index: src/org/codehaus/groovy/eclipse/refactoring/actions/OrganizeGroovyImports.java =================================================================== --- src/org/codehaus/groovy/eclipse/refactoring/actions/OrganizeGroovyImports.java (revision 21263) +++ src/org/codehaus/groovy/eclipse/refactoring/actions/OrganizeGroovyImports.java (working copy) @@ -1,3 +1,18 @@ +/* + * Copyright 2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.codehaus.groovy.eclipse.refactoring.actions; import greclipse.org.eclipse.jdt.core.dom.rewrite.ImportRewrite; @@ -38,20 +53,14 @@ import org.codehaus.groovy.eclipse.core.GroovyCore; import org.codehaus.jdt.groovy.model.GroovyCompilationUnit; import org.eclipse.core.runtime.CoreException; -import org.eclipse.jdt.core.Flags; -import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.ISourceRange; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.SourceRange; -import org.eclipse.jdt.core.search.IJavaSearchConstants; -import org.eclipse.jdt.core.search.IJavaSearchScope; -import org.eclipse.jdt.core.search.SearchEngine; import org.eclipse.jdt.core.search.TypeNameMatch; import org.eclipse.jdt.internal.core.search.JavaSearchTypeNameMatch; import org.eclipse.jdt.internal.corext.codemanipulation.OrganizeImportsOperation; import org.eclipse.jdt.internal.corext.codemanipulation.OrganizeImportsOperation.IChooseImportQuery; -import org.eclipse.jdt.internal.corext.util.TypeNameMatchCollector; import org.eclipse.text.edits.MalformedTreeException; import org.eclipse.text.edits.MultiTextEdit; import org.eclipse.text.edits.TextEdit; @@ -66,7 +75,7 @@ * From {@link OrganizeImportsOperation.TypeReferenceProcessor.UnresolvedTypeData} * */ - static class UnresolvedTypeData { + public static class UnresolvedTypeData { final String ref; final boolean isAnnotation; final List foundInfos; @@ -87,6 +96,10 @@ } foundInfos.add(info); } + + public List getFoundInfos() { + return foundInfos; + } } private class FindUnresolvedReferencesVisitor extends ClassCodeVisitorSupport { @@ -303,8 +316,8 @@ } private final GroovyCompilationUnit unit; - private HashMap missingTypes; - private HashMap importsSlatedForRemoval; + private Map missingTypes; + private Map importsSlatedForRemoval; private IChooseImportQuery query; @@ -446,7 +459,7 @@ private IType[] resolveMissingTypes() throws JavaModelException { // fill in all the potential matches - searchForTypes(); + new TypeSearch().searchForTypes(unit, missingTypes); List missingTypesNoChoiceRequired = new ArrayList(); List missingTypesChoiceRequired = new ArrayList(); List ranges = new ArrayList(); @@ -487,48 +500,4 @@ } } - - /** - * Use a SearchEngine to look for the types - * This will not find inner types, however - * @see OrganizeImportsOperation.TypeReferenceProcessor#process(org.eclipse.core.runtime.IProgressMonitor) - * @param missingType - * @throws JavaModelException - */ - private void searchForTypes() throws JavaModelException { - char[][] allTypes = new char[missingTypes.size()][]; - int i = 0; - for (String simpleName : missingTypes.keySet()) { - allTypes[i++] = simpleName.toCharArray(); - } - final List typesFound= new ArrayList(); - TypeNameMatchCollector collector= new TypeNameMatchCollector(typesFound); - IJavaSearchScope scope= SearchEngine.createJavaSearchScope(new IJavaElement[] { unit.getJavaProject() }); - new SearchEngine().searchAllTypeNames(null, allTypes, scope, collector, IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, null); - - for (TypeNameMatch match : typesFound) { - UnresolvedTypeData data = missingTypes.get(match.getSimpleTypeName()); - if (data == null) { - GroovyCore.logException("GRECLIPSE-735: Match not found in missing types: " + match.getFullyQualifiedName(), - new Exception()); - continue; - } - if (isOfKind(match, data.isAnnotation)) { - data.addInfo(match); - } - } - } - - /** - * If looking for an annotation, then filter out non-annoations, - * otherwise everything is acceptable. - * @param match - * @param isAnnotation - * @return - */ - private boolean isOfKind(TypeNameMatch match, boolean isAnnotation) { - return isAnnotation ? Flags.isAnnotation(match.getModifiers()) : true; - } - - } #P org.codehaus.groovy.eclipse.codeassist.completion Index: src/org/codehaus/groovy/eclipse/codeassist/processors/GroovyImportRewriteFactory.java =================================================================== --- src/org/codehaus/groovy/eclipse/codeassist/processors/GroovyImportRewriteFactory.java (revision 0) +++ src/org/codehaus/groovy/eclipse/codeassist/processors/GroovyImportRewriteFactory.java (revision 0) @@ -0,0 +1,190 @@ +/* + * Copyright 2003-2010 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.codehaus.groovy.eclipse.codeassist.processors; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.codehaus.groovy.ast.ModuleNode; +import org.codehaus.groovy.eclipse.codeassist.processors.GroovyProposalTypeSearchRequestor.CharArraySequence; +import org.codehaus.groovy.eclipse.core.GroovyCore; +import org.codehaus.jdt.groovy.model.GroovyCompilationUnit; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.core.dom.AST; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.ASTParser; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; +import org.eclipse.jdt.ui.CodeStyleConfiguration; + +/** + * + * @author Andrew Eisenberg + * @author Nieraj Singh + * @created Oct 27, 2009 + */ +public class GroovyImportRewriteFactory { + + private static final Pattern IMPORTS_PATTERN = Pattern + .compile("(\\A|[\\n\\r])import\\s"); + + private static final Pattern PACKAGE_PATTERN = Pattern + .compile("(\\A|[\\n\\r])package\\s"); + + private static final Pattern EOL_PATTERN = Pattern.compile("($|[\\n\\r])"); + + private ImportRewrite rewrite; + // set to true if there is a problem creating the rewrite + private boolean cantCreateRewrite = false; + + /** + * This should never be null + */ + private GroovyCompilationUnit unit; + + /** + * This may be null + */ + private ModuleNode module; + + /** + * If this constructor is used, the a check may be performed on the module + * for unrecoverable errors before generating an import rewrite. Compilation + * unit should never be null, although the module can be null + */ + public GroovyImportRewriteFactory(GroovyCompilationUnit unit, ModuleNode module) { + this.unit = unit; + this.module = module; + } + + /** + * Module is null in this case. Only a compilation unit is passed. + */ + public GroovyImportRewriteFactory(GroovyCompilationUnit unit) { + this.unit = unit; + } + + /** + * Returns an import rewrite for the module node only if + * ModuleNode.encounteredUnrecoverableError() + * + * Tries to find the start and end locations of the import statements. Makes + * a best guess using regular expression. This method ensures that even if + * the ComplationUnit is unparseable, the imports are still placed in the + * correct location. + * + * @return an {@link ImportRewrite} for the ModuleNode if it encountered an + * unrecoverable error, or null if no problems. + */ + public ImportRewrite getImportRewrite(IProgressMonitor monitor) { + + // When invoked in organise imports, it is important to check for + // unrecoverable errors. The reason is that if there are no + // unrecoverable errors, then a standard import rewrite should be used + // instead of the one generated by this method. However in the + // case of a individual add import quick fix, which also contains no + // unresolved errors if only the unresolved type is the error, + // this special import rewrite should be used, therefore for add import + // quick fix, it is not necessary to pass a module to check for + // unrecoverable errors. + if (module != null && !module.encounteredUnrecoverableError()) { + return null; + } + + if (rewrite == null && !cantCreateRewrite) { + + // find a reasonable substring that contains + // what looks to be the import dependencies + CharArraySequence contents = new CharArraySequence( + unit.getContents()); + CharArraySequence imports = findImportsRegion(contents); + + // Now send this to a parser + // need to be very careful here that if we can't parse, then + // don't send to rewriter + ASTParser parser = ASTParser.newParser(AST.JLS3); + parser.setSource(unit.cloneCachingContents(CharOperation.concat( + imports.chars(), "\nclass X { }".toCharArray()))); + parser.setKind(ASTParser.K_COMPILATION_UNIT); + ASTNode result = null; + try { + result = parser.createAST(monitor); + } catch (IllegalStateException e) { + GroovyCore.logException("Can't create ImportRewrite for:\n" + + imports, e); + } + if (result instanceof CompilationUnit) { + rewrite = CodeStyleConfiguration.createImportRewrite( + (CompilationUnit) result, true); + + } else { + // something wierd happened. + // ensure we don't try again + cantCreateRewrite = true; + } + } + + return rewrite; + } + + /** + * Convenience methpd for + * {@link GroovyProposalTypeSearchRequestor#findImportsRegion(CharArraySequence)} + */ + public static CharArraySequence findImportsRegion(String contents) { + return findImportsRegion(new CharArraySequence(contents)); + } + + /** + * Finds a region of text that kind of looks like where the imports should + * be placed. Uses regular expressions. + * + * @param contents + * the contents of a compilation unit + * @return a presumed region + */ + public static CharArraySequence findImportsRegion(CharArraySequence contents) { + // heuristics: + // look for last index of ^import + // if that returns -1, then look for ^package + Matcher matcher = IMPORTS_PATTERN.matcher(contents); + int importsEnd = 0; + while (matcher.find(importsEnd)) { + importsEnd = matcher.end(); + } + + if (importsEnd == 0) { + // no imports found, look for package declaration + matcher = PACKAGE_PATTERN.matcher(contents); + if (matcher.find()) { + importsEnd = matcher.end(); + } + + } + + if (importsEnd > 0) { + // look for end of line + matcher = EOL_PATTERN.matcher(contents); + if (matcher.find(importsEnd)) { + importsEnd = matcher.end(); + } + } + + return contents.subSequence(0, importsEnd); + } + +} Index: src/org/codehaus/groovy/eclipse/codeassist/processors/GroovyProposalTypeSearchRequestor.java =================================================================== --- src/org/codehaus/groovy/eclipse/codeassist/processors/GroovyProposalTypeSearchRequestor.java (revision 21246) +++ src/org/codehaus/groovy/eclipse/codeassist/processors/GroovyProposalTypeSearchRequestor.java (working copy) @@ -1,5 +1,5 @@ /* - * Copyright 2003-2009 the original author or authors. + * Copyright 2003-2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,8 +21,6 @@ import java.util.LinkedList; import java.util.List; import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import org.codehaus.groovy.ast.ImportNode; import org.codehaus.groovy.ast.ModuleNode; @@ -33,7 +31,6 @@ import org.codehaus.groovy.eclipse.codeassist.proposals.Relevance; import org.codehaus.groovy.eclipse.codeassist.requestor.ContentAssistContext; import org.codehaus.groovy.eclipse.codeassist.requestor.ContentAssistLocation; -import org.codehaus.groovy.eclipse.core.GroovyCore; import org.codehaus.groovy.eclipse.core.preferences.PreferenceConstants; import org.codehaus.groovy.eclipse.core.util.ReflectionUtils; import org.codehaus.jdt.groovy.model.GroovyCompilationUnit; @@ -46,10 +43,6 @@ import org.eclipse.jdt.core.Signature; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.compiler.IProblem; -import org.eclipse.jdt.core.dom.AST; -import org.eclipse.jdt.core.dom.ASTNode; -import org.eclipse.jdt.core.dom.ASTParser; -import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; import org.eclipse.jdt.internal.codeassist.CompletionEngine; import org.eclipse.jdt.internal.codeassist.ISearchRequestor; @@ -78,7 +71,7 @@ * Method parts are omitted or commented out when they are not relevant for * or not supported by groovy completion. */ -class GroovyProposalTypeSearchRequestor implements ISearchRequestor, +public class GroovyProposalTypeSearchRequestor implements ISearchRequestor, RelevanceConstants { private static final char[][] DEFAULT_GROOVY_IMPORTS = { "java.math.BigDecimal".toCharArray(), "java.math.BigInteger".toCharArray() }; @@ -218,11 +211,7 @@ private final String completionExpression; - // will be non-null if there is an unrevoverable error in the module node - private ImportRewrite rewrite; - - // set to true if there is a problem creating the rewrite - private boolean cantCreateRewrite = false; + private GroovyImportRewriteFactory groovyRewriter; private boolean shouldAcceptConstructors; @@ -247,6 +236,7 @@ this.isImport = context.location == ContentAssistLocation.IMPORT; this.shouldAcceptConstructors = context.location == ContentAssistLocation.CONSTRUCTOR; this.completionExpression = context.completionExpression; + groovyRewriter = new GroovyImportRewriteFactory(this.unit, this.module); } public void acceptConstructor(int modifiers, char[] simpleTypeName, @@ -578,7 +568,7 @@ LazyGenericTypeProposal javaCompletionProposal = new LazyGenericTypeProposal(proposal, javaContext); javaCompletionProposal.setRelevance(proposal.getRelevance()); - ImportRewrite r = forceImportRewrite(); + ImportRewrite r = groovyRewriter.getImportRewrite(monitor); if (r != null) { ReflectionUtils.setPrivateField( LazyJavaTypeCompletionProposal.class, "fImportRewrite", @@ -860,7 +850,7 @@ LazyJavaCompletionProposal javaCompletionProposal = new GroovyJavaMethodCompletionProposal( proposal, javaContext, getProposalOptions()); javaCompletionProposal.setRelevance(proposal.getRelevance()); - ImportRewrite r = forceImportRewrite(); + ImportRewrite r = groovyRewriter.getImportRewrite(monitor); if (r != null) { ReflectionUtils.setPrivateField( LazyJavaTypeCompletionProposal.class, "fImportRewrite", @@ -923,114 +913,8 @@ return proposal; } - /** - * Returns an import rewrite for the module node only if - * ModuleNode.encounteredUnrecoverableError() - * - * Tries to find the start and end locations of the import statements. Makes - * a best guess using regular expression. This method ensures that even if - * the ComplationUnit is unparseable, the imports are still placed in the - * correct location. - * - * @return an {@link ImportRewrite} for the ModuleNode if it encountered an - * unrecoverable error, or null if no problems. - */ - private ImportRewrite forceImportRewrite() { - - if (!module.encounteredUnrecoverableError()) { - return null; - } - - if (rewrite == null && !cantCreateRewrite) { - - // find a reasonable substring that contains - // what looks to be the import dependencies - CharArraySequence contents = new CharArraySequence( - unit.getContents()); - CharArraySequence imports = findImportsRegion(contents); - - // Now send this to a parser - // need to be very careful here that if we can't parse, then - // don't send to rewriter - ASTParser parser = ASTParser.newParser(AST.JLS3); - parser.setSource(unit.cloneCachingContents(CharOperation.concat( - imports.chars(), "\nclass X { }".toCharArray()))); - parser.setKind(ASTParser.K_COMPILATION_UNIT); - ASTNode result = null; - try { - result = parser.createAST(monitor); - } catch (IllegalStateException e) { - GroovyCore.logException("Can't create ImportRewrite for:\n" - + imports, e); - } - if (result instanceof CompilationUnit) { - rewrite = ImportRewrite.create((CompilationUnit) result, true); - } else { - // something wierd happened. - // ensure we don't try again - cantCreateRewrite = true; - } - } - - return rewrite; - } - - /** - * Convenience methpd for - * {@link GroovyProposalTypeSearchRequestor#findImportsRegion(CharArraySequence)} - */ - static CharArraySequence findImportsRegion(String contents) { - return findImportsRegion(new CharArraySequence(contents)); - } - - private static final Pattern IMPORTS_PATTERN = Pattern - .compile("(\\A|[\\n\\r])import\\s"); - - private static final Pattern PACKAGE_PATTERN = Pattern - .compile("(\\A|[\\n\\r])package\\s"); - - private static final Pattern EOL_PATTERN = Pattern.compile("($|[\\n\\r])"); - private ProposalOptions groovyProposalPrefs; - /** - * Finds a region of text that kind of looks like where the imports should - * be placed. Uses regular expressions. - * - * @param contents - * the contents of a compilation unit - * @return a presumed region - */ - private static CharArraySequence findImportsRegion( - CharArraySequence contents) { - // heuristics: - // look for last index of ^import - // if that returns -1, then look for ^package - Matcher matcher = IMPORTS_PATTERN.matcher(contents); - int importsEnd = 0; - while (matcher.find(importsEnd)) { - importsEnd = matcher.end(); - } - - if (importsEnd == 0) { - // no imports found, look for package declaration - matcher = PACKAGE_PATTERN.matcher(contents); - if (matcher.find()) { - importsEnd = matcher.end(); - } - - } - - if (importsEnd > 0) { - // look for end of line - matcher = EOL_PATTERN.matcher(contents); - if (matcher.find(importsEnd)) { - importsEnd = matcher.end(); - } - } - - return contents.subSequence(0, importsEnd); - } /** * Made public for testing