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}