001/* 002 * GeoAPI - Java interfaces for OGC/ISO standards 003 * Copyright © 2008-2024 Open Geospatial Consortium, Inc. 004 * http://www.geoapi.org 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.opengis.test.metadata; 019 020import java.util.Date; 021import org.opengis.metadata.citation.*; 022import org.opengis.test.ValidatorContainer; 023 024import static org.junit.jupiter.api.Assertions.fail; 025 026 027/** 028 * Validates {@link Citation} and related objects from the 029 * {@code org.opengis.metadata.citation} package. 030 * 031 * <p>This class is provided for users wanting to override the validation methods. When the default 032 * behavior is sufficient, the {@link org.opengis.test.Validators} static methods provide a more 033 * convenient way to validate various kinds of objects.</p> 034 * 035 * @author Martin Desruisseaux (Geomatys) 036 * @version 3.1 037 * @since 2.2 038 */ 039public class CitationValidator extends MetadataValidator { 040 /** 041 * Creates a new validator instance. 042 * 043 * @param container the set of validators to use for validating other kinds of objects 044 * (see {@linkplain #container field javadoc}). 045 */ 046 public CitationValidator(final ValidatorContainer container) { 047 super(container, "org.opengis.metadata.citation"); 048 } 049 050 /** 051 * Validates the given citation. 052 * 053 * @param object the object to validate, or {@code null}. 054 */ 055 public void validate(final Citation object) { 056 if (object == null) { 057 return; 058 } 059 validateMandatory(object.getTitle()); 060 validateOptional (object.getEdition()); 061 validate("alternateTitle", object.getAlternateTitles(), ValidatorContainer::validate, false); 062 validate("identifier", object.getIdentifiers(), ValidatorContainer::validate, false); 063 container.validate(object.getOtherCitationDetails()); 064 validate(toArray(CitationDate.class, object.getDates())); 065 for (final Responsibility e : toArray(Responsibility.class, object.getCitedResponsibleParties())) { 066 validate(e); 067 } 068 } 069 070 /** 071 * Validates citation dates. If more than one dates is given, then this method will check 072 * for the following constraints: 073 * 074 * <ul> 075 * <li>{@link DateType#CREATION} shall be before or equals to all other type of dates, ignoring user-defined codes.</li> 076 * <li>{@link DateType#LAST_UPDATE} shall be before or equals to {@link DateType#NEXT_UPDATE}.</li> 077 * <li>{@link DateType#VALIDITY_BEGINS} shall be before or equals to {@link DateType#VALIDITY_EXPIRES}.</li> 078 * </ul> 079 * 080 * Those constraints are verified in their iteration order. It is possible for example to have more than one 081 * (<var>validity begins</var>, <var>validity expires</var>) pair. 082 * 083 * @param dates the citation dates to validate. 084 * 085 * @since 3.1 086 */ 087 public void validate(final CitationDate... dates) { 088 if (dates == null) { 089 return; 090 } 091 Date creation = null; 092 Date lastUpdate = null; 093 Date validityBegins = null; 094 final int lastOrdinal = DateType.DISTRIBUTION.ordinal(); 095 for (final CitationDate date : dates) { 096 if (date != null) { 097 final DateType type = date.getDateType(); 098 final Date time = date.getDate(); 099 mandatory(type, "CitationDate: shall have a date type."); 100 mandatory(time, "CitationDate: shall have a timestamp."); 101 if (type != null && time != null) { 102 if (type.equals(DateType.CREATION)) { 103 creation = time; 104 } else if (type.equals(DateType.LAST_UPDATE)) { 105 lastUpdate = time; 106 } else if (type.equals(DateType.NEXT_UPDATE)) { 107 assertOrdered(DateType.LAST_UPDATE, lastUpdate, DateType.NEXT_UPDATE, time); 108 } else if (type.equals(DateType.VALIDITY_BEGINS)) { 109 validityBegins = time; 110 } else if (type.equals(DateType.VALIDITY_EXPIRES)) { 111 assertOrdered(DateType.VALIDITY_BEGINS, validityBegins, DateType.VALIDITY_EXPIRES, time); 112 } 113 if (type.ordinal() <= lastOrdinal) { 114 assertOrdered(DateType.CREATION, creation, type, time); 115 } 116 } 117 } 118 } 119 } 120 121 /** 122 * Asserts that the date {@code d2} is equal or after {@code d1}. 123 * 124 * @param t1 type of the first date. 125 * @param d1 the first date to compare. 126 * @param t2 type of the second date. 127 * @param d2 the second date to compare. 128 */ 129 private static void assertOrdered(final DateType t1, final Date d1, final DateType t2, final Date d2) { 130 if (d1 != null && d2.before(d1)) { 131 fail("The ‘" + t2.identifier().orElse("<?>") + "’ date (" + d2 + ") shall be equal or after " 132 + "the ‘" + t1.identifier().orElse("<?>") + "’ date (" + d1 + ")."); 133 } 134 } 135 136 /** 137 * Validates the given responsible party. 138 * 139 * @param object the object to validate, or {@code null}. 140 * 141 * @since 3.1 142 */ 143 public void validate(final Responsibility object) { 144 if (object == null) { 145 return; 146 } 147 mandatory(object.getRole(), "Responsibility: shall have a role."); 148 for (final Party e : toArray(Party.class, object.getParties())) { 149 validate(e); 150 } 151 } 152 153 /** 154 * Validates the given party. 155 * 156 * @param object the object to validate, or {@code null}. 157 * 158 * @since 3.1 159 */ 160 public void validate(final Party object) { 161 if (object == null) { 162 return; 163 } 164 boolean isMandatory = true; 165 if (object instanceof Individual) { 166 isMandatory &= isNullOrEmpty(((Individual) object).getPositionName()); 167 } 168 if (object instanceof Organisation) { 169 isMandatory &= isNullOrEmpty(((Organisation) object).getLogo()); 170 } 171 if (isMandatory) { 172 mandatory(object.getName(), "Party: shall have a name."); 173 } 174 for (final Contact e : toArray(Contact.class, object.getContactInfo())) { 175 validate(e); 176 } 177 } 178 179 /** 180 * Validates the given contact information. 181 * 182 * @param object the object to validate, or {@code null}. 183 * 184 * @since 3.1 185 */ 186 public void validate(final Contact object) { 187 if (object == null) { 188 return; 189 } 190 for (final Telephone e : toArray(Telephone.class, object.getPhones())) { 191 validate(e); 192 } 193 for (final Address e : toArray(Address.class, object.getAddresses())) { 194 validate(e); 195 } 196 for (final OnlineResource e : toArray(OnlineResource.class, object.getOnlineResources())) { 197 validate(e); 198 } 199 validateOptional(object.getHoursOfService()); 200 validateOptional(object.getContactInstructions()); 201 } 202 203 /** 204 * Validates the given telephone information. 205 * 206 * @param object the object to validate, or {@code null}. 207 * 208 * @since 3.1 209 */ 210 public void validate(final Telephone object) { 211 if (object == null) { 212 return; 213 } 214 mandatory(object.getNumber(), "Telephone: shall have a number."); 215 } 216 217 /** 218 * Validates the given address. 219 * 220 * @param object the object to validate, or {@code null}. 221 * 222 * @since 3.1 223 */ 224 public void validate(final Address object) { 225 if (object == null) { 226 return; 227 } 228 validate(object.getDeliveryPoints()); 229 validateOptional(object.getCity()); 230 validateOptional(object.getAdministrativeArea()); 231 validateOptional(object.getCountry()); 232 validate(object.getElectronicMailAddresses()); 233 } 234 235 /** 236 * Validates the given online resource. 237 * 238 * @param object the object to validate, or {@code null}. 239 * 240 * @since 3.1 241 */ 242 public void validate(final OnlineResource object) { 243 if (object == null) { 244 return; 245 } 246 mandatory(object.getLinkage(), "OnlineResource: shall have a linkage."); 247 validateOptional(object.getDescription()); 248 } 249}