001/* 002 * GeoAPI - Java interfaces for OGC/ISO standards 003 * http://www.geoapi.org 004 * 005 * Copyright (C) 2008-2019 Open Geospatial Consortium, Inc. 006 * All Rights Reserved. http://www.opengeospatial.org/ogc/legal 007 * 008 * Permission to use, copy, and modify this software and its documentation, with 009 * or without modification, for any purpose and without fee or royalty is hereby 010 * granted, provided that you include the following on ALL copies of the software 011 * and documentation or portions thereof, including modifications, that you make: 012 * 013 * 1. The full text of this NOTICE in a location viewable to users of the 014 * redistributed or derivative work. 015 * 2. Notice of any changes or modifications to the OGC files, including the 016 * date changes were made. 017 * 018 * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE 019 * NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 020 * TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT 021 * THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY 022 * PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. 023 * 024 * COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR 025 * CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION. 026 * 027 * The name and trademarks of copyright holders may NOT be used in advertising or 028 * publicity pertaining to the software without specific, written prior permission. 029 * Title to copyright in this software and any associated documentation will at all 030 * times remain with copyright holders. 031 */ 032package org.opengis.test.util; 033 034import java.util.List; 035import org.opengis.util.*; 036import org.opengis.test.Validator; 037import org.opengis.test.ValidatorContainer; 038import static org.opengis.test.Assert.*; 039 040 041/** 042 * Validates {@link GenericName} and related objects from the {@code org.opengis.util} package. 043 * <p> 044 * This class is provided for users wanting to override the validation methods. When the default 045 * behavior is sufficient, the {@link org.opengis.test.Validators} static methods provide a more 046 * convenient way to validate various kinds of objects. 047 * 048 * @author Martin Desruisseaux (Geomatys) 049 * @version 3.1 050 * @since 2.2 051 */ 052public class NameValidator extends Validator { 053 /** 054 * Creates a new validator instance. 055 * 056 * @param container the set of validators to use for validating other kinds of objects 057 * (see {@linkplain #container field javadoc}). 058 */ 059 public NameValidator(final ValidatorContainer container) { 060 super(container, "org.opengis.util"); 061 } 062 063 /** 064 * Ensures that the {@link CharSequence} methods are consistent with the {@code toString()} value. 065 * 066 * @param object the object to validate, or {@code null}. 067 */ 068 public void validate(final InternationalString object) { 069 if (object == null) { 070 return; 071 } 072 final int length = object.length(); 073 final String s = object.toString(); 074 mandatory("CharSequence: toString() shall never returns null.", s); 075 if (s != null) { 076 assertEquals("CharSequence: length is inconsistent with toString() length.", s.length(), length); 077 boolean expectLowSurrogate = false; 078 for (int i=0; i<length; i++) { 079 final char c = s.charAt(i); 080 assertEquals("CharSequence: character inconsistent with toString().", c, object.charAt(i)); 081 if (expectLowSurrogate) { 082 assertTrue("CharSequence: High surrogate shall be followed by low surrogate.", Character.isLowSurrogate(c)); 083 } 084 expectLowSurrogate = Character.isHighSurrogate(c); 085 } 086 assertFalse("CharSequence: High surrogate shall be followed by low surrogate.", expectLowSurrogate); 087 } 088 mandatory("InternationalString: toString(Locale) shall not return null.", object.toString(null)); 089 assertEquals("InternationalString: shall be equal to itself.", object, object); 090 assertEquals("InternationalString: shall be comparable to itself.", 0, object.compareTo(object)); 091 } 092 093 /** 094 * Ensures that ISO 19103 or GeoAPI restrictions apply. 095 * 096 * @param object the object to validate, or {@code null}. 097 */ 098 public void validate(final NameSpace object) { 099 if (object == null) { 100 return; 101 } 102 final GenericName name = object.name(); 103 mandatory("NameSpace: shall have a name.", name); 104 if (name != null) { 105 final NameSpace scope = name.scope(); 106 mandatory("NameSpace: identifier shall have a global scope.", scope); 107 if (scope != null) { 108 assertTrue("NameSpace: identifier scope shall be global.", scope.isGlobal()); 109 } 110 /* 111 * Following test is a consequence of the previous one, so we check the scope first in 112 * order to report the error as a bad scope before to reference this GeoAPI extension. 113 */ 114 assertSame("NameSpace: the identifier shall be fully qualified.", name, name.toFullyQualifiedName()); 115 /* 116 * Do not validate global namespaces because their name could be anything including 117 * an empty name, and the 'validate' method below does not accept empty collections. 118 */ 119 if (!object.isGlobal()) { 120 validate(name, name.getParsedNames()); 121 } 122 } 123 } 124 125 /** 126 * For each interface implemented by the given object, invokes the corresponding 127 * {@code validate(…)} method defined in this class (if any). 128 * 129 * @param object the object to dispatch to {@code validate(…)} methods, or {@code null}. 130 * @return number of {@code validate(…)} methods invoked in this class for the given object. 131 */ 132 public int dispatch(final GenericName object) { 133 int n = 0; 134 if (object != null) { 135 if (object instanceof LocalName) {validate((LocalName) object); n++;} 136 if (object instanceof ScopedName) {validate((ScopedName) object); n++;} 137 } 138 return n; 139 } 140 141 /** 142 * Performs some tests that are common to all subclasses of {@link GenericName}. This method 143 * shall not invokes {@link #validate(LocalName)} or {@link #validate(ScopedName)} in order 144 * to avoid never-ending loop. 145 * 146 * <p>This method shall not validate the scope, since it could leads to a never-ending loop.</p> 147 */ 148 private void validate(final GenericName object, final List<? extends LocalName> parsedNames) { 149 mandatory("GenericName: getParsedNames() shall not return null.", parsedNames); 150 if (parsedNames != null) { 151 validate(parsedNames); 152 assertFalse("GenericName: getParsedNames() shall not return an empty list.", parsedNames.isEmpty()); 153 final int size = parsedNames.size(); 154 assertEquals("GenericName: getParsedNames() list size shall be equal to depth().", 155 size, object.depth()); 156 assertEquals("GenericName: head() shall be the first element in getParsedNames() list.", 157 parsedNames.get(0), object.head()); 158 assertEquals("GenericName: tip() shall be the last element in getParsedNames() list.", 159 parsedNames.get(size-1), object.tip()); 160 } 161 /* 162 * Validates fully qualified name. 163 */ 164 final GenericName fullyQualified = object.toFullyQualifiedName(); 165 mandatory("GenericName: toFullyQualifiedName() shall not return null.", fullyQualified); 166 if (fullyQualified != null) { 167 assertEquals("GenericName: toFullyQualifiedName() inconsistent with the global scope status.", 168 object.scope().isGlobal(), fullyQualified == object); 169 } 170 /* 171 * Validates string representations. 172 */ 173 final String unlocalized = object.toString(); 174 mandatory("GenericName: toString() shall never returns null.", unlocalized); 175 if (unlocalized != null && fullyQualified != null) { 176 assertTrue("GenericName: fully qualified name shall end with the name.", 177 fullyQualified.toString().endsWith(unlocalized)); 178 } 179 final InternationalString localized = object.toInternationalString(); 180 validate(localized); 181 if (localized != null && fullyQualified != null) { 182 assertTrue("GenericName: fully qualified name shall end with the name (localized version).", 183 fullyQualified.toInternationalString().toString().endsWith(localized.toString())); 184 } 185 /* 186 * Validates comparisons. 187 */ 188 assertEquals("GenericName: shall be equal to itself.", object, object); 189 assertEquals("GenericName: shall be comparable to itself.", 0, object.compareTo(object)); 190 } 191 192 /** 193 * Ensures that ISO 19103 or GeoAPI restrictions apply. 194 * 195 * @param object the object to validate, or {@code null}. 196 */ 197 public void validate(final LocalName object) { 198 if (object == null) { 199 return; 200 } 201 validate(object.scope()); 202 final List<? extends LocalName> parsedNames = object.getParsedNames(); 203 validate(object, parsedNames); 204 if (parsedNames != null) { 205 assertEquals("LocalName: shall have exactly one parsed name.", 1, parsedNames.size()); 206 assertSame("LocalName: the parsed name element shall be the enclosing local name.", 207 object, parsedNames.get(0)); 208 } 209 } 210 211 /** 212 * Ensures that ISO 19103 or GeoAPI restrictions apply. 213 * 214 * @param object the object to validate, or {@code null}. 215 */ 216 public void validate(final ScopedName object) { 217 if (object == null) { 218 return; 219 } 220 final List<? extends LocalName> parsedNames = object.getParsedNames(); 221 validate(object, parsedNames); 222 final NameSpace scope = object.scope(); 223 validate(scope); 224 if (scope != null) { 225 assertEquals("ScopedName: head.scope shall be equal to the scope.", scope, object.head().scope()); 226 } 227 if (parsedNames != null) { 228 boolean global = (scope != null) && scope.isGlobal(); 229 for (final LocalName name : parsedNames) { 230 assertNotNull("ScopedName: getParsedNames() can not contain null element.", name); 231 assertNotSame("ScopedName: the enclosing scoped name can not be in any parsed name.", object, name); 232 assertEquals("ScopedName: inconsistent value of isGlobal().", global, name.scope().isGlobal()); 233 global = false; // Only the first name may be global. 234 validate(name); 235 } 236 } 237 /* 238 * Validates tail. 239 */ 240 final int depth = object.depth(); 241 final GenericName tail = object.tail(); 242 mandatory("ScopedName: tail() shall not return null.", tail); 243 if (tail != null) { 244 assertEquals("ScopedName: tail() shall have one less element than the enclosing scoped name.", 245 depth-1, tail.depth()); 246 assertEquals("ScopedName: tip().toString() and tail.tip().toString() shall be equal.", 247 object.tip(), tail.tip()); 248 if (parsedNames != null) { 249 assertEquals("ScopedName: tail() shall be defined as subList(1, depth).", 250 parsedNames.subList(1, depth), tail.getParsedNames()); 251 } 252 } 253 /* 254 * Validates path. 255 */ 256 final GenericName path = object.path(); 257 mandatory("ScopedName: the path shall not be null.", path); 258 if (path != null) { 259 assertEquals("ScopedName: path() shall have one less element than the enclosing scoped name.", 260 depth-1, path.depth()); 261 assertEquals("ScopedName: head() and path.head() shall be equal.", 262 object.head(), path.head()); 263 if (parsedNames != null) { 264 assertEquals("ScopedName: path() shall be defined as subList(0, depth-1).", 265 parsedNames.subList(0, depth-1), path.getParsedNames()); 266 } 267 } 268 } 269}