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.AbstractList; 020import java.util.ArrayList; 021import java.util.List; 022import java.util.stream.Collectors; 023 024import javax.xml.bind.annotation.XmlAccessType; 025import javax.xml.bind.annotation.XmlAccessorType; 026import javax.xml.bind.annotation.XmlElement; 027import javax.xml.bind.annotation.XmlElementRef; 028import javax.xml.bind.annotation.XmlRootElement; 029 030import org.apache.camel.ExpressionFactory; 031import org.apache.camel.Predicate; 032import org.apache.camel.builder.ExpressionClause; 033import org.apache.camel.model.language.ExpressionDefinition; 034import org.apache.camel.spi.AsPredicate; 035import org.apache.camel.spi.Metadata; 036import org.apache.camel.util.ObjectHelper; 037 038/** 039 * Routes messages based on a series of predicates 040 */ 041@Metadata(label = "eip,routing") 042@XmlRootElement(name = "choice") 043@XmlAccessorType(XmlAccessType.FIELD) 044public class ChoiceDefinition extends ProcessorDefinition<ChoiceDefinition> implements OutputNode { 045 @XmlElementRef 046 @AsPredicate 047 private List<WhenDefinition> whenClauses = new ArrayList<>(); 048 @XmlElement 049 private OtherwiseDefinition otherwise; 050 051 private transient boolean onlyWhenOrOtherwise = true; 052 053 public ChoiceDefinition() { 054 } 055 056 @Override 057 public List<ProcessorDefinition<?>> getOutputs() { 058 // wrap the outputs into a list where we can on the inside control the 059 // when/otherwise 060 // but make it appear as a list on the outside 061 return new AbstractList<ProcessorDefinition<?>>() { 062 063 public ProcessorDefinition<?> get(int index) { 064 if (index < whenClauses.size()) { 065 return whenClauses.get(index); 066 } 067 if (index == whenClauses.size()) { 068 return otherwise; 069 } 070 throw new IndexOutOfBoundsException("Index " + index + " is out of bounds with size " + size()); 071 } 072 073 public boolean add(ProcessorDefinition<?> def) { 074 if (def instanceof WhenDefinition) { 075 return whenClauses.add((WhenDefinition)def); 076 } else if (def instanceof OtherwiseDefinition) { 077 otherwise = (OtherwiseDefinition)def; 078 return true; 079 } 080 throw new IllegalArgumentException("Expected either a WhenDefinition or OtherwiseDefinition but was " + ObjectHelper.classCanonicalName(def)); 081 } 082 083 public int size() { 084 return whenClauses.size() + (otherwise == null ? 0 : 1); 085 } 086 087 public void clear() { 088 whenClauses.clear(); 089 otherwise = null; 090 } 091 092 public ProcessorDefinition<?> set(int index, ProcessorDefinition<?> element) { 093 if (index < whenClauses.size()) { 094 if (element instanceof WhenDefinition) { 095 return whenClauses.set(index, (WhenDefinition)element); 096 } 097 throw new IllegalArgumentException("Expected WhenDefinition but was " + ObjectHelper.classCanonicalName(element)); 098 } else if (index == whenClauses.size()) { 099 ProcessorDefinition<?> old = otherwise; 100 otherwise = (OtherwiseDefinition)element; 101 return old; 102 } 103 throw new IndexOutOfBoundsException("Index " + index + " is out of bounds with size " + size()); 104 } 105 106 public ProcessorDefinition<?> remove(int index) { 107 if (index < whenClauses.size()) { 108 return whenClauses.remove(index); 109 } else if (index == whenClauses.size()) { 110 ProcessorDefinition<?> old = otherwise; 111 otherwise = null; 112 return old; 113 } 114 throw new IndexOutOfBoundsException("Index " + index + " is out of bounds with size " + size()); 115 } 116 }; 117 } 118 119 @Override 120 public String toString() { 121 return "Choice[" + getWhenClauses() + (getOtherwise() != null ? " " + getOtherwise() : "") + "]"; 122 } 123 124 @Override 125 public void addOutput(ProcessorDefinition<?> output) { 126 if (onlyWhenOrOtherwise) { 127 if (output instanceof WhenDefinition || output instanceof OtherwiseDefinition) { 128 // okay we are adding a when or otherwise so allow any kind of 129 // output after this again 130 onlyWhenOrOtherwise = false; 131 } else { 132 throw new IllegalArgumentException("A new choice clause should start with a when() or otherwise(). " 133 + "If you intend to end the entire choice and are using endChoice() then use end() instead."); 134 } 135 } 136 super.addOutput(output); 137 } 138 139 @Override 140 public ProcessorDefinition<?> end() { 141 // we end a block so only when or otherwise is supported 142 onlyWhenOrOtherwise = true; 143 return super.end(); 144 } 145 146 @Override 147 public ChoiceDefinition endChoice() { 148 // we end a block so only when or otherwise is supported 149 onlyWhenOrOtherwise = true; 150 return super.endChoice(); 151 } 152 153 // Fluent API 154 // ------------------------------------------------------------------------- 155 156 /** 157 * Sets the predicate for the when node 158 * 159 * @param predicate the predicate 160 * @return the builder 161 */ 162 public ChoiceDefinition when(@AsPredicate Predicate predicate) { 163 addClause(new WhenDefinition(predicate)); 164 return this; 165 } 166 167 /** 168 * Creates an expression for the when node 169 * 170 * @return expression to be used as builder to configure the when node 171 */ 172 @AsPredicate 173 public ExpressionClause<ChoiceDefinition> when() { 174 ExpressionClause<ChoiceDefinition> clause = new ExpressionClause<>(this); 175 addClause(new WhenDefinition(clause)); 176 return clause; 177 } 178 179 private void addClause(ProcessorDefinition<?> when) { 180 onlyWhenOrOtherwise = true; 181 popBlock(); 182 addOutput(when); 183 pushBlock(when); 184 } 185 186 /** 187 * Sets the otherwise node 188 * 189 * @return the builder 190 */ 191 public ChoiceDefinition otherwise() { 192 OtherwiseDefinition answer = new OtherwiseDefinition(); 193 addClause(answer); 194 return this; 195 } 196 197 @Override 198 public void setId(String value) { 199 // when setting id, we should set it on the fine grained element, if 200 // possible 201 if (otherwise != null) { 202 otherwise.setId(value); 203 } else if (!getWhenClauses().isEmpty()) { 204 int size = getWhenClauses().size(); 205 getWhenClauses().get(size - 1).setId(value); 206 } else { 207 super.setId(value); 208 } 209 } 210 211 // Properties 212 // ------------------------------------------------------------------------- 213 214 @Override 215 public String getShortName() { 216 return "choice"; 217 } 218 219 @Override 220 public String getLabel() { 221 return getOutputs().stream().map(ProcessorDefinition::getLabel) 222 .collect(Collectors.joining(",", getShortName() + "[", "]")); 223 } 224 225 public List<WhenDefinition> getWhenClauses() { 226 return whenClauses; 227 } 228 229 /** 230 * Sets the when clauses 231 */ 232 public void setWhenClauses(List<WhenDefinition> whenClauses) { 233 this.whenClauses = whenClauses; 234 } 235 236 public OtherwiseDefinition getOtherwise() { 237 return otherwise; 238 } 239 240 public void setOtherwise(OtherwiseDefinition otherwise) { 241 this.otherwise = otherwise; 242 } 243 244 @Override 245 public void configureChild(ProcessorDefinition<?> output) { 246 if (whenClauses == null || whenClauses.isEmpty()) { 247 return; 248 } 249 for (WhenDefinition when : whenClauses) { 250 ExpressionDefinition exp = when.getExpression(); 251 if (exp.getExpressionType() != null) { 252 exp = exp.getExpressionType(); 253 } 254 Predicate pre = exp.getPredicate(); 255 if (pre instanceof ExpressionClause) { 256 ExpressionClause<?> clause = (ExpressionClause<?>)pre; 257 if (clause.getExpressionType() != null) { 258 // if using the Java DSL then the expression may have been 259 // set using the 260 // ExpressionClause which is a fancy builder to define 261 // expressions and predicates 262 // using fluent builders in the DSL. However we need 263 // afterwards a callback to 264 // reset the expression to the expression type the 265 // ExpressionClause did build for us 266 ExpressionFactory model = clause.getExpressionType(); 267 if (model instanceof ExpressionDefinition) { 268 when.setExpression((ExpressionDefinition)model); 269 } 270 } 271 } 272 } 273 } 274}