Show:
#include "gdal_common.hpp"
#include "gdal_majorobject.hpp"
#include "gdal_driver.hpp"
#include "gdal_dataset.hpp"
#include "utils/string_list.hpp"

namespace node_gdal {

Nan::Persistent<FunctionTemplate> Driver::constructor;
ObjectCache<GDALDriver, Driver> Driver::cache;
#if GDAL_VERSION_MAJOR < 2
ObjectCache<OGRSFDriver, Driver> Driver::cache_ogr;
#endif

void Driver::Initialize(Local<Object> target)
{
	Nan::HandleScope scope;

	Local<FunctionTemplate> lcons = Nan::New<FunctionTemplate>(Driver::New);
	lcons->InstanceTemplate()->SetInternalFieldCount(1);
	lcons->SetClassName(Nan::New("Driver").ToLocalChecked());

	Nan::SetPrototypeMethod(lcons, "toString", toString);
	Nan::SetPrototypeMethod(lcons, "open", open);
	Nan::SetPrototypeMethod(lcons, "create", create);
	Nan::SetPrototypeMethod(lcons, "createCopy", createCopy);
	Nan::SetPrototypeMethod(lcons, "deleteDataset", deleteDataset);
	Nan::SetPrototypeMethod(lcons, "rename", rename);
	Nan::SetPrototypeMethod(lcons, "copyFiles", copyFiles);
	Nan::SetPrototypeMethod(lcons, "getMetadata", getMetadata);

	ATTR(lcons, "description", descriptionGetter, READ_ONLY_SETTER);

	Nan::Set(target, Nan::New("Driver").ToLocalChecked(), Nan::GetFunction(lcons).ToLocalChecked());

	constructor.Reset(lcons);
}


#if GDAL_VERSION_MAJOR < 2
Driver::Driver(GDALDriver *driver)
	: Nan::ObjectWrap(), uses_ogr(false), this_gdaldriver(driver), this_ogrdriver(0)
{
	LOG("Created GDAL Driver [%p]", driver);
}
Driver::Driver(OGRSFDriver *driver)
	: Nan::ObjectWrap(), uses_ogr(true), this_gdaldriver(0), this_ogrdriver(driver)
{
	LOG("Created OGR Driver [%p]", driver);
}
Driver::Driver()
	: Nan::ObjectWrap(), uses_ogr(false), this_gdaldriver(0), this_ogrdriver(0)
{
}
#else
Driver::Driver(GDALDriver *driver)
	: Nan::ObjectWrap(), this_gdaldriver(driver)
{
	LOG("Created GDAL Driver [%p]", driver);
}
Driver::Driver()
	: Nan::ObjectWrap(), this_gdaldriver(0)
{
}
#endif



Driver::~Driver()
{
	dispose();
}

void Driver::dispose()
{
	#if GDAL_VERSION_MAJOR < 2
	if(uses_ogr) {
		if(this_ogrdriver) {
			LOG("Disposing OGR Driver [%p]", this_ogrdriver);
			cache_ogr.erase(this_ogrdriver);
			LOG("Disposed OGR Driver [%p]", this_ogrdriver);
			this_ogrdriver = NULL;
		}
		return;
	}
	#endif
	if(this_gdaldriver) {
		LOG("Disposing GDAL Driver [%p]", this_gdaldriver);
		cache.erase(this_gdaldriver);
		LOG("Disposed GDAL Driver [%p]", this_gdaldriver);
		this_gdaldriver = NULL;
	}
}


/**
 * Format specific driver.
 *
 * An instance of this class is created for each supported format, and
 * manages information about the format.
 *
 * This roughly corresponds to a file format, though some drivers may
 * be gateways to many formats through a secondary multi-library.
 *
 * @class gdal.Driver
 */
NAN_METHOD(Driver::New)
{
	Nan::HandleScope scope;

	if (!info.IsConstructCall()) {
		Nan::ThrowError("Cannot call constructor as function, you need to use 'new' keyword");
		return;
	}

	if (info[0]->IsExternal()) {
		Local<External> ext = info[0].As<External>();
		void* ptr = ext->Value();
		Driver *f =  static_cast<Driver *>(ptr);
		f->Wrap(info.This());

		info.GetReturnValue().Set(info.This());
		return;
	} else {
		Nan::ThrowError("Cannot create Driver directly");
		return;
	}
}

Local<Value> Driver::New(GDALDriver *driver)
{
	Nan::EscapableHandleScope scope;

	if (!driver) {
		return scope.Escape(Nan::Null());
	}
	if (cache.has(driver)) {
		return scope.Escape(cache.get(driver));
	}

	Driver *wrapped = new Driver(driver);
	Local<Value> ext = Nan::New<External>(wrapped);
	Local<Object> obj = Nan::NewInstance(Nan::GetFunction(Nan::New(Driver::constructor)).ToLocalChecked(), 1, &ext).ToLocalChecked();

	//LOG("ADDING DRIVER TO CACHE [%p]", driver);
	cache.add(driver, obj);
	//LOG("DONE ADDING DRIVER TO CACHE [%p]", driver);

	return scope.Escape(obj);
}

#if GDAL_VERSION_MAJOR < 2
Local<Value> Driver::New(OGRSFDriver *driver)
{
	Nan::EscapableHandleScope scope;

	if (!driver) {
		return scope.Escape(Nan::Null());
	}
	if (cache_ogr.has(driver)) {
		return scope.Escape(cache_ogr.get(driver));
	}

	Driver *wrapped = new Driver(driver);
	Local<Value> ext = Nan::New<External>(wrapped);
	v8::Local<v8::Object> obj = Nan::NewInstance(Nan::GetFunction(Nan::New(Driver::constructor)).ToLocalChecked(), 1, &ext).ToLocalChecked();

	cache_ogr.add(driver, obj);

	return scope.Escape(obj);
}
#endif

NAN_METHOD(Driver::toString)
{
	Nan::HandleScope scope;
	info.GetReturnValue().Set(Nan::New("Driver").ToLocalChecked());
}

/**
 * @readOnly
 * @attribute description
 * @type String
 */
NAN_GETTER(Driver::descriptionGetter)
{
	Nan::HandleScope scope;
	Driver* driver = Nan::ObjectWrap::Unwrap<Driver>(info.This());

	#if GDAL_VERSION_MAJOR < 2
	if (driver->uses_ogr) {
		info.GetReturnValue().Set(SafeString::New(driver->getOGRSFDriver()->GetName()));
		return;
	}
	#endif

	info.GetReturnValue().Set(SafeString::New(driver->getGDALDriver()->GetDescription()));
}

/**
 * @throws Error
 * @method deleteDataset
 * @param {string} filename
 */
NAN_METHOD(Driver::deleteDataset)
{
	Nan::HandleScope scope;

	std::string name("");
	NODE_ARG_STR(0, "dataset name", name);

	Driver* driver = Nan::ObjectWrap::Unwrap<Driver>(info.This());

	#if GDAL_VERSION_MAJOR < 2
	if (driver->uses_ogr) {
		OGRErr err = driver->getOGRSFDriver()->DeleteDataSource(name.c_str());
		if(err) {
			NODE_THROW_OGRERR(err);
			return;
		}
		return;
	}
	#endif

	CPLErr err = driver->getGDALDriver()->Delete(name.c_str());
	if (err) {
		NODE_THROW_CPLERR(err);
		return;
	}
	return;
}

/**
 * Create a new dataset with this driver.
 *
 * @throws Error
 * @method create
 * @param {String} filename
 * @param {Integer} [x_size=0] raster width in pixels (ignored for vector datasets)
 * @param {Integer} [y_size=0] raster height in pixels (ignored for vector datasets)
 * @param {Integer} [band_count=0]
 * @param {Integer} [data_type=gdal.GDT_Byte] pixel data type (ignored for vector datasets) (see {{#crossLink "Constants (GDT)"}}data types{{/crossLink}})
 * @param {String[]|object} [creation_options] An array or object containing driver-specific dataset creation options
 * @return gdal.Dataset
 */
NAN_METHOD(Driver::create)
{
	Nan::HandleScope scope;
	Driver *driver = Nan::ObjectWrap::Unwrap<Driver>(info.This());

	std::string filename;
	unsigned int x_size = 0, y_size = 0, n_bands = 0;
	GDALDataType type = GDT_Byte;
	std::string type_name = "";
	StringList options;

	NODE_ARG_STR(0, "filename", filename);

	if(info.Length() < 3){
		if(info.Length() > 1 && options.parse(info[1])){
			return; //error parsing string list
		}
	} else {
		NODE_ARG_INT(1, "x size", x_size);
		NODE_ARG_INT(2, "y size", y_size);
		NODE_ARG_INT_OPT(3, "number of bands", n_bands);
		NODE_ARG_OPT_STR(4, "data type", type_name);
		if(info.Length() > 5 && options.parse(info[5])){
			return; //error parsing string list
		}
		if(!type_name.empty()) {
			type = GDALGetDataTypeByName(type_name.c_str());
		}
	}


	#if GDAL_VERSION_MAJOR < 2
	if(driver->uses_ogr){
		OGRSFDriver *raw = driver->getOGRSFDriver();
		OGRDataSource *ds = raw->CreateDataSource(filename.c_str(), options.get());

		if (!ds) {
			Nan::ThrowError("Error creating dataset");
			return;
		}

		info.GetReturnValue().Set(Dataset::New(ds));
		return;
	}
	#endif

	GDALDriver *raw = driver->getGDALDriver();
	GDALDataset* ds = raw->Create(filename.c_str(), x_size, y_size, n_bands, type, options.get());

	if (!ds) {
		Nan::ThrowError("Error creating dataset");
		return;
	}

	info.GetReturnValue().Set(Dataset::New(ds));
}

/**
 * Create a copy of a dataset.
 *
 * @throws Error
 * @method createCopy
 * @param {String} filename
 * @param {gdal.Dataset} src
 * @param {Boolean} [strict=false]
 * @param {String[]|object} [options=null] An array or object containing driver-specific dataset creation options
 * @return gdal.Dataset
 */
NAN_METHOD(Driver::createCopy)
{
	Nan::HandleScope scope;
	Driver *driver = Nan::ObjectWrap::Unwrap<Driver>(info.This());

	if(!driver->isAlive()){
		Nan::ThrowError("Driver object has already been destroyed");
		return;
	}

	std::string filename;
	Dataset* src_dataset;
	unsigned int strict = 0;
	StringList options;

	NODE_ARG_STR(0, "filename", filename);

	//NODE_ARG_STR(1, "source dataset", src_dataset)
	if(info.Length() < 2){
		Nan::ThrowError("source dataset must be provided");
		return;
	}
	if (IS_WRAPPED(info[1], Dataset)) {
		src_dataset = Nan::ObjectWrap::Unwrap<Dataset>(info[1].As<Object>());
	} else {
		Nan::ThrowError("source dataset must be a Dataset object");
		return;
	}

	if(!src_dataset->isAlive()){
		Nan::ThrowError("Dataset object has already been destroyed");
		return;
	}

	if(info.Length() > 2 && options.parse(info[2])){
		return; //error parsing string list
	}

	#if GDAL_VERSION_MAJOR < 2
	if (driver->uses_ogr != src_dataset->uses_ogr){
		Nan::ThrowError("Driver unable to copy dataset");
		return;
	}
	if (driver->uses_ogr) {
		OGRSFDriver *raw = driver->getOGRSFDriver();
		OGRDataSource *raw_ds = src_dataset->getDatasource();

		OGRDataSource *ds = raw->CopyDataSource(raw_ds, filename.c_str(), options.get());

		if (!ds) {
			Nan::ThrowError("Error copying dataset.");
			return;
		}

		info.GetReturnValue().Set(Dataset::New(ds));
		return;
	}
	#endif

	GDALDriver *raw = driver->getGDALDriver();
	GDALDataset *raw_ds = src_dataset->getDataset();
	GDALDataset *ds = raw->CreateCopy(filename.c_str(), raw_ds, strict, options.get(), NULL, NULL);

	if (!ds) {
		Nan::ThrowError("Error copying dataset");
		return;
	}

	info.GetReturnValue().Set(Dataset::New(ds));
}

/**
 * Copy the files of a dataset.
 *
 * @throws Error
 * @method copyFiles
 * @param {String} name_old New name for the dataset.
 * @param {String} name_new Old name of the dataset.
 */
NAN_METHOD(Driver::copyFiles)
{
	Nan::HandleScope scope;
	Driver *driver = Nan::ObjectWrap::Unwrap<Driver>(info.This());
	std::string old_name;
	std::string new_name;

	#if GDAL_VERSION_MAJOR < 2
	if(driver->uses_ogr) {
		Nan::ThrowError("Driver unable to copy files");
		return;
	}
	#endif

	NODE_ARG_STR(0, "new name", new_name);
	NODE_ARG_STR(1, "old name", old_name);

	CPLErr err = driver->getGDALDriver()->CopyFiles(new_name.c_str(), old_name.c_str());
	if (err) {
		NODE_THROW_CPLERR(err);
		return;
	}

	return;
}

/**
 * Renames the dataset.
 *
 * @throws Error
 * @method rename
 * @param {String} new_name New name for the dataset.
 * @param {String} old_name Old name of the dataset.
 */
NAN_METHOD(Driver::rename)
{
	Nan::HandleScope scope;
	Driver *driver = Nan::ObjectWrap::Unwrap<Driver>(info.This());
	std::string old_name;
	std::string new_name;

	#if GDAL_VERSION_MAJOR < 2
	if(driver->uses_ogr) {
		Nan::ThrowError("Driver unable to rename files");
		return;
	}
	#endif

	NODE_ARG_STR(0, "new name", new_name);
	NODE_ARG_STR(1, "old name", old_name);

	CPLErr err = driver->getGDALDriver()->Rename(new_name.c_str(), old_name.c_str());
	if (err) {
		NODE_THROW_CPLERR(err);
		return;
	}

	return;
}

/**
 * Returns metadata about the driver.
 *
 * @throws Error
 * @method getMetadata
 * @param {String} [domain]
 * @return Object
 */
NAN_METHOD(Driver::getMetadata)
{
	Nan::HandleScope scope;
	Driver *driver = Nan::ObjectWrap::Unwrap<Driver>(info.This());

	Local<Object> result;

	std::string domain("");
	NODE_ARG_OPT_STR(0, "domain", domain);

	#if GDAL_VERSION_MAJOR < 2
	if (driver->uses_ogr){
		result = Nan::New<Object>();
		Nan::Set(result, Nan::New("DCAP_VECTOR").ToLocalChecked(), Nan::New("YES").ToLocalChecked());
		info.GetReturnValue().Set(result);
		return;
	}
	#endif

	GDALDriver* raw = driver->getGDALDriver();
	result = MajorObject::getMetadata(raw, domain.empty() ? NULL : domain.c_str());
	#if GDAL_VERSION_MAJOR < 2
		Nan::Set(result, Nan::New("DCAP_RASTER").ToLocalChecked(), Nan::New("YES").ToLocalChecked());
	#endif
	info.GetReturnValue().Set(result);
}

/**
 * Opens a dataset.
 *
 * @throws Error
 * @method open
 * @param {String} path
 * @param {String} [mode=`"r"`] The mode to use to open the file: `"r"` or `"r+"`
 * @return {gdal.Dataset}
 */
NAN_METHOD(Driver::open)
{
	Nan::HandleScope scope;
	Driver *driver = Nan::ObjectWrap::Unwrap<Driver>(info.This());

	std::string path;
	std::string mode = "r";
	GDALAccess access = GA_ReadOnly;

	NODE_ARG_STR(0, "path", path);
	NODE_ARG_OPT_STR(1, "mode", mode);

	if (mode == "r+") {
		access = GA_Update;
	} else if (mode != "r") {
		Nan::ThrowError("Invalid open mode. Must be \"r\" or \"r+\"");
		return;
	}

	#if GDAL_VERSION_MAJOR < 2
	if (driver->uses_ogr){
		OGRSFDriver *raw = driver->getOGRSFDriver();
		OGRDataSource *ds = raw->Open(path.c_str(), static_cast<int>(access));
		if (!ds) {
			Nan::ThrowError("Error opening dataset");
			return;
		}
		info.GetReturnValue().Set(Dataset::New(ds));
		return;
	}
	#endif

	GDALDriver *raw = driver->getGDALDriver();
	GDALOpenInfo *open_info = new GDALOpenInfo(path.c_str(), access);
	GDALDataset *ds = raw->pfnOpen(open_info);
	delete open_info;
	if (!ds) {
		Nan::ThrowError("Error opening dataset");
		return;
	}
	info.GetReturnValue().Set(Dataset::New(ds));
}

} // namespace node_gdal