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 java.io.IOException;
016    import java.io.Serializable;
017    import java.util.List;
018    import java.util.Map;
019    
020    import net.sf.oval.context.OValContext;
021    import net.sf.oval.internal.Log;
022    
023    /**
024     * An instance of this class provides detailed information about a single constraint 
025     * violation that occurred during validation.
026     * 
027     * @author Sebastian Thomschke
028     */
029    public class ConstraintViolation implements Serializable
030    {
031            private static final Log LOG = Log.getLog(ConstraintViolation.class);
032    
033            private static final long serialVersionUID = 1L;
034    
035            private final ConstraintViolation[] causes;
036            private final OValContext checkDeclaringContext;
037            private final String checkName;
038            private final OValContext context;
039            private final String errorCode;
040            private transient Object invalidValue;
041            private final String message;
042            private final String messageTemplate;
043            private final Map<String, ? extends Serializable> messageVariables;
044    
045            private final int severity;
046            private transient Object validatedObject;
047    
048            public ConstraintViolation(final Check check, final String message, final Object validatedObject,
049                            final Object invalidValue, final OValContext context)
050            {
051                    this(check, message, validatedObject, invalidValue, context, (ConstraintViolation[]) null);
052            }
053    
054            public ConstraintViolation(final Check check, final String message, final Object validatedObject,
055                            final Object invalidValue, final OValContext context, final ConstraintViolation... causes)
056            {
057                    checkName = check.getClass().getName();
058                    checkDeclaringContext = check.getContext();
059                    errorCode = check.getErrorCode();
060                    this.message = message;
061                    messageTemplate = check.getMessage();
062                    messageVariables = check.getMessageVariables();
063                    severity = check.getSeverity();
064                    this.validatedObject = validatedObject;
065                    this.invalidValue = invalidValue;
066                    this.context = context;
067                    this.causes = causes != null && causes.length == 0 ? null : causes;
068            }
069    
070            public ConstraintViolation(final Check check, final String message, final Object validatedObject,
071                            final Object invalidValue, final OValContext context, final List<ConstraintViolation> causes)
072            {
073                    checkName = check.getClass().getName();
074                    checkDeclaringContext = check.getContext();
075                    errorCode = check.getErrorCode();
076                    this.message = message;
077                    messageTemplate = check.getMessage();
078                    messageVariables = check.getMessageVariables();
079                    severity = check.getSeverity();
080                    this.validatedObject = validatedObject;
081                    this.invalidValue = invalidValue;
082                    this.context = context;
083                    this.causes = causes == null || causes.size() == 0 ? null : causes.toArray(new ConstraintViolation[causes
084                                    .size()]);
085            }
086    
087            /**
088             * @return the causes or null of no causes exists
089             */
090            public ConstraintViolation[] getCauses()
091            {
092                    return causes == null ? null : causes.clone();
093            }
094    
095            /**
096             * @return Returns the context where the constraint was declared.
097             * 
098             * @see net.sf.oval.context.ClassContext
099             * @see net.sf.oval.context.ConstraintSetContext
100             * @see net.sf.oval.context.FieldContext
101             * @see net.sf.oval.context.MethodEntryContext
102             * @see net.sf.oval.context.MethodExitContext
103             * @see net.sf.oval.context.MethodParameterContext
104             * @see net.sf.oval.context.MethodReturnValueContext
105             */
106            public OValContext getCheckDeclaringContext()
107            {
108                    return checkDeclaringContext;
109            }
110    
111            /**
112             * @return the fully qualified class name of the corresponding check
113             */
114            public String getCheckName()
115            {
116                    return checkName;
117            }
118    
119            /**
120             * @return Returns the context where the constraint violation occurred.
121             * 
122             * @see net.sf.oval.context.ClassContext
123             * @see net.sf.oval.context.FieldContext
124             * @see net.sf.oval.context.MethodEntryContext
125             * @see net.sf.oval.context.MethodExitContext
126             * @see net.sf.oval.context.MethodParameterContext
127             * @see net.sf.oval.context.MethodReturnValueContext
128             */
129            public OValContext getContext()
130            {
131                    return context;
132            }
133    
134            /**
135             * @return the error code
136             */
137            public String getErrorCode()
138            {
139                    return errorCode;
140            }
141    
142            /**
143             * @return Returns the value that was validated.
144             */
145            public Object getInvalidValue()
146            {
147                    return invalidValue;
148            }
149    
150            /**
151             * @return the localized and rendered message
152             */
153            public String getMessage()
154            {
155                    return message;
156            }
157    
158            /**
159             * @return the raw message specified for the constraint without variable resolution and localization
160             */
161            public String getMessageTemplate()
162            {
163                    return messageTemplate;
164            }
165    
166            /**
167             * Returns the message variables provided by the corresponding check.
168             * @return an unmodifiable map holding the message variables provided by the corresponding check.
169             */
170            public Map<String, ? extends Serializable> getMessageVariables()
171            {
172                    return messageVariables;
173            }
174    
175            /**
176             * @return the severity
177             */
178            public int getSeverity()
179            {
180                    return severity;
181            }
182    
183            /**
184             * @return the validatedObject
185             */
186            public Object getValidatedObject()
187            {
188                    return validatedObject;
189            }
190    
191            /**
192             * see http://java.sun.com/developer/technicalArticles/ALT/serialization/
193             * 
194             * @param in
195             * @throws IOException
196             * @throws ClassNotFoundException
197             */
198            private void readObject(final java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
199            {
200                    in.defaultReadObject();
201                    if (in.readBoolean()) validatedObject = in.readObject();
202                    if (in.readBoolean()) invalidValue = in.readObject();
203            }
204    
205            /**
206             * {@inheritDoc}
207             */
208            @Override
209            public String toString()
210            {
211                    return getClass().getName() + ": " + message;
212            }
213    
214            /**
215             * see http://java.sun.com/developer/technicalArticles/ALT/serialization/
216             * 
217             * @param out
218             * @throws IOException
219             */
220            private void writeObject(final java.io.ObjectOutputStream out) throws IOException
221            {
222                    out.defaultWriteObject();
223                    if (validatedObject instanceof Serializable)
224                    {
225                            // indicate validatedObject implements Serializable
226                            out.writeBoolean(true);
227                            out.writeObject(validatedObject);
228                    }
229                    else
230                    {
231                            LOG.warn("Field 'validatedObject' not serialized because the field value object " + validatedObject
232                                            + " of type " + invalidValue.getClass() + " does not implement " + Serializable.class.getName());
233    
234                            // indicate validatedObject does not implement Serializable
235                            out.writeBoolean(false);
236                    }
237    
238                    if (invalidValue instanceof Serializable)
239                    {
240                            // indicate value implements Serializable
241                            out.writeBoolean(true);
242                            out.writeObject(invalidValue);
243                    }
244                    else
245                    {
246                            final String warning = //
247                            "Field 'invalidValue' could not be serialized because the field value object {1} does not implement java.io.Serializable.";
248                            LOG.warn(warning, invalidValue);
249                            // indicate value does not implement Serializable
250                            out.writeBoolean(false);
251                    }
252            }
253    }