    1   /*
    2    * Copyright 1999-2004 The Apache Software Foundation
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *     http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  package org.apache.commons.jxpath.ri.compiler;
    17
    18  import java.text.DecimalFormat;
    19  import java.text.DecimalFormatSymbols;
    20  import java.text.NumberFormat;
    21  import java.util.Collection;
    22  import java.util.Locale;
    23
    24  import org.apache.commons.jxpath.JXPathContext;
    25  import org.apache.commons.jxpath.JXPathException;
    26  import org.apache.commons.jxpath.ri.Compiler;
    27  import org.apache.commons.jxpath.ri.EvalContext;
    28  import org.apache.commons.jxpath.ri.InfoSetUtil;
    29  import org.apache.commons.jxpath.ri.model.NodePointer;
    30
    31  /***
    32   * An element of the compile tree representing one of built-in functions
    33   * like "position()" or "number()".
    34   *
    35   * @author Dmitri Plotnikov
    36   * @version $Revision: 1.16 $ $Date: 2004/04/01 02:53:45 $
    37   */
    38  public class CoreFunction extends Operation {
    39
    40      private static final Double ZERO = new Double(0);
    41      private int functionCode;
    42
    43      public CoreFunction(int functionCode, Expression args[]) {
    44          super(args);
    45          this.functionCode = functionCode;
    46      }
    47
    48      public int getFunctionCode() {
    49          return functionCode;
    50      }
    51
    52      protected String getFunctionName() {
    53          switch (functionCode) {
    54              case Compiler.FUNCTION_LAST :
    55                  return "last";
    56              case Compiler.FUNCTION_POSITION :
    57                  return "position";
    58              case Compiler.FUNCTION_COUNT :
    59                  return "count";
    60              case Compiler.FUNCTION_ID :
    61                  return "id";
    62              case Compiler.FUNCTION_LOCAL_NAME :
    63                  return "local-name";
    64              case Compiler.FUNCTION_NAMESPACE_URI :
    65                  return "namespace-uri";
    66              case Compiler.FUNCTION_NAME :
    67                  return "name";
    68              case Compiler.FUNCTION_STRING :
    69                  return "string";
    70              case Compiler.FUNCTION_CONCAT :
    71                  return "concat";
    72              case Compiler.FUNCTION_STARTS_WITH :
    73                  return "starts-with";
    74              case Compiler.FUNCTION_CONTAINS :
    75                  return "contains";
    76              case Compiler.FUNCTION_SUBSTRING_BEFORE :
    77                  return "substring-before";
    78              case Compiler.FUNCTION_SUBSTRING_AFTER :
    79                  return "substring-after";
    80              case Compiler.FUNCTION_SUBSTRING :
    81                  return "substring";
    82              case Compiler.FUNCTION_STRING_LENGTH :
    83                  return "string-length";
    84              case Compiler.FUNCTION_NORMALIZE_SPACE :
    85                  return "normalize-space";
    86              case Compiler.FUNCTION_TRANSLATE :
    87                  return "translate";
    88              case Compiler.FUNCTION_BOOLEAN :
    89                  return "boolean";
    90              case Compiler.FUNCTION_NOT :
    91                  return "not";
    92              case Compiler.FUNCTION_TRUE :
    93                  return "true";
    94              case Compiler.FUNCTION_FALSE :
    95                  return "false";
    96              case Compiler.FUNCTION_LANG :
    97                  return "lang";
    98              case Compiler.FUNCTION_NUMBER :
    99                  return "number";
    100             case Compiler.FUNCTION_SUM :
    101                 return "sum";
    102             case Compiler.FUNCTION_FLOOR :
    103                 return "floor";
    104             case Compiler.FUNCTION_CEILING :
    105                 return "ceiling";
    106             case Compiler.FUNCTION_ROUND :
    107                 return "round";
    108             case Compiler.FUNCTION_KEY :
    109                 return "key";
    110             case Compiler.FUNCTION_FORMAT_NUMBER:
    111                 return "format-number";
    112         }
    113         return "unknownFunction" + functionCode + "()";
    114     }
    115
    116     public Expression getArg1() {
    117         return args[0];
    118     }
    119
    120     public Expression getArg2() {
    121         return args[1];
    122     }
    123
    124     public Expression getArg3() {
    125         return args[2];
    126     }
    127
    128     public int getArgumentCount() {
    129         if (args == null) {
    130             return 0;
    131         }
    132         return args.length;
    133     }
    134
    135     /***
    136      * Returns true if any argument is context dependent or if
    137      * the function is last(), position(), boolean(), local-name(),
    138      * name(), string(), lang(), number().
    139      */
    140     public boolean computeContextDependent() {
    141         if (super.computeContextDependent()) {
    142             return true;
    143         }
    144
    145         switch(functionCode) {
    146             case Compiler.FUNCTION_LAST:
    147             case Compiler.FUNCTION_POSITION:
    148                 return true;
    149
    150             case Compiler.FUNCTION_BOOLEAN:
    151             case Compiler.FUNCTION_LOCAL_NAME:
    152             case Compiler.FUNCTION_NAME:
    153             case Compiler.FUNCTION_NAMESPACE_URI:
    154             case Compiler.FUNCTION_STRING:
    155             case Compiler.FUNCTION_LANG:
    156             case Compiler.FUNCTION_NUMBER:
    157                 return args == null || args.length == 0;
    158
    159             case Compiler.FUNCTION_COUNT:
    160             case Compiler.FUNCTION_ID:
    161             case Compiler.FUNCTION_CONCAT:
    162             case Compiler.FUNCTION_STARTS_WITH:
    163             case Compiler.FUNCTION_CONTAINS:
    164             case Compiler.FUNCTION_SUBSTRING_BEFORE:
    165             case Compiler.FUNCTION_SUBSTRING_AFTER:
    166             case Compiler.FUNCTION_SUBSTRING:
    167             case Compiler.FUNCTION_STRING_LENGTH:
    168             case Compiler.FUNCTION_NORMALIZE_SPACE:
    169             case Compiler.FUNCTION_TRANSLATE:
    170             case Compiler.FUNCTION_NOT:
    171             case Compiler.FUNCTION_TRUE:
    172             case Compiler.FUNCTION_FALSE:
    173             case Compiler.FUNCTION_SUM:
    174             case Compiler.FUNCTION_FLOOR:
    175             case Compiler.FUNCTION_CEILING:
    176             case Compiler.FUNCTION_ROUND:
    177                 return false;
    178
    179             case Compiler.FUNCTION_FORMAT_NUMBER:
    180                 return args != null && args.length == 2;
    181         }
    182
    183         return false;
    184     }
    185
    186     public String toString() {
    187         StringBuffer buffer = new StringBuffer();
    188         buffer.append(getFunctionName());
    189         buffer.append('(');
    190         Expression args[] = getArguments();
    191         if (args != null) {
    192             for (int i = 0; i < args.length; i++) {
    193                 if (i > 0) {
    194                     buffer.append(", ");
    195                 }
    196                 buffer.append(args[i]);
    197             }
    198         }
    199         buffer.append(')');
    200         return buffer.toString();
    201     }
    202
    203     public Object compute(EvalContext context) {
    204         return computeValue(context);
    205     }
    206
    207     /***
    208      * Computes a built-in function
    209      */
    210     public Object computeValue(EvalContext context) {
    211         switch (functionCode) {
    212             case Compiler.FUNCTION_LAST :
    213                 return functionLast(context);
    214             case Compiler.FUNCTION_POSITION :
    215                 return functionPosition(context);
    216             case Compiler.FUNCTION_COUNT :
    217                 return functionCount(context);
    218             case Compiler.FUNCTION_LANG :
    219                 return functionLang(context);
    220             case Compiler.FUNCTION_ID :
    221                 return functionID(context);
    222             case Compiler.FUNCTION_LOCAL_NAME :
    223                 return functionLocalName(context);
    224             case Compiler.FUNCTION_NAMESPACE_URI :
    225                 return functionNamespaceURI(context);
    226             case Compiler.FUNCTION_NAME :
    227                 return functionName(context);
    228             case Compiler.FUNCTION_STRING :
    229                 return functionString(context);
    230             case Compiler.FUNCTION_CONCAT :
    231                 return functionConcat(context);
    232             case Compiler.FUNCTION_STARTS_WITH :
    233                 return functionStartsWith(context);
    234             case Compiler.FUNCTION_CONTAINS :
    235                 return functionContains(context);
    236             case Compiler.FUNCTION_SUBSTRING_BEFORE :
    237                 return functionSubstringBefore(context);
    238             case Compiler.FUNCTION_SUBSTRING_AFTER :
    239                 return functionSubstringAfter(context);
    240             case Compiler.FUNCTION_SUBSTRING :
    241                 return functionSubstring(context);
    242             case Compiler.FUNCTION_STRING_LENGTH :
    243                 return functionStringLength(context);
    244             case Compiler.FUNCTION_NORMALIZE_SPACE :
    245                 return functionNormalizeSpace(context);
    246             case Compiler.FUNCTION_TRANSLATE :
    247                 return functionTranslate(context);
    248             case Compiler.FUNCTION_BOOLEAN :
    249                 return functionBoolean(context);
    250             case Compiler.FUNCTION_NOT :
    251                 return functionNot(context);
    252             case Compiler.FUNCTION_TRUE :
    253                 return functionTrue(context);
    254             case Compiler.FUNCTION_FALSE :
    255                 return functionFalse(context);
    256             case Compiler.FUNCTION_NULL :
    257                 return functionNull(context);
    258             case Compiler.FUNCTION_NUMBER :
    259                 return functionNumber(context);
    260             case Compiler.FUNCTION_SUM :
    261                 return functionSum(context);
    262             case Compiler.FUNCTION_FLOOR :
    263                 return functionFloor(context);
    264             case Compiler.FUNCTION_CEILING :
    265                 return functionCeiling(context);
    266             case Compiler.FUNCTION_ROUND :
    267                 return functionRound(context);
    268             case Compiler.FUNCTION_KEY :
    269                 return functionKey(context);
    270             case Compiler.FUNCTION_FORMAT_NUMBER :
    271                 return functionFormatNumber(context);
    272         }
    273         return null;
    274     }
    275
    276     protected Object functionLast(EvalContext context) {
    277         assertArgCount(0);
    278         // Move the position to the beginning and iterate through
    279         // the context to count nodes.
    280         int old = context.getCurrentPosition();
    281         context.reset();
    282         int count = 0;
    283         while (context.nextNode()) {
    284             count++;
    285         }
    286
    287         // Restore the current position.
    288         if (old != 0) {
    289             context.setPosition(old);
    290         }
    291         return new Double(count);
    292     }
    293
    294     protected Object functionPosition(EvalContext context) {
    295         assertArgCount(0);
    296         return new Integer(context.getCurrentPosition());
    297     }
    298
    299     protected Object functionCount(EvalContext context) {
    300         assertArgCount(1);
    301         Expression arg1 = getArg1();
    302         int count = 0;
    303         Object value = arg1.compute(context);
    304         if (value instanceof NodePointer) {
    305             value = ((NodePointer) value).getValue();
    306         }
    307         if (value instanceof EvalContext) {
    308             EvalContext ctx = (EvalContext) value;
    309             while (ctx.hasNext()) {
    310                 ctx.next();
    311                 count++;
    312             }
    313         }
    314         else if (value instanceof Collection) {
    315             count = ((Collection) value).size();
    316         }
    317         else if (value == null) {
    318             count = 0;
    319         }
    320         else {
    321             count = 1;
    322         }
    323         return new Double(count);
    324     }
    325
    326     protected Object functionLang(EvalContext context) {
    327         assertArgCount(1);
    328         String lang = InfoSetUtil.stringValue(getArg1().computeValue(context));
    329         NodePointer pointer = (NodePointer) context.getSingleNodePointer();
    330         if (pointer == null) {
    331             return Boolean.FALSE;
    332         }
    333         return pointer.isLanguage(lang) ? Boolean.TRUE : Boolean.FALSE;
    334     }
    335
    336     protected Object functionID(EvalContext context) {
    337         assertArgCount(1);
    338         String id = InfoSetUtil.stringValue(getArg1().computeValue(context));
    339         JXPathContext jxpathContext = context.getJXPathContext();
    340         NodePointer pointer = (NodePointer) jxpathContext.getContextPointer();
    341         return pointer.getPointerByID(jxpathContext, id);
    342     }
    343
    344     protected Object functionKey(EvalContext context) {
    345         assertArgCount(2);
    346         String key = InfoSetUtil.stringValue(getArg1().computeValue(context));
    347         String value = InfoSetUtil.stringValue(getArg2().computeValue(context));
    348         JXPathContext jxpathContext = context.getJXPathContext();
    349         NodePointer pointer = (NodePointer) jxpathContext.getContextPointer();
    350         return pointer.getPointerByKey(jxpathContext, key, value);
    351     }
    352
    353     protected Object functionNamespaceURI(EvalContext context) {
    354         if (getArgumentCount() == 0) {
    355             NodePointer ptr = context.getCurrentNodePointer();
    356             String str = ptr.getNamespaceURI();
    357             return str == null ? "" : str;
    358         }
    359         assertArgCount(1);
    360         Object set = getArg1().compute(context);
    361         if (set instanceof EvalContext) {
    362             EvalContext ctx = (EvalContext) set;
    363             if (ctx.hasNext()) {
    364                 NodePointer ptr = (NodePointer) ctx.next();
    365                 String str = ptr.getNamespaceURI();
    366                 return str == null ? "" : str;
    367             }
    368         }
    369         return "";
    370     }
    371
    372     protected Object functionLocalName(EvalContext context) {
    373         if (getArgumentCount() == 0) {
    374             NodePointer ptr = context.getCurrentNodePointer();
    375             return ptr.getName().getName();
    376         }
    377         assertArgCount(1);
    378         Object set = getArg1().compute(context);
    379         if (set instanceof EvalContext) {
    380             EvalContext ctx = (EvalContext) set;
    381             if (ctx.hasNext()) {
    382                 NodePointer ptr = (NodePointer) ctx.next();
    383                 return ptr.getName().getName();
    384             }
    385         }
    386         return "";
    387     }
    388
    389     protected Object functionName(EvalContext context) {
    390         if (getArgumentCount() == 0) {
    391             NodePointer ptr = context.getCurrentNodePointer();
    392             return ptr.getName().toString();
    393         }
    394         assertArgCount(1);
    395         Object set = getArg1().compute(context);
    396         if (set instanceof EvalContext) {
    397             EvalContext ctx = (EvalContext) set;
    398             if (ctx.hasNext()) {
    399                 NodePointer ptr = (NodePointer) ctx.next();
    400                 return ptr.getName().toString();
    401             }
    402         }
    403         return "";
    404     }
    405
    406     protected Object functionString(EvalContext context) {
    407         if (getArgumentCount() == 0) {
    408             return InfoSetUtil.stringValue(context.getCurrentNodePointer());
    409         }
    410         assertArgCount(1);
    411         return InfoSetUtil.stringValue(getArg1().computeValue(context));
    412     }
    413
    414     protected Object functionConcat(EvalContext context) {
    415         if (getArgumentCount() < 2) {
    416             assertArgCount(2);
    417         }
    418         StringBuffer buffer = new StringBuffer();
    419         Expression args[] = getArguments();
    420         for (int i = 0; i < args.length; i++) {
    421             buffer.append(InfoSetUtil.stringValue(args[i].compute(context)));
    422         }
    423         return buffer.toString();
    424     }
    425
    426     protected Object functionStartsWith(EvalContext context) {
    427         assertArgCount(2);
    428         String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
    429         String s2 = InfoSetUtil.stringValue(getArg2().computeValue(context));
    430         return s1.startsWith(s2) ? Boolean.TRUE : Boolean.FALSE;
    431     }
    432
    433     protected Object functionContains(EvalContext context) {
    434         assertArgCount(2);
    435         String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
    436         String s2 = InfoSetUtil.stringValue(getArg2().computeValue(context));
    437         return s1.indexOf(s2) != -1 ? Boolean.TRUE : Boolean.FALSE;
    438     }
    439
    440     protected Object functionSubstringBefore(EvalContext context) {
    441         assertArgCount(2);
    442         String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
    443         String s2 = InfoSetUtil.stringValue(getArg2().computeValue(context));
    444         int index = s1.indexOf(s2);
    445         if (index == -1) {
    446             return "";
    447         }
    448         return s1.substring(0, index);
    449     }
    450
    451     protected Object functionSubstringAfter(EvalContext context) {
    452         assertArgCount(2);
    453         String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
    454         String s2 = InfoSetUtil.stringValue(getArg2().computeValue(context));
    455         int index = s1.indexOf(s2);
    456         if (index == -1) {
    457             return "";
    458         }
    459         return s1.substring(index + s2.length());
    460     }
    461
    462     protected Object functionSubstring(EvalContext context) {
    463         int ac = getArgumentCount();
    464         if (ac != 2 && ac != 3) {
    465             assertArgCount(2);
    466         }
    467
    468         String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
    469         double from = InfoSetUtil.doubleValue(getArg2().computeValue(context));
    470         if (Double.isNaN(from)) {
    471             return "";
    472         }
    473
    474         from = Math.round(from);
    475         if (ac == 2) {
    476             if (from < 1) {
    477                 from = 1;
    478             }
    479             return s1.substring((int) from - 1);
    480         }
    481         else {
    482             double length =
    483                 InfoSetUtil.doubleValue(getArg3().computeValue(context));
    484             length = Math.round(length);
    485             if (length < 0) {
    486                 return "";
    487             }
    488
    489             double to = from + length;
    490             if (to < 1) {
    491                 return "";
    492             }
    493
    494             if (to > s1.length() + 1) {
    495                 if (from < 1) {
    496                     from = 1;
    497                 }
    498                 return s1.substring((int) from - 1);
    499             }
    500
    501             if (from < 1) {
    502                 from = 1;
    503             }
    504             return s1.substring((int) from - 1, (int) (to - 1));
    505         }
    506     }
    507
    508     protected Object functionStringLength(EvalContext context) {
    509         String s;
    510         if (getArgumentCount() == 0) {
    511             s = InfoSetUtil.stringValue(context.getCurrentNodePointer());
    512         }
    513         else {
    514             assertArgCount(1);
    515             s = InfoSetUtil.stringValue(getArg1().computeValue(context));
    516         }
    517         return new Double(s.length());
    518     }
    519
    520     protected Object functionNormalizeSpace(EvalContext context) {
    521         assertArgCount(1);
    522         String s = InfoSetUtil.stringValue(getArg1().computeValue(context));
    523         char chars[] = s.toCharArray();
    524         int out = 0;
    525         int phase = 0;
    526         for (int in = 0; in < chars.length; in++) {
    527             switch(chars[in]) {
    528                 case 0x20:
    529                 case 0x9:
    530                 case 0xD:
    531                 case 0xA:
    532                     if (phase == 0) {      // beginning
    533                         ;
    534                     }
    535                     else if (phase == 1) { // non-space
    536                         phase = 2;
    537                         chars[out++] = ' ';
    538                     }
    539                     break;
    540                 default:
    541                     chars[out++] = chars[in];
    542                     phase = 1;
    543             }
    544         }
    545         if (phase == 2) { // trailing-space
    546             out--;
    547         }
    548         return new String(chars, 0, out);
    549     }
    550
    551     protected Object functionTranslate(EvalContext context) {
    552         assertArgCount(3);
    553         String s1 = InfoSetUtil.stringValue(getArg1().computeValue(context));
    554         String s2 = InfoSetUtil.stringValue(getArg2().computeValue(context));
    555         String s3 = InfoSetUtil.stringValue(getArg3().computeValue(context));
    556         char chars[] = s1.toCharArray();
    557         int out = 0;
    558         for (int in = 0; in < chars.length; in++) {
    559             char c = chars[in];
    560             int inx = s2.indexOf(c);
    561             if (inx != -1) {
    562                 if (inx < s3.length()) {
    563                     chars[out++] = s3.charAt(inx);
    564                 }
    565             }
    566             else {
    567                 chars[out++] = c;
    568             }
    569         }
    570         return new String(chars, 0, out);
    571     }
    572
    573     protected Object functionBoolean(EvalContext context) {
    574         assertArgCount(1);
    575         return InfoSetUtil.booleanValue(getArg1().computeValue(context))
    576             ? Boolean.TRUE
    577             : Boolean.FALSE;
    578     }
    579
    580     protected Object functionNot(EvalContext context) {
    581         assertArgCount(1);
    582         return InfoSetUtil.booleanValue(getArg1().computeValue(context))
    583             ? Boolean.FALSE
    584             : Boolean.TRUE;
    585     }
    586
    587     protected Object functionTrue(EvalContext context) {
    588         assertArgCount(0);
    589         return Boolean.TRUE;
    590     }
    591
    592     protected Object functionFalse(EvalContext context) {
    593         assertArgCount(0);
    594         return Boolean.FALSE;
    595     }
    596
    597     protected Object functionNull(EvalContext context) {
    598         assertArgCount(0);
    599         return null;
    600     }
    601
    602     protected Object functionNumber(EvalContext context) {
    603         if (getArgumentCount() == 0) {
    604             return InfoSetUtil.number(context.getCurrentNodePointer());
    605         }
    606         assertArgCount(1);
    607         return InfoSetUtil.number(getArg1().computeValue(context));
    608     }
    609
    610     protected Object functionSum(EvalContext context) {
    611         assertArgCount(1);
    612         Object v = getArg1().compute(context);
    613         if (v == null) {
    614             return ZERO;
    615         }
    616         else if (v instanceof EvalContext) {
    617             double sum = 0.0;
    618             EvalContext ctx = (EvalContext) v;
    619             while (ctx.hasNext()) {
    620                 NodePointer ptr = (NodePointer) ctx.next();
    621                 sum += InfoSetUtil.doubleValue(ptr);
    622             }
    623             return new Double(sum);
    624         }
    625         throw new JXPathException(
    626             "Invalid argument type for 'sum': " + v.getClass().getName());
    627     }
    628
    629     protected Object functionFloor(EvalContext context) {
    630         assertArgCount(1);
    631         double v = InfoSetUtil.doubleValue(getArg1().computeValue(context));
    632         return new Double(Math.floor(v));
    633     }
    634
    635     protected Object functionCeiling(EvalContext context) {
    636         assertArgCount(1);
    637         double v = InfoSetUtil.doubleValue(getArg1().computeValue(context));
    638         return new Double(Math.ceil(v));
    639     }
    640
    641     protected Object functionRound(EvalContext context) {
    642         assertArgCount(1);
    643         double v = InfoSetUtil.doubleValue(getArg1().computeValue(context));
    644         return new Double(Math.round(v));
    645     }
    646
    647     private Object functionFormatNumber(EvalContext context) {
    648         int ac = getArgumentCount();
    649         if (ac != 2 && ac != 3) {
    650             assertArgCount(2);
    651         }
    652
    653         double number =
    654             InfoSetUtil.doubleValue(getArg1().computeValue(context));
    655         String pattern =
    656             InfoSetUtil.stringValue(getArg2().computeValue(context));
    657
    658         DecimalFormatSymbols symbols = null;
    659         if (ac == 3) {
    660             String symbolsName =
    661                 InfoSetUtil.stringValue(getArg3().computeValue(context));
    662             symbols =
    663                 context.getJXPathContext().getDecimalFormatSymbols(symbolsName);
    664         }
    665         else {
    666             NodePointer pointer = context.getCurrentNodePointer();
    667             Locale locale;
    668             if (pointer != null) {
    669                 locale = pointer.getLocale();
    670             }
    671             else {
    672                 locale = context.getJXPathContext().getLocale();
    673             }
    674             symbols = new DecimalFormatSymbols(locale);
    675         }
    676
    677         DecimalFormat format = (DecimalFormat) NumberFormat.getInstance();
    678         format.setDecimalFormatSymbols(symbols);
    679         format.applyLocalizedPattern(pattern);
    680         return format.format(number);
    681     }
    682
    683     private void assertArgCount(int count) {
    684         if (getArgumentCount() != count) {
    685             throw new JXPathException("Incorrect number of argument: " + this);
    686         }
    687     }
    688 }