001 /*
002 * GeoAPI - Java interfaces for OGC/ISO standards
003 * http://www.geoapi.org
004 *
005 * Copyright (C) 2008-2013 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 */
032 package org.opengis.test.util;
033
034 import java.util.List;
035 import org.opengis.util.*;
036 import org.opengis.test.Validator;
037 import org.opengis.test.ValidatorContainer;
038 import 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 */
052 public 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 // Following test is a consequence of the previous one, so we check the scope first in
111 // order to report the error as a bad scope before to reference this GeoAPI extension.
112 assertSame("NameSpace: the identifier shall be fully qualified.", name, name.toFullyQualifiedName());
113 }
114 // Do not validate global namespaces because their name could be anything including
115 // an empty name, and the 'validate' method below does not accept empty collections.
116 if (!object.isGlobal()) {
117 validate(name, name.getParsedNames());
118 }
119 }
120
121 /**
122 * For each interface implemented by the given object, invokes the corresponding
123 * {@code validate(...)} method defined in this class (if any).
124 *
125 * @param object The object to dispatch to {@code validate(...)} methods, or {@code null}.
126 * @return Number of {@code validate(...)} methods invoked in this class for the given object.
127 */
128 public int dispatch(final GenericName object) {
129 int n = 0;
130 if (object != null) {
131 if (object instanceof LocalName) {validate((LocalName) object); n++;}
132 if (object instanceof ScopedName) {validate((ScopedName) object); n++;}
133 }
134 return n;
135 }
136
137 /**
138 * Performs some tests that are common to all subclasses of {@link GenericName}. This method
139 * shall not invokes {@link #validate(LocalName)} or {@link #validate(ScopedName)} in order
140 * to avoid never-ending loop.
141 *
142 * <p>This method shall not validate the scope, since it could leads to a never-ending loop.</p>
143 */
144 private void validate(final GenericName object, final List<? extends LocalName> parsedNames) {
145 mandatory("GenericName: getParsedNames() shall not return null.", parsedNames);
146 if (parsedNames != null) {
147 validate(parsedNames);
148 assertFalse("GenericName: getParsedNames() shall not return an empty list.", parsedNames.isEmpty());
149 final int size = parsedNames.size();
150 assertEquals("GenericName: getParsedNames() list size shall be equal to depth().",
151 size, object.depth());
152 assertEquals("GenericName: head() shall be the first element in getParsedNames() list.",
153 parsedNames.get(0), object.head());
154 assertEquals("GenericName: tip() shall be the last element in getParsedNames() list.",
155 parsedNames.get(size-1), object.tip());
156 }
157 /*
158 * Validates fully qualified name.
159 */
160 final GenericName fullyQualified = object.toFullyQualifiedName();
161 mandatory("GenericName: toFullyQualifiedName() shall not return null.", fullyQualified);
162 if (fullyQualified != null) {
163 assertEquals("GenericName: toFullyQualifiedName() inconsistent with the global scope status.",
164 object.scope().isGlobal(), fullyQualified == object);
165 }
166 /*
167 * Validates string representations.
168 */
169 final String unlocalized = object.toString();
170 mandatory("GenericName: toString() shall never returns null.", unlocalized);
171 if (unlocalized != null && fullyQualified != null) {
172 assertTrue("GenericName: fully qualified name shall end with the name.",
173 fullyQualified.toString().endsWith(unlocalized));
174 }
175 final InternationalString localized = object.toInternationalString();
176 validate(localized);
177 if (localized != null && fullyQualified != null) {
178 assertTrue("GenericName: fully qualified name shall end with the name (localized version).",
179 fullyQualified.toInternationalString().toString().endsWith(localized.toString()));
180 }
181 /*
182 * Validates comparisons.
183 */
184 assertEquals("GenericName: shall be equal to itself.", object, object);
185 assertEquals("GenericName: shall be comparable to itself.", 0, object.compareTo(object));
186 }
187
188 /**
189 * Ensures that ISO 19103 or GeoAPI restrictions apply.
190 *
191 * @param object The object to validate, or {@code null}.
192 */
193 public void validate(final LocalName object) {
194 if (object == null) {
195 return;
196 }
197 validate(object.scope());
198 final List<? extends LocalName> parsedNames = object.getParsedNames();
199 validate(object, parsedNames);
200 if (parsedNames != null) {
201 assertEquals("LocalName: shall have exactly one parsed name.", 1, parsedNames.size());
202 assertSame("LocalName: the parsed name element shall be the enclosing local name.",
203 object, parsedNames.get(0));
204 }
205 }
206
207 /**
208 * Ensures that ISO 19103 or GeoAPI restrictions apply.
209 *
210 * @param object The object to validate, or {@code null}.
211 */
212 public void validate(final ScopedName object) {
213 if (object == null) {
214 return;
215 }
216 final List<? extends LocalName> parsedNames = object.getParsedNames();
217 validate(object, parsedNames);
218 final NameSpace scope = object.scope();
219 validate(scope);
220 if (scope != null) {
221 assertEquals("ScopedName: head.scope shall be equal to the scope.", scope, object.head().scope());
222 }
223 if (parsedNames != null) {
224 boolean global = scope.isGlobal();
225 for (final LocalName name : parsedNames) {
226 assertNotNull("ScopedName: getParsedNames() can not contain null element.", name);
227 assertNotSame("ScopedName: the enclosing scoped name can not be in any parsed name.", object, name);
228 assertEquals("ScopedName: inconsistent value of isGlobal().", global, name.scope().isGlobal());
229 global = false; // Only the first name may be global.
230 validate(name);
231 }
232 }
233 /*
234 * Validates tail.
235 */
236 final int depth = object.depth();
237 final GenericName tail = object.tail();
238 mandatory("ScopedName: tail() shall not return null.", tail);
239 if (tail != null) {
240 assertEquals("ScopedName: tail() shall have one less element than the enclosing scoped name.",
241 depth-1, tail.depth());
242 assertEquals("ScopedName: tip().toString() and tail.tip().toString() shall be equal.",
243 object.tip(), tail.tip());
244 if (parsedNames != null) {
245 assertEquals("ScopedName: tail() shall be defined as subList(1, depth).",
246 parsedNames.subList(1, depth), tail.getParsedNames());
247 }
248 }
249 /*
250 * Validates path.
251 */
252 final GenericName path = object.path();
253 mandatory("ScopedName: the path shall not be null.", path);
254 if (path != null) {
255 assertEquals("ScopedName: path() shall have one less element than the enclosing scoped name.",
256 depth-1, path.depth());
257 assertEquals("ScopedName: head() and path.head() shall be equal.",
258 object.head(), path.head());
259 if (parsedNames != null) {
260 assertEquals("ScopedName: path() shall be defined as subList(0, depth-1).",
261 parsedNames.subList(0, depth-1), path.getParsedNames());
262 }
263 }
264 }
265 }