001 // Copyright 2006, 2008, 2010 The Apache Software Foundation 002 // 003 // Licensed under the Apache License, Version 2.0 (the "License"); 004 // you may not use this file except in compliance with the License. 005 // You may obtain a copy of the License at 006 // 007 // http://www.apache.org/licenses/LICENSE-2.0 008 // 009 // Unless required by applicable law or agreed to in writing, software 010 // distributed under the License is distributed on an "AS IS" BASIS, 011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 // See the License for the specific language governing permissions and 013 // limitations under the License. 014 015 package org.apache.tapestry5.ioc.internal.services; 016 017 import org.apache.tapestry5.ioc.AnnotationProvider; 018 import org.apache.tapestry5.ioc.internal.util.CollectionFactory; 019 import org.apache.tapestry5.ioc.services.ClassPropertyAdapter; 020 import org.apache.tapestry5.ioc.services.PropertyAdapter; 021 022 import java.lang.annotation.Annotation; 023 import java.lang.reflect.*; 024 import java.util.List; 025 026 public class PropertyAdapterImpl implements PropertyAdapter 027 { 028 private final ClassPropertyAdapter classAdapter; 029 030 private final String name; 031 032 private final Method readMethod; 033 034 private final Method writeMethod; 035 036 private final Class type; 037 038 private final boolean castRequired; 039 040 // Synchronized by this; lazily initialized 041 private AnnotationProvider annotationProvider; 042 043 private final Field field; 044 045 private final Class declaringClass; 046 047 PropertyAdapterImpl(ClassPropertyAdapter classAdapter, String name, Class type, Method readMethod, 048 Method writeMethod) 049 { 050 this.classAdapter = classAdapter; 051 this.name = name; 052 this.type = type; 053 054 this.readMethod = readMethod; 055 this.writeMethod = writeMethod; 056 057 declaringClass = readMethod != null ? readMethod.getDeclaringClass() : writeMethod.getDeclaringClass(); 058 059 castRequired = readMethod != null && readMethod.getReturnType() != type; 060 061 field = null; 062 } 063 064 PropertyAdapterImpl(ClassPropertyAdapter classAdapter, String name, Class type, Field field) 065 { 066 this.classAdapter = classAdapter; 067 this.name = name; 068 this.type = type; 069 070 this.field = field; 071 072 declaringClass = field.getDeclaringClass(); 073 074 castRequired = field.getType() != type; 075 076 readMethod = null; 077 writeMethod = null; 078 } 079 080 public String getName() 081 { 082 return name; 083 } 084 085 public Method getReadMethod() 086 { 087 return readMethod; 088 } 089 090 public Class getType() 091 { 092 return type; 093 } 094 095 public Method getWriteMethod() 096 { 097 return writeMethod; 098 } 099 100 public boolean isRead() 101 { 102 return field != null || readMethod != null; 103 } 104 105 public boolean isUpdate() 106 { 107 return writeMethod != null || (field != null && !isFinal(field)); 108 } 109 110 private boolean isFinal(Member member) 111 { 112 return Modifier.isFinal(member.getModifiers()); 113 } 114 115 public Object get(Object instance) 116 { 117 if (field == null && readMethod == null) 118 throw new UnsupportedOperationException(ServiceMessages.readNotSupported(instance, name)); 119 120 Throwable fail; 121 122 try 123 { 124 if (field == null) 125 return readMethod.invoke(instance); 126 else 127 return field.get(instance); 128 } catch (InvocationTargetException ex) 129 { 130 fail = ex.getTargetException(); 131 } catch (Exception ex) 132 { 133 fail = ex; 134 } 135 136 throw new RuntimeException(ServiceMessages.readFailure(name, instance, fail), fail); 137 } 138 139 public void set(Object instance, Object value) 140 { 141 if (field == null && writeMethod == null) 142 throw new UnsupportedOperationException(ServiceMessages.writeNotSupported(instance, name)); 143 144 Throwable fail; 145 146 try 147 { 148 if (field == null) 149 writeMethod.invoke(instance, value); 150 else 151 field.set(instance, value); 152 153 return; 154 } catch (InvocationTargetException ex) 155 { 156 fail = ex.getTargetException(); 157 } catch (Exception ex) 158 { 159 fail = ex; 160 } 161 162 throw new RuntimeException(ServiceMessages.writeFailure(name, instance, fail), fail); 163 } 164 165 public <T extends Annotation> T getAnnotation(Class<T> annotationClass) 166 { 167 return getAnnnotationProvider().getAnnotation(annotationClass); 168 } 169 170 /** 171 * Creates (as needed) the annotation provider for this property. 172 */ 173 private synchronized AnnotationProvider getAnnnotationProvider() 174 { 175 if (annotationProvider == null) 176 { 177 List<AnnotationProvider> providers = CollectionFactory.newList(); 178 179 if (readMethod != null) 180 providers.add(new AccessableObjectAnnotationProvider(readMethod)); 181 182 if (writeMethod != null) 183 providers.add(new AccessableObjectAnnotationProvider(writeMethod)); 184 185 // There's an assumption here, that the fields match the property name (we ignore case 186 // which leads to a manageable ambiguity) and that the field and the getter/setter 187 // are in the same class (i.e., that we don't have a getter exposing a protected field inherted 188 // from a base class, or some other oddity). 189 190 Class cursor = getBeanType(); 191 192 out: 193 while (cursor != null) 194 { 195 for (Field f : cursor.getDeclaredFields()) 196 { 197 if (f.getName().equalsIgnoreCase(name)) 198 { 199 providers.add(new AccessableObjectAnnotationProvider(f)); 200 201 break out; 202 } 203 } 204 205 cursor = cursor.getSuperclass(); 206 } 207 208 annotationProvider = AnnotationProviderChain.create(providers); 209 } 210 211 return annotationProvider; 212 } 213 214 public boolean isCastRequired() 215 { 216 return castRequired; 217 } 218 219 public ClassPropertyAdapter getClassAdapter() 220 { 221 return classAdapter; 222 } 223 224 public Class getBeanType() 225 { 226 return classAdapter.getBeanType(); 227 } 228 229 public boolean isField() 230 { 231 return field != null; 232 } 233 234 public Field getField() 235 { 236 return field; 237 } 238 239 public Class getDeclaringClass() 240 { 241 return declaringClass; 242 } 243 }