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     */
017    package org.apache.camel.component.properties;
018    
019    import java.util.ArrayList;
020    import java.util.List;
021    import java.util.Properties;
022    
023    import org.apache.camel.util.StringHelper;
024    import org.slf4j.Logger;
025    import org.slf4j.LoggerFactory;
026    
027    /**
028     * A parser to parse a string which contains property placeholders
029     *
030     * @version 
031     */
032    public class DefaultPropertiesParser implements AugmentedPropertyNameAwarePropertiesParser {
033        protected final transient Logger log = LoggerFactory.getLogger(getClass());
034        
035        @Override
036        public String parseUri(String text, Properties properties, String prefixToken, String suffixToken) throws IllegalArgumentException {
037            return parseUri(text, properties, prefixToken, suffixToken, null, null, false);
038        }
039    
040        public String parseUri(String text, Properties properties, String prefixToken, String suffixToken,
041                               String propertyPrefix, String propertySuffix, boolean fallbackToUnaugmentedProperty) throws IllegalArgumentException {
042            String answer = text;
043            boolean done = false;
044    
045            // the placeholders can contain nested placeholders so we need to do recursive parsing
046            // we must therefore also do circular reference check and must keep a list of visited keys
047            List<String> visited = new ArrayList<String>();
048            while (!done) {
049                List<String> replaced = new ArrayList<String>();
050                answer = doParseUri(answer, properties, replaced, prefixToken, suffixToken, propertyPrefix, propertySuffix, fallbackToUnaugmentedProperty);
051    
052                // check the replaced with the visited to avoid circular reference
053                for (String replace : replaced) {
054                    if (visited.contains(replace)) {
055                        throw new IllegalArgumentException("Circular reference detected with key [" + replace + "] from text: " + text);
056                    }
057                }
058                // okay all okay so add the replaced as visited
059                visited.addAll(replaced);
060    
061                // we are done when we can no longer find any prefix tokens in the answer
062                done = findTokenPosition(answer, 0, prefixToken) == -1;
063            }
064            return answer;
065        }
066    
067        public String parseProperty(String key, String value, Properties properties) {
068            return value;
069        }
070    
071        private String doParseUri(String uri, Properties properties, List<String> replaced, String prefixToken, String suffixToken,
072                                  String propertyPrefix, String propertySuffix, boolean fallbackToUnaugmentedProperty) {
073            StringBuilder sb = new StringBuilder();
074    
075            int pivot = 0;
076            int size = uri.length();
077            while (pivot < size) {
078                int idx = findTokenPosition(uri, pivot, prefixToken);
079                if (idx < 0) {
080                    sb.append(createConstantPart(uri, pivot, size));
081                    break;
082                } else {
083                    if (pivot < idx) {
084                        sb.append(createConstantPart(uri, pivot, idx));
085                    }
086                    pivot = idx + prefixToken.length();
087                    int endIdx = findTokenPosition(uri, pivot, suffixToken);
088                    if (endIdx < 0) {
089                        throw new IllegalArgumentException("Expecting " + suffixToken + " but found end of string from text: " + uri);
090                    }
091                    String key = uri.substring(pivot, endIdx);
092                    String augmentedKey = key;
093                    
094                    if (propertyPrefix != null) {
095                        log.debug("Augmenting property key [{}] with prefix: {}", key, propertyPrefix);
096                        augmentedKey = propertyPrefix + augmentedKey;
097                    }
098                    
099                    if (propertySuffix != null) {
100                        log.debug("Augmenting property key [{}] with suffix: {}", key, propertySuffix);
101                        augmentedKey = augmentedKey + propertySuffix;
102                    }
103    
104                    String part = createPlaceholderPart(augmentedKey, properties, replaced);
105                    
106                    // Note: Only fallback to unaugmented when the original key was actually augmented
107                    if (part == null && fallbackToUnaugmentedProperty && (propertyPrefix != null || propertySuffix != null)) {
108                        log.debug("Property wth key [{}] not found, attempting with unaugmented key: {}", augmentedKey, key);
109                        part = createPlaceholderPart(key, properties, replaced);
110                    }
111                    
112                    if (part == null) {
113                        StringBuilder esb = new StringBuilder();
114                        esb.append("Property with key [").append(augmentedKey).append("] ");
115                        if (fallbackToUnaugmentedProperty && (propertyPrefix != null || propertySuffix != null)) {
116                            esb.append("(and original key [").append(key).append("]) ");
117                        }
118                        esb.append("not found in properties from text: ").append(uri);
119                        throw new IllegalArgumentException(esb.toString());
120                    }
121                    sb.append(part);
122                    pivot = endIdx + suffixToken.length();
123                }
124            }
125            return sb.toString();
126        }
127        
128        private int findTokenPosition(String uri, int pivot, String token) {
129            int idx = uri.indexOf(token, pivot);
130            while (idx > 0) {
131                // grab part as the previous char + token + next char, to test if the token is quoted
132                String part = null;
133                int len = idx + token.length() + 1;
134                if (uri.length() >= len) {
135                    part = uri.substring(idx - 1, len);
136                }
137                if (StringHelper.isQuoted(part)) {
138                    // the token was quoted, so regard it as a literal
139                    // and then try to find from next position
140                    pivot = idx + token.length() + 1;
141                    idx = uri.indexOf(token, pivot);
142                } else {
143                    // found token
144                    return idx;
145                }
146            }
147            return idx;
148        }
149    
150        private String createConstantPart(String uri, int start, int end) {
151            return uri.substring(start, end);
152        }
153    
154        private String createPlaceholderPart(String key, Properties properties, List<String> replaced) {
155            // keep track of which parts we have replaced
156            replaced.add(key);
157            
158            String propertyValue = System.getProperty(key);
159            if (propertyValue != null) {
160                log.debug("Found a JVM system property: {} with value: {} to be used.", key, propertyValue);
161            } else if (properties != null) {
162                propertyValue = properties.getProperty(key);
163            }
164    
165            return parseProperty(key, propertyValue, properties);
166        }
167    
168    }