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.language;
018
019import java.util.List;
020import java.util.Map;
021import javax.xml.bind.annotation.XmlAccessType;
022import javax.xml.bind.annotation.XmlAccessorType;
023import javax.xml.bind.annotation.XmlAnyAttribute;
024import javax.xml.bind.annotation.XmlAttribute;
025import javax.xml.bind.annotation.XmlID;
026import javax.xml.bind.annotation.XmlRootElement;
027import javax.xml.bind.annotation.XmlTransient;
028import javax.xml.bind.annotation.XmlType;
029import javax.xml.bind.annotation.XmlValue;
030import javax.xml.namespace.QName;
031
032import org.apache.camel.AfterPropertiesConfigured;
033import org.apache.camel.CamelContext;
034import org.apache.camel.CamelContextAware;
035import org.apache.camel.Exchange;
036import org.apache.camel.Expression;
037import org.apache.camel.NoSuchLanguageException;
038import org.apache.camel.Predicate;
039import org.apache.camel.model.OtherAttributesAware;
040import org.apache.camel.spi.Language;
041import org.apache.camel.spi.Metadata;
042import org.apache.camel.spi.RouteContext;
043import org.apache.camel.util.CollectionStringBuffer;
044import org.apache.camel.util.ExpressionToPredicateAdapter;
045import org.apache.camel.util.IntrospectionSupport;
046import org.apache.camel.util.ObjectHelper;
047import org.apache.camel.util.ResourceHelper;
048
049/**
050 * A useful base class for an expression
051 */
052@Metadata(label = "language", title = "Expression")
053@XmlRootElement
054@XmlType(name = "expression") // must be named expression
055@XmlAccessorType(XmlAccessType.FIELD)
056public class ExpressionDefinition implements Expression, Predicate, OtherAttributesAware {
057    @XmlAttribute
058    @XmlID
059    private String id;
060    @XmlValue @Metadata(required = "true")
061    private String expression;
062    @XmlAttribute @Metadata(defaultValue = "true")
063    private Boolean trim;
064    @XmlTransient
065    private Predicate predicate;
066    @XmlTransient
067    private Expression expressionValue;
068    @XmlTransient
069    private ExpressionDefinition expressionType;
070    // use xs:any to support optional property placeholders
071    @XmlAnyAttribute
072    private Map<QName, Object> otherAttributes;
073
074    public ExpressionDefinition() {
075    }
076
077    public ExpressionDefinition(String expression) {
078        this.expression = expression;
079    }
080
081    public ExpressionDefinition(Predicate predicate) {
082        this.predicate = predicate;
083    }
084
085    public ExpressionDefinition(Expression expression) {
086        this.expressionValue = expression;
087    }
088
089    public static String getLabel(List<ExpressionDefinition> expressions) {
090        CollectionStringBuffer buffer = new CollectionStringBuffer();
091        for (ExpressionDefinition expression : expressions) {
092            buffer.append(expression.getLabel());
093        }
094        return buffer.toString();
095    }
096
097    @Override
098    public String toString() {
099        StringBuilder sb = new StringBuilder();
100        if (getLanguage() != null) {
101            sb.append(getLanguage()).append("{");
102        }
103        if (getPredicate() != null) {
104            sb.append(getPredicate().toString());
105        }
106        if (getExpressionValue() != null) {
107            sb.append(getExpressionValue().toString());
108        }
109        if (getPredicate() == null && getExpressionValue() == null && getExpression() != null) {
110            sb.append(getExpression());
111        }
112        if (getLanguage() != null) {
113            sb.append("}");
114        }
115        return sb.toString();
116    }
117
118    public Object evaluate(Exchange exchange) {
119        return evaluate(exchange, Object.class);
120    }
121
122    public <T> T evaluate(Exchange exchange, Class<T> type) {
123        if (expressionValue == null) {
124            expressionValue = createExpression(exchange.getContext());
125        }
126        ObjectHelper.notNull(expressionValue, "expressionValue");
127        return expressionValue.evaluate(exchange, type);
128    }
129
130    public void assertMatches(String text, Exchange exchange) throws AssertionError {
131        if (!matches(exchange)) {
132            throw new AssertionError(text + getExpression() + " for exchange: " + exchange);
133        }
134    }
135
136    public boolean matches(Exchange exchange) {
137        if (predicate == null) {
138            predicate = createPredicate(exchange.getContext());
139        }
140        ObjectHelper.notNull(predicate, "predicate");
141        return predicate.matches(exchange);
142    }
143
144    public String getLanguage() {
145        return "";
146    }
147
148    public final Predicate createPredicate(RouteContext routeContext) {
149        return createPredicate(routeContext.getCamelContext());
150    }
151
152    public Predicate createPredicate(CamelContext camelContext) {
153        if (predicate == null) {
154            if (getExpressionType() != null) {
155                predicate = getExpressionType().createPredicate(camelContext);
156            } else if (getExpressionValue() != null) {
157                predicate = new ExpressionToPredicateAdapter(getExpressionValue());
158            } else if (getExpression() != null) {
159                ObjectHelper.notNull("language", getLanguage());
160                Language language = camelContext.resolveLanguage(getLanguage());
161                if (language == null) {
162                    throw new NoSuchLanguageException(getLanguage());
163                }
164                String exp = getExpression();
165                // should be true by default
166                boolean isTrim = getTrim() == null || getTrim();
167                // trim if configured to trim
168                if (exp != null && isTrim) {
169                    exp = exp.trim();
170                }
171                // resolve the expression as it may be an external script from the classpath/file etc
172                exp = ResourceHelper.resolveOptionalExternalScript(camelContext, exp);
173
174                predicate = language.createPredicate(exp);
175                configurePredicate(camelContext, predicate);
176            }
177        }
178        // inject CamelContext if its aware
179        if (predicate instanceof CamelContextAware) {
180            ((CamelContextAware) predicate).setCamelContext(camelContext);
181        }
182        return predicate;
183    }
184
185    public final Expression createExpression(RouteContext routeContext) {
186        return createExpression(routeContext.getCamelContext());
187    }
188
189    public Expression createExpression(CamelContext camelContext) {
190        if (getExpressionValue() == null) {
191            if (getExpressionType() != null) {
192                setExpressionValue(getExpressionType().createExpression(camelContext));
193            } else if (getExpression() != null) {
194                ObjectHelper.notNull("language", getLanguage());
195                Language language = camelContext.resolveLanguage(getLanguage());
196                if (language == null) {
197                    throw new NoSuchLanguageException(getLanguage());
198                }
199                String exp = getExpression();
200                // should be true by default
201                boolean isTrim = getTrim() == null || getTrim();
202                // trim if configured to trim
203                if (exp != null && isTrim) {
204                    exp = exp.trim();
205                }
206                // resolve the expression as it may be an external script from the classpath/file etc
207                exp = ResourceHelper.resolveOptionalExternalScript(camelContext, exp);
208
209                setExpressionValue(language.createExpression(exp));
210                configureExpression(camelContext, getExpressionValue());
211            }
212        }
213        // inject CamelContext if its aware
214        if (getExpressionValue() instanceof CamelContextAware) {
215            ((CamelContextAware) getExpressionValue()).setCamelContext(camelContext);
216        }
217        return getExpressionValue();
218    }
219
220    public String getExpression() {
221        return expression;
222    }
223
224    /**
225     * The expression value in your chosen language syntax
226     */
227    public void setExpression(String expression) {
228        this.expression = expression;
229    }
230
231    public String getId() {
232        return id;
233    }
234
235    /**
236     * Sets the id of this node
237     */
238    public void setId(String value) {
239        this.id = value;
240    }
241
242    public Predicate getPredicate() {
243        return predicate;
244    }
245
246    public Expression getExpressionValue() {
247        return expressionValue;
248    }
249
250    protected void setExpressionValue(Expression expressionValue) {
251        this.expressionValue = expressionValue;
252    }
253
254    public ExpressionDefinition getExpressionType() {
255        return expressionType;
256    }
257
258    public Boolean getTrim() {
259        return trim;
260    }
261
262    /**
263     * Whether to trim the value to remove leading and trailing whitespaces and line breaks
264     */
265    public void setTrim(Boolean trim) {
266        this.trim = trim;
267    }
268
269    @Override
270    public Map<QName, Object> getOtherAttributes() {
271        return otherAttributes;
272    }
273
274    @Override
275    public void setOtherAttributes(Map<QName, Object> otherAttributes) {
276        this.otherAttributes = otherAttributes;
277    }
278
279    /**
280     * Returns some descriptive text to describe this node
281     */
282    public String getLabel() {
283        Predicate predicate = getPredicate();
284        if (predicate != null) {
285            return predicate.toString();
286        }
287        Expression expressionValue = getExpressionValue();
288        if (expressionValue != null) {
289            return expressionValue.toString();
290        }
291
292        String exp = getExpression();
293        return exp != null ? exp : "";
294    }
295
296    /**
297     * Allows derived classes to set a lazily created expressionType instance
298     * such as if using the {@link org.apache.camel.builder.ExpressionClause}
299     */
300    protected void setExpressionType(ExpressionDefinition expressionType) {
301        this.expressionType = expressionType;
302    }
303
304    protected void configurePredicate(CamelContext camelContext, Predicate predicate) {
305        // allows to perform additional logic after the properties has been configured which may be needed
306        // in the various camel components outside camel-core
307        if (predicate instanceof AfterPropertiesConfigured) {
308            ((AfterPropertiesConfigured) predicate).afterPropertiesConfigured(camelContext);
309        }
310    }
311
312    protected void configureExpression(CamelContext camelContext, Expression expression) {
313        // allows to perform additional logic after the properties has been configured which may be needed
314        // in the various camel components outside camel-core
315        if (expression instanceof AfterPropertiesConfigured) {
316            ((AfterPropertiesConfigured) expression).afterPropertiesConfigured(camelContext);
317        }
318    }
319
320    /**
321     * Sets a named property on the object instance using introspection
322     */
323    protected void setProperty(Object bean, String name, Object value) {
324        try {
325            IntrospectionSupport.setProperty(bean, name, value);
326        } catch (Exception e) {
327            throw new IllegalArgumentException("Failed to set property " + name + " on " + bean
328                                               + ". Reason: " + e, e);
329        }
330    }
331}