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.reifier.language;
018
019import java.util.LinkedHashMap;
020import java.util.Map;
021import java.util.function.BiFunction;
022
023import org.apache.camel.AfterPropertiesConfigured;
024import org.apache.camel.CamelContext;
025import org.apache.camel.CamelContextAware;
026import org.apache.camel.Expression;
027import org.apache.camel.NoSuchLanguageException;
028import org.apache.camel.Predicate;
029import org.apache.camel.model.ExpressionSubElementDefinition;
030import org.apache.camel.model.language.ConstantExpression;
031import org.apache.camel.model.language.ExchangePropertyExpression;
032import org.apache.camel.model.language.ExpressionDefinition;
033import org.apache.camel.model.language.GroovyExpression;
034import org.apache.camel.model.language.HeaderExpression;
035import org.apache.camel.model.language.Hl7TerserExpression;
036import org.apache.camel.model.language.JsonPathExpression;
037import org.apache.camel.model.language.LanguageExpression;
038import org.apache.camel.model.language.MethodCallExpression;
039import org.apache.camel.model.language.MvelExpression;
040import org.apache.camel.model.language.OgnlExpression;
041import org.apache.camel.model.language.RefExpression;
042import org.apache.camel.model.language.SimpleExpression;
043import org.apache.camel.model.language.SpELExpression;
044import org.apache.camel.model.language.TokenizerExpression;
045import org.apache.camel.model.language.XMLTokenizerExpression;
046import org.apache.camel.model.language.XPathExpression;
047import org.apache.camel.model.language.XQueryExpression;
048import org.apache.camel.reifier.AbstractReifier;
049import org.apache.camel.spi.Language;
050import org.apache.camel.spi.PropertyConfigurer;
051import org.apache.camel.spi.PropertyConfigurerAware;
052import org.apache.camel.spi.ReifierStrategy;
053import org.apache.camel.support.ExpressionToPredicateAdapter;
054import org.apache.camel.support.PropertyBindingSupport;
055import org.apache.camel.support.ScriptHelper;
056import org.apache.camel.util.ObjectHelper;
057
058public class ExpressionReifier<T extends ExpressionDefinition> extends AbstractReifier {
059
060    private static final Map<Class<?>, BiFunction<CamelContext, ExpressionDefinition, ExpressionReifier<? extends ExpressionDefinition>>> EXPRESSIONS;
061
062    static {
063        Map<Class<?>, BiFunction<CamelContext, ExpressionDefinition, ExpressionReifier<? extends ExpressionDefinition>>> map = new LinkedHashMap<>();
064        map.put(ConstantExpression.class, ExpressionReifier::new);
065        map.put(ExchangePropertyExpression.class, ExpressionReifier::new);
066        map.put(ExpressionDefinition.class, ExpressionReifier::new);
067        map.put(GroovyExpression.class, ExpressionReifier::new);
068        map.put(HeaderExpression.class, ExpressionReifier::new);
069        map.put(Hl7TerserExpression.class, ExpressionReifier::new);
070        map.put(JsonPathExpression.class, JsonPathExpressionReifier::new);
071        map.put(LanguageExpression.class, ExpressionReifier::new);
072        map.put(MethodCallExpression.class, MethodCallExpressionReifier::new);
073        map.put(MvelExpression.class, ExpressionReifier::new);
074        map.put(OgnlExpression.class, ExpressionReifier::new);
075        map.put(RefExpression.class, ExpressionReifier::new);
076        map.put(SimpleExpression.class, SimpleExpressionReifier::new);
077        map.put(SpELExpression.class, ExpressionReifier::new);
078        map.put(TokenizerExpression.class, TokenizerExpressionReifier::new);
079        map.put(XMLTokenizerExpression.class, XMLTokenizerExpressionReifier::new);
080        map.put(XPathExpression.class, XPathExpressionReifier::new);
081        map.put(XQueryExpression.class, XQueryExpressionReifier::new);
082        EXPRESSIONS = map;
083        ReifierStrategy.addReifierClearer(ExpressionReifier::clearReifiers);
084    }
085
086    protected final T definition;
087
088    public ExpressionReifier(CamelContext camelContext, T definition) {
089        super(camelContext);
090        this.definition = definition;
091    }
092
093    public static ExpressionReifier<? extends ExpressionDefinition> reifier(CamelContext camelContext, ExpressionSubElementDefinition definition) {
094        return reifier(camelContext, definition.getExpressionType());
095    }
096
097    public static ExpressionReifier<? extends ExpressionDefinition> reifier(CamelContext camelContext, ExpressionDefinition definition) {
098        BiFunction<CamelContext, ExpressionDefinition, ExpressionReifier<? extends ExpressionDefinition>> reifier = EXPRESSIONS.get(definition.getClass());
099        if (reifier != null) {
100            return reifier.apply(camelContext, definition);
101        }
102        throw new IllegalStateException("Unsupported definition: " + definition);
103    }
104
105    public static void clearReifiers() {
106        EXPRESSIONS.clear();
107    }
108
109    public Expression createExpression() {
110        Expression expression = definition.getExpressionValue();
111        if (expression == null) {
112            if (definition.getExpressionType() != null) {
113                expression = reifier(camelContext, definition.getExpressionType()).createExpression();
114            } else {
115                ObjectHelper.notNull(definition.getLanguage(), "language");
116                Language language = camelContext.resolveLanguage(definition.getLanguage());
117                if (language == null) {
118                    throw new NoSuchLanguageException(definition.getLanguage());
119                }
120                String exp = parseString(definition.getExpression());
121                // should be true by default
122                boolean isTrim = parseBoolean(definition.getTrim(), true);
123                // trim if configured to trim
124                if (exp != null && isTrim) {
125                    exp = exp.trim();
126                }
127                // resolve the expression as it may be an external script from
128                // the classpath/file etc
129                exp = ScriptHelper.resolveOptionalExternalScript(camelContext, exp);
130                configureLanguage(language);
131                expression = language.createExpression(exp);
132                configureExpression(expression);
133            }
134        }
135        // inject CamelContext if its aware
136        if (expression instanceof CamelContextAware) {
137            ((CamelContextAware) expression).setCamelContext(camelContext);
138        }
139        return expression;
140    }
141
142    public Predicate createPredicate() {
143        Predicate predicate = definition.getPredicate();
144        if (predicate == null) {
145            if (definition.getExpressionType() != null) {
146                predicate = reifier(camelContext, definition.getExpressionType()).createPredicate();
147            } else if (definition.getExpressionValue() != null) {
148                predicate = new ExpressionToPredicateAdapter(definition.getExpressionValue());
149            } else if (definition.getExpression() != null) {
150                ObjectHelper.notNull(definition.getLanguage(), "language");
151                Language language = camelContext.resolveLanguage(definition.getLanguage());
152                if (language == null) {
153                    throw new NoSuchLanguageException(definition.getLanguage());
154                }
155                String exp = parseString(definition.getExpression());
156                // should be true by default
157                boolean isTrim = parseBoolean(definition.getTrim(), true);
158                // trim if configured to trim
159                if (exp != null && isTrim) {
160                    exp = exp.trim();
161                }
162                // resolve the expression as it may be an external script from
163                // the classpath/file etc
164                exp = ScriptHelper.resolveOptionalExternalScript(camelContext, exp);
165                configureLanguage(language);
166                predicate = language.createPredicate(exp);
167                configurePredicate(predicate);
168            }
169        }
170        // inject CamelContext if its aware
171        if (predicate instanceof CamelContextAware) {
172            ((CamelContextAware) predicate).setCamelContext(camelContext);
173        }
174        return predicate;
175    }
176
177    protected void configureLanguage(Language language) {
178    }
179
180    protected void configurePredicate(Predicate predicate) {
181        // allows to perform additional logic after the properties has been
182        // configured which may be needed
183        // in the various camel components outside camel-core
184        if (predicate instanceof AfterPropertiesConfigured) {
185            ((AfterPropertiesConfigured)predicate).afterPropertiesConfigured(camelContext);
186        }
187    }
188
189    protected void configureExpression(Expression expression) {
190        // allows to perform additional logic after the properties has been
191        // configured which may be needed
192        // in the various camel components outside camel-core
193        if (expression instanceof AfterPropertiesConfigured) {
194            ((AfterPropertiesConfigured)expression).afterPropertiesConfigured(camelContext);
195        }
196    }
197
198    protected void setProperties(Object target, Map<String, Object> properties) {
199        properties.entrySet().removeIf(e -> e.getValue() == null);
200
201        PropertyConfigurer configurer = null;
202        if (target instanceof PropertyConfigurerAware) {
203            configurer = ((PropertyConfigurerAware) target).getPropertyConfigurer(target);
204        } else if (target instanceof PropertyConfigurer) {
205            configurer = (PropertyConfigurer) target;
206        }
207        PropertyBindingSupport.build()
208                .withConfigurer(configurer)
209                .bind(camelContext, target, properties);
210    }
211
212}