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