Package org.graphstream.ui.graphicGraph

Source Code of org.graphstream.ui.graphicGraph.GraphicGraph

/*
* Copyright 2006 - 2013
*     Stefan Balev     <stefan.balev@graphstream-project.org>
*     Julien Baudry    <julien.baudry@graphstream-project.org>
*     Antoine Dutot    <antoine.dutot@graphstream-project.org>
*     Yoann Pigné      <yoann.pigne@graphstream-project.org>
*     Guilhelm Savin   <guilhelm.savin@graphstream-project.org>
*
* This file is part of GraphStream <http://graphstream-project.org>.
*
* GraphStream is a library whose purpose is to handle static or dynamic
* graph, create them from scratch, file or any source and display them.
*
* This program is free software distributed under the terms of two licenses, the
* CeCILL-C license that fits European law, and the GNU Lesser General Public
* License. You can  use, modify and/ or redistribute the software under the terms
* of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following
* URL <http://www.cecill.info> or under the terms of the GNU LGPL 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL-C and LGPL licenses and that you accept their terms.
*/
package org.graphstream.ui.graphicGraph;

import org.graphstream.graph.Edge;
import org.graphstream.graph.EdgeFactory;
import org.graphstream.graph.Element;
import org.graphstream.graph.ElementNotFoundException;
import org.graphstream.graph.Graph;
import org.graphstream.graph.IdAlreadyInUseException;
import org.graphstream.graph.Node;
import org.graphstream.graph.NodeFactory;
import org.graphstream.graph.implementations.AbstractElement;
import org.graphstream.stream.AttributeSink;
import org.graphstream.stream.ElementSink;
import org.graphstream.stream.Sink;
import org.graphstream.stream.SourceBase.ElementType;
import org.graphstream.stream.file.FileSink;
import org.graphstream.stream.file.FileSource;
import org.graphstream.ui.geom.Point3;
import org.graphstream.ui.graphicGraph.stylesheet.Style;
import org.graphstream.ui.graphicGraph.stylesheet.StyleConstants;
import org.graphstream.ui.graphicGraph.stylesheet.StyleConstants.Units;
import org.graphstream.ui.graphicGraph.stylesheet.StyleSheet;
import org.graphstream.ui.graphicGraph.stylesheet.Value;
import org.graphstream.ui.graphicGraph.stylesheet.Values;
import org.graphstream.ui.view.Viewer;
import org.graphstream.util.GraphListeners;

import java.io.IOException;
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Graph representation used in display classes.
*
* <p>
* Warning: This class is NOT a general graph class, and it should NOT be used
* as it. This class is particularly dedicated to fast drawing of the graph and
* is internally arranged to be fast for this task only. It implements graph
* solely to be easily susceptible to be used as a sink and source for graph
* events. Some of the common methods of the Graph interface are not functional
* and will throw an exception if used (as documented in their respective
* JavaDoc).
* </p>
*
* <p>
* The purpose of the graphic graph is to represent a graph with some often used
* graphic attributes (like position, label, etc.) stored as fields in the nodes
* and edges and most of the style stored in styles pertaining to a style sheet
* that tries to imitate the way CSS works. For example, the GraphicNode class
* defines a label, a position (x,y,z) and a style that is taken from the style
* sheet.
* </p>
*
* <p>
* The style sheet is uploaded on the graph using an attribute correspondingly
* named "ui.stylesheet" or "ui.stylesheet" (the second one is better). It can
* be a string that contains the whole style sheet, or an URL of the form :
* </p>
*
* <pre>
* url(name)
* </pre>
*
* <p>
* The graphic graph does not completely duplicate a graph, it only store things
* that are useful for drawing it. Although it implements "Graph", some methods
* are not implemented and will throw a runtime exception. These methods are
* mostly utility methods like write(), read(), and naturally display().
* </p>
*
* <p>
* The graphic graph has the ability to store attributes like any other graph
* element, however the attributes stored by the graphic graph are restricted.
* There is a filter on the attribute adding methods that let pass only:
* <ul>
* <li>All attributes starting with "ui.".</li>
* <li>The "x", "y", "z", "xy" and "xyz" attributes.</li>
* <li>The "stylesheet" attribute (although "ui.stylesheet" is preferred).</li>
* <li>The "label" attribute.</li>
* </ul>
* All other attributes are filtered and not stored. The result is that if the
* graphic graph is used as an input (a source of graph events) some attributes
* will not pass through the filter.
* </p>
*
* <p>
* The implementation of this graph relies on the StyleGroupSet class and this
* is indeed its way to store its elements (grouped by style and Z level).
* </p>
*
* <p>
* In addition to this, it provides, as all graphs do, the relational
* information for edges.
* </p>
*
* TODO : this graph cannot handle modification inside event listener methods !!
*/
public class GraphicGraph extends AbstractElement implements Graph,
    StyleGroupListener {

  /**
   * class level logger
   */
  private static final Logger logger = Logger.getLogger(GraphicGraph.class
      .getSimpleName());

  /**
   * Set of styles.
   */
  protected StyleSheet styleSheet;

  /**
   * Associate graphic elements with styles.
   */
  protected StyleGroupSet styleGroups;

  /**
   * Connectivity. The way nodes are connected one with another via edges. The
   * map is sorted by node. For each node an array of edges lists the
   * connectivity.
   */
  protected final Map<GraphicNode, List<GraphicEdge>> connectivity;

  /**
   * The style of this graph. This is a shortcut to avoid searching it in the
   * style sheet.
   */
  public StyleGroup style;

  /**
   * Memorize the step events.
   */
  public double step = 0;

  /**
   * Set to true each time the graph was modified internally and a redraw is
   * needed.
   */
  public boolean graphChanged;

  /**
   * Set to true each time a sprite or node moved.
   */
  protected boolean boundsChanged = true;

  /**
   * Maximum position of a node or sprite in the graphic graph. Computed by
   * {@link #computeBounds()}.
   */
  protected Point3 hi = new Point3();

  /**
   * Minimum position of a node or sprite in the graphic graph. Computed by
   * {@link #computeBounds()}.
   */
  protected Point3 lo = new Point3();

  /**
   * Set of listeners of this graph.
   */
  protected GraphListeners listeners;

  /**
   * Time of other known sources.
   */
  // protected SinkTime sinkTime = new SinkTime();

  /**
   * Are null attributes access an error ?
   */
  protected boolean nullAttrError = false;

  /**
   * Report back the XYZ events on nodes and sprites? If enabled, each change
   * in the position of nodes and sprites will be sent to potential listeners
   * of the graph. By default this is disabled as long there are no listeners.
   */
  protected boolean feedbackXYZ = true;

  /**
   * New empty graphic graph.
   *
   * A default style sheet is created, it then can be "cascaded" with other
   * style sheets.
   */
  public GraphicGraph(String id) {
    super(id);

    listeners = new GraphListeners(this);
    styleSheet = new StyleSheet();
    styleGroups = new StyleGroupSet(styleSheet);
    connectivity = new HashMap<GraphicNode, List<GraphicEdge>>();

    styleGroups.addListener(this);
    styleGroups.addElement(this); // Add style to this graph.

    style = styleGroups.getStyleFor(this);
  }

  // Access

  /**
   * True if the graph was edited or changed in any way since the last reset
   * of the "changed" flag.
   *
   * @return true if the graph was changed.
   */
  public boolean graphChangedFlag() {
    return graphChanged;
  }

  /**
   * Reset the "changed" flag.
   *
   * @see #graphChangedFlag()
   */
  public void resetGraphChangedFlag() {
    graphChanged = false;
  }

  /**
   * The style sheet. This style sheet is the result of the "cascade" or
   * accumulation of styles added via attributes of the graph.
   *
   * @return A style sheet.
   */
  public StyleSheet getStyleSheet() {
    return styleSheet;
  }

  /**
   * The graph style group.
   *
   * @return A style group.
   */
  public StyleGroup getStyle() {
    return style;
  }

  /**
   * The complete set of style groups.
   *
   * @return The style groups.
   */
  public StyleGroupSet getStyleGroups() {
    return styleGroups;
  }

  @Override
  public String toString() {
    return String.format("[%s %d nodes %d edges]", getId(), getNodeCount(),
        getEdgeCount());
  }

  public double getStep() {
    return step;
  }

  /**
   * The maximum position of a node or sprite. Notice that this is updated
   * only each time the {@link #computeBounds()} method is called.
   *
   * @return The maximum node or sprite position.
   */
  public Point3 getMaxPos() {
    return hi;
  }

  /**
   * The minimum position of a node or sprite. Notice that this is updated
   * only each time the {@link #computeBounds()} method is called.
   *
   * @return The minimum node or sprite position.
   */
  public Point3 getMinPos() {
    return lo;
  }

  /**
   * Does the graphic graph publish via attribute changes the XYZ changes on
   * nodes and sprites when changed ?. This is disabled by default, and
   * enabled as soon as there is at least one listener.
   */
  public boolean feedbackXYZ() {
    return feedbackXYZ;
  }

  // Command

  /**
   * Should the graphic graph publish via attribute changes the XYZ changes on
   * nodes and sprites when changed ?.
   */
  public void feedbackXYZ(boolean on) {
    feedbackXYZ = on;
  }

  /**
   * Compute the overall bounds of the graphic graph according to the nodes
   * and sprites positions. We can only compute the graph bounds from the
   * nodes and sprites centres since the node and graph bounds may in certain
   * circumstances be computed according to the graph bounds. The bounds are
   * stored in the graph metrics.
   *
   * This operation will process each node and sprite and is therefore costly.
   * However it does this computation again only when a node or sprite moved.
   * Therefore it can be called several times, if nothing moved in the graph,
   * the computation will not be redone.
   *
   * @see #getMaxPos()
   * @see #getMinPos()
   */
  public void computeBounds() {
    if (boundsChanged) {
      boolean effectiveChange = false;

      lo.x = lo.y = lo.z = Double.MAX_VALUE;
      hi.x = hi.y = hi.z = -Double.MAX_VALUE;

      for (Node n : getEachNode()) {
        GraphicNode node = (GraphicNode) n;

        if (!node.hidden && node.positionned) {
          effectiveChange = true;

          if (node.x < lo.x)
            lo.x = node.x;
          if (node.x > hi.x)
            hi.x = node.x;
          if (node.y < lo.y)
            lo.y = node.y;
          if (node.y > hi.y)
            hi.y = node.y;
          if (node.z < lo.z)
            lo.z = node.z;
          if (node.z > hi.z)
            hi.z = node.z;
        }
      }

      for (GraphicSprite sprite : spriteSet()) {
        if (!sprite.isAttached()
            && sprite.getUnits() == StyleConstants.Units.GU) {
          double x = sprite.getX();
          double y = sprite.getY();
          double z = sprite.getZ();

          if (!sprite.hidden) {
            effectiveChange = true;

            if (x < lo.x)
              lo.x = x;
            if (x > hi.x)
              hi.x = x;
            if (y < lo.y)
              lo.y = y;
            if (y > hi.y)
              hi.y = y;
            if (z < lo.z)
              lo.z = z;
            if (z > hi.z)
              hi.z = z;
          }
        }
      }

      if (hi.x - lo.x < 0.000001) {
        hi.x = hi.x + 1;
        lo.x = lo.x - 1;
      }
      if (hi.y - lo.y < 0.000001) {
        hi.y = hi.y + 1;
        lo.y = lo.y - 1;
      }
      if (hi.z - lo.z < 0.000001) {
        hi.z = hi.z + 1;
        lo.z = lo.z - 1;
      }

      //
      // Prevent infinities that can be produced by Double.MAX_VALUE.
      //
      if (effectiveChange)
        boundsChanged = false;
      else {
        lo.x = lo.y = lo.z = -1;
        hi.x = hi.y = hi.z = 1;
      }
    }
  }

  protected void moveNode(String id, double x, double y, double z) {
    GraphicNode node = (GraphicNode) styleGroups.getNode(id);

    if (node != null) {
      node.x = x;
      node.y = y;
      node.z = z;
      node.addAttribute("x", x);
      node.addAttribute("y", y);
      node.addAttribute("z", z);

      graphChanged = true;
    }
  }

  @SuppressWarnings("unchecked")
  public <T extends Node> T getNode(String id) {
    return (T) styleGroups.getNode(id);
  }

  @SuppressWarnings("unchecked")
  public <T extends Edge> T getEdge(String id) {
    return (T) styleGroups.getEdge(id);
  }

  public GraphicSprite getSprite(String id) {
    return styleGroups.getSprite(id);
  }

  @Override
  protected void attributeChanged(AttributeChangeEvent event,
      String attribute, Object oldValue, Object newValue) {

    // One of the most important method. Most of the communication comes
    // from attributes.

    if (attribute.equals("ui.repaint")) {
      graphChanged = true;
    } else if (attribute.equals("ui.stylesheet")
        || attribute.equals("stylesheet")) {
      if (event == AttributeChangeEvent.ADD
          || event == AttributeChangeEvent.CHANGE) {
        if (newValue instanceof String) {
          try {
            styleSheet.load((String) newValue);
            graphChanged = true;
          } catch (Exception e) {
            logger.log(
                Level.WARNING,
                String.format(
                    "Error while parsing style sheet for graph '%s'.",
                    getId()), e);
          }
        } else {
          logger.warning(String
              .format("Error with stylesheet specification what to do with '%s'.",
                  newValue));
        }
      } else // Remove the style.
      {
        styleSheet.clear();
        graphChanged = true;
      }
    } else if (attribute.startsWith("ui.sprite.")) {
      // Defers the sprite handling to the sprite API.
      spriteAttribute(event, null, attribute, newValue);
      graphChanged = true;
    }

    listeners.sendAttributeChangedEvent(getId(), ElementType.GRAPH,
        attribute, event, oldValue, newValue);
  }

  /**
   * Display the node/edge relations.
   */
  public void printConnectivity() {
    Iterator<GraphicNode> keys = connectivity.keySet().iterator();

    System.err.printf("Graphic graph connectivity:%n");

    while (keys.hasNext()) {
      GraphicNode node = keys.next();
      System.err.printf("    [%s] -> ", node.getId());
      Iterable<GraphicEdge> edges = connectivity.get(node);
      for (GraphicEdge edge : edges)
        System.err.printf(" (%s %d)", edge.getId(),
            edge.getMultiIndex());
      System.err.printf("%n");
    }
  }

  // Style group listener interface

  public void elementStyleChanged(Element element, StyleGroup oldStyle,
      StyleGroup style) {
    if (element instanceof GraphicElement) {
      GraphicElement ge = (GraphicElement) element;
      ge.style = style;
      graphChanged = true;
    } else if (element instanceof GraphicGraph) {
      GraphicGraph gg = (GraphicGraph) element;
      gg.style = style;
      graphChanged = true;
    } else {
      throw new RuntimeException("WTF ?");
    }
  }

  public void styleChanged(StyleGroup style) {

  }

  // Graph interface

  public Iterable<? extends Edge> getEachEdge() {
    return styleGroups.edges();
  }

  public Iterable<? extends Node> getEachNode() {
    return styleGroups.nodes();
  }

  @SuppressWarnings("all")
  public <T extends Node> Collection<T> getNodeSet() {
    return new AbstractCollection<T>() {
      public Iterator<T> iterator() {
        return getNodeIterator();
      }

      public int size() {
        return getNodeCount();
      }
    };
  }

  @SuppressWarnings("all")
  public <T extends Edge> Collection<T> getEdgeSet() {
    return new AbstractCollection<T>() {
      public Iterator<T> iterator() {
        return getEdgeIterator();
      }

      public int size() {
        return getEdgeCount();
      }
    };
  }

  @SuppressWarnings("unchecked")
  public Iterator<Node> iterator() {
    return (Iterator<Node>) styleGroups.getNodeIterator();
  }

  /*
   * (non-Javadoc)
   *
   * @see org.graphstream.stream.Source#addSink(org.graphstream.stream.Sink)
   */
  public void addSink(Sink listener) {
    listeners.addSink(listener);
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * org.graphstream.stream.Source#removeSink(org.graphstream.stream.Sink)
   */
  public void removeSink(Sink listener) {
    listeners.removeSink(listener);
  }

  /*
   * *(non-Javadoc)
   *
   * @see
   * org.graphstream.stream.Source#addAttributeSink(org.graphstream.stream
   * .AttributeSink)
   */
  public void addAttributeSink(AttributeSink listener) {
    listeners.addAttributeSink(listener);
  }

  /*
   * *(non-Javadoc)
   *
   * @see
   * org.graphstream.stream.Source#removeAttributeSink(org.graphstream.stream
   * .AttributeSink)
   */
  public void removeAttributeSink(AttributeSink listener) {
    listeners.removeAttributeSink(listener);
  }

  /*
   * *(non-Javadoc)
   *
   * @see org.graphstream.stream.Source#addElementSink(org.graphstream.stream.
   * ElementSink)
   */
  public void addElementSink(ElementSink listener) {
    listeners.addElementSink(listener);
  }

  /*
   * *(non-Javadoc)
   *
   * @see
   * org.graphstream.stream.Source#removeElementSink(org.graphstream.stream
   * .ElementSink)
   */
  public void removeElementSink(ElementSink listener) {
    listeners.removeElementSink(listener);
  }

  /*
   * *(non-Javadoc)
   *
   * @see org.graphstream.graph.Graph#attributeSinks()
   */
  public Iterable<AttributeSink> attributeSinks() {
    return listeners.attributeSinks();
  }

  /*
   * *(non-Javadoc)
   *
   * @see org.graphstream.graph.Graph#elementSinks()
   */
  public Iterable<ElementSink> elementSinks() {
    return listeners.elementSinks();
  }

  /*
   * (non-Javadoc)
   *
   * @see org.graphstream.graph.Graph#addEdge(java.lang.String,
   * java.lang.String, java.lang.String)
   */
  @SuppressWarnings("unchecked")
  public <T extends Edge> T addEdge(String id, String from, String to)
      throws IdAlreadyInUseException, ElementNotFoundException {
    return (T) addEdge(id, from, to, false);
  }

  /*
   * (non-Javadoc)
   *
   * @see org.graphstream.graph.Graph#addEdge(java.lang.String,
   * java.lang.String, java.lang.String, boolean)
   */
  @SuppressWarnings("unchecked")
  public <T extends Edge> T addEdge(String id, String from, String to,
      boolean directed) throws IdAlreadyInUseException,
      ElementNotFoundException {
    GraphicEdge edge = (GraphicEdge) styleGroups.getEdge(id);

    if (edge == null) {
      GraphicNode n1 = (GraphicNode) styleGroups.getNode(from);
      GraphicNode n2 = (GraphicNode) styleGroups.getNode(to);

      if (n1 == null)
        throw new ElementNotFoundException("node \"%s\"", from);

      if (n2 == null)
        throw new ElementNotFoundException("node \"%s\"", to);

      edge = new GraphicEdge(id, n1, n2, directed, null);// , attributes);

      styleGroups.addElement(edge);

      List<GraphicEdge> l1 = connectivity.get(n1);
      List<GraphicEdge> l2 = connectivity.get(n2);

      if (l1 == null) {
        l1 = new ArrayList<GraphicEdge>();
        connectivity.put(n1, l1);
      }

      if (l2 == null) {
        l2 = new ArrayList<GraphicEdge>();
        connectivity.put(n2, l2);
      }

      l1.add(edge);
      l2.add(edge);
      edge.countSameEdges(l1);

      graphChanged = true;

      listeners.sendEdgeAdded(id, from, to, directed);
    }

    return (T) edge;
  }

  /*
   * (non-Javadoc)
   *
   * @see org.graphstream.graph.Graph#addNode(java.lang.String)
   */
  @SuppressWarnings("unchecked")
  public <T extends Node> T addNode(String id) throws IdAlreadyInUseException {
    GraphicNode node = (GraphicNode) styleGroups.getNode(id);

    if (node == null) {
      node = new GraphicNode(this, id, null);// , attributes);

      styleGroups.addElement(node);

      graphChanged = true;

      listeners.sendNodeAdded(id);
    }

    return (T) node;
  }

  /*
   * (non-Javadoc)
   *
   * @see org.graphstream.graph.Graph#clear()
   */
  public void clear() {
    listeners.sendGraphCleared();

    clearAttributesWithNoEvent();

    connectivity.clear();
    styleGroups.clear();
    styleSheet.clear();

    step = 0;
    graphChanged = true;

    styleGroups.addElement(this);
    style = styleGroups.getStyleFor(this);
  }

  /*
   * (non-Javadoc)
   *
   * @see org.graphstream.graph.Graph#removeEdge(java.lang.String)
   */
  @SuppressWarnings("unchecked")
  public <T extends Edge> T removeEdge(String id)
      throws ElementNotFoundException {
    GraphicEdge edge = (GraphicEdge) styleGroups.getEdge(id);

    if (edge != null) {
      listeners.sendEdgeRemoved(id);

      if (connectivity.get(edge.from) != null)
        connectivity.get(edge.from).remove(edge);
      if (connectivity.get(edge.to) != null)
        connectivity.get(edge.to).remove(edge);

      styleGroups.removeElement(edge);
      edge.removed();

      graphChanged = true;
    }

    return (T) edge;
  }

  /*
   * (non-Javadoc)
   *
   * @see org.graphstream.graph.Graph#removeEdge(java.lang.String,
   * java.lang.String)
   */
  @SuppressWarnings("unchecked")
  public <T extends Edge> T removeEdge(String from, String to)
      throws ElementNotFoundException {
    GraphicNode node0 = (GraphicNode) styleGroups.getNode(from);
    GraphicNode node1 = (GraphicNode) styleGroups.getNode(to);

    if (node0 != null && node1 != null) {
      Collection<GraphicEdge> edges0 = connectivity.get(node0);
      Collection<GraphicEdge> edges1 = connectivity.get(node1);

      for (GraphicEdge edge0 : edges0) {
        for (GraphicEdge edge1 : edges1) {
          if (edge0 == edge1) {
            removeEdge(edge0.getId());
            return (T) edge0;
          }
        }
      }
    }

    return null;
  }

  /*
   * *(non-Javadoc)
   *
   * @see org.graphstream.graph.Graph#removeNode(java.lang.String)
   */
  @SuppressWarnings("unchecked")
  public <T extends Node> T removeNode(String id)
      throws ElementNotFoundException {
    GraphicNode node = (GraphicNode) styleGroups.getNode(id);

    if (node != null) {
      listeners.sendNodeRemoved(id);

      if (connectivity.get(node) != null) {
        // We must do a copy of the connectivity set for the node
        // since we will be modifying the connectivity as we process
        // edges.
        List<GraphicEdge> l = new ArrayList<GraphicEdge>(
            connectivity.get(node));

        for (GraphicEdge edge : l)
          removeEdge(edge.getId());

        connectivity.remove(node);
      }

      styleGroups.removeElement(node);
      node.removed();

      graphChanged = true;
    }

    return (T) node;
  }

  public Viewer display() {
    throw new RuntimeException(
        "GraphicGraph is used by display() and cannot recursively define display()");
  }

  public Viewer display(boolean autoLayout) {
    throw new RuntimeException(
        "GraphicGraph is used by display() and cannot recursively define display()");
  }

  /*
   * (non-Javadoc)
   *
   * @see org.graphstream.graph.Graph#stepBegins(double)
   */
  public void stepBegins(double step) {
    listeners.sendStepBegins(step);
    this.step = step;
  }

  public EdgeFactory<? extends Edge> edgeFactory() {
    throw new RuntimeException("GraphicGraph does not support EdgeFactory");
  }

  public int getEdgeCount() {
    return styleGroups.getEdgeCount();
  }

  @SuppressWarnings("unchecked")
  public <T extends Edge> Iterator<T> getEdgeIterator() {
    return (Iterator<T>) styleGroups.getEdgeIterator();
  }

  public int getNodeCount() {
    return styleGroups.getNodeCount();
  }

  public int getSpriteCount() {
    return styleGroups.getSpriteCount();
  }

  @SuppressWarnings("unchecked")
  public <T extends Node> Iterator<T> getNodeIterator() {
    return (Iterator<T>) styleGroups.getNodeIterator();
  }

  public Iterator<? extends GraphicSprite> getSpriteIterator() {
    return styleGroups.getSpriteIterator();
  }

  public Iterable<? extends GraphicSprite> spriteSet() {
    return styleGroups.sprites();
  }

  public boolean isAutoCreationEnabled() {
    return false;
  }

  public NodeFactory<? extends Node> nodeFactory() {
    throw new RuntimeException("GraphicGraph does not support NodeFactory");
  }

  public void setAutoCreate(boolean on) {
    throw new RuntimeException(
        "GraphicGraph does not support auto-creation");
  }

  public boolean isStrict() {
    return false;
  }

  public void setStrict(boolean on) {
    throw new RuntimeException(
        "GraphicGraph does not support strict checking");
  }

  @Override
  public boolean nullAttributesAreErrors() {
    return nullAttrError;
  }

  public void setNullAttributesAreErrors(boolean on) {
    nullAttrError = on;
  }

  public void setEdgeFactory(EdgeFactory<? extends Edge> ef) {
    throw new RuntimeException(
        "you cannot change the edge factory for graphic graphs !");
  }

  public void setNodeFactory(NodeFactory<? extends Node> nf) {
    throw new RuntimeException(
        "you cannot change the node factory for graphic graphs !");
  }

  public void read(String filename) throws IOException {
    throw new RuntimeException("GraphicGraph does not support I/O");
  }

  public void read(FileSource input, String filename) throws IOException {
    throw new RuntimeException("GraphicGraph does not support I/O");
  }

  public void write(FileSink output, String filename) throws IOException {
    throw new RuntimeException("GraphicGraph does not support I/O");
  }

  public void write(String filename) throws IOException {
    throw new RuntimeException("GraphicGraph does not support I/O");
  }

  // Output interface

  /*
   * (non-Javadoc)
   *
   * @see
   * org.graphstream.stream.AttributeSink#edgeAttributeAdded(java.lang.String,
   * long, java.lang.String, java.lang.String, java.lang.Object)
   */
  public void edgeAttributeAdded(String sourceId, long timeId, String edgeId,
      String attribute, Object value) {
    listeners
        .edgeAttributeAdded(sourceId, timeId, edgeId, attribute, value);
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * org.graphstream.stream.AttributeSink#edgeAttributeChanged(java.lang.String
   * , long, java.lang.String, java.lang.String, java.lang.Object,
   * java.lang.Object)
   */
  public void edgeAttributeChanged(String sourceId, long timeId,
      String edgeId, String attribute, Object oldValue, Object newValue) {
    listeners.edgeAttributeChanged(sourceId, timeId, edgeId, attribute,
        oldValue, newValue);
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * org.graphstream.stream.AttributeSink#edgeAttributeRemoved(java.lang.String
   * , long, java.lang.String, java.lang.String)
   */
  public void edgeAttributeRemoved(String sourceId, long timeId,
      String edgeId, String attribute) {
    listeners.edgeAttributeRemoved(sourceId, timeId, edgeId, attribute);
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * org.graphstream.stream.AttributeSink#graphAttributeAdded(java.lang.String
   * , long, java.lang.String, java.lang.Object)
   */
  public void graphAttributeAdded(String sourceId, long timeId,
      String attribute, Object value) {
    listeners.graphAttributeAdded(sourceId, timeId, attribute, value);
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * org.graphstream.stream.AttributeSink#graphAttributeChanged(java.lang.
   * String, long, java.lang.String, java.lang.Object, java.lang.Object)
   */
  public void graphAttributeChanged(String sourceId, long timeId,
      String attribute, Object oldValue, Object newValue) {
    listeners.graphAttributeChanged(sourceId, timeId, attribute, oldValue,
        newValue);
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * org.graphstream.stream.AttributeSink#graphAttributeRemoved(java.lang.
   * String, long, java.lang.String)
   */
  public void graphAttributeRemoved(String sourceId, long timeId,
      String attribute) {
    listeners.graphAttributeRemoved(sourceId, timeId, attribute);
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * org.graphstream.stream.AttributeSink#nodeAttributeAdded(java.lang.String,
   * long, java.lang.String, java.lang.String, java.lang.Object)
   */
  public void nodeAttributeAdded(String sourceId, long timeId, String nodeId,
      String attribute, Object value) {
    listeners
        .nodeAttributeAdded(sourceId, timeId, nodeId, attribute, value);
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * org.graphstream.stream.AttributeSink#nodeAttributeChanged(java.lang.String
   * , long, java.lang.String, java.lang.String, java.lang.Object,
   * java.lang.Object)
   */
  public void nodeAttributeChanged(String sourceId, long timeId,
      String nodeId, String attribute, Object oldValue, Object newValue) {
    listeners.nodeAttributeChanged(sourceId, timeId, nodeId, attribute,
        oldValue, newValue);
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * org.graphstream.stream.AttributeSink#nodeAttributeRemoved(java.lang.String
   * , long, java.lang.String, java.lang.String)
   */
  public void nodeAttributeRemoved(String sourceId, long timeId,
      String nodeId, String attribute) {
    listeners.nodeAttributeRemoved(sourceId, timeId, nodeId, attribute);
  }

  /*
   * (non-Javadoc)
   *
   * @see org.graphstream.stream.ElementSink#edgeAdded(java.lang.String, long,
   * java.lang.String, java.lang.String, java.lang.String, boolean)
   */
  public void edgeAdded(String sourceId, long timeId, String edgeId,
      String fromNodeId, String toNodeId, boolean directed) {
    listeners.edgeAdded(sourceId, timeId, edgeId, fromNodeId, toNodeId,
        directed);
  }

  /*
   * *(non-Javadoc)
   *
   * @see org.graphstream.stream.ElementSink#edgeRemoved(java.lang.String,
   * long, java.lang.String)
   */
  public void edgeRemoved(String sourceId, long timeId, String edgeId) {
    listeners.edgeRemoved(sourceId, timeId, edgeId);
  }

  /*
   * *(non-Javadoc)
   *
   * @see org.graphstream.stream.ElementSink#graphCleared(java.lang.String,
   * long)
   */
  public void graphCleared(String sourceId, long timeId) {
    listeners.graphCleared(sourceId, timeId);
  }

  /*
   * (non-Javadoc)
   *
   * @see org.graphstream.stream.ElementSink#nodeAdded(java.lang.String, long,
   * java.lang.String)
   */
  public void nodeAdded(String sourceId, long timeId, String nodeId) {
    listeners.nodeAdded(sourceId, timeId, nodeId);
  }

  /*
   * (non-Javadoc)
   *
   * @see org.graphstream.stream.ElementSink#nodeRemoved(java.lang.String,
   * long, java.lang.String)
   */
  public void nodeRemoved(String sourceId, long timeId, String nodeId) {
    listeners.nodeRemoved(sourceId, timeId, nodeId);
  }

  /*
   * (non-Javadoc)
   *
   * @see org.graphstream.stream.ElementSink#stepBegins(java.lang.String,
   * long, double)
   */
  public void stepBegins(String sourceId, long timeId, double time) {
    listeners.sendStepBegins(sourceId, timeId, time);
    stepBegins(time);
  }

  // Sprite interface

  protected void spriteAttribute(AttributeChangeEvent event, Element element,
      String attribute, Object value) {
    String spriteId = attribute.substring(10); // Remove the "ui.sprite."
                          // prefix.
    int pos = spriteId.indexOf('.'); // Look if there is something after the
                      // sprite id.
    String attr = null;

    if (pos > 0) {
      attr = spriteId.substring(pos + 1); // Cut the sprite id.
      spriteId = spriteId.substring(0, pos); // Cut the sprite attribute
                          // name.
    }

    if (attr == null) {
      addOrChangeSprite(event, element, spriteId, value);
    } else {
      if (event == AttributeChangeEvent.ADD) {
        GraphicSprite sprite = styleGroups.getSprite(spriteId);

        // We add the sprite, in case of a replay, some attributes of
        // the sprite can be
        // changed before the sprite is declared.
        if (sprite == null) {
          addOrChangeSprite(AttributeChangeEvent.ADD, element,
              spriteId, null);
          sprite = styleGroups.getSprite(spriteId);
        }

        sprite.addAttribute(attr, value);
      } else if (event == AttributeChangeEvent.CHANGE) {
        GraphicSprite sprite = styleGroups.getSprite(spriteId);

        if (sprite == null) {
          addOrChangeSprite(AttributeChangeEvent.ADD, element,
              spriteId, null);
          sprite = styleGroups.getSprite(spriteId);
        }

        sprite.changeAttribute(attr, value);
      } else if (event == AttributeChangeEvent.REMOVE) {
        GraphicSprite sprite = styleGroups.getSprite(spriteId);

        if (sprite != null)
          sprite.removeAttribute(attr);
      }
    }
  }

  protected void addOrChangeSprite(AttributeChangeEvent event,
      Element element, String spriteId, Object value) {

    if (event == AttributeChangeEvent.ADD
        || event == AttributeChangeEvent.CHANGE) {
      GraphicSprite sprite = styleGroups.getSprite(spriteId);

      if (sprite == null)
        sprite = addSprite_(spriteId);

      if (element != null) {
        if (element instanceof GraphicNode)
          sprite.attachToNode((GraphicNode) element);
        else if (element instanceof GraphicEdge)
          sprite.attachToEdge((GraphicEdge) element);
      }

      if (value != null && (!(value instanceof Boolean)))
        positionSprite(sprite, value);
    } else if (event == AttributeChangeEvent.REMOVE) {
      if (element == null) {
        if (styleGroups.getSprite(spriteId) != null) {
          removeSprite_(spriteId);
        }
      } else {
        GraphicSprite sprite = styleGroups.getSprite(spriteId);

        if (sprite != null)
          sprite.detach();
      }
    }
  }

  public GraphicSprite addSprite(String id) {
    String prefix = String.format("ui.sprite.%s", id);
    logger.info(String.format("Added sprite %s.", id));
    addAttribute(prefix, 0, 0, 0);
    GraphicSprite s = styleGroups.getSprite(id);
    assert (s != null);
    return s;
  }

  protected GraphicSprite addSprite_(String id) {
    GraphicSprite s = new GraphicSprite(id, this);
    styleGroups.addElement(s);
    graphChanged = true;

    return s;
  }

  public void removeSprite(String id) {
    String prefix = String.format("ui.sprite.%s", id);
    removeAttribute(prefix);
  }

  protected GraphicSprite removeSprite_(String id) {
    GraphicSprite sprite = (GraphicSprite) styleGroups.getSprite(id);

    if (sprite != null) {
      sprite.detach();
      styleGroups.removeElement(sprite);
      sprite.removed();

      graphChanged = true;
    }

    return sprite;
  }

  protected void positionSprite(GraphicSprite sprite, Object value) {
    if (value instanceof Object[]) {
      Object[] values = (Object[]) value;

      if (values.length == 4) {
        if (values[0] instanceof Number && values[1] instanceof Number
            && values[2] instanceof Number
            && values[3] instanceof Style.Units) {
          sprite.setPosition(((Number) values[0]).doubleValue(),
              ((Number) values[1]).doubleValue(),
              ((Number) values[2]).doubleValue(),
              (Style.Units) values[3]);
        } else {
          logger.warning("Cannot parse values[4] for sprite position.");
        }
      } else if (values.length == 3) {
        if (values[0] instanceof Number && values[1] instanceof Number
            && values[2] instanceof Number) {
          sprite.setPosition(((Number) values[0]).doubleValue(),
              ((Number) values[1]).doubleValue(),
              ((Number) values[2]).doubleValue(), Units.GU);
        } else {
          logger.warning("Cannot parse values[3] for sprite position.");
        }
      } else if (values.length == 1) {
        if (values[0] instanceof Number) {
          sprite.setPosition(((Number) values[0]).doubleValue());
        } else {
          logger.warning("Sprite position percent is not a number.");
        }
      } else {
        logger.warning(String
            .format("Cannot transform value '%s' (length=%d) into a position%n",
                Arrays.toString(values), values.length));
      }
    } else if (value instanceof Number) {
      sprite.setPosition(((Number) value).doubleValue());
    } else if (value instanceof Value) {
      sprite.setPosition(((Value) value).value);
    } else if (value instanceof Values) {
      sprite.setPosition((Values) value);
    } else if (value == null) {
      throw new RuntimeException("What do you expect with a null value ?");
    } else {
      logger.warning(String
          .format("Cannot place sprite with posiiton '%s' (instance of %s)%n",
              value, value.getClass().getName()));
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see org.graphstream.stream.Source#clearAttributeSinks()
   */
  public void clearAttributeSinks() {
    listeners.clearAttributeSinks();
  }

  /*
   * *(non-Javadoc)
   *
   * @see org.graphstream.stream.Source#clearElementSinks()
   */
  public void clearElementSinks() {
    listeners.clearElementSinks();
  }

  /*
   * (non-Javadoc)
   *
   * @see org.graphstream.stream.Source#clearSinks()
   */
  public void clearSinks() {
    listeners.clearSinks();
  }

  // stubs for the new methods

  public <T extends Edge> T addEdge(String id, int index1, int index2) {
    throw new RuntimeException("not implemented !");
  }

  public <T extends Edge> T addEdge(String id, int fromIndex, int toIndex,
      boolean directed) {
    throw new RuntimeException("not implemented !");
  }

  public <T extends Edge> T addEdge(String id, Node node1, Node node2) {
    throw new RuntimeException("not implemented !");
  }

  public <T extends Edge> T addEdge(String id, Node from, Node to,
      boolean directed) {
    throw new RuntimeException("not implemented !");
  }

  public <T extends Edge> T getEdge(int index)
      throws IndexOutOfBoundsException {
    throw new RuntimeException("not implemented !");
  }

  public <T extends Node> T getNode(int index)
      throws IndexOutOfBoundsException {
    throw new RuntimeException("not implemented !");
  }

  public <T extends Edge> T removeEdge(int index) {
    throw new RuntimeException("not implemented !");
  }

  public <T extends Edge> T removeEdge(int fromIndex, int toIndex) {
    throw new RuntimeException("not implemented !");
  }

  public <T extends Edge> T removeEdge(Node node1, Node node2) {
    throw new RuntimeException("not implemented !");
  }

  public <T extends Edge> T removeEdge(Edge edge) {
    throw new RuntimeException("not implemented !");
  }

  public <T extends Node> T removeNode(int index) {
    throw new RuntimeException("not implemented !");
  }

  public <T extends Node> T removeNode(Node node) {
    throw new RuntimeException("not implemented !");
  }

  /**
   * Replay all the elements of the graph and all attributes as new events to
   * all connected sinks.
   *
   * Be very careful with this method, it introduces new events in the event
   * stream and some sinks may therefore receive them twice !! Graph replay is
   * always dangerous !
   */
  public void replay() {
    // Replay all graph attributes.

    if (getAttributeKeySet() != null)
      for (String key : getAttributeKeySet()) {
        listeners.sendGraphAttributeAdded(id, key, getAttribute(key));
      }

    // Replay all nodes and their attributes.

    for (Node node : this) {
      listeners.sendNodeAdded(id, node.getId());

      if (node.getAttributeKeySet() != null) {
        for (String key : node.getAttributeKeySet()) {
          listeners.sendNodeAttributeAdded(id, node.getId(), key,
              node.getAttribute(key));
        }
      }
    }

    // Replay all edges and their attributes.

    for (Edge edge : getEachEdge()) {
      listeners.sendEdgeAdded(id, edge.getId(), edge.getSourceNode()
          .getId(), edge.getTargetNode().getId(), edge.isDirected());

      if (edge.getAttributeKeySet() != null) {
        for (String key : edge.getAttributeKeySet()) {
          listeners.sendEdgeAttributeAdded(id, edge.getId(), key,
              edge.getAttribute(key));
        }
      }
    }
  }
}
TOP

Related Classes of org.graphstream.ui.graphicGraph.GraphicGraph

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.