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.util;
018
019 import java.util.ArrayList;
020 import java.util.List;
021 import java.util.regex.Matcher;
022 import java.util.regex.Pattern;
023
024 /**
025 * Helper for Camel OGNL (Object-Graph Navigation Language) expressions.
026 *
027 * @version
028 */
029 public final class OgnlHelper {
030
031 private static final Pattern INDEX_PATTERN = Pattern.compile("^(.*)\\[(.*)\\]$");
032
033 private OgnlHelper() {
034 }
035
036 /**
037 * Tests whether or not the given String is a Camel OGNL expression.
038 * <p/>
039 * An expression is considered an OGNL expression when it contains either one of the following chars: . or [
040 *
041 * @param expression the String
042 * @return <tt>true</tt> if a Camel OGNL expression, otherwise <tt>false</tt>.
043 */
044 public static boolean isValidOgnlExpression(String expression) {
045 if (ObjectHelper.isEmpty(expression)) {
046 return false;
047 }
048
049 // the brackets should come in a pair
050 int bracketBegin = StringHelper.countChar(expression, '[');
051 int bracketEnd = StringHelper.countChar(expression, ']');
052 if (bracketBegin > 0 && bracketEnd > 0) {
053 return bracketBegin == bracketEnd;
054 }
055
056 return expression.contains(".");
057 }
058
059 public static boolean isInvalidValidOgnlExpression(String expression) {
060 if (ObjectHelper.isEmpty(expression)) {
061 return false;
062 }
063
064 if (!expression.contains(".") && !expression.contains("[") && !expression.contains("]")) {
065 return false;
066 }
067
068 // the brackets should come in pair
069 int bracketBegin = StringHelper.countChar(expression, '[');
070 int bracketEnd = StringHelper.countChar(expression, ']');
071 if (bracketBegin > 0 || bracketEnd > 0) {
072 return bracketBegin != bracketEnd;
073 }
074
075 // check for double dots
076 if (expression.contains("..")) {
077 return true;
078 }
079
080 return false;
081 }
082
083 /**
084 * Tests whether or not the given Camel OGNL expression is using the Elvis operator or not.
085 *
086 * @param ognlExpression the Camel OGNL expression
087 * @return <tt>true</tt> if the Elvis operator is used, otherwise <tt>false</tt>.
088 */
089 public static boolean isNullSafeOperator(String ognlExpression) {
090 if (ObjectHelper.isEmpty(ognlExpression)) {
091 return false;
092 }
093
094 return ognlExpression.startsWith("?");
095 }
096
097 /**
098 * Removes any leading operators from the Camel OGNL expression.
099 * <p/>
100 * Will remove any leading of the following chars: ? or .
101 *
102 * @param ognlExpression the Camel OGNL expression
103 * @return the Camel OGNL expression without any leading operators.
104 */
105 public static String removeLeadingOperators(String ognlExpression) {
106 if (ObjectHelper.isEmpty(ognlExpression)) {
107 return ognlExpression;
108 }
109
110 if (ognlExpression.startsWith("?")) {
111 ognlExpression = ognlExpression.substring(1);
112 }
113 if (ognlExpression.startsWith(".")) {
114 ognlExpression = ognlExpression.substring(1);
115 }
116
117 return ognlExpression;
118 }
119
120 /**
121 * Removes any trailing operators from the Camel OGNL expression.
122 *
123 * @param ognlExpression the Camel OGNL expression
124 * @return the Camel OGNL expression without any trailing operators.
125 */
126 public static String removeTrailingOperators(String ognlExpression) {
127 if (ObjectHelper.isEmpty(ognlExpression)) {
128 return ognlExpression;
129 }
130
131 if (ognlExpression.contains("[")) {
132 return ObjectHelper.before(ognlExpression, "[");
133 }
134 return ognlExpression;
135 }
136
137 public static String removeOperators(String ognlExpression) {
138 return removeLeadingOperators(removeTrailingOperators(ognlExpression));
139 }
140
141 public static KeyValueHolder<String, String> isOgnlIndex(String ognlExpression) {
142 Matcher matcher = INDEX_PATTERN.matcher(ognlExpression);
143 if (matcher.matches()) {
144
145 // to avoid empty strings as we want key/value to be null in such cases
146 String key = matcher.group(1);
147 if (ObjectHelper.isEmpty(key)) {
148 key = null;
149 }
150
151 // to avoid empty strings as we want key/value to be null in such cases
152 String value = matcher.group(2);
153 if (ObjectHelper.isEmpty(value)) {
154 value = null;
155 }
156
157 return new KeyValueHolder<String, String>(key, value);
158 }
159
160 return null;
161 }
162
163 /**
164 * Regular expression with repeating groups is a pain to get right
165 * and then nobody understands the reg exp afterwards.
166 * So we use a bit ugly/low-level Java code to split the OGNL into methods.
167 *
168 * @param ognl the ognl expression
169 * @return a list of methods, will return an empty list, if ognl expression has no methods
170 */
171 public static List<String> splitOgnl(String ognl) {
172 List<String> methods = new ArrayList<String>();
173
174 // return an empty list if ognl is empty
175 if (ObjectHelper.isEmpty(ognl)) {
176 return methods;
177 }
178
179 StringBuilder sb = new StringBuilder();
180
181 int j = 0; // j is used as counter per method
182 boolean squareBracket = false; // special to keep track if we are inside a square bracket block, eg: [foo]
183 boolean parenthesisBracket = false; // special to keep track if we are inside a parenthesis block, eg: bar(${body}, ${header.foo})
184 for (int i = 0; i < ognl.length(); i++) {
185 char ch = ognl.charAt(i);
186 // special for starting a new method
187 if (j == 0 || (j == 1 && ognl.charAt(i - 1) == '?')
188 || (ch != '.' && ch != '?' && ch != ']')) {
189 sb.append(ch);
190 // special if we are doing square bracket
191 if (ch == '[' && !parenthesisBracket) {
192 squareBracket = true;
193 } else if (ch == '(') {
194 parenthesisBracket = true;
195 } else if (ch == ')') {
196 parenthesisBracket = false;
197 }
198 j++; // advance
199 } else {
200 if (ch == '.' && !squareBracket && !parenthesisBracket) {
201 // only treat dot as a method separator if not inside a square bracket block
202 // as dots can be used in key names when accessing maps
203
204 // a dit denotes end of this method and a new method is to be invoked
205 String s = sb.toString();
206
207 // reset sb
208 sb.setLength(0);
209
210 // pass over ? to the new method
211 if (s.endsWith("?")) {
212 sb.append("?");
213 s = s.substring(0, s.length() - 1);
214 }
215
216 // add the method
217 methods.add(s);
218
219 // reset j to begin a new method
220 j = 0;
221 } else if (ch == ']' && !parenthesisBracket) {
222 // append ending ] to method name
223 sb.append(ch);
224 String s = sb.toString();
225
226 // reset sb
227 sb.setLength(0);
228
229 // add the method
230 methods.add(s);
231
232 // reset j to begin a new method
233 j = 0;
234
235 // no more square bracket
236 squareBracket = false;
237 }
238
239 // and don't lose the char if its not an ] end marker (as we already added that)
240 if (ch != ']' || parenthesisBracket) {
241 sb.append(ch);
242 }
243
244 // check for end of parenthesis
245 if (ch == ')') {
246 parenthesisBracket = false;
247 }
248
249 // only advance if already begun on the new method
250 if (j > 0) {
251 j++;
252 }
253 }
254 }
255
256 // add remainder in buffer when reached end of data
257 if (sb.length() > 0) {
258 methods.add(sb.toString());
259 }
260
261 return methods;
262 }
263
264 }