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 */ 017package org.apache.camel.model; 018 019import java.util.ArrayList; 020import java.util.Iterator; 021import java.util.LinkedHashSet; 022import java.util.List; 023import java.util.Set; 024 025import org.apache.camel.NamedNode; 026import org.slf4j.Logger; 027import org.slf4j.LoggerFactory; 028 029/** 030 * Helper class for ProcessorDefinition and the other model classes. 031 */ 032public final class ProcessorDefinitionHelper { 033 034 public static final String PREFIX = "{" + Constants.PLACEHOLDER_QNAME + "}"; 035 036 private static final Logger LOG = LoggerFactory.getLogger(ProcessorDefinitionHelper.class); 037 038 private ProcessorDefinitionHelper() { 039 } 040 041 /** 042 * Looks for the given type in the list of outputs and recurring all the 043 * children as well. 044 * 045 * @param outputs list of outputs, can be null or empty. 046 * @param type the type to look for 047 * @return the found definitions, or <tt>null</tt> if not found 048 */ 049 public static <T> Iterator<T> filterTypeInOutputs(List<ProcessorDefinition<?>> outputs, Class<T> type) { 050 return filterTypeInOutputs(outputs, type, -1); 051 } 052 053 /** 054 * Looks for the given type in the list of outputs and recurring all the 055 * children as well. 056 * 057 * @param outputs list of outputs, can be null or empty. 058 * @param type the type to look for 059 * @param maxDeep maximum levels deep to traverse 060 * @return the found definitions, or <tt>null</tt> if not found 061 */ 062 public static <T> Iterator<T> filterTypeInOutputs(List<ProcessorDefinition<?>> outputs, Class<T> type, int maxDeep) { 063 List<T> found = new ArrayList<>(); 064 doFindType(outputs, type, found, maxDeep); 065 return found.iterator(); 066 } 067 068 /** 069 * Looks for the given type in the list of outputs and recurring all the 070 * children as well. Will stop at first found and return it. 071 * 072 * @param outputs list of outputs, can be null or empty. 073 * @param type the type to look for 074 * @return the first found type, or <tt>null</tt> if not found 075 */ 076 public static <T> T findFirstTypeInOutputs(List<ProcessorDefinition<?>> outputs, Class<T> type) { 077 List<T> found = new ArrayList<>(); 078 doFindType(outputs, type, found, -1); 079 if (found.isEmpty()) { 080 return null; 081 } 082 return found.iterator().next(); 083 } 084 085 /** 086 * Is the given child the first in the outputs from the parent? 087 * 088 * @param parentType the type the parent must be 089 * @param node the node 090 * @return <tt>true</tt> if first child, <tt>false</tt> otherwise 091 */ 092 public static boolean isFirstChildOfType(Class<?> parentType, ProcessorDefinition<?> node) { 093 if (node == null || node.getParent() == null) { 094 return false; 095 } 096 097 if (node.getParent().getOutputs().isEmpty()) { 098 return false; 099 } 100 101 if (!(node.getParent().getClass().equals(parentType))) { 102 return false; 103 } 104 105 return node.getParent().getOutputs().get(0).equals(node); 106 } 107 108 /** 109 * Is the given node parent(s) of the given type 110 * 111 * @param parentType the parent type 112 * @param node the current node 113 * @param recursive whether or not to check grand parent(s) as well 114 * @return <tt>true</tt> if parent(s) is of given type, <tt>false</tt> 115 * otherwise 116 */ 117 public static boolean isParentOfType(Class<? extends ProcessorDefinition> parentType, ProcessorDefinition<?> node, boolean recursive) { 118 return findFirstParentOfType(parentType, node, recursive) != null; 119 } 120 121 /** 122 * Is the given node parent(s) of the given type 123 * 124 * @param parentType the parent type 125 * @param node the current node 126 * @param recursive whether or not to check grand parent(s) as well 127 * @return <tt>true</tt> if parent(s) is of given type, <tt>false</tt> 128 * otherwise 129 */ 130 public static <T extends ProcessorDefinition> T findFirstParentOfType(Class<T> parentType, ProcessorDefinition<?> node, boolean recursive) { 131 if (node == null || node.getParent() == null) { 132 return null; 133 } 134 135 if (parentType.isAssignableFrom(node.getParent().getClass())) { 136 return parentType.cast(node.getParent()); 137 } else if (recursive) { 138 // recursive up the tree of parents 139 return findFirstParentOfType(parentType, node.getParent(), true); 140 } else { 141 // no match 142 return null; 143 } 144 } 145 146 /** 147 * Gets the route definition the given node belongs to. 148 * 149 * @param node the node 150 * @return the route, or <tt>null</tt> if not possible to find 151 */ 152 public static RouteDefinition getRoute(NamedNode node) { 153 if (node == null) { 154 return null; 155 } 156 157 ProcessorDefinition<?> def = (ProcessorDefinition)node; 158 // drill to the top 159 while (def != null && def.getParent() != null) { 160 def = def.getParent(); 161 } 162 163 if (def instanceof RouteDefinition) { 164 return (RouteDefinition)def; 165 } else { 166 // not found 167 return null; 168 } 169 } 170 171 /** 172 * Gets the route id the given node belongs to. 173 * 174 * @param node the node 175 * @return the route id, or <tt>null</tt> if not possible to find 176 */ 177 public static String getRouteId(NamedNode node) { 178 RouteDefinition route = getRoute(node); 179 return route != null ? route.getId() : null; 180 } 181 182 /** 183 * Traverses the node, including its children (recursive), and gathers all 184 * the node ids. 185 * 186 * @param node the target node 187 * @param set set to store ids, if <tt>null</tt> a new set will be created 188 * @param onlyCustomId whether to only store custom assigned ids (ie. 189 * {@link org.apache.camel.model.OptionalIdentifiedDefinition#hasCustomIdAssigned()} 190 * @param includeAbstract whether to include abstract nodes (ie. 191 * {@link org.apache.camel.model.ProcessorDefinition#isAbstract()} 192 * @return the set with the found ids. 193 */ 194 public static Set<String> gatherAllNodeIds(ProcessorDefinition<?> node, Set<String> set, boolean onlyCustomId, boolean includeAbstract) { 195 if (node == null) { 196 return set; 197 } 198 199 // skip abstract 200 if (node.isAbstract() && !includeAbstract) { 201 return set; 202 } 203 204 if (set == null) { 205 set = new LinkedHashSet<>(); 206 } 207 208 // add ourselves 209 if (node.getId() != null) { 210 if (!onlyCustomId || node.hasCustomIdAssigned() && onlyCustomId) { 211 set.add(node.getId()); 212 } 213 } 214 215 // traverse outputs and recursive children as well 216 List<ProcessorDefinition<?>> children = node.getOutputs(); 217 if (children != null && !children.isEmpty()) { 218 for (ProcessorDefinition<?> child : children) { 219 // traverse children also 220 gatherAllNodeIds(child, set, onlyCustomId, includeAbstract); 221 } 222 } 223 224 return set; 225 } 226 227 private static <T> void doFindType(List<ProcessorDefinition<?>> outputs, Class<T> type, List<T> found, int maxDeep) { 228 229 // do we have any top level abstracts, then we should max deep one more 230 // level down 231 // as that is really what we want to traverse as well 232 if (maxDeep > 0) { 233 for (ProcessorDefinition<?> out : outputs) { 234 if (out.isAbstract() && out.isTopLevelOnly()) { 235 maxDeep = maxDeep + 1; 236 break; 237 } 238 } 239 } 240 241 // start from level 1 242 doFindType(outputs, type, found, 1, maxDeep); 243 } 244 245 @SuppressWarnings({"unchecked", "rawtypes"}) 246 private static <T> void doFindType(List<ProcessorDefinition<?>> outputs, Class<T> type, List<T> found, int current, int maxDeep) { 247 if (outputs == null || outputs.isEmpty()) { 248 return; 249 } 250 251 // break out 252 if (maxDeep > 0 && current > maxDeep) { 253 return; 254 } 255 256 for (ProcessorDefinition out : outputs) { 257 258 // send is much common 259 if (out instanceof SendDefinition) { 260 SendDefinition send = (SendDefinition)out; 261 List<ProcessorDefinition<?>> children = send.getOutputs(); 262 doFindType(children, type, found, ++current, maxDeep); 263 } 264 265 // special for choice 266 if (out instanceof ChoiceDefinition) { 267 ChoiceDefinition choice = (ChoiceDefinition)out; 268 269 // ensure to add ourself if we match also 270 if (type.isInstance(choice)) { 271 found.add((T)choice); 272 } 273 274 // only look at when/otherwise if current < maxDeep (or max deep 275 // is disabled) 276 if (maxDeep < 0 || current < maxDeep) { 277 for (WhenDefinition when : choice.getWhenClauses()) { 278 if (type.isInstance(when)) { 279 found.add((T)when); 280 } 281 List<ProcessorDefinition<?>> children = when.getOutputs(); 282 doFindType(children, type, found, ++current, maxDeep); 283 } 284 285 // otherwise is optional 286 if (choice.getOtherwise() != null) { 287 List<ProcessorDefinition<?>> children = choice.getOtherwise().getOutputs(); 288 doFindType(children, type, found, ++current, maxDeep); 289 } 290 } 291 292 // do not check children as we already did that 293 continue; 294 } 295 296 // special for try ... catch ... finally 297 if (out instanceof TryDefinition) { 298 TryDefinition doTry = (TryDefinition)out; 299 300 // ensure to add ourself if we match also 301 if (type.isInstance(doTry)) { 302 found.add((T)doTry); 303 } 304 305 // only look at children if current < maxDeep (or max deep is 306 // disabled) 307 if (maxDeep < 0 || current < maxDeep) { 308 List<ProcessorDefinition<?>> doTryOut = doTry.getOutputsWithoutCatches(); 309 doFindType(doTryOut, type, found, ++current, maxDeep); 310 311 List<CatchDefinition> doTryCatch = doTry.getCatchClauses(); 312 for (CatchDefinition doCatch : doTryCatch) { 313 doFindType(doCatch.getOutputs(), type, found, ++current, maxDeep); 314 } 315 316 if (doTry.getFinallyClause() != null) { 317 doFindType(doTry.getFinallyClause().getOutputs(), type, found, ++current, maxDeep); 318 } 319 } 320 321 // do not check children as we already did that 322 continue; 323 } 324 325 // special for some types which has special outputs 326 if (out instanceof OutputDefinition) { 327 OutputDefinition outDef = (OutputDefinition)out; 328 329 // ensure to add ourself if we match also 330 if (type.isInstance(outDef)) { 331 found.add((T)outDef); 332 } 333 334 List<ProcessorDefinition<?>> outDefOut = outDef.getOutputs(); 335 doFindType(outDefOut, type, found, ++current, maxDeep); 336 337 // do not check children as we already did that 338 continue; 339 } 340 341 if (type.isInstance(out)) { 342 found.add((T)out); 343 } 344 345 // try children as well 346 List<ProcessorDefinition<?>> children = out.getOutputs(); 347 doFindType(children, type, found, ++current, maxDeep); 348 } 349 } 350 351}