001 /*
002 * Copyright (c) 2008, Your Corporation. All Rights Reserved.
003 */
004
005 package org.chenillekit.tapestry.core.models;
006
007 import java.util.List;
008 import java.util.Map;
009
010 import org.apache.tapestry5.OptionGroupModel;
011 import org.apache.tapestry5.OptionModel;
012 import org.apache.tapestry5.internal.OptionModelImpl;
013 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
014 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
015 import org.apache.tapestry5.util.AbstractSelectModel;
016
017 import ognl.Ognl;
018
019 /**
020 * Defines the possible options for a <select> [X]HTML element.
021 * This select model implementation based on <a href="http://www.ognl.org/">OGNL</a>.
022 * <p/>
023 * <h4>Use Case</h4>
024 * your object list contains entities of type <code>User</code>. Each <code>User</code> object contains
025 * an object typed <code>Address</code>, and every <code>Address</code> object contains
026 * a property <code>name</code>.
027 * <p/>
028 * If you want to display in the list the value of the name property you should define a
029 * <code>labelExpression</code> like <code>address.name</code>
030 *
031 * @version $Id: OgnlSelectModel.java 670 2010-07-19 09:22:02Z mlusetti $
032 */
033 public class OgnlSelectModel<T> extends AbstractSelectModel
034 {
035 private static final Map<String, Object> _cache = CollectionFactory.newMap();
036
037 private List<T> _objectList;
038 private String _labelExpression;
039
040 /**
041 * constructs an ognl select model.
042 *
043 * @param labelExpression the ognl expression converted to label.
044 */
045 public OgnlSelectModel(String labelExpression)
046 {
047 this(null, labelExpression);
048 }
049
050 /**
051 * constructs an ognl select model.
052 *
053 * @param objectList list of objects that should listed by select tag
054 * @param labelExpression the ognl expression converted to label.
055 */
056 public OgnlSelectModel(List<T> objectList, String labelExpression)
057 {
058 assert InternalUtils.isNonBlank(labelExpression);
059
060 _labelExpression = labelExpression;
061
062 // more carfully i think, so we copy the object list
063 if (objectList != null)
064 setObjectList(objectList);
065 }
066
067 /**
068 * set the object list.
069 *
070 * @param objectList the object list
071 */
072 public void setObjectList(List<T> objectList)
073 {
074 _objectList = CollectionFactory.newList(objectList);
075 }
076
077 /**
078 * The list of groups, returns always null
079 *
080 * @return always null
081 */
082 public List<OptionGroupModel> getOptionGroups()
083 {
084 return null;
085 }
086
087 /**
088 * The list of ungrouped options, which appear after any grouped options.
089 *
090 * @return the ungrouped options
091 */
092 public List<OptionModel> getOptions()
093 {
094 List<OptionModel> optionModelList = CollectionFactory.newList();
095
096 if (_objectList != null)
097 {
098 for (int i = 1; i <= _objectList.size(); i++)
099 {
100 String label = String.valueOf(getValue(_objectList.get(i - 1), _labelExpression));
101 optionModelList.add(new OptionModelImpl(label, _objectList.get(i - 1)));
102 }
103 }
104
105 return optionModelList;
106 }
107
108 /**
109 * Gets a parsed OGNL expression from the input string.
110 *
111 * @param expression the ognl expression.
112 *
113 * @return value of evaluated expression
114 */
115 private synchronized Object getParsedExpression(String expression)
116 {
117 Object result = _cache.get(expression);
118
119 if (result == null)
120 {
121 try
122 {
123 result = Ognl.parseExpression(expression);
124 }
125 catch (Exception ex)
126 {
127 throw new RuntimeException(ex);
128 }
129
130 _cache.put(expression, result);
131 }
132
133 return result;
134 }
135
136 /**
137 * Reads the current value of the property (or other resource). When reading properties of
138 * objects that are primitive types, this will return an instance of the wrapper type. In some
139 * cases, a binding is read only and this method will throw a runtime exception.
140 *
141 * @param rootObject
142 * @param expression the ognl expression.
143 *
144 * @return value of evaluated expression
145 */
146 private Object getValue(Object rootObject, String expression)
147 {
148 Object ognlExpression = getParsedExpression(expression);
149
150 try
151 {
152 Map context = Ognl.createDefaultContext(rootObject);
153 return Ognl.getValue(ognlExpression, context, rootObject);
154 }
155 catch (Exception ex)
156 {
157 throw new RuntimeException(ex);
158 }
159 }
160 }