/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2012-2014, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotoolkit.storage.coverage;
import java.awt.Point;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.util.Classes;
import org.apache.sis.util.collection.TreeTable.Node;
import org.apache.sis.util.logging.Logging;
import org.geotoolkit.utility.parameter.ParametersExt;
import org.geotoolkit.storage.DataNode;
import org.geotoolkit.storage.DataStore;
import org.geotoolkit.storage.StorageEvent;
import org.geotoolkit.storage.StorageListener;
import org.geotoolkit.version.Version;
import org.geotoolkit.version.VersionControl;
import org.geotoolkit.version.VersioningException;
import org.opengis.util.GenericName;
import org.opengis.metadata.Metadata;
import org.opengis.parameter.ParameterValue;
import org.opengis.parameter.ParameterValueGroup;
/**
* Abstract implementation of a coverage store.
*
* @author Johann Sorel (Geomatys)
* @module
*/
public abstract class AbstractCoverageStore extends DataStore implements CoverageStore {
protected static final String NO_NAMESPACE = "no namespace";
private static final Logger LOGGER = Logging.getLogger("org.geotoolkit.storage.coverage");
private final String defaultNamespace;
protected final ParameterValueGroup parameters;
protected final Set<StorageListener> storeListeners = new HashSet<>();
private final HashMap<GenericName, CoverageReference> cachedRefs = new HashMap<>();
protected AbstractCoverageStore(final ParameterValueGroup params) {
this.parameters = params;
ParameterValue pv = ParametersExt.getValue(params, AbstractCoverageStoreFactory.NAMESPACE.getName().getCode());
String namespace = (pv==null) ? null : pv.stringValue();
if (namespace == null) {
defaultNamespace = "http://geotoolkit.org";
} else if (namespace.equals(NO_NAMESPACE)) {
defaultNamespace = null;
} else {
defaultNamespace = namespace;
}
//redirect warning listener events to default logger
listeners.getLogger().setUseParentHandlers(false);
listeners.getLogger().addHandler(new Handler() {
@Override
public void publish(LogRecord record) {
getLogger().log(record);
}
@Override
public void flush() {}
@Override
public void close() throws SecurityException {}
});
}
@Override
public Metadata getMetadata() throws DataStoreException {
return null;
}
@Override
public ParameterValueGroup getConfiguration() {
return parameters;
}
protected String getDefaultNamespace() {
return defaultNamespace;
}
protected Logger getLogger(){
return LOGGER;
}
/**
* Returns the root node of the data store.
* This node is the main access point to the content of the store.
*
* TODO move this in Apache SIS DataStore class when ready
*
* @return DataNode never null.
*/
public abstract DataNode getRootNode() throws DataStoreException;
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append(Classes.getShortClassName(this));
try {
final DataNode node = getRootNode();
sb.append(' ');
sb.append(node.toString());
} catch (DataStoreException ex) {
Logging.getLogger("org.geotoolkit.storage").log(Level.WARNING, null, ex);
}
return sb.toString();
}
@Override
public CoverageReference create(GenericName name) throws DataStoreException {
throw new DataStoreException("Creation of new coverage not supported.");
}
@Override
public void delete(GenericName name) throws DataStoreException {
throw new DataStoreException("Deletion of coverage not supported.");
}
////////////////////////////////////////////////////////////////////////////
// Convinient methods, fallback on getRootNode //
////////////////////////////////////////////////////////////////////////////
@Override
public final Set<GenericName> getNames() throws DataStoreException {
final Map<GenericName,CoverageReference> map = listReferences();
return map.keySet();
}
@Override
public final CoverageReference getCoverageReference(GenericName name) throws DataStoreException {
final Map<GenericName,CoverageReference> map = listReferences();
final CoverageReference ref = map.get(name);
if(ref==null){
final StringBuilder sb = new StringBuilder("Type name : ");
sb.append(name);
sb.append(" do not exist in this datastore, available names are : ");
for(final GenericName n : map.keySet()){
sb.append(n).append(", ");
}
throw new DataStoreException(sb.toString());
}
return ref;
}
protected Map<GenericName,CoverageReference> listReferences() throws DataStoreException {
if (cachedRefs.isEmpty()) {
listReferences(getRootNode(), cachedRefs);
}
return cachedRefs;
}
private Map<GenericName,CoverageReference> listReferences(Node node, Map<GenericName,CoverageReference> map){
if(node instanceof CoverageReference){
final CoverageReference cr = (CoverageReference) node;
map.put(cr.getName(), cr);
}
for(Node child : node.getChildren()){
listReferences(child, map);
}
return map;
}
////////////////////////////////////////////////////////////////////////////
// versioning methods : handle nothing by default //
////////////////////////////////////////////////////////////////////////////
@Override
public boolean handleVersioning() {
return false;
}
@Override
public VersionControl getVersioning(GenericName typeName) throws VersioningException {
throw new VersioningException("Versioning not supported");
}
@Override
public CoverageReference getCoverageReference(GenericName name, Version version) throws DataStoreException {
throw new DataStoreException("Versioning not supported");
}
////////////////////////////////////////////////////////////////////////////
// convinient methods //
////////////////////////////////////////////////////////////////////////////
protected CoverageStoreManagementEvent fireCoverageAdded(final GenericName name){
final CoverageStoreManagementEvent event = CoverageStoreManagementEvent.createCoverageAddEvent(this, name);
sendStructureEvent(event);
return event;
}
protected CoverageStoreManagementEvent fireCoverageUpdated(final GenericName name){
final CoverageStoreManagementEvent event = CoverageStoreManagementEvent.createCoverageUpdateEvent(this, name);
sendStructureEvent(event);
return event;
}
protected CoverageStoreManagementEvent fireCoverageDeleted(final GenericName name){
final CoverageStoreManagementEvent event = CoverageStoreManagementEvent.createCoverageDeleteEvent(this, name);
sendStructureEvent(event);
return event;
}
protected CoverageStoreManagementEvent firePyramidAdded(final GenericName name, final String pyramidId){
final CoverageStoreManagementEvent event = CoverageStoreManagementEvent.createPyramidAddEvent(this, name, pyramidId);
sendStructureEvent(event);
return event;
}
protected CoverageStoreManagementEvent firePyramidUpdated(final GenericName name, final String pyramidId){
final CoverageStoreManagementEvent event = CoverageStoreManagementEvent.createPyramidUpdateEvent(this, name, pyramidId);
sendStructureEvent(event);
return event;
}
protected CoverageStoreManagementEvent firePyramidDeleted(final GenericName name, final String pyramidId){
final CoverageStoreManagementEvent event = CoverageStoreManagementEvent.createPyramidDeleteEvent(this, name, pyramidId);
sendStructureEvent(event);
return event;
}
protected CoverageStoreManagementEvent fireMosaicAdded(final GenericName name, final String pyramidId, final String mosaicId){
final CoverageStoreManagementEvent event = CoverageStoreManagementEvent.createMosaicAddEvent(this, name, pyramidId, mosaicId);
sendStructureEvent(event);
return event;
}
protected CoverageStoreManagementEvent fireMosaicUpdated(final GenericName name, final String pyramidId, final String mosaicId){
final CoverageStoreManagementEvent event = CoverageStoreManagementEvent.createMosaicUpdateEvent(this, name, pyramidId, mosaicId);
sendStructureEvent(event);
return event;
}
protected CoverageStoreManagementEvent fireMosaicDeleted(final GenericName name, final String pyramidId, final String mosaicId){
final CoverageStoreManagementEvent event = CoverageStoreManagementEvent.createMosaicDeleteEvent(this, name, pyramidId, mosaicId);
sendStructureEvent(event);
return event;
}
protected CoverageStoreContentEvent fireDataUpdated(final GenericName name){
final CoverageStoreContentEvent event = CoverageStoreContentEvent.createDataUpdateEvent(this, name);
sendContentEvent(event);
return event;
}
protected CoverageStoreContentEvent fireTileAdded(final GenericName name,
final String pyramidId, final String mosaicId, final List<Point> tiles){
final CoverageStoreContentEvent event = CoverageStoreContentEvent.createTileAddEvent(this, name, pyramidId, mosaicId, tiles);
sendContentEvent(event);
return event;
}
protected CoverageStoreContentEvent fireTileUpdated(final GenericName name,
final String pyramidId, final String mosaicId, final List<Point> tiles){
final CoverageStoreContentEvent event = CoverageStoreContentEvent.createTileUpdateEvent(this, name, pyramidId, mosaicId, tiles);
sendContentEvent(event);
return event;
}
protected CoverageStoreContentEvent fireTileDeleted(final GenericName name,
final String pyramidId, final String mosaicId, final List<Point> tiles){
final CoverageStoreContentEvent event = CoverageStoreContentEvent.createTileDeleteEvent(this, name, pyramidId, mosaicId, tiles);
sendContentEvent(event);
return event;
}
/**
* Convinient method to check that the given type name exist.
* Will raise a datastore exception if the name do not exist in this datastore.
* @param candidate Name to test.
* @throws DataStoreException if name do not exist.
*/
protected void typeCheck(final GenericName candidate) throws DataStoreException{
final Collection<GenericName> names = getNames();
if(!names.contains(candidate)){
final StringBuilder sb = new StringBuilder("Type name : ");
sb.append(candidate);
sb.append(" do not exist in this datastore, available names are : ");
for(final GenericName n : names){
sb.append(n).append(", ");
}
throw new DataStoreException(sb.toString());
}
}
public void addStorageListener(final StorageListener listener) {
synchronized (storeListeners) {
storeListeners.add(listener);
}
}
public void removeStorageListener(final StorageListener listener) {
synchronized (storeListeners) {
storeListeners.remove(listener);
}
}
/**
* Forward a structure event to all listeners.
* @param event , event to send to listeners.
*/
protected void sendStructureEvent(final StorageEvent event){
cachedRefs.clear();
final StorageListener[] lst;
synchronized (storeListeners) {
lst = storeListeners.toArray(new StorageListener[storeListeners.size()]);
}
for(final StorageListener listener : lst){
listener.structureChanged(event);
}
}
/**
* Forward a data event to all listeners.
* @param event , event to send to listeners.
*/
protected void sendContentEvent(final StorageEvent event){
final StorageListener[] lst;
synchronized (storeListeners) {
lst = storeListeners.toArray(new StorageListener[storeListeners.size()]);
}
for(final StorageListener listener : lst){
listener.contentChanged(event);
}
}
/**
* Forward given event, changing the source by this object.
* For implementation use only.
* @param event
*/
public void forwardStructureEvent(StorageEvent event){
sendStructureEvent(event.copy(this));
}
/**
* Forward given event, changing the source by this object.
* For implementation use only.
* @param event
*/
public void forwardContentEvent(StorageEvent event){
sendContentEvent(event.copy(this));
}
}