Skip to content

Commit

Permalink
Refactored validation result to support contextual info
Browse files Browse the repository at this point in the history
  • Loading branch information
benfortuna committed Feb 28, 2022
1 parent 27339e8 commit 3a6a40b
Show file tree
Hide file tree
Showing 14 changed files with 148 additions and 93 deletions.
11 changes: 6 additions & 5 deletions src/main/java/net/fortuna/ical4j/model/component/VEvent.java
Expand Up @@ -321,7 +321,7 @@ public ComponentList<Component> getComponents() {
* {@inheritDoc}
*/
@Override
public final void validate(final boolean recurse) throws ValidationException {
public final ValidationResult validate(final boolean recurse) throws ValidationException {
ValidationResult result = new ValidationResult();
// validate that getAlarms() only contains VAlarm components
// final Iterator iterator = getAlarms().iterator();
Expand All @@ -342,8 +342,8 @@ public final void validate(final boolean recurse) throws ValidationException {
if (status != null && !Status.VEVENT_TENTATIVE.getValue().equals(status.getValue())
&& !Status.VEVENT_CONFIRMED.getValue().equals(status.getValue())
&& !Status.VEVENT_CANCELLED.getValue().equals(status.getValue())) {
result.getErrors().add("Status property ["
+ status + "] is not applicable for VEVENT");
result.getEntries().add(new ValidationEntry("Status property ["
+ status + "] is not applicable for VEVENT", ValidationEntry.Level.ERROR, getName()));
}

if (getProperty(Property.DTEND) != null) {
Expand Down Expand Up @@ -378,9 +378,9 @@ else if (startValue != null && !Value.DATE_TIME.equals(startValue)) {
startEndValueMismatch = true;
}
if (startEndValueMismatch) {
result.getErrors().add("Property [" + Property.DTEND
result.getEntries().add(new ValidationEntry("Property [" + Property.DTEND
+ "] must have the same [" + Parameter.VALUE
+ "] as [" + Property.DTSTART + "]");
+ "] as [" + Property.DTSTART + "]", ValidationEntry.Level.ERROR, getName()));
}
}
}
Expand All @@ -391,6 +391,7 @@ else if (startValue != null && !Value.DATE_TIME.equals(startValue)) {
if (result.hasErrors()) {
throw new ValidationException(result);
}
return result;
}

/**
Expand Down
Expand Up @@ -34,6 +34,7 @@
import net.fortuna.ical4j.model.PropertyList;
import net.fortuna.ical4j.model.property.Method;
import net.fortuna.ical4j.util.CompatibilityHints;
import net.fortuna.ical4j.validate.ValidationEntry;
import net.fortuna.ical4j.validate.ValidationException;
import net.fortuna.ical4j.validate.ValidationResult;
import net.fortuna.ical4j.validate.Validator;
Expand Down Expand Up @@ -71,13 +72,13 @@ public XComponent(final String name, final PropertyList properties) {
* {@inheritDoc}
*/
@Override
public final void validate(final boolean recurse) throws ValidationException {
public final ValidationResult validate(final boolean recurse) throws ValidationException {
ValidationResult result = new ValidationResult();
if (!CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION)
&& !getName().startsWith(EXPERIMENTAL_PREFIX)) {

result.getErrors().add("Experimental components must have the following prefix: "
+ EXPERIMENTAL_PREFIX);
result.getEntries().add(new ValidationEntry("Experimental components must have the following prefix: "
+ EXPERIMENTAL_PREFIX, ValidationEntry.Level.ERROR, getName()));
}

if (recurse) {
Expand All @@ -86,6 +87,7 @@ public final void validate(final boolean recurse) throws ValidationException {
if (result.hasErrors()) {
throw new ValidationException(result);
}
return result;
}

/**
Expand Down
Expand Up @@ -35,34 +35,30 @@ public void validate(Calendar target) throws ValidationException {
ValidationResult result = new ValidationResult();

for (ValidationRule rule : rules) {
boolean warnOnly = CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION)
&& rule.isRelaxedModeSupported();

if (warnOnly) {
result.getWarnings().addAll(apply(rule, (PropertyContainer) target));
} else {
result.getErrors().addAll(apply(rule, (PropertyContainer) target));
}
result.getEntries().addAll(apply(rule, Calendar.VCALENDAR, (PropertyContainer) target));
}

if (!CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION)) {
// require VERSION:2.0 for RFC2445..
if (!Version.VERSION_2_0.equals(target.getProperty(Property.VERSION))) {
result.getErrors().add("Unsupported Version: " + target.getProperty(Property.VERSION).getValue());
result.getEntries().add(new ValidationEntry("Unsupported Version: " + target.getProperty(Property.VERSION).getValue(),
ValidationEntry.Level.ERROR, Calendar.VCALENDAR));
}
}

// must contain at least one component
if (target.getComponents().isEmpty()) {
result.getErrors().add("Calendar must contain at least one component");
result.getEntries().add(new ValidationEntry("Calendar must contain at least one component",
ValidationEntry.Level.ERROR, Calendar.VCALENDAR));
}

// validate properties..
for (final Property property : target.getProperties()) {
boolean isCalendarProperty = calendarProperties.stream().filter(calProp -> calProp.isInstance(property)) != null;

if (!(property instanceof XProperty) && !isCalendarProperty) {
result.getErrors().add("Invalid property: " + property.getName());
result.getEntries().add(new ValidationEntry("Invalid property: " + property.getName(),
ValidationEntry.Level.ERROR, Calendar.VCALENDAR));
}
}

Expand Down
Expand Up @@ -34,7 +34,6 @@
import net.fortuna.ical4j.model.Component;
import net.fortuna.ical4j.model.ComponentList;
import net.fortuna.ical4j.model.component.*;
import net.fortuna.ical4j.util.CompatibilityHints;

import java.util.Arrays;
import java.util.List;
Expand Down Expand Up @@ -147,14 +146,7 @@ public void validate(T target) throws ValidationException {
ValidationResult result = new ValidationResult();

for (ValidationRule rule : rules) {
boolean warnOnly = CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION)
&& rule.isRelaxedModeSupported();

if (warnOnly) {
result.getWarnings().addAll(apply(rule, target));
} else {
result.getErrors().addAll(apply(rule, target));
}
result.getEntries().addAll(apply(rule, target.getName(), target));
}
if (result.hasErrors()) {
throw new ValidationException(result);
Expand Down
Expand Up @@ -33,7 +33,6 @@

import net.fortuna.ical4j.model.Property;
import net.fortuna.ical4j.model.property.*;
import net.fortuna.ical4j.util.CompatibilityHints;

import java.util.Arrays;
import java.util.List;
Expand Down Expand Up @@ -141,14 +140,7 @@ public PropertyValidator(List<ValidationRule> rules) {
public void validate(Property target) throws ValidationException {
ValidationResult result = new ValidationResult();
for (ValidationRule rule : rules) {
boolean warnOnly = CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION)
&& rule.isRelaxedModeSupported();

if (warnOnly) {
result.getWarnings().addAll(apply(rule, target));
} else {
result.getErrors().addAll(apply(rule, target));
}
result.getEntries().addAll(apply(rule, target.getName(), target));
}
if (result.hasErrors()) {
throw new ValidationException(result);
Expand Down
80 changes: 80 additions & 0 deletions src/main/java/net/fortuna/ical4j/validate/ValidationEntry.java
@@ -0,0 +1,80 @@
/*
* Copyright (c) 2022, Ben Fortuna
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* o Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* o Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* o Neither the name of Ben Fortuna nor the names of any other contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/

package net.fortuna.ical4j.validate;

public class ValidationEntry {

public enum Level {
ERROR, WARNING, INFO
}

private final String message;

private final Level level;

private final String context;

public ValidationEntry(String message, Level level, String context) {
this.message = message;
this.level = level;
this.context = context;
}

public ValidationEntry(ValidationRule rule, String context, String...instances) {
this.message = rule.getMessage(instances);
this.level = rule.getLevel();
this.context = context;
}

public String getMessage() {
return message;
}

public Level getLevel() {
return level;
}

public String getContext() {
return context;
}

@Override
public String toString() {
return "ValidationEntry{" +
"message='" + message + '\'' +
", level=" + level +
", context='" + context + '\'' +
'}';
}
}
Expand Up @@ -67,7 +67,7 @@ public ValidationException(ValidationResult result) {
* @param message a message
*/
public ValidationException(final String message) {
this(new ValidationResult(message));
this(new ValidationResult(new ValidationEntry(message, ValidationEntry.Level.ERROR, null)));
}

/**
Expand All @@ -76,7 +76,8 @@ public ValidationException(final String message) {
* @param args message arguments
*/
public ValidationException(final String message, Object[] args) {
this(new ValidationResult(MessageFormat.format(message, args)));
this(new ValidationResult(new ValidationEntry(MessageFormat.format(message, args),
ValidationEntry.Level.ERROR, null)));
}

public ValidationResult getResult() {
Expand Down
24 changes: 7 additions & 17 deletions src/main/java/net/fortuna/ical4j/validate/ValidationResult.java
Expand Up @@ -42,34 +42,24 @@
*/
public final class ValidationResult {

private final List<String> errors = new ArrayList<>();
private final List<ValidationEntry> entries = new ArrayList<>();

private final List<String> warnings = new ArrayList<>();

public ValidationResult() {
}

public ValidationResult(String...errors) {
this.errors.addAll(Arrays.asList(errors));
}

public List<String> getErrors() {
return errors;
public ValidationResult(ValidationEntry...entries) {
this.entries.addAll(Arrays.asList(entries));
}

public List<String> getWarnings() {
return warnings;
public List<ValidationEntry> getEntries() {
return entries;
}

public boolean hasErrors() {
return !errors.isEmpty();
return entries.stream().anyMatch(e -> e.getLevel() == ValidationEntry.Level.ERROR);
}

@Override
public String toString() {
return "ValidationResult{" +
"errors=" + errors +
", warnings=" + warnings +
"entries=" + entries +
'}';
}
}
Expand Up @@ -39,10 +39,7 @@
import net.fortuna.ical4j.model.parameter.Value;
import net.fortuna.ical4j.model.property.DtEnd;
import net.fortuna.ical4j.model.property.DtStart;
import net.fortuna.ical4j.validate.ComponentValidator;
import net.fortuna.ical4j.validate.ValidationException;
import net.fortuna.ical4j.validate.ValidationResult;
import net.fortuna.ical4j.validate.Validator;
import net.fortuna.ical4j.validate.*;

public class AvailableValidator implements Validator<Available> {

Expand All @@ -59,7 +56,8 @@ public void validate(Available target) throws ValidationException {
*/
final DtStart start = target.getProperty(Property.DTSTART);
if (Value.DATE.equals(start.getParameter(Parameter.VALUE))) {
result.getErrors().add("Property [" + Property.DTSTART + "] must be a " + Value.DATE_TIME);
result.getEntries().add(new ValidationEntry("Property [" + Property.DTSTART + "] must be a " + Value.DATE_TIME,
ValidationEntry.Level.ERROR, target.getName()));
}

/*
Expand All @@ -72,7 +70,8 @@ public void validate(Available target) throws ValidationException {
final DtEnd end = target.getProperty(Property.DTEND);
/* Must be DATE_TIME */
if (end != null && Value.DATE.equals(end.getParameter(Parameter.VALUE))) {
result.getErrors().add("Property [" + Property.DTEND + "] must be a " + Value.DATE_TIME);
result.getEntries().add(new ValidationEntry("Property [" + Property.DTEND + "] must be a " + Value.DATE_TIME,
ValidationEntry.Level.ERROR, target.getName()));
}

/*
Expand Down
Expand Up @@ -39,10 +39,7 @@
import net.fortuna.ical4j.model.parameter.Value;
import net.fortuna.ical4j.model.property.DtEnd;
import net.fortuna.ical4j.model.property.DtStart;
import net.fortuna.ical4j.validate.ComponentValidator;
import net.fortuna.ical4j.validate.ValidationException;
import net.fortuna.ical4j.validate.ValidationResult;
import net.fortuna.ical4j.validate.Validator;
import net.fortuna.ical4j.validate.*;

public class VAvailabilityValidator implements Validator<VAvailability> {

Expand Down Expand Up @@ -70,13 +67,15 @@ public void validate(VAvailability target) throws ValidationException {
*/
final DtStart start = target.getProperty(Property.DTSTART);
if (Value.DATE.equals(start.getParameter(Parameter.VALUE))) {
result.getErrors().add("Property [" + Property.DTSTART + "] must be a " + Value.DATE_TIME);
result.getEntries().add(new ValidationEntry("Property [" + Property.DTSTART + "] must be a " + Value.DATE_TIME,
ValidationEntry.Level.ERROR, target.getName()));
}

/* Must be DATE_TIME */
final DtEnd end = target.getProperty(Property.DTEND);
if (end != null && Value.DATE.equals(end.getParameter(Parameter.VALUE))) {
result.getErrors().add("Property [" + Property.DTEND + "] must be a " + Value.DATE_TIME);
result.getEntries().add(new ValidationEntry("Property [" + Property.DTEND + "] must be a " + Value.DATE_TIME,
ValidationEntry.Level.ERROR, target.getName()));
}

if (result.hasErrors()) {
Expand Down

0 comments on commit 3a6a40b

Please sign in to comment.