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.ProcessorDefinition;
027    import org.apache.camel.model.RouteDefinition;
028    import static org.apache.camel.util.ObjectHelper.isEmpty;
029    
030    /**
031     * @version $Revision: 750806 $
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<RouteDefinition>> 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<RouteDefinition>> map) {
055            Set<Map.Entry<String, List<RouteDefinition>>> entries = map.entrySet();
056            for (Map.Entry<String, List<RouteDefinition>> 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<RouteDefinition> 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 (RouteDefinition route : routes) {
074                List<FromDefinition> inputs = route.getInputs();
075                boolean first = true;
076                for (FromDefinition 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 RouteDefinition 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 (ProcessorDefinition output : route.getOutputs()) {
097                NodeData newData = printNode(writer, from, output);
098                from = newData;
099            }
100        }
101    
102        @SuppressWarnings("unchecked")
103        protected NodeData printNode(PrintWriter writer, NodeData fromData, ProcessorDefinition node) {
104            if (node instanceof MulticastDefinition) {
105                // no need for a multicast node
106                List<ProcessorDefinition> outputs = node.getOutputs();
107                for (ProcessorDefinition output : outputs) {
108                    printNode(writer, fromData, output);
109                }
110                return fromData;
111            }
112            NodeData toData = getNodeData(node);
113    
114            printNode(writer, toData);
115    
116            if (fromData != null) {
117                writer.print("<Edge fromID=\"");
118                writer.print(encode(fromData.id));
119                writer.print("\" toID=\"");
120                writer.print(encode(toData.id));
121                String association = toData.edgeLabel;
122                if (isEmpty(association)) {
123                    writer.print("\" association=\"");
124                    writer.print(encode(association));
125                }
126                writer.println("\"/>");
127            }
128    
129            // now lets write any children
130            List<ProcessorDefinition> outputs = toData.outputs;
131            if (outputs != null) {
132                for (ProcessorDefinition output : outputs) {
133                    NodeData newData = printNode(writer, toData, output);
134                    if (!isMulticastNode(node)) {
135                        toData = newData;
136                    }
137                }
138            }
139            return toData;
140        }
141    
142        protected void printNode(PrintWriter writer, NodeData data) {
143            if (!data.nodeWritten) {
144                data.nodeWritten = true;
145    
146                writer.println();
147                writer.print("<Node id=\"");
148                writer.print(encode(data.id));
149                writer.print("\" name=\"");
150                String name = data.label;
151                if (isEmpty(name)) {
152                    name = data.tooltop;
153                }
154                writer.print(encode(name));
155                writer.print("\" nodeType=\"");
156                String nodeType = data.image;
157                if (isEmpty(nodeType)) {
158                    nodeType = data.shape;
159                    if (isEmpty(nodeType)) {
160                        nodeType = "node";
161                    }
162                }
163                writer.print(encode(nodeType));
164                writer.print("\" description=\"");
165                writer.print(encode(data.tooltop));
166                if (addUrl) {
167                    writer.print("\" url=\"");
168                    writer.print(encode(data.url));
169                }
170                writer.println("\"/>");
171            }
172        }
173    
174        protected String encode(String text) {
175            if (text == null) {
176                return "";
177            }
178            return text.replaceAll("\"", "&quot;").replaceAll("<", "&lt;").
179                    replaceAll(">", "&gt;").replaceAll("&", "&amp;");
180        }
181    }