001 /******************************************************************************* 002 * Portions created by Sebastian Thomschke are copyright (c) 2005-2011 Sebastian 003 * Thomschke. 004 * 005 * All Rights Reserved. This program and the accompanying materials 006 * are made available under the terms of the Eclipse Public License v1.0 007 * which accompanies this distribution, and is available at 008 * http://www.eclipse.org/legal/epl-v10.html 009 * 010 * Contributors: 011 * Sebastian Thomschke - initial implementation. 012 *******************************************************************************/ 013 package net.sf.oval.configuration.annotation; 014 015 import static net.sf.oval.Validator.getCollectionFactory; 016 017 import java.lang.annotation.Annotation; 018 import java.lang.reflect.AccessibleObject; 019 import java.lang.reflect.Field; 020 import java.lang.reflect.Method; 021 import java.util.Collection; 022 import java.util.List; 023 024 import javax.persistence.Basic; 025 import javax.persistence.Column; 026 import javax.persistence.GeneratedValue; 027 import javax.persistence.Lob; 028 import javax.persistence.ManyToMany; 029 import javax.persistence.ManyToOne; 030 import javax.persistence.OneToMany; 031 import javax.persistence.OneToOne; 032 import javax.persistence.Version; 033 034 import net.sf.oval.Check; 035 import net.sf.oval.collection.CollectionFactory; 036 import net.sf.oval.configuration.Configurer; 037 import net.sf.oval.configuration.pojo.elements.ClassConfiguration; 038 import net.sf.oval.configuration.pojo.elements.ConstraintSetConfiguration; 039 import net.sf.oval.configuration.pojo.elements.FieldConfiguration; 040 import net.sf.oval.configuration.pojo.elements.MethodConfiguration; 041 import net.sf.oval.configuration.pojo.elements.MethodReturnValueConfiguration; 042 import net.sf.oval.constraint.AssertValidCheck; 043 import net.sf.oval.constraint.LengthCheck; 044 import net.sf.oval.constraint.NotNullCheck; 045 import net.sf.oval.constraint.RangeCheck; 046 import net.sf.oval.internal.util.ReflectionUtils; 047 048 /** 049 * Constraints configurer that interprets certain EJB3 JPA annotations: 050 * <ul> 051 * <li>javax.persistence.Basic(optional=false) => net.sf.oval.constraint.NotNullCheck 052 * <li>javax.persistence.OneToOne(optional=false) => net.sf.oval.constraint.NotNullCheck, net.sf.oval.constraint.AssertValidCheck 053 * <li>javax.persistence.ManyToOne(optional=false) => net.sf.oval.constraint.NotNullCheck, net.sf.oval.constraint.AssertValidCheck 054 * <li>javax.persistence.ManyToMany => net.sf.oval.constraint.AssertValidCheck 055 * <li>javax.persistence.Column(nullable=false) => net.sf.oval.constraint.NotNullCheck 056 * <li>javax.persistence.Column(length=5) => net.sf.oval.constraint.LengthCheck 057 * </ul> 058 * @author Sebastian Thomschke 059 */ 060 public class JPAAnnotationsConfigurer implements Configurer 061 { 062 protected Boolean applyFieldConstraintsToSetters; 063 protected Boolean applyFieldConstraintsToConstructors; 064 065 /** 066 * @return the applyFieldConstraintsToConstructors 067 */ 068 public Boolean getApplyFieldConstraintsToConstructors() 069 { 070 return applyFieldConstraintsToConstructors; 071 } 072 073 /** 074 * {@inheritDoc} 075 */ 076 public ClassConfiguration getClassConfiguration(final Class< ? > clazz) 077 { 078 final CollectionFactory cf = getCollectionFactory(); 079 080 final ClassConfiguration config = new ClassConfiguration(); 081 config.type = clazz; 082 config.applyFieldConstraintsToConstructors = applyFieldConstraintsToConstructors; 083 config.applyFieldConstraintsToSetters = applyFieldConstraintsToSetters; 084 085 List<Check> checks = cf.createList(2); 086 087 /* 088 * determine field checks 089 */ 090 for (final Field field : config.type.getDeclaredFields()) 091 { 092 093 // loop over all annotations of the current field 094 for (final Annotation annotation : field.getAnnotations()) 095 if (annotation instanceof Basic) 096 initializeChecks((Basic) annotation, checks); 097 else if (annotation instanceof Column) 098 initializeChecks((Column) annotation, checks, field); 099 else if (annotation instanceof OneToOne) 100 initializeChecks((OneToOne) annotation, checks); 101 else if (annotation instanceof ManyToOne) 102 initializeChecks((ManyToOne) annotation, checks); 103 else if (annotation instanceof ManyToMany) 104 initializeChecks((ManyToMany) annotation, checks); 105 else if (annotation instanceof OneToMany) initializeChecks((OneToMany) annotation, checks); 106 if (checks.size() > 0) 107 { 108 if (config.fieldConfigurations == null) config.fieldConfigurations = cf.createSet(8); 109 110 final FieldConfiguration fc = new FieldConfiguration(); 111 fc.name = field.getName(); 112 fc.checks = checks; 113 checks = cf.createList(); // create a new list for the next field with checks 114 config.fieldConfigurations.add(fc); 115 } 116 } 117 118 /* 119 * determine getter checks 120 */ 121 for (final Method method : config.type.getDeclaredMethods()) 122 { 123 // consider getters only 124 if (!ReflectionUtils.isGetter(method)) continue; 125 126 // loop over all annotations 127 for (final Annotation annotation : method.getAnnotations()) 128 if (annotation instanceof Basic) 129 initializeChecks((Basic) annotation, checks); 130 else if (annotation instanceof Column) 131 initializeChecks((Column) annotation, checks, method); 132 else if (annotation instanceof OneToOne) 133 initializeChecks((OneToOne) annotation, checks); 134 else if (annotation instanceof ManyToOne) 135 initializeChecks((ManyToOne) annotation, checks); 136 else if (annotation instanceof ManyToMany) 137 initializeChecks((ManyToMany) annotation, checks); 138 else if (annotation instanceof OneToMany) initializeChecks((OneToMany) annotation, checks); 139 140 // check if anything has been configured for this method at all 141 if (checks.size() > 0) 142 { 143 if (config.methodConfigurations == null) config.methodConfigurations = cf.createSet(2); 144 145 final MethodConfiguration mc = new MethodConfiguration(); 146 mc.name = method.getName(); 147 mc.isInvariant = true; 148 mc.returnValueConfiguration = new MethodReturnValueConfiguration(); 149 mc.returnValueConfiguration.checks = checks; 150 checks = cf.createList(); // create a new list for the next method having return value checks 151 config.methodConfigurations.add(mc); 152 } 153 } 154 return config; 155 } 156 157 /** 158 * {@inheritDoc} 159 */ 160 public ConstraintSetConfiguration getConstraintSetConfiguration(final String constraintSetId) 161 { 162 return null; 163 } 164 165 protected void initializeChecks(final Basic annotation, final Collection<Check> checks) 166 { 167 assert annotation != null; 168 assert checks != null; 169 170 if (!annotation.optional()) checks.add(new NotNullCheck()); 171 } 172 173 protected void initializeChecks(final Column annotation, final Collection<Check> checks, 174 final AccessibleObject fieldOrMethod) 175 { 176 assert annotation != null; 177 assert checks != null; 178 179 /* If the value is generated (annotated with @GeneratedValue) it is allowed to be null 180 * before the entity has been persisted, same is true in case of optimistic locking 181 * when a field is annotated with @Version. 182 * Therefore and because of the fact that there is no generic way to determine if an entity 183 * has been persisted already, a not-null check will not be performed for such fields. 184 */ 185 if (!annotation.nullable() && !fieldOrMethod.isAnnotationPresent(GeneratedValue.class) 186 && !fieldOrMethod.isAnnotationPresent(Version.class)) checks.add(new NotNullCheck()); 187 188 // only consider length parameter if @Lob is not present 189 if (!fieldOrMethod.isAnnotationPresent(Lob.class)) 190 { 191 final LengthCheck lengthCheck = new LengthCheck(); 192 lengthCheck.setMax(annotation.length()); 193 checks.add(lengthCheck); 194 } 195 196 // only consider precision/scale for numeric fields 197 if (annotation.precision() > 0 198 && Number.class.isAssignableFrom(fieldOrMethod instanceof Field ? ((Field) fieldOrMethod).getType() 199 : ((Method) fieldOrMethod).getReturnType())) 200 { 201 /* 202 * precision = 6, scale = 2 => -9999.99<=x<=9999.99 203 * precision = 4, scale = 1 => -999.9<=x<=999.9 204 */ 205 final RangeCheck rangeCheck = new RangeCheck(); 206 rangeCheck.setMax(Math.pow(10, annotation.precision() - annotation.scale()) 207 - Math.pow(0.1, annotation.scale())); 208 rangeCheck.setMin(-1 * rangeCheck.getMax()); 209 checks.add(rangeCheck); 210 } 211 } 212 213 protected void initializeChecks(final ManyToMany annotation, final Collection<Check> checks) 214 { 215 assert annotation != null; 216 assert checks != null; 217 218 checks.add(new AssertValidCheck()); 219 } 220 221 protected void initializeChecks(final ManyToOne annotation, final Collection<Check> checks) 222 { 223 assert annotation != null; 224 assert checks != null; 225 226 if (!annotation.optional()) checks.add(new NotNullCheck()); 227 checks.add(new AssertValidCheck()); 228 } 229 230 protected void initializeChecks(final OneToMany annotation, final Collection<Check> checks) 231 { 232 assert annotation != null; 233 assert checks != null; 234 235 checks.add(new AssertValidCheck()); 236 } 237 238 protected void initializeChecks(final OneToOne annotation, final Collection<Check> checks) 239 { 240 assert annotation != null; 241 assert checks != null; 242 243 if (!annotation.optional()) checks.add(new NotNullCheck()); 244 checks.add(new AssertValidCheck()); 245 } 246 247 /** 248 * @return the applyFieldConstraintsToSetter 249 */ 250 public Boolean isApplyFieldConstraintsToSetter() 251 { 252 return applyFieldConstraintsToSetters; 253 } 254 255 /** 256 * @param applyFieldConstraintsToConstructors the applyFieldConstraintsToConstructors to set 257 */ 258 public void setApplyFieldConstraintsToConstructors(final Boolean applyFieldConstraintsToConstructors) 259 { 260 this.applyFieldConstraintsToConstructors = applyFieldConstraintsToConstructors; 261 } 262 263 /** 264 * @param applyFieldConstraintsToSetters the applyFieldConstraintsToSetter to set 265 */ 266 public void setApplyFieldConstraintsToSetters(final Boolean applyFieldConstraintsToSetters) 267 { 268 this.applyFieldConstraintsToSetters = applyFieldConstraintsToSetters; 269 } 270 271 }