View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  package org.apache.commons.beanutils.expression;
    18
    19  /***
    20   * Default Property Name Expression {@link Resolver} Implementation.
    21   * <p>
    22   * This class assists in resolving property names in the following five formats,
    23   * with the layout of an identifying String in parentheses:
    24   * <ul>
    25   * <li><strong>Simple (<code>name</code>)</strong> - The specified
    26   *     <code>name</code> identifies an individual property of a particular
    27   *     JavaBean.  The name of the actual getter or setter method to be used
    28   *     is determined using standard JavaBeans instrospection, so that (unless
    29   *     overridden by a <code>BeanInfo</code> class, a property named "xyz"
    30   *     will have a getter method named <code>getXyz()</code> or (for boolean
    31   *     properties only) <code>isXyz()</code>, and a setter method named
    32   *     <code>setXyz()</code>.</li>
    33   * <li><strong>Nested (<code>name1.name2.name3</code>)</strong> The first
    34   *     name element is used to select a property getter, as for simple
    35   *     references above.  The object returned for this property is then
    36   *     consulted, using the same approach, for a property getter for a
    37   *     property named <code>name2</code>, and so on.  The property value that
    38   *     is ultimately retrieved or modified is the one identified by the
    39   *     last name element.</li>
    40   * <li><strong>Indexed (<code>name[index]</code>)</strong> - The underlying
    41   *     property value is assumed to be an array, or this JavaBean is assumed
    42   *     to have indexed property getter and setter methods.  The appropriate
    43   *     (zero-relative) entry in the array is selected.  <code>List</code>
    44   *     objects are now also supported for read/write.  You simply need to define
    45   *     a getter that returns the <code>List</code></li>
    46   * <li><strong>Mapped (<code>name(key)</code>)</strong> - The JavaBean
    47   *     is assumed to have an property getter and setter methods with an
    48   *     additional attribute of type <code>java.lang.String</code>.</li>
    49   * <li><strong>Combined (<code>name1.name2[index].name3(key)</code>)</strong> -
    50   *     Combining mapped, nested, and indexed references is also
    51   *     supported.</li>
    52   * </ul>
    53   *
    54   * @version $Revision: 473888 $ $Date: 2006-11-12 06:21:24 +0000 (Sun, 12 Nov 2006) $
    55   * @since 1.8.0
    56   */
    57  public class DefaultResolver implements Resolver {
    58
    59      private static final char NESTED        = '.';
    60      private static final char MAPPED_START  = '(';
    61      private static final char MAPPED_END    = ')';
    62      private static final char INDEXED_START = '[';
    63      private static final char INDEXED_END   = ']';
    64
    65      /***
    66       * Default Constructor.
    67       */
    68      public DefaultResolver() {
    69      }
    70
    71      /***
    72       * Return the index value from the property expression or -1.
    73       *
    74       * @param expression The property expression
    75       * @return The index value or -1 if the property is not indexed
    76       * @throws IllegalArgumentException If the indexed property is illegally
    77       * formed or has an invalid (non-numeric) value.
    78       */
    79      public int getIndex(String expression) {
    80          if (expression == null || expression.length() == 0) {
    81              return -1;
    82          }
    83          for (int i = 0; i < expression.length(); i++) {
    84              char c = expression.charAt(i);
    85              if (c == NESTED || c == MAPPED_START) {
    86                  return -1;
    87              } else if (c == INDEXED_START) {
    88                  int end = expression.indexOf(INDEXED_END, i);
    89                  if (end < 0) {
    90                      throw new IllegalArgumentException("Missing End Delimiter");
    91                  }
    92                  String value = expression.substring(i + 1, end);
    93                  if (value.length() == 0) {
    94                      throw new IllegalArgumentException("No Index Value");
    95                  }
    96                  int index = 0;
    97                  try {
    98                      index = Integer.parseInt(value, 10);
    99                  } catch (Exception e) {
    100                     throw new IllegalArgumentException("Invalid index value '"
    101                             + value + "'");
    102                 }
    103                 return index;
    104             }
    105         }
    106         return -1;
    107     }
    108
    109     /***
    110      * Return the map key from the property expression or <code>null</code>.
    111      *
    112      * @param expression The property expression
    113      * @return The index value
    114      * @throws IllegalArgumentException If the mapped property is illegally formed.
    115      */
    116     public String getKey(String expression) {
    117         if (expression == null || expression.length() == 0) {
    118             return null;
    119         }
    120         for (int i = 0; i < expression.length(); i++) {
    121             char c = expression.charAt(i);
    122             if (c == NESTED || c == INDEXED_START) {
    123                 return null;
    124             } else if (c == MAPPED_START) {
    125                 int end = expression.indexOf(MAPPED_END, i);
    126                 if (end < 0) {
    127                     throw new IllegalArgumentException("Missing End Delimiter");
    128                 }
    129                 return expression.substring(i + 1, end);
    130             }
    131         }
    132         return null;
    133     }
    134
    135     /***
    136      * Return the property name from the property expression.
    137      *
    138      * @param expression The property expression
    139      * @return The property name
    140      */
    141     public String getProperty(String expression) {
    142         if (expression == null || expression.length() == 0) {
    143             return expression;
    144         }
    145         for (int i = 0; i < expression.length(); i++) {
    146             char c = expression.charAt(i);
    147             if (c == NESTED) {
    148                 return expression.substring(0, i);
    149             } else if (c == MAPPED_START || c == INDEXED_START) {
    150                 return expression.substring(0, i);
    151             }
    152         }
    153         return expression;
    154     }
    155
    156     /***
    157      * Indicates whether or not the expression
    158      * contains nested property expressions or not.
    159      *
    160      * @param expression The property expression
    161      * @return The next property expression
    162      */
    163     public boolean hasNested(String expression) {
    164         if (expression == null || expression.length() == 0) {
    165             return false;
    166         } else {
    167             return (remove(expression) != null);
    168         }
    169     }
    170
    171     /***
    172      * Indicate whether the expression is for an indexed property or not.
    173      *
    174      * @param expression The property expression
    175      * @return <code>true</code> if the expresion is indexed,
    176      *  otherwise <code>false</code>
    177      */
    178     public boolean isIndexed(String expression) {
    179         if (expression == null || expression.length() == 0) {
    180             return false;
    181         }
    182         for (int i = 0; i < expression.length(); i++) {
    183             char c = expression.charAt(i);
    184             if (c == NESTED || c == MAPPED_START) {
    185                 return false;
    186             } else if (c == INDEXED_START) {
    187                 return true;
    188             }
    189         }
    190         return false;
    191     }
    192
    193     /***
    194      * Indicate whether the expression is for a mapped property or not.
    195      *
    196      * @param expression The property expression
    197      * @return <code>true</code> if the expresion is mapped,
    198      *  otherwise <code>false</code>
    199      */
    200     public boolean isMapped(String expression) {
    201         if (expression == null || expression.length() == 0) {
    202             return false;
    203         }
    204         for (int i = 0; i < expression.length(); i++) {
    205             char c = expression.charAt(i);
    206             if (c == NESTED || c == INDEXED_START) {
    207                 return false;
    208             } else if (c == MAPPED_START) {
    209                 return true;
    210             }
    211         }
    212         return false;
    213     }
    214
    215     /***
    216      * Extract the next property expression from the
    217      * current expression.
    218      *
    219      * @param expression The property expression
    220      * @return The next property expression
    221      */
    222     public String next(String expression) {
    223         if (expression == null || expression.length() == 0) {
    224             return null;
    225         }
    226         boolean indexed = false;
    227         boolean mapped  = false;
    228         for (int i = 0; i < expression.length(); i++) {
    229             char c = expression.charAt(i);
    230             if (indexed) {
    231                 if (c == INDEXED_END) {
    232                     return expression.substring(0, i + 1);
    233                 }
    234             } else if (mapped) {
    235                 if (c == MAPPED_END) {
    236                     return expression.substring(0, i + 1);
    237                 }
    238             } else {
    239                 if (c == NESTED) {
    240                     return expression.substring(0, i);
    241                 } else if (c == MAPPED_START) {
    242                     mapped = true;
    243                 } else if (c == INDEXED_START) {
    244                     indexed = true;
    245                 }
    246             }
    247         }
    248         return expression;
    249     }
    250
    251     /***
    252      * Remove the last property expresson from the
    253      * current expression.
    254      *
    255      * @param expression The property expression
    256      * @return The new expression value, with first property
    257      * expression removed - null if there are no more expressions
    258      */
    259     public String remove(String expression) {
    260         if (expression == null || expression.length() == 0) {
    261             return null;
    262         }
    263         String property = next(expression);
    264         if (expression.length() == property.length()) {
    265             return null;
    266         }
    267         int start = property.length();
    268         if (expression.charAt(start) == NESTED) {
    269             start++;
    270         }
    271         return expression.substring(start);
    272     }
    273 }