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.FromType;
025 import org.apache.camel.model.InterceptorRef;
026 import org.apache.camel.model.MulticastType;
027 import org.apache.camel.model.ProcessorType;
028 import org.apache.camel.model.RouteType;
029 import org.apache.camel.model.PipelineType;
030 import org.apache.camel.model.ToType;
031
032 import static org.apache.camel.util.ObjectHelper.isNotNullAndNonEmpty;
033
034
035 /**
036 * A <a href="http://www.graphviz.org/">DOT</a> file creator plugin which
037 * creates a DOT file showing the current routes
038 *
039 * @version $Revision: 56920 $
040 */
041 public class RouteDotGenerator extends GraphGeneratorSupport {
042
043 public RouteDotGenerator(String dir) {
044 super(dir, ".dot");
045 }
046
047 // Implementation methods
048 //-------------------------------------------------------------------------
049
050 protected void printRoutes(PrintWriter writer, Map<String, List<RouteType>> map) {
051 Set<Map.Entry<String, List<RouteType>>> entries = map.entrySet();
052 for (Map.Entry<String, List<RouteType>> entry : entries) {
053 String group = entry.getKey();
054 printRoutes(writer, group, entry.getValue());
055 }
056 }
057
058 protected void printRoutes(PrintWriter writer, String group, List<RouteType> routes) {
059 if (group != null) {
060 writer.println("subgraph cluster_" + (clusterCounter++) + " {");
061 writer.println("label = \"" + group + "\";");
062 writer.println("color = grey;");
063 writer.println("style = \"dashed\";");
064 writer.println("URL = \"" + group + ".html\";");
065 writer.println();
066 }
067 for (RouteType route : routes) {
068 List<FromType> inputs = route.getInputs();
069 for (FromType input : inputs) {
070 printRoute(writer, route, input);
071 }
072 writer.println();
073 }
074 if (group != null) {
075 writer.println("}");
076 writer.println();
077 }
078 }
079
080 protected String escapeNodeId(String text) {
081 return text.replace('.', '_').replace("$", "_");
082 }
083
084 protected void printRoute(PrintWriter writer, final RouteType route, FromType input) {
085 NodeData nodeData = getNodeData(input);
086
087 printNode(writer, nodeData);
088
089 // TODO we should add a transactional client / event driven consumer / polling client
090
091 NodeData from = nodeData;
092 for (ProcessorType output : route.getOutputs()) {
093 NodeData newData = printNode(writer, from, output);
094 from = newData;
095 }
096 }
097
098 protected NodeData printNode(PrintWriter writer, NodeData fromData, ProcessorType node) {
099 if (node instanceof MulticastType || node instanceof InterceptorRef) {
100 // no need for a multicast or interceptor node
101 List<ProcessorType> outputs = node.getOutputs();
102 boolean isPipeline = isPipeline(node);
103 for (ProcessorType output : outputs) {
104 NodeData out = printNode(writer, fromData, output);
105 // if in pipeline then we should move the from node to the next in the pipeline
106 if (isPipeline) {
107 fromData = out;
108 }
109 }
110 return fromData;
111 }
112 NodeData toData = getNodeData(node);
113
114 printNode(writer, toData);
115
116 if (fromData != null) {
117 writer.print(fromData.id);
118 writer.print(" -> ");
119 writer.print(toData.id);
120 writer.println(" [");
121
122 String label = fromData.edgeLabel;
123 if (isNotNullAndNonEmpty(label)) {
124 writer.println("label = \"" + label + "\"");
125 }
126 writer.println("];");
127 }
128
129 // now lets write any children
130 //List<ProcessorType> outputs = node.getOutputs();
131 List<ProcessorType> outputs = toData.outputs;
132 if (outputs != null) {
133 for (ProcessorType output : outputs) {
134 NodeData newData = printNode(writer, toData, output);
135 if (!isMulticastNode(node)) {
136 toData = newData;
137 }
138 }
139 }
140 return toData;
141 }
142
143 protected void printNode(PrintWriter writer, NodeData data) {
144 if (!data.nodeWritten) {
145 data.nodeWritten = true;
146
147 writer.println();
148 writer.print(data.id);
149 writer.println(" [");
150 writer.println("label = \"" + data.label + "\"");
151 writer.println("tooltip = \"" + data.tooltop + "\"");
152 if (data.url != null) {
153 writer.println("URL = \"" + data.url + "\"");
154 }
155
156 String image = data.image;
157 if (image != null) {
158 writer.println("shapefile = \"" + image + "\"");
159 writer.println("peripheries=0");
160 }
161 String shape = data.shape;
162 if (shape == null && image != null) {
163 shape = "custom";
164 }
165 if (shape != null) {
166 writer.println("shape = \"" + shape + "\"");
167 }
168 writer.println("];");
169 writer.println();
170 }
171 }
172
173 protected void generateFile(PrintWriter writer, Map<String, List<RouteType>> map) {
174 writer.println("digraph CamelRoutes {");
175 writer.println();
176
177 writer.println("node [style = \"rounded,filled\", fillcolor = yellow, "
178 + "fontname=\"Helvetica-Oblique\"];");
179 writer.println();
180 printRoutes(writer, map);
181
182 writer.println("}");
183 }
184
185 /**
186 * Is the given node a pipeline
187 */
188 private static boolean isPipeline(ProcessorType node) {
189 if (node instanceof MulticastType) {
190 return false;
191 }
192 if (node instanceof PipelineType) {
193 return true;
194 }
195 if (node.getOutputs().size() > 1) {
196 // is pipeline if there is more than 1 output and they are all To types
197 for (Object type : node.getOutputs()) {
198 if (!(type instanceof ToType)) {
199 return false;
200 }
201 }
202 return true;
203 }
204 return false;
205 }
206
207 }