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