define([
'core/js/api/utils/ArrayUtils',
'core/js/api/utils/StringUtils',
'entity/js/api/DataModel',
'entity/js/api/Entity',
'entity/js/api/Property',
'entity/js/api/PropertyType',
'entity/js/api/Relation',
'entity/js/api/RelationCardinality'
], function (
ArrayUtils,
StringUtils,
DataModel,
Entity,
Property,
PropertyType,
Relation,
RelationCardinality
) {
'use strict';
/**
* A utility object for entities.
*
* @exports entity.dt/js/api/EntityUtils
*/
var EntityUtils = function () {
AbcsLib.throwStaticClassError();
};
var INVALID_CHARS_REGEXP = /[^\w\d]+/g;
EntityUtils.replaceInvalidChars = function (name) {
//return name.replace(/[\s\|,'"<>=~^;!\[\]]/g, '_');//keep less restrictive case commented for now.
//we may be more restrictive then necessary, may be less restrictive for user prided ids
var replaced = name.replace(INVALID_CHARS_REGEXP, '_').replace(/^_+/g, '');//remove starting '_';
return replaced;
};
var ENTITY_RESERVED_WORDS = ['DESCRIBE'];
//taken from http://docs.oracle.com/cd/B10501_01/appdev.920/a42525/apb.htm#toc141
var RESERVED_WORDS = ['ACCESS', 'ELSE', 'MODIFY', 'START', 'ADD', 'EXCLUSIVE', 'NOAUDIT', 'SELECT', 'ALL',
'EXISTS', 'NOCOMPRESS', 'SESSION', 'ALTER', 'FILE', 'NOT', 'SET', 'AND', 'FLOAT', 'NOTFOUND',
'SHARE', 'ANY', 'FOR', 'NOWAIT', 'SIZE', 'ARRAYLEN', 'FROM', 'NULL', 'SMALLINT', 'AS',
'GRANT', 'NUMBER', 'SQLBUF', 'ASC', 'GROUP', 'OF', 'SUCCESSFUL', 'AUDIT', 'HAVING',
'OFFLINE', 'SYNONYM', 'BETWEEN', 'IDENTIFIED', 'ON', 'SYSDATE', 'BY', 'IMMEDIATE', 'ONLINE',
'TABLE', 'CHAR', 'IN', 'OPTION', 'THEN', 'CHECK', 'INCREMENT', 'OR', 'TO', 'CLUSTER',
'INDEX', 'ORDER', 'TRIGGER', 'COLUMN', 'INITIAL', 'PCTFREE', 'UID', 'COMMENT', 'INSERT',
'PRIOR', 'UNION', 'COMPRESS', 'INTEGER', 'PRIVILEGES', 'UNIQUE', 'CONNECT', 'INTERSECT',
'PUBLIC', 'UPDATE', 'CREATE', 'INTO', 'RAW', 'USER', 'CURRENT', 'IS', 'RENAME', 'VALIDATE',
'DATE', 'LEVEL', 'RESOURCE', 'VALUES', 'DECIMAL', 'LIKE', 'REVOKE', 'VARCHAR', 'DEFAULT',
'LOCK', 'ROW', 'VARCHAR2', 'DELETE', 'LONG', 'ROWID', 'VIEW', 'DESC', 'MAXEXTENTS',
'ROWLABEL', 'WHENEVER', 'DISTINCT', 'MINUS', 'ROWNUM', 'WHERE', 'DROP', 'MODE', 'ROWS',
'WITH', 'ADMIN', 'CURSOR', 'FOUND', 'MOUNT', 'AFTER', 'CYCLE', 'FUNCTION', 'NEXT',
'ALLOCATE', 'DATABASE', 'GO', 'NEW', 'ANALYZE', 'DATAFILE', 'GOTO', 'NOARCHIVELOG',
'ARCHIVE', 'DBA', 'GROUPS', 'NOCACHE', 'ARCHIVELOG', 'DEC', 'INCLUDING', 'NOCYCLE',
'AUTHORIZATION', 'DECLARE', 'INDICATOR', 'NOMAXVALUE', 'AVG', 'DISABLE', 'INITRANS',
'NOMINVALUE', 'BACKUP', 'DISMOUNT', 'INSTANCE', 'NONE', 'BEGIN', 'DOUBLE', 'INT', 'NOORDER',
'BECOME', 'DUMP', 'KEY', 'NORESETLOGS', 'BEFORE', 'EACH', 'LANGUAGE', 'NORMAL', 'BLOCK',
'ENABLE', 'LAYER', 'NOSORT', 'BODY', 'END', 'LINK', 'NUMERIC', 'CACHE', 'ESCAPE', 'LISTS',
'OFF', 'CANCEL', 'EVENTS', 'LOGFILE', 'OLD', 'CASCADE', 'EXCEPT', 'MANAGE', 'ONLY',
'CHANGE', 'EXCEPTIONS', 'MANUAL', 'OPEN', 'CHARACTER', 'EXEC', 'MAX', 'OPTIMAL',
'CHECKPOINT', 'EXPLAIN', 'MAXDATAFILES', 'OWN', 'CLOSE', 'EXECUTE', 'MAXINSTANCES',
'PACKAGE', 'COBOL', 'EXTENT', 'MAXLOGFILES', 'PARALLEL', 'COMMIT', 'EXTERNALLY',
'MAXLOGHISTORY', 'PCTINCREASE', 'COMPILE', 'FETCH', 'MAXLOGMEMBERS', 'PCTUSED', 'CONSTRAINT',
'FLUSH', 'MAXTRANS', 'PLAN', 'CONSTRAINTS', 'FREELIST', 'MAXVALUE', 'PLI', 'CONTENTS',
'FREELISTS', 'MIN', 'PRECISION', 'CONTINUE', 'FORCE', 'MINEXTENTS', 'PRIMARY', 'CONTROLFILE',
'FOREIGN', 'MINVALUE', 'PRIVATE', 'COUNT', 'FORTRAN', 'MODULE', 'PROCEDURE', 'PROFILE',
'SAVEPOINT', 'SQLSTATE', 'TRACING', 'QUOTA', 'SCHEMA', 'STATEMENT_ID', 'TRANSACTION', 'READ',
'SCN', 'STATISTICS', 'TRIGGERS', 'REAL', 'SECTION', 'STOP', 'TRUNCATE', 'RECOVER', 'SEGMENT',
'STORAGE', 'UNDER', 'REFERENCES', 'SEQUENCE', 'SUM', 'UNLIMITED', 'REFERENCING', 'SHARED',
'SWITCH', 'UNTIL', 'RESETLOGS', 'SNAPSHOT', 'SYSTEM', 'USE', 'RESTRICTED', 'SOME', 'TABLES',
'USING', 'REUSE', 'SORT', 'TABLESPACE', 'WHEN', 'ROLE', 'SQL', 'TEMPORARY', 'WRITE', 'ROLES',
'SQLCODE', 'THREAD', 'WORK', 'ROLLBACK', 'SQLERROR', 'TIME', 'ABORT', 'BETWEEN', 'CRASH',
'DIGITS', 'ACCEPT', 'BINARY_INTEGER', 'CREATE', 'DISPOSE', 'ACCESS', 'BODY', 'CURRENT',
'DISTINCT', 'ADD', 'BOOLEAN', 'CURRVAL', 'DO', 'ALL', 'BY', 'CURSOR', 'DROP', 'ALTER', 'CASE',
'DATABASE', 'ELSE', 'AND', 'CHAR', 'DATA_BASE', 'ELSIF', 'ANY', 'CHAR_BASE', 'DATE', 'END',
'ARRAY', 'CHECK', 'DBA', 'ENTRY', 'ARRAYLEN', 'CLOSE', 'DEBUGOFF', 'EXCEPTION', 'AS',
'CLUSTER', 'DEBUGON', 'EXCEPTION_INIT', 'ASC', 'CLUSTERS', 'DECLARE', 'EXISTS', 'ASSERT',
'COLAUTH', 'DECIMAL', 'EXIT', 'ASSIGN', 'COLUMNS', 'DEFAULT', 'FALSE', 'AT', 'COMMIT',
'DEFINITION', 'FETCH', 'AUTHORIZATION', 'COMPRESS', 'DELAY', 'FLOAT', 'AVG', 'CONNECT',
'DELETE', 'FOR', 'BASE_TABLE', 'CONSTANT', 'DELTA', 'FORM', 'BEGIN', 'COUNT', 'DESC', 'FROM',
'FUNCTION', 'NEW', 'RELEASE', 'SUM', 'GENERIC', 'NEXTVAL', 'REMR', 'TABAUTH', 'GOTO',
'NOCOMPRESS', 'RENAME', 'TABLE', 'GRANT', 'NOT', 'RESOURCE', 'TABLES', 'GROUP', 'NULL',
'RETURN', 'TASK', 'HAVING', 'NUMBER', 'REVERSE', 'TERMINATE', 'IDENTIFIED', 'NUMBER_BASE',
'REVOKE', 'THEN', 'IF', 'OF', 'ROLLBACK', 'TO', 'IN', 'ON', 'ROWID', 'TRUE', 'INDEX', 'OPEN',
'ROWLABEL', 'TYPE', 'INDEXES', 'OPTION', 'ROWNUM', 'UNION', 'INDICATOR', 'OR', 'ROWTYPE',
'UNIQUE', 'INSERT', 'ORDER', 'RUN', 'UPDATE', 'INTEGER', 'OTHERS', 'SAVEPOINT', 'USE',
'INTERSECT', 'OUT', 'SCHEMA', 'VALUES', 'INTO', 'PACKAGE', 'SELECT', 'VARCHAR', 'IS',
'PARTITION', 'SEPARATE', 'VARCHAR2', 'LEVEL', 'PCTFREE', 'SET', 'VARIANCE', 'LIKE',
'POSITIVE', 'SIZE', 'VIEW', 'LIMITED', 'PRAGMA', 'SMALLINT', 'VIEWS', 'LOOP', 'PRIOR',
'SPACE', 'WHEN', 'MAX', 'PRIVATE', 'SQL', 'WHERE', 'MIN', 'PROCEDURE', 'SQLCODE', 'WHILE',
'MINUS', 'PUBLIC', 'SQLERRM', 'WITH', 'MLSLABEL', 'RAISE', 'START', 'WORK', 'MOD', 'RANGE',
'STATEMENT', 'XOR', 'MODE', 'REAL', 'STDDEV', 'NATURAL', 'RECORD', 'SUBTYPE', 'SHAREDRT', 'SAMPLE'];
var normalizeTypeFor = function(typeFor) {
if (!typeFor) {
// for safety if typeFor unknown assume Entity as it is smallest
typeFor = Entity;
}
if (!(typeFor === Entity || typeFor === Property || typeFor === Relation)) {
throw new Error('typeFor must be Entity, Property or Relation');
}
return typeFor;
};
EntityUtils.replaceReservedWord = function (text) {
if (EntityUtils.isReservedWord(text)) {
return text + '_';
}
return text;
};
/**
* Tests whether the given text is a reserved word.
* @param {String} text to check
* @param {Object} typeFor the type of identifier the text is for
* (entity/js/api/Entity or entity/js/api/Property)
* @return {Boolean} true if the text is a reserved word.
*/
EntityUtils.isReservedWord = function (text, typeFor) {
typeFor = normalizeTypeFor(typeFor);
var upper = text.toUpperCase();
if (ArrayUtils.contains(RESERVED_WORDS, upper)) {
return true;
}
return typeFor === Entity
&& ArrayUtils.contains(ENTITY_RESERVED_WORDS, upper);
};
/**
* Construct a valid id based on the text passed in, optionally
* using camelCase.
* @param {String} text The string to base the id on
* @param {String} camelCase, optionally pass in StringUtils.CAMELCASE_FIRST_UPPER
* if an id using CamelCase formatting is required or StringUtils.CAMELCASE_FIRST_LOWER
* for camelCase.
* @param {Object} typeFor the type the id is for (entity/js/api/Entity or
* entity/js/api/Property)
* @returns {String} A valid ID string.
*/
EntityUtils._makeValidId = function (text, camelCase, typeFor) {
var id = EntityUtils.replaceInvalidChars(text);
if (camelCase) {
id = StringUtils.toCamelCase(id.replace(/_/g,' '), camelCase);
}
return EntityUtils.replaceReservedWord(id, typeFor);
};
/**
* Checks whether the given id is a reserved word or contains illegal
* characters. It does not to a length or exists check!
* @param {type} id the id to check
* @param {type} typeFor the type the id is for (entity/js/api/Entity or
* entity/js/api/Property)
* @returns {Boolean} false if the id is not valid, true otherwise
*/
EntityUtils.isValidId = function (id, typeFor) {
//check invalid characters
if (INVALID_CHARS_REGEXP.test(id)) {
return false;
}
return !EntityUtils.isReservedWord(id, typeFor);
};
/**
* Derive a reasonable and unique entity ID from the given entity displayName.
*
* @param {string} displayName - the entity display name or suggested id
* @param {Boolean} useCamelCase, optionally pass in true if an id using
* CamelCase formatting is required.
* @returns {string}
*/
EntityUtils.generateEntityId = function (displayName, useCamelCase) {
AbcsLib.checkDefined(displayName, 'displayName');
var camelCase = useCamelCase ? StringUtils.CAMELCASE_FIRST_UPPER : undefined;
var clear = EntityUtils._makeValidId(displayName, camelCase, Entity);
if (!clear || clear.length < 2 || !(/^[a-zA-Z]{2}/.test(clear))) {
clear = EntityUtils._makeValidId('bo_' + displayName, camelCase, Entity);
} else if (clear.indexOf('breeze2_') === 0) {
clear = clear.replace(/^breeze2_/g, 'breeze_2_');//let 'breeze2_' be special prefix
}
//now check if such entity exist and if so add a number postfix and try again until lucky
var count = 2;
var gen = EntityUtils._shortenToMaxLength(clear);
while (DataModel.getInstance().getEntities().entityExists(gen)) {
var suffix = '_' + count;
count++;
gen = EntityUtils._shortenToMaxLength(clear, suffix);
}
return gen;
};
/**
* Tests whether an identifier for the given type is a valid length
* @param {String} id the id to test
* @param {Object} typeFor the type of object the identifier is for
* (entity/js/api/Entity or entity/js/api/Property)
* @return {int} the maximum length of an identifier
*/
EntityUtils.isValidIdLength = function(id, typeFor) {
return id.length <= EntityUtils._getIdMaxLength(typeFor);
};
EntityUtils._getIdMaxLength = function(typeFor) {
typeFor = normalizeTypeFor(typeFor);
// database limitation of table/column names
var max = 30;
if (typeFor === Entity) {
// See BUFP-2609 - need to take into account '_PK_SEQ'
max -= 7;
} else if (typeFor === Relation) { // for the accessors
max = 1000; // no real limit
}
return max;
};
/**
* Will truncate the given name+suffix to the maximum length for the given
* type. The suffix, if given, will always be
* appended, so any truncation of the text will be from name.
* @param {String} name The name to be truncated
* @param {String} suffix Suffix to include at the end of the name if given.
* @param {Object} typeFor the type the id is for (entity/js/api/Entity or
* entity/js/api/Property)
* @returns {String} shortened string with suffix
*/
EntityUtils._shortenToMaxLength = function (name, suffix, typeFor) {
var suffLen = suffix ? suffix.length : 0;
var max = EntityUtils._getIdMaxLength(typeFor) - suffLen; /** for the dash and the unique numeric id **/
if (name.length > max) {
name = name.substring(0, max);
}
if (suffix) {
return name + suffix;
} else {
return name;
}
};
/**
* Derive a reasonable and unique entity property ID from the given property displayName.
*
* @param {string} displayName - the property display name
* @param {Entity} entity - the entity the property belongs to (optional, if
* present the id is ensured to be unique).
* @param {Array} usedIdsExtra extra ids already reserved (optional)
* @param {Boolean} camelCase, optionally pass in true if an id using
* camelCase formatting is required.
* @param {boolean} accessorsInExtras if true, foreign relations are not rebuilt
* to get all accessors of the entity, and the ids of thoses accessors are assumed to be
* containsed within the extras array
* @returns {string}
*/
EntityUtils.generatePropertyId = function (displayName, entity, usedIdsExtra, camelCase, accessorsInExtras) {
AbcsLib.checkDefined(displayName, 'displayName');
var id = this.formatPropertyId(displayName, entity, camelCase);
return EntityUtils._generateUniquePropertyOrAccessorId(id, entity, usedIdsExtra, Property, accessorsInExtras);
};
/**
* Generates and sets a default Accessor ID(s) on the given Relation.
* Accessors are used by RAMP for traversal of relations so this is only applicable
* to Relations of Custom BOs.
* @param {Relation} relation The Relation on which the default Accessor ID
* (and/or reverse Accessor ID) should be set.
*/
EntityUtils.setDefaultAccessors = function (relation) {
AbcsLib.checkDataType(relation, Relation);
if (relation.setAccessorID && relation.setReverseAccessorID) {
var sourceEntity = relation.getSourceEntity();
var targetEntity = relation.getTargetEntity();
if (sourceEntity && sourceEntity.isInternal() && targetEntity && targetEntity.isInternal()) {
var accessorID = EntityUtils._getDefaultAccessorName(relation, true);
relation.setAccessorID(EntityUtils._generateUniquePropertyOrAccessorId(accessorID, sourceEntity, undefined, Relation));
if (relation.isChildRelationship()) {
var reverseAccessorID = EntityUtils._getDefaultAccessorName(relation, false);
// namespace of reverse accessor needs to be within properties and
// accessors of the target entity
relation.setReverseAccessorID(EntityUtils._generateUniquePropertyOrAccessorId(reverseAccessorID, targetEntity, undefined, Relation));
}
}
}
};
EntityUtils._getDefaultAccessorName = function(relation, bForward) {
// logic is copied from DefinitionBuilder default accessor generation
var manySUFFIX = 'Collection';
var oneSUFFIX = 'Object';
var accName = null;
var card = relation.getCardinality();
if (!card) {
card = RelationCardinality.MANY_TO_ONE;
}
var endIsMany = card === RelationCardinality.MANY_TO_MANY ||
(bForward && card === RelationCardinality.ONE_TO_MANY) ||
(!bForward && card === RelationCardinality.MANY_TO_ONE);
if (endIsMany) {
var entityID = (bForward ? relation.getTargetEntityID() : relation.getSourceEntityID());
if (!relation.isChildRelationship()) {
accName = entityID + '_' + relation.getMappingPropertyID() + manySUFFIX;
} else {
accName = entityID + manySUFFIX;
}
} else {
// TODO - As part of BUFP-11854 (and after BUFP-11852 is done),
// the following hard coded refId + Object should change to:
// String accessorID = relation.getMappingPropertyID();
// if (accessorID.startsWith("ref2")) {
// accessorID = accessorID.substring(4);
// }
accName = bForward ?
relation.getMappingPropertyID() + oneSUFFIX :
relation.getSourceEntityID() + oneSUFFIX;
}
return accName;
};
/**
* Return IDs of all Accessors of the given Entity.
* Accessors are used by RAMP for traversal of relations so this is only applicable
* to Relations of Custom BOs.
* @param {Entity} entity The entiy owning the Accessors
* @returns {String[]} Returns an array (possibly of zero length) of IDs of
* all Accessors of the given Entity.
*/
EntityUtils.getAccessorIds = function(entity) {
var ids = [];
if (entity) {
AbcsLib.checkDataType(entity, Entity);
entity.getRelations().forEach(function(rel) { // intentionally gets owned and foreign rels
var accId = rel.getAccessorID();
if (accId) {
ids.push(accId);
}
});
}
return ids;
};
/**
* The ids of Properties and Accessors (used by RAMP for traversal of relations)
* need to be in the same namespace as both appear at the same level in the
* REST response. This function will find a unique id, based on the id passed in
* with some possible numeric suffix and trunctaion of length if necessary, within
* the existing Property IDs, Accessor IDs and any other strings passed in as "extras".
* The check for uniqueness will be case INSENSITIVE.
*
* @param {String} id The base value of the ID which will be used in generating
* a unique id if not already unique.
* @param {Entity} entity The Entity from which existing Property ID and Accessor
* IDs will be tested
* @param {String[]} extras an array of additional string to test against
* @param {Object} typeFor indicator of the type of object the id is for
* @param {boolean} accessorsInExtras if true, foreign relations are not rebuilt
* to get all accessors of the entity, and the ids of thoses accessors are assumed to be
* containsed within the extras array
* @returns {String} A unique ID suitable for use as a Property or Accessor
* ID for the given Entity.
*/
EntityUtils._generateUniquePropertyOrAccessorId = function (id, entity, extras, typeFor, accessorsInExtras) {
var existingIDs = {};
if (entity) {
entity.getProperties().forEach(function(prop){
existingIDs[prop.getId().toUpperCase()] = true;
});
if (!accessorsInExtras) {
EntityUtils.getAccessorIds(entity).forEach(function(id){
existingIDs[id.toUpperCase()] = true;
});
}
}
if (extras && extras.length) {
extras.forEach(function(id) {
existingIDs[id.toUpperCase()] = true;
});
}
//now check if such id exists and if so add a number postfix and try again until lucky
var count = 2;//if you have an object "MyObject" it seems reasonable to have second one marked as "MyObject_2"
var gen = EntityUtils._shortenToMaxLength(id, undefined, typeFor);
while (existingIDs[gen.toUpperCase()]) {
var suffix = '_' + count;
count++;
gen = EntityUtils._shortenToMaxLength(id, suffix, typeFor);
}
return gen;
};
/**
* Formats given property display name into a format Breeze understands.
*
* @param {string} displayName - the property display name
* @param {Entity} [entity] - the entity the property belongs to
* @param {Boolean} useCamelCase, optionally pass in true if an id using
* camelCase formatting is required.
* @returns {string}
*/
EntityUtils.formatPropertyId = function (displayName, entity, useCamelCase) {
AbcsLib.checkDefined(displayName, 'displayName');
var camelCase = useCamelCase ? StringUtils.CAMELCASE_FIRST_LOWER : undefined;
var clear = EntityUtils._makeValidId(displayName, camelCase, Property);
if (clear === '_' || !clear) {
//sometimes there are only special characters or empty name, it's more frienly to have:
clear = 'pid';
}
if (clear.length < 2 || !(/^[a-zA-Z]{2}/.test(clear))) {
if (camelCase) {
clear = EntityUtils._makeValidId('pid ' + displayName, camelCase, Property);
} else {
clear = 'pid' + clear;
}
}
if (entity && entity._isExtensionEntity()) {
// prefix extEntity properties for easier identification;
// this is needed to be done for extEntity primary key property
// but for all other properties this can be removed if desirable;
// prefixing makes them easier to identify
clear = 'ext_' + clear;
}
return clear;
};
/**
* Seeks for the existing relation between the parent's and child entity if
* there is any. Searches for a relation connecting this entity and the
* view's parent entity that has cardinality many-to-one (i.e. may be
* displayed as a table)
*
* @param {Entity} parentEntity parent's entity to seek the relation to
* @param {Entity} childEntity child's entity to seek for its relation
* @returns {Relation|false} relation if found, false otherwise
*/
EntityUtils.findParentChildRelation = function (parentEntity, childEntity) {
AbcsLib.checkDefined(parentEntity, 'parentEntity');
AbcsLib.checkDefined(childEntity, 'childEntity');
var parentRelation;
var rels = childEntity.getOwnRelations();
$.each(rels, function (index, rel) {
if (rel.getTargetEntityID() === parentEntity.getId()
&& rel.isChildRelationship() // find the correct parent/child relation
&& rel.getCardinality() === RelationCardinality.MANY_TO_ONE) {
parentRelation = rel;
return false;
}
});
return parentRelation;
};
/**
* Gets the specific display type for the given property type. Some types
* are displayed to the user slightly differently than the
* property.getType() would suggest,
* e.g. 'lookup' and 'reference' both share PropertyType.REFERENCE and must
* be differentiated by the target entity of their relation.
*
* @param {Entity} entity the entity
* @param {Entity} property the property to determine which type
* @returns {String} the display type of the property
*/
EntityUtils.getDisplayType = function (entity, property) {
var type = property.getType();
if (type === PropertyType.REFERENCE) {
var relation = entity.getRelation(property);
var target = relation && relation.getTargetEntity();
if (target && target.isLookupEntity()) {
return 'LOOKUP';
}
}
return type;
};
/**
* Gets the PropertyType to set as the taragetPropertyType for a Relation
* targetted at the given Entity.
* @param {type} entity The target Entity
* @returns {PropertyType} The PropertyType to use. undefined is expected
* for any Relation where the generic PropertyType.REFERENCE is to be used.
* This will cover numeric keys. However if the id of the target entity
* is not numeric (or not known), a different PropertyType (typically
* PropertyType.TEXT) will be returned.
*/
EntityUtils.getPropertyTypeForRelationTarget = function (entity) {
var propType; // Default value of undefined maps to PropertyType.REFERENCE
if (!entity.isInternal()) {
// For external entities, ServicesLoader holds a cache of the
// original parsed service.json which for an entity optionally
// includes a primaryKey indicator. if this is set we can use it
// to determine the type of the target property. Otherwise, we use
// TEXT as this will cover all cases
propType = PropertyType.TEXT;
var entities = EntityUtils.getEntitiesInTheSameProvider(entity);
for (var i = 0; i < entities.length; i++) {
var extEnt = entities[i];
if (extEnt.id === entity.getAttribute('internalId')) {
if (extEnt.primaryKey) {
// Primary key Proerty defined against the entity
// so we can use this to determine the type
var pk = entity.getProperty(extEnt.primaryKey);
if (pk.getType() === PropertyType.NUMBER) {
propType = undefined;
}
}
break;
}
}
}
return propType;
};
/**
* Gets the default mapping property id for a reference to the given target.
* @param {String} target target entity or entity id.
* @returns {String} default mapping property id
*/
EntityUtils.mappingPropertyIdFor = function(target) {
if (target && target.getId) {
target = target.getId();
}
return 'ref2' + target;
};
/**
* Returns all the entities in the same provider as the given entity
* @param {Entity} entity the context entity
* @returns {Array} an array of entities in this current provider, or an
* empty array if the provider could not be found
*/
EntityUtils.getEntitiesInTheSameProvider = function (entity) {
if (entity) {
AbcsLib.checkDataType(entity, Entity);
var entityProvider = entity.getEntityProvider();
if (entityProvider) {
return entityProvider.getEntities();
}
}
return [];
};
return EntityUtils;
});