/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2012-2013, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotoolkit.gui.swing.etl;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Point;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import javax.swing.SwingUtilities;
import org.apache.sis.util.ArgumentChecks;
import org.geotoolkit.gui.swing.resource.MessageBundle;
import org.geotoolkit.process.ProcessDescriptor;
import org.geotoolkit.processing.chain.model.Constant;
import org.geotoolkit.processing.chain.model.DataLink;
import org.geotoolkit.processing.chain.model.Element;
import org.geotoolkit.processing.chain.model.ElementCondition;
import org.geotoolkit.processing.chain.model.ElementManual;
import org.geotoolkit.processing.chain.model.ElementProcess;
import org.geotoolkit.processing.chain.model.FlowLink;
import org.geotoolkit.processing.chain.model.Parameter;
import org.geotoolkit.processing.chain.model.Positionable;
import org.geotoolkit.processing.chain.model.event.EventChain;
import org.geotoolkit.util.collection.CollectionChangeEvent;
import org.netbeans.api.visual.action.AcceptProvider;
import org.netbeans.api.visual.action.ActionFactory;
import org.netbeans.api.visual.action.ConnectorState;
import org.netbeans.api.visual.widget.ConnectionWidget;
import org.netbeans.api.visual.widget.Widget;
import org.openide.util.Exceptions;
/**
* Basic scene for a chain. Only ChainElement and chain input/output both without parameters will be display.
*
* @author Johann Sorel (Geomatys)
* @author Quentin Boileau (Geomatys)
*/
public class ChainSceneBasic extends ChainScene {
protected EventChain chain = null;
protected final boolean editable;
public ChainSceneBasic(final EventChain chain, final boolean editable) {
ArgumentChecks.ensureNonNull("chain", chain);
this.editable = editable;
setBackground(Color.WHITE);
addChild(getMainLayer());
addChild(getActionLayer());
addChild(getConnectionLayer());
//default actions
getActions().addAction(ActionFactory.createZoomAction());
getActions().addAction(ActionFactory.createPanAction());
getActions().addAction(ActionFactory.createAcceptAction(new DropHandler()));
getActions().addAction(HOVER_ACTION);
setChain(chain);
}
@Override
protected void setChain(final EventChain chain) {
getMapping().clear();
if(this.chain != null){
this.chain.removeListener(this);
}
this.chain = chain;
if(this.chain != null){
this.chain.addListener(this);
}
final WChainParametersBasic inParameters = new WChainParametersBasic(this, MessageBundle.format("inputs"),Integer.MIN_VALUE, editable);
final WChainParametersBasic outParameters = new WChainParametersBasic(this, MessageBundle.format("outputs"), Integer.MAX_VALUE, editable);
getMapping().put(IN, inParameters);
getMapping().put(OUT, outParameters);
getMapping().put(ElementProcess.BEGIN, inParameters.getExecutionWidget());
getMapping().put(ElementProcess.END, outParameters.getExecutionWidget());
addWigetToMainLayer(inParameters);
addWigetToMainLayer(outParameters);
for(final Element ele : chain.getElements()){
if(ele instanceof ElementProcess){
createElementProcess((ElementProcess)ele);
}else if(ele instanceof ElementManual){
createElementManual((ElementManual)ele);
}else if(ele instanceof ElementCondition){
createElementCondition((ElementCondition)ele);
}
}
for(final FlowLink execlink : chain.getFlowLinks()){
createFlowLink(execlink);
}
if(SwingUtilities.isEventDispatchThread()){
replaceAll();
}else{
try {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
replaceAll();
}
});
} catch (InterruptedException ex) {
Exceptions.printStackTrace(ex);
} catch (InvocationTargetException ex) {
Exceptions.printStackTrace(ex);
}
}
getScene().validate();
repaint();
}
private void replaceAll(){
int minX=0;
int maxX=0;
for(Widget widget : getMapping().values()){
if(widget instanceof WPositionable){
final WPositionable element = (WPositionable) widget;
final Positionable positionable = element.getObject();
widget.setPreferredLocation(new Point(positionable.getX(), positionable.getY()));
if(positionable.getX()< minX) {minX = positionable.getX();}
if(positionable.getX()> maxX) {maxX = positionable.getX();}
}
}
final Widget win = getMapping().get(IN);
final Widget wout = getMapping().get(OUT);
win.setPreferredLocation(new Point(minX-150, 0));
wout.setPreferredLocation(new Point(maxX+150, 0));
//refresh
validate();
}
/**
* {@inheritDoc }
*/
@Override
public void dispose() {
this.chain.removeListener(this);
}
/**
* {@inheritDoc }
*/
@Override
public EventChain getChain() {
return chain;
}
/**
* {@inheritDoc }
*/
@Override
protected void createElementProcess(final ElementProcess desc){
final WElementProcess widget = new WElementProcess(this,desc, false, editable);
widget.setPreferredLocation(new Point(desc.getX(), desc.getY()));
getMapping().put(desc, widget);
addWigetToMainLayer(widget);
validate();
repaint();
}
/**
* {@inheritDoc }
*/
@Override
protected void removeElementProcess(final ElementProcess desc){
final Widget widget = getMapping().remove(desc);
ArgumentChecks.ensureNonNull("ElementProcess", widget);
getMainLayer().removeChild(widget);
getScene().validate();
}
/**
* {@inheritDoc }
*/
@Override
protected void createElementManual(final ElementManual mi){
final WElementManuel widget = new WElementManuel(this, mi, editable);
widget.setPreferredLocation(new Point(mi.getX(), mi.getY()));
getMapping().put(mi, widget);
getMainLayer().addChild(widget);
validate();
repaint();
}
/**
* {@inheritDoc }
*/
@Override
protected void removeElementManual(final ElementManual mi){
final Widget widget = getMapping().remove(mi);
ArgumentChecks.ensureNonNull("ElementManual", widget);
getMainLayer().removeChild(widget);
getScene().validate();
}
/**
* {@inheritDoc }
*/
@Override
protected void createElementCondition(ElementCondition ce) {
final WElementCondition widget = new WElementCondition(ce, this, false, editable);
widget.setPreferredLocation(new Point(ce.getX(), ce.getY()));
getMapping().put(ce, widget);
addWigetToMainLayer(widget);
validate();
repaint();
}
/**
* {@inheritDoc }
*/
@Override
protected void removeElementCondition(ElementCondition ce) {
final Widget widget = getMapping().remove(ce);
ArgumentChecks.ensureNonNull("ElementCondition", widget);
getMainLayer().removeChild(widget);
getScene().validate();
}
/**
* {@inheritDoc }
*/
@Override
protected void createFlowLink(final FlowLink link){
final EventChain sequence = getChain();
final Object source = link.getSource(sequence);
final Object target = link.getTarget(sequence);
Widget w_source = getMapping().get(source);
Widget w_target = getMapping().get(target);
if(w_source instanceof WElementProcess){
final WElementProcess pw = (WElementProcess) w_source;
w_source = pw.getExecutionWidget();
} else if (w_source instanceof WChainParametersFull) {
final WChainParametersFull pw = (WChainParametersFull) w_source;
w_source = pw.getExecutionWidget();
} else if (w_source instanceof WElementManuel) {
final WElementManuel pw = (WElementManuel) w_source;
w_source = pw.getExecutionWidget();
} else if (w_source instanceof WElementCondition) {
final WElementCondition pw = (WElementCondition) w_source;
//if link already exist, find if it's a success or failed output.
if (pw.getSuccessLinks().contains(link)) {
w_source = pw.getWExecutionSuccess();
} else if (pw.getFailedLinks().contains(link)) {
w_source = pw.getWExecutionFailed();
} else {
//new link : check type and add to Widgetlinks
//TODO
// if (DcnsConstants.EXEC_LINK_SUCCESS.equals(link.getType())) {
// w_source = pw.getWExecutionSuccess();
// pw.getSuccessLinks().add(link);
// } else {
// w_source = pw.getWExecutionFailed();
// pw.getFailedLinks().add(link);
// }
}
}
if(w_target instanceof WElementProcess){
final WElementProcess pw = (WElementProcess) w_target;
w_target = pw.getExecutionWidget();
} else if(w_target instanceof WChainParametersFull){
final WChainParametersFull pw = (WChainParametersFull) w_target;
w_target = pw.getExecutionWidget();
} else if (w_target instanceof WElementManuel) {
final WElementManuel pw = (WElementManuel) w_target;
w_target = pw.getExecutionWidget();
} else if (w_target instanceof WElementCondition) {
final WElementCondition pw = (WElementCondition) w_target;
w_target = pw.getWExecutionTitle();
}
ArgumentChecks.ensureNonNull("link source", w_source);
ArgumentChecks.ensureNonNull("link target", w_target);
final ConnectionWidget connection = new WFlowLink(this,link);
connection.setSourceAnchor(new SideAnchor(w_source, false));
connection.setTargetAnchor(new SideAnchor(w_target, true));
getMapping().put(link, connection);
getConnectionLayer().addChild(connection);
getScene().validate();
}
/**
* {@inheritDoc }
*/
@Override
protected void removeFlowLink(final FlowLink link){
final Widget widget = getMapping().remove(link);
ArgumentChecks.ensureNonNull("Flow link widget", widget);
getConnectionLayer().removeChild(widget);
getScene().validate();
}
/**
* {@inheritDoc }
*/
@Override
protected void createInParameter(Parameter param) {
//Do nothing action not supported in basic view.
}
/**
* {@inheritDoc }
*/
@Override
protected void removeInParameter(Parameter param) {
//Do nothing action not supported in basic view.
}
/**
* {@inheritDoc }
*/
@Override
protected void createOutParameter(Parameter param) {
//Do nothing action not supported in basic view.
}
/**
* {@inheritDoc }
*/
@Override
protected void removeOutParameter(Parameter param) {
//Do nothing action not supported in basic view.
}
/**
* {@inheritDoc }
*/
@Override
protected void createConstant(Constant cst) {
//Do nothing action not supported in basic view.
}
/**
* {@inheritDoc }
*/
@Override
protected void removeConstant(Constant cst) {
//Do nothing action not supported in basic view.
}
/**
* {@inheritDoc }
*/
@Override
protected void createDataLink(DataLink link) {
//Do nothing action not supported in basic view.
}
/**
* {@inheritDoc }
*/
@Override
protected void removeDataLink(DataLink link) {
//Do nothing action not supported in basic view.
}
/**
* Return the Widget associate with the input object.
* @param obj input object like Constant, Element, ...
* @return widget or null if no found.
*/
@Override
public Widget getWidget(final Object obj) {
return getMapping().get(obj);
}
/**
* {@inheritDoc }
*/
@Override
protected Widget attachNodeWidget(final ProcessDescriptor n) {
return null;
}
/**
* {@inheritDoc }
*/
@Override
protected Widget attachEdgeWidget(String e) {
return null;
}
/**
* {@inheritDoc }
*/
@Override
protected void attachEdgeSourceAnchor(String e, ProcessDescriptor n, ProcessDescriptor n1) {
}
/**
* {@inheritDoc }
*/
@Override
protected void attachEdgeTargetAnchor(String e, ProcessDescriptor n, ProcessDescriptor n1) {
}
private static Collection<Object> getDropCandidates(Transferable transferable){
final DataFlavor[] flavors = transferable.getTransferDataFlavors();
final Collection<Object> candidates = new ArrayList<Object>();
for(DataFlavor flavor : flavors){
Object obj;
try {
obj = transferable.getTransferData(flavor);
if(obj instanceof Collection){
for(Object c : (Collection)obj){
if(c instanceof ProcessDescriptor){
candidates.add((ProcessDescriptor)c);
}else if(c instanceof Constant){
candidates.add(c);
}else if(c instanceof ElementManual){
candidates.add(c);
}else if(c instanceof ElementCondition){
candidates.add(c);
}
}
}else if(obj instanceof ProcessDescriptor){
candidates.add(obj);
}else if(obj instanceof Constant){
candidates.add(obj);
}else if(obj instanceof ElementManual){
candidates.add(obj);
}else if(obj instanceof ElementCondition){
candidates.add(obj);
}
} catch (UnsupportedFlavorException ex) {
} catch (IOException ex) {}
}
return candidates;
}
////////////////////////////////////////////////////////////////////////////
// chain events /////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
/**
* {@inheritDoc }
*/
@Override
public void descriptorChange(CollectionChangeEvent event) {
if(event.getType() == CollectionChangeEvent.ITEM_ADDED){
for(Object obj : event.getItems()){
if(obj instanceof ElementProcess){
createElementProcess((ElementProcess)obj);
}else if(obj instanceof ElementCondition){
createElementCondition((ElementCondition)obj);
}else if(obj instanceof ElementManual){
createElementManual((ElementManual)obj);
}
}
}else if(event.getType() == CollectionChangeEvent.ITEM_REMOVED){
for(Object obj : event.getItems()){
if(obj instanceof ElementProcess){
removeElementProcess((ElementProcess)obj);
}else if(obj instanceof ElementCondition){
removeElementCondition((ElementCondition)obj);
}else if(obj instanceof ElementManual){
removeElementManual((ElementManual)obj);
}
}
}
}
/**
* {@inheritDoc }
*/
@Override
public void executionLinkChange(CollectionChangeEvent event) {
if(event.getType() == CollectionChangeEvent.ITEM_ADDED){
for(Object obj : event.getItems()){
createFlowLink((FlowLink)obj);
}
}else if(event.getType() == CollectionChangeEvent.ITEM_REMOVED){
for(Object obj : event.getItems()){
removeFlowLink((FlowLink)obj);
}
}
}
/**
* {@inheritDoc }
*/
@Override
public void constantChange(CollectionChangeEvent event) {
//Do nothing action not supported in basic view.
}
/**
* {@inheritDoc }
*/
@Override
public void linkChange(CollectionChangeEvent event) {
//Do nothing action not supported in basic view.
}
/**
* {@inheritDoc }
*/
@Override
public void inputChange(CollectionChangeEvent event) {
//Do nothing action not supported in basic view.
}
/**
* {@inheritDoc }
*/
@Override
public void outputChange(CollectionChangeEvent event) {
//Do nothing action not supported in basic view.
}
private class DropHandler implements AcceptProvider {
@Override
public ConnectorState isAcceptable(final Widget widget, final Point point, final Transferable transferable) {
final Collection<Object> candidates = getDropCandidates(transferable);
if (candidates != null && !candidates.isEmpty()) {
setCursor(new Cursor(Cursor.HAND_CURSOR));
return ConnectorState.ACCEPT;
} else {
return ConnectorState.REJECT_AND_STOP;
}
}
@Override
public void accept(final Widget widget, final Point point, final Transferable transferable) {
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
final Collection<Object> candidates = getDropCandidates(transferable);
final EventChain seq = getChain();
for(Object candidate : candidates){
if(candidate instanceof ProcessDescriptor){
final ProcessDescriptor desc = (ProcessDescriptor) candidate;
final ElementProcess pdesc = new ElementProcess(
seq.getNextId(),
desc.getIdentifier().getAuthority().getIdentifiers().iterator().next().getCode(),
desc.getIdentifier().getCode(),
point.x,
point.y);
seq.getElements().add(pdesc);
}else if(candidate instanceof Constant){
final Constant ref = new Constant((Constant)candidate);
ref.setId(seq.getNextId());
ref.setX(point.x);
ref.setY(point.y);
seq.getConstants().add(ref);
}else if(candidate instanceof ElementManual){
final ElementManual ref = new ElementManual((ElementManual)candidate);
ref.setId(seq.getNextId());
ref.setX(point.x);
ref.setY(point.y);
seq.getElements().add(ref);
} else if(candidate instanceof ElementCondition){
final ElementCondition ref = new ElementCondition((ElementCondition)candidate);
ref.setId(seq.getNextId());
ref.setX(point.x);
ref.setY(point.y);
seq.getElements().add(ref);
}
}
}
}
}