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.isNullOrBlank; 029 030 /** 031 * @version $Revision: 382 $ 032 */ 033 public class XmlGraphGenerator extends GraphGeneratorSupport { 034 private boolean addUrl = true; 035 036 public XmlGraphGenerator(String dir) { 037 super(dir, ".xml"); 038 } 039 040 protected void generateFile(PrintWriter writer, Map<String, List<RouteType>> map) { 041 writer.println("<?xml version='1.0' encoding='UTF-8'?>"); 042 writer.println("<Graph>"); 043 writer.println(); 044 045 if (map.size() > 0) { 046 writer.println("<Node id='root' name='Camel Routes' description='Collection of Camel Routes' nodeType='root'/>"); 047 } 048 printRoutes(writer, map); 049 050 writer.println(); 051 writer.println("</Graph>"); 052 } 053 054 protected void printRoutes(PrintWriter writer, Map<String, List<RouteType>> map) { 055 Set<Map.Entry<String, List<RouteType>>> entries = map.entrySet(); 056 for (Map.Entry<String, List<RouteType>> entry : entries) { 057 String group = entry.getKey(); 058 printRoutes(writer, group, entry.getValue()); 059 } 060 } 061 062 protected void printRoutes(PrintWriter writer, String group, List<RouteType> routes) { 063 group = encode(group); 064 if (group != null) { 065 int idx = group.lastIndexOf('.'); 066 String name = group; 067 if (idx > 0 && idx < group.length() - 1) { 068 name = group.substring(idx + 1); 069 } 070 writer.println("<Node id='" + group + "' name='" + name + "' description='" + group + "' nodeType='group'/>"); 071 writer.println("<Edge fromID='root' toID='" + group + "'/>"); 072 } 073 for (RouteType route : routes) { 074 List<FromType> inputs = route.getInputs(); 075 boolean first = true; 076 for (FromType input : inputs) { 077 NodeData nodeData = getNodeData(input); 078 if (first) { 079 first = false; 080 if (group != null) { 081 writer.println("<Edge fromID='" + group + "' toID='" + encode(nodeData.id) + "'/>"); 082 } 083 } 084 printRoute(writer, route, nodeData); 085 } 086 writer.println(); 087 } 088 } 089 090 protected void printRoute(PrintWriter writer, final RouteType route, NodeData nodeData) { 091 printNode(writer, nodeData); 092 093 // TODO we should add a transactional client / event driven consumer / polling client 094 095 NodeData from = nodeData; 096 for (ProcessorType output : route.getOutputs()) { 097 NodeData newData = printNode(writer, from, output); 098 from = newData; 099 } 100 } 101 102 protected NodeData printNode(PrintWriter writer, NodeData fromData, ProcessorType node) { 103 if (node instanceof MulticastType) { 104 // no need for a multicast node 105 List<ProcessorType> outputs = node.getOutputs(); 106 for (ProcessorType output : outputs) { 107 printNode(writer, fromData, output); 108 } 109 return fromData; 110 } 111 NodeData toData = getNodeData(node); 112 113 printNode(writer, toData); 114 115 if (fromData != null) { 116 writer.print("<Edge fromID=\""); 117 writer.print(encode(fromData.id)); 118 writer.print("\" toID=\""); 119 writer.print(encode(toData.id)); 120 String association = toData.edgeLabel; 121 if (isNullOrBlank(association)) { 122 writer.print("\" association=\""); 123 writer.print(encode(association)); 124 } 125 writer.println("\"/>"); 126 } 127 128 // now lets write any children 129 List<ProcessorType> outputs = toData.outputs; 130 if (outputs != null) { 131 for (ProcessorType output : outputs) { 132 NodeData newData = printNode(writer, toData, output); 133 if (!isMulticastNode(node)) { 134 toData = newData; 135 } 136 } 137 } 138 return toData; 139 } 140 141 protected void printNode(PrintWriter writer, NodeData data) { 142 if (!data.nodeWritten) { 143 data.nodeWritten = true; 144 145 writer.println(); 146 writer.print("<Node id=\""); 147 writer.print(encode(data.id)); 148 writer.print("\" name=\""); 149 String name = data.label; 150 if (isNullOrBlank(name)) { 151 name = data.tooltop; 152 } 153 writer.print(encode(name)); 154 writer.print("\" nodeType=\""); 155 String nodeType = data.image; 156 if (isNullOrBlank(nodeType)) { 157 nodeType = data.shape; 158 if (isNullOrBlank(nodeType)) { 159 nodeType = "node"; 160 } 161 } 162 writer.print(encode(nodeType)); 163 writer.print("\" description=\""); 164 writer.print(encode(data.tooltop)); 165 if (addUrl) { 166 writer.print("\" url=\""); 167 writer.print(encode(data.url)); 168 } 169 writer.println("\"/>"); 170 } 171 } 172 173 protected String encode(String text) { 174 if (text == null) { 175 return ""; 176 } 177 return text.replaceAll("\"", """).replaceAll("<", "<"). 178 replaceAll(">", ">").replaceAll("&", "&"); 179 } 180 }