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