| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| DefaultResolver |
|
| 9.444444444444445;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 | } |