blob: 5bb83bc1ef9b8de715032743d9f254acd5c0dd2d [file] [log] [blame]
/*
* Copyright (c) 2014-2017 BSI Business Systems Integration AG.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BSI Business Systems Integration AG - initial API and implementation
*/
import {objects, scout, TypeDescriptor} from './index';
import $ from 'jquery';
/**
* @singleton
*/
export default class ObjectFactory {
constructor() {
// use createUniqueId() to generate a new ID
this.uniqueIdSeqNo = 0;
this._registry = {};
}
static NAMESPACE_SEPARATOR = '.';
static MODEL_VARIANT_SEPARATOR = ':';
/**
* Creates an object from the given objectType. Only the constructor is called.
*
* OBJECT TYPE:
*
* An object type may consist of three parts: [name.space.]Class[:Variant]
* 1. Name spaces (optional)
* All name space parts have to end with a dot ('.') character. If this part is omitted, the default
* name space "scout." is assumed.
* Examples: "scout.", "my.custom.namespace."
* 2. Scout class name (mandatory)
* Examples: "Desktop", "Session", "StringField"
* 3. Model variant (optional)
* Custom variants of a class can be created by adding the custom class prefix after
* the Scout class name and a colon character (':'). This prefix is then combined with
* the class name.
* Examples: ":Offline", ":Horizontal"
*
* Full examples:
* Object type: Outline -> Constructor: Outline
* Object type: myNamespace.Outline -> Constructor: myNamespace.Outline
* Object type: Outline:MyVariant -> Constructor: scout.MyVariantOutline
* Object type: myNamespace.Outline:MyVariant -> Constructor: myNamespace.MyVariantOutline
* Object type: Outline:myNamespace.MyVariant -> Constructor: myNamespace.MyVariantOutline
* Object type: myNamespace.Outline:yourNamespace.MyVariant -> Constructor: yourNamespace.MyVariantOutline
*
* RESOLVING THE CONSTRUCTOR:
*
* When scout.objectFactories contains a create function for the given objectType, this function is called.
*
* Otherwise it tries to find the constructor function by the following logic:
* If the objectType provides a name space, it is used. Otherwise it takes the default "scout" name space.
* If the object type provides a variant ("Type:Variant"), the final object type is built by prepending
* the variant to the type ("VariantType"). If no such type can be found and the option "variantLenient"
* is set to true, a second attempt is made without the variant.
*
* @param objectType (mandatory) String describing the type of the object to be created.
* @param options (optional) Options object, currently supporting the following two options:
* - model = Model object to be passed to the constructor or create function
* - variantLenient = Flag to allow a second attempt to resolve the class
* without variant (see description above).
*/
_createObjectByType(objectType, options) {
if (typeof objectType !== 'string') {
throw new Error('missing or invalid object type');
}
options = options || {};
var createFunc = this._registry[objectType];
if (createFunc) {
// 1. - Use factory function registered for the given objectType
var scoutObject = createFunc(options.model);
if (!scoutObject) {
throw new Error('Failed to create object for objectType "' + objectType + '": Factory function did not return a valid object');
}
return scoutObject;
}
// 2. - Resolve class by name
return TypeDescriptor.newInstance(objectType, options);
}
/**
* Creates and initializes a new Scout object. When the created object has an init function, the
* model object is passed to that function. Otherwise the init call is omitted.
*
* @param objectType A string with the requested objectType. This argument is optional, but if it
* is omitted, the argument "model" becomes mandatory and MUST contain a
* property named "objectType". If both, objectType and model, are set, the
* objectType parameter always wins before the model.objectType property.
* @param model The model object passed to the constructor function and to the init() method.
* This argument is mandatory if it is the first argument, otherwise it is
* optional (see above). This function may set/overwrite the properties 'id' and
* 'objectType' on the model object.
* @param options Options object, see table below. This argument is optional.
*
* An error is thrown if the argument list does not match this definition.
*
* List of options:
*
* OPTION DEFAULT VALUE DESCRIPTION
* ------------------------------------------------------------------------------------------------------
* variantLenient false Controls if the object factory may try to resolve the
* scoutClass without the model variant part if the initial
* objectType could not be resolved.
*
* ensureUniqueId true Controls if the resulting object should be assigned the
* attribute "id" if it is not defined. If the created object has an
* init() function, we also set the property 'id' on the model object
* to allow the init() function to copy the attribute from the model
* to the scoutObject.
*/
create(objectType, model, options) {
// Normalize arguments
if (typeof objectType === 'string') {
options = options || {};
} else if (objects.isPlainObject(objectType)) {
options = model || {};
model = objectType;
if (!model.objectType) {
throw new Error('Missing mandatory property "objectType" on model');
}
objectType = model.objectType;
} else {
throw new Error('Invalid arguments');
}
options.model = model;
// Create object
var scoutObject = this._createObjectByType(objectType, options);
if (objects.isFunction(scoutObject.init)) {
if (model) {
if (model.id === undefined && scout.nvl(options.ensureUniqueId, true)) {
model.id = this.createUniqueId();
}
model.objectType = objectType;
}
// Initialize object
scoutObject.init(model);
}
if (scoutObject.id === undefined && scout.nvl(options.ensureUniqueId, true)) {
scoutObject.id = this.createUniqueId();
}
if (scoutObject.objectType === undefined) {
scoutObject.objectType = objectType;
}
return scoutObject;
}
/**
* Returns a new unique ID to be used for Widgets/Adapters created by the UI
* without a model delivered by the server-side client.
* @return string ID with prefix 'ui'
*/
createUniqueId() {
return 'ui' + (++this.uniqueIdSeqNo).toString();
}
register(objectType, createFunc) {
$.log.isDebugEnabled() && $.log.debug('(ObjectFactory) registered create-function for objectType ' + objectType);
this._registry[objectType] = createFunc;
}
unregister(objectType) {
$.log.isDebugEnabled() && $.log.debug('(ObjectFactory) unregistered objectType ' + objectType);
delete this._registry[objectType];
}
get(objectType) {
return this._registry[objectType];
}
/**
* Cannot init ObjectFactory until Log4Javascript is initialized.
* That's why we call this method in the scout._init method.
*/
init() {
for (var objectType in scout.objectFactories) {
if (scout.objectFactories.hasOwnProperty(objectType)) {
this.register(objectType, scout.objectFactories[objectType]);
}
}
}
static get() {
return objectFactory;
}
static _set(newFactory) {
objectFactory = newFactory;
}
}
let objectFactory = new ObjectFactory();