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