/*
* Copyright (c) 2006, 2008 Borland Software Corporation
*
* 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
*
* Contributors:
* Artem Tikhomirov (Borland) - initial API and implementation
*/
package org.eclipse.gmf.internal.graphdef.codegen;
import java.util.HashSet;
import java.util.LinkedList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.gmf.gmfgraph.Canvas;
import org.eclipse.gmf.gmfgraph.ChildAccess;
import org.eclipse.gmf.gmfgraph.Compartment;
import org.eclipse.gmf.gmfgraph.Connection;
import org.eclipse.gmf.gmfgraph.CustomFigure;
import org.eclipse.gmf.gmfgraph.DiagramLabel;
import org.eclipse.gmf.gmfgraph.FigureAccessor;
import org.eclipse.gmf.gmfgraph.FigureDescriptor;
import org.eclipse.gmf.gmfgraph.FigureGallery;
import org.eclipse.gmf.gmfgraph.GMFGraphFactory;
import org.eclipse.gmf.gmfgraph.GMFGraphPackage;
import org.eclipse.gmf.gmfgraph.Node;
import org.eclipse.gmf.gmfgraph.RealFigure;
import org.eclipse.gmf.graphdef.codegen.StandaloneGenerator.Config;
import org.eclipse.gmf.graphdef.codegen.StandaloneGenerator.Processor;
import org.eclipse.gmf.graphdef.codegen.StandaloneGenerator.ProcessorCallback;
public class CanvasProcessor extends Processor {
private final DiagramElementsCopier myElementCopier;
private ProcessorCallback myCallback;
final Canvas myInput;
private Canvas myOutcome;
private FigureGallery myOutcomeGallery;
public CanvasProcessor(Canvas input) {
assert input != null;
myInput = input;
myElementCopier = new DiagramElementsCopier();
}
public Canvas getOutcome() {
return myOutcome;
}
public void go(ProcessorCallback callback, Config config) throws InterruptedException {
myCallback = callback;
myOutcomeGallery = GMFGraphFactory.eINSTANCE.createFigureGallery();
myOutcomeGallery.setName(myInput.getFigures().size() == 1 ? myInput.getFigures().get(0).getName() : "GeneratedGallery");
// TODO respect implementation from original FigureGallery, see (#x#)
myOutcomeGallery.setImplementationBundle(config.getPluginID());
handleNodes();
handleLinks();
handleCompartments();
handleLabels();
// can't use
// = (Canvas) diagramElementCopier.copy(myInput);
// here because Copier.copy doesn't respect already copied elements
myOutcome = GMFGraphFactory.eINSTANCE.createCanvas();
myOutcome.setName(myInput.getName());
myOutcome.getFigures().add(myOutcomeGallery);
myOutcome.getCompartments().addAll(myElementCopier.copyAll(myInput.getCompartments()));
myOutcome.getLabels().addAll(myElementCopier.copyAll(myInput.getLabels()));
myOutcome.getNodes().addAll(myElementCopier.copyAll(myInput.getNodes()));
myOutcome.getConnections().addAll(myElementCopier.copyAll(myInput.getConnections()));
if (!myOutcome.eContents().isEmpty()) {
myElementCopier.copyReferences();
}
myCallback = null;
}
@Override
public String[] getRequiredBundles() {
HashSet<String> rv = new HashSet<String>();
for (FigureGallery next : myInput.getFigures()) {
if (next.getImplementationBundle() != null && next.getImplementationBundle().trim().length() > 0) {
// need this for a while, though this should be done in the fqnswitch. But as I'm trying to get rid of the
// switch, that's a temp hack to pass through
rv.add(next.getImplementationBundle());
}
}
return rv.toArray(new String[rv.size()]);
}
private void handleNodes() throws InterruptedException {
for (Node next : myInput.getNodes()) {
handleFigure(next.getFigure());
}
}
private void handleLinks() throws InterruptedException {
for (Connection next : myInput.getConnections()) {
handleFigure(next.getFigure());
}
}
private void handleCompartments() throws InterruptedException {
for (Compartment next : myInput.getCompartments()) {
FigureDescriptor nextFigure = next.getFigure();
if (nextFigure == null){
throw new NullPointerException("Compartment without figure : " + next);
}
handleFigure(nextFigure);
}
}
private void handleLabels() throws InterruptedException {
for (DiagramLabel next : myInput.getLabels()) {
if (next.getAccessor() == null) {
handleFigure(next.getFigure());
}
// else nothing to do as child accessors will get copied as part of parent figure process
}
}
private void handleFigure(FigureDescriptor fd) throws InterruptedException {
if (myElementCopier.isSubstituted(fd)) {
// already processed, nothing to do
return;
// XXX originally CustomFigures do not get into history of elementCopier,
// hence may still get copied more than once. Perhaps, makes sense to have separate 'History'
// to keep track of processed figures?
}
if (fd.getActualFigure() instanceof CustomFigure && isPlainBareCustomFigure((CustomFigure) fd.getActualFigure())) {
// XXX an implementationBundle might be an issue here (#x#),
// since myOutcomeGallery gonna get one we generate, while the original CustomFigure
// may have one specified in the ownining FigureGallery.
final CustomFigure f = (CustomFigure) fd.getActualFigure();
myOutcomeGallery.getFigures().add(myElementCopier.xcopy(f));
} else {
String fqn = myCallback.visitFigure(fd);
final FigureDescriptor newFD = createCustomFigure(fd, fqn);
myElementCopier.registerSubstitution(fd, newFD);
for (ChildAccess ca : fd.getAccessors()) {
FigureAccessor newFA = GMFGraphFactory.eINSTANCE.createFigureAccessor();
newFA.setAccessor(ca.getAccessor());
newFA.setTypedFigure(createReferencedFigure(ca));
((CustomFigure) newFD.getActualFigure()).getCustomChildren().add(newFA);
final ChildAccess newCA = myElementCopier.xcopy(ca);
newCA.setFigure(newFA.getTypedFigure());
newFD.getAccessors().add(newCA);
}
}
}
/**
* FIXME diplicates {@link org.eclipse.gmf.bridge.genmodel.InnerClassViewmapProducer#isBareInstance}
* Should be merged somehow.
*/
private static boolean isPlainBareCustomFigure(CustomFigure figure) {
if (!figure.getChildren().isEmpty()) {
return false;
}
final LinkedList<EStructuralFeature> featuresToCheck = new LinkedList<EStructuralFeature>(figure.eClass().getEAllStructuralFeatures());
featuresToCheck.remove(GMFGraphPackage.eINSTANCE.getRealFigure_Name());
featuresToCheck.remove(GMFGraphPackage.eINSTANCE.getRealFigure_Children());
featuresToCheck.remove(GMFGraphPackage.eINSTANCE.getCustomClass_QualifiedClassName());
featuresToCheck.remove(GMFGraphPackage.eINSTANCE.getCustomFigure_CustomChildren());
for(EStructuralFeature next : featuresToCheck) {
if (next.isDerived()) {
continue;
}
if (figure.eIsSet(next)) {
return false;
}
}
return true;
}
private FigureDescriptor createCustomFigure(FigureDescriptor original, String fqn) {
CustomFigure cf = GalleryMirrorProcessor.createCustomFigure(original.getActualFigure());
cf.setQualifiedClassName(fqn);
FigureDescriptor fd = GMFGraphFactory.eINSTANCE.createFigureDescriptor();
fd.setName(original.getName());
fd.setActualFigure(cf);
myOutcomeGallery.getDescriptors().add(fd);
return fd;
}
private static RealFigure createReferencedFigure(ChildAccess ca) {
// XXX ca.getFigure() may be FigureRef, need to revisit this usecase
if (false == ca.getFigure() instanceof RealFigure) {
return null;
}
EClass eType = ca.getFigure().eClass();
// it's just a type holder, hence no need to copy any attribute/children but type only.
RealFigure copy = (RealFigure) eType.getEPackage().getEFactoryInstance().create(eType);
if (copy instanceof CustomFigure) {
((CustomFigure) copy).setQualifiedClassName(((CustomFigure) ca.getFigure()).getQualifiedClassName());
}
return copy;
}
}