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; 014 015 import static net.sf.oval.Validator.*; 016 017 import java.io.Serializable; 018 import java.util.Collections; 019 import java.util.Map; 020 021 import net.sf.oval.context.OValContext; 022 import net.sf.oval.expression.ExpressionLanguage; 023 024 /** 025 * Partial implementation of check classes. 026 * 027 * @author Sebastian Thomschke 028 */ 029 public abstract class AbstractCheck implements Check 030 { 031 private static final long serialVersionUID = 1L; 032 033 private OValContext context; 034 private String errorCode; 035 private String message; 036 private Map<String, ? extends Serializable> messageVariables; 037 private Map<String, ? extends Serializable> messageVariablesUnmodifiable; 038 private boolean messageVariablesUpToDate = true; 039 private String[] profiles; 040 private int severity; 041 private ConstraintTarget[] appliesTo; 042 private String target; 043 private String when; 044 private transient String whenFormula; 045 private transient String whenLang; 046 047 protected Map<String, ? extends Serializable> createMessageVariables() 048 { 049 return null; 050 } 051 052 /** 053 * {@inheritDoc} 054 */ 055 public ConstraintTarget[] getAppliesTo() 056 { 057 return appliesTo == null ? getAppliesToDefault() : appliesTo; 058 } 059 060 /** 061 * 062 * @return the default behavior when the constraint is validated for a array/map/collection reference. 063 */ 064 protected ConstraintTarget[] getAppliesToDefault() 065 { 066 // default behavior is only validate the array/map/collection reference and not the contained keys/values 067 return new ConstraintTarget[]{ConstraintTarget.CONTAINER}; 068 } 069 070 /** 071 * {@inheritDoc} 072 */ 073 public OValContext getContext() 074 { 075 return context; 076 } 077 078 /** 079 * {@inheritDoc} 080 */ 081 public String getErrorCode() 082 { 083 /* 084 * if the error code has not been initialized (which might be the case when using XML configuration), 085 * construct the string based on this class' name minus the appendix "Check" 086 */ 087 if (errorCode == null) 088 { 089 final String className = getClass().getName(); 090 if (className.endsWith("Check")) 091 errorCode = className.substring(0, getClass().getName().length() - "Check".length()); 092 else 093 errorCode = className; 094 } 095 return errorCode; 096 } 097 098 /** 099 * {@inheritDoc} 100 */ 101 public String getMessage() 102 { 103 /* 104 * if the message has not been initialized (which might be the case when using XML configuration), 105 * construct the string based on this class' name minus the appendix "Check" plus the appendix ".violated" 106 */ 107 if (message == null) 108 { 109 final String className = getClass().getName(); 110 if (className.endsWith("Check")) 111 message = className.substring(0, getClass().getName().length() - "Check".length()) + ".violated"; 112 else 113 message = className + ".violated"; 114 } 115 return message; 116 } 117 118 /** 119 * Values that are used to fill place holders when rendering the error message. 120 * A key "min" with a value "4" will replace the place holder {min} in an error message 121 * like "Value cannot be smaller than {min}" with the string "4". 122 * 123 * <b>Note:</b> Override {@link #createMessageVariables()} to create and fill the map 124 * 125 * @return an unmodifiable map 126 */ 127 public final Map<String, ? extends Serializable> getMessageVariables() 128 { 129 if (!messageVariablesUpToDate) 130 { 131 messageVariables = createMessageVariables(); 132 if (messageVariables == null) 133 messageVariablesUnmodifiable = null; 134 else 135 messageVariablesUnmodifiable = Collections.unmodifiableMap(messageVariables); 136 messageVariablesUpToDate = true; 137 } 138 return messageVariablesUnmodifiable; 139 } 140 141 /** 142 * {@inheritDoc} 143 */ 144 public String[] getProfiles() 145 { 146 return profiles; 147 } 148 149 /** 150 * {@inheritDoc} 151 */ 152 public int getSeverity() 153 { 154 return severity; 155 } 156 157 /** 158 * @return the target 159 */ 160 public String getTarget() 161 { 162 return target; 163 } 164 165 /** 166 * {@inheritDoc} 167 */ 168 public String getWhen() 169 { 170 return when; 171 } 172 173 /** 174 * {@inheritDoc} 175 */ 176 public boolean isActive(final Object validatedObject, final Object valueToValidate, final Validator validator) 177 { 178 if (when == null) return true; 179 180 // this triggers parsing of when, happens when this check instance was deserialized 181 if (whenLang == null) setWhen(when); 182 183 final Map<String, Object> values = getCollectionFactory().createMap(); 184 values.put("_value", valueToValidate); 185 values.put("_this", validatedObject); 186 187 final ExpressionLanguage el = validator.getExpressionLanguageRegistry().getExpressionLanguage(whenLang); 188 return el.evaluateAsBoolean(whenFormula, values); 189 } 190 191 /** 192 * Calling this method indicates that the {@link #createMessageVariables()} method needs to be called before the message 193 * for the next violation of this check is rendered. 194 */ 195 protected void requireMessageVariablesRecreation() 196 { 197 messageVariablesUpToDate = false; 198 } 199 200 /** 201 * {@inheritDoc} 202 */ 203 public void setAppliesTo(final ConstraintTarget... targets) 204 { 205 appliesTo = targets; 206 } 207 208 /** 209 * {@inheritDoc} 210 */ 211 public void setContext(final OValContext context) 212 { 213 this.context = context; 214 } 215 216 /** 217 * {@inheritDoc} 218 */ 219 public void setErrorCode(final String failureCode) 220 { 221 errorCode = failureCode; 222 } 223 224 /** 225 * {@inheritDoc} 226 */ 227 public void setMessage(final String message) 228 { 229 this.message = message; 230 } 231 232 /** 233 * {@inheritDoc} 234 */ 235 public void setProfiles(final String... profiles) 236 { 237 this.profiles = profiles; 238 } 239 240 /** 241 * {@inheritDoc} 242 */ 243 public void setSeverity(final int severity) 244 { 245 this.severity = severity; 246 } 247 248 /** 249 * @param target the target to set 250 */ 251 public void setTarget(final String target) 252 { 253 this.target = target; 254 } 255 256 /** 257 * {@inheritDoc} 258 */ 259 public void setWhen(final String when) 260 { 261 synchronized (this) 262 { 263 if (when == null || when.length() == 0) 264 { 265 this.when = null; 266 whenFormula = null; 267 whenLang = null; 268 } 269 else 270 { 271 final String[] parts = when.split(":", 2); 272 if (parts.length == 0) 273 throw new IllegalArgumentException("[when] is missing the scripting language declaration"); 274 this.when = when; 275 whenLang = parts[0]; 276 whenFormula = parts[1]; 277 } 278 } 279 } 280 }