001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2007, GeoTools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * This package contains documentation from OpenGIS specifications.
017: * OpenGIS consortium's work is fully acknowledged here.
018: */
019: package org.geotools.referencing.factory;
020:
021: // J2SE dependencies
022: import java.util.Set;
023: import java.util.Iterator;
024:
025: // OpenGIS dependencies
026: import org.opengis.util.GenericName;
027: import org.opengis.metadata.Identifier;
028: import org.opengis.metadata.citation.Citation;
029: import org.opengis.referencing.IdentifiedObject;
030: import org.opengis.referencing.AuthorityFactory;
031: import org.opengis.referencing.FactoryException;
032: import org.opengis.referencing.ReferenceIdentifier;
033: import org.opengis.referencing.NoSuchAuthorityCodeException;
034:
035: // Geotools dependencies
036: import org.geotools.referencing.CRS;
037: import org.geotools.referencing.AbstractIdentifiedObject;
038: import org.geotools.metadata.iso.citation.Citations;
039: import org.geotools.util.logging.Logging;
040:
041: /**
042: * Looks up an object from an {@linkplain AuthorityFactory authority factory} which is
043: * {@linkplain CRS#equalsIgnoreMetadata equals, ignoring metadata}, to the specified
044: * object. The main purpose of this class is to get a fully {@linkplain IdentifiedObject
045: * identified object} from an incomplete one, for example from an object without
046: * {@linkplain IdentifiedObject#getIdentifiers identifiers} or "{@code AUTHORITY[...]}"
047: * element in <cite>Well Known Text</cite> terminology.
048: *
049: * @since 2.4
050: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/factory/IdentifiedObjectFinder.java $
051: * @version $Id: IdentifiedObjectFinder.java 27862 2007-11-12 19:51:19Z desruisseaux $
052: * @author Martin Desruisseaux
053: */
054: public class IdentifiedObjectFinder {
055: /**
056: * The proxy for object creation.
057: */
058: final AuthorityFactoryProxy proxy;
059:
060: /**
061: * {@code true} for performing full scans, or {@code false} otherwise.
062: */
063: private boolean fullScan = true;
064:
065: /**
066: * Creates a finder using the same proxy than the specified finder.
067: */
068: IdentifiedObjectFinder(final IdentifiedObjectFinder finder) {
069: this .proxy = finder.proxy;
070: }
071:
072: /**
073: * Creates a finder using the specified factory. This constructor is
074: * protected because instances of this class should not be created directly.
075: * Use {@link AbstractAuthorityFactory#getIdentifiedObjectFinder} instead.
076: *
077: * @param factory The factory to scan for the identified objects.
078: * @param type The type of objects to lookup.
079: */
080: protected IdentifiedObjectFinder(final AuthorityFactory factory,
081: final Class/*<? extends IdentifiedObject>*/type) {
082: proxy = AuthorityFactoryProxy.getInstance(factory, type);
083: }
084:
085: /*
086: * Do NOT provide the following method:
087: *
088: * public AuthorityFactory getAuthorityFactory() {
089: * return proxy.getAuthorityFactory();
090: * }
091: *
092: * because the returned factory may not be the one the user would expect. Some of our
093: * AbstractAuthorityFactory implementations create proxy to the underlying backing
094: * store rather than to the factory on which 'getIdentifiedObjectFinder()' was invoked.
095: */
096:
097: /**
098: * If {@code true}, an exhaustive full scan against all registered objects
099: * will be performed (may be slow). Otherwise only a fast lookup based on
100: * embedded identifiers and names will be performed. The default value is
101: * {@code true}.
102: */
103: public boolean isFullScanAllowed() {
104: return fullScan;
105: }
106:
107: /**
108: * Set whatever an exhaustive scan against all registered objects is allowed.
109: * The default value is {@code true}.
110: */
111: public void setFullScanAllowed(final boolean fullScan) {
112: this .fullScan = fullScan;
113: }
114:
115: /**
116: * Lookups an object which is
117: * {@linkplain CRS#equalsIgnoreMetadata equals, ignoring metadata}, to the
118: * specified object. The default implementation tries to instantiate some
119: * {@linkplain IdentifiedObject identified objects} from the authority factory
120: * specified at construction time, in the following order:
121: * <p>
122: * <ul>
123: * <li>If the specified object contains {@linkplain IdentifiedObject#getIdentifiers
124: * identifiers} associated to the same authority than the factory, then those
125: * identifiers are used for {@linkplain AuthorityFactory#createObject creating
126: * objects} to be tested.</li>
127: * <li>If the authority factory can create objects from their {@linkplain
128: * IdentifiedObject#getName name} in addition of identifiers, then the name and
129: * {@linkplain IdentifiedObject#getAlias aliases} are used for creating objects
130: * to be tested.</li>
131: * <li>If {@linkplain #isFullScanAllowed full scan is allowed}, then full
132: * {@linkplain #getCodeCandidates set of authority codes} are used for
133: * creating objects to be tested.</li>
134: * </ul>
135: * <p>
136: * The first of the above created objects which is equals to the specified object in the
137: * the sense of {@link CRS#equalsIgnoreMetadata equalsIgnoreMetadata} is returned.
138: *
139: * @param object The object looked up.
140: * @return The identified object, or {@code null} if not found.
141: * @throws FactoryException if an error occured while creating an object.
142: */
143: public IdentifiedObject find(final IdentifiedObject object)
144: throws FactoryException {
145: /*
146: * First check if one of the identifiers can be used to spot directly an
147: * identified object (and check it's actually equal to one in the factory).
148: */
149: IdentifiedObject candidate = createFromIdentifiers(object);
150: if (candidate != null) {
151: return candidate;
152: }
153: /*
154: * We are unable to find the object from its identifiers. Try a quick name lookup.
155: * Some implementations like the one backed by the EPSG database are capable to find
156: * an object from its name.
157: */
158: candidate = createFromNames(object);
159: if (candidate != null) {
160: return candidate;
161: }
162: /*
163: * Here we exhausted the quick paths. Bail out if the user does not want a full scan.
164: */
165: return fullScan ? createFromCodes(object) : null;
166: }
167:
168: /**
169: * Returns the identifier of the specified object, or {@code null} if none. The default
170: * implementation invokes <code>{@linkplain #find find}(object)</code> and extracts the
171: * code from the returned {@linkplain IdentifiedObject identified object}.
172: */
173: public String findIdentifier(final IdentifiedObject object)
174: throws FactoryException {
175: final IdentifiedObject candidate = find(object);
176: return (candidate != null) ? getIdentifier(candidate) : null;
177: }
178:
179: /**
180: * Returns the identifier for the specified object.
181: */
182: final String getIdentifier(final IdentifiedObject object) {
183: Citation authority = proxy.getAuthorityFactory().getAuthority();
184: if (ReferencingFactory.ALL.equals(authority)) {
185: /*
186: * "All" is a pseudo-authority declared by AllAuthoritiesFactory. This is not a real
187: * authority, so we will not find any identifier if we search for this authority. We
188: * will rather pickup the first identifier, regardless its authority.
189: */
190: authority = null;
191: }
192: ReferenceIdentifier identifier = AbstractIdentifiedObject
193: .getIdentifier(object, authority);
194: if (identifier == null) {
195: identifier = object.getName();
196: // Should never be null past this point, since 'name' is a mandatory attribute.
197: }
198: final String codespace = identifier.getCodeSpace();
199: final String code = identifier.getCode();
200: if (codespace != null) {
201: return codespace
202: + org.geotools.util.GenericName.DEFAULT_SEPARATOR
203: + code;
204: } else {
205: return code;
206: }
207: }
208:
209: /**
210: * Creates an object {@linkplain CRS#equalsIgnoreMetadata equals, ignoring metadata}, to the
211: * specified object using only the {@linkplain IdentifiedObject#getIdentifiers identifiers}.
212: * If no such object is found, returns {@code null}.
213: * <p>
214: * This method may be used in order to get a fully identified object from a partially
215: * identified one.
216: *
217: * @param object The object looked up.
218: * @return The identified object, or {@code null} if not found.
219: * @see #createFromCodes
220: * @see #createFromNames
221: * @throws FactoryException if an error occured while creating an object.
222: */
223: final IdentifiedObject createFromIdentifiers(
224: final IdentifiedObject object) throws FactoryException {
225: final Citation authority = proxy.getAuthorityFactory()
226: .getAuthority();
227: final boolean isAll = ReferencingFactory.ALL.equals(authority);
228: for (final Iterator it = object.getIdentifiers().iterator(); it
229: .hasNext();) {
230: final Identifier id = (Identifier) it.next();
231: if (!isAll
232: && !Citations.identifierMatches(authority, id
233: .getAuthority())) {
234: // The identifier is not for this authority. Looks the other ones.
235: continue;
236: }
237: IdentifiedObject candidate;
238: try {
239: candidate = proxy.create(id.getCode());
240: } catch (NoSuchAuthorityCodeException e) {
241: // The identifier was not recognized. No problem, let's go on.
242: continue;
243: }
244: candidate = deriveEquivalent(candidate, object);
245: if (candidate != null) {
246: return candidate;
247: }
248: }
249: return null;
250: }
251:
252: /**
253: * Creates an object {@linkplain CRS#equalsIgnoreMetadata equals, ignoring metadata}, to
254: * the specified object using only the {@linkplain IdentifiedObject#getName name} and
255: * {@linkplain IdentifiedObject#getAlias aliases}. If no such object is found, returns
256: * {@code null}.
257: * <p>
258: * This method may be used with some {@linkplain AuthorityFactory authority factory}
259: * implementations like the one backed by the EPSG database, which are capable to find
260: * an object from its name when the identifier is unknown.
261: *
262: * @param object The object looked up.
263: * @return The identified object, or {@code null} if not found.
264: * @see #createFromCodes
265: * @see #createFromIdentifiers
266: * @throws FactoryException if an error occured while creating an object.
267: */
268: final IdentifiedObject createFromNames(final IdentifiedObject object)
269: throws FactoryException {
270: IdentifiedObject candidate;
271: try {
272: candidate = proxy.create(object.getName().getCode());
273: candidate = deriveEquivalent(candidate, object);
274: if (candidate != null) {
275: return candidate;
276: }
277: } catch (FactoryException e) {
278: /*
279: * The identifier was not recognized. No problem, let's go on.
280: * Note: we catch a more generic exception than NoSuchAuthorityCodeException
281: * because this attempt may fail for various reasons (character string
282: * not supported by the underlying database for primary key, duplicated
283: * name found, etc.).
284: */
285: }
286: for (final Iterator it = object.getAlias().iterator(); it
287: .hasNext();) {
288: final GenericName id = (GenericName) it.next();
289: try {
290: candidate = proxy.create(id.toString());
291: } catch (FactoryException e) {
292: // The name was not recognized. No problem, let's go on.
293: continue;
294: }
295: candidate = deriveEquivalent(candidate, object);
296: if (candidate != null) {
297: return candidate;
298: }
299: }
300: return null;
301: }
302:
303: /**
304: * Creates an object {@linkplain CRS#equalsIgnoreMetadata equals, ignoring metadata}, to the
305: * specified object. This method scans the {@linkplain #getAuthorityCodes authority codes},
306: * create the objects and returns the first one which is equals to the specified object in
307: * the sense of {@link CRS#equalsIgnoreMetadata equalsIgnoreMetadata}.
308: * <p>
309: * This method may be used in order to get a fully {@linkplain IdentifiedObject identified
310: * object} from an object without {@linkplain IdentifiedObject#getIdentifiers identifiers}.
311: * <p>
312: * Scaning the whole set of authority codes may be slow. Users should try
313: * <code>{@linkplain #createFromIdentifiers createFromIdentifiers}(object)</code> and/or
314: * <code>{@linkplain #createFromNames createFromNames}(object)</code> before to fallback
315: * on this method.
316: *
317: * @param object The object looked up.
318: * @return The identified object, or {@code null} if not found.
319: * @throws FactoryException if an error occured while scanning through authority codes.
320: *
321: * @see #createFromIdentifiers
322: * @see #createFromNames
323: */
324: final IdentifiedObject createFromCodes(final IdentifiedObject object)
325: throws FactoryException {
326: final Set/*<String>*/codes = getCodeCandidates(object);
327: for (final Iterator it = codes.iterator(); it.hasNext();) {
328: final String code = (String) it.next();
329: IdentifiedObject candidate;
330: try {
331: candidate = proxy.create(code);
332: } catch (FactoryException e) {
333: // Some object cannot be created properly.
334: continue;
335: }
336: candidate = deriveEquivalent(candidate, object);
337: if (candidate != null) {
338: return candidate;
339: }
340: }
341: return null;
342: }
343:
344: /**
345: * Returns a set of authority codes that <strong>may</strong> identify the same object than
346: * the specified one. The returned set must contains the code of every objects that are
347: * {@linkplain CRS#equalsIgnoreMetadata equals, ignoring metadata}, to the specified one.
348: * However the set is not required to contains only the codes of those objects; it may
349: * conservatively contains the code for more objects if an exact search is too expensive.
350: * <p>
351: * This method is invoked by the default {@link #find find} method implementation. The caller
352: * may iterates through every returned codes, instantiate the objects and compare them with
353: * the specified one in order to determine which codes are really applicable.
354: * <p>
355: * The default implementation returns the same set than
356: * <code>{@linkplain AuthorityFactory#getAuthorityCodes getAuthorityCodes}(type)</code>
357: * where {@code type} is the interface specified at construction type. Subclasses should
358: * override this method in order to return a smaller set, if they can.
359: *
360: * @param object The object looked up.
361: * @return A set of code candidates.
362: * @throws FactoryException if an error occured while fetching the set of code candidates.
363: */
364: protected Set/*<String>*/getCodeCandidates(
365: final IdentifiedObject object) throws FactoryException {
366: return proxy.getAuthorityCodes();
367: }
368:
369: /*
370: * Do NOT define the following method in IdentifiedObjectFinder's API:
371: *
372: * protected IdentifiedObject create(String code) throws FactoryException {
373: * return proxy.create(code);
374: * }
375: *
376: * We may be tempted to put such method in order to allow BufferedAuthorityFactory to
377: * override it with caching service, but it conflicts with AuthorityFactoryAdapter's
378: * work. The later (or to be more accurate, OrderedAxisAuthorityFactory) expects axis
379: * in (latitude,longitude) order first, in order to test this CRS before to switch to
380: * (longitude,latitude) order and test again. If the BufferedAuthorityFactory's cache
381: * is in the way, we get directly (longitude,latitude) order and miss an opportunity
382: * to identify the user's CRS.
383: *
384: * We should invoke directly AuthorityFactoryProxy.create(String) instead.
385: */
386:
387: /**
388: * Returns {@code candidate}, or an object derived from {@code candidate}, if it is
389: * {@linkplain CRS#equalsIgnoreMetadata equals ignoring metadata} to the specified
390: * model. Otherwise returns {@code null}.
391: * <p>
392: * This method is overriden by factories that may test many flavors of
393: * {@code candidate}, for example {@link TransformedAuthorityFactory}.
394: *
395: * @param candidate An object created by the factory specified at construction time.
396: * @return {@code candidate}, or an object derived from {@code candidate} (for example with axis
397: * order forced to (<var>longitude</var>, <var>latitude</var>), or {@code null} if none
398: * of the above is {@linkplain CRS#equalsIgnoreMetadata equals ignoring metadata} to the
399: * specified model.
400: *
401: * @throws FactoryException if an error occured while creating a derived object.
402: */
403: protected IdentifiedObject deriveEquivalent(
404: final IdentifiedObject candidate,
405: final IdentifiedObject model) throws FactoryException {
406: return CRS.equalsIgnoreMetadata(candidate, model) ? candidate
407: : null;
408: }
409:
410: /**
411: * Returns a string representation of this finder, for debugging purpose only.
412: */
413: //@Override
414: public String toString() {
415: return proxy.toString(IdentifiedObjectFinder.class);
416: }
417:
418: /**
419: * A finder which delegate part of its work to an other finder. This adapter forwards
420: * some method calls to the underlying finder. This class should not be public, because
421: * not all method are overriden. The choice is tuned for {@link BufferedAuthorityFactory}
422: * and {@link AuthorityFactoryAdapter} needs and may not be appropriate in the general case.
423: *
424: * @author Martin Desruisseaux
425: */
426: static class Adapter extends IdentifiedObjectFinder {
427: /**
428: * The finder on which to delegate the work.
429: */
430: protected final IdentifiedObjectFinder finder;
431:
432: /**
433: * Creates an adapter for the specified finder.
434: */
435: protected Adapter(final IdentifiedObjectFinder finder) {
436: super (finder);
437: this .finder = finder;
438: }
439:
440: /**
441: * Set whatever an exhaustive scan against all registered objects is allowed.
442: */
443: //@Override
444: public void setFullScanAllowed(final boolean fullScan) {
445: finder.setFullScanAllowed(fullScan);
446: super .setFullScanAllowed(fullScan);
447: }
448:
449: /**
450: * Returns a set of authority codes that <strong>may</strong> identify the same object
451: * than the specified one. The default implementation delegates to the backing finder.
452: */
453: //@Override
454: protected Set/*<String>*/getCodeCandidates(
455: final IdentifiedObject object) throws FactoryException {
456: return finder.getCodeCandidates(object);
457: }
458:
459: /**
460: * Returns {@code candidate}, or an object derived from {@code candidate}, if it is
461: * {@linkplain CRS#equalsIgnoreMetadata equals ignoring metadata} to the specified
462: * model. The default implementation delegates to the backing finder.
463: */
464: //@Override
465: protected IdentifiedObject deriveEquivalent(
466: final IdentifiedObject candidate,
467: final IdentifiedObject model) throws FactoryException {
468: return finder.deriveEquivalent(candidate, model);
469: }
470: }
471: }
|