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