Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHenrik Rentz-Reichert2019-11-29 11:30:12 -0500
committerHenrik Rentz-Reichert2019-12-02 08:55:36 -0500
commit38e797b73db3e87b6f734fbbfbcee7129c00ed09 (patch)
tree69306fb98a900aed8d7e3f52eef8fa1af8d1d2f4
parent34912487f8dc3014f3f43cfb285dac815adda60d (diff)
downloadorg.eclipse.etrice-38e797b73db3e87b6f734fbbfbcee7129c00ed09.tar.gz
org.eclipse.etrice-38e797b73db3e87b6f734fbbfbcee7129c00ed09.tar.xz
org.eclipse.etrice-38e797b73db3e87b6f734fbbfbcee7129c00ed09.zip
Bug 546282 - [ui] Implement "organize imports"v_3.0.2
Implemented a fairly general mechanism that can be adapted easily for other models, e.g. one using the FSM part only. Conflicts: plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/imports/ImportOrganizer.java Change-Id: If6f6d6039833c2e54a337b60d41be6bb20b0ed0a
-rw-r--r--plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/imports/ImportOrganizer.java57
-rw-r--r--plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/imports/NamespaceSelectionDialog.java365
-rw-r--r--plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/imports/NamespaceSelectionLabelProvider.java33
3 files changed, 450 insertions, 5 deletions
diff --git a/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/imports/ImportOrganizer.java b/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/imports/ImportOrganizer.java
index d93dde004..8e14e8ebb 100644
--- a/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/imports/ImportOrganizer.java
+++ b/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/imports/ImportOrganizer.java
@@ -32,13 +32,18 @@ import org.eclipse.emf.ecore.EReference;
import org.eclipse.etrice.core.common.base.BaseFactory;
import org.eclipse.etrice.core.common.base.Import;
import org.eclipse.etrice.core.common.ui.imports.IOrganizeImportHelper.ImportRegionResult;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.widgets.Display;
import org.eclipse.xtext.formatting.IWhitespaceInformationProvider;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.util.ReplaceRegion;
+import org.eclipse.xtext.xbase.lib.Pair;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.inject.Inject;
@@ -57,7 +62,11 @@ public class ImportOrganizer {
private IWhitespaceInformationProvider whitespaceInformationProvider;
public List<ReplaceRegion> getOrganizedImportChanges(XtextResource resource) {
- Set<QualifiedName> typeUsages = getTypeUsages(resource);
+ return getOrganizedImportChanges(resource, true);
+ }
+
+ public List<ReplaceRegion> getOrganizedImportChanges(XtextResource resource, boolean interactive) {
+ Set<QualifiedName> typeUsages = getTypeUsages(resource, interactive);
typeUsages = simplifyImports(typeUsages);
@@ -68,9 +77,11 @@ public class ImportOrganizer {
return changes;
}
- protected Set<QualifiedName> getTypeUsages(XtextResource resource) {
+ protected Set<QualifiedName> getTypeUsages(XtextResource resource, boolean interactive) {
Set<QualifiedName> result = new HashSet<>();
+ Multimap<Pair<EClass, String>, QualifiedName> ambiguous = HashMultimap.create();
+
Multimap<EClass, EReference> typeReferences = organizeImportHelper.getTypeReferences();
EObject root = resource.getContents().get(0);
TreeIterator<EObject> it = root.eAllContents();
@@ -84,7 +95,14 @@ public class ImportOrganizer {
if (((EObject) refObject).eIsProxy()) {
List<INode> nodes = NodeModelUtils.findNodesForFeature((EObject) object, ref);
if (!nodes.isEmpty()) {
- result.addAll(organizeImportHelper.resolveFullyQualifiedName(nodes.get(0).getText().trim(), ref.getEReferenceType(), resource));
+ String refText = nodes.get(0).getText().trim();
+ List<QualifiedName> resolved = organizeImportHelper.resolveFullyQualifiedName(refText, ref.getEReferenceType(), resource);
+ if (resolved.size()==1) {
+ result.addAll(resolved);
+ }
+ else if (resolved.size()>1) {
+ ambiguous.putAll(new Pair<EClass, String>(object.eClass(), refText), resolved);
+ }
}
}
else {
@@ -108,14 +126,43 @@ public class ImportOrganizer {
}
}
+ if (!ambiguous.isEmpty()) {
+ QualifiedName[][] namespaces = new QualifiedName[ambiguous.asMap().size()][];
+ int i = 0;
+ for (Collection<QualifiedName> lists : ambiguous.asMap().values()) {
+ QualifiedName[] list = new QualifiedName[lists.size()];
+ namespaces[i++] = lists.toArray(list);
+ }
+ result.addAll(doChooseImports(namespaces));
+ }
+
// remove our own namespace
QualifiedName ownNamespace = organizeImportHelper.getFullyQualifiedName(root);
result.removeIf(fqn -> fqn.startsWith(ownNamespace));
return result;
}
+
+ protected Set<QualifiedName> doChooseImports(QualifiedName[][] openChoices) {
+ NamespaceSelectionLabelProvider labelProvider = new NamespaceSelectionLabelProvider();
+ NamespaceSelectionDialog dialog = new NamespaceSelectionDialog(Display.getDefault().getActiveShell(), labelProvider);
+ dialog.setTitle("Organize Imports");
+ dialog.setMessage("&Choose type to import:");
+ dialog.setElements(openChoices);
+ Set<QualifiedName> result = new HashSet<>();
+ if (dialog.open() == Window.OK) {
+ Object[] res = dialog.getResult();
+ for (int i = 0; i < res.length; i++) {
+ Object[] array= (Object[]) res[i];
+ if (array.length>0 && array[0] instanceof QualifiedName) {
+ result.add((QualifiedName) array[0]);
+ }
+ }
+ }
+ return result;
+ }
- private Set<QualifiedName> simplifyImports(Set<QualifiedName> typeUsages) {
+ protected Set<QualifiedName> simplifyImports(Set<QualifiedName> typeUsages) {
Map<QualifiedName, List<QualifiedName>> grouped = typeUsages.stream().collect(Collectors.groupingBy(fqn->fqn.skipLast(1)));
Set<QualifiedName> result = new HashSet<>();
grouped.forEach((namespace, names) -> {
@@ -166,7 +213,7 @@ public class ImportOrganizer {
return result;
}
- private String serializeImports(List<Import> allImportDeclarations, String newLine) {
+ protected String serializeImports(List<Import> allImportDeclarations, String newLine) {
if (allImportDeclarations.isEmpty()) {
return "";
}
diff --git a/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/imports/NamespaceSelectionDialog.java b/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/imports/NamespaceSelectionDialog.java
new file mode 100644
index 000000000..ceb76f6a3
--- /dev/null
+++ b/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/imports/NamespaceSelectionDialog.java
@@ -0,0 +1,365 @@
+/*******************************************************************************
+ * Copyright (c) 2019 protos software gmbh (http://www.protos.de).
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * CONTRIBUTORS:
+ * Henrik Rentz-Reichert (initial contribution)
+ *
+ *******************************************************************************/
+
+package org.eclipse.etrice.core.common.ui.imports;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.dialogs.AbstractElementListSelectionDialog;
+import org.eclipse.ui.dialogs.FilteredList;
+
+/**
+ * @author Henrik Rentz-Reichert
+ *
+ * (initial code from org.eclipse.jdt.internal.ui.dialogs.MultiElementListSelectionDialog)
+ */
+public class NamespaceSelectionDialog extends AbstractElementListSelectionDialog {
+
+ private static class Page {
+ private Object[] elements;
+ public String filter;
+ public boolean okState= false;
+
+ public Page(Object[] elements) {
+ this.elements= elements;
+ }
+ }
+
+ private Page[] fPages;
+ private int fCurrentPage;
+ private int fNumberOfPages;
+
+ private Button fFinishButton;
+ private Button fBackButton;
+ private Button fNextButton;
+ private Button fSkipButton;
+
+ private Label fPageInfoLabel;
+ private String fPageInfoMessage= "Page {0} of {1}";
+ private Comparator<?> fComparator;
+
+ /**
+ * Constructs a multi-page list selection dialog.
+ * @param parent The parent shell
+ * @param renderer the label renderer.
+ */
+ public NamespaceSelectionDialog(Shell parent, ILabelProvider renderer) {
+ super(parent, renderer);
+ }
+
+ /**
+ * Sets message shown in the right top corner. Use {0} and {1} as placeholders
+ * for the current and the total number of pages.
+ * @param message the message.
+ */
+ public void setPageInfoMessage(String message) {
+ fPageInfoMessage= message;
+ }
+
+ /**
+ * Sets the elements to be displayed in the dialog.
+ * @param elements an array of pages holding arrays of elements
+ */
+ public void setElements(Object[][] elements) {
+ fNumberOfPages= elements.length;
+ fPages= new Page[fNumberOfPages];
+ for (int i= 0; i != fNumberOfPages; i++)
+ fPages[i]= new Page(elements[i]);
+
+ initializeResult(fNumberOfPages);
+ }
+
+ /*
+ * @see Window#open()
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public int open() {
+ List<Object[]> selection= getInitialElementSelections();
+ if (selection == null || selection.size() != fNumberOfPages) {
+ setInitialSelections(new Object[fNumberOfPages]);
+ selection= getInitialElementSelections();
+ }
+
+ Assert.isTrue(selection.size() == fNumberOfPages);
+
+ return super.open();
+ }
+
+ /*
+ * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(Composite)
+ */
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ Composite contents= (Composite) super.createDialogArea(parent);
+
+ createMessageArea(contents);
+ createFilterText(contents);
+ createFilteredList(contents);
+
+ fCurrentPage= 0;
+ setPageData();
+
+ applyDialogFont(contents);
+ return contents;
+ }
+
+ /*
+ * @see org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(Composite)
+ */
+ @Override
+ protected void createButtonsForButtonBar(Composite parent) {
+ fSkipButton= createButton(parent, IDialogConstants.SKIP_ID, IDialogConstants.SKIP_LABEL, false);
+ fBackButton= createButton(parent, IDialogConstants.BACK_ID, IDialogConstants.BACK_LABEL, false);
+
+ // XXX: Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=279425
+ boolean HAS_BUG_279425= true;
+ fNextButton= createButton(parent, IDialogConstants.NEXT_ID, IDialogConstants.NEXT_LABEL, !HAS_BUG_279425);
+ fFinishButton= createButton(parent, IDialogConstants.OK_ID, IDialogConstants.FINISH_LABEL, HAS_BUG_279425);
+
+ createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
+ }
+
+ /*
+ * XXX: Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=279425
+ * The whole method can be removed once that bug is fixed.
+ * @see org.eclipse.jface.dialogs.Dialog#initializeBounds()
+ * @since 3.5.1
+ */
+ @Override
+ protected void initializeBounds() {
+ super.initializeBounds();
+ fNextButton.getShell().setDefaultButton(fNextButton);
+ }
+
+ /*
+ * @see org.eclipse.ui.dialogs.SelectionDialog#createMessageArea(Composite)
+ */
+ @Override
+ protected Label createMessageArea(Composite parent) {
+ Composite composite= new Composite(parent, SWT.NONE);
+
+ GridLayout layout= new GridLayout();
+ layout.marginHeight= 0;
+ layout.marginWidth= 0;
+ layout.horizontalSpacing= 5;
+ layout.numColumns= 2;
+ composite.setLayout(layout);
+
+ GridData data= new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+ composite.setLayoutData(data);
+
+ Label messageLabel= super.createMessageArea(composite);
+
+ fPageInfoLabel= new Label(composite, SWT.NULL);
+ fPageInfoLabel.setText(getPageInfoMessage());
+
+ data= new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+ data.horizontalAlignment= GridData.END;
+ fPageInfoLabel.setLayoutData(data);
+ applyDialogFont(messageLabel);
+ return messageLabel;
+ }
+
+ /*
+ * @see org.eclipse.ui.dialogs.SelectionStatusDialog#computeResult()
+ */
+ @Override
+ protected void computeResult() {
+ setResult(fCurrentPage, getSelectedElements());
+ }
+
+ /*
+ * @see org.eclipse.jface.dialogs.Dialog#buttonPressed(int)
+ */
+ @Override
+ protected void buttonPressed(int buttonId) {
+ if (buttonId == IDialogConstants.SKIP_ID) {
+ boolean isLastPage= fCurrentPage == fNumberOfPages - 1 ? true : false;
+ turnPage(true, true);
+ if (isLastPage) {
+ buttonPressed(IDialogConstants.OK_ID);
+ }
+ } else if (buttonId == IDialogConstants.BACK_ID) {
+ turnPage(false, false);
+ } else if (buttonId == IDialogConstants.NEXT_ID) {
+ turnPage(true, false);
+ } else {
+ super.buttonPressed(buttonId);
+ }
+ }
+
+ /**
+ * @see AbstractElementListSelectionDialog#handleDefaultSelected()
+ */
+ @Override
+ protected void handleDefaultSelected() {
+ if (validateCurrentSelection()) {
+ if (fCurrentPage == fNumberOfPages - 1) {
+ buttonPressed(IDialogConstants.OK_ID);
+ } else {
+ buttonPressed(IDialogConstants.NEXT_ID);
+ }
+ }
+ }
+
+ /**
+ * @see AbstractElementListSelectionDialog#updateButtonsEnableState(IStatus)
+ */
+ @Override
+ protected void updateButtonsEnableState(IStatus status) {
+ boolean isOK= !status.matches(IStatus.ERROR);
+ fPages[fCurrentPage].okState= isOK;
+
+ boolean isAllOK= isOK;
+ for (int i= 0; i != fNumberOfPages; i++)
+ isAllOK = isAllOK && fPages[i].okState;
+
+ fFinishButton.setEnabled(isAllOK);
+
+ boolean nextButtonEnabled= isOK && (fCurrentPage < fNumberOfPages - 1);
+
+ fNextButton.setEnabled(nextButtonEnabled);
+ fBackButton.setEnabled(fCurrentPage != 0);
+
+ // since there will always be a default selection - see FilteredList.TableUpdateJob.defaultSelect()
+ fSkipButton.setEnabled(true);
+
+ if (nextButtonEnabled) {
+ getShell().setDefaultButton(fNextButton);
+ } else if (isAllOK) {
+ getShell().setDefaultButton(fFinishButton);
+ }
+ }
+
+ private void turnPage(boolean toNextPage, boolean skipSelection) {
+ Page page= fPages[fCurrentPage];
+
+ // store filter
+ String filter= getFilter();
+ if (filter == null)
+ filter= ""; //$NON-NLS-1$
+ page.filter= filter;
+
+ if (skipSelection) {
+ setSelection(null);
+ }
+
+ // store selection
+ Object[] selectedElements= getSelectedElements();
+ @SuppressWarnings("unchecked")
+ List<Object[]> list= getInitialElementSelections();
+ list.set(fCurrentPage, selectedElements);
+
+ // store result
+ setResult(fCurrentPage, selectedElements);
+
+ if (toNextPage) {
+ if (fCurrentPage + 1 >= fNumberOfPages)
+ return;
+
+ fCurrentPage++;
+ } else {
+ if (fCurrentPage - 1 < 0)
+ return;
+
+ fCurrentPage--;
+ }
+
+ if (fPageInfoLabel != null && !fPageInfoLabel.isDisposed())
+ fPageInfoLabel.setText(getPageInfoMessage());
+
+ setPageData();
+
+ validateCurrentSelection();
+ }
+
+ private void setPageData() {
+ Page page= fPages[fCurrentPage];
+
+ // 1. set elements
+ setListElements(page.elements);
+
+ // 2. apply filter
+ String filter= page.filter;
+ if (filter == null)
+ filter= ""; //$NON-NLS-1$
+ setFilter(filter);
+
+ // 3. select elements
+ Object[] selectedElements= (Object[]) getInitialElementSelections().get(fCurrentPage);
+ setSelection(selectedElements);
+ fFilteredList.setFocus();
+ }
+
+ private String getPageInfoMessage() {
+ if (fPageInfoMessage == null)
+ return ""; //$NON-NLS-1$
+
+ Object[] args= new Object[] { Integer.toString(fCurrentPage + 1), Integer.toString(fNumberOfPages) };
+ return MessageFormat.format(fPageInfoMessage, args);
+ }
+
+ private void initializeResult(int length) {
+ List<Object> result= new ArrayList<>(length);
+ for (int i= 0; i != length; i++)
+ result.add(null);
+
+ setResult(result);
+ }
+
+ /**
+ * Gets the current Page.
+ * @return Returns a int
+ */
+ public int getCurrentPage() {
+ return fCurrentPage;
+ }
+
+ /**
+ * Set the <code>Comparator</code> used to sort
+ * the elements in the List.
+ *
+ * @param comparator the comparator to use, not null.
+ */
+ public void setComparator(Comparator<?> comparator) {
+ fComparator= comparator;
+ if (fFilteredList != null)
+ fFilteredList.setComparator(fComparator);
+ }
+
+ @Override
+ protected FilteredList createFilteredList(Composite parent) {
+ FilteredList filteredList= super.createFilteredList(parent);
+ if (fComparator != null) {
+ filteredList.setComparator(fComparator);
+ }
+ return filteredList;
+ }
+}
diff --git a/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/imports/NamespaceSelectionLabelProvider.java b/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/imports/NamespaceSelectionLabelProvider.java
new file mode 100644
index 000000000..1ea6a404c
--- /dev/null
+++ b/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/imports/NamespaceSelectionLabelProvider.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2019 protos software gmbh (http://www.protos.de).
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * CONTRIBUTORS:
+ * Henrik Rentz-Reichert (initial contribution)
+ *
+ *******************************************************************************/
+
+package org.eclipse.etrice.core.common.ui.imports;
+
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.xtext.naming.QualifiedName;
+
+/**
+ * @author Henrik Rentz-Reichert
+ *
+ */
+public class NamespaceSelectionLabelProvider extends LabelProvider {
+ @Override
+ public String getText(Object element) {
+ if (element instanceof QualifiedName) {
+ return element.toString();
+ }
+ else return super.getText(element);
+ }
+
+}

Back to the top