/*
* Copyright (c) 2009-2011, IETR/INSA of Rennes
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the IETR/INSA of Rennes nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
package net.sf.orcc.ui.launching.tabs;
import static net.sf.orcc.OrccLaunchConstants.MAPPING;
import static net.sf.orcc.OrccLaunchConstants.PROJECT;
import static net.sf.orcc.OrccLaunchConstants.XDF_FILE;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.events.XMLEvent;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.ui.AbstractLaunchConfigurationTab;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.IContentProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
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.graphics.Font;
import org.eclipse.swt.graphics.Image;
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.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import net.sf.orcc.OrccException;
import net.sf.orcc.df.Instance;
import net.sf.orcc.df.Network;
import net.sf.orcc.df.transform.Instantiator;
import net.sf.orcc.graph.Vertex;
import net.sf.orcc.ui.OrccUiActivator;
import net.sf.orcc.util.OrccLogger;
import net.sf.orcc.util.OrccUtil;
import net.sf.orcc.util.util.EcoreHelper;
/**
* This class defines a tab for mapping a network onto an architecture.
*
* @author Matthieu Wipliez
* @author Herve Yviquel
* @author Endri Bezati
*
*/
public class MappingTab extends AbstractLaunchConfigurationTab {
/**
* This class provides content for the mapping tree.
*
* @author Matthieu Wipliez
* @author Herve Yviquel
*
*/
private static class TreeContentProvider implements ITreeContentProvider {
@Override
public void dispose() {
}
@Override
public Object[] getChildren(Object parentElement) {
return getElements(parentElement);
}
@Override
public Object[] getElements(Object inputElement) {
if (inputElement instanceof Network) {
Network network = (Network) inputElement;
EList<Vertex> vertices = new BasicEList<Vertex>(network.getChildren());
return vertices.toArray();
}
return new Object[0];
}
@Override
public Object getParent(Object element) {
return null;
}
@Override
public boolean hasChildren(Object element) {
if (element instanceof Network) {
return true;
} else {
return false;
}
}
@Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
}
}
/**
* This class defines editing support for the "component" column.
*
* @author Matthieu Wipliez
* @author Herve Yviquel
*
*/
private class TreeEditingSupport extends EditingSupport {
private final CellEditor editor;
public TreeEditingSupport() {
super(viewer);
editor = new TextCellEditor(viewer.getTree());
}
@Override
protected boolean canEdit(Object element) {
return true;
}
@Override
protected CellEditor getCellEditor(Object element) {
return editor;
}
@Override
protected Object getValue(Object element) {
String component = labelProvider.getColumnText(element, 1);
if (component != null) {
return component;
}
return "";
}
private void setMapping(Vertex vertex, String component) {
if (vertex instanceof Instance) {
Instance instance = (Instance) vertex;
mapping.put(instance.getHierarchicalName(), component);
} else if (vertex instanceof Network) {
Network network = (Network) vertex;
mapping.put(network.getName(), component);
for (Vertex subEntity : network.getChildren()) {
setMapping(subEntity, component);
}
}
}
@Override
protected void setValue(Object element, Object value) {
if (element instanceof Vertex) {
Vertex vertex = (Vertex) element;
String component = (String) value;
if (component == null || component.contains(",")) {
return;
}
setMapping(vertex, component);
getViewer().refresh();
updateLaunchConfigurationDialog();
}
}
}
/**
* This class provides labels for the mapping tree.
*
* @author Matthieu Wipliez
*
*/
private class TreeLabelProvider extends LabelProvider implements ITableLabelProvider {
@Override
public Image getColumnImage(Object element, int columnIndex) {
return null;
}
@Override
public String getColumnText(Object element, int columnIndex) {
if (columnIndex == 0) {
return EcoreHelper.getFeature((Vertex) element, "name");
} else {
if (element instanceof Instance) {
Instance instance = (Instance) element;
return mapping.get(instance.getHierarchicalName());
}
if (element instanceof Network) {
Network network = (Network) element;
Set<String> subComponents = new TreeSet<String>();
getComponents(subComponents, network);
return OrccUtil.toString(subComponents, ", ");
}
}
return null;
}
/**
* Fills the components set with all the components that instance and if
* instance is a network, its sub-instances too, are mapped to.
*
* @param components
* a set of components
* @param instance
* an instance
*/
private void getComponents(Set<String> components, Network network) {
for (Vertex entity : network.getChildren()) {
Instance instance = entity.getAdapter(Instance.class);
if (instance == null) {
Network subNetwork = entity.getAdapter(Network.class);
getComponents(components, subNetwork);
} else {
String component = mapping.get(instance.getHierarchicalName());
if (component != null) {
components.add(component);
}
}
}
}
@Override
public String getText(Object element) {
// only getColumnText should be called
return "";
}
}
/**
* This class defines an XCF XML parser
*
* @author Endri Bezati
*
*/
private static class XcfParser {
public static final String ACTOR = "Instance";
public static final String ACTOR_ID = "id";
public static final String PARTITION = "Partition";
public static final String PARTITION_ID = "id";
Map<String, String> parse(String fileName) {
Map<String, String> xcfMapping = new HashMap<String, String>();
File file = new File(fileName);
XMLStreamReader reader = null;
try {
InputStream stream = new BufferedInputStream(new FileInputStream(file));
reader = XMLInputFactory.newInstance().createXMLStreamReader(stream);
} catch (Exception e) {
// Do nothing
}
String component = null;
String actor = null;
try {
try {
while (reader.hasNext()) {
reader.next();
switch (reader.getEventType()) {
case XMLEvent.START_ELEMENT: {
String xmlElement = reader.getName().toString();
if (xmlElement.equals(PARTITION)) {
component = reader.getAttributeValue("", PARTITION_ID);
if (component == null) {
throw new OrccException("Parsing error in \"" + file.getAbsolutePath()
+ "\": component name specified. Line "
+ reader.getLocation().getLineNumber());
}
} else if (xmlElement.equals(ACTOR)) {
actor = reader.getAttributeValue("", ACTOR_ID);
if (actor == null) {
throw new OrccException("Parsing error in \"" + file.getAbsolutePath()
+ "\": actor name not specified. Line "
+ reader.getLocation().getLineNumber());
}
xcfMapping.put(actor, component);
}
break;
}
case XMLEvent.END_ELEMENT: {
String xmlElement = reader.getName().toString();
if (xmlElement.equals(PARTITION)) {
component = null;
} else if (xmlElement.equals(ACTOR)) {
actor = null;
}
break;
}
}
}
} catch (XMLStreamException e1) {
e1.printStackTrace();
}
try {
reader.close();
} catch (Exception e) {
OrccLogger.warnln("File \"%s\" has not been properly closed" + file);
}
} catch (OrccException e) {
OrccLogger.warnln("Error parsing the XML file");
}
// Give the hierarchical name
Map<String, String> hierarchicalMapping = new HashMap<String, String>();
for (Vertex child : network.getChildren()) {
Instance instance = child.getAdapter(Instance.class);
if (instance != null) {
for (String key : xcfMapping.keySet()) {
if (instance.getHierarchicalName().contains(key)) {
hierarchicalMapping.put(instance.getHierarchicalName(), xcfMapping.get(key));
}
}
}
}
for (Network subNetwork : network.getAllNetworks()) {
for (Vertex child : subNetwork.getChildren()) {
Instance instance = child.getAdapter(Instance.class);
if (instance != null) {
for (String key : xcfMapping.keySet()) {
if (instance.getHierarchicalName().contains(key)) {
hierarchicalMapping.put(instance.getHierarchicalName(), xcfMapping.get(key));
}
}
}
}
}
return hierarchicalMapping;
}
}
private static Network network;
private IContentProvider contentProvider;
private ITableLabelProvider labelProvider;
private Map<String, String> mapping;
private Text text;
private String value;
private TreeViewer viewer;
/**
* Browses the file system.
*
* @param shell
* a shell
*/
private void browseFileSystem(Shell shell) {
String file;
FileDialog dialog = new FileDialog(shell, SWT.OPEN);
String extension = "*.xcf";
if (extension != null) {
dialog.setFilterExtensions(new String[] { extension });
}
// set initial directory
dialog.setFilterPath(value);
file = dialog.open();
if (file != null) {
text.setText(file);
}
XcfParser parser = new XcfParser();
mapping = parser.parse(file);
viewer.refresh();
updateLaunchConfigurationDialog();
}
@Override
public void createControl(Composite parent) {
Composite composite = new Composite(parent, SWT.NONE);
setControl(composite);
Font font = parent.getFont();
composite.setFont(font);
GridLayout layout = new GridLayout(1, false);
layout.verticalSpacing = 0;
composite.setLayout(layout);
GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
composite.setLayoutData(data);
createLabel(composite);
createTreeViewer(composite);
createXCFInputFile(composite);
}
private void createLabel(Composite composite) {
String html = "Warning: the mapping feature is experimental and likely to evolve.\n"
+ "Furthermore, the name of the components is implementation-specific, "
+ "and is likely to change in the future.\n" + "A component should be named like an identifier"
+ " (only letters, digits, underscore allowed).\n\n";
Label label = new Label(composite, SWT.NONE);
GridData data = new GridData(SWT.FILL, SWT.TOP, false, false);
label.setLayoutData(data);
label.setText(html);
}
/**
* Creates the tree viewer on the given composite. The tree has two columns,
* and the right column has editing support so we can modify the components.
*
* @param composite
*/
private void createTreeViewer(Composite composite) {
Tree tree = new Tree(composite, SWT.BORDER | SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL);
GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
tree.setLayoutData(data);
tree.setHeaderVisible(true);
TreeColumn column = new TreeColumn(tree, SWT.LEFT);
column.setText("instance");
column.setWidth(300);
column = new TreeColumn(tree, SWT.RIGHT);
column.setText("component");
column.setWidth(100);
viewer = new TreeViewer(tree);
TreeViewerColumn viewerColumn = new TreeViewerColumn(viewer, column);
viewerColumn.setEditingSupport(new TreeEditingSupport());
contentProvider = new TreeContentProvider();
viewer.setContentProvider(contentProvider);
labelProvider = new TreeLabelProvider();
viewer.setLabelProvider(labelProvider);
}
private void createXCFInputFile(final Composite composite) {
Composite comp = new Composite(composite, SWT.NONE);
comp.setLayout(new GridLayout(3, false));
GridData data = new GridData(SWT.FILL, SWT.TOP, true, false);
data.horizontalSpan = 3;
comp.setLayoutData(data);
Font font = composite.getFont();
Label lbl = new Label(comp, SWT.NONE);
lbl.setFont(font);
lbl.setText("XCF Mapping File" + ":");
lbl.setToolTipText("An XCF Mapping file containing the mapping configuration.");
data = new GridData(SWT.LEFT, SWT.CENTER, false, false);
lbl.setLayoutData(data);
text = new Text(comp, SWT.BORDER | SWT.SINGLE);
text.setFont(font);
data = new GridData(SWT.FILL, SWT.CENTER, true, false);
text.setLayoutData(data);
text.setText("");
text.addModifyListener(new ModifyListener() {
@Override
public void modifyText(ModifyEvent e) {
value = text.getText();
}
});
Button buttonBrowse = new Button(comp, SWT.PUSH);
buttonBrowse.setFont(font);
data = new GridData(SWT.FILL, SWT.CENTER, false, false);
buttonBrowse.setLayoutData(data);
buttonBrowse.setText("&Browse...");
buttonBrowse.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
browseFileSystem(composite.getShell());
}
});
}
@Override
public Image getImage() {
return OrccUiActivator.getImage("icons/orcc.png");
}
@Override
public String getName() {
return "Mapping";
}
@Override
@SuppressWarnings("unchecked")
public void initializeFrom(ILaunchConfiguration configuration) {
IFile xdfFile = null;
try {
mapping = new HashMap<String, String>(configuration.getAttribute(MAPPING, Collections.EMPTY_MAP));
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
String name = configuration.getAttribute(PROJECT, "");
if (root.getFullPath().isValidSegment(name)) {
IProject project = root.getProject(name);
if (project.exists()) {
xdfFile = OrccUtil.getFile(project, configuration.getAttribute(XDF_FILE, ""),
OrccUtil.NETWORK_SUFFIX);
}
}
} catch (CoreException e) {
mapping = new HashMap<String, String>(0);
e.printStackTrace();
}
if (xdfFile != null) {
ResourceSet set = new ResourceSetImpl();
network = EcoreHelper.getEObject(set, xdfFile);
new Instantiator(false).doSwitch(network);
Set<String> instances = new HashSet<String>();
for (Vertex child : network.getChildren()) {
Instance instance = child.getAdapter(Instance.class);
if (instance != null) {
instances.add(instance.getHierarchicalName());
}
}
for (Network subNetwork : network.getAllNetworks()) {
for (Vertex child : subNetwork.getChildren()) {
Instance instance = child.getAdapter(Instance.class);
if (instance != null) {
instances.add(instance.getHierarchicalName());
}
}
}
Iterator<Entry<String, String>> it = mapping.entrySet().iterator();
while (it.hasNext()) {
Entry<String, String> entry = it.next();
if (!instances.contains(entry.getKey())) {
it.remove();
}
}
}
viewer.setInput(network);
viewer.refresh();
}
@Override
public boolean isValid(ILaunchConfiguration launchConfig) {
setErrorMessage(null);
return true;
}
@Override
public void performApply(ILaunchConfigurationWorkingCopy configuration) {
if (isValid(configuration)) {
configuration.setAttribute(MAPPING, new HashMap<String, String>(mapping));
}
}
@Override
public void setDefaults(ILaunchConfigurationWorkingCopy configuration) {
configuration.setAttribute(MAPPING, new HashMap<String, String>());
}
}