001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.model;
018
019import java.lang.reflect.Field;
020import java.lang.reflect.Method;
021
022import org.apache.wicket.Application;
023import org.apache.wicket.Session;
024import org.apache.wicket.WicketRuntimeException;
025import org.apache.wicket.core.util.lang.PropertyResolver;
026import org.apache.wicket.core.util.lang.PropertyResolverConverter;
027import org.apache.wicket.util.string.Strings;
028
029/**
030 * Serves as a base class for different kinds of property models. By default, this class uses
031 * {@link PropertyResolver} to resolve expressions on the target model object. Note that the
032 * property resolver by default provides access to private members and methods. If guaranteeing
033 * encapsulation of the target objects is a big concern, you should consider using an alternative
034 * implementation.
035 * 
036 * @see PropertyResolver
037 * @see org.apache.wicket.model.IDetachable
038 * 
039 * @author Chris Turner
040 * @author Eelco Hillenius
041 * @author Jonathan Locke
042 * 
043 * @param <T>
044 *            The Model object type
045 */
046public abstract class AbstractPropertyModel<T> extends ChainingModel<T>
047        implements
048                IObjectClassAwareModel<T>,
049                IPropertyReflectionAwareModel<T>
050{
051        private static final long serialVersionUID = 1L;
052
053        /**
054         * Constructor
055         * 
056         * @param modelObject
057         *            The nested model object
058         */
059        public AbstractPropertyModel(final Object modelObject)
060        {
061                super(modelObject);
062        }
063
064        @Override
065        @SuppressWarnings("unchecked")
066        public T getObject()
067        {
068                final String expression = propertyExpression();
069                if (Strings.isEmpty(expression))
070                {
071                        // Return a meaningful value for an empty property expression
072                        return (T) getInnermostModelOrObject();
073                }
074                else if (expression.startsWith("."))
075                {
076                        throw new IllegalArgumentException(
077                                "Property expressions cannot start with a '.' character");
078                }
079
080                final Object target = getInnermostModelOrObject();
081                if (target != null)
082                {
083                        return (T)PropertyResolver.getValue(expression, target);
084                }
085                return null;
086        }
087
088        /**
089         * Gets the property expression for this model
090         * 
091         * @return The property expression
092         */
093        public final String getPropertyExpression()
094        {
095                return propertyExpression();
096        }
097
098        /**
099         * Applies the property expression on the model object using the given object argument.
100         * 
101         * @param object
102         *            The object that will be used when setting a value on the model object
103         * @see IModel#setObject(Object)
104         */
105        @Override
106        @SuppressWarnings("unchecked")
107        public void setObject(T object)
108        {
109                final String expression = propertyExpression();
110                if (Strings.isEmpty(expression))
111                {
112                        // TODO check, really do this?
113                        // why not just set the target to the object?
114                        Object target = getTarget();
115                        if (target instanceof IModel)
116                        {
117                                ((IModel<T>)target).setObject(object);
118                        }
119                        else
120                        {
121                                setTarget(object);
122                        }
123                }
124                else
125                {
126                        PropertyResolverConverter prc = new PropertyResolverConverter(
127                                        Application.get().getConverterLocator(), Session.get().getLocale());
128                        PropertyResolver.setValue(expression, getInnermostModelOrObject(), object, prc);
129                }
130        }
131
132        /**
133         * @return model object class
134         */
135        @Override
136        @SuppressWarnings("unchecked")
137        public Class<T> getObjectClass()
138        {
139                final String expression = propertyExpression();
140                final Object target = getInnermostModelOrObject();
141
142                if (Strings.isEmpty(expression))
143                {
144                        // Return a meaningful value for an empty property expression
145                        return (Class<T>)(target != null ? target.getClass() : null);
146                }
147
148                if (target != null)
149                {
150                        try
151                        {
152                                return (Class<T>)PropertyResolver.getPropertyClass(expression, target);
153                        }
154                        catch (Exception e)
155                        {
156                                // ignore.
157                        }
158                }
159                else if (getTarget() instanceof IObjectClassAwareModel)
160                {
161                        try
162                        {
163                                Class<?> targetClass = ((IObjectClassAwareModel<?>) getTarget()).getObjectClass();
164                                if (targetClass != null)
165                                {
166                                        return PropertyResolver.getPropertyClass(expression, targetClass);
167                                }
168                        }
169                        catch (WicketRuntimeException e)
170                        {
171                                // it was just a try.
172                        }
173
174                }
175                return null;
176        }
177
178        @Override
179        public Field getPropertyField()
180        {
181                String expression = propertyExpression();
182                if (Strings.isEmpty(expression) == false)
183                {
184                        Object target = getInnermostModelOrObject();
185                        if (target != null)
186                        {
187                                try
188                                {
189                                        return PropertyResolver.getPropertyField(expression, target);
190                                }
191                                catch (Exception ignore)
192                                {
193                                        // ignore.
194                                }
195                        }
196                }
197                return null;
198        }
199
200        @Override
201        public Method getPropertyGetter()
202        {
203                String expression = propertyExpression();
204                if (Strings.isEmpty(expression) == false)
205                {
206                        Object target = getInnermostModelOrObject();
207                        if (target != null)
208                        {
209                                try
210                                {
211                                        return PropertyResolver.getPropertyGetter(expression, target);
212                                }
213                                catch (Exception ignore)
214                                {
215                                }
216                        }
217                }
218                return null;
219        }
220
221        @Override
222        public Method getPropertySetter()
223        {
224                String expression = propertyExpression();
225                if (Strings.isEmpty(expression) == false)
226                {
227                        Object target = getInnermostModelOrObject();
228                        if (target != null)
229                        {
230                                try
231                                {
232                                        return PropertyResolver.getPropertySetter(expression, target);
233                                }
234                                catch (Exception ignore)
235                                {
236                                }
237                        }
238                }
239                return null;
240        }
241
242        /**
243         * @return The property expression for the component
244         */
245        protected abstract String propertyExpression();
246}