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 null safe operator or not. 085 * 086 * @param ognlExpression the Camel OGNL expression 087 * @return <tt>true</tt> if the null safe 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 }