This project has retired. For details please refer to its Attic page.
Source code
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.Configuration;
022import org.apache.reef.tang.types.ClassNode;
023import org.apache.reef.tang.types.NamedParameterNode;
024import org.apache.reef.tang.types.Node;
025import org.apache.reef.tang.types.PackageNode;
026import org.apache.reef.tang.util.walk.AbstractClassHierarchyNodeVisitor;
027import org.apache.reef.tang.util.walk.EdgeVisitor;
028import org.apache.reef.tang.util.walk.Walk;
029
030/**
031 * Build a Graphviz representation of the configuration graph.
032 */
033public final class GraphvizConfigVisitor
034    extends AbstractClassHierarchyNodeVisitor implements EdgeVisitor<Node> {
035
036  /**
037   * Legend for the configuration graph in Graphviz format.
038   */
039  private static final String LEGEND =
040      "  subgraph cluster_legend {\n"
041          + "    label=\"Legend\";\n"
042          + "    shape=box;\n"
043          + "    subgraph cluster_1 {\n"
044          + "      style=invis; label=\"\";\n"
045          + "      ex1l [shape=point, label=\"\"]; ex1r [shape=point, label=\"\"];\n"
046          + "      ex2l [shape=point, label=\"\"]; ex2r [shape=point, label=\"\"];\n"
047          + "      ex3l [shape=point, label=\"\"]; ex3r [shape=point, label=\"\"];\n"
048          + "      ex4l [shape=point, label=\"\"]; ex4r [shape=point, label=\"\"];\n"
049          + "      ex1l -> ex1r [style=solid, dir=back, arrowtail=diamond, label=\"contains\"];\n"
050          + "      ex2l -> ex2r [style=dashed, dir=back, arrowtail=empty, label=\"implements\"];\n"
051          + "      ex3l -> ex3r [style=\"dashed,bold\", dir=back, arrowtail=empty, label=\"external\"];\n"
052          + "      ex4l -> ex4r [style=solid, dir=back, arrowtail=normal, label=\"binds\"];\n"
053          + "    }\n"
054          + "    subgraph cluster_2 {\n"
055          + "      style=invis; label=\"\";\n"
056          + "      PackageNode [shape=folder];\n"
057          + "      ClassNode [shape=box];\n"
058          + "      Singleton [shape=box, style=filled];\n"
059          + "      NamedParameterNode [shape=oval];\n"
060          + "    }\n"
061          + "  }\n";
062
063  /**
064   * Accumulate string representation of the graph here.
065   */
066  private final transient StringBuilder graphStr = new StringBuilder(
067      "digraph ConfigMain {\n"
068          + "  rankdir=LR;\n");
069
070  /**
071   * Entire TANG configuration object.
072   */
073  private final transient Configuration config;
074
075  /**
076   * If true, plot IS-A edges for know implementations.
077   */
078  private final transient boolean showImpl;
079
080  /**
081   * Create a new TANG configuration visitor.
082   *
083   * @param config     Entire TANG configuration object.
084   * @param showImpl   If true, plot IS-A edges for know implementations.
085   * @param showLegend If true, add legend to the plot.
086   */
087  public GraphvizConfigVisitor(final Configuration config,
088                               final boolean showImpl, final boolean showLegend) {
089    super();
090    this.config = config;
091    this.showImpl = showImpl;
092    if (showLegend) {
093      this.graphStr.append(LEGEND);
094    }
095  }
096
097  /**
098   * Produce a Graphviz DOT string for a given TANG configuration.
099   *
100   * @param config     TANG configuration object.
101   * @param showImpl   If true, plot IS-A edges for know implementations.
102   * @param showLegend If true, add legend to the plot.
103   * @return configuration graph represented as a string in Graphviz DOT format.
104   */
105  public static String getGraphvizString(final Configuration config,
106                                         final boolean showImpl, final boolean showLegend) {
107    final GraphvizConfigVisitor visitor = new GraphvizConfigVisitor(config, showImpl, showLegend);
108    final Node root = config.getClassHierarchy().getNamespace();
109    Walk.preorder(visitor, visitor, root);
110    return visitor.toString();
111  }
112
113  /**
114   * @return TANG configuration represented as a Graphviz DOT string.
115   */
116  @Override
117  public String toString() {
118    return this.graphStr.toString() + "}\n";
119  }
120
121  /**
122   * Process current class configuration node.
123   *
124   * @param node Current configuration node.
125   * @return true to proceed with the next node, false to cancel.
126   */
127  @Override
128  public boolean visit(final ClassNode<?> node) {
129
130    this.graphStr
131        .append("  ")
132        .append(node.getName())
133        .append(" [label=\"")
134        .append(node.getName())
135        .append("\", shape=box")
136//            .append(config.isSingleton(node) ? ", style=filled" : "")
137        .append("];\n");
138
139    final ClassNode<?> boundImplNode = config.getBoundImplementation(node);
140    if (boundImplNode != null) {
141      this.graphStr
142          .append("  ")
143          .append(node.getName())
144          .append(" -> ")
145          .append(boundImplNode.getName())
146          .append(" [style=solid, dir=back, arrowtail=normal];\n");
147    }
148
149    for (final Object implNodeObj : node.getKnownImplementations()) {
150      final ClassNode<?> implNode = (ClassNode<?>) implNodeObj;
151      if (implNode != boundImplNode && implNode != node
152          && (implNode.isExternalConstructor() || this.showImpl)) {
153        this.graphStr
154            .append("  ")
155            .append(node.getName())
156            .append(" -> ")
157            .append(implNode.getName())
158            .append(" [style=\"dashed")
159            .append(implNode.isExternalConstructor() ? ",bold" : "")
160            .append("\", dir=back, arrowtail=empty];\n");
161      }
162    }
163
164    return true;
165  }
166
167  /**
168   * Process current package configuration node.
169   *
170   * @param node Current configuration node.
171   * @return true to proceed with the next node, false to cancel.
172   */
173  @Override
174  public boolean visit(final PackageNode node) {
175    if (!node.getName().isEmpty()) {
176      this.graphStr
177          .append("  ")
178          .append(node.getName())
179          .append(" [label=\"")
180          .append(node.getFullName())
181          .append("\", shape=folder];\n");
182    }
183    return true;
184  }
185
186  /**
187   * Process current configuration node for the named parameter.
188   *
189   * @param node Current configuration node.
190   * @return true to proceed with the next node, false to cancel.
191   */
192  @Override
193  public boolean visit(final NamedParameterNode<?> node) {
194    this.graphStr
195        .append("  ")
196        .append(node.getName())
197        .append(" [label=\"")
198        .append(node.getSimpleArgName())           // parameter type, e.g. "Integer"
199        .append("\\n")
200        .append(node.getName())                    // short name, e.g. "NumberOfThreads"
201        .append(" = ")
202        .append(config.getNamedParameter(node))   // bound value, e.g. "16"
203        .append("\\n(default = ")
204        .append(instancesToString(node.getDefaultInstanceAsStrings())) // default value, e.g. "4"
205        .append(")\", shape=oval];\n");
206    return true;
207  }
208
209  private String instancesToString(final String[] s) {
210    if (s.length == 0) {
211      return "null";
212    } else if (s.length == 1) {
213      return s[0];
214    } else {
215      final StringBuffer sb = new StringBuffer("[" + s[0]);
216      for (int i = 1; i < s.length; i++) {
217        sb.append(",");
218        sb.append(s[i]);
219      }
220      sb.append("]");
221      return sb.toString();
222    }
223  }
224
225  /**
226   * Process current edge of the configuration graph.
227   *
228   * @param nodeFrom Current configuration node.
229   * @param nodeTo   Destination configuration node.
230   * @return true to proceed with the next node, false to cancel.
231   */
232  @Override
233  public boolean visit(final Node nodeFrom, final Node nodeTo) {
234    if (!nodeFrom.getName().isEmpty()) {
235      this.graphStr
236          .append("  ")
237          .append(nodeFrom.getName())
238          .append(" -> ")
239          .append(nodeTo.getName())
240          .append(" [style=solid, dir=back, arrowtail=diamond];\n");
241    }
242    return true;
243  }
244}