/*globals ObjectRow,DataRelation,define,self,jsDataSet,jsDataQuery,metaModel,appMeta,sqlFun,_ */
/**
* @module getDataUtils
* @description
* Collection of utility functions for GetData
*/
(function (q,logger,logType,jsDataSet,_) {
/** Detect free variable `global` from Node.js. */
let freeGlobal = typeof global === 'object' && global && global.Object === Object && global;
/** Detect free variable `self`. */
let freeSelf = typeof self === 'object' && self && self.Object === Object && self;
/** Used as a reference to the global object. */
let root = freeGlobal || freeSelf || Function('return this')();
/** Detect free variable `exports`. */
let freeExports = typeof exports === 'object' && exports && !exports.nodeType && exports;
/** Detect free variable `module`. */
let freeModule = freeExports && typeof module === 'object' && module && !module.nodeType && module;
//noinspection JSUnresolvedVariable
/** Detect free variable `global` from Node.js or Browserified code and use it as `root`. (thanks lodash)*/
let moduleExports = freeModule && freeModule.exports === freeExports;
let getDataUtils = {};
let dataRowState = jsDataSet.dataRowState;
/**
* @function getJsObjectFromJson
* @public
* @description SYNC
* Given a json representation of the DataSet/DataTable returns a javascript object
* @param {string} json Json string
* @returns {object} an object (DataTable or DataSet)
*/
getDataUtils.getJsObjectFromJson = function (json) {
// riconverto la stringa json proveniente dal server
return JSON.parse(json);
};
/**
* @function getJsDataTableFromJson
* @public
* @description SYNC
* Given a json representation of the DataTable returns a Js DataTable
* @param {string} jsonJsDataTable JSon string
* @returns {DataTable} the datatable
*/
getDataUtils.getJsDataTableFromJson = function (jsonJsDataTable) {
// riconverto la stringa json proveniente dal server
let objParsed = getDataUtils.getJsObjectFromJson(jsonJsDataTable);
// creo nuovo jsDataSet da popolare
let dt = new jsDataSet.DataTable(objParsed.name);
// deserializzo il json proveniente dal server e popolo ds
dt.deSerialize(objParsed, true);
return dt;
};
/**
* @function getJsDataSetFromJson
* @public
* @description SYNC
* Given a json representation of the DataSet returns a JsDataSet
* @param {string} jsonJsDataSet JSon string
* @returns {DataSet} the dataset
*/
getDataUtils.getJsDataSetFromJson = function (jsonJsDataSet) {
// riconverto la stringa json proveniente dal server
let objParsed = getDataUtils.getJsObjectFromJson(jsonJsDataSet);
// creo nuovo jsDataSet da popolare
let ds = new jsDataSet.DataSet(objParsed.name);
// deserializzo il json proveniente dal server e popolo ds
ds.deSerialize(objParsed, true);
return ds;
};
/**
* @function getJsonFromJsDataSet
* @public
* @description SYNC
* Given a jsDataSet returns the json string. First it calls the methods serialize() of jsDataSet and then returns the json representation of the dataset object
* @param {DataSet} ds
* @param {boolean} serializeStructure. If true it serialize data and structure
* @returns {string} the json string
*/
getDataUtils.getJsonFromJsDataSet = function (ds, serializeStructure) {
return JSON.stringify(ds.serialize(serializeStructure));
};
/**
* @function getJsonFromJsDataSet
* @public
* @description SYNC
* Serializes a DataTable with the data and structure
* @param {DataTable} dt
* @returns {string} the json string
*/
getDataUtils.getJsonFromDataTable = function (dt) {
let objser = dt.serialize(true);
return JSON.stringify(objser);
};
/**
* @function getJsonFromMessages
* @public
* @description SYNC
* Given an array of message object returns the json string
* @param {string[]} messages
* @return {string}
*/
getDataUtils.getJsonFromMessages = function (messages) {
if (!messages) return;
if (messages.length === 0) return;
return JSON.stringify(messages);
};
/**
* @function getJsDataQueryFromJson
* @description SYNC
* Given a json representation of the JsDataQuery returns a JsDataQuery
* @public
* @param {string} jsonJsDataQuery Json string
* @returns {sqlFun} the jsDataQuery representation of the json
*/
getDataUtils.getJsDataQueryFromJson = function (jsonJsDataQuery) {
// riconverto la stringa json proveniente dal server
let objParsed = getDataUtils.getJsObjectFromJson(jsonJsDataQuery);
return q.fromObject(objParsed);
};
/**
* @function getJsonFromJsDataQuery
* @public
* @description SYNC
* Given jsDataQuery returns the json string. first it converts jsDataQuery into js object and to a json string
* @param {jsDataQuery} dataQuery
* @returns {string} the json string
*/
getDataUtils.getJsonFromJsDataQuery = function (dataQuery) {
return JSON.stringify(q.toObject(dataQuery));
};
/**
* @function getDataRelationSerialized
* @public
* @description SYNC
* Serializes the DataRelation "rel"
* @param {DataRelation} rel
* @returns {string} the string of DataRelation serialized
*/
getDataUtils.getDataRelationSerialized = function (rel) {
if (!rel) return "";
return JSON.stringify(rel.serialize());
};
/**
* @function cloneDataTable
* @public
* @description SYNC
* Returns a cloned copy of "dt" input using the ser/der methods of the framework
* @param {DataTable} dt
* @returns {DataTable}
*/
getDataUtils.cloneDataTable = function (dt) {
let dsClone = getDataUtils.cloneDataSet(dt.dataset);
let t = getDataUtils.getJsDataTableFromJson(appMeta.getDataUtils.getJsonFromDataTable(dt));
dt.dataset = dsClone;
return t;
};
/**
* @function cloneDataSet
* @public
* @description SYNC
* Returns a cloned copy of "ds" input using the ser/der methods of the framework
* @param {DataSet} ds
* @returns {DataSet}
*/
getDataUtils.cloneDataSet = function (ds) {
return getDataUtils.getJsDataSetFromJson(appMeta.getDataUtils.getJsonFromJsDataSet(ds, true));
};
/**
* @function mergeDataSet
* @public
* @description SYNC
* Merges the rows of dsSource into dsTarget
* @param {DataSet} dsDest. DataSet target, where inject new rows, taken form dsSource
* @param {DataSet} dsSource. The new DataSet, with modifies read from server. Need to merge these rows into dsTarget
* @param {boolean} checkExistence
*/
getDataUtils.mergeDataSet = function (dsDest, dsSource, checkExistence) {
_.forEach(dsSource.tables, function (tSource) {
// se il mio dsTarget contiene la tabella allora effettuo merge delle righe
if (dsDest.tables[tSource.name]){
// se non ci sono inutile fare il check esistenza. così si va più rapidi
if (!dsDest.tables[tSource.name].rows.length) {
getDataUtils.mergeRowsIntoTable(dsDest.tables[tSource.name], tSource.rows, false);
} else {
getDataUtils.mergeRowsIntoTable(dsDest.tables[tSource.name], tSource.rows, checkExistence);
}
}else{
logger.log(logType.ERROR, "Table " + tSource.name + " does not exists in dataset " + dsDest.name);
}
});
};
/**
* @function mergeDataSetChanges
* @public
* @description SYNC
* Merges rows modified of dsSource into dsDest. Use "merge" method of DataTable of jsDataSet
* @param {DataSet} dsDest
* @param {DataSet} dsSource
* @param {boolean} changesCommittedToDB
*/
getDataUtils.mergeDataSetChanges = function (dsDest, dsSource, changesCommittedToDB) {
_.forEach(dsSource.tables,
function(tSource) {
// se il mio dsTarget contiene la tabella allora effettuo merge delle righe
if (dsDest.tables[tSource.name]) {
// Questo non basta, vedi righe successive. dsDest.tables[tSource.name].merge(tSource);
// ciclo sulle righe originali del dest attraverso un contatore. ragiono al livello posizionale.
// 1. se riga è modified faccio merge. i 2 indici source e dest allineati
// 2. se riga è added inserisco riga corrispondente, aumento gli indici
// 3. deleted . faccio acceptChanges() così la riga viene detachata, rimango fermo sugli indici. solo se la transazione è ok
// recupero tabella di destinazione
let tDest = dsDest.tables[tSource.name];
// Indice delle righe del source, và con l'indice del dest cioè quello di partenza, ma se la riga del source è deleted non viene aumentato
// poichè il js nelle iterazioni successive deve copiare per le mod e add quella con lo stesso indice.
// var rSourceIndex = 0; // NON SERVE, tengo solo l'indicedella dest.
let rDestIndex = 0;
try {
for(rDestIndex; rDestIndex < tDest.rows.length;) {
// ottengo la i-esima riga dest. a seconda dello stato effettuo operazioni,
let rowDest = tDest.rows[rDestIndex];
let currState = rowDest.getRow().state;
if (currState === dataRowState.unchanged){
// non fai nulla nel caso unchanged
rDestIndex++;
continue;
}
if (currState === dataRowState.modified){
// 1. se riga è modified faccio merge. i 2 indici source e dest allineati
rowDest.getRow().makeSameAs(tSource.rows[rDestIndex].getRow());
// aumento contatore delle righe del source
rDestIndex++;
continue;
}
if (currState === dataRowState.added){
// 2. se riga è added inserisco riga corrispondente, aumento gli indici
rowDest.getRow().makeSameAs(tSource.rows[rDestIndex].getRow());
// aumento contatore delle righe del source
rDestIndex++;
continue;
}
if (currState === dataRowState.deleted){
// potrei aver preso degli errori e quindi il commit non è stato fatto, dovrò aumentare il contatore senza cancellare la riga
if (changesCommittedToDB) {
// NON aumento contatore delle righe del source! poichè era deleted, quindi sul source non la trovo
// poichè il server avrà fatto acceptChanges()
// qui io voglio che diventi detached e quindi a sua volta eseguo acceptChanges() sulla riga. Verrà tolto il metodo getRow()
rowDest.getRow().acceptChanges();
continue;
} else{
rDestIndex++;
continue;
}
}
}
} catch (e){
logger.log(logType.ERROR, "Dataset disallineati dopo il salvataggio " + e.message);
}
} else {
logger.log(logType.ERROR, "La tabella " + tSource.name + " non esiste sul dataset " + dsDest.name);
}
});
};
/**
* @function mergeRowsIntoTable
* @public
* @description SYNC
* Merges given "rows" in a specified table "tDest"
* @param {DataTable} tDest
* @param {ObjectRow[]} rows
* @param {boolean} checkExistence
*/
getDataUtils.mergeRowsIntoTable = function(tDest, rows, checkExistence) {
_.forEach(rows,
function(r) {
if (!checkExistence) {
tDest.add({}).makeSameAs(r.getRow());
return true;
}
let oldRow = tDest.existingRow(r);
if (oldRow) {
oldRow.getRow().makeSameAs(r.getRow());
} else {
tDest.add({}).makeSameAs(r.getRow());
}
return true;
});
};
/**
* @method getAutoChildRelation
* @private
* @description SYNC
* Gets a relation that connects a table with its self. Should be the same as AutoParent
* @param {DataTable} dt
* @returns {DataRelation} the auto child relation
*/
getDataUtils.getAutoChildRelation = function (dt) {
let autoChildRel = null;
if (!dt) return null;
_.forEach(dt.childRelations(), function (rel) {
if (rel.parentTable === dt.name && rel.childTable === dt.name) {
autoChildRel = rel;
// ho trovato la rel esco dal ciclo for
return false;
}
});
return autoChildRel;
};
/**
* @method getAutoParentRelation
* @private
* @description SYNC
* Gets a relation that connects a table with its self
* @param {DataTable} dt
* @returns {DataRelation} the auto parent relation
*/
getDataUtils.getAutoParentRelation = function (dt) {
let autoParentRel = null;
if (!dt) return null;
_.forEach(dt.parentRelations(), function (rel) {
if (rel.parentTable === dt.name) {
autoParentRel = rel;
// ho trovato la rel esco dal ciclo for
return false;
}
});
return autoParentRel;
};
/**
* @method containsNull
* @public
* @description SYNC
* Returns true if there is a null value or "", for some value in row on the columns cols
* @param {ObjectRow} row
* @param {DataColumn[]} cols
* @returns {boolean} true or false depending if there are null values on row in cols
*/
getDataUtils.containsNull = function (row, cols) {
return _.some(cols, function (c) {
return row[c.name] === null || row[c.name] === "";
});
};
/**
* @method compareRows
* @private
* @description SYNC
* Returns true if it is the same row. It compares the columns field key
* @param {DataTable} table
* @param {ObjectRow} r1
* @param {ObjectRow} r2
* @returns {boolean} true if r1 and r2 are the same row
*/
getDataUtils.isSameRow = function (table, r1, r2) {
if (!r1 || !r2) return false;
return _.every(table.key(), function (k) {
return r1[k] === r2[k];
}); // torno true se non trovo val differenti sulla chiave
};
// Some AMD build optimizers like r.js check for condition patterns like the following:
//noinspection JSUnresolvedVariable
if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {
// Expose lodash to the global object when an AMD loader is present to avoid
// errors in cases where lodash is loaded by a script tag and not intended
// as an AMD module. See http://requirejs.org/docs/errors.html#mismatch for
// more details.
root.getDataUtils = getDataUtils;
// Define as an anonymous module so, through path mapping, it can be
// referenced as the "underscore" module.
//noinspection JSUnresolvedFunction
define(function () {
return getDataUtils;
});
}
// Check for `exports` after `define` in case a build optimizer adds an `exports` object.
else if (freeExports && freeModule) {
// Export for Node.js or RingoJS.
if (moduleExports) {
(freeModule.exports = getDataUtils).getDataUtils = getDataUtils;
}
// Export for Narwhal or Rhino -require.
else {
freeExports.getDataUtils = getDataUtils;
}
}
else {
// Export for a browser or Rhino.
if (root.appMeta){
root.appMeta.getDataUtils = getDataUtils;
}
else {
root.getDataUtils=getDataUtils;
}
}
}((typeof jsDataQuery === 'undefined') ? require('./jsDataQuery') : jsDataQuery,
(typeof appMeta === 'undefined') ? require('./Logger').logger : appMeta.logger,
(typeof appMeta === 'undefined') ? require('./Logger').logTypeEnum : appMeta.logTypeEnum,
(typeof jsDataSet === 'undefined') ? require('./jsDataSet') : jsDataSet,
(typeof _ === 'undefined') ? require('lodash') : _
));