001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.camel.view;
018
019 import java.io.PrintWriter;
020 import java.util.List;
021 import java.util.Map;
022 import java.util.Set;
023
024 import org.apache.camel.model.FromDefinition;
025 import org.apache.camel.model.MulticastDefinition;
026 import org.apache.camel.model.PipelineDefinition;
027 import org.apache.camel.model.ProcessorDefinition;
028 import org.apache.camel.model.RouteDefinition;
029 import org.apache.camel.model.ToDefinition;
030
031 import static org.apache.camel.util.ObjectHelper.isNotEmpty;
032 /**
033 * A <a href="http://www.graphviz.org/">DOT</a> file creator plugin which
034 * creates a DOT file showing the current routes
035 *
036 * @version $Revision: 883288 $
037 */
038 public class RouteDotGenerator extends GraphGeneratorSupport {
039
040 public RouteDotGenerator(String dir) {
041 super(dir, ".dot");
042 }
043
044 // Implementation methods
045 //-------------------------------------------------------------------------
046
047 protected void printRoutes(PrintWriter writer, Map<String, List<RouteDefinition>> map) {
048 Set<Map.Entry<String, List<RouteDefinition>>> entries = map.entrySet();
049 for (Map.Entry<String, List<RouteDefinition>> entry : entries) {
050 String group = entry.getKey();
051 printRoutes(writer, group, entry.getValue());
052 }
053 }
054
055 protected void printRoutes(PrintWriter writer, String group, List<RouteDefinition> routes) {
056 if (group != null) {
057 writer.println("subgraph cluster_" + (clusterCounter++) + " {");
058 writer.println("label = \"" + group + "\";");
059 writer.println("color = grey;");
060 writer.println("style = \"dashed\";");
061 writer.println("URL = \"" + group + ".html\";");
062 writer.println();
063 }
064 for (RouteDefinition route : routes) {
065 List<FromDefinition> inputs = route.getInputs();
066 for (FromDefinition input : inputs) {
067 printRoute(writer, route, input);
068 }
069 writer.println();
070 }
071 if (group != null) {
072 writer.println("}");
073 writer.println();
074 }
075 }
076
077 protected void printRoute(PrintWriter writer, final RouteDefinition route, FromDefinition input) {
078 NodeData nodeData = getNodeData(input);
079
080 printNode(writer, nodeData);
081
082 NodeData from = nodeData;
083 for (ProcessorDefinition<?> output : route.getOutputs()) {
084 NodeData newData = printNode(writer, from, output);
085 from = newData;
086 }
087 }
088
089 @SuppressWarnings("unchecked")
090 protected NodeData printNode(PrintWriter writer, NodeData fromData, ProcessorDefinition node) {
091 if (node instanceof MulticastDefinition) {
092 // no need for a multicast or interceptor node
093 List<ProcessorDefinition> outputs = node.getOutputs();
094 boolean isPipeline = isPipeline(node);
095 for (ProcessorDefinition output : outputs) {
096 NodeData out = printNode(writer, fromData, output);
097 // if in pipeline then we should move the from node to the next in the pipeline
098 if (isPipeline) {
099 fromData = out;
100 }
101 }
102 return fromData;
103 }
104 NodeData toData = getNodeData(node);
105
106 printNode(writer, toData);
107
108 if (fromData != null) {
109 writer.print(fromData.id);
110 writer.print(" -> ");
111 writer.print(toData.id);
112 writer.println(" [");
113
114 String label = fromData.edgeLabel;
115 if (isNotEmpty(label)) {
116 writer.println("label = \"" + label + "\"");
117 }
118 writer.println("];");
119 }
120
121 // now lets write any children
122 List<ProcessorDefinition> outputs = toData.outputs;
123 if (outputs != null) {
124 for (ProcessorDefinition output : outputs) {
125 NodeData newData = printNode(writer, toData, output);
126 if (!isMulticastNode(node)) {
127 toData = newData;
128 }
129 }
130 }
131 return toData;
132 }
133
134 protected void printNode(PrintWriter writer, NodeData data) {
135 if (!data.nodeWritten) {
136 data.nodeWritten = true;
137
138 writer.println();
139 writer.print(data.id);
140 writer.println(" [");
141 writer.println("label = \"" + data.label + "\"");
142 writer.println("tooltip = \"" + data.tooltop + "\"");
143 if (data.url != null) {
144 writer.println("URL = \"" + data.url + "\"");
145 }
146
147 String image = data.image;
148 if (image != null) {
149 writer.println("shapefile = \"" + image + "\"");
150 writer.println("peripheries=0");
151 }
152 String shape = data.shape;
153 if (shape == null && image != null) {
154 shape = "custom";
155 }
156 if (shape != null) {
157 writer.println("shape = \"" + shape + "\"");
158 }
159 writer.println("];");
160 writer.println();
161 }
162 }
163
164 protected void generateFile(PrintWriter writer, Map<String, List<RouteDefinition>> map) {
165 writer.println("digraph CamelRoutes {");
166 writer.println();
167
168 writer.println("node [style = \"rounded,filled\", fillcolor = yellow, "
169 + "fontname=\"Helvetica-Oblique\"];");
170 writer.println();
171 printRoutes(writer, map);
172
173 writer.println("}");
174 }
175
176 /**
177 * Is the given node a pipeline
178 */
179 private static boolean isPipeline(ProcessorDefinition node) {
180 if (node instanceof MulticastDefinition) {
181 return false;
182 }
183 if (node instanceof PipelineDefinition) {
184 return true;
185 }
186 if (node.getOutputs().size() > 1) {
187 // is pipeline if there is more than 1 output and they are all To types
188 for (Object type : node.getOutputs()) {
189 if (!(type instanceof ToDefinition)) {
190 return false;
191 }
192 }
193 return true;
194 }
195 return false;
196 }
197
198 }