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