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.language.simple.ast; 018 019import org.apache.camel.Expression; 020import org.apache.camel.builder.ExpressionBuilder; 021import org.apache.camel.language.simple.types.SimpleParserException; 022import org.apache.camel.language.simple.types.SimpleToken; 023import org.apache.camel.util.ObjectHelper; 024import org.apache.camel.util.OgnlHelper; 025import org.apache.camel.util.StringHelper; 026 027/** 028 * Represents one of built-in functions of the 029 * <a href="http://camel.apache.org/simple.html">simple language</a> 030 */ 031public class SimpleFunctionExpression extends LiteralExpression { 032 033 public SimpleFunctionExpression(SimpleToken token) { 034 super(token); 035 } 036 037 @Override 038 public Expression createExpression(String expression) { 039 String function = text.toString(); 040 return createSimpleExpression(function, true); 041 } 042 043 /** 044 * Creates a Camel {@link Expression} based on this model. 045 * 046 * @param expression the input string 047 * @param strict whether to throw exception if the expression was not a function, 048 * otherwise <tt>null</tt> is returned 049 * @return the created {@link Expression} 050 * @throws org.apache.camel.language.simple.types.SimpleParserException 051 * should be thrown if error parsing the model 052 */ 053 public Expression createExpression(String expression, boolean strict) { 054 String function = text.toString(); 055 return createSimpleExpression(function, strict); 056 } 057 058 private Expression createSimpleExpression(String function, boolean strict) { 059 // return the function directly if we can create function without analyzing the prefix 060 Expression answer = createSimpleExpressionDirectly(function); 061 if (answer != null) { 062 return answer; 063 } 064 065 // body and headers first 066 answer = createSimpleExpressionBodyOrHeader(function, strict); 067 if (answer != null) { 068 return answer; 069 } 070 071 // camelContext OGNL 072 String remainder = ifStartsWithReturnRemainder("camelContext", function); 073 if (remainder != null) { 074 boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(remainder); 075 if (invalid) { 076 throw new SimpleParserException("Valid syntax: ${camelContext.OGNL} was: " + function, token.getIndex()); 077 } 078 return ExpressionBuilder.camelContextOgnlExpression(remainder); 079 } 080 081 // Exception OGNL 082 remainder = ifStartsWithReturnRemainder("exception", function); 083 if (remainder != null) { 084 boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(remainder); 085 if (invalid) { 086 throw new SimpleParserException("Valid syntax: ${exception.OGNL} was: " + function, token.getIndex()); 087 } 088 return ExpressionBuilder.exchangeExceptionOgnlExpression(remainder); 089 } 090 091 // property 092 remainder = ifStartsWithReturnRemainder("property", function); 093 if (remainder == null) { 094 remainder = ifStartsWithReturnRemainder("exchangeProperty", function); 095 } 096 if (remainder != null) { 097 // remove leading character (dot or ?) 098 if (remainder.startsWith(".") || remainder.startsWith("?")) { 099 remainder = remainder.substring(1); 100 } 101 // remove starting and ending brackets 102 if (remainder.startsWith("[") && remainder.endsWith("]")) { 103 remainder = remainder.substring(1, remainder.length() - 1); 104 } 105 106 // validate syntax 107 boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(remainder); 108 if (invalid) { 109 throw new SimpleParserException("Valid syntax: ${exchangeProperty.OGNL} was: " + function, token.getIndex()); 110 } 111 112 if (OgnlHelper.isValidOgnlExpression(remainder)) { 113 // ognl based property 114 return ExpressionBuilder.propertyOgnlExpression(remainder); 115 } else { 116 // regular property 117 return ExpressionBuilder.exchangePropertyExpression(remainder); 118 } 119 } 120 121 // system property 122 remainder = ifStartsWithReturnRemainder("sys.", function); 123 if (remainder != null) { 124 return ExpressionBuilder.systemPropertyExpression(remainder); 125 } 126 remainder = ifStartsWithReturnRemainder("sysenv.", function); 127 if (remainder != null) { 128 return ExpressionBuilder.systemEnvironmentExpression(remainder); 129 } 130 131 // exchange OGNL 132 remainder = ifStartsWithReturnRemainder("exchange", function); 133 if (remainder != null) { 134 boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(remainder); 135 if (invalid) { 136 throw new SimpleParserException("Valid syntax: ${exchange.OGNL} was: " + function, token.getIndex()); 137 } 138 return ExpressionBuilder.exchangeOgnlExpression(remainder); 139 } 140 141 // file: prefix 142 remainder = ifStartsWithReturnRemainder("file:", function); 143 if (remainder != null) { 144 Expression fileExpression = createSimpleFileExpression(remainder, strict); 145 if (fileExpression != null) { 146 return fileExpression; 147 } 148 } 149 150 // date: prefix 151 remainder = ifStartsWithReturnRemainder("date:", function); 152 if (remainder != null) { 153 String[] parts = remainder.split(":"); 154 if (parts.length < 2) { 155 throw new SimpleParserException("Valid syntax: ${date:command:pattern} was: " + function, token.getIndex()); 156 } 157 String command = ObjectHelper.before(remainder, ":"); 158 String pattern = ObjectHelper.after(remainder, ":"); 159 return ExpressionBuilder.dateExpression(command, pattern); 160 } 161 162 // bean: prefix 163 remainder = ifStartsWithReturnRemainder("bean:", function); 164 if (remainder != null) { 165 return ExpressionBuilder.beanExpression(remainder); 166 } 167 168 // properties: prefix 169 remainder = ifStartsWithReturnRemainder("properties:", function); 170 if (remainder != null) { 171 String[] parts = remainder.split(":"); 172 if (parts.length > 2) { 173 throw new SimpleParserException("Valid syntax: ${properties:key[:default]} was: " + function, token.getIndex()); 174 } 175 return ExpressionBuilder.propertiesComponentExpression(remainder, null); 176 } 177 178 // properties-location: prefix 179 remainder = ifStartsWithReturnRemainder("properties-location:", function); 180 if (remainder != null) { 181 String[] parts = remainder.split(":"); 182 if (parts.length > 3) { 183 throw new SimpleParserException("Valid syntax: ${properties-location:location:key[:default]} was: " + function, token.getIndex()); 184 } 185 186 String locations = null; 187 String key = remainder; 188 if (parts.length >= 2) { 189 locations = ObjectHelper.before(remainder, ":"); 190 key = ObjectHelper.after(remainder, ":"); 191 } 192 return ExpressionBuilder.propertiesComponentExpression(key, locations); 193 } 194 195 // ref: prefix 196 remainder = ifStartsWithReturnRemainder("ref:", function); 197 if (remainder != null) { 198 return ExpressionBuilder.refExpression(remainder); 199 } 200 201 // const: prefix 202 remainder = ifStartsWithReturnRemainder("type:", function); 203 if (remainder != null) { 204 Expression exp = ExpressionBuilder.typeExpression(remainder); 205 // we want to cache this expression so we wont re-evaluate it as the type/constant wont change 206 return ExpressionBuilder.cacheExpression(exp); 207 } 208 209 // miscellaneous functions 210 Expression misc = createSimpleExpressionMisc(function); 211 if (misc != null) { 212 return misc; 213 } 214 215 if (strict) { 216 throw new SimpleParserException("Unknown function: " + function, token.getIndex()); 217 } else { 218 return null; 219 } 220 } 221 222 private Expression createSimpleExpressionBodyOrHeader(String function, boolean strict) { 223 // bodyAs 224 String remainder = ifStartsWithReturnRemainder("bodyAs", function); 225 if (remainder != null) { 226 String type = ObjectHelper.between(remainder, "(", ")"); 227 remainder = ObjectHelper.after(remainder, ")"); 228 if (type == null || ObjectHelper.isNotEmpty(remainder)) { 229 throw new SimpleParserException("Valid syntax: ${bodyAs(type)} was: " + function, token.getIndex()); 230 } 231 232 type = StringHelper.removeQuotes(type); 233 return ExpressionBuilder.bodyExpression(type); 234 } 235 // mandatoryBodyAs 236 remainder = ifStartsWithReturnRemainder("mandatoryBodyAs", function); 237 if (remainder != null) { 238 String type = ObjectHelper.between(remainder, "(", ")"); 239 remainder = ObjectHelper.after(remainder, ")"); 240 if (type == null || ObjectHelper.isNotEmpty(remainder)) { 241 throw new SimpleParserException("Valid syntax: ${mandatoryBodyAs(type)} was: " + function, token.getIndex()); 242 } 243 type = StringHelper.removeQuotes(type); 244 return ExpressionBuilder.mandatoryBodyExpression(type); 245 } 246 247 // body OGNL 248 remainder = ifStartsWithReturnRemainder("body", function); 249 if (remainder == null) { 250 remainder = ifStartsWithReturnRemainder("in.body", function); 251 } 252 if (remainder != null) { 253 boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(remainder); 254 if (invalid) { 255 throw new SimpleParserException("Valid syntax: ${body.OGNL} was: " + function, token.getIndex()); 256 } 257 return ExpressionBuilder.bodyOgnlExpression(remainder); 258 } 259 260 // headerAs 261 remainder = ifStartsWithReturnRemainder("headerAs", function); 262 if (remainder != null) { 263 String keyAndType = ObjectHelper.between(remainder, "(", ")"); 264 if (keyAndType == null) { 265 throw new SimpleParserException("Valid syntax: ${headerAs(key, type)} was: " + function, token.getIndex()); 266 } 267 268 String key = ObjectHelper.before(keyAndType, ","); 269 String type = ObjectHelper.after(keyAndType, ","); 270 remainder = ObjectHelper.after(remainder, ")"); 271 if (ObjectHelper.isEmpty(key) || ObjectHelper.isEmpty(type) || ObjectHelper.isNotEmpty(remainder)) { 272 throw new SimpleParserException("Valid syntax: ${headerAs(key, type)} was: " + function, token.getIndex()); 273 } 274 key = StringHelper.removeQuotes(key); 275 type = StringHelper.removeQuotes(type); 276 return ExpressionBuilder.headerExpression(key, type); 277 } 278 279 // headers function 280 if ("in.headers".equals(function) || "headers".equals(function)) { 281 return ExpressionBuilder.headersExpression(); 282 } 283 284 // in header function 285 remainder = ifStartsWithReturnRemainder("in.headers", function); 286 if (remainder == null) { 287 remainder = ifStartsWithReturnRemainder("in.header", function); 288 } 289 if (remainder == null) { 290 remainder = ifStartsWithReturnRemainder("headers", function); 291 } 292 if (remainder == null) { 293 remainder = ifStartsWithReturnRemainder("header", function); 294 } 295 if (remainder != null) { 296 // remove leading character (dot or ?) 297 if (remainder.startsWith(".") || remainder.startsWith("?")) { 298 remainder = remainder.substring(1); 299 } 300 // remove starting and ending brackets 301 if (remainder.startsWith("[") && remainder.endsWith("]")) { 302 remainder = remainder.substring(1, remainder.length() - 1); 303 } 304 // remove quotes from key 305 String key = StringHelper.removeLeadingAndEndingQuotes(remainder); 306 307 // validate syntax 308 boolean invalid = OgnlHelper.isInvalidValidOgnlExpression(key); 309 if (invalid) { 310 throw new SimpleParserException("Valid syntax: ${header.name[key]} was: " + function, token.getIndex()); 311 } 312 313 if (OgnlHelper.isValidOgnlExpression(key)) { 314 // ognl based header 315 return ExpressionBuilder.headersOgnlExpression(key); 316 } else { 317 // regular header 318 return ExpressionBuilder.headerExpression(key); 319 } 320 } 321 322 // out header function 323 remainder = ifStartsWithReturnRemainder("out.header.", function); 324 if (remainder == null) { 325 remainder = ifStartsWithReturnRemainder("out.headers.", function); 326 } 327 if (remainder != null) { 328 return ExpressionBuilder.outHeaderExpression(remainder); 329 } 330 331 return null; 332 } 333 334 private Expression createSimpleExpressionDirectly(String expression) { 335 if (ObjectHelper.isEqualToAny(expression, "body", "in.body")) { 336 return ExpressionBuilder.bodyExpression(); 337 } else if (ObjectHelper.equal(expression, "out.body")) { 338 return ExpressionBuilder.outBodyExpression(); 339 } else if (ObjectHelper.equal(expression, "id")) { 340 return ExpressionBuilder.messageIdExpression(); 341 } else if (ObjectHelper.equal(expression, "exchangeId")) { 342 return ExpressionBuilder.exchangeIdExpression(); 343 } else if (ObjectHelper.equal(expression, "exchange")) { 344 return ExpressionBuilder.exchangeExpression(); 345 } else if (ObjectHelper.equal(expression, "exception")) { 346 return ExpressionBuilder.exchangeExceptionExpression(); 347 } else if (ObjectHelper.equal(expression, "exception.message")) { 348 return ExpressionBuilder.exchangeExceptionMessageExpression(); 349 } else if (ObjectHelper.equal(expression, "exception.stacktrace")) { 350 return ExpressionBuilder.exchangeExceptionStackTraceExpression(); 351 } else if (ObjectHelper.equal(expression, "threadName")) { 352 return ExpressionBuilder.threadNameExpression(); 353 } else if (ObjectHelper.equal(expression, "camelId")) { 354 return ExpressionBuilder.camelContextNameExpression(); 355 } else if (ObjectHelper.equal(expression, "routeId")) { 356 return ExpressionBuilder.routeIdExpression(); 357 } else if (ObjectHelper.equal(expression, "null")) { 358 return ExpressionBuilder.nullExpression(); 359 } 360 361 return null; 362 } 363 364 private Expression createSimpleFileExpression(String remainder, boolean strict) { 365 if (ObjectHelper.equal(remainder, "name")) { 366 return ExpressionBuilder.fileNameExpression(); 367 } else if (ObjectHelper.equal(remainder, "name.noext")) { 368 return ExpressionBuilder.fileNameNoExtensionExpression(); 369 } else if (ObjectHelper.equal(remainder, "name.noext.single")) { 370 return ExpressionBuilder.fileNameNoExtensionSingleExpression(); 371 } else if (ObjectHelper.equal(remainder, "name.ext") || ObjectHelper.equal(remainder, "ext")) { 372 return ExpressionBuilder.fileExtensionExpression(); 373 } else if (ObjectHelper.equal(remainder, "name.ext.single")) { 374 return ExpressionBuilder.fileExtensionSingleExpression(); 375 } else if (ObjectHelper.equal(remainder, "onlyname")) { 376 return ExpressionBuilder.fileOnlyNameExpression(); 377 } else if (ObjectHelper.equal(remainder, "onlyname.noext")) { 378 return ExpressionBuilder.fileOnlyNameNoExtensionExpression(); 379 } else if (ObjectHelper.equal(remainder, "onlyname.noext.single")) { 380 return ExpressionBuilder.fileOnlyNameNoExtensionSingleExpression(); 381 } else if (ObjectHelper.equal(remainder, "parent")) { 382 return ExpressionBuilder.fileParentExpression(); 383 } else if (ObjectHelper.equal(remainder, "path")) { 384 return ExpressionBuilder.filePathExpression(); 385 } else if (ObjectHelper.equal(remainder, "absolute")) { 386 return ExpressionBuilder.fileAbsoluteExpression(); 387 } else if (ObjectHelper.equal(remainder, "absolute.path")) { 388 return ExpressionBuilder.fileAbsolutePathExpression(); 389 } else if (ObjectHelper.equal(remainder, "length") || ObjectHelper.equal(remainder, "size")) { 390 return ExpressionBuilder.fileSizeExpression(); 391 } else if (ObjectHelper.equal(remainder, "modified")) { 392 return ExpressionBuilder.fileLastModifiedExpression(); 393 } 394 if (strict) { 395 throw new SimpleParserException("Unknown file language syntax: " + remainder, token.getIndex()); 396 } 397 return null; 398 } 399 400 private Expression createSimpleExpressionMisc(String function) { 401 String remainder; 402 403 // random function 404 remainder = ifStartsWithReturnRemainder("random", function); 405 if (remainder != null) { 406 String values = ObjectHelper.between(remainder, "(", ")"); 407 if (values == null || ObjectHelper.isEmpty(values)) { 408 throw new SimpleParserException("Valid syntax: ${random(min,max)} or ${random(max)} was: " + function, token.getIndex()); 409 } 410 if (values.contains(",")) { 411 String[] tokens = values.split(",", -1); 412 if (tokens.length > 2) { 413 throw new SimpleParserException("Valid syntax: ${random(min,max)} or ${random(max)} was: " + function, token.getIndex()); 414 } 415 int min = Integer.parseInt(tokens[0].trim()); 416 int max = Integer.parseInt(tokens[1].trim()); 417 return ExpressionBuilder.randomExpression(min, max); 418 } else { 419 int max = Integer.parseInt(values.trim()); 420 return ExpressionBuilder.randomExpression(max); 421 } 422 } 423 424 // collate function 425 remainder = ifStartsWithReturnRemainder("collate", function); 426 if (remainder != null) { 427 String values = ObjectHelper.between(remainder, "(", ")"); 428 if (values == null || ObjectHelper.isEmpty(values)) { 429 throw new SimpleParserException("Valid syntax: ${collate(group)} was: " + function, token.getIndex()); 430 } 431 String exp = "${body}"; 432 int num = Integer.parseInt(values.trim()); 433 return ExpressionBuilder.collateExpression(exp, num); 434 } 435 436 // messageHistory function 437 remainder = ifStartsWithReturnRemainder("messageHistory", function); 438 if (remainder != null) { 439 boolean detailed; 440 String values = ObjectHelper.between(remainder, "(", ")"); 441 if (values == null || ObjectHelper.isEmpty(values)) { 442 detailed = true; 443 } else { 444 detailed = Boolean.valueOf(values); 445 } 446 return ExpressionBuilder.messageHistoryExpression(detailed); 447 } else if (ObjectHelper.equal(function, "messageHistory")) { 448 return ExpressionBuilder.messageHistoryExpression(true); 449 } 450 return null; 451 } 452 453 private String ifStartsWithReturnRemainder(String prefix, String text) { 454 if (text.startsWith(prefix)) { 455 String remainder = text.substring(prefix.length()); 456 if (remainder.length() > 0) { 457 return remainder; 458 } 459 } 460 return null; 461 } 462 463}