/*
   * (non-Javadoc)
   * @see net.fortuna.ical4j.model.Component#validate(boolean)
   */
  public final void validate(final boolean recurse) throws ValidationException {

    if (!CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION)) {

      // From "4.8.4.7 Unique Identifier":
      // Conformance: The property MUST be specified in the "VEVENT", "VTODO",
      // "VJOURNAL" or "VFREEBUSY" calendar components.
      PropertyValidator.getInstance().assertOne(Property.UID, getProperties());

      // From "4.8.7.2 Date/Time Stamp":
      // Conformance: This property MUST be included in the "VEVENT", "VTODO",
      // "VJOURNAL" or "VFREEBUSY" calendar components.
      PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties());
    }

    /*
     * ; the following are optional, ; but MUST NOT occur more than once class / created / description / dtstart /
     * dtstamp / last-mod / organizer / recurid / seq / status / summary / uid / url /
     */
    PropertyValidator.getInstance().assertOneOrLess(Property.CLASS, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.CREATED, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.DESCRIPTION, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.DTSTART, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.DTSTAMP, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.LAST_MODIFIED, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.ORGANIZER, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.RECURRENCE_ID, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.SEQUENCE, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.STATUS, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.SUMMARY, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.UID, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.URL, getProperties());

    Status status = (Status) getProperty(Property.STATUS);
    if (status != null
        && !Status.VJOURNAL_DRAFT.equals(status)
        && !Status.VJOURNAL_FINAL.equals(status)
        && !Status.VJOURNAL_CANCELLED.equals(status)) {
      throw new ValidationException(
          "Status property [" + status.toString() + "] may not occur in VJOURNAL");
    }

    /*
     * ; the following are optional, ; and MAY occur more than once attach / attendee / categories / comment /
     * contact / exdate / exrule / related / rdate / rrule / rstatus / x-prop
     */

    if (recurse) {
      validateProperties();
    }
  }
  /*
   * (non-Javadoc)
   * @see net.fortuna.ical4j.model.Component#validate(boolean)
   */
  public final void validate(final boolean recurse) throws ValidationException {

    // validate that getAlarms() only contains VAlarm components
    Iterator iterator = getAlarms().iterator();
    while (iterator.hasNext()) {
      Component component = (Component) iterator.next();

      if (!(component instanceof VAlarm)) {
        throw new ValidationException(
            "Component [" + component.getName() + "] may not occur in VTODO");
      }

      ((VAlarm) component).validate(recurse);
    }

    if (!CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION)) {

      // From "4.8.4.7 Unique Identifier":
      // Conformance: The property MUST be specified in the "VEVENT", "VTODO",
      // "VJOURNAL" or "VFREEBUSY" calendar components.
      PropertyValidator.getInstance().assertOne(Property.UID, getProperties());

      // From "4.8.7.2 Date/Time Stamp":
      // Conformance: This property MUST be included in the "VEVENT", "VTODO",
      // "VJOURNAL" or "VFREEBUSY" calendar components.
      PropertyValidator.getInstance().assertOne(Property.DTSTAMP, getProperties());
    }

    /*
     * ; the following are optional, ; but MUST NOT occur more than once class / completed / created / description /
     * dtstamp / dtstart / geo / last-mod / location / organizer / percent / priority / recurid / seq / status /
     * summary / uid / url /
     */
    PropertyValidator.getInstance().assertOneOrLess(Property.CLASS, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.COMPLETED, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.CREATED, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.DESCRIPTION, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.DTSTAMP, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.DTSTART, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.GEO, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.LAST_MODIFIED, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.LOCATION, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.ORGANIZER, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.PERCENT_COMPLETE, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.PRIORITY, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.RECURRENCE_ID, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.SEQUENCE, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.STATUS, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.SUMMARY, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.UID, getProperties());
    PropertyValidator.getInstance().assertOneOrLess(Property.URL, getProperties());

    Status status = (Status) getProperty(Property.STATUS);
    if (status != null
        && !Status.VTODO_NEEDS_ACTION.equals(status)
        && !Status.VTODO_COMPLETED.equals(status)
        && !Status.VTODO_IN_PROCESS.equals(status)
        && !Status.VTODO_CANCELLED.equals(status)) {
      throw new ValidationException(
          "Status property [" + status.toString() + "] may not occur in VTODO");
    }

    /*
     * ; either 'due' or 'duration' may appear in ; a 'todoprop', but 'due' and 'duration' ; MUST NOT occur in the
     * same 'todoprop' due / duration /
     */
    try {
      PropertyValidator.getInstance().assertNone(Property.DUE, getProperties());
    } catch (ValidationException ve) {
      PropertyValidator.getInstance().assertNone(Property.DURATION, getProperties());
    }

    /*
     * ; the following are optional, ; and MAY occur more than once attach / attendee / categories / comment /
     * contact / exdate / exrule / rstatus / related / resources / rdate / rrule / x-prop
     */

    if (recurse) {
      validateProperties();
    }
  }