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    }