/******************************************************************************* * Copyright © 2008, 2013 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.edt.ide.ui.internal.actions; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.ResourceBundle; import java.util.Set; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceRuleFactory; import org.eclipse.core.resources.IWorkspaceRunnable; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubProgressMonitor; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.jobs.MultiRule; import org.eclipse.edt.compiler.core.ast.Name; import org.eclipse.edt.ide.core.internal.search.PartInfo; import org.eclipse.edt.ide.core.model.EGLCore; import org.eclipse.edt.ide.core.model.EGLModelException; import org.eclipse.edt.ide.core.model.IEGLElement; import org.eclipse.edt.ide.core.model.IEGLFile; import org.eclipse.edt.ide.core.model.IEGLProject; import org.eclipse.edt.ide.core.model.IPackageFragment; import org.eclipse.edt.ide.core.model.IPackageFragmentRoot; import org.eclipse.edt.ide.ui.EDTUIPlugin; import org.eclipse.edt.ide.ui.internal.UINlsStrings; import org.eclipse.edt.ide.ui.internal.codemanipulation.OrganizeImportsOperation; import org.eclipse.edt.ide.ui.internal.codemanipulation.OrganizeImportsOperation.IChooseImportQuery; import org.eclipse.edt.ide.ui.internal.codemanipulation.OrganizeImportsOperation.SyntaxErrorHelper; import org.eclipse.edt.ide.ui.internal.dialogs.MultiElementListSelectionDialog; import org.eclipse.edt.ide.ui.internal.dialogs.ProblemDialog; import org.eclipse.edt.ide.ui.internal.editor.EGLEditor; import org.eclipse.edt.ide.ui.internal.util.EditorUtility; import org.eclipse.edt.ide.ui.internal.util.PartInfoLabelProvider; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IStatusLineManager; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.operation.IRunnableContext; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IEditingSupport; import org.eclipse.jface.text.IEditingSupportRegistry; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.window.Window; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IEditorActionBarContributor; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.IObjectActionDelegate; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchSite; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.part.EditorActionBarContributor; import org.eclipse.ui.progress.IProgressConstants; import org.eclipse.ui.progress.IProgressService; import org.eclipse.ui.texteditor.ResourceAction; import com.ibm.icu.text.MessageFormat; public class OrganizeImportsAction extends ResourceAction implements IObjectActionDelegate { /** <code>true</code> if the query dialog is showing. */ private boolean fIsQueryShowing= false; protected IStructuredSelection selection = null; protected IWorkbenchSite fSite; protected EGLEditor fEditor; //key is the IEGLFile, value is whether or not this file needs to be saved //if file is opened in the EGL editor, then it does not need to be saved //otherwise the file needs to be saved private Hashtable fileNeedsSave = new Hashtable(); public OrganizeImportsAction() { this(UINlsStrings.getResourceBundleForConstructedKeys(), ""); //$NON-NLS-1$ } public OrganizeImportsAction(ResourceBundle bundle, String prefix, int style) { super(bundle, prefix, style); } public OrganizeImportsAction(ResourceBundle bundle, String prefix) { super(bundle, prefix); } public void setActivePart(IAction action, IWorkbenchPart targetPart) { fSite = targetPart.getSite(); } //init the selection and site, called from the editor menu public void init(EGLEditor editor) { fEditor = editor; if(selection == null && editor != null) { IEditorInput editorInput = editor.getEditorInput(); // Could be a VirtualEditorInput if coming from PageDesigners QEV if (editorInput instanceof IFileEditorInput) { IResource resource = ((IFileEditorInput) editorInput).getFile(); IEGLElement element = EGLCore.create(resource); init(element, editor.getSite()); } } } //init the selection and site, called from Part Reference, part list public void init(IResource resource, IWorkbenchSite site) { IEGLElement element = EGLCore.create(resource); init(element, site); } protected void init(IEGLElement eglfile, IWorkbenchSite site) { fSite = site; selection = new StructuredSelection(eglfile); } public void run(IAction action) { run(); } public void run() { // RATLC01446894 - Reset selection to match fEditor.editorInput. // Could be different if >1 file is open from Search results, in which case // organize imports was performed on the wrong file. if(fEditor != null) { IEditorInput editorInput = fEditor.getEditorInput(); // Could be a VirtualEditorInput if coming from PageDesigners QEV if (editorInput instanceof IFileEditorInput) { IResource resource = ((IFileEditorInput) editorInput).getFile(); IEGLElement element = EGLCore.create(resource); init(element, fEditor.getSite()); } } if(selection != null) { List eglFiles = getEGLFiles(selection); int selCnt = eglFiles.size(); if(selCnt == 0) return; if(selCnt == 1) { IEGLFile eglFile = (IEGLFile)(eglFiles.get(0)); runOnSingle(eglFile); } else runOnMultiple(eglFiles); } super.run(); } protected IEditingSupport createViewerHelper() { return new IEditingSupport() { public boolean isOriginator(DocumentEvent event, IRegion subjectRegion) { return true; // assume true, since we only register while we are active } public boolean ownsFocusShell() { return fIsQueryShowing; } }; } protected void registerHelper(IEditingSupport helper) { if (fEditor == null) return; ISourceViewer viewer= fEditor.getViewer(); if (viewer instanceof IEditingSupportRegistry) { IEditingSupportRegistry registry= (IEditingSupportRegistry) viewer; registry.register(helper); } } protected void deregisterHelper(IEditingSupport helper) { if (fEditor == null) return; ISourceViewer viewer= fEditor.getViewer(); if (viewer instanceof IEditingSupportRegistry) { IEditingSupportRegistry registry= (IEditingSupportRegistry) viewer; registry.unregister(helper); } } protected void runOnSingle(IEGLFile eglFile) { IEditingSupport helper= createViewerHelper(); //EGLOrganizeImportsOperation op = new EGLOrganizeImportsOperation(eglFile, needsave, createChooseImportQuery()); OrganizeImportsOperation op = createOperation(eglFile); IProgressService progressService= PlatformUI.getWorkbench().getProgressService(); IRunnableContext context= fSite.getWorkbenchWindow(); if (context == null) { context= progressService; } try { registerHelper(helper); progressService.runInUI(context, new WorkbenchRunnableAdapter(op, op.getScheduleRule()), op.getScheduleRule()); postRun(op); }catch (InvocationTargetException e) { e.printStackTrace(); } catch (InterruptedException e) { } finally { deregisterHelper(helper); } } protected OrganizeImportsOperation createOperation(IEGLFile eglFile) { boolean needsave = ((Boolean)(fileNeedsSave.get(eglFile))).booleanValue(); return new OrganizeImportsOperation(eglFile, needsave, createChooseImportQuery()); } protected void postRun(OrganizeImportsOperation op) { SyntaxErrorHelper syntaxErr = op.getSyntaxError(); if(syntaxErr != null) { MessageDialog.openInformation(fSite.getShell(), UINlsStrings.OrganizeImportsAction_status_title, syntaxErr.fErrMsg); //highlight the syntax error location if(fEditor != null && syntaxErr.fSynErr.startOffset > 0) fEditor.selectAndReveal(syntaxErr.fSynErr.startOffset, syntaxErr.fSynErr.endOffset-syntaxErr.fSynErr.startOffset+1); } else { if(fEditor != null) setStatusBarMessage(getOrganizeSummary(op)); } } private String getOrganizeSummary(OrganizeImportsOperation op) { int nImportsAdded= op.getNumberOfImportsAdded(); int nImportsRemoved = op.getNumberOfImportsRemoved(); String[] args = {String.valueOf(nImportsAdded), String.valueOf(nImportsRemoved)}; return MessageFormat.format(UINlsStrings.OrganizeImportsAction_summary, args); } protected IChooseImportQuery createChooseImportQuery() { return new IChooseImportQuery() { public PartInfo[] chooseImports(Map /*<Name>, <List of <PartInfo> >*/mapOpenChoices) { int cnt = mapOpenChoices.size(); ArrayList openChoices = new ArrayList(cnt); ArrayList regionList = new ArrayList(cnt); Set keys = mapOpenChoices.keySet(); Iterator it = keys.iterator(); while(it.hasNext()) { Name unrevoledTypeName = (Name)(it.next()); List partInfos = (List)(mapOpenChoices.get(unrevoledTypeName)); Region region = new Region(unrevoledTypeName.getOffset(), unrevoledTypeName.getLength()); regionList.add(region); //conver partInfos to array int choicesPerPartCnt = partInfos.size(); PartInfo[] openChoice = (PartInfo[]) partInfos.toArray(new PartInfo[choicesPerPartCnt]); openChoices.add(openChoice); } PartInfo[][] chooseImportChoices = (PartInfo[][])openChoices.toArray(new PartInfo[openChoices.size()][]); IRegion[] regions = (IRegion[])regionList.toArray(new IRegion[regionList.size()]); return doChooseImports(chooseImportChoices, regions); } }; } protected PartInfo[] doChooseImports(PartInfo[][] openChoices, final IRegion[] ranges) { // remember selection ISelection sel= fEditor != null ? fEditor.getSelectionProvider().getSelection() : null; PartInfo[] result= null; ILabelProvider labelProvider= new PartInfoLabelProvider(PartInfoLabelProvider.SHOW_FULLYQUALIFIED); MultiElementListSelectionDialog dialog= new MultiElementListSelectionDialog(fSite.getShell(), labelProvider) { protected void handleSelectionChanged() { super.handleSelectionChanged(); // show choices in editor doListSelectionChanged(getCurrentPage(), ranges); } }; fIsQueryShowing= true; dialog.setTitle(UINlsStrings.OrganizeImportsAction_selectiondialog_title); dialog.setMessage(UINlsStrings.OrganizeImportsAction_selectiondialog_message); dialog.setElements(openChoices); if (dialog.open() == Window.OK) { Object[] res= dialog.getResult(); result= new PartInfo[res.length]; for (int i= 0; i < res.length; i++) { Object[] array= (Object[]) res[i]; if (array.length > 0) result[i]= (PartInfo) array[0]; } } // restore selection if (sel instanceof ITextSelection) { ITextSelection textSelection= (ITextSelection) sel; fEditor.selectAndReveal(textSelection.getOffset(), textSelection.getLength()); } fIsQueryShowing= false; return result; } protected void doListSelectionChanged(int page, IRegion[] ranges) { if (fEditor != null && ranges != null && page >= 0 && page < ranges.length) { IRegion range= ranges[page]; fEditor.selectAndReveal(range.getOffset(), range.getLength()); } } private void runOnMultiple(final List eglFiles) { String message= UINlsStrings.OrganizeImportsAction_status_description; //$NON-NLS-1$ final MultiStatus status= new MultiStatus(EDTUIPlugin.PLUGIN_ID, IStatus.OK, message, null); IProgressService progressService = PlatformUI.getWorkbench().getProgressService(); try { progressService.run(true, true, new WorkbenchRunnableAdapter(new IWorkspaceRunnable() { public void run(IProgressMonitor monitor){ doRunOnMultiple(eglFiles, status, monitor); } })); if(!status.isOK()) { String title = UINlsStrings.OrganizeImportsAction_status_title; ProblemDialog.open(fSite.getShell(), title, null, status); } } catch (InvocationTargetException e) { e.printStackTrace(); } catch (InterruptedException e) { //cancel by user } //workspace lock /* ISchedulingRule rule = modifyRule(eglFiles); String Jobtitle = EGLUINlsStrings.OrganizeImportsAction_op_description; WorkspaceJob job = new WorkspaceJob(Jobtitle) { (non-Javadoc) * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException { doRunOnMultiple(eglFiles, status, monitor); if(isModal(this)) { //the progress dialog is still open so just open the message showResults(); } else { //setProperty(IProgressConstants.KEEP_PROPERTY, Boolean.TRUE); //setProperty(IProgressConstants.ACTION_PROPERTY, getOrganizeUsedFormsCompeletedAction()); } return status; } }; job.setRule(rule); job.setUser(true); job.schedule(); */ } static final class OrganizeImportError extends RuntimeException { private static final long serialVersionUID= 1L; } private void doRunOnMultiple(List eglfiles, MultiStatus status, IProgressMonitor monitor) throws OperationCanceledException { if(monitor == null) monitor = new NullProgressMonitor(); monitor.setTaskName(UINlsStrings.OrganizeImportsAction_op_description); int size = eglfiles.size(); monitor.beginTask("", size); //$NON-NLS-1$ IChooseImportQuery query= new IChooseImportQuery() { public PartInfo[] chooseImports(Map mapOpenChoices) { throw new OrganizeImportError(); } }; try{ for(int i=0; i<size; i++) { IEGLFile eglfile = (IEGLFile)(eglfiles.get(i)); String fileLocation = eglfile.getPath().makeRelative().toString(); if(testOnEGLPath(eglfile, fileLocation, status)) { monitor.subTask(fileLocation); boolean needsave = ((Boolean)(fileNeedsSave.get(eglfile))).booleanValue(); OrganizeImportsOperation op = new OrganizeImportsOperation(eglfile, needsave, query); /* try { op.run(new SubProgressMonitor(monitor, 1)); } catch (CoreException e) { e.printStackTrace(); String message = EGLUINlsStrings.OrganizeAction_error_unexpected; status.add(new Status(IStatus.ERROR, EGLUIPlugin.PLUGIN_ID, IStatus.ERROR, message, e)); } */ runInSync(op, status, fileLocation, monitor); SyntaxErrorHelper syntaxErr = op.getSyntaxError(); if(syntaxErr != null) { String msg = MessageFormat.format(UINlsStrings.OrganizeAction_error_syntax, new String[]{fileLocation}); status.add(new Status(IStatus.INFO, EDTUIPlugin.PLUGIN_ID, IStatus.ERROR, msg, null)); } if(monitor.isCanceled()) throw new OperationCanceledException(); } } } finally { monitor.done(); } } private boolean testOnEGLPath(IEGLElement eglelem, String fileLocation, MultiStatus status){ IEGLProject eglProj = eglelem.getEGLProject(); if(!eglProj.isOnEGLPath(eglelem)) { String message = MessageFormat.format(UINlsStrings.OrganizeImportsAction_multi_error_notoncp, new String[]{fileLocation}); status.add(new Status(IStatus.INFO, EDTUIPlugin.PLUGIN_ID, IStatus.ERROR, message, null)); return false; } return true; } private void runInSync(final OrganizeImportsOperation op, final MultiStatus status, final String fileLocation, final IProgressMonitor monitor) { Runnable runnable = new Runnable() { public void run(){ try { op.run(new SubProgressMonitor(monitor, 1)); } catch (CoreException e) { e.printStackTrace(); String message = UINlsStrings.OrganizeAction_error_unexpected; status.add(new Status(IStatus.ERROR, EDTUIPlugin.PLUGIN_ID, IStatus.ERROR, message, e)); } catch (OrganizeImportError e){ String[] args = {fileLocation}; String message = MessageFormat.format(UINlsStrings.OrganizeImportsAction_multi_error_unresolvable, args); status.add(new Status(IStatus.INFO, EDTUIPlugin.PLUGIN_ID, IStatus.ERROR, message, e)); } catch (OperationCanceledException e) { //Canceled monitor.setCanceled(true); } } }; fSite.getShell().getDisplay().syncExec(runnable); } private boolean isModal(Job job) { Boolean isModal = (Boolean)job.getProperty(IProgressConstants.PROPERTY_IN_DIALOG); if(isModal == null ) return false; return isModal.booleanValue(); } private void showResults(){ Display.getDefault().syncExec(new Runnable() { public void run(){ getOrganizeImportsCompeletedAction().run(); } }); } private Action getOrganizeImportsCompeletedAction() { return new Action(UINlsStrings.ViewOrgImportStatus){ public void run(){ String title = UINlsStrings.OrganizeImportsAction_status_title; String message = UINlsStrings.OrganizeImportsAction_status_description; MessageDialog.openInformation(fSite.getShell(), title, message); } }; } private ISchedulingRule modifyRule(List eglFiles) { ISchedulingRule combinedRule = null; IResourceRuleFactory ruleFactory = ResourcesPlugin.getWorkspace().getRuleFactory(); for(Iterator it = eglFiles.iterator(); it.hasNext();) { IEGLFile eglfile = (IEGLFile)(it.next()); try { ISchedulingRule rule = ruleFactory.modifyRule(eglfile.getUnderlyingResource()); combinedRule = MultiRule.combine(rule, combinedRule); } catch (EGLModelException e) { e.printStackTrace(); } } return combinedRule; } private boolean needSave(IEGLFile eglfile) { boolean needSave = false; IEditorPart editor = EditorUtility.isOpenInEditor(eglfile); if(editor == null) { needSave = true; } else if(editor instanceof EGLEditor) fEditor = (EGLEditor)editor; return needSave; } private void collectEGLFiles(IPackageFragment pkg, List result) throws EGLModelException { IEGLFile[] eglfiles = pkg.getEGLFiles(); for(int i=0; i<eglfiles.length; i++) { result.add(eglfiles[i]); fileNeedsSave.put(eglfiles[i], new Boolean(needSave(eglfiles[i]))); } } private void collectEGLFiles(IPackageFragmentRoot pkgRoot, List result) throws EGLModelException { if(pkgRoot.getKind() == IPackageFragmentRoot.K_SOURCE) { IEGLElement[] children = pkgRoot.getChildren(); for(int i=0; i<children.length; i++) { collectEGLFiles((IPackageFragment)children[i], result); } } } private void getEGLElements(IEGLElement eglElem, List result) throws EGLModelException { if(eglElem != null) { switch(eglElem.getElementType()) { case IEGLElement.EGL_PROJECT: IPackageFragmentRoot[] pkgRoots = ((IEGLProject)eglElem).getPackageFragmentRoots(); for(int i=0; i<pkgRoots.length; i++) { collectEGLFiles(pkgRoots[i], result); } break; case IEGLElement.PACKAGE_FRAGMENT_ROOT: collectEGLFiles((IPackageFragmentRoot)eglElem, result); break; case IEGLElement.PACKAGE_FRAGMENT: collectEGLFiles((IPackageFragment)eglElem, result); break; case IEGLElement.EGL_FILE: result.add(eglElem); fileNeedsSave.put(eglElem, new Boolean(needSave((IEGLFile)eglElem))); break; } } } private List getEGLFiles(IStructuredSelection selection) { List result = new ArrayList(); if(selection != null) { Iterator it = selection.iterator(); while(it.hasNext()) { try { Object element = it.next(); if(element instanceof IEGLElement) { IEGLElement eglElem = (IEGLElement)element; getEGLElements(eglElem, result); } else if(element instanceof IProject) { IEGLProject eglproj = EGLCore.create((IProject)element); getEGLElements(eglproj, result); } else if(element instanceof IResource) { IEGLElement eglResourceElem = EGLCore.create((IResource)element); getEGLElements(eglResourceElem, result); } } catch(EGLModelException e) { EDTUIPlugin.log(e); } } } return result; } public void selectionChanged(IAction action, ISelection selection) { // enable the action action.setEnabled(true); // make sure we have a structured selection if (selection instanceof IStructuredSelection) { this.selection = (IStructuredSelection) selection; if(this.selection.size() == 1) { Object selobj = this.selection.getFirstElement(); if(selobj instanceof IEGLFile) action.setEnabled(true); } } } protected void setStatusBarMessage(String message) { IEditorActionBarContributor contributor= fEditor.getEditorSite().getActionBarContributor(); if (contributor instanceof EditorActionBarContributor) { IStatusLineManager manager= ((EditorActionBarContributor) contributor).getActionBars().getStatusLineManager(); manager.setMessage(message); } } }