Skip to content

Commit

Permalink
Refactor validation
Browse files Browse the repository at this point in the history
  • Loading branch information
benfortuna committed Jun 21, 2020
1 parent 4252511 commit 1d922bc
Show file tree
Hide file tree
Showing 21 changed files with 690 additions and 699 deletions.
52 changes: 28 additions & 24 deletions src/main/java/net/fortuna/ical4j/model/component/Available.java
Expand Up @@ -34,13 +34,16 @@
import net.fortuna.ical4j.model.*;
import net.fortuna.ical4j.model.parameter.Value;
import net.fortuna.ical4j.model.property.DtEnd;
import net.fortuna.ical4j.validate.PropertyValidator;
import net.fortuna.ical4j.validate.ComponentValidator;
import net.fortuna.ical4j.validate.ValidationException;
import net.fortuna.ical4j.validate.ValidationRule;
import net.fortuna.ical4j.validate.Validator;

import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Optional;

import static net.fortuna.ical4j.model.Property.*;
import static net.fortuna.ical4j.validate.ValidationRule.ValidationType.*;

/**
* $Id$ [05-Apr-2004]
* <p/>
Expand Down Expand Up @@ -91,6 +94,16 @@ public class Available extends Component {

private static final long serialVersionUID = -2494710612002978763L;

private final Validator<Available> validator = new ComponentValidator<>(
new ValidationRule<>(One, DTSTART, DTSTAMP, UID),
new ValidationRule<>(OneOrLess, CREATED, LAST_MODIFIED, RECURRENCE_ID, RRULE, SUMMARY),
// can't have both DTEND and DURATION..
new ValidationRule<>(One, p->!p.getProperties().getFirst(DURATION).isPresent(), DTEND),
new ValidationRule<>(None, p->p.getProperties().getFirst(DTEND).isPresent(), DURATION),
new ValidationRule<>(One, p->!p.getProperties().getFirst(DTEND).isPresent(), DURATION),
new ValidationRule<>(None, p->p.getProperties().getFirst(DURATION).isPresent(), DTEND)
);

/**
* Default constructor.
*/
Expand All @@ -110,23 +123,22 @@ public Available(final PropertyList properties) {
/**
* {@inheritDoc}
*/
public final void validate(final boolean recurse)
throws ValidationException {
public final void validate(final boolean recurse) throws ValidationException {

validator.validate(this);
/*
* ; dtstamp / dtstart / uid are required, but MUST NOT occur more than once /
*/
Arrays.asList(Property.DTSTART, Property.DTSTAMP, Property.UID).forEach(property -> PropertyValidator.assertOne(property, getProperties()));

/* If specified, the "DTSTART" and "DTEND" properties in
* "VAVAILABILITY" components and "AVAILABLE" sub-components MUST be
* "DATE-TIME" values specified as either date with UTC time or date
* with local time and a time zone reference.
*/
try {
if (getRequiredProperty(Property.DTSTART).getParameter(Parameter.VALUE).equals(Optional.of(Value.DATE))) {
throw new ValidationException("Property [" + Property.DTSTART
+ "] must be a " + Value.DATE_TIME);
if (getProperties().getRequired(DTSTART).getParameters().getFirst(Parameter.VALUE)
.equals(Optional.of(Value.DATE))) {
throw new ValidationException("Property [" + DTSTART + "] must be a " + Value.DATE_TIME);
}
} catch (ConstraintViolationException cve) {
throw new ValidationException("Missing required property", cve);
Expand All @@ -139,8 +151,6 @@ public final void validate(final boolean recurse)
* created / last-mod / recurid / rrule /
* summary /
*/
Arrays.asList(Property.CREATED, Property.LAST_MODIFIED, Property.RECURRENCE_ID,
Property.RRULE, Property.SUMMARY).forEach(property -> PropertyValidator.assertOneOrLess(property, getProperties()));

/*
; either a 'dtend' or a 'duration' is required
Expand All @@ -149,18 +159,12 @@ public final void validate(final boolean recurse)
; 'availableprop', and each MUST NOT occur more
; than once
*/
final Optional<DtEnd<?>> end = getProperty(Property.DTEND);
final Optional<DtEnd<?>> end = getProperties().getFirst(DTEND);
if (end.isPresent()) {
PropertyValidator.assertOne(Property.DTEND,
getProperties());
/* Must be DATE_TIME */
if (Value.DATE.equals(end.get().getParameter(Parameter.VALUE))) {
throw new ValidationException("Property [" + Property.DTEND
+ "] must be a " + Value.DATE_TIME);
if (Optional.of(Value.DATE).equals(end.get().getParameters().getFirst(Parameter.VALUE))) {
throw new ValidationException("Property [" + DTEND + "] must be a " + Value.DATE_TIME);
}
} else {
PropertyValidator.assertOne(Property.DURATION,
getProperties());
}

/*
Expand All @@ -175,8 +179,8 @@ public final void validate(final boolean recurse)
}

@Override
public Available copy() throws URISyntaxException {
return new Factory().createComponent(getProperties());
protected ComponentFactory<Available> newFactory() {
return new Factory();
}

public static class Factory extends Content.Factory implements ComponentFactory<Available> {
Expand All @@ -191,12 +195,12 @@ public Available createComponent() {
}

@Override
public Available createComponent(PropertyList properties) throws URISyntaxException {
public Available createComponent(PropertyList properties) {
return new Available(properties);
}

@Override
public Available createComponent(PropertyList properties, ComponentList subComponents) {
public Available createComponent(PropertyList properties, ComponentList<?> subComponents) {
throw new UnsupportedOperationException(String.format("%s does not support sub-components", AVAILABLE));
}
}
Expand Down
34 changes: 21 additions & 13 deletions src/main/java/net/fortuna/ical4j/model/component/Observance.java
Expand Up @@ -34,8 +34,10 @@
import net.fortuna.ical4j.model.*;
import net.fortuna.ical4j.model.property.*;
import net.fortuna.ical4j.util.TimeZones;
import net.fortuna.ical4j.validate.PropertyValidator;
import net.fortuna.ical4j.validate.ComponentValidator;
import net.fortuna.ical4j.validate.ValidationException;
import net.fortuna.ical4j.validate.ValidationRule;
import net.fortuna.ical4j.validate.Validator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -49,6 +51,9 @@
import java.time.temporal.Temporal;
import java.util.*;

import static net.fortuna.ical4j.model.Property.*;
import static net.fortuna.ical4j.validate.ValidationRule.ValidationType.One;

/**
* $Id$ [05-Apr-2004]
* <p/>
Expand All @@ -74,6 +79,10 @@ public abstract class Observance extends Component {
*/
public static final String DAYLIGHT = "DAYLIGHT";

private Validator<Observance> validator = new ComponentValidator<>(
new ValidationRule<>(One, TZOFFSETFROM, TZOFFSETTO, DTSTART)
);

// TODO: clear cache when observance definition changes (??)
private long[] onsetsMillisec;
private OffsetDateTime[] onsetsDates;
Expand Down Expand Up @@ -118,20 +127,19 @@ protected Observance(final String name, final PropertyList properties) {
*/
public final void validate(final boolean recurse) throws ValidationException {

validator.validate(this);

// From "4.8.3.3 Time Zone Offset From":
// Conformance: This property MUST be specified in a "VTIMEZONE"
// calendar component.
PropertyValidator.assertOne(Property.TZOFFSETFROM, getProperties());

// From "4.8.3.4 Time Zone Offset To":
// Conformance: This property MUST be specified in a "VTIMEZONE"
// calendar component.
PropertyValidator.assertOne(Property.TZOFFSETTO, getProperties());

/*
* ; the following are each REQUIRED, ; but MUST NOT occur more than once dtstart / tzoffsetto / tzoffsetfrom /
*/
PropertyValidator.assertOne(Property.DTSTART, getProperties());

/*
* ; the following are optional, ; and MAY occur more than once comment / rdate / rrule / tzname / x-prop
Expand All @@ -152,7 +160,7 @@ public final void validate(final boolean recurse) throws ValidationException {
public final OffsetDateTime getLatestOnset(final Temporal date) {
TzOffsetTo offsetTo;
try {
offsetTo = getRequiredProperty(Property.TZOFFSETTO);
offsetTo = getProperties().getRequired(TZOFFSETTO);
} catch (ConstraintViolationException e) {
Logger log = LoggerFactory.getLogger(Observance.class);
log.error("Unexpected error calculating latest onset", e);
Expand All @@ -164,7 +172,7 @@ public final OffsetDateTime getLatestOnset(final Temporal date) {

if (initialOnset == null) {
try {
DtStart dtStart = getRequiredProperty(Property.DTSTART);
DtStart dtStart = getProperties().getRequired(DTSTART);
initialOnset = LocalDateTime.from(dtStart.getDate()).atOffset(offsetTo.getOffset());
} catch (ConstraintViolationException e) {
Logger log = LoggerFactory.getLogger(Observance.class);
Expand All @@ -190,15 +198,15 @@ public final OffsetDateTime getLatestOnset(final Temporal date) {
// get first onset without adding TZFROM as this may lead to a day boundary
// change which would be incompatible with BYDAY RRULES
// we will have to add the offset to all cacheable onsets
Optional<DtStart<LocalDateTime>> startDate = getProperty(Property.DTSTART);
Optional<DtStart<LocalDateTime>> startDate = getProperty(DTSTART);

// collect all onsets for the purposes of caching..
final List<OffsetDateTime> cacheableOnsets = new ArrayList<>();
cacheableOnsets.add(initialOnset);

// check rdates for latest applicable onset..
Optional<TzOffsetFrom> offsetFrom = getProperty(Property.TZOFFSETFROM);
final List<Property> rdates = getProperties(Property.RDATE);
Optional<TzOffsetFrom> offsetFrom = getProperty(TZOFFSETFROM);
final List<Property> rdates = getProperties(RDATE);
for (Property rdate : rdates) {
List<LocalDateTime> rdateDates = ((RDate<LocalDateTime>) rdate).getDates();
for (final LocalDateTime rdateDate : rdateDates) {
Expand All @@ -215,7 +223,7 @@ public final OffsetDateTime getLatestOnset(final Temporal date) {
}

// check recurrence rules for latest applicable onset..
final List<Property> rrules = getProperties(Property.RRULE);
final List<Property> rrules = getProperties(RRULE);
for (Property rrule : rrules) {
// include future onsets to determine onset period..
onsetLimit = Instant.from(offsetDate.plus(10, ChronoUnit.YEARS));
Expand Down Expand Up @@ -273,7 +281,7 @@ private OffsetDateTime getCachedOnset(final Temporal date) {
*/
@Deprecated
public final Optional<DtStart<LocalDateTime>> getStartDate() {
return getProperty(Property.DTSTART);
return getProperty(DTSTART);
}

/**
Expand All @@ -284,7 +292,7 @@ public final Optional<DtStart<LocalDateTime>> getStartDate() {
*/
@Deprecated
public final Optional<TzOffsetFrom> getOffsetFrom() {
return getProperty(Property.TZOFFSETFROM);
return getProperty(TZOFFSETFROM);
}

/**
Expand All @@ -295,6 +303,6 @@ public final Optional<TzOffsetFrom> getOffsetFrom() {
*/
@Deprecated
public final Optional<TzOffsetTo> getOffsetTo() {
return getProperty(Property.TZOFFSETTO);
return getProperty(TZOFFSETTO);
}
}

0 comments on commit 1d922bc

Please sign in to comment.