001/*
002 * ModeShape (http://www.modeshape.org)
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *       http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.modeshape.schematic;
017
018import org.modeshape.schematic.document.Document;
019import org.modeshape.schematic.document.JsonSchema.Type;
020import org.modeshape.schematic.document.Path;
021
022/**
023 * A library of JSON Schema documents. Because JSON Schemas are in fact JSON documents, this library is also a DocumentLibrary.
024 * 
025 * @author Randall Hauch <rhauch@redhat.com> (C) 2011 Red Hat Inc.
026 */
027public interface SchemaLibrary extends DocumentLibrary {
028
029    /**
030     * Validate the supplied document against the JSON Schema with the supplied URI.
031     * 
032     * @param document the document to be validated; may not be null
033     * @param schemaUri the URI of the JSON Schema that should be used to validate the document; may not be null
034     * @return the results of the validation; never null
035     */
036    public Results validate( Document document,
037                             String schemaUri );
038
039    /**
040     * Look for fields within the document (including nested documents) whose values are not of the expected type for the given
041     * schema, but whose values can be converted into the expected type.
042     * <p>
043     * This is often useful when the results from {@link #validate(Document, String)} contain only
044     * {@link Results#hasOnlyTypeMismatchErrors() mismatched type errors}. In such a case, the document can be converted and the
045     * resulting document will likely satisfy the schema.
046     * </p>
047     * 
048     * @param document the document to be validated; may not be null
049     * @param results the results from the {@link #validate(Document, String)} call; may not be null
050     * @return the converted document, or the same input <code>document</code> if the
051     */
052    public Document convertValues( Document document,
053                                   Results results );
054
055    /**
056     * Look for fields within the document (including nested documents) whose values are not of the expected type for the given
057     * schema, but whose values can be converted into the expected type.
058     * <p>
059     * This method is similar to {@link #convertValues(Document, Results)}, except that this method automatically runs a JSON
060     * Schema validation to obtain the results. If you've already {@link #validate(Document, String) validated} the document, then
061     * instead of calling this method (which would validate a second time) try calling {@link #convertValues(Document, Results)}.
062     * </p>
063     * 
064     * @param document the document to be validated; may not be null
065     * @param schemaUri the URI of the JSON Schema that should be used to validate the document; may not be null
066     * @return the converted document, or the same input <code>document</code> if the
067     */
068    public Document convertValues( Document document,
069                                   String schemaUri );
070
071    /**
072     * The results from a {@link SchemaLibrary#validate(Document, String) validation}.
073     * 
074     * @author Randall Hauch <rhauch@redhat.com> (C) 2011 Red Hat Inc.
075     * @since 5.1
076     */
077    public interface Results extends Iterable<Problem> {
078        /**
079         * Determine if these results contain at least one error or warning. Calling this method is equivalent to (but more
080         * efficient than) calling:
081         * 
082         * <pre>
083         * hasErrors() || hasWarnings()
084         * </pre>
085         * 
086         * or
087         * 
088         * <pre>
089         * problemCount() != 0
090         * </pre>
091         * 
092         * or
093         * 
094         * <pre>
095         * errorCount() != 0 || warningCount() != 0
096         * </pre>
097         * 
098         * @return <code>true</code> if there is at least one error or warning, or <code>false</code> if there are no errors or
099         *         warnings
100         */
101        boolean hasProblems();
102
103        /**
104         * Determine if these results contain at least one error.
105         * 
106         * @return <code>true</code> if there is at least one error, or <code>false</code> if there are no errors
107         */
108        boolean hasErrors();
109
110        /**
111         * Determine if these results contain at least one warning.
112         * 
113         * @return <code>true</code> if there is at least one warning, or <code>false</code> if there are no errors
114         */
115        boolean hasWarnings();
116
117        /**
118         * Determine if these results contain only errors that are {@link MismatchedTypeProblem mismatched fields}, where the
119         * value of a field has a type that does not match but can be converted to the type defined in the schema.
120         * <p>
121         * All type mismatch errors are considered {@link #hasErrors() errors}, but not all errors are type mismatch errors.
122         * </p>
123         * <p>
124         * If this method return true, then consider calling {@link SchemaLibrary#convertValues(Document, Results)} to convert the
125         * mismatched values and then revalidating.
126         * </p>
127         * 
128         * @return <code>true</code> if there is at least one mismatched type error, or <code>false</code> if there are no errors
129         * @see SchemaLibrary#convertValues(Document, Results)
130         */
131        boolean hasOnlyTypeMismatchErrors();
132
133        /**
134         * Determine the number of errors within these results.
135         * 
136         * @return the number of errors; always 0 or a positive number
137         */
138        int errorCount();
139
140        /**
141         * Determine the number of warnings within these results.
142         * 
143         * @return the number of warnings; always 0 or a positive number
144         */
145        int warningCount();
146
147        /**
148         * Determine the number of problems (that is, errors and warnings) within these results.
149         * 
150         * @return the number of errors and warnings; always 0 or a positive number
151         */
152        int problemCount();
153
154    }
155
156    public enum ProblemType {
157        /** The problem type signaling a validation error. */
158        ERROR,
159
160        /** The problem type signaling a validation warning. */
161        WARNING;
162    }
163
164    public interface Problem {
165        /**
166         * Get the type of problem.
167         * 
168         * @return the type; never null
169         */
170        ProblemType getType();
171
172        /**
173         * The path to the field about which this problem applies.
174         * 
175         * @return the path; never null
176         */
177        Path getPath();
178
179        /**
180         * Get the message describing the problem.
181         * 
182         * @return the message; never null
183         */
184        String getReason();
185
186        /**
187         * Get the exception that was the cause of this problem, if there was an exception.
188         * 
189         * @return the exception; may be null
190         */
191        Throwable getCause();
192    }
193
194    /**
195     * A special type of problem where a field value was not of the expected type, but where the field value could be converted to
196     * the expected type
197     */
198    public interface MismatchedTypeProblem extends Problem {
199        /**
200         * Get the actual field value.
201         * 
202         * @return the actual field value
203         */
204        Object getActualValue();
205
206        /**
207         * Get the converted field value that would satisfy the type expected by the schema.
208         * 
209         * @return the converted field value
210         */
211        Object getConvertedValue();
212
213        Type getActualType();
214
215        Type getExpectedType();
216    }
217
218}