001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/metaobj/tags/sakai_2-4-1/metaobj-util/tool-lib/src/java/org/sakaiproject/metaobj/utils/mvc/impl/BeanWrapperBase.java $
003: * $Id: BeanWrapperBase.java 14230 2006-09-05 18:02:51Z chmaurer@iupui.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2004, 2005, 2006 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.sakaiproject.metaobj.utils.mvc.impl;
021:
022: import java.beans.PropertyEditor;
023: import java.util.ArrayList;
024: import java.util.HashMap;
025: import java.util.Iterator;
026: import java.util.List;
027: import java.util.Map;
028:
029: import org.apache.commons.logging.Log;
030: import org.apache.commons.logging.LogFactory;
031: import org.springframework.beans.BeanWrapper;
032: import org.springframework.beans.BeanWrapperImpl;
033: import org.springframework.beans.BeansException;
034:
035: /**
036: * Created by IntelliJ IDEA.
037: * User: John Ellis
038: * Date: Apr 23, 2004
039: * Time: 1:50:42 PM
040: * To change this template use File | Settings | File Templates.
041: */
042: public abstract class BeanWrapperBase extends BeanWrapperImpl {
043: protected final Log logger = LogFactory.getLog(getClass());
044: protected List customEditors = new ArrayList();
045:
046: /**
047: * The nested path of the object
048: */
049: private String nestedPath = "";
050:
051: /* Map with cached nested BeanWrappers */
052: private Map nestedBeanWrappers;
053:
054: public BeanWrapperBase() {
055: }
056:
057: /**
058: * Create new BeanWrapperImpl for the given object.
059: *
060: * @param object object wrapped by this BeanWrapper.
061: * @throws org.springframework.beans.BeansException
062: * if the object cannot be wrapped by a BeanWrapper
063: */
064: public BeanWrapperBase(Object object) throws BeansException {
065: super .setWrappedInstance(object);
066: }
067:
068: /**
069: * Create new BeanWrapperImpl for the given object,
070: * registering a nested path that the object is in.
071: *
072: * @param object object wrapped by this BeanWrapper.
073: * @param nestedPath the nested path of the object
074: * @param rootObject the root object at the top of the path
075: * @throws org.springframework.beans.BeansException
076: * if the object cannot be wrapped by a BeanWrapper
077: */
078: public BeanWrapperBase(Object object, String nestedPath,
079: Object rootObject) throws BeansException {
080: super (object, nestedPath, rootObject);
081: this .nestedPath = nestedPath;
082: }
083:
084: public Object getPropertyValue(String propertyName)
085: throws BeansException {
086: if (isNestedProperty(propertyName)) {
087: BeanWrapper nestedBw = getBeanWrapperForPropertyPath(propertyName);
088: return nestedBw
089: .getPropertyValue(getFinalPath(propertyName));
090: } else {
091: return super .getPropertyValue(propertyName);
092: }
093: }
094:
095: /**
096: * Recursively navigate to return a BeanWrapper for the nested property path.
097: *
098: * @param propertyPath property property path, which may be nested
099: * @return a BeanWrapper for the target bean
100: */
101: protected BeanWrapperImpl getBeanWrapperForPropertyPath(
102: String propertyPath) {
103: int pos = propertyPath.indexOf(NESTED_PROPERTY_SEPARATOR);
104: // Handle nested properties recursively
105: if (pos > -1) {
106: String nestedProperty = propertyPath.substring(0, pos);
107: String nestedPath = propertyPath.substring(pos + 1);
108: logger.debug("Navigating to nested property '"
109: + nestedProperty + "' of property path '"
110: + propertyPath + "'");
111: BeanWrapperImpl nestedBw = getNestedBeanWrapper(nestedProperty);
112:
113: if (nestedBw instanceof BeanWrapperBase) {
114: return ((BeanWrapperBase) nestedBw)
115: .getBeanWrapperForPropertyPath(nestedPath);
116: } else {
117: return nestedBw;
118: }
119: } else {
120: return this ;
121: }
122: }
123:
124: /**
125: * Retrieve a BeanWrapper for the given nested property.
126: * Create a new one if not found in the cache.
127: * <p>Note: Caching nested BeanWrappers is necessary now,
128: * to keep registered custom editors for nested properties.
129: *
130: * @param nestedProperty property to create the BeanWrapper for
131: * @return the BeanWrapper instance, either cached or newly created
132: */
133: protected BeanWrapperImpl getNestedBeanWrapper(String nestedProperty) {
134: if (this .nestedBeanWrappers == null) {
135: this .nestedBeanWrappers = new HashMap();
136: }
137:
138: BeanWrapperImpl nextWrapper = (BeanWrapperImpl) nestedBeanWrappers
139: .get(nestedProperty);
140:
141: if (nextWrapper == null) {
142: nextWrapper = createNestedWrapper(nestedPath,
143: nestedProperty);
144: ;
145: nestedBeanWrappers.put(nestedProperty, nextWrapper);
146: return nextWrapper;
147: } else if (nextWrapper instanceof BeanWrapperBase) {
148: return ((BeanWrapperBase) nextWrapper)
149: .getBeanWrapperForPropertyPath(nestedProperty);
150: } else {
151: return nextWrapper;
152: }
153: }
154:
155: protected BeanWrapperImpl createNestedWrapper(String parentPath,
156: String nestedProperty) {
157: BeanWrapperImpl nextWrapper = null;
158:
159: nextWrapper = constructWrapper(this
160: .getPropertyValue(nestedProperty), parentPath
161: + NESTED_PROPERTY_SEPARATOR + nestedProperty);
162:
163: nestedBeanWrappers.put(nestedProperty, nextWrapper);
164:
165: for (Iterator i = customEditors.iterator(); i.hasNext();) {
166: CustomEditorHolder holder = (CustomEditorHolder) i.next();
167: nextWrapper.registerCustomEditor(holder.requiredType,
168: holder.propertyPath, holder.propertyEditor);
169: }
170:
171: return nextWrapper;
172: }
173:
174: abstract protected BeanWrapperBase constructWrapper(
175: Object propertyValue, String nestedProperty);
176:
177: public void registerCustomEditor(Class requiredType,
178: String propertyPath, PropertyEditor propertyEditor) {
179: CustomEditorHolder holder = new CustomEditorHolder();
180: holder.requiredType = requiredType;
181: holder.propertyPath = propertyPath;
182: holder.propertyEditor = propertyEditor;
183:
184: customEditors.add(holder);
185:
186: super .registerCustomEditor(requiredType, propertyPath,
187: propertyEditor);
188: }
189:
190: public void setPropertyValue(String propertyName, Object value)
191: throws BeansException {
192:
193: if (isNestedProperty(propertyName)) {
194: BeanWrapper nestedBw = getBeanWrapperForPropertyPath(propertyName);
195: nestedBw
196: .setPropertyValue(getFinalPath(propertyName), value);
197: return;
198: } else {
199: super .setPropertyValue(propertyName, value);
200: }
201: }
202:
203: /**
204: * Is the property nested? That is, does it contain the nested
205: * property separator (usually ".").
206: *
207: * @param path property path
208: * @return boolean is the property nested
209: */
210: protected boolean isNestedProperty(String path) {
211: return path.indexOf(NESTED_PROPERTY_SEPARATOR) != -1;
212: }
213:
214: /**
215: * Get the last component of the path. Also works if not nested.
216: *
217: * @param nestedPath property path we know is nested
218: * @return last component of the path (the property on the target bean)
219: */
220: protected String getFinalPath(String nestedPath) {
221: String finalPath = nestedPath.substring(nestedPath
222: .lastIndexOf(NESTED_PROPERTY_SEPARATOR) + 1);
223: if (logger.isDebugEnabled() && !nestedPath.equals(finalPath)) {
224: logger.debug("Final path in nested property value '"
225: + nestedPath + "' is '" + finalPath + "'");
226: }
227: return finalPath;
228: }
229:
230: public PropertyEditor findCustomEditor(Class requiredType,
231: String propertyPath) {
232: if (propertyPath != null && isNestedProperty(propertyPath)) {
233: BeanWrapperImpl bw = getBeanWrapperForPropertyPath(propertyPath);
234: return bw.findCustomEditor(requiredType,
235: getFinalPath(propertyPath));
236: } else {
237: return super .findCustomEditor(requiredType, propertyPath);
238: }
239: }
240:
241: private class CustomEditorHolder {
242: public Class requiredType;
243: public String propertyPath;
244: public PropertyEditor propertyEditor;
245: }
246:
247: }
|