@sap/odata-v4
Version:
OData V4.0 server library
858 lines (718 loc) • 26.1 kB
JavaScript
'use strict';
const KeyFactory = require('./KeyFactory');
const FullQualifiedName = require('../FullQualifiedName');
const IllegalCallError = require('../errors/IllegalCallError');
/**
* Adds a function to an array which is container in the given map 'map' under the key 'key'
* If the key is not in the map the map entries will be created
*
* @param {Map.<string, Array>} map Map
* @param {string} key Key
* @param {EdmFunction} ffunction Function to be added to the array in the map
* @private
*/
function addToMapArray(map, key, ffunction) {
let arr = map.get(key);
if (!arr) {
map.set(key, [ffunction]);
} else {
arr.push(ffunction);
}
}
/**
* The entity data model.
*/
class Edm {
constructor() {
/**
* @type {Map.<string, EdmReference>}
* @private
*/
this._references = null;
/**
* @type {Map.<string,EdmSchema>}
* @private
*/
this._schemas = null;
/**
* @type {EdmSchema[]}
* @private
*/
this._schemaList = null;
/**
* @type {Map.<?string,EdmEntityContainer>}
* @private
*/
this._entityContainers = new Map();
/**
* @type {Map.<string, EdmEnumType>}
* @private
*/
this._enumTypes = new Map();
/**
* @type {Map.<string,EdmTypeDefinition>}
* @private
*/
this._typeDefinitions = new Map();
/**
* @type {Map.<string,EdmEntityType>}
* @private
*/
this._entityTypes = new Map();
/**
* @type {Map.<string,EdmComplexType>}
* @private
*/
this._complexTypes = new Map();
/**
* @type {Map.<string,EdmAction>}
* @private
*/
this._boundActions = new Map();
/**
* @type {Map.<string,EdmAction>}
* @private
*/
this._unboundActions = new Map();
/**
* @type {Map.<string,EdmFunction[]>}
* @private
*/
this._boundFunctionsByName = new Map();
/**
* @type {Map.<string,EdmFunction>}
* @private
*/
this._boundFunctionsByKey = new Map();
/**
* @type {Map.<string,EdmFunction[]>}
* @private
*/
this._unboundFunctionsByName = new Map();
/**
* @type {Map.<string,EdmFunction>}
* @private
*/
this._unboundFunctionByKey = new Map();
/**
* @type {Map.<string,string>}
* @private
*/
this._aliasToNamespaceInfo = null;
/**
* @type {Map.<string,EdmTerm>}
* @private
*/
this._terms = new Map();
}
/**
* Return the references.
*
* @returns {Map.<string, EdmReference>}
*/
getReferences() {
if (!this._references) {
this.initReferences();
}
return this._references;
}
/**
* Return the schemas
*
* @returns {EdmSchema[]}
*/
getSchemas() {
if (!this._schemaList) {
this._initSchemas();
}
return this._schemaList;
}
/**
* Returns the schema given by namespace or alias, or null if schema is not found
*
* @param {string} namespace Namespace or alias for namespace
* @returns {?EdmSchema}
*/
getSchema(namespace) {
let schema;
if (!this._schemas) {
this._initSchemas();
}
schema = this._schemas.get(namespace);
if (!schema) {
schema = this._schemas.get(this._aliasToNamespaceInfo.get(namespace));
}
return schema;
}
/**
* Initialize the {@link Edm#_schemas} map and {@link Edm#_schemaList} array
* @private
*/
_initSchemas() {
this.loadAliasToNamespaceInfo();
this._schemas = this.createSchemas();
this._schemaList = Array.from(this._schemas.values());
}
/**
* Fill the map containing the references
*/
initReferences() {
this._references = this.createReferences();
}
/**
* Fill the map which links aliases to namespaces
*/
loadAliasToNamespaceInfo() {
this._aliasToNamespaceInfo = this.createAliasToNamespaceInfo();
}
/**
* Return the entity container described via namespace or alias and name.
* If namespaceOrAliasFQN is null the default entity container is returned.
* If no container is found null is returned.
*
* @override
* @param {FullQualifiedName} [namespaceOrAliasFQN] The namespace or alias and the name of the entity container to
* be loaded
* @returns {?EdmEntityContainer}
*/
getEntityContainer(namespaceOrAliasFQN) {
const fqn = this.resolvePossibleAlias(namespaceOrAliasFQN);
const fqnString = FullQualifiedName.getFQNAsString(fqn);
let container = this._entityContainers.get(fqnString);
if (container) {
return container;
}
container = this.createEntityContainer(fqn);
if (container) {
// fqnString may null if default container was requested
this.cacheEntityContainer(fqn, container);
if (fqn === null) {
// if default container was requested store container also under his real name
this.cacheEntityContainer(container.getFullQualifiedName(), container);
} else {
// we don't know if the by fqn requested container is the default container so
// we look if the map contains already a default container
let defaultContainer = this._entityContainers.get(null);
if (!defaultContainer) {
// if no default container is in the map add the default container
// we ask the provider for the default container and
defaultContainer = this.createEntityContainer(null);
// if the default container is the currently requested container
if (FullQualifiedName.equals(fqn, defaultContainer.getFullQualifiedName())) {
// add the current container as default container
this.cacheEntityContainer(null, container);
} else {
// add the default container with "null" and with his fqn
this.cacheEntityContainer(null, defaultContainer);
this.cacheEntityContainer(defaultContainer.getFullQualifiedName(),
defaultContainer);
}
}
}
}
return container;
}
/**
* Return the enumeration type described via namespace or alias and name, or null if not found
*
* @override
* @param {FullQualifiedName} namespaceOrAliasFQN Namespace or alias and name
* @returns {?EdmEnumType}
*/
getEnumType(namespaceOrAliasFQN) {
const fqn = this.resolvePossibleAlias(namespaceOrAliasFQN);
const fqnString = FullQualifiedName.getFQNAsString(fqn);
let enumType = this._enumTypes.get(fqnString);
if (enumType) return enumType;
enumType = this.createEnumType(fqn);
if (enumType) this._enumTypes.set(fqnString, enumType);
return enumType;
}
/**
* Return the type definition described via namespace or alias and name, or null if not found
*
* @override
* @param {FullQualifiedName} namespaceOrAliasFQN Namespace or alias and name
* @returns {?EdmTypeDefinition}
*/
getTypeDefinition(namespaceOrAliasFQN) {
const fqn = this.resolvePossibleAlias(namespaceOrAliasFQN);
const fqnString = FullQualifiedName.getFQNAsString(fqn);
let typeDefinition = this._typeDefinitions.get(fqnString);
if (typeDefinition) {
return typeDefinition;
}
typeDefinition = this.createTypeDefinition(fqn);
if (typeDefinition) {
this._typeDefinitions.set(fqnString, typeDefinition);
}
return typeDefinition;
}
/**
* Return the entity type described via namespace or alias and name, or null if not found
*
* @override
* @param {FullQualifiedName} namespaceOrAliasFQN Namespace or alias and name
* @returns {?EdmEntityType}
*/
getEntityType(namespaceOrAliasFQN) {
const fqn = this.resolvePossibleAlias(namespaceOrAliasFQN);
const fqnString = FullQualifiedName.getFQNAsString(fqn);
let entityType = this._entityTypes.get(fqnString);
if (entityType) {
return entityType;
}
entityType = this.createEntityType(fqn);
if (entityType) {
this._entityTypes.set(fqnString, entityType);
}
return entityType;
}
/**
* Return the complex type described via namespace or alias and name, or null if not found
*
* @override
* @param {FullQualifiedName} namespaceOrAliasFQN Namespace or alias and name
* @returns {?EdmComplexType}
*/
getComplexType(namespaceOrAliasFQN) {
const fqn = this.resolvePossibleAlias(namespaceOrAliasFQN);
const fqnString = FullQualifiedName.getFQNAsString(fqn);
let complexType = this._complexTypes.get(fqnString);
if (complexType) {
return complexType;
}
complexType = this.createComplexType(fqn);
if (complexType) {
this._complexTypes.set(fqnString, complexType);
}
return complexType;
}
/**
* Return the unbound action described via namespace or alias and name, or null if not found
*
* @override
* @param {FullQualifiedName} namespaceOrAliasFQN Namespace or alias and name
* @returns {?EdmAction}
*/
getUnboundAction(namespaceOrAliasFQN) {
const fqn = this.resolvePossibleAlias(namespaceOrAliasFQN);
const fqnString = FullQualifiedName.getFQNAsString(fqn);
let action = this._unboundActions.get(fqnString);
if (action) {
return action;
}
action = this.createUnboundAction(fqn);
if (action) {
this._unboundActions.set(fqnString, action);
}
return action;
}
/**
* Return the bound action described via namespace or alias, name and binding information,
* or null if not found
*
* @override
* @param {FullQualifiedName} namespaceOrAliasFQN Namespace or alias and name
* @param {FullQualifiedName} bindingParameterTypeName Name of the binding parameter
* @param {boolean} isBindingParameterCollection True if the binding parameter is a collection, otherwise false
* @returns {?EdmAction}
*/
getBoundAction(namespaceOrAliasFQN, bindingParameterTypeName, isBindingParameterCollection) {
const actionFqn = this.resolvePossibleAlias(namespaceOrAliasFQN);
const bindingParameterTypeFqn = this.resolvePossibleAlias(bindingParameterTypeName);
const key = KeyFactory.createActionKey(
actionFqn, bindingParameterTypeFqn, isBindingParameterCollection);
let action = this._boundActions.get(key);
if (action) {
return action;
}
action = this.createBoundAction(
actionFqn, bindingParameterTypeFqn, isBindingParameterCollection);
if (action) {
this._boundActions.set(key, action);
}
return action;
}
/**
* Return the unbound functions described via namespace or alias and name
*
* @override
* @param {FullQualifiedName} namespaceOrAliasFQN Namespace or alias and name
* @returns {EdmFunction[]}
*/
getUnboundFunctions(namespaceOrAliasFQN) {
const functionFqn = this.resolvePossibleAlias(namespaceOrAliasFQN);
const functionFqnString = FullQualifiedName.getFQNAsString(functionFqn);
let functions = this._unboundFunctionsByName.get(functionFqnString);
if (!functions) {
functions = this.createUnboundFunctions(functionFqn);
this._unboundFunctionsByName.set(functionFqnString, functions);
for (let unbound of functions) { // EdmFunction
const key = KeyFactory.createFunctionKey(
new FullQualifiedName(unbound.getNamespace(), unbound.getName()),
unbound.getBindingParameterTypeFqn(),
unbound.isBindingParameterTypeCollection(),
Array.from(unbound.getParameters().keys()));
this._unboundFunctionByKey.set(key, unbound);
}
}
return functions;
}
/**
* Return the bound functions described via namespace or alias and name
*
* @override
* @param {FullQualifiedName} namespaceOrAliasFQN Namespace or alias and name
* @returns {EdmFunction[]}
*/
getBoundFunctions(namespaceOrAliasFQN) {
const functionFqn = this.resolvePossibleAlias(namespaceOrAliasFQN);
const functionFqnString = FullQualifiedName.getFQNAsString(functionFqn);
let functions = this._boundFunctionsByName.get(functionFqnString);
if (!functions) {
functions = this.createBoundFunctions(functionFqn);
this._boundFunctionsByName.set(functionFqnString, functions);
for (let unbound of functions) { // EdmFunction
const key = KeyFactory.createFunctionKey(
new FullQualifiedName(unbound.getNamespace(), unbound.getName()),
unbound.getBindingParameterTypeFqn(),
unbound.isBindingParameterTypeCollection(),
Array.from(unbound.getParameters().keys()));
this._boundFunctionsByKey.set(key, unbound);
}
}
return functions;
}
/**
* Return the unbound function described via namespace or alias, name and parameter names,
* or null if not found
*
* @override
* @param {FullQualifiedName} functionName namespaceOrAliasFQN Namespace or alias and name
* @param {string[]} parameterNames Parameter names
* @returns {?EdmFunction}
*/
getUnboundFunction(functionName, parameterNames) {
const functionFqn = this.resolvePossibleAlias(functionName);
let key = KeyFactory.createFunctionKey(functionFqn, null, null, parameterNames);
let unboundFunction = this._unboundFunctionByKey.get(key);
if (!unboundFunction) {
unboundFunction = this.createUnboundFunction(functionFqn, parameterNames);
if (unboundFunction) {
this._unboundFunctionByKey.set(key, unboundFunction);
}
}
return unboundFunction;
}
/**
* Return the bound action described via namespace or alias, name, binding information and parameter names
*
* @override
* @param {FullQualifiedName} functionName namespaceOrAliasFQN Namespace or alias and name
* @param {FullQualifiedName} bindingParameterTypeName Binding parameter name
* @param {boolean} isBindingParameterCollection True if binding parameter is a collection, otherwise false
* @param {string[]} parameterNames Parameter names
* @returns {EdmFunction}
*/
getBoundFunction(functionName, bindingParameterTypeName,
isBindingParameterCollection, parameterNames) {
const functionFqn = this.resolvePossibleAlias(functionName);
const bindingParameterTypeFqn = this.resolvePossibleAlias(bindingParameterTypeName);
let key = KeyFactory.createFunctionKey(functionFqn, bindingParameterTypeFqn,
isBindingParameterCollection, parameterNames);
let ffunction = this._boundFunctionsByKey.get(key);
if (!ffunction) {
ffunction = this.createBoundFunction(functionFqn, bindingParameterTypeFqn,
isBindingParameterCollection, parameterNames);
if (ffunction != null) {
this._boundFunctionsByKey.set(key, ffunction);
}
}
return ffunction;
}
/**
* Return the term described via namespace or alias and name, or null if not found
*
* @override
* @param {FullQualifiedName} namespaceOrAliasFQN Namespace or alias and name
* @returns {?EdmTerm}
*/
getTerm(namespaceOrAliasFQN) {
const fqn = this.resolvePossibleAlias(namespaceOrAliasFQN);
const fqnString = FullQualifiedName.getFQNAsString(fqn);
let term = this._terms.get(fqnString);
if (term) {
return term;
}
term = this.createTerm(fqn);
if (term) {
this._terms.set(fqnString, term);
}
return term;
}
/**
* Resolves an alias of an namespace
* null may be returned if the input parameter is null
*
* @param {FullQualifiedName} namespaceOrAliasFQN Namespace or alias and name
* @returns {FullQualifiedName}
*/
resolvePossibleAlias(namespaceOrAliasFQN) {
if (!this._aliasToNamespaceInfo) {
this.loadAliasToNamespaceInfo();
}
let finalFQN = null; // FullQualifiedName
if (namespaceOrAliasFQN != null) {
const namespace = this._aliasToNamespaceInfo.get(namespaceOrAliasFQN.namespace);
// If not contained in info it must be a namespace
if (!namespace) {
finalFQN = namespaceOrAliasFQN;
} else {
finalFQN = new FullQualifiedName(namespace, namespaceOrAliasFQN.name);
}
}
return finalFQN;
}
/**
* Creates the schemas map
*
* @abstract
* @protected
* @returns {Map.<string, EdmSchema>}
*/
createSchemas() {
throw IllegalCallError.createForCallingAbstractMethod('createSchemas');
}
/**
* Creates the references list
*
* @abstract
* @protected
* @returns {Map.<string, EdmReference>}
*/
createReferences() {
throw IllegalCallError.createForCallingAbstractMethod('createReferences');
}
/**
* Creates a map to resolve aliases to namespaces
*
* @abstract
* @protected
* @returns {Map.<string, string>}
*/
createAliasToNamespaceInfo() {
throw IllegalCallError.createForCallingAbstractMethod('createAliasToNamespaceInfo');
}
/**
* Stores a alias/namespaces tuple
*
* @param {string} alias Alias
* @param {string} namespace Namespace
* @private
*/
cacheAliasNamespaceInfo(alias, namespace) {
if (!this._aliasToNamespaceInfo) {
this.loadAliasToNamespaceInfo();
}
this._aliasToNamespaceInfo.set(alias, namespace);
}
/**
* Creates an entity container.
*
* @abstract
* @protected
* @param {FullQualifiedName} containerName Container name
* @returns {EdmEntityContainer}
*/
// eslint-disable-next-line no-unused-vars
createEntityContainer(containerName) {
throw IllegalCallError.createForCallingAbstractMethod('createEntityContainer');
}
/**
* Caches an entity container.
*
* @param {FullQualifiedName} containerName Container name
* @param {EdmEntityContainer} container Container
* @private
*/
cacheEntityContainer(containerName, container) {
const key = FullQualifiedName.getFQNAsString(containerName);
this._entityContainers.set(key, container);
}
/**
* Creates an enumeration type.
*
* @abstract
* @protected
* @param {FullQualifiedName} enumTypeName Name of the enumeration type
* @returns {EdmEnumType}
*/
createEnumType(enumTypeName) { // eslint-disable-line no-unused-vars
throw IllegalCallError.createForCallingAbstractMethod('createEnumType');
}
/**
* Creates a type definition.
*
* @abstract
* @protected
* @param {FullQualifiedName} typeDefinitionName Name of the type definition
* @returns {EdmTypeDefinition}
*/
// eslint-disable-next-line no-unused-vars
createTypeDefinition(typeDefinitionName) {
throw IllegalCallError.createForCallingAbstractMethod('createTypeDefinition');
}
/**
* Creates an entity type.
*
* @abstract
* @protected
* @param {FullQualifiedName} entityTypeName Type name
* @returns {EdmEntityType}
*/
// eslint-disable-next-line no-unused-vars
createEntityType(entityTypeName) {
throw IllegalCallError.createForCallingAbstractMethod('createEntityType');
}
/**
* Creates a complex type.
* @abstract
* @protected
* @param {FullQualifiedName} complexTypeName Complex type name
* @returns {EdmComplexType}
*/
// eslint-disable-next-line no-unused-vars
createComplexType(complexTypeName) {
throw IllegalCallError.createForCallingAbstractMethod('createComplexType');
}
/**
* Creates an unbound action
*
* @abstract
* @protected
* @param {FullQualifiedName} actionName Action name
* @returns {EdmAction}
*/
// eslint-disable-next-line no-unused-vars
createUnboundAction(actionName) {
throw IllegalCallError.createForCallingAbstractMethod('createUnboundAction');
}
/**
* Creates all overloadings of an unbound function
*
* @abstract
* @protected
* @param {FullQualifiedName} functionName Function name
* @returns {EdmFunction[]}
*/
// eslint-disable-next-line no-unused-vars
createUnboundFunctions(functionName) {
throw IllegalCallError.createForCallingAbstractMethod('createUnboundFunctions');
}
/**
* Creates a specific overloading of an unbound function
*
* @abstract
* @protected
* @param {FullQualifiedName} functionName Function name
* @param {string[]} parameterNames Parameter names to identify the overloading
* @returns {EdmFunction}
*/
// eslint-disable-next-line no-unused-vars
createUnboundFunction(functionName, parameterNames) {
throw IllegalCallError.createForCallingAbstractMethod('createUnboundFunction');
}
/**
* Creates a bound action
*
* @abstract
* @protected
* @param {FullQualifiedName} actionName Action name
* @param {FullQualifiedName} bindingParameterTypeName Name of the binding parameter type
* @param {boolean} isBindingParameterCollection True if the binding parameter is a collection, otherwise false
* @returns {EdmAction}
*/
// eslint-disable-next-line no-unused-vars
createBoundAction(actionName, bindingParameterTypeName, isBindingParameterCollection) {
throw IllegalCallError.createForCallingAbstractMethod('createBoundAction');
}
/**
* Creates all overloadings of an bound function
*
* @abstract
* @protected
* @param {FullQualifiedName} functionName Function name
* @returns {EdmFunction[]}
*/
// eslint-disable-next-line no-unused-vars
createBoundFunctions(functionName) {
throw IllegalCallError.createForCallingAbstractMethod('createBoundFunctions');
}
/**
* Creates a specific overloading of a bound function
*
* @abstract
* @protected
* @param {FullQualifiedName} functionName Function name
* @param {FullQualifiedName} bindingParameterTypeName Name of the binding parameter type
* @param {boolean} isBindingParameterCollection True if the binding parameter is a collection, otherwise false
* @param {string[]} parameterNames Parameter names to identify the overloading
* @returns {EdmFunction}
*/
// eslint-disable-next-line no-unused-vars
createBoundFunction(functionName, bindingParameterTypeName, isBindingParameterCollection, parameterNames) {
throw IllegalCallError.createForCallingAbstractMethod('createBoundFunction');
}
/**
* Creates a term
*
* @abstract
* @protected
* @param {FullQualifiedName} termName Term name
* @returns {EdmTerm}
*/
// eslint-disable-next-line no-unused-vars
createTerm(termName) {
throw IllegalCallError.createForCallingAbstractMethod('createTerm');
}
/**
* Cache a function
*
* @param {FullQualifiedName} functionName Function name
* @param {EdmFunction} ffunction
* @private
*/
cacheFunction(functionName, ffunction) {
const functionNameString = FullQualifiedName.getFQNAsString(functionName);
const key = KeyFactory.createFunctionKey(functionName,
ffunction.getBindingParameterTypeFqn(),
ffunction.isBindingParameterTypeCollection(),
Array.from(ffunction.getParameters().keys()));
if (ffunction.isBound()) {
this._boundFunctionsByKey.set(key, ffunction);
addToMapArray(this._boundFunctionsByName, functionNameString, ffunction);
} else {
this._unboundFunctionByKey.set(key, ffunction);
addToMapArray(this._unboundFunctionByKey, functionNameString, ffunction);
}
}
/**
* Cache an action
* @param {FullQualifiedName} actionName Action name
* @param {EdmAction} action
* @private
*/
cacheAction(actionName, action) {
const actionNameString = FullQualifiedName.getFQNAsString(actionName);
if (action.isBound()) {
const key = KeyFactory.createActionKey(actionName,
action.getBindingParameterTypeFqn(),
action.isBindingParameterTypeCollection());
this._boundActions.set(key, action);
} else {
this._unboundActions.set(actionNameString, action);
}
}
}
module.exports = Edm;