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;
018
019import java.util.ArrayList;
020import java.util.List;
021
022import org.apache.camel.Expression;
023import org.apache.camel.NoSuchLanguageException;
024import org.apache.camel.Processor;
025import org.apache.camel.builder.ExpressionBuilder;
026import org.apache.camel.model.ProcessorDefinition;
027import org.apache.camel.model.ToDynamicDefinition;
028import org.apache.camel.processor.SendDynamicProcessor;
029import org.apache.camel.spi.Language;
030import org.apache.camel.spi.RouteContext;
031import org.apache.camel.util.Pair;
032import org.apache.camel.util.StringHelper;
033import org.apache.camel.util.URISupport;
034
035public class ToDynamicReifier<T extends ToDynamicDefinition> extends ProcessorReifier<T> {
036
037    public ToDynamicReifier(ProcessorDefinition<?> definition) {
038        super((T)definition);
039    }
040
041    @Override
042    public Processor createProcessor(RouteContext routeContext) throws Exception {
043        String uri;
044        Expression exp;
045        if (definition.getEndpointProducerBuilder() != null) {
046            uri = definition.getEndpointProducerBuilder().getUri();
047            exp = definition.getEndpointProducerBuilder().expr();
048        } else {
049            uri = StringHelper.notEmpty(definition.getUri(), "uri", this);
050            exp = createExpression(routeContext, uri);
051        }
052
053        SendDynamicProcessor processor = new SendDynamicProcessor(uri, exp);
054        processor.setCamelContext(routeContext.getCamelContext());
055        processor.setPattern(definition.getPattern());
056        if (definition.getCacheSize() != null) {
057            processor.setCacheSize(definition.getCacheSize());
058        }
059        if (definition.getIgnoreInvalidEndpoint() != null) {
060            processor.setIgnoreInvalidEndpoint(definition.getIgnoreInvalidEndpoint());
061        }
062        return processor;
063    }
064
065    protected Expression createExpression(RouteContext routeContext, String uri) {
066        List<Expression> list = new ArrayList<>();
067
068        String[] parts = safeSplitRaw(uri);
069        for (String part : parts) {
070            // the part may have optional language to use, so you can mix
071            // languages
072            String value = StringHelper.after(part, "language:");
073            if (value != null) {
074                String before = StringHelper.before(value, ":");
075                String after = StringHelper.after(value, ":");
076                if (before != null && after != null) {
077                    // maybe its a language, must have language: as prefix
078                    try {
079                        Language partLanguage = routeContext.getCamelContext().resolveLanguage(before);
080                        if (partLanguage != null) {
081                            Expression exp = partLanguage.createExpression(after);
082                            list.add(exp);
083                            continue;
084                        }
085                    } catch (NoSuchLanguageException e) {
086                        // ignore
087                    }
088                }
089            }
090            // fallback and use simple language
091            Language lan = routeContext.getCamelContext().resolveLanguage("simple");
092            Expression exp = lan.createExpression(part);
093            list.add(exp);
094        }
095
096        Expression exp;
097        if (list.size() == 1) {
098            exp = list.get(0);
099        } else {
100            exp = ExpressionBuilder.concatExpression(list);
101        }
102
103        return exp;
104    }
105
106    // Utilities
107    // -------------------------------------------------------------------------
108
109    /**
110     * We need to split the string safely for each + sign, but avoid splitting
111     * within RAW(...).
112     */
113    private static String[] safeSplitRaw(String s) {
114        List<String> list = new ArrayList<>();
115
116        if (!s.contains("+")) {
117            // no plus sign so there is only one part, so no need to split
118            list.add(s);
119        } else {
120            // there is a plus sign so we need to split in a safe manner
121            List<Pair<Integer>> rawPairs = URISupport.scanRaw(s);
122            StringBuilder sb = new StringBuilder();
123            char[] chars = s.toCharArray();
124            for (int i = 0; i < chars.length; i++) {
125                char ch = chars[i];
126                if (ch != '+' || URISupport.isRaw(i, rawPairs)) {
127                    sb.append(ch);
128                } else {
129                    list.add(sb.toString());
130                    sb.setLength(0);
131                }
132            }
133            // any leftover?
134            if (sb.length() > 0) {
135                list.add(sb.toString());
136                sb.setLength(0);
137            }
138        }
139
140        return list.toArray(new String[list.size()]);
141    }
142
143}