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