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