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