/**
* This file is protected by Copyright.
* Please refer to the COPYRIGHT file distributed with this source distribution.
*
* This file is part of REDHAWK IDE.
*
* 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.
*/
package gov.redhawk.ide.graphiti.ui.diagram.providers;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.transaction.RunnableWithResult;
import org.eclipse.graphiti.dt.IDiagramTypeProvider;
import org.eclipse.graphiti.features.ICreateFeature;
import org.eclipse.graphiti.palette.IPaletteCompartmentEntry;
import org.eclipse.graphiti.palette.IToolEntry;
import org.eclipse.graphiti.palette.impl.ObjectCreationToolEntry;
import org.eclipse.graphiti.palette.impl.PaletteCompartmentEntry;
import org.eclipse.graphiti.palette.impl.StackEntry;
import org.eclipse.graphiti.pattern.CreateFeatureForPattern;
import org.eclipse.ui.progress.WorkbenchJob;
import gov.redhawk.core.graphiti.ui.diagram.providers.AbstractToolBehaviorProvider;
import gov.redhawk.ide.graphiti.ui.diagram.patterns.AbstractFindByPattern;
import gov.redhawk.ide.graphiti.ui.diagram.util.DUtil;
import gov.redhawk.ide.graphiti.ui.palette.PaletteTreeEntry;
import gov.redhawk.ide.sdr.SdrPackage;
import gov.redhawk.ide.sdr.SoftPkgRegistry;
import gov.redhawk.model.sca.commands.ScaModelCommand;
import mil.jpeojtrs.sca.spd.Implementation;
import mil.jpeojtrs.sca.spd.SoftPkg;
/**
* Provides tool behavior for Graphiti diagrams that have a palette (only the XML editors, sandbox diagrams)
*/
public abstract class AbstractPaletteToolBehaviorProvider extends AbstractToolBehaviorProvider {
private final WorkbenchJob refreshPaletteJob = new WorkbenchJob("Refresh Palette") {
@Override
public IStatus runInUIThread(final IProgressMonitor monitor) {
// refresh palette which will call IToolBehaviorProvider.getPalette()
getDiagramTypeProvider().getDiagramBehavior().refreshPalette();
return Status.OK_STATUS;
}
};
private final Adapter sdrListener = new AdapterImpl() {
@Override
public void notifyChanged(final Notification msg) {
if (msg.getFeatureID(SoftPkgRegistry.class) == SdrPackage.SOFT_PKG_REGISTRY__COMPONENTS) {
refreshPaletteAsync();
}
}
};
private List<IPaletteCompartmentEntry> paletteCompartments = null;
private List<SoftPkgRegistry> registries = new ArrayList<SoftPkgRegistry>();
public AbstractPaletteToolBehaviorProvider(IDiagramTypeProvider diagramTypeProvider) {
super(diagramTypeProvider);
}
@Override
public void dispose() {
for (SoftPkgRegistry registry : registries) {
registry.eAdapters().remove(sdrListener);
}
super.dispose();
}
@Override
public boolean isShowFlyoutPalette() {
return true;
}
@Override
public IPaletteCompartmentEntry[] getPalette() {
// Allow child classes to contribute palette compartments the first time we get the palette
if (paletteCompartments == null) {
paletteCompartments = new ArrayList<IPaletteCompartmentEntry>();
addPaletteCompartments(paletteCompartments);
}
// Allow subclasses to refresh their palette entries
refreshPalette();
return paletteCompartments.toArray(new IPaletteCompartmentEntry[paletteCompartments.size()]);
}
/**
* The tool behavior provider should contribute {@link IPaletteCompartmentEntry}. This is only invoked once.
*/
protected abstract void addPaletteCompartments(List<IPaletteCompartmentEntry> compartments);
/**
* The tool behavior provider should refresh its {@link IToolEntry}s in each {@link PaletteCompartmentEntry}.
*/
protected abstract void refreshPalette();
/**
* Starts an asynchronous refresh of the palette in the UI thread.
*/
protected void refreshPaletteAsync() {
refreshPaletteJob.schedule(1000);
}
/**
* Creates a {@link PaletteCompartmentEntry} for findby's, and adds all findby patterns found in the feature
* provider.
* @return
*/
protected PaletteCompartmentEntry createFindByCompartmentEntry() {
PaletteCompartmentEntry compartmentEntry = new PaletteCompartmentEntry("Find By", null);
for (ICreateFeature cf : getFeatureProvider().getCreateFeatures()) {
// Search for create features that use a pattern derived from the abstract FindBy patten
if ((cf instanceof CreateFeatureForPattern && ((CreateFeatureForPattern) cf).getPattern() instanceof AbstractFindByPattern)) {
ObjectCreationToolEntry objectCreationToolEntry = new ObjectCreationToolEntry(cf.getCreateName(), cf.getCreateDescription(),
cf.getCreateImageId(), cf.getCreateLargeImageId(), cf);
compartmentEntry.addToolEntry(objectCreationToolEntry);
}
}
return compartmentEntry;
}
/**
* Subclasses must provide a {@link ICreateFeature} for the given {@link SoftPkg} implementation.
* @param spd
* @param implId
* @param iconId
* @return
*/
protected abstract ICreateFeature getCreateFeature(SoftPkg spd, String implId, String iconId);
/**
* Refreshes the contents of a {@link PaletteCompartmentEntry} with the contents of a {@link SoftPkgRegistry}.
* @param compartmentEntry
* @param container
* @param iconId
*/
protected void refreshCompartmentEntry(PaletteCompartmentEntry compartmentEntry, final SoftPkgRegistry container, String iconId) {
compartmentEntry.getToolEntries().clear();
List<SoftPkg> spds;
try {
spds = ScaModelCommand.runExclusive(container, new RunnableWithResult.Impl<List<SoftPkg>>() {
@Override
public void run() {
setResult(new ArrayList<SoftPkg>(container.getComponents()));
}
});
} catch (InterruptedException e) {
spds = new ArrayList<SoftPkg>();
}
for (SoftPkg spd : spds) {
for (Implementation impl : spd.getImplementation()) {
if (impl.isExecutable()) {
addToolToCompartment(compartmentEntry, spd, iconId);
break;
}
}
}
sort(compartmentEntry.getToolEntries());
}
/**
* Creates and adds {@link IToolEntry} to the specified {@link PaletteCompartmentEntry} for the specified
* {@link SoftPkg}.
* @param compartment
* @param spd
* @param iconId
*/
protected void addToolToCompartment(PaletteCompartmentEntry compartment, SoftPkg spd, String iconId) {
Assert.isNotNull(compartment, "Cannot add tool to non-existent compartment");
String[] segments = getNameSegments(spd);
PaletteCompartmentEntry folder = compartment;
for (int index = 0; index < segments.length - 1; ++index) {
folder = getSegmentEntry(folder, segments[index]);
}
folder.addToolEntry(makeTool(spd, iconId));
}
private String[] getNameSegments(SoftPkg spd) {
String fullName = spd.getName();
if (fullName == null) {
return new String[] { "" };
}
if (!fullName.contains(".")) {
return new String[] { fullName };
}
return fullName.split("\\.");
}
private PaletteTreeEntry getSegmentEntry(PaletteCompartmentEntry parent, String label) {
if (label == null) {
return null;
}
if (parent == null) {
return new PaletteTreeEntry(label);
}
for (IToolEntry entry : parent.getToolEntries()) {
if (entry instanceof PaletteTreeEntry && label.equals(entry.getLabel())) {
return (PaletteTreeEntry) entry;
}
}
return new PaletteTreeEntry(label, parent);
}
private IToolEntry makeTool(SoftPkg spd, String iconId) {
List<IToolEntry> newEntries = createPaletteEntries(spd, iconId);
if (newEntries != null && newEntries.size() > 1) {
sort(newEntries);
IToolEntry firstEntry = newEntries.get(0);
StackEntry stackEntry = new StackEntry(firstEntry.getLabel(), ((ObjectCreationToolEntry) firstEntry).getDescription(), firstEntry.getIconId());
for (IToolEntry entry : newEntries) {
stackEntry.addCreationToolEntry((ObjectCreationToolEntry) entry);
}
return stackEntry;
}
return newEntries.get(0);
}
/**
* Creates a new IToolEntry for the {@link SoftPkg}, or in the case of a runtime diagram, an {@link IToolEntry} for
* each of the {@link SoftPkg}'s implementations. Also assigns the createComponentFeature to the palette entry so
* that the diagram knows which shape to create.
*/
private List<IToolEntry> createPaletteEntries(SoftPkg spd, String iconId) {
String label = getLastSegment(getNameSegments(spd));
List<IToolEntry> entries = new ArrayList<IToolEntry>(spd.getImplementation().size());
if (spd.getImplementation().size() == 1 || DUtil.isDiagramWorkpace(this.getDiagramTypeProvider().getDiagram())) {
entries.add(createSpdToolEntry(label, spd, null, iconId));
} else {
for (Implementation impl : spd.getImplementation()) {
entries.add(createSpdToolEntry(label, spd, impl.getId(), iconId));
}
}
return entries;
}
private String getLastSegment(String[] segments) {
if (segments == null || segments.length < 1) {
return "";
}
return segments[segments.length - 1];
}
private ObjectCreationToolEntry createSpdToolEntry(String label, SoftPkg spd, String implId, String iconId) {
if (implId == null) {
// Use default implementation
implId = spd.getImplementation().get(0).getId();
} else {
// Implementation given, include it in the label
label = label + " (" + implId + ")";
}
String description = spd.getDescription();
if (description == null) {
description = "Create a new instance of \"" + label + "\"";
}
ICreateFeature createComponentFeature = getCreateFeature(spd, implId, iconId);
return new ObjectCreationToolEntry(label, description, iconId, null, createComponentFeature);
}
/**
* Sorts a list of {@link IToolEntry} according to:
* <ol>
* <li>Folders ({@link PaletteTreeEntry}) before other objects</li>
* <li>Case-insensitive label comparison</li>
* </ol>
* @param entries
*/
protected void sort(List<IToolEntry> entries) {
Collections.sort(entries, new Comparator<IToolEntry>() {
@Override
public int compare(final IToolEntry o1, final IToolEntry o2) {
// Put the namespace folders together at the top
if (o1 instanceof PaletteTreeEntry && !(o2 instanceof PaletteTreeEntry)) {
return -1;
}
if (o2 instanceof PaletteTreeEntry && !(o1 instanceof PaletteTreeEntry)) {
return 1;
}
final String str1 = o1.getLabel();
final String str2 = o2.getLabel();
if (str1 == null) {
if (str2 == null) {
return 0;
} else {
return 1;
}
} else if (str2 == null) {
return -1;
} else {
return str1.compareToIgnoreCase(str2);
}
}
});
for (IToolEntry entry : entries) {
if (entry instanceof PaletteTreeEntry) {
sort(((PaletteTreeEntry) entry).getToolEntries());
}
}
}
/**
* Add a listener on a {@link SoftPkgRegistry} (i.e. part of the SDRROOT) that will refresh the palette when there
* are changes.
*/
protected void addTargetSdrRefreshJob(SoftPkgRegistry container) {
container.eAdapters().add(sdrListener);
registries.add(container);
}
}