Skip to content

Commit d6c7326

Browse files
committed
InstanceValidator: use the new ValidationStack, fix issue #112 as a result
The code is becoming quite messy, though. Signed-off-by: Francis Galiegue <[email protected]>
1 parent a606a50 commit d6c7326

File tree

1 file changed

+16
-97
lines changed

1 file changed

+16
-97
lines changed

src/main/java/com/github/fge/jsonschema/processors/validation/InstanceValidator.java

Lines changed: 16 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -37,20 +37,12 @@
3737
import com.github.fge.jsonschema.processors.data.SchemaContext;
3838
import com.github.fge.jsonschema.processors.data.ValidatorList;
3939
import com.github.fge.msgsimple.bundle.MessageBundle;
40-
import com.github.fge.uritemplate.URITemplate;
41-
import com.github.fge.uritemplate.URITemplateException;
42-
import com.github.fge.uritemplate.URITemplateParseException;
43-
import com.github.fge.uritemplate.vars.VariableMap;
44-
import com.google.common.base.Equivalence;
4540
import com.google.common.collect.Lists;
46-
import com.google.common.collect.Sets;
4741

4842
import javax.annotation.ParametersAreNonnullByDefault;
4943
import javax.annotation.concurrent.NotThreadSafe;
50-
import java.net.URI;
5144
import java.util.Collections;
5245
import java.util.List;
53-
import java.util.Set;
5446

5547
/**
5648
* Processor for validating one schema/instance pair
@@ -73,28 +65,7 @@ public final class InstanceValidator
7365
private final MessageBundle validationMessages;
7466
private final Processor<SchemaContext, ValidatorList> keywordBuilder;
7567

76-
/*
77-
* It is possible to trigger a validation loop if there is a repeated
78-
* triplet schema ID/schema pointer/instance pointer while we validate;
79-
* example schema:
80-
*
81-
* { "oneOf": [ {}, { "$ref": "#" } ] }
82-
*
83-
* Whatever the data, validation will end up validating, for a same pointer
84-
* into the instance, the following pointers into the schema:
85-
*
86-
* "" -> "/oneOf/0" -> "/oneOf/1" -> "/oneOf/0" <-- LOOP
87-
*
88-
* This is not a JSON Reference loop here, but truly a validation loop.
89-
*
90-
* We therefore use this set to record the triplets seen by using an
91-
* Equivalence over FullData which detects this. This is helped by the fact
92-
* that SchemaTree now implements equals()/hashCode() in -core; since this
93-
* class is instantiated for each instance validation, we are certain that
94-
* what instance pointer is seen is the one of the instance we validate.
95-
*/
96-
private final Set<Equivalence.Wrapper<FullData>> visited
97-
= Sets.newLinkedHashSet();
68+
private final ValidationStack stack;
9869

9970
/**
10071
* Constructor -- do not use directly!
@@ -110,6 +81,10 @@ public InstanceValidator(final MessageBundle syntaxMessages,
11081
this.syntaxMessages = syntaxMessages;
11182
this.validationMessages = validationMessages;
11283
this.keywordBuilder = keywordBuilder;
84+
85+
final String errmsg
86+
= validationMessages.getMessage("err.common.validationLoop");
87+
stack = new ValidationStack(errmsg);
11388
}
11489

11590
@Override
@@ -120,8 +95,7 @@ public FullData process(final ProcessingReport report,
12095
/*
12196
* We don't want the same validation context to appear twice, see above
12297
*/
123-
if (!visited.add(FULL_DATA_EQUIVALENCE.wrap(input)))
124-
throw new ProcessingException(validationLoopMessage(input));
98+
stack.push(input);
12599

126100

127101
/*
@@ -159,22 +133,25 @@ public FullData process(final ProcessingReport report,
159133
* reason to go any further. Unless the user has asked to continue even
160134
* in this case.
161135
*/
162-
if (!(report.isSuccess() || data.isDeepCheck()))
136+
if (!(report.isSuccess() || data.isDeepCheck())) {
137+
stack.pop();
163138
return input;
139+
}
164140

165141
/*
166142
* Now check whether this is a container node with a size greater than
167143
* 0. If not, no need to go see the children.
168144
*/
169145
final JsonNode node = data.getInstance().getNode();
170-
if (node.size() == 0)
171-
return input;
172146

173-
if (node.isArray())
174-
processArray(report, data);
175-
else
176-
processObject(report, data);
147+
if (node.isContainerNode()) {
148+
if (node.isArray())
149+
processArray(report, data);
150+
else
151+
processObject(report, data);
152+
}
177153

154+
stack.pop();
178155
return input;
179156
}
180157

@@ -243,23 +220,6 @@ private void processObject(final ProcessingReport report,
243220
}
244221
}
245222

246-
private ProcessingMessage validationLoopMessage(final FullData input)
247-
{
248-
final String errmsg
249-
= validationMessages.getMessage("err.common.validationLoop");
250-
final ArrayNode node = JacksonUtils.nodeFactory().arrayNode();
251-
for (final Equivalence.Wrapper<FullData> e: visited)
252-
//noinspection ConstantConditions
253-
node.add(toJson(e.get()));
254-
return input.newMessage()
255-
.put("domain", "validation")
256-
.setMessage(errmsg)
257-
.putArgument("alreadyVisited", toJson(input))
258-
.putArgument("instancePointer",
259-
input.getInstance().getPointer().toString())
260-
.put("validationPath", node);
261-
}
262-
263223
private ProcessingMessage collectSyntaxErrors(final ProcessingReport report)
264224
{
265225
/*
@@ -280,45 +240,4 @@ private ProcessingMessage collectSyntaxErrors(final ProcessingReport report)
280240
sb.append(JacksonUtils.prettyPrint(arrayNode));
281241
return new ProcessingMessage().setMessage(sb.toString());
282242
}
283-
284-
private static String toJson(final FullData data)
285-
{
286-
final SchemaTree tree = data.getSchema();
287-
final URI baseUri = tree.getLoadingRef().getLocator();
288-
final VariableMap vars = VariableMap.newBuilder().addScalarValue("ptr",
289-
tree.getPointer()).freeze();
290-
// TODO: there should be an easier way to do that...
291-
final URITemplate template;
292-
try {
293-
template = new URITemplate(baseUri + "{+ptr}");
294-
} catch (URITemplateParseException e) {
295-
throw new IllegalStateException("wtf??", e);
296-
}
297-
try {
298-
return template.toString(vars);
299-
} catch (URITemplateException e) {
300-
throw new IllegalStateException("wtf??", e);
301-
}
302-
}
303-
304-
@ParametersAreNonnullByDefault
305-
private static final Equivalence<FullData> FULL_DATA_EQUIVALENCE
306-
= new Equivalence<FullData>()
307-
{
308-
@Override
309-
protected boolean doEquivalent(final FullData a, final FullData b)
310-
{
311-
final JsonPointer ptra = a.getInstance().getPointer();
312-
final JsonPointer ptrb = b.getInstance().getPointer();
313-
return a.getSchema().equals(b.getSchema())
314-
&& ptra.equals(ptrb);
315-
}
316-
317-
@Override
318-
protected int doHash(final FullData t)
319-
{
320-
return t.getSchema().hashCode()
321-
^ t.getInstance().getPointer().hashCode();
322-
}
323-
};
324243
}

0 commit comments

Comments
 (0)