package org.eclipse.emf.refactor.smells.eraser.ui; | |
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Set; | |
import org.eclipse.emf.ecore.EAttribute; | |
import org.eclipse.emf.ecore.ENamedElement; | |
import org.eclipse.emf.ecore.EObject; | |
import org.eclipse.emf.ecore.EOperation; | |
import org.eclipse.emf.refactor.refactoring.core.Refactoring; | |
import org.eclipse.emf.refactor.smells.core.ModelSmell; | |
import org.eclipse.emf.refactor.smells.eraser.managers.EraseManager; | |
import org.eclipse.emf.refactor.smells.eraser.utils.SetSorter; | |
import org.eclipse.emf.refactor.smells.runtime.core.EObjectGroup; | |
import org.eclipse.jface.dialogs.Dialog; | |
import org.eclipse.swt.SWT; | |
import org.eclipse.swt.custom.TableEditor; | |
import org.eclipse.swt.events.ModifyEvent; | |
import org.eclipse.swt.events.ModifyListener; | |
import org.eclipse.swt.events.SelectionAdapter; | |
import org.eclipse.swt.events.SelectionEvent; | |
import org.eclipse.swt.layout.GridData; | |
import org.eclipse.swt.layout.GridLayout; | |
import org.eclipse.swt.widgets.Combo; | |
import org.eclipse.swt.widgets.Composite; | |
import org.eclipse.swt.widgets.Control; | |
import org.eclipse.swt.widgets.Shell; | |
import org.eclipse.swt.widgets.TabFolder; | |
import org.eclipse.swt.widgets.TabItem; | |
import org.eclipse.swt.widgets.Table; | |
import org.eclipse.swt.widgets.TableColumn; | |
import org.eclipse.swt.widgets.TableItem; | |
/** | |
* Dialog class for providing the user with suggestions for applicable refactorings based on a given | |
* smell occurence. | |
* | |
* @author Matthias Burhenne | |
* | |
*/ | |
public class SuggestionDialog extends Dialog { | |
private static final String APPLICABLE_REFACTORINGS_TAB_LABEL = "Applicable Refactorings"; | |
private static final String SUGGESTED_REFACTORINGS_TAB_LABEL = "Suggested Refactorings"; | |
private static final String CONTEXT_OBJECTS = "contextObjects"; | |
private static final String SELECTED_CONTEXT_OBJECT = "selectedContextObject"; | |
private static final String POSSIBLE_SMELLS_COLUMNL_LABEL = "Possible Smells"; | |
private static final String DESCRIPTION_COLUMN_LABEL = "Description"; | |
private static final String CONTEXT_COLUMN_LABEL = "Context"; | |
private static final String REFACTORING_NAME_COLUMN_LABEL = "Refactoring"; | |
private static final String NO_MODEL_ELEMENT_SELECTED_ERROR_MESSAGE = "No model element was selected. In order to perform the\n" + | |
"selected refactoring a context object is needed.\n" + | |
"Please select it via the combo box in column " + CONTEXT_COLUMN_LABEL + "!"; | |
private static final String NO_REFACTORING_SELECTED_ERROR_MESSAGE = "No refactoring was selected. Please select a refactoring\n" + | |
"by selecting one of the table rows!"; | |
private static final String NO_CONTEXT_ELEMENT_SELECTED_ERROR_TITLE = "No model element selected"; | |
private static final String NO_REFACTORING_SELECTED_ERROR_MESSAGE_TITLE = "No refactoring selected"; | |
private static final int CONTEXT_COLUMN_INDEX = 1; | |
private static final String REFACTORING_TABLE_ITEM_DATA = "refactoring"; | |
private static final String CONTEXT_OBJECTS_COMBO = "contextObjectsCombo"; | |
private final Map<Refactoring, Set<ModelSmell>> relationMap; | |
private final EObjectGroup eObjects; | |
private final Map<Refactoring, Set<EObject>> dynamicallyCalculatedRefactorings; | |
private Refactoring selectedRefactoring = null; | |
private EObject selectedContextObject = null; | |
private TableItem selectedItem = null; | |
private Table staticTable; | |
private Table dynamicTable; | |
public SuggestionDialog(Shell parentShell, Map<Refactoring, Set<ModelSmell>> relationMap, EObjectGroup eObjects) { | |
super(parentShell); | |
this.relationMap = relationMap; | |
this.eObjects = eObjects; | |
this.dynamicallyCalculatedRefactorings = EraseManager.getInstance().getApplicableRefactoringsDynamically(eObjects); | |
} | |
protected void configureShell(Shell shell) { | |
super.configureShell(shell); | |
shell.setText("Refactoring Quick Fix Dialog"); | |
} | |
@Override | |
protected Control createContents(Composite parent) { | |
parent.setLayout(new GridLayout(1, true)); | |
TabFolder tabs = new TabFolder(parent, SWT.NULL); | |
GridData gridData = new GridData(GridData.FILL_BOTH); | |
gridData.horizontalAlignment = GridData.FILL; | |
tabs.setLayoutData(gridData); | |
TabItem staticDefinitionsTab = new TabItem(tabs, SWT.NULL); | |
staticDefinitionsTab.setText(SUGGESTED_REFACTORINGS_TAB_LABEL); | |
Composite staticComposite = new Composite(tabs, SWT.NULL); | |
staticComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); | |
staticDefinitionsTab.setControl(staticComposite); | |
GridLayout staticLayout = new GridLayout(); | |
staticComposite.setLayout(staticLayout); | |
staticTable = new Table(staticComposite, SWT.SINGLE | |
| SWT.FULL_SELECTION | SWT.BORDER | SWT.V_SCROLL); | |
createRelationTableColumns(staticTable); | |
try { | |
fillStaticRelationsTable(staticTable); | |
} catch (ClassNotFoundException e) { | |
e.printStackTrace(); | |
} | |
staticTable.setHeaderVisible(true); | |
staticTable.setLinesVisible(true); | |
TabItem dynamicRelationsTab = new TabItem(tabs, SWT.NULL); | |
dynamicRelationsTab.setText(APPLICABLE_REFACTORINGS_TAB_LABEL); | |
Composite dynamicComposite = new Composite(tabs, SWT.NULL); | |
dynamicComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); | |
dynamicRelationsTab.setControl(dynamicComposite); | |
GridLayout dynamicLayout = new GridLayout(); | |
dynamicComposite.setLayout(dynamicLayout); | |
dynamicTable = new Table(dynamicComposite, SWT.SINGLE | |
| SWT.FULL_SELECTION | SWT.BORDER | SWT.V_SCROLL); | |
createRelationTableColumns(dynamicTable); | |
fillDynamicRelationsTable(dynamicallyCalculatedRefactorings, dynamicTable); | |
dynamicTable.setHeaderVisible(true); | |
dynamicTable.setLinesVisible(true); | |
return super.createContents(parent); | |
} | |
/* | |
* fills the table contained in the tab for applicable refactorings | |
*/ | |
private void fillDynamicRelationsTable( | |
Map<Refactoring, Set<EObject>> dynamicMap, | |
Table dynamicTable) { | |
for(Refactoring refactoring : SetSorter.sortRefactoringSet(dynamicMap.keySet())){ | |
String causedSmells = buildCausedSmellsTableEntry(refactoring); | |
TableItem tableItem = new TableItem(dynamicTable, SWT.LEFT); | |
tableItem.setText(0, refactoring.getName()); | |
tableItem.setText(1, refactoring.getNamespaceUri()); | |
tableItem.setText(2, refactoring.getId()); | |
tableItem.setText(3, causedSmells); | |
Combo contextObjectsCombo = new Combo(dynamicTable, SWT.LEFT); | |
for(EObject contextObject : dynamicMap.get(refactoring)){ | |
String label = getEObjectLabel(contextObject); | |
contextObjectsCombo.add(label); | |
contextObjectsCombo.setData(label, contextObject); | |
} | |
tableItem.setData(REFACTORING_TABLE_ITEM_DATA, refactoring); | |
tableItem.setData(CONTEXT_OBJECTS_COMBO, contextObjectsCombo); | |
ArrayList<EObject> contextObjects = new ArrayList<EObject>(); | |
contextObjects.addAll(dynamicMap.get(refactoring)); | |
tableItem.setData(CONTEXT_OBJECTS, contextObjects); | |
} | |
} | |
/* | |
* Fills the table for refactorings suggested in the tab containing the suggested refactorings | |
*/ | |
private void fillStaticRelationsTable(Table table) throws ClassNotFoundException { | |
Refactoring[] refactorings = {}; | |
refactorings = relationMap.keySet().toArray(refactorings); | |
Arrays.sort(refactorings); | |
for(Refactoring refactoring : refactorings){ | |
String smells = buildCausedSmellsTableEntry(refactoring); | |
ArrayList<EObject> contextObjects = new ArrayList<EObject>(); | |
for(EObject eObject : eObjects.getEObjects()){ | |
if(EraseManager.getInstance().passesInitialCheck(eObject, refactoring)){ | |
contextObjects.add(eObject); | |
} | |
} | |
TableItem tableItem = new TableItem(table, SWT.LEFT); | |
tableItem.setText(0, refactoring.getName()); | |
tableItem.setText(1, refactoring.getNamespaceUri()); | |
tableItem.setText(2, refactoring.getId()); | |
tableItem.setText(3, smells); | |
Combo contextObjectsCombo = new Combo(table, SWT.LEFT); | |
for(EObject contextObject : contextObjects){ | |
String label = getEObjectLabel(contextObject); | |
contextObjectsCombo.add(label); | |
contextObjectsCombo.setData(label, contextObject); | |
} | |
tableItem.setData(REFACTORING_TABLE_ITEM_DATA, refactoring); | |
tableItem.setData(CONTEXT_OBJECTS_COMBO, contextObjectsCombo); | |
tableItem.setData(CONTEXT_OBJECTS, contextObjects); | |
} | |
} | |
/* | |
* Builds a String for the table column containing the names of possibly caused smells | |
*/ | |
private String buildCausedSmellsTableEntry(Refactoring refactoring) { | |
String smells = ""; | |
Set<ModelSmell> currentSet = EraseManager.getInstance().getCausedModelSmells(refactoring); | |
if(currentSet != null){ | |
for(ModelSmell smell : currentSet){ | |
smells += smell.getName() + ", "; | |
} | |
if(smells.endsWith(", ") && smells.length() > 2){ | |
smells = smells.substring(0, smells.length()-2); | |
} | |
} | |
return smells; | |
} | |
/* | |
* Adds the columns to the tables inside the two tabs | |
*/ | |
private void createRelationTableColumns(Table table) { | |
//Column 1: | |
TableColumn refactoringNameColumn = new TableColumn(table, SWT.LEFT); | |
refactoringNameColumn.setText(REFACTORING_NAME_COLUMN_LABEL); | |
refactoringNameColumn.setWidth(250); | |
//Column 2: | |
TableColumn refactoringContextColumn = new TableColumn(table, SWT.LEFT); | |
refactoringContextColumn.setText(CONTEXT_COLUMN_LABEL); | |
refactoringContextColumn.setWidth(150); | |
//Column 3: | |
TableColumn refactoringDescriptionColumn = new TableColumn(table, SWT.LEFT); | |
refactoringDescriptionColumn.setText(DESCRIPTION_COLUMN_LABEL); | |
refactoringDescriptionColumn.setWidth(300); | |
//Column 4: | |
TableColumn causedSmellsColumn = new TableColumn(table, SWT.LEFT); | |
causedSmellsColumn.setText(POSSIBLE_SMELLS_COLUMNL_LABEL); | |
causedSmellsColumn.setWidth(300); | |
final TableEditor tableEditor = new TableEditor(table); | |
final Table editorTable = table; | |
tableEditor.horizontalAlignment = SWT.LEFT; | |
tableEditor.grabHorizontal = true; | |
tableEditor.minimumWidth = refactoringContextColumn.getWidth(); | |
table.addSelectionListener(new SelectionAdapter() { | |
public void widgetSelected(SelectionEvent e) { | |
TableItem item = (TableItem) e.item; | |
if (item == null) | |
return; | |
String oldSelectedText = item.getText(CONTEXT_COLUMN_INDEX); | |
Combo oldEditor = (Combo)tableEditor.getEditor(); | |
if(oldEditor != null && selectedItem != null){ | |
String oldLabel = oldEditor.getText(); | |
selectedItem.setText(CONTEXT_COLUMN_INDEX, oldLabel); | |
oldEditor.dispose(); | |
} | |
selectedItem = item; | |
Combo newEditor = new Combo(editorTable, SWT.NONE); | |
@SuppressWarnings("unchecked") | |
List<EObject> contextObjects = (List<EObject>)item.getData(CONTEXT_OBJECTS); | |
for(EObject contextObject : contextObjects){ | |
String label = getEObjectLabel(contextObject); | |
newEditor.setData(label, contextObject); | |
newEditor.add(label); | |
} | |
for(int i = 0; i < newEditor.getItemCount(); i++){ | |
if(newEditor.getItem(i).equals(oldSelectedText)){ | |
newEditor.select(i); | |
selectedItem.setData(SELECTED_CONTEXT_OBJECT, newEditor.getData("" + i)); | |
} | |
} | |
newEditor.addModifyListener(new ModifyListener() { | |
@Override | |
public void modifyText(ModifyEvent e) { | |
Combo me = (Combo)e.widget; | |
int index = me.getSelectionIndex(); | |
String key = me.getItem(index); | |
EObject contextObject = (EObject)me.getData(key); | |
selectedItem.setData(SELECTED_CONTEXT_OBJECT, contextObject); | |
} | |
}); | |
newEditor.setFocus(); | |
tableEditor.setEditor(newEditor, item, CONTEXT_COLUMN_INDEX); | |
} | |
}); | |
} | |
/** | |
* Getter for the selected refactoring. To be used after okPressed() has been called. | |
* Will return null beforehand. | |
* @return - the Refactoring selected by the user or null | |
*/ | |
public Refactoring getSelectedRefactoring(){ | |
return selectedRefactoring; | |
} | |
/** | |
* Getter for the selected context object. To be used after okPressed() has been called. | |
* Will return null beforehand. | |
* @return - the EObject selected by the user or null | |
*/ | |
public EObject getSelectedContextObject(){ | |
return selectedContextObject; | |
} | |
@Override | |
protected void okPressed() { | |
try{ | |
if(selectedItem != null){ | |
selectedContextObject = (EObject) selectedItem.getData(SELECTED_CONTEXT_OBJECT); //selectedEObject; | |
selectedRefactoring = (Refactoring)selectedItem.getData(REFACTORING_TABLE_ITEM_DATA); | |
} | |
}catch(NullPointerException ex){ | |
selectedContextObject = null; | |
selectedRefactoring = null; | |
} | |
if(selectedRefactoring == null){ | |
org.eclipse.jface.dialogs. | |
MessageDialog.openError( | |
getShell(), | |
NO_REFACTORING_SELECTED_ERROR_MESSAGE_TITLE, | |
NO_REFACTORING_SELECTED_ERROR_MESSAGE); | |
return; | |
} | |
if(selectedContextObject == null){ | |
org.eclipse.jface.dialogs. | |
MessageDialog.openError( | |
getShell(), | |
NO_CONTEXT_ELEMENT_SELECTED_ERROR_TITLE, | |
NO_MODEL_ELEMENT_SELECTED_ERROR_MESSAGE); | |
return; | |
} | |
super.okPressed(); | |
} | |
/* | |
* Builds the label used in the ComboBoxes for selecting the context object. | |
* The content of the label depends on the type of object and the available fields. | |
*/ | |
private String getEObjectLabel(EObject eObject){ | |
String name = null; | |
String label = null; | |
String id = null; | |
for (EAttribute attribute : eObject.eClass().getEAllAttributes()) { | |
if (attribute.getName().equalsIgnoreCase("name")) { | |
name = (String) eObject.eGet(attribute); | |
} | |
} | |
for(EOperation operation : eObject.eClass().getEAllOperations()){ | |
try{ | |
if(operation.getName().equals("getName") && name == null) | |
name = (String)eObject.eInvoke(operation, null); | |
if(operation.getName().equals("getLabel") && label == null) | |
label = (String)eObject.eInvoke(operation, null); | |
if(operation.getName().equals("getID") && id == null) | |
id = (String)eObject.eInvoke(operation, null); | |
if(operation.getName().equals("getId") && id == null) | |
id = (String)eObject.eInvoke(operation, null); | |
} catch (Exception e) { | |
} | |
} | |
if(name != null && !name.isEmpty()) | |
return name; | |
if(label != null && !label.isEmpty()) | |
return label; | |
if(id != null && !id.isEmpty()) | |
return id; | |
if(eObject instanceof ENamedElement) | |
if(((ENamedElement) eObject).getName() != null && !((ENamedElement) eObject).getName().equals("")) | |
return ((ENamedElement)eObject).getName(); | |
else | |
return eObject.toString(); | |
else | |
return eObject.toString(); | |
} | |
} |