Coverage Report - org.apache.commons.beanutils.expression.DefaultResolver
 
Classes in this File Line Coverage Branch Coverage Complexity
DefaultResolver
97%
90/92
95%
95/100
9.444
 
 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 01:21:24 -0500 (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  200
     public DefaultResolver() {
 69  200
     }
 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  700
         if (expression == null || expression.length() == 0) {
 81  2
             return -1;
 82  
         }
 83  8332
         for (int i = 0; i < expression.length(); i++) {
 84  7900
             char c = expression.charAt(i);
 85  7900
             if (c == NESTED || c == MAPPED_START) {
 86  24
                 return -1;
 87  7876
             } else if (c == INDEXED_START) {
 88  242
                 int end = expression.indexOf(INDEXED_END, i);
 89  242
                 if (end < 0) {
 90  1
                     throw new IllegalArgumentException("Missing End Delimiter");
 91  
                 }
 92  241
                 String value = expression.substring(i + 1, end);
 93  241
                 if (value.length() == 0) {
 94  1
                     throw new IllegalArgumentException("No Index Value");
 95  
                 }
 96  240
                 int index = 0;
 97  
                 try {
 98  240
                     index = Integer.parseInt(value, 10);
 99  1
                 } catch (Exception e) {
 100  1
                     throw new IllegalArgumentException("Invalid index value '"
 101  
                             + value + "'");
 102  239
                 }
 103  239
                 return index;
 104  
             }
 105  
         }
 106  432
         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  589
         if (expression == null || expression.length() == 0) {
 118  2
             return null;
 119  
         }
 120  7325
         for (int i = 0; i < expression.length(); i++) {
 121  6897
             char c = expression.charAt(i);
 122  6897
             if (c == NESTED || c == INDEXED_START) {
 123  49
                 return null;
 124  6848
             } else if (c == MAPPED_START) {
 125  110
                 int end = expression.indexOf(MAPPED_END, i);
 126  110
                 if (end < 0) {
 127  1
                     throw new IllegalArgumentException("Missing End Delimiter");
 128  
                 }
 129  109
                 return expression.substring(i + 1, end);
 130  
             }
 131  
         }
 132  428
         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  3303
         if (expression == null || expression.length() == 0) {
 143  8
             return expression;
 144  
         }
 145  42191
         for (int i = 0; i < expression.length(); i++) {
 146  39292
             char c = expression.charAt(i);
 147  39292
             if (c == NESTED) {
 148  4
                 return expression.substring(0, i);
 149  39288
             } else if (c == MAPPED_START || c == INDEXED_START) {
 150  392
                 return expression.substring(0, i);
 151  
             }
 152  
         }
 153  2899
         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  5021
         if (expression == null || expression.length() == 0) {
 165  6
             return false;
 166  
         } else {
 167  5015
             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  1876
         if (expression == null || expression.length() == 0) {
 180  2
             return false;
 181  
         }
 182  22801
         for (int i = 0; i < expression.length(); i++) {
 183  21050
             char c = expression.charAt(i);
 184  21050
             if (c == NESTED || c == MAPPED_START) {
 185  11
                 return false;
 186  21039
             } else if (c == INDEXED_START) {
 187  112
                 return true;
 188  
             }
 189  
         }
 190  1751
         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  1964
         if (expression == null || expression.length() == 0) {
 202  2
             return false;
 203  
         }
 204  23780
         for (int i = 0; i < expression.length(); i++) {
 205  22010
             char c = expression.charAt(i);
 206  22010
             if (c == NESTED || c == INDEXED_START) {
 207  112
                 return false;
 208  21898
             } else if (c == MAPPED_START) {
 209  80
                 return true;
 210  
             }
 211  
         }
 212  1770
         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  5467
         if (expression == null || expression.length() == 0) {
 224  0
             return null;
 225  
         }
 226  5467
         boolean indexed = false;
 227  5467
         boolean mapped  = false;
 228  70148
         for (int i = 0; i < expression.length(); i++) {
 229  65586
             char c = expression.charAt(i);
 230  65586
             if (indexed) {
 231  495
                 if (c == INDEXED_END) {
 232  243
                     return expression.substring(0, i + 1);
 233  
                 }
 234  65091
             } else if (mapped) {
 235  1686
                 if (c == MAPPED_END) {
 236  149
                     return expression.substring(0, i + 1);
 237  
                 }
 238  
             } else {
 239  63405
                 if (c == NESTED) {
 240  513
                     return expression.substring(0, i);
 241  62892
                 } else if (c == MAPPED_START) {
 242  149
                     mapped = true;
 243  62743
                 } else if (c == INDEXED_START) {
 244  243
                     indexed = true;
 245  
                 }
 246  
             }
 247  
         }
 248  4562
         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  5236
         if (expression == null || expression.length() == 0) {
 261  0
             return null;
 262  
         }
 263  5236
         String property = next(expression);
 264  5236
         if (expression.length() == property.length()) {
 265  4795
             return null;
 266  
         }
 267  441
         int start = property.length();
 268  441
         if (expression.charAt(start) == NESTED) {
 269  363
             start++;
 270  
         }
 271  441
         return expression.substring(start);
 272  
     }
 273  
 }