| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| PropertyEpsgFactory |
|
| 3.0;3 |
| 1 | /* | |
| 2 | * Geotoolkit.org - An Open Source Java GIS Toolkit | |
| 3 | * http://www.geotoolkit.org | |
| 4 | * | |
| 5 | * (C) 2005-2012, Open Source Geospatial Foundation (OSGeo) | |
| 6 | * (C) 2009-2012, Geomatys | |
| 7 | * | |
| 8 | * This library is free software; you can redistribute it and/or | |
| 9 | * modify it under the terms of the GNU Lesser General Public | |
| 10 | * License as published by the Free Software Foundation; | |
| 11 | * version 2.1 of the License. | |
| 12 | * | |
| 13 | * This library is distributed in the hope that it will be useful, | |
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| 16 | * Lesser General Public License for more details. | |
| 17 | */ | |
| 18 | package org.geotoolkit.referencing.factory.epsg; | |
| 19 | ||
| 20 | import java.util.Set; | |
| 21 | import java.util.Map; | |
| 22 | import java.util.TreeSet; | |
| 23 | import java.util.TreeMap; | |
| 24 | import java.io.PrintWriter; | |
| 25 | import java.io.IOException; | |
| 26 | import net.jcip.annotations.ThreadSafe; | |
| 27 | ||
| 28 | import org.opengis.util.FactoryException; | |
| 29 | import org.opengis.metadata.citation.Citation; | |
| 30 | import org.opengis.referencing.IdentifiedObject; | |
| 31 | import org.opengis.referencing.crs.CRSAuthorityFactory; | |
| 32 | import org.opengis.referencing.crs.CoordinateReferenceSystem; | |
| 33 | ||
| 34 | import org.geotoolkit.factory.Hints; | |
| 35 | import org.geotoolkit.factory.AuthorityFactoryFinder; | |
| 36 | import org.geotoolkit.referencing.factory.AbstractAuthorityFactory; | |
| 37 | import org.geotoolkit.referencing.factory.wkt.PropertyAuthorityFactory; | |
| 38 | import org.geotoolkit.metadata.iso.citation.Citations; | |
| 39 | import org.geotoolkit.io.TableWriter; | |
| 40 | import org.geotoolkit.io.IndentedLineWriter; | |
| 41 | import org.geotoolkit.resources.Vocabulary; | |
| 42 | ||
| 43 | ||
| 44 | /** | |
| 45 | * Authority factory for {@linkplain CoordinateReferenceSystem Coordinate Reference Systems} | |
| 46 | * beyong the one defined in the EPSG database. This factory is used as a fallback when a | |
| 47 | * requested code is not found in the EPSG database, or when there is no connection at all | |
| 48 | * to the EPSG database. The CRS are defined as <cite>Well Known Text</cite> in a property | |
| 49 | * file named {@value #FILENAME}. The search path is as below, in that order: | |
| 50 | * | |
| 51 | * <ol> | |
| 52 | * <li><p>If a value for {@link Hints#CRS_AUTHORITY_EXTRA_DIRECTORY} exists in the hints map | |
| 53 | * given at construction time, then that value will be used as the directory where to | |
| 54 | * search for the {@value #FILENAME} file. Reminder: such hint can be | |
| 55 | * {@linkplain Hints#putSystemDefault defined system-wide} for convenience.</p></li> | |
| 56 | * <li><p>The {@value #FILENAME} files found in all {@code org/geotoolkit/referencing/factory/epsg} | |
| 57 | * directories on the classpath are merged with the values found in previous step, if any. | |
| 58 | * If the same value is defined twice, the value of previous step have precedence.</p></li> | |
| 59 | * </ol> | |
| 60 | * | |
| 61 | * This factory can also be used to provide custom extensions or overrides to a main EPSG factory: | |
| 62 | * | |
| 63 | * <ul> | |
| 64 | * <li><p>In order to provide a custom extension, you can create a subclass that invoke the | |
| 65 | * {@link #PropertyEpsgFactory(Hints, String, Citation[])} constructor with different constants. | |
| 66 | * You can also subclass {@link PropertyAuthorityFactory} directly for getting more control.</p></li> | |
| 67 | * | |
| 68 | * <li><p>In order to make the factory be an override, override the | |
| 69 | * {@link #setOrdering setOrdering(...)} method as below: | |
| 70 | * | |
| 71 | * {@preformat java | |
| 72 | * protected void setOrdering(Organizer organizer) { | |
| 73 | * organizer.before(ThreadedEpsgFactory.class); | |
| 74 | * } | |
| 75 | * }</p></li> | |
| 76 | * </ul> | |
| 77 | * | |
| 78 | * {@section Caching of CRS objects} | |
| 79 | * This factory doesn't cache any result. Any call to a {@code createFoo} method | |
| 80 | * will trig a new WKT parsing. For adding caching service, this factory should | |
| 81 | * be wrapped in {@link org.geotoolkit.referencing.factory.CachingAuthorityFactory}. | |
| 82 | * Note that this is done automatically when this factory is used through the | |
| 83 | * {@link org.geotoolkit.referencing.CRS} static methods. | |
| 84 | * | |
| 85 | * {@section Troubleshotting} | |
| 86 | * If the {@value #FILENAME} file is on the classpath but seems to be ignored, | |
| 87 | * the following actions may provide some useful informations: | |
| 88 | * | |
| 89 | * <ul> | |
| 90 | * <li><p>Print the list of every registered factories using the code snippet documented | |
| 91 | * in the {@link org.geotoolkit.referencing.factory.FactoryDependencies} class and verify | |
| 92 | * that {@code PropertyEpsgFactory} is presents.</p></li> | |
| 93 | * | |
| 94 | * <li><p>Set the logging level for the {@code org.geotoolkit} loggers to {@code CONFIG}. | |
| 95 | * See <a href="http://java.sun.com/javase/6/docs/technotes/guides/logging/overview.html">Java | |
| 96 | * Logging Overview</a> for the standard way, or use the following Geotk convenience method: | |
| 97 | * | |
| 98 | * {@preformat java | |
| 99 | * Logging.GEOTOOLKIT.forceMonolineConsoleOutput(Level.CONFIG); | |
| 100 | * } | |
| 101 | * </p></li> | |
| 102 | * | |
| 103 | * <li><p>Force the system to ignore any factory other than {@code PropertyEpsgFactory}: | |
| 104 | * | |
| 105 | * {@preformat java | |
| 106 | * Hints.putSystemDefault(Hints.CRS_AUTHORITY_FACTORY, PropertyEpsgFactory.class); | |
| 107 | * } | |
| 108 | * </p></li> | |
| 109 | * </ul> | |
| 110 | * | |
| 111 | * @author Martin Desruisseaux (IRD) | |
| 112 | * @author Jody Garnett (Refractions) | |
| 113 | * @author Rueben Schulz (UBC) | |
| 114 | * @author Andrea Aime (TOPP) | |
| 115 | * @version 3.00 | |
| 116 | * | |
| 117 | * @since 2.1 | |
| 118 | * @module | |
| 119 | */ | |
| 120 | @ThreadSafe | |
| 121 | public class PropertyEpsgFactory extends PropertyAuthorityFactory implements CRSAuthorityFactory { | |
| 122 | /** | |
| 123 | * The default filename to read, which is {@value}. The default | |
| 124 | * {@code PropertyEpsgFactory} implementation will search for every occurrences of | |
| 125 | * {@code org/geotoolkit/referencing/factory/espg/epsg.properties} on the classpath. | |
| 126 | * However a different directory for this filename can be specified using | |
| 127 | * {@link Hints#CRS_AUTHORITY_EXTRA_DIRECTORY}. | |
| 128 | */ | |
| 129 | public static final String FILENAME = "epsg.properties"; | |
| 130 | ||
| 131 | /** | |
| 132 | * Constructs a default authority factory. | |
| 133 | * | |
| 134 | * @throws IOException If an error occurred while reading the definition files. | |
| 135 | * Note that do exception is thrown if there is no file - in this case | |
| 136 | * the factory is only considered not {@linkplain #availability available}. | |
| 137 | */ | |
| 138 | public PropertyEpsgFactory() throws IOException { | |
| 139 | 1 | this(EMPTY_HINTS); |
| 140 | 1 | } |
| 141 | ||
| 142 | /** | |
| 143 | * Constructs an authority factory from the given hints. | |
| 144 | * Hints of special interest are: | |
| 145 | * <p> | |
| 146 | * <ul> | |
| 147 | * <li>{@link Hints#CRS_AUTHORITY_EXTRA_DIRECTORY}</li> | |
| 148 | * <li>{@link Hints#FORCE_LONGITUDE_FIRST_AXIS_ORDER}</li> | |
| 149 | * </ul> | |
| 150 | * <p> | |
| 151 | * This constructor recognizes also {@link Hints#CRS_FACTORY CRS}, {@link Hints#CS_FACTORY CS}, | |
| 152 | * {@link Hints#DATUM_FACTORY DATUM} and {@link Hints#MATH_TRANSFORM_FACTORY MATH_TRANSFORM} | |
| 153 | * {@code FACTORY} hints. | |
| 154 | * | |
| 155 | * @param userHints An optional set of hints, or {@code null} if none. | |
| 156 | * @throws IOException If an error occurred while reading the definition files. | |
| 157 | * Note that do exception is thrown if there is no file - in this case | |
| 158 | * the factory is only considered not {@linkplain #availability available}. | |
| 159 | */ | |
| 160 | public PropertyEpsgFactory(final Hints userHints) throws IOException { | |
| 161 | 2 | this(userHints, FILENAME, Citations.EPSG); |
| 162 | 2 | } |
| 163 | ||
| 164 | /** | |
| 165 | * Constructs an authority factory for the given authorities. This constructor recognizes | |
| 166 | * the same hints than {@link #PropertyEpsgFactory(Hints)}. The {@code authorities} argument | |
| 167 | * should enumerate all relevant authorities, with {@linkplain Citations#EPSG EPSG} in last | |
| 168 | * position. For example {@link EsriExtension} returns {{@linkplain Citations#ESRI ESRI}, | |
| 169 | * {@linkplain Citations#EPSG EPSG}}. | |
| 170 | * | |
| 171 | * @param userHints | |
| 172 | * An optional set of hints, or {@code null} if none. | |
| 173 | * @param filename | |
| 174 | * The name of the file to look in the {@link Hints#CRS_AUTHORITY_EXTRA_DIRECTORY} | |
| 175 | * directory and in every {@code org/geotoolkit/referencing/factory/espg} directories | |
| 176 | * found on the classpath. | |
| 177 | * @param authorities | |
| 178 | * The organizations or parties responsible for definition and maintenance of the | |
| 179 | * database. Should contains at least {@link Citations#EPSG}. | |
| 180 | * @throws IOException If an error occurred while reading the definition files. | |
| 181 | * Note that do exception is thrown if there is no file - in this case | |
| 182 | * the factory is only considered not {@linkplain #availability available}. | |
| 183 | * | |
| 184 | * @since 3.00 | |
| 185 | */ | |
| 186 | public PropertyEpsgFactory(final Hints userHints, final String filename, final Citation... authorities) | |
| 187 | throws IOException | |
| 188 | { | |
| 189 | 8 | super(userHints, Hints.CRS_AUTHORITY_EXTRA_DIRECTORY, PropertyEpsgFactory.class, filename, authorities); |
| 190 | 8 | } |
| 191 | ||
| 192 | /** | |
| 193 | * Invoked by {@code FactoryRegistry} on registration. The default implementation | |
| 194 | * declares that this factory should give precedence to {@link ThreadedEpsgFactory} | |
| 195 | * and {@link LongitudeFirstEpsgFactory}. | |
| 196 | */ | |
| 197 | @Override | |
| 198 | protected void setOrdering(final Organizer organizer) { | |
| 199 | 1 | super.setOrdering(organizer); |
| 200 | 1 | organizer.after(ThreadedEpsgFactory.class, true); |
| 201 | 1 | organizer.after(LongitudeFirstEpsgFactory.class, false); |
| 202 | 1 | } |
| 203 | ||
| 204 | /** | |
| 205 | * Prints a list of codes that duplicate the ones provided by {@link ThreadedEpsgFactory}. | |
| 206 | * This is used in order to check the content of the {@value #FILENAME} file (or whatever | |
| 207 | * property file used as backing store for this factory) from the command line. | |
| 208 | * | |
| 209 | * {@preformat text | |
| 210 | * java -jar geotk-referencing.jar test duplicates | |
| 211 | * } | |
| 212 | * | |
| 213 | * @param out The writer where to print the report. | |
| 214 | * @return The set of duplicated codes. | |
| 215 | * @throws FactoryException if an error occurred. | |
| 216 | * | |
| 217 | * @see org.geotoolkit.console.ReferencingCommands#test | |
| 218 | * | |
| 219 | * @since 2.4 | |
| 220 | */ | |
| 221 | public Set<String> reportDuplicates(final PrintWriter out) throws FactoryException { | |
| 222 | 0 | final AbstractAuthorityFactory sqlFactory = |
| 223 | (AbstractAuthorityFactory) AuthorityFactoryFinder.getCRSAuthorityFactory( | |
| 224 | "EPSG", new Hints(Hints.CRS_AUTHORITY_FACTORY, ThreadedEpsgFactory.class)); | |
| 225 | 0 | final Vocabulary resources = Vocabulary.getResources(null); |
| 226 | 0 | out.println(resources.getLabel(Vocabulary.Keys.COMPARE_WITH)); |
| 227 | try { | |
| 228 | 0 | final IndentedLineWriter w = new IndentedLineWriter(out); |
| 229 | 0 | w.setIndentation(4); |
| 230 | 0 | w.write(sqlFactory.getBackingStoreDescription()); |
| 231 | 0 | w.flush(); |
| 232 | 0 | } catch (IOException e) { |
| 233 | // Should never happen, since we are writing to a PrintWriter. | |
| 234 | 0 | throw new AssertionError(e); |
| 235 | 0 | } |
| 236 | 0 | out.println(); |
| 237 | 0 | final Set<String> wktCodes = this. getAuthorityCodes(IdentifiedObject.class); |
| 238 | 0 | final Set<String> sqlCodes = sqlFactory.getAuthorityCodes(IdentifiedObject.class); |
| 239 | 0 | final Set<String> duplicated = new TreeSet<String>(); |
| 240 | 0 | for (String code : wktCodes) { |
| 241 | 0 | code = code.trim(); |
| 242 | 0 | if (sqlCodes.contains(code)) { |
| 243 | 0 | duplicated.add(code); |
| 244 | /* | |
| 245 | * Note: we don't use wktCodes.retainsAll(sqlCode) because the Set implementations | |
| 246 | * are usually not the standard ones, but rather some implementations backed | |
| 247 | * by a connection to the resources of the underlying factory. We also close | |
| 248 | * the connection after this loop for the same reason. In addition, we take | |
| 249 | * this opportunity for sorting the codes. | |
| 250 | */ | |
| 251 | } | |
| 252 | } | |
| 253 | 0 | if (duplicated.isEmpty()) { |
| 254 | 0 | out.println(resources.getString(Vocabulary.Keys.NO_DUPLICATION_FOUND)); |
| 255 | } else { | |
| 256 | 0 | for (final String code : duplicated) { |
| 257 | 0 | out.print(resources.getLabel(Vocabulary.Keys.DUPLICATED_VALUE)); |
| 258 | 0 | out.println(code); |
| 259 | } | |
| 260 | } | |
| 261 | 0 | return duplicated; |
| 262 | } | |
| 263 | ||
| 264 | /** | |
| 265 | * Prints a list of CRS that can't be instantiated. This is used in order to check the content | |
| 266 | * of the {@value #FILENAME} file (or whatever property file used as backing store for this | |
| 267 | * factory) from the command line. To lauch from the command line, use the following: | |
| 268 | * | |
| 269 | * {@preformat text | |
| 270 | * java -jar geotk-referencing.jar test creates | |
| 271 | * } | |
| 272 | * | |
| 273 | * @param out The writer where to print the report. | |
| 274 | * @return The set of codes that can't be instantiated. | |
| 275 | * @throws FactoryException if an error occurred while | |
| 276 | * {@linkplain #getAuthorityCodes fetching authority codes}. | |
| 277 | * | |
| 278 | * @see org.geotoolkit.console.ReferencingCommands#test | |
| 279 | * | |
| 280 | * @since 2.4 | |
| 281 | */ | |
| 282 | public Set<String> reportInstantiationFailures(final PrintWriter out) throws FactoryException { | |
| 283 | 0 | final Set<String> codes = getAuthorityCodes(CoordinateReferenceSystem.class); |
| 284 | 0 | final Map<String,String> failures = new TreeMap<String,String>(); |
| 285 | 0 | for (final String code : codes) { |
| 286 | try { | |
| 287 | 0 | createCoordinateReferenceSystem(code); |
| 288 | 0 | } catch (FactoryException exception) { |
| 289 | 0 | failures.put(code, exception.getLocalizedMessage()); |
| 290 | 0 | } |
| 291 | } | |
| 292 | 0 | if (!failures.isEmpty()) { |
| 293 | 0 | final TableWriter writer = new TableWriter(out, " "); |
| 294 | 0 | for (final Map.Entry<String,String> entry : failures.entrySet()) { |
| 295 | 0 | writer.write(entry.getKey()); |
| 296 | 0 | writer.write(':'); |
| 297 | 0 | writer.nextColumn(); |
| 298 | 0 | writer.write(entry.getValue()); |
| 299 | 0 | writer.nextLine(); |
| 300 | } | |
| 301 | try { | |
| 302 | 0 | writer.flush(); |
| 303 | 0 | } catch (IOException e) { |
| 304 | // Should not happen, since we are writing to a PrintWriter | |
| 305 | 0 | throw new AssertionError(e); |
| 306 | 0 | } |
| 307 | } | |
| 308 | 0 | return failures.keySet(); |
| 309 | } | |
| 310 | } |