001    /*******************************************************************************
002     * Portions created by Sebastian Thomschke are copyright (c) 2005-2013 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, final Object invalidValue,
049                            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, final Object invalidValue,
055                            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, final Object invalidValue,
071                            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.size()]);
084            }
085    
086            /**
087             * @return the causes or null of no causes exists
088             */
089            public ConstraintViolation[] getCauses()
090            {
091                    return causes == null ? null : (ConstraintViolation[]) causes.clone();
092            }
093    
094            /**
095             * @return Returns the context where the constraint was declared.
096             *
097             * @see net.sf.oval.context.ClassContext
098             * @see net.sf.oval.context.FieldContext
099             * @see net.sf.oval.context.MethodEntryContext
100             * @see net.sf.oval.context.MethodExitContext
101             * @see net.sf.oval.context.MethodParameterContext
102             * @see net.sf.oval.context.MethodReturnValueContext
103             */
104            public OValContext getCheckDeclaringContext()
105            {
106                    return checkDeclaringContext;
107            }
108    
109            /**
110             * @return the fully qualified class name of the corresponding check
111             */
112            public String getCheckName()
113            {
114                    return checkName;
115            }
116    
117            /**
118             * @return Returns the context where the constraint violation occurred.
119             *
120             * @see net.sf.oval.context.ClassContext
121             * @see net.sf.oval.context.FieldContext
122             * @see net.sf.oval.context.MethodEntryContext
123             * @see net.sf.oval.context.MethodExitContext
124             * @see net.sf.oval.context.MethodParameterContext
125             * @see net.sf.oval.context.MethodReturnValueContext
126             */
127            public OValContext getContext()
128            {
129                    return context;
130            }
131    
132            /**
133             * @return the error code
134             */
135            public String getErrorCode()
136            {
137                    return errorCode;
138            }
139    
140            /**
141             * @return Returns the value that was validated.
142             */
143            public Object getInvalidValue()
144            {
145                    return invalidValue;
146            }
147    
148            /**
149             * @return the localized and rendered message
150             */
151            public String getMessage()
152            {
153                    return message;
154            }
155    
156            /**
157             * @return the raw message specified for the constraint without variable resolution and localization
158             */
159            public String getMessageTemplate()
160            {
161                    return messageTemplate;
162            }
163    
164            /**
165             * Returns the message variables provided by the corresponding check.
166             * @return an unmodifiable map holding the message variables provided by the corresponding check.
167             */
168            public Map<String, ? extends Serializable> getMessageVariables()
169            {
170                    return messageVariables;
171            }
172    
173            /**
174             * @return the severity
175             */
176            public int getSeverity()
177            {
178                    return severity;
179            }
180    
181            /**
182             * @return the validatedObject
183             */
184            public Object getValidatedObject()
185            {
186                    return validatedObject;
187            }
188    
189            /**
190             * see http://java.sun.com/developer/technicalArticles/ALT/serialization/
191             *
192             * @param in
193             * @throws IOException
194             * @throws ClassNotFoundException
195             */
196            private void readObject(final java.io.ObjectInputStream in) throws IOException, ClassNotFoundException
197            {
198                    in.defaultReadObject();
199                    if (in.readBoolean()) validatedObject = in.readObject();
200                    if (in.readBoolean()) invalidValue = in.readObject();
201            }
202    
203            /**
204             * {@inheritDoc}
205             */
206            @Override
207            public String toString()
208            {
209                    return getClass().getName() + ": " + message;
210            }
211    
212            /**
213             * see http://java.sun.com/developer/technicalArticles/ALT/serialization/
214             *
215             * @param out
216             * @throws IOException
217             */
218            private void writeObject(final java.io.ObjectOutputStream out) throws IOException
219            {
220                    out.defaultWriteObject();
221                    if (validatedObject instanceof Serializable)
222                    {
223                            // indicate validatedObject implements Serializable
224                            out.writeBoolean(true);
225                            out.writeObject(validatedObject);
226                    }
227                    else
228                    {
229                            LOG.warn("Field 'validatedObject' not serialized because the field value object " + validatedObject + " of type "
230                                            + invalidValue.getClass() + " does not implement " + Serializable.class.getName());
231    
232                            // indicate validatedObject does not implement Serializable
233                            out.writeBoolean(false);
234                    }
235    
236                    if (invalidValue instanceof Serializable)
237                    {
238                            // indicate value implements Serializable
239                            out.writeBoolean(true);
240                            out.writeObject(invalidValue);
241                    }
242                    else
243                    {
244                            final String warning = //
245                            "Field 'invalidValue' could not be serialized because the field value object {1} does not implement java.io.Serializable.";
246                            LOG.warn(warning, invalidValue);
247                            // indicate value does not implement Serializable
248                            out.writeBoolean(false);
249                    }
250            }
251    }