earthengine-api/javascript/src/deserializer.js
Matt Hancher 94dbed5ce0 v0.1.18
2014-02-13 15:22:13 -08:00

178 lines
5.3 KiB
JavaScript

/**
* @fileoverview A deserializer that decodes EE object trees from JSON DAGs.
*
*/
goog.provide('ee.Deserializer');
goog.require('ee.ApiFunction');
goog.require('ee.ComputedObject');
goog.require('ee.CustomFunction');
goog.require('ee.Date');
goog.require('ee.Encodable');
goog.require('ee.Function');
goog.require('ee.Geometry');
goog.require('goog.array');
goog.require('goog.json');
goog.require('goog.object');
/**
* A deserializer for EE object trees.
* @constructor
*/
ee.Deserializer = function() {};
// Exporting manually to avoid marking the class public in the docs.
goog.exportSymbol('ee.Deserializer', ee.Deserializer);
/**
* Deserialize an object from a JSON string appropriate for API calls.
* @param {string} json The JSON represenation of the input.
* @return {*} The deserialized object.
* @export
*/
ee.Deserializer.fromJSON = function(json) {
return ee.Deserializer.decode(goog.json.parse(json));
};
/**
* Decodes an object previously encoded using the EE API v2 (DAG) format.
*
* @param {*} json The serialied object to decode.
* @return {*} The decoded object.
* @export
*/
ee.Deserializer.decode = function(json) {
var namedValues = {};
// Incrementally decode scope entries if there are any.
if (goog.isObject(json) && json['type'] == 'CompoundValue') {
var scopes = json['scope'];
for (var i = 0; i < scopes.length; i++) {
var key = scopes[i][0];
var value = scopes[i][1];
if (key in namedValues) {
throw Error('Duplicate scope key "' + key + '" in scope #' + i + '.');
}
namedValues[key] = ee.Deserializer.decodeValue_(value, namedValues);
}
json = json['value'];
}
// Decode the final value.
return ee.Deserializer.decodeValue_(json, namedValues);
};
/**
* Decodes an object previously encoded using the EE API v2 (DAG) format. This
* uses a provided scopre for ValueRef lookup and does not not allow the input
* to be a CompoundValue.
*
* @param {*} json The serialied object to decode.
* @param {Object} namedValues The objects that can be referenced by ValueRefs.
* @return {*} The decoded object.
* @private
*/
ee.Deserializer.decodeValue_ = function(json, namedValues) {
// Check for primitive values.
if (goog.isNull(json) ||
goog.isNumber(json) ||
goog.isBoolean(json) ||
goog.isString(json)) {
return json;
}
// Check for array values.
if (goog.isArray(json)) {
return goog.array.map(json, function(element) {
return ee.Deserializer.decodeValue_(element, namedValues);
});
}
// Ensure that we've got a proper object at this point.
if (!goog.isObject(json) || goog.isFunction(json)) {
throw Error('Cannot decode object: ' + json);
}
// Check for explicitly typed values.
var typeName = json['type'];
switch (typeName) {
case 'ValueRef':
if (json['value'] in namedValues) {
return namedValues[json['value']];
} else {
throw Error('Unknown ValueRef: ' + json);
}
case 'ArgumentRef':
var varName = json['value'];
if (!goog.isString(varName)) {
throw Error('Invalid variable name: ' + varName);
}
return ee.CustomFunction.variable(Object, varName);
case 'Date':
var microseconds = json['value'];
if (!goog.isNumber(microseconds)) {
throw Error('Invalid date value: ' + microseconds);
}
return new ee.Date(microseconds / 1000);
case 'Bytes':
var result = new ee.Encodable();
result.encode = function(encoder) { return json; };
return result;
case 'Invocation':
var func;
if ('functionName' in json) {
func = ee.ApiFunction.lookup(json['functionName']);
} else {
func = ee.Deserializer.decodeValue_(json['function'], namedValues);
}
var args = goog.object.map(json['arguments'], function(element) {
return ee.Deserializer.decodeValue_(element, namedValues);
});
if (func instanceof ee.Function) {
return func.apply(args);
} else if (func instanceof ee.ComputedObject) {
// We have to allow ComputedObjects for cases where invocations
// return a function, e.g. Image.parseExpression().
return new ee.ComputedObject(/** @type {?} */(func), args);
} else {
throw Error('Invalid function value: ' + json['function']);
}
case 'Dictionary':
return goog.object.map(json['value'], function(element) {
return ee.Deserializer.decodeValue_(element, namedValues);
});
case 'Function':
var body = ee.Deserializer.decodeValue_(json['body'], namedValues);
var signature = {
'name': '',
'args': goog.array.map(json['argumentNames'], function(argName) {
return {
'name': argName,
'type': 'Object',
'optional': false
};
}),
'returns': 'Object'
};
return new ee.CustomFunction(signature, function() { return body; });
case 'Point':
case 'MultiPoint':
case 'LineString':
case 'MultiLineString':
case 'Polygon':
case 'MultiPolygon':
case 'LinearRing':
case 'GeometryCollection':
return new ee.Geometry(json);
case 'CompoundValue':
throw Error('Nested CompoundValues are disallowed.');
default:
throw Error('Unknown encoded object type: ' + typeName);
}
};