/* Copyright (c) 2013-2014 Boundless and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Distribution License v1.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/org/documents/edl-v10.html * * Contributors: * Gabriel Roldan (Boundless) - initial implementation */ package org.locationtech.geogig.geotools.data; import java.awt.RenderingHints.Key; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.util.Collections; import java.util.Map; import javax.annotation.Nullable; import org.geotools.data.DataStoreFactorySpi; import org.geotools.util.KVP; import org.locationtech.geogig.api.ContextBuilder; import org.locationtech.geogig.api.GeoGIG; import org.locationtech.geogig.api.GlobalContextBuilder; import org.locationtech.geogig.cli.CLIContextBuilder; import org.locationtech.geogig.repository.Repository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Preconditions; public class GeoGigDataStoreFactory implements DataStoreFactorySpi { private static final Logger LOGGER = LoggerFactory.getLogger(GeoGigDataStoreFactory.class); /** GEO_GIG */ public static final String DISPLAY_NAME = "GeoGIG"; static { if (GlobalContextBuilder.builder == null || GlobalContextBuilder.builder.getClass().equals(ContextBuilder.class)) { GlobalContextBuilder.builder = new CLIContextBuilder(); } } public static interface RepositoryLookup { public File resolve(String repository); } public static class DefaultRepositoryLookup implements RepositoryLookup { @Override public File resolve(String repository) { return new File(repository); } } public static final Param RESOLVER_CLASS_NAME = new Param( "resolver", String.class, "Fully qualified class name for the RepositoryLookup that resolves the REPOSITORY parameter to an actual path", false, DefaultRepositoryLookup.class.getName(), new KVP(Param.LEVEL, "advanced")); public static final Param REPOSITORY = new Param("geogig_repository", String.class, "Root directory for the geogig repository", true, "/path/to/repository") { @Override public String lookUp(Map<String, ?> map) throws IOException { if (null == map.get(key)) { throw new IOException(String.format("Parameter %s is required: %s", key, description)); } String value = String.valueOf(map.get(key)); return value; } }; public static final Param BRANCH = new Param( "branch", String.class, "Optional branch name the DataStore operates against, defaults to the currently checked out branch", false); public static final Param HEAD = new Param( "head", String.class, "Optional refspec (branch name, commit id, etc.) the DataStore operates against, defaults to the currently checked out branch", false); public static final Param DEFAULT_NAMESPACE = new Param("namespace", String.class, "Optional namespace for feature types that do not declare a Namespace themselves", false); public static final Param CREATE = new Param("create", Boolean.class, "Optional flag to enable creation of a new repository if it does not exist", false); @Override public String getDisplayName() { return DISPLAY_NAME; } @Override public String getDescription() { return "GeoGIG Versioning DataStore"; } @Override public Param[] getParametersInfo() { return new Param[] { RESOLVER_CLASS_NAME, REPOSITORY, BRANCH, HEAD, DEFAULT_NAMESPACE, CREATE }; } @Override public boolean canProcess(Map<String, Serializable> params) { try { final String repoParam = (String) REPOSITORY.lookUp(params); final String resolverClass = (String) RESOLVER_CLASS_NAME.lookUp(params); if (resolverClass != null) { return true; } File repository = new File(repoParam); // check that repository points to a file, and either that fiel is a directory, or the // the create option is set return repository instanceof File && ((File) repository).isDirectory() || Boolean.TRUE.equals(CREATE.lookUp(params)); } catch (IOException ignoreMe) { // } if (params.containsKey(REPOSITORY.key)) { LOGGER.warn("Unable to process parameter %s: '%s'", REPOSITORY.key, params.get(REPOSITORY.key)); } return false; } /** * @see org.geotools.data.DataAccessFactory#isAvailable() */ @Override public boolean isAvailable() { return true; } @Override public Map<Key, ?> getImplementationHints() { return Collections.emptyMap(); } @Override public GeoGigDataStore createDataStore(Map<String, Serializable> params) throws IOException { @Nullable final String lookUpClass = (String) RESOLVER_CLASS_NAME.lookUp(params); RepositoryLookup resolver = resolver(lookUpClass); final String repositoryLocation = (String) REPOSITORY.lookUp(params); @Nullable final String defaultNamespace = (String) DEFAULT_NAMESPACE.lookUp(params); @Nullable final String branch = (String) BRANCH.lookUp(params); @Nullable final String head = (String) HEAD.lookUp(params); @Nullable final String effectiveHead = (head == null) ? branch : head; @Nullable final Boolean create = (Boolean) CREATE.lookUp(params); final File repositoryDirectory = resolver.resolve(repositoryLocation); if (create != null && create.booleanValue()) { if (!repositoryDirectory.exists()) { return createNewDataStore(params); } } GeoGIG geogig; try { geogig = new GeoGIG(repositoryDirectory); } catch (RuntimeException e) { throw new IOException(e.getMessage(), e); } Repository repository = geogig.getRepository(); if (null == repository) { if (create != null && create.booleanValue()) { return createNewDataStore(params); } throw new IOException(String.format("Directory is not a geogig repository: '%s'", repositoryDirectory.getAbsolutePath())); } GeoGigDataStore store = new GeoGigDataStore(geogig); if (defaultNamespace != null) { store.setNamespaceURI(defaultNamespace); } if (effectiveHead != null) { store.setHead(effectiveHead); } return store; } private RepositoryLookup resolver(@Nullable String lookUpClass) throws IOException { if (null == lookUpClass) { return new DefaultRepositoryLookup(); } try { Class<?> clazz = Class.forName(lookUpClass); Object instance = clazz.newInstance(); Preconditions.checkArgument(instance instanceof RepositoryLookup); return RepositoryLookup.class.cast(instance); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | ClassCastException e) { throw new IOException(String.format( "Parameter '%s' ('%s') can't be instantiated as a %s", RESOLVER_CLASS_NAME.key, lookUpClass, RepositoryLookup.class.getName())); } } /** * @see org.geotools.data.DataStoreFactorySpi#createNewDataStore(java.util.Map) */ @Override public GeoGigDataStore createNewDataStore(Map<String, Serializable> params) throws IOException { String defaultNamespace = (String) DEFAULT_NAMESPACE.lookUp(params); File repositoryRoot = new File((String) REPOSITORY.lookUp(params)); if (!repositoryRoot.isDirectory()) { if (repositoryRoot.exists()) { throw new IOException(repositoryRoot.getAbsolutePath() + " is not a directory"); } repositoryRoot.mkdirs(); } GeoGIG geogig = new GeoGIG(repositoryRoot); try { Repository repository = geogig.getOrCreateRepository(); Preconditions.checkState(repository != null); } catch (RuntimeException e) { throw new IOException(e); } GeoGigDataStore store = new GeoGigDataStore(geogig); if (defaultNamespace != null) { store.setNamespaceURI(defaultNamespace); } return store; } }