Package org.springframework.ide.eclipse.beans.ui.graph.model

Source Code of org.springframework.ide.eclipse.beans.ui.graph.model.Graph

/*******************************************************************************
* Copyright (c) 2004, 2010 Spring IDE Developers
* 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:
*     Spring IDE Developers - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.beans.ui.graph.model;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.Platform;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Insets;
import org.eclipse.draw2d.graph.DirectedGraph;
import org.eclipse.draw2d.graph.DirectedGraphLayout;
import org.eclipse.draw2d.graph.Edge;
import org.eclipse.draw2d.graph.EdgeList;
import org.eclipse.draw2d.graph.Node;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.graphics.Font;
import org.springframework.ide.eclipse.beans.core.BeansCorePlugin;
import org.springframework.ide.eclipse.beans.core.internal.model.BeansConnection;
import org.springframework.ide.eclipse.beans.core.internal.model.BeansConnection.BeanType;
import org.springframework.ide.eclipse.beans.core.internal.model.BeansModelUtils;
import org.springframework.ide.eclipse.beans.core.model.IBean;
import org.springframework.ide.eclipse.beans.core.model.IBeansComponent;
import org.springframework.ide.eclipse.beans.core.model.IBeansConfig;
import org.springframework.ide.eclipse.beans.core.model.IBeansConfigSet;
import org.springframework.ide.eclipse.beans.core.model.IBeansModelElement;
import org.springframework.ide.eclipse.beans.ui.BeansUIPlugin;
import org.springframework.ide.eclipse.beans.ui.graph.BeansGraphPlugin;
import org.springframework.ide.eclipse.beans.ui.graph.editor.GraphEditorInput;
import org.springframework.ide.eclipse.beans.ui.graph.figures.BeanFigure;
import org.springframework.ide.eclipse.core.SpringCoreUtils;
import org.springframework.ide.eclipse.core.model.IModelElement;

/**
* This class builds the graphical representation of the model data (given as {@link GraphEditorInput}) via GEF's
* {@link DirectedGraphLayout}.
* @author Torsten Juergeleit
* @author Christian Dupuis
*/
public class Graph implements IAdaptable {

  private static final String CLASS_ATTRIBUTE = "class";

  private static final String GRAPH_CONTENT_EXTENDER_EXTENSION_POINT = BeansGraphPlugin.PLUGIN_ID
      + ".graphContentExtender";

  /*
   * Max width of rows with orphan beans (unconnected beans) if no subgraph is available
   */
  private static final int MAX_ORPHAN_ROW_WIDTH = 600;

  /* Default amount of empty space to be left around a node */
  private static final Insets DEFAULT_PADDING = new Insets(16);

  private static final String ERROR_TITLE = "Graph.error.title";

  private GraphEditorInput input;

  private DirectedGraph graph;

  private Map<String, Bean> beans = new HashMap<String, Bean>();

  private List<Reference> beanReferences = new ArrayList<Reference>();

  private String elementId;

  private String contextId;

  public Graph() {
    graph = new DirectedGraph();
  }

  public Graph(GraphEditorInput input) {
    this.input = input;
    this.elementId = input.getElementId();
    this.contextId = input.getContextId();
  }

  /**
   * Initializes the embedded graph with nodes from GraphEditorInput's beans and edges from GraphEditorInput's bean
   * references.
   */
  @SuppressWarnings("unchecked")
  public void init() {

    createBeansMap();
    createReferences();
    extendGraphContent();

    graph = new DirectedGraph();

    for (Bean bean : beans.values()) {
      graph.nodes.add(bean);
    }

    for (Reference reference : beanReferences) {
      graph.edges.add(reference);
    }
  }

  public Object getAdapter(Class adapter) {
    return input.getAdapter(adapter);
  }

  protected Collection getBeans() {
    return beans.values();
  }

  protected Bean getBean(String name) {
    return (Bean) beans.get(name);
  }

  public List getNodes() {
    return graph.nodes;
  }

  @SuppressWarnings({ "unchecked", "deprecation" })
  public void layout(Font font) {

    // Iterate through all graph nodes (beans) to calculate label width
    Iterator beans = graph.nodes.iterator();
    while (beans.hasNext()) {
      Bean bean = (Bean) beans.next();

      // Calculate bean's dimension with a temporary bean figure
      BeanFigure dummy = new BeanFigure(bean);
      dummy.setFont(font);
      Dimension size = dummy.getPreferredSize();
      bean.width = size.width;
      bean.height = size.height;
      bean.preferredHeight = size.height;
    }

    // Remove all unreferenced single beans and connect all unreferenced
    // subgraphs with a temporary root bean
    Bean root = new Bean();
    graph.nodes.add(root);

    EdgeList rootEdges = new EdgeList();
    List<Bean> orphanBeans = new ArrayList<Bean>();
    beans = getBeans().iterator();
    while (beans.hasNext()) {
      Bean bean = (Bean) beans.next();
      if (bean.incoming.isEmpty() && bean.outgoing.isEmpty()) {
        orphanBeans.add(bean);
        graph.nodes.remove(bean);
      }
      else {
        Reference reference = new Reference(BeanType.STANDARD, root, bean, false);
        reference.weight = 0;
        rootEdges.add(reference);
        graph.edges.add(reference);
      }
    }

    // Calculate position of all beans in graph
    try {
      new DirectedGraphLayout().visit(graph);

      // Re-invert edges inverted while breaking cycles; this only seems to be required on earlier GEF versions
      if (!SpringCoreUtils.isEclipseSameOrNewer(3, 6)) {
        for (int i = 0; i < graph.edges.size(); i++) {
          Edge e = graph.edges.getEdge(i);
          if (e.isFeedback()) {
            e.invert();
          }
        }
      }

      // Remove temporary root and root edges
      for (int i = 0; i < rootEdges.size(); i++) {
        Edge e = rootEdges.getEdge(i);
        e.source.outgoing.remove(e);
        e.target.incoming.remove(e);
        graph.edges.remove(e);
      }
      graph.nodes.remove(root);

      // Re-align nodes and edges' bend points topmost vertical position
      int maxY = 0; // max height of graph
      int maxX = 0; // max width of graph
      int ranks = graph.ranks.size();
      if (ranks > 1) {
        int deltaY = graph.ranks.getRank(1).getNode(0).y;
        Iterator nodes = graph.nodes.iterator();
        while (nodes.hasNext()) {
          Bean node = (Bean) nodes.next();

          // Move node vertically and update max height
          node.y -= deltaY;
          if ((node.y + node.height) > maxY) {
            maxY = node.y + node.height;
          }

          // Update max width
          if ((node.x + node.width) > maxX) {
            maxX = node.x + node.width;
          }
        }
        Iterator edges = graph.edges.iterator();
        while (edges.hasNext()) {
          Edge edge = (Edge) edges.next();
          if (edge.vNodes != null) {
            Iterator points = edge.vNodes.iterator();
            while (points.hasNext()) {
              Node node = (Node) points.next();
              node.y -= deltaY;
            }
          }
        }
      }

      // Re-add all unconnected beans to the bottom of the graph
      int x = 0; // current horizontal position in current row
      int y = maxY; // current row
      if (maxY > 0) {
        y += DEFAULT_PADDING.getHeight();
      }
      if (maxX < MAX_ORPHAN_ROW_WIDTH) {
        maxX = MAX_ORPHAN_ROW_WIDTH;
      }
      maxY = 0; // max height of all figures in current row
      beans = orphanBeans.iterator();
      while (beans.hasNext()) {
        Bean bean = (Bean) beans.next();

        // If current row is filled then start new row
        if ((x + bean.width) > maxX) {
          bean.x = x = 0;
          bean.y = y += maxY + DEFAULT_PADDING.getHeight();
          maxY = bean.height;
        }
        else {
          bean.y = y;
          bean.x = x;
          if (bean.height > maxY) {
            maxY = bean.height;
          }
        }
        x += bean.width + DEFAULT_PADDING.getWidth();
        graph.nodes.add(bean);
      }
    }
    catch (RuntimeException e) {

      // If an error occured during layouting (graph contains cylces,
      // graph not fully connected, ...) then clear graph, invalidate
      // editor input (not saved when Eclipse is closed) and display an
      // error message
      graph = new DirectedGraph();
      input.setHasError(true);
      MessageDialog.openError(BeansGraphPlugin.getActiveWorkbenchWindow().getShell(),
          BeansGraphPlugin.getResourceString(ERROR_TITLE), e.getMessage());

    }
  }

  @SuppressWarnings("deprecation")
  protected void extendGraphContent() {
    if (BeansUIPlugin.getDefault().getPluginPreferences()
        .getBoolean(BeansUIPlugin.SHOULD_SHOW_EXTENDED_CONTENT_PREFERENCE_ID)) {
      IExtensionPoint point = Platform.getExtensionRegistry().getExtensionPoint(
          GRAPH_CONTENT_EXTENDER_EXTENSION_POINT);
      if (point != null) {
        for (IExtension extension : point.getExtensions()) {
          for (IConfigurationElement config : extension.getConfigurationElements()) {
            if (config.getAttribute(CLASS_ATTRIBUTE) != null) {
              try {
                Object provider = config.createExecutableExtension(CLASS_ATTRIBUTE);
                if (provider instanceof IGraphContentExtender) {
                  ((IGraphContentExtender) provider).addAdditionalBeans(beans, beanReferences,
                      (IBeansModelElement) getElement(elementId),
                      (IBeansModelElement) getElement(contextId));
                }
              }
              catch (CoreException e) {
                BeansGraphPlugin.log(e);
              }
            }
          }
        }
      }
    }
  }

  /**
   * Creates a list with all beans belonging to the specified config / config set or being connected with the
   * specified bean.
   */
  protected void createBeansMap() {
    Set<IBean> list = new LinkedHashSet<IBean>();
    if (getElement(elementId) instanceof IBeansConfig) {
      IBeansConfig bc = (IBeansConfig) getElement(elementId);
      list.addAll(bc.getBeans());
      // add component registered beans
      addBeansFromComponents(list, bc.getComponents());
    }
    else if (getElement(elementId) instanceof IBeansConfigSet) {
      IBeansConfigSet bcs = (IBeansConfigSet) getElement(elementId);
      list.addAll(bcs.getBeans());
      // add component registered beans
      addBeansFromComponents(list, bcs.getComponents());
    }
    else if (getElement(elementId) instanceof IBean) {
      list.add((IBean) getElement(elementId));
      for (BeansConnection beanRef : BeansModelUtils.getBeanReferences(getElement(elementId),
          getElement(contextId), true)) {
        if (beanRef.getType() != BeanType.INNER) {
          list.add(beanRef.getTarget());
        }
      }
    }

    // Marshall all beans into a graph bean node
    beans = new LinkedHashMap<String, Bean>();
    for (IBean bean : list) {
      if (shouldAddBean(bean)) {
        beans.put(bean.getElementName(), new Bean(bean));
      }
    }
  }

  @SuppressWarnings("deprecation")
  private boolean shouldAddBean(IBean bean) {
    return !bean.isInfrastructure()
        || (bean.isInfrastructure() && BeansUIPlugin.getDefault().getPluginPreferences()
            .getBoolean(BeansUIPlugin.SHOULD_SHOW_INFRASTRUCTURE_BEANS_PREFERENCE_ID));
  }

  private void addBeansFromComponents(Set<IBean> beans, Set<IBeansComponent> components) {
    for (IBeansComponent component : components) {
      Set<IBean> nestedBeans = component.getBeans();
      for (IBean nestedBean : nestedBeans) {
        if (shouldAddBean(nestedBean)) {
          beans.add(nestedBean);
        }
      }
      addBeansFromComponents(beans, component.getComponents());
    }
  }

  protected void createReferences() {
    beanReferences = new ArrayList<Reference>();
    // Add all beans defined in GraphEditorInput as nodes to the graph
    Iterator beans = this.beans.values().iterator();
    while (beans.hasNext()) {
      Bean bean = (Bean) beans.next();

      // Add all beans references from bean (parent, factory or
      // depends-on beans) to list of graph edges
      Iterator beanRefs = BeansModelUtils.getBeanReferences(bean.getBean(),
          BeansCorePlugin.getModel().getElement(contextId), false).iterator();
      while (beanRefs.hasNext()) {
        BeansConnection beanRef = (BeansConnection) beanRefs.next();
        Bean targetBean = this.beans.get(beanRef.getTarget().getElementName());
        if (targetBean != null && targetBean != bean && beanRef.getSource() instanceof IBean) {
          beanReferences.add(new Reference(beanRef.getType(), bean, targetBean, bean, beanRef.isInner()));
        }
      }

      // Add all bean references in bean's constructor arguments to list
      // of graph edges
      ConstructorArgument[] cargs = bean.getConstructorArguments();
      for (ConstructorArgument carg : cargs) {
        Iterator cargRefs = BeansModelUtils.getBeanReferences(carg.getBeanConstructorArgument(),
            BeansCorePlugin.getModel().getElement(contextId), false).iterator();
        while (cargRefs.hasNext()) {
          BeansConnection beanRef = (BeansConnection) cargRefs.next();
          Bean targetBean = this.beans.get(beanRef.getTarget().getElementName());
          if (targetBean != null && targetBean != bean) {
            beanReferences.add(new Reference(beanRef.getType(), bean, targetBean, carg, beanRef.isInner()));
          }
        }
      }

      // Add all bean references in properties to list of graph edges
      Property[] properties = bean.getProperties();
      for (Property property : properties) {
        Iterator propRefs = BeansModelUtils.getBeanReferences(property.getBeanProperty(),
            BeansCorePlugin.getModel().getElement(contextId), false).iterator();
        while (propRefs.hasNext()) {
          BeansConnection beanRef = (BeansConnection) propRefs.next();
          Bean targetBean = this.beans.get(beanRef.getTarget().getElementName());
          if (targetBean != null && targetBean != bean) {
            beanReferences.add(new Reference(beanRef.getType(), bean, targetBean, property, beanRef
                .isInner()));
          }
        }
      }
    }
  }

  private IModelElement getElement(String elementId) {
    return BeansCorePlugin.getModel().getElement(elementId);
  }

}
TOP

Related Classes of org.springframework.ide.eclipse.beans.ui.graph.model.Graph

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.