package edu.ucsd.arcum.ui.views; import static edu.ucsd.arcum.interpreter.query.ArcumDeclarationTable.lookupSymbolTable; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.TextFileChange; import org.eclipse.swt.SWT; 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.*; import org.eclipse.text.edits.ReplaceEdit; import org.eclipse.ui.part.ViewPart; import edu.ucsd.arcum.ArcumPlugin; import edu.ucsd.arcum.EclipseUtil; import edu.ucsd.arcum.builders.ParseArcumCodeOperation; import edu.ucsd.arcum.exceptions.ArcumError; import edu.ucsd.arcum.exceptions.UserCompilationProblem; import edu.ucsd.arcum.interpreter.ast.Option; import edu.ucsd.arcum.interpreter.ast.RequireMap; import edu.ucsd.arcum.interpreter.ast.TopLevelConstruct; import edu.ucsd.arcum.interpreter.query.ArcumDeclarationTable; import edu.ucsd.arcum.interpreter.query.OptionMatchTable; import edu.ucsd.arcum.interpreter.transformation.ResolvedConceptMapEntry; import edu.ucsd.arcum.interpreter.transformation.TransformationAlgorithm; import edu.ucsd.arcum.ui.UIUtil; import edu.ucsd.arcum.util.FileUtil; import edu.ucsd.arcum.util.SystemUtil; public class FragmentsView extends ViewPart { private TreeViewer entitiesTreeViewer; private Button refreshButton; private Button transformButton; private Combo entrySelect; private Combo transformSelect; private EntityTableContentProvider tableContentProvider; private RefreshAction refreshAction; private EntrySelectionListener entrySelectionListener; private TransformSelectionListener transformSelectionListener; @Override public void createPartControl(Composite parent) { layoutControls(parent); hookActionListeners(); } private void hookActionListeners() { this.entrySelectionListener = new EntrySelectionListener(); entrySelect.addSelectionListener(entrySelectionListener); this.refreshAction = new RefreshAction(); refreshButton.addSelectionListener(refreshAction); this.transformSelectionListener = new TransformSelectionListener(); transformSelect.addSelectionListener(transformSelectionListener); transformButton.addSelectionListener(new TransformButtonAction()); } private void layoutControls(Composite parent) { GridLayout layout = new GridLayout(1, true); layout.marginWidth = 0; layout.marginHeight = 0; parent.setLayout(layout); parent.setLayoutData(fillGreedy()); Composite topControls = new Composite(parent, SWT.NONE); makeControls(topControls); GridData data = new GridData(); data.grabExcessHorizontalSpace = true; data.grabExcessVerticalSpace = false; topControls.setLayoutData(data); Composite bottomTable = new Composite(parent, SWT.NONE); makeTable(bottomTable); bottomTable.setLayoutData(fillGreedy()); } private GridData fillGreedy() { GridData data = new GridData(); data.grabExcessHorizontalSpace = true; data.grabExcessVerticalSpace = true; data.horizontalAlignment = SWT.FILL; data.verticalAlignment = SWT.FILL; return data; } private void makeControls(Composite parent) { GridLayout layout = new GridLayout(); layout.numColumns = 7; layout.makeColumnsEqualWidth = false; parent.setLayout(layout); Label focusOn = new Label(parent, SWT.LEFT); focusOn.setText("Focus on map entry:"); this.entrySelect = new Combo(parent, SWT.DROP_DOWN | SWT.READ_ONLY); GridData data = new GridData(); data.horizontalAlignment = SWT.FILL; data.grabExcessHorizontalSpace = true; entrySelect.setLayoutData(data); entrySelect.setEnabled(false); entrySelect.setVisible(true); this.refreshButton = new Button(parent, SWT.PUSH); refreshButton.setText("Refresh"); Label spacer = new Label(parent, SWT.LEFT); spacer.setText(" "); Label transformTo = new Label(parent, SWT.LEFT); transformTo.setText("Transform to:"); this.transformSelect = new Combo(parent, SWT.DROP_DOWN | SWT.READ_ONLY); transformSelect.setVisible(true); transformSelect.setEnabled(false); this.transformButton = new Button(parent, SWT.PUSH); transformButton.setText("Transform"); } private void makeTable(Composite parent) { GridLayout layout = new GridLayout(1, true); layout.marginWidth = 0; layout.marginHeight = 0; parent.setLayout(layout); parent.setLayoutData(fillGreedy()); this.entitiesTreeViewer = new TreeViewer(parent, SWT.SINGLE | SWT.BORDER | SWT.FULL_SELECTION); Tree control = entitiesTreeViewer.getTree(); GridData data = fillGreedy(); control.setLayoutData(data); control.setHeaderVisible(true); control.setLinesVisible(true); TreeColumn column = new TreeColumn(control, SWT.LEFT, 0); column.setText("Concept"); column.setWidth(200); column.setAlignment(SWT.LEFT); column = new TreeColumn(control, SWT.LEFT, 1); column.setText("Program Fragment"); column.setWidth(250); column.setAlignment(SWT.LEFT); column = new TreeColumn(control, SWT.LEFT, 2); column.setText("Resource"); column.setWidth(150); column.setAlignment(SWT.LEFT); column = new TreeColumn(control, SWT.LEFT, 3); column.setText("Path"); column.setWidth(200); column.setAlignment(SWT.LEFT); column = new TreeColumn(control, SWT.LEFT, 4); column.setText("Line"); column.setWidth(100); column.setAlignment(SWT.LEFT); this.tableContentProvider = new EntityTableContentProvider(); entitiesTreeViewer.setContentProvider(tableContentProvider); entitiesTreeViewer.setLabelProvider(new EntityTableLabelProvider()); entitiesTreeViewer.addDoubleClickListener(new EntityTableDoubleClickListener(entitiesTreeViewer)); } @Override public void setFocus() { } private boolean checkForUnsavedEditors() { if (UIUtil.existsUnsavedEditor()) { UIUtil.notify("You must save all modified editors first", "Code Unsaved"); return true; } return false; } private class RefreshAction extends SelectionAdapter { public void widgetSelected(SelectionEvent e) { if (checkForUnsavedEditors()) { return; } entrySelectionListener.clearEntries(); List<String> items = new ArrayList<String>(); List<IProject> projects = EclipseUtil.getOpenProjects(); for (IProject project: projects) { IJavaProject javaProject = JavaCore.create(project); if (!javaProject.exists()) continue; try { if (!project.hasNature(ArcumPlugin.NATURE_ID)) continue; } catch (CoreException ce) { SystemUtil.getErrStream().printf("Impossible"); ce.printStackTrace(SystemUtil.getErrStream()); } if (UIUtil.hasErrors(project)) { return; } ParseArcumCodeOperation.rematchAllCodeWithDialog(project); ArcumDeclarationTable declTable = lookupSymbolTable(project); List<RequireMap> maps = declTable.getAllMaps(); for (RequireMap map: maps) { List<ResolvedConceptMapEntry> bindings = map.getResolvedBindings(); for (ResolvedConceptMapEntry binding: bindings) { items.add(makeEntrySelectMenuItem(binding, project)); } } } int curSelection = entrySelect.getSelectionIndex(); if (curSelection == -1) { curSelection = 0; } entrySelect.setItems(items.toArray(new String[0])); if (curSelection >= items.size()) { curSelection = 0; } entrySelect.select(curSelection); entrySelect.setEnabled(true); entrySelectionListener.widgetSelected(null); } private String makeEntrySelectMenuItem(ResolvedConceptMapEntry binding, IProject project) { String entryText = String.format("%s - %s", binding.getDisplayString(), project.getName()); entrySelectionListener.bindEntry(entryText, binding, project); return entryText; } } private class EntrySelectionListener extends SelectionAdapter { private Map<String, IProject> projectLookup; private Map<String, ResolvedConceptMapEntry> entryLookup; public void widgetSelected(SelectionEvent e) { int index = entrySelect.getSelectionIndex(); String entryText = entrySelect.getItem(index); IProject project = projectLookup.get(entryText); ArcumDeclarationTable declTable = lookupSymbolTable(project); ResolvedConceptMapEntry entry = entryLookup.get(entryText); String optionArgs = entry.getArgumentSourceText(); OptionMatchTable optionMatchTable; try { optionMatchTable = declTable.constructEntityTable(optionArgs); } catch (UserCompilationProblem ucp) { ucp.printStackTrace(SystemUtil.getErrStream()); ArcumError.fatalUserError(ucp.getPosition(), "%s", ucp.getMessage()); return; } catch (CoreException ce) { ce.printStackTrace(SystemUtil.getErrStream()); throw new RuntimeException(ce); } catch (ArcumError ae) { ae.printStackTrace(SystemUtil.getErrStream()); throw ae; } catch (RuntimeException re) { re.printStackTrace(SystemUtil.getErrStream()); ArcumError.fatalUserError(null, "%s: %s", re.getClass().getCanonicalName(), re.getMessage()); throw re; } refreshTable(optionMatchTable); refreshTransformSelect(declTable, project, entry.getOption(), entry.getSourceFile(), optionArgs); } private void refreshTable(OptionMatchTable optionMatchTable) { tableContentProvider.setEntityTable(optionMatchTable); entitiesTreeViewer.setInput(optionMatchTable); entitiesTreeViewer.refresh(); } private void refreshTransformSelect(ArcumDeclarationTable declTable, IProject project, Option option, IFile file, String optionArgs) { TopLevelConstruct optionInterface = option.getOptionInterface(); List<Option> options = declTable.getImplementingOptions(optionInterface); options.remove(option); transformSelectionListener.clearEntries(); List<String> items = new ArrayList<String>(); for (Option alternative: options) { String itemName = alternative.getName(); items.add(itemName); transformSelectionListener.bindEntry(declTable, itemName, option, alternative, file, optionArgs); } transformSelect.setItems(items.toArray(new String[0])); transformSelect.select(0); transformSelect.setEnabled(true); } public void bindEntry(String entryText, ResolvedConceptMapEntry entry, IProject project) { projectLookup.put(entryText, project); entryLookup.put(entryText, entry); } public void clearEntries() { this.projectLookup = new HashMap<String, IProject>(); this.entryLookup = new HashMap<String, ResolvedConceptMapEntry>(); } } private class TransformSelectionListener extends SelectionAdapter { private Map<String, Transformer> transformerLookup; public void bindEntry(ArcumDeclarationTable symbTab, String itemName, Option option, Option alternative, IFile file, String optionArgs) { Transformer transformer = new Transformer(symbTab, option, alternative, optionArgs, file); transformerLookup.put(itemName, transformer); } public void clearEntries() { this.transformerLookup = new HashMap<String, Transformer>(); } public Transformer lookup(String item) { return transformerLookup.get(item); } } private class TransformButtonAction extends SelectionAdapter { public void widgetSelected(SelectionEvent se) { try { if (checkForUnsavedEditors()) { return; } List<IProject> projects = EclipseUtil.getOpenProjects(); for (IProject project: projects) { IJavaProject javaProject = JavaCore.create(project); if (!javaProject.exists()) continue; if (UIUtil.hasErrors(project)) { return; } } int index = transformSelect.getSelectionIndex(); if (index < 0) { String message = "Transformation cannot be performed because" + " there are no alternative options available."; UIUtil.notify(message, "Nothing to Transform to"); } else { String item = transformSelect.getItem(index); Transformer transformer = transformSelectionListener.lookup(item); transformer.doTransform(); } } catch (ArcumError e) { e.printStackTrace(System.err); throw e; } catch (RuntimeException e) { e.printStackTrace(System.err); throw e; } catch (Exception e) { e.printStackTrace(System.err); throw new RuntimeException(e); } } } private class Transformer { private ArcumDeclarationTable symbTab; private Option option; private Option alternative; private String srcText; private IFile file; public Transformer(ArcumDeclarationTable symbTab, Option option, Option alternative, String srcText, IFile file) { this.symbTab = symbTab; this.option = option; this.alternative = alternative; this.srcText = srcText; this.file = file; } public void doTransform() throws Exception { TransformationAlgorithm xform; Change change; xform = new TransformationAlgorithm(symbTab, option, alternative, srcText); change = makeMapEntryEdit(srcText); xform.addChange(change); boolean completed = xform.transform(); if (completed) { // saveAllArcumSources(); // ParseArcumCodeOperation.rematchAllCodeWithDialog(symbTab.getProject()); refreshAction.widgetSelected(null); } } private Change makeMapEntryEdit(String srcText) throws CoreException, IOException { String changeMessage = String.format("Change concept map to use %s", option.getName(), alternative.getName()); TextFileChange change = new TextFileChange(changeMessage, file); String newText = srcText.replaceFirst(option.getName(), alternative.getName()); String map = FileUtil.readFileWithThrow(file); ReplaceEdit replaceEdit = new ReplaceEdit(map.indexOf(srcText), srcText.length(), newText); change.setEdit(replaceEdit); return change; } } }