001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.reef.tang.util.walk.graphviz;
020
021import org.apache.reef.tang.implementation.Constructor;
022import org.apache.reef.tang.implementation.InjectionPlan;
023import org.apache.reef.tang.implementation.Subplan;
024import org.apache.reef.tang.implementation.java.JavaInstance;
025import org.apache.reef.tang.util.walk.AbstractInjectionPlanNodeVisitor;
026import org.apache.reef.tang.util.walk.EdgeVisitor;
027import org.apache.reef.tang.util.walk.Walk;
028
029/**
030 * Build a Graphviz representation of the injection plan graph.
031 */
032public final class GraphvizInjectionPlanVisitor
033    extends AbstractInjectionPlanNodeVisitor implements EdgeVisitor<InjectionPlan<?>> {
034  /**
035   * Legend for the configuration graph in Graphviz format.
036   */
037  private static final String LEGEND =
038      "  subgraph cluster_legend {\n"
039          + "    shape=box;\n"
040          + "    label=\"Legend\";\n"
041          + "    Constructor [shape=box];\n"
042          + "    JavaInstance [shape=box, style=bold];\n"
043          + "    Subplan [shape=oval, style=dashed];\n"
044          + "    RequiredSingleton [shape=box, style=filled];\n"
045          + "    Subplan -> Constructor -> RequiredSingleton -> JavaInstance [style=invis];\n"
046          + "  }\n";
047
048  /**
049   * Accumulate string representation of the graph here.
050   */
051  private final transient StringBuilder graphStr =
052      new StringBuilder("digraph InjectionPlanMain {\n");
053
054  /**
055   * Create a new visitor to build a graphviz string for the injection plan.
056   *
057   * @param showLegend if true, show legend on the graph.
058   */
059  public GraphvizInjectionPlanVisitor(final boolean showLegend) {
060    if (showLegend) {
061      this.graphStr.append(LEGEND);
062    }
063    this.graphStr.append("subgraph cluster_main {\n  style=invis;\n");
064  }
065
066  /**
067   * Produce a Graphviz DOT string for a given TANG injection plan.
068   *
069   * @param injectionPlan TANG injection plan.
070   * @param showLegend    if true, show legend on the graph.
071   * @return Injection plan represented as a string in Graphviz DOT format.
072   */
073  public static String getGraphvizString(
074      final InjectionPlan<?> injectionPlan, final boolean showLegend) {
075    final GraphvizInjectionPlanVisitor visitor = new GraphvizInjectionPlanVisitor(showLegend);
076    Walk.preorder(visitor, visitor, injectionPlan);
077    return visitor.toString();
078  }
079
080  /**
081   * Process current injection plan node of Constructor type.
082   *
083   * @param node Current injection plan node.
084   * @return true to proceed with the next node, false to cancel.
085   */
086  @Override
087  public boolean visit(final Constructor<?> node) {
088    this.graphStr
089        .append("  \"")
090        .append(node.getClass())
091        .append('_')
092        .append(node.getNode().getName())
093        .append("\" [label=\"")
094        .append(node.getNode().getName())
095        .append("\", shape=box];\n");
096    return true;
097  }
098
099  /**
100   * Process current injection plan node of JavaInstance type.
101   *
102   * @param node Current injection plan node.
103   * @return true to proceed with the next node, false to cancel.
104   */
105  @Override
106  public boolean visit(final JavaInstance<?> node) {
107    this.graphStr
108        .append("  \"")
109        .append(node.getClass())
110        .append('_')
111        .append(node.getNode().getName())
112        .append("\" [label=\"")
113        .append(node.getNode().getName())
114        .append(" = ")
115        .append(node.getInstanceAsString())
116        .append("\", shape=box, style=bold];\n");
117    return true;
118  }
119
120  /**
121   * Process current injection plan node of Subplan type.
122   *
123   * @param node Current injection plan node.
124   * @return true to proceed with the next node, false to cancel.
125   */
126  @Override
127  public boolean visit(final Subplan<?> node) {
128    this.graphStr
129        .append("  \"")
130        .append(node.getClass())
131        .append('_')
132        .append(node.getNode().getName())
133        .append("\" [label=\"")
134        .append(node.getNode().getName())
135        .append("\", shape=oval, style=dashed];\n");
136    return true;
137  }
138
139  /**
140   * Process current edge of the injection plan.
141   *
142   * @param nodeFrom Current injection plan node.
143   * @param nodeTo   Destination injection plan node.
144   * @return true to proceed with the next node, false to cancel.
145   */
146  @Override
147  public boolean visit(final InjectionPlan<?> nodeFrom, final InjectionPlan<?> nodeTo) {
148    this.graphStr
149        .append("  \"")
150        .append(nodeFrom.getClass())
151        .append('_')
152        .append(nodeFrom.getNode().getName())
153        .append("\" -> \"")
154        .append(nodeTo.getClass())
155        .append('_')
156        .append(nodeTo.getNode().getName())
157        .append("\" [style=solid];\n");
158    return true;
159  }
160
161  /**
162   * @return TANG injection plan represented as a Graphviz DOT string.
163   */
164  @Override
165  public String toString() {
166    return this.graphStr.toString() + "}}\n";
167  }
168}