001: /*
002: * Copyright (c) 2002-2006 by OpenSymphony
003: * All rights reserved.
004: */
005:
006: package com.opensymphony.xwork.util;
007:
008: import ognl.ObjectPropertyAccessor;
009: import ognl.OgnlException;
010: import ognl.OgnlRuntime;
011: import ognl.SetPropertyAccessor;
012: import org.apache.commons.logging.Log;
013: import org.apache.commons.logging.LogFactory;
014:
015: import com.opensymphony.xwork.ObjectFactory;
016:
017: import java.util.*;
018:
019: /**
020: * @author Gabe
021: */
022: public class XWorkCollectionPropertyAccessor extends
023: SetPropertyAccessor {
024:
025: private static final Log LOG = LogFactory
026: .getLog(XWorkCollectionPropertyAccessor.class);
027: private static final String CONTEXT_COLLECTION_MAP = "xworkCollectionPropertyAccessorContextSetMap";
028:
029: public static final String KEY_PROPERTY_FOR_CREATION = "makeNew";
030:
031: //use a basic object Ognl property accessor here
032: //to access properties of the objects in the Set
033: //so that nothing is put in the context to screw things up
034: private ObjectPropertyAccessor _accessor = new ObjectPropertyAccessor();
035:
036: /**
037: * Gets the property of a Collection by indexing the collection
038: * based on a key property. For example, if the key property were
039: * 'id', this method would convert the key Object to whatever
040: * type the id property was, and then access the Set like it was
041: * a Map returning a JavaBean with the value of id property matching
042: * the input.
043: *
044: * @see ognl.PropertyAccessor#getProperty(java.util.Map, Object, Object)
045: */
046: public Object getProperty(Map context, Object target, Object key)
047: throws OgnlException {
048:
049: LOG.debug("Entering getProperty()");
050:
051: //check if it is a generic type property.
052: //if so, return the value from the
053: //superclass which will determine this.
054: if (!OgnlContextState.isGettingByKeyProperty(context)
055: && !key.equals(KEY_PROPERTY_FOR_CREATION)) {
056: return super .getProperty(context, target, key);
057: } else {
058: //reset context property
059: OgnlContextState.setGettingByKeyProperty(context, false);
060: }
061: Collection c = (Collection) target;
062:
063: //get the bean that this collection is a property of
064: Class lastBeanClass = OgnlContextState
065: .getLastBeanClassAccessed(context);
066:
067: //get the property name that this collection uses
068: String lastPropertyClass = OgnlContextState
069: .getLastBeanPropertyAccessed(context);
070:
071: //if one or the other is null, assume that it isn't
072: //set up correctly so just return whatever the
073: //superclass would
074: if (lastBeanClass == null || lastPropertyClass == null) {
075: OgnlContextState.updateCurrentPropertyPath(context, key);
076: return super .getProperty(context, target, key);
077: }
078:
079: ObjectTypeDeterminer objTypeDeterminer = XWorkConverter
080: .getInstance().getObjectTypeDeterminer();
081:
082: //get the key property to index the
083: //collection with from the ObjectTypeDeterminer
084: String keyProperty = objTypeDeterminer.getKeyProperty(
085: lastBeanClass, lastPropertyClass);
086:
087: //get the collection class of the
088: Class collClass = XWorkConverter.getInstance()
089: .getObjectTypeDeterminer().getElementClass(
090: lastBeanClass, lastPropertyClass, key);
091:
092: Class keyType = null;
093: Class toGetTypeFrom = (collClass != null) ? collClass : c
094: .iterator().next().getClass();
095: try {
096: keyType = OgnlRuntime.getPropertyDescriptor(toGetTypeFrom,
097: keyProperty).getPropertyType();
098: } catch (Exception exc) {
099: throw new OgnlException(
100: "Error getting property descriptor: "
101: + exc.getMessage());
102: }
103:
104: if (OgnlContextState.isCreatingNullObjects(context)) {
105: Map collMap = getSetMap(context, c, keyProperty, collClass);
106: if (key.toString().equals(KEY_PROPERTY_FOR_CREATION)) {
107: //this should return the XWorkList
108: //for this set that contains new entries
109: //then the ListPropertyAccessor will be called
110: //to access it in the next sequence
111: return collMap.get(null);
112: }
113: Object realKey = XWorkConverter.getInstance().convertValue(
114: context, key, keyType);
115: Object value = collMap.get(realKey);
116: if (value == null
117: && OgnlContextState.isCreatingNullObjects(context)
118: && objTypeDeterminer.shouldCreateIfNew(
119: lastBeanClass, lastPropertyClass, c,
120: keyProperty, false)) {
121: //create a new element and
122: //set the value of keyProperty
123: //to be the given value
124: try {
125: value = ObjectFactory.getObjectFactory().buildBean(
126: collClass, context);
127:
128: //set the value of the keyProperty
129: _accessor.setProperty(context, value, keyProperty,
130: realKey);
131:
132: //add the new object to the collection
133: c.add(value);
134:
135: //add to the Map if accessed later
136: collMap.put(realKey, value);
137:
138: } catch (Exception exc) {
139: throw new OgnlException(
140: "Error adding new element to collection",
141: exc);
142:
143: }
144:
145: }
146: return value;
147: } else {
148: if (key.toString().equals(KEY_PROPERTY_FOR_CREATION)) {
149: return null;
150: }
151: //with getting do iteration
152: //don't assume for now it is
153: //optimized to create the Map
154: //and unlike setting, there is
155: //no easy key for the Set
156: Object realKey = XWorkConverter.getInstance().convertValue(
157: context, key, keyType);
158: return getPropertyThroughIteration(context, c, keyProperty,
159: realKey);
160: }
161: }
162:
163: /*
164: * Gets an indexed Map by a given key property with the key being
165: * the value of the property and the value being the
166: */
167: private Map getSetMap(Map context, Collection collection,
168: String property, Class valueClass) throws OgnlException {
169: LOG.debug("getting set Map");
170: String path = OgnlContextState.getCurrentPropertyPath(context);
171: Map map = OgnlContextState.getSetMap(context, path);
172:
173: if (map == null) {
174: LOG.debug("creating set Map");
175: map = new HashMap();
176: map.put(null, new SurrugateList(collection));
177: for (Iterator i = collection.iterator(); i.hasNext();) {
178: Object currTest = i.next();
179: Object currKey = _accessor.getProperty(context,
180: currTest, property);
181: if (currKey != null) {
182: map.put(currKey, currTest);
183: }
184: }
185: OgnlContextState.setSetMap(context, map, path);
186: }
187: return map;
188: }
189:
190: /*
191: * gets a bean with the given
192: */
193: public Object getPropertyThroughIteration(Map context,
194: Collection collection, String property, Object key)
195: throws OgnlException {
196: //TODO
197: for (Iterator i = collection.iterator(); i.hasNext();) {
198: Object currTest = i.next();
199: if (_accessor.getProperty(context, currTest, property)
200: .equals(key)) {
201: return currTest;
202: }
203: }
204: //none found
205: return null;
206: }
207:
208: public void setProperty(Map arg0, Object arg1, Object arg2,
209: Object arg3) throws OgnlException {
210:
211: super .setProperty(arg0, arg1, arg2, arg3);
212: }
213: }
214:
215: /**
216: * @author Gabe
217: */
218: class SurrugateList extends ArrayList {
219:
220: private Collection surrugate;
221:
222: public SurrugateList(Collection surrugate) {
223: this .surrugate = surrugate;
224: }
225:
226: public void add(int arg0, Object arg1) {
227: if (arg1 != null) {
228: surrugate.add(arg1);
229: }
230: super .add(arg0, arg1);
231: }
232:
233: public boolean add(Object arg0) {
234: if (arg0 != null) {
235: surrugate.add(arg0);
236: }
237: return super .add(arg0);
238: }
239:
240: public boolean addAll(Collection arg0) {
241: surrugate.addAll(arg0);
242: return super .addAll(arg0);
243: }
244:
245: public boolean addAll(int arg0, Collection arg1) {
246: surrugate.addAll(arg1);
247: return super .addAll(arg0, arg1);
248: }
249:
250: public Object set(int arg0, Object arg1) {
251: if (arg1 != null) {
252: surrugate.add(arg1);
253: }
254: return super.set(arg0, arg1);
255: }
256: }
|