/* Copyright 2013 The jeo project. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.jeo.data;
import java.io.IOException;
import java.util.Locale;
import io.jeo.geom.Bounds;
import org.osgeo.proj4j.CoordinateReferenceSystem;
/**
* Reference to a data object.
* <p>
* A handle is a lightweight proxy to a data object meant to convey information about the object
* without necessarily having to load it. The {@link #resolve()} method is used to obtain the
* underlying data object from the handle.
* </p>
*
* @author Justin Deoliveira, Boundless
*
* @param <T>
*/
public abstract class Handle<T> implements Disposable {
/**
* object name
*/
protected String name;
/**
* object title
*/
protected String title;
/**
* object description
*/
protected String description;
/**
* object coordinate reference system
*/
protected CoordinateReferenceSystem crs;
/**
* object bounding box
*/
protected Bounds bounds;
/**
* object type
*/
protected Class<T> type;
/**
* object driver
*/
protected Driver<?> driver;
/**
* The live object.
*/
protected T obj;
/**
* Creates a new handle.
*
* @param name Name of the object.
* @param type The type of the data object.
* @param driver The format driver for the data type.
*/
@SuppressWarnings("unchecked")
protected Handle(String name, @SuppressWarnings("rawtypes") Class type, Driver<?> driver) {
this.name = name;
this.type = type;
this.driver = driver;
}
protected Handle(String name, Driver<?> driver) {
this(name, driver.type(), driver);
}
/**
* The name of the data object.
*/
public String name() {
return name;
}
/**
* The type of the data object.
*/
public Class<T> type() {
return type;
}
/**
* The format driver for the data type.
*/
public Driver<?> driver() {
return driver;
}
/**
* Returns the crs of the data object resolving the handle if necessary.
*/
public CoordinateReferenceSystem crs() throws IOException {
if (crs == null ) {
if (Dataset.class.isAssignableFrom(type)) {
crs = ((Dataset) resolve()).crs();
}
}
return crs;
}
/**
* Returns the bounds of the data object resolving the handle if necessary.
*/
public Bounds bounds() throws IOException {
if (bounds == null) {
if (Dataset.class.isAssignableFrom(type)) {
}
bounds = ((Dataset)resolve()).bounds();
}
return bounds;
}
/**
* Resolves the handle returning the underlying data object.
*
* @return The data object.
*
* @throws IOException I/O errors that occur resolving the data object.
*/
public T resolve() throws IOException {
if (obj == null) {
try {
obj = doResolve();
}
catch(ClassCastException e) {
throw new IOException("handle wrong type, expected: " + type.getName(), e);
}
}
return obj;
}
/**
* Create a Handle that resolves by calling {@link Workspace#get(String)}.
*/
public static Handle<Dataset> to(String name, final Workspace workspace) {
return new Handle<Dataset>(name, Dataset.class, workspace.driver()) {
@Override
protected Dataset doResolve() throws IOException {
return workspace.get(name);
}
};
}
public static Handle<Dataset> to(final Dataset d) {
return new Handle<Dataset>(d.name(), d.getClass(), d.driver()) {
@Override
protected Dataset doResolve() throws IOException {
return d;
}
};
}
/**
* Subclass hook to perform the resolving of the data object.
*
* @return The resolved object.
*/
protected abstract T doResolve() throws IOException;
@Override
public void close() {
if (obj != null && (obj instanceof Disposable)) {
((Disposable)obj).close();
}
obj = null;
}
@Override
public String toString() {
return String.format(Locale.ROOT,"%s[%s]", type.getSimpleName(), name);
}
}