View Javadoc
1   /*******************************************************************************
2    * Portions created by Sebastian Thomschke are copyright (c) 2005-2013 Sebastian
3    * Thomschke.
4    *
5    * All Rights Reserved. This program and the accompanying materials
6    * are made available under the terms of the Eclipse Public License v1.0
7    * which accompanies this distribution, and is available at
8    * http://www.eclipse.org/legal/epl-v10.html
9    *
10   * Contributors:
11   *     Sebastian Thomschke - initial implementation.
12   *******************************************************************************/
13  package net.sf.oval;
14  
15  import static net.sf.oval.Validator.*;
16  
17  import java.io.Serializable;
18  import java.util.Collections;
19  import java.util.Map;
20  
21  import net.sf.oval.context.OValContext;
22  import net.sf.oval.expression.ExpressionLanguage;
23  
24  /**
25   * Partial implementation of check classes.
26   *
27   * @author Sebastian Thomschke
28   */
29  public abstract class AbstractCheck implements Check
30  {
31  	private static final long serialVersionUID = 1L;
32  
33  	private OValContext context;
34  	private String errorCode;
35  	private String message;
36  	private Map<String, ? extends Serializable> messageVariables;
37  	private Map<String, ? extends Serializable> messageVariablesUnmodifiable;
38  	private boolean messageVariablesUpToDate = true;
39  	private String[] profiles;
40  	private int severity;
41  	private ConstraintTarget[] appliesTo;
42  	private String target;
43  	private String when;
44  	private transient String whenFormula;
45  	private transient String whenLang;
46  
47  	protected Map<String, ? extends Serializable> createMessageVariables()
48  	{
49  		return null;
50  	}
51  
52  	/**
53  	 * {@inheritDoc}
54  	 */
55  	public ConstraintTarget[] getAppliesTo()
56  	{
57  		return appliesTo == null ? getAppliesToDefault() : appliesTo;
58  	}
59  
60  	/**
61  	 *
62  	 * @return the default behavior when the constraint is validated for a array/map/collection reference.
63  	 */
64  	protected ConstraintTarget[] getAppliesToDefault()
65  	{
66  		// default behavior is only validate the array/map/collection reference and not the contained keys/values
67  		return new ConstraintTarget[]{ConstraintTarget.CONTAINER};
68  	}
69  
70  	/**
71  	 * {@inheritDoc}
72  	 */
73  	public OValContext getContext()
74  	{
75  		return context;
76  	}
77  
78  	/**
79  	 * {@inheritDoc}
80  	 */
81  	public String getErrorCode()
82  	{
83  		/*
84  		 * if the error code has not been initialized (which might be the case when using XML configuration),
85  		 * construct the string based on this class' name minus the appendix "Check"
86  		 */
87  		if (errorCode == null)
88  		{
89  			final String className = getClass().getName();
90  			if (className.endsWith("Check"))
91  				errorCode = className.substring(0, getClass().getName().length() - "Check".length());
92  			else
93  				errorCode = className;
94  		}
95  		return errorCode;
96  	}
97  
98  	/**
99  	 * {@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 	@SuppressWarnings("javadoc")
128 	public final Map<String, ? extends Serializable> getMessageVariables()
129 	{
130 		if (!messageVariablesUpToDate)
131 		{
132 			messageVariables = createMessageVariables();
133 			if (messageVariables == null)
134 				messageVariablesUnmodifiable = null;
135 			else
136 				messageVariablesUnmodifiable = Collections.unmodifiableMap(messageVariables);
137 			messageVariablesUpToDate = true;
138 		}
139 		return messageVariablesUnmodifiable;
140 	}
141 
142 	/**
143 	 * {@inheritDoc}
144 	 */
145 	public String[] getProfiles()
146 	{
147 		return profiles;
148 	}
149 
150 	/**
151 	 * {@inheritDoc}
152 	 */
153 	public int getSeverity()
154 	{
155 		return severity;
156 	}
157 
158 	/**
159 	 * @return the target
160 	 */
161 	public String getTarget()
162 	{
163 		return target;
164 	}
165 
166 	/**
167 	 * {@inheritDoc}
168 	 */
169 	public String getWhen()
170 	{
171 		return when;
172 	}
173 
174 	/**
175 	 * {@inheritDoc}
176 	 */
177 	public boolean isActive(final Object validatedObject, final Object valueToValidate, final Validator validator)
178 	{
179 		if (when == null) return true;
180 
181 		// this triggers parsing of when, happens when this check instance was deserialized
182 		if (whenLang == null) setWhen(when);
183 
184 		final Map<String, Object> values = getCollectionFactory().createMap();
185 		values.put("_value", valueToValidate);
186 		values.put("_this", validatedObject);
187 
188 		final ExpressionLanguage el = validator.getExpressionLanguageRegistry().getExpressionLanguage(whenLang);
189 		return el.evaluateAsBoolean(whenFormula, values);
190 	}
191 
192 	/**
193 	 * Calling this method indicates that the {@link #createMessageVariables()} method needs to be called before the message
194 	 * for the next violation of this check is rendered.
195 	 */
196 	protected void requireMessageVariablesRecreation()
197 	{
198 		messageVariablesUpToDate = false;
199 	}
200 
201 	/**
202 	 * {@inheritDoc}
203 	 */
204 	public void setAppliesTo(final ConstraintTarget... targets)
205 	{
206 		appliesTo = targets;
207 	}
208 
209 	/**
210 	 * {@inheritDoc}
211 	 */
212 	public void setContext(final OValContext context)
213 	{
214 		this.context = context;
215 	}
216 
217 	/**
218 	 * {@inheritDoc}
219 	 */
220 	public void setErrorCode(final String failureCode)
221 	{
222 		errorCode = failureCode;
223 	}
224 
225 	/**
226 	 * {@inheritDoc}
227 	 */
228 	public void setMessage(final String message)
229 	{
230 		this.message = message;
231 	}
232 
233 	/**
234 	 * {@inheritDoc}
235 	 */
236 	public void setProfiles(final String... profiles)
237 	{
238 		this.profiles = profiles;
239 	}
240 
241 	/**
242 	 * {@inheritDoc}
243 	 */
244 	public void setSeverity(final int severity)
245 	{
246 		this.severity = severity;
247 	}
248 
249 	/**
250 	 * @param target the target to set
251 	 */
252 	public void setTarget(final String target)
253 	{
254 		this.target = target;
255 	}
256 
257 	/**
258 	 * {@inheritDoc}
259 	 */
260 	public void setWhen(final String when)
261 	{
262 		synchronized (this)
263 		{
264 			if (when == null || when.length() == 0)
265 			{
266 				this.when = null;
267 				whenFormula = null;
268 				whenLang = null;
269 			}
270 			else
271 			{
272 				final String[] parts = when.split(":", 2);
273 				if (parts.length == 0) 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 }