org.esa.snap.graphbuilder.rcp.dialogs.support.GraphNode.java Source code

Java tutorial

Introduction

Here is the source code for org.esa.snap.graphbuilder.rcp.dialogs.support.GraphNode.java

Source

/*
 * Copyright (C) 2014 by Array Systems Computing Inc. http://www.array.ca
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 3 of the License, or (at your option)
 * any later version.
 * This program 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 General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, see http://www.gnu.org/licenses/
 */
package org.esa.snap.graphbuilder.rcp.dialogs.support;

import com.bc.ceres.binding.ConversionException;
import com.bc.ceres.binding.Converter;
import com.bc.ceres.binding.Property;
import com.bc.ceres.binding.PropertyContainer;
import com.bc.ceres.binding.PropertyDescriptor;
import com.bc.ceres.binding.dom.DomConverter;
import com.bc.ceres.binding.dom.DomElement;
import com.bc.ceres.binding.dom.XppDomElement;
import com.thoughtworks.xstream.io.xml.xppdom.XppDom;
import org.apache.commons.math3.util.FastMath;
import org.esa.snap.core.datamodel.Product;
import org.esa.snap.core.gpf.GPF;
import org.esa.snap.core.gpf.OperatorSpi;
import org.esa.snap.core.gpf.annotations.ParameterDescriptorFactory;
import org.esa.snap.core.gpf.graph.GraphException;
import org.esa.snap.core.gpf.graph.Node;
import org.esa.snap.core.gpf.graph.NodeSource;
import org.esa.snap.core.util.SystemUtils;
import org.esa.snap.graphbuilder.gpf.ui.OperatorUI;
import org.esa.snap.graphbuilder.gpf.ui.UIValidation;

import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.util.HashMap;
import java.util.Map;

/**
 * Represents a node of the graph for the GraphBuilder
 * Stores, saves and loads the display position for the node
 * User: lveci
 * Date: Jan 17, 2008
 */
public class GraphNode {

    private final Node node;
    private final Map<String, Object> parameterMap = new HashMap<>(10);
    private OperatorUI operatorUI = null;

    private int nodeWidth = 60;
    private int nodeHeight = 25;
    private int halfNodeHeight = 0;
    private int halfNodeWidth = 0;
    private static final int hotSpotSize = 10;
    private static final int halfHotSpotSize = hotSpotSize / 2;
    private int hotSpotOffset = 0;

    private Point displayPosition = new Point(0, 0);

    private XppDom displayParameters;
    private static Color shadowColor = new Color(0, 0, 0, 64);

    public GraphNode(final Node n) throws IllegalArgumentException {
        node = n;
        displayParameters = new XppDom("node");
        displayParameters.setAttribute("id", node.getId());

        initParameters();
    }

    public void setOperatorUI(final OperatorUI ui) {
        operatorUI = ui;
    }

    public OperatorUI GetOperatorUI() {
        return operatorUI;
    }

    private void initParameters() throws IllegalArgumentException {

        final OperatorSpi operatorSpi = GPF.getDefaultInstance().getOperatorSpiRegistry()
                .getOperatorSpi(node.getOperatorName());
        if (operatorSpi == null)
            return;

        final ParameterDescriptorFactory parameterDescriptorFactory = new ParameterDescriptorFactory();
        final PropertyContainer valueContainer = PropertyContainer.createMapBacked(parameterMap,
                operatorSpi.getOperatorClass(), parameterDescriptorFactory);

        final DomElement config = node.getConfiguration();
        final int count = config.getChildCount();
        for (int i = 0; i < count; ++i) {
            final DomElement child = config.getChild(i);
            final String name = child.getName();
            final String value = child.getValue();
            if (name == null || value == null)
                continue;

            try {
                if (child.getChildCount() == 0) {
                    final Converter converter = getConverter(valueContainer, name);
                    if (converter == null) {
                        final String msg = "Graph parameter " + name + " not found for Operator "
                                + operatorSpi.getOperatorAlias();
                        //throw new IllegalArgumentException(msg);
                        SystemUtils.LOG.warning(msg);
                    } else {
                        parameterMap.put(name, converter.parse(value));
                    }
                } else {
                    final DomConverter domConverter = getDomConverter(valueContainer, name);
                    if (domConverter != null) {
                        try {
                            final Object obj = domConverter.convertDomToValue(child, null);
                            parameterMap.put(name, obj);
                        } catch (Exception e) {
                            SystemUtils.LOG.warning(e.getMessage());
                        }
                    } else {
                        final Converter converter = getConverter(valueContainer, name);
                        final Object[] objArray = new Object[child.getChildCount()];
                        int c = 0;
                        for (DomElement ch : child.getChildren()) {
                            final String v = ch.getValue();

                            if (converter != null) {
                                objArray[c++] = converter.parse(v);
                            } else {
                                objArray[c++] = v;
                            }
                        }
                        parameterMap.put(name, objArray);
                    }
                }

            } catch (ConversionException e) {
                throw new IllegalArgumentException(name);
            }
        }
    }

    private static Converter getConverter(final PropertyContainer valueContainer, final String name) {
        final Property[] properties = valueContainer.getProperties();

        for (Property p : properties) {

            final PropertyDescriptor descriptor = p.getDescriptor();
            if (descriptor != null && (descriptor.getName().equals(name)
                    || (descriptor.getAlias() != null && descriptor.getAlias().equals(name)))) {
                return descriptor.getConverter();
            }
        }
        return null;
    }

    private static DomConverter getDomConverter(final PropertyContainer valueContainer, final String name) {
        final Property[] properties = valueContainer.getProperties();

        for (Property p : properties) {

            final PropertyDescriptor descriptor = p.getDescriptor();
            if (descriptor != null && (descriptor.getName().equals(name)
                    || (descriptor.getAlias() != null && descriptor.getAlias().equals(name)))) {
                return descriptor.getDomConverter();
            }
        }
        return null;
    }

    void setDisplayParameters(final XppDom presentationXML) {
        for (XppDom params : presentationXML.getChildren()) {
            final String id = params.getAttribute("id");
            if (id != null && id.equals(node.getId())) {
                displayParameters = params;
                final XppDom dpElem = displayParameters.getChild("displayPosition");
                if (dpElem != null) {
                    displayPosition.x = (int) Float.parseFloat(dpElem.getAttribute("x"));
                    displayPosition.y = (int) Float.parseFloat(dpElem.getAttribute("y"));
                }
                return;
            }
        }
    }

    void updateParameters() throws GraphException {
        if (operatorUI != null) {
            final XppDomElement config = new XppDomElement("parameters");
            updateParameterMap(config);
            node.setConfiguration(config);
        }
    }

    void AssignParameters(final XppDom presentationXML) throws GraphException {

        updateParameters();
        AssignDisplayParameters(presentationXML);
    }

    void AssignDisplayParameters(final XppDom presentationXML) {
        XppDom nodeElem = null;
        for (XppDom elem : presentationXML.getChildren()) {
            final String id = elem.getAttribute("id");
            if (id != null && id.equals(node.getId())) {
                nodeElem = elem;
                break;
            }
        }
        if (nodeElem == null) {
            presentationXML.addChild(displayParameters);
        }

        XppDom dpElem = displayParameters.getChild("displayPosition");
        if (dpElem == null) {
            dpElem = new XppDom("displayPosition");
            displayParameters.addChild(dpElem);
        }

        dpElem.setAttribute("y", String.valueOf(displayPosition.getY()));
        dpElem.setAttribute("x", String.valueOf(displayPosition.getX()));
    }

    /**
     * Gets the display position of a node
     *
     * @return Point The position of the node
     */
    public Point getPos() {
        return displayPosition;
    }

    /**
     * Sets the display position of a node and writes it to the xml
     *
     * @param p The position of the node
     */
    public void setPos(Point p) {
        displayPosition = p;
    }

    public Node getNode() {
        return node;
    }

    public int getWidth() {
        return nodeWidth;
    }

    public int getHeight() {
        return nodeHeight;
    }

    public static int getHotSpotSize() {
        return hotSpotSize;
    }

    int getHalfNodeWidth() {
        return halfNodeWidth;
    }

    public int getHalfNodeHeight() {
        return halfNodeHeight;
    }

    private void setSize(final int width, final int height) {
        nodeWidth = width;
        nodeHeight = height;
        halfNodeHeight = nodeHeight / 2;
        halfNodeWidth = nodeWidth / 2;
        hotSpotOffset = halfNodeHeight - halfHotSpotSize;
    }

    public int getHotSpotOffset() {
        return hotSpotOffset;
    }

    /**
     * Gets the uniqe node identifier.
     *
     * @return the identifier
     */
    public String getID() {
        return node.getId();
    }

    /**
     * Gets the name of the operator.
     *
     * @return the name of the operator.
     */
    public String getOperatorName() {
        return node.getOperatorName();
    }

    public Map<String, Object> getParameterMap() {
        return parameterMap;
    }

    public void connectOperatorSource(final String id) {
        // check if already a source for this node
        disconnectOperatorSources(id);

        //check if connected sources exists
        String cntStr = "";
        if (node.getSources().length > 0) {
            cntStr = "." + node.getSources().length;
        }

        final NodeSource ns = new NodeSource("sourceProduct" + cntStr, id);
        node.addSource(ns);
    }

    public void disconnectOperatorSources(final String id) {

        for (NodeSource ns : node.getSources()) {
            if (ns.getSourceNodeId().equals(id)) {
                node.removeSource(ns);
            }
        }
    }

    public void disconnectAllSources() {
        final NodeSource[] sources = node.getSources();
        for (NodeSource source : sources) {
            node.removeSource(source);
        }
    }

    public boolean isNodeSource(final GraphNode source) {

        for (NodeSource ns : node.getSources()) {
            if (ns.getSourceNodeId().equals(source.getID())) {
                return true;
            }
        }
        return false;
    }

    boolean HasSources() {
        return node.getSources().length > 0;
    }

    public UIValidation validateParameterMap() {
        if (operatorUI != null)
            return operatorUI.validateParameters();
        return new UIValidation(UIValidation.State.OK, "");
    }

    void setSourceProducts(final Product[] products) {
        if (operatorUI != null) {
            operatorUI.setSourceProducts(products);
        }
    }

    void updateParameterMap(final XppDomElement parentElement) throws GraphException {
        //if(operatorUI.hasSourceProducts())
        operatorUI.updateParameters();
        operatorUI.convertToDOM(parentElement);
    }

    /**
     * Draw a GraphNode as a rectangle with a name
     *
     * @param g   The Java2D Graphics
     * @param col The color to draw
     */
    public void drawNode(final Graphics2D g, final Color col) {
        final int x = displayPosition.x;
        final int y = displayPosition.y;

        g.setFont(g.getFont().deriveFont(Font.BOLD, 11));
        final FontMetrics metrics = g.getFontMetrics();
        final String name = node.getId();
        final Rectangle2D rect = metrics.getStringBounds(name, g);
        final int stringWidth = (int) rect.getWidth();
        setSize(Math.max(stringWidth, 50) + 10, 25);

        int step = 4;
        int alpha = 96;
        for (int i = 0; i < step; ++i) {
            g.setColor(new Color(0, 0, 0, alpha - (32 * i)));
            g.drawLine(x + i + 1, y + nodeHeight + i, x + nodeWidth + i - 1, y + nodeHeight + i);
            g.drawLine(x + nodeWidth + i, y + i, x + nodeWidth + i, y + nodeHeight + i);
        }

        Shape clipShape = new Rectangle(x, y, nodeWidth, nodeHeight);

        g.setComposite(AlphaComposite.SrcAtop);
        g.setPaint(new GradientPaint(x, y, col, x + nodeWidth, y + nodeHeight, col.darker()));
        g.fill(clipShape);

        g.setColor(Color.blue);
        g.draw3DRect(x, y, nodeWidth - 1, nodeHeight - 1, true);

        g.setColor(Color.BLACK);
        g.drawString(name, x + (nodeWidth - stringWidth) / 2, y + 15);
    }

    /**
     * Draws the hotspot where the user can join the node to a source node
     *
     * @param g   The Java2D Graphics
     * @param col The color to draw
     */
    public void drawHeadHotspot(final Graphics g, final Color col) {
        final Point p = displayPosition;
        g.setColor(col);
        g.drawOval(p.x - halfHotSpotSize, p.y + hotSpotOffset, hotSpotSize, hotSpotSize);
    }

    /**
     * Draws the hotspot where the user can join the node to a source node
     *
     * @param g   The Java2D Graphics
     * @param col The color to draw
     */
    public void drawTailHotspot(final Graphics g, final Color col) {
        final Point p = displayPosition;
        g.setColor(col);

        final int x = p.x + nodeWidth;
        final int y = p.y + halfNodeHeight;
        final int[] xpoints = { x, x + hotSpotOffset, x, x };
        final int[] ypoints = { y - halfHotSpotSize, y, y + halfHotSpotSize, y - halfHotSpotSize };
        g.fillPolygon(xpoints, ypoints, xpoints.length);
    }

    /**
     * Draw a line between source and target nodes
     *
     * @param g   The Java2D Graphics
     * @param src the source GraphNode
     */
    public void drawConnectionLine(final Graphics2D g, final GraphNode src) {

        final Point nodePos = displayPosition;
        final Point srcPos = src.displayPosition;

        final int nodeEndX = nodePos.x + nodeWidth;
        final int nodeMidY = nodePos.y + halfNodeHeight;
        final int srcEndX = srcPos.x + src.getWidth();
        final int srcMidY = srcPos.y + src.getHalfNodeHeight();

        if (srcEndX <= nodePos.x) {
            if (srcPos.y > nodePos.y + nodeHeight) {
                // to UR
                drawArrow(g, nodePos.x + halfNodeWidth, nodePos.y + nodeHeight, srcEndX, srcMidY);
            } else if (srcPos.y + src.getHeight() < nodePos.y) {
                // to DR
                drawArrow(g, nodePos.x + halfNodeWidth, nodePos.y, srcEndX, srcMidY);
            } else {
                // to R
                drawArrow(g, nodePos.x, nodeMidY, srcEndX, srcMidY);
            }
        } else if (srcPos.x >= nodeEndX) {
            if (srcPos.y > nodePos.y + nodeHeight) {
                // to UL
                drawArrow(g, nodePos.x + halfNodeWidth, nodePos.y + nodeHeight, srcPos.x,
                        srcPos.y + halfNodeHeight);
            } else if (srcPos.y + src.getHeight() < nodePos.y) {
                // to DL
                drawArrow(g, nodePos.x + halfNodeWidth, nodePos.y, srcPos.x, srcPos.y + halfNodeHeight);
            } else {
                // to L
                drawArrow(g, nodeEndX, nodeMidY, srcPos.x, srcPos.y + halfNodeHeight);
            }
        } else {
            if (srcPos.y > nodePos.y + nodeHeight) {
                // U
                drawArrow(g, nodePos.x + halfNodeWidth, nodePos.y + nodeHeight, srcPos.x + src.getHalfNodeWidth(),
                        srcPos.y);
            } else {
                // D
                drawArrow(g, nodePos.x + halfNodeWidth, nodePos.y, srcPos.x + src.getHalfNodeWidth(),
                        srcPos.y + src.getHeight());
            }
        }
    }

    /**
     * Draws an arrow head at the correct angle
     *
     * @param g     The Java2D Graphics
     * @param tailX position X on target node
     * @param tailY position Y on target node
     * @param headX position X on source node
     * @param headY position Y on source node
     */
    private static void drawArrow(final Graphics2D g, final int tailX, final int tailY, final int headX,
            final int headY) {

        final double t1 = Math.abs(headY - tailY);
        final double t2 = Math.abs(headX - tailX);
        double theta = Math.atan(t1 / t2);
        if (headX >= tailX) {
            if (headY > tailY)
                theta = Math.PI + theta;
            else
                theta = -(Math.PI + theta);
        } else if (headX < tailX && headY > tailY)
            theta = 2 * Math.PI - theta;
        final double cosTheta = FastMath.cos(theta);
        final double sinTheta = FastMath.sin(theta);

        final Point p2 = new Point(-8, -3);
        final Point p3 = new Point(-8, +3);

        int x = (int) Math.round((cosTheta * p2.x) - (sinTheta * p2.y));
        p2.y = (int) Math.round((sinTheta * p2.x) + (cosTheta * p2.y));
        p2.x = x;
        x = (int) Math.round((cosTheta * p3.x) - (sinTheta * p3.y));
        p3.y = (int) Math.round((sinTheta * p3.x) + (cosTheta * p3.y));
        p3.x = x;

        p2.translate(tailX, tailY);
        p3.translate(tailX, tailY);

        Stroke oldStroke = g.getStroke();
        g.setStroke(new BasicStroke(2));
        g.drawLine(tailX, tailY, headX, headY);
        g.drawLine(tailX, tailY, p2.x, p2.y);
        g.drawLine(p3.x, p3.y, tailX, tailY);
        g.setStroke(oldStroke);
    }

}