Source: src/main/javascript/oracle/oj/ojvalidation/ValidationFactory.js

Oracle® JavaScript Extension Toolkit (JET)
1.1.2

E65298-01

/**
 * Copyright (c) 2014, Oracle and/or its affiliates.
 * All rights reserved.
 */

/**
 * The ojvalidation module.
 * @name oj.Validation
 * @class 
 * @export
 * @since 0.6
 * 
 */
oj.Validation = {};

/**
 * Internal properties to hold all factory provider callbacks or instances by name
 * @private
 */
oj.Validation._converterFactories = {}; oj.Validation._validatorFactories = {};

/**
 * Internal properties to hold the default factory instances.
 * @private
 */
oj.Validation._defaultConverterFactories = {}; oj.Validation._defaultValidatorFactories = {};

/**
 * Internal property that identifies the type that is the contract for conveters and validators.
 * @private
 */
oj.Validation._CONTRACTS = {'converter' : {name: "oj.ConverterFactory",  type: oj.ConverterFactory},
                            'validator': {name: "oj.ValidatorFactory", type: oj.ValidatorFactory}};

/**
 * Module method to register and retrieve converter factory instances by name. 
 * When passed only the name, an existing factory (registered for the name) is returned. Callers can 
 * expect to get back the default 'number' and 'datetime' converters. 
 * When passed two arguments, a new factory for the name is registered. If the name already exists 
 * the new instance replaces the old one. 
 * 
 * @param {string} type a case insensitive name of the converter factory. 
 * @param {Object=} instance the instance of the factory that implements the contract for 
 * oj.ConverterFactory.
 * 
 * @export
 * @see oj.ConverterFactory
 */
oj.Validation.converterFactory = function (type, instance)
{
  var retValue;
  if (type && !instance)
  {
    // getter
    retValue = oj.Validation._getFactory(type, oj.Validation._converterFactories);
  }
  else if (type && instance)
  {
    // setter
    retValue = oj.Validation._registerFactory(type, 
                                              instance, 
                                              oj.Validation._converterFactories,
                                              oj.Validation._CONTRACTS['converter']);
  }
  
  return retValue;
};

/**
 * Module method to register and retrieve validator factory instances by name. 
 * When passed only the name, an existing factory (registered for the name) is returned. 
 * When passed two arguments, a new factory for the name is registered. If the name already exists 
 * the new instance replaces the old one. 
 * 
 * @param {string} type a case insensitive name of the validator factory. 
 * @param {Object=} instance the instance of the factory that implements the contract for 
 * oj.ValidatorFactory.
 * 
 * @export
 * @see oj.ValidatorFactory
 */
oj.Validation.validatorFactory = function (type, instance)
{
  var retValue;
  if (type && !instance)
  {
    // getter
    retValue = oj.Validation._getFactory(type, oj.Validation._validatorFactories);
  }
  else if (type && instance)
  {
    // setter
    retValue = oj.Validation._registerFactory(type, 
                                              instance, 
                                              oj.Validation._validatorFactories, 
                                              oj.Validation._CONTRACTS['validator']);
  }
  
  return retValue;
};

/**
 * Returns the default converter factory instances for the supported types as defined by the 
 * oj.ConverterFactory.
 * 
 * @param {string} type The default converter factory for the type. Supported types are 'number' and 
 * 'datetime'
 * @return {Object} an instance of oj.ConverterFactory or null if an unknown type is requested.
 * 
 * @export
 * @see oj.ConverterFactory
 * 
 */
oj.Validation.getDefaultConverterFactory = function (type)
{
  return oj.Validation._getFactory(type, oj.Validation._defaultConverterFactories);
};

/**
 * Returns the default validator factory instance for the requested types as defined by the 
 * oj.ValidatorFactory.
 * 
 * @param {string} type The default converter factory for the type. Supported types are 'number' and 
 * 'datetime'
 * @return {Object} an instance of oj.ConverterFactory or null if an unknown type is requested.
 * 
 * @export
 * @see oj.ValidatorFactory
 */
oj.Validation.getDefaultValidatorFactory = function (type)
{
  return oj.Validation._getFactory(type, oj.Validation._defaultValidatorFactories);
};

// PACKAGE PRIVATE METHODS
/**
 * Called only by internal jet converter factory implementations.
 * 
 * @param {string} name
 * @param {Object} instance
 * @private
 */
oj.Validation.__registerDefaultConverterFactory = function (name, instance)
{
  // save to both factories
  var contractDef = oj.Validation._CONTRACTS['converter'];
  oj.Validation._registerFactory (name, instance, oj.Validation._defaultConverterFactories, contractDef);
  oj.Validation._registerFactory(name, instance, oj.Validation._converterFactories, contractDef);
};

/**
 * Called only by internal jet validator factory implementations.
 * 
 * @param {string} name of the validator factory
 * @param {Object} instance of the validator factory that creates instances of the validator
 * @private
 */
oj.Validation.__registerDefaultValidatorFactory = function (name, instance)
{
  // save to both factories
  var contractDef = oj.Validation._CONTRACTS['validator'];
  oj.Validation._registerFactory (name, instance, oj.Validation._defaultValidatorFactories, contractDef);
  oj.Validation._registerFactory(name, instance, oj.Validation._validatorFactories, contractDef);
};


/**
 * Checks that the instance implements the interface type. If it doesn't it throws an error.
 * @param {Object} instance
 * @param {Object} type
 * @param {string} typeName 
 * @throws {Error} if instance does not implement the methods defined on type.  
 * @private
 */
oj.Validation._doImplementsCheck = function (instance, type, typeName)
{
  if (type)
  {
    // Check that instance duck types providerType
    if (!oj.Validation._quacksLike(instance, type))
    {
      throw new Error("Factory instance does not implement the methods expected by the factory of type " + typeName);
    }
  }
};

/**
 * Retrieves the converter factory by name from the provided factories.
 * @private
 */
oj.Validation._getFactory = function(name, factories)
{
  oj.Assert.assertString(name);
  var cachedInstance = null;
  
  if (name)
  {
    name = name.toLowerCase();

    // getter called to retrieve the factory instance 
    var providerProps = factories[name] || {}; 
    cachedInstance = providerProps['instance'] || null;
  }
  // TODO: log a warning that name is null
  return cachedInstance;
};

/**
 * Tests whether an object 'quacks like a duck'. Returns `true` if the thingie has all of the 
 * methods of the second, parameter 'duck'. Returns `false` otherwise. 
 *
 * @param {Object} thingie the object to test.
 * @param {Object} duck The archetypal object, or 'duck', that the test is against.
 * @private
*/
oj.Validation._quacksLike = function(thingie, duck) 
{
  var valid = true, property;

  oj.Assert.assertObject(thingie);
  oj.Assert.assertObject(duck);

  for (property in duck) 
  {
    // Ensure that thingie defines the same functions as duck. We don't care about other properties
    if (duck.hasOwnProperty(property)) 
    {
      if (typeof duck[property] === "function" && 
              !thingie[property] && typeof thingie[property] !== "function") 
      {
        valid = false;
        break;
      }
    }
  }
  
  return valid;
};

/**
 * Registers the factory instance by the name, storing it into the factories object, after ensruing 
 * that the instance duck types the specified contract.
 * 
 * @private
 */
oj.Validation._registerFactory = function(name, instance, factories, contractDef)
{
  oj.Assert.assertString(name);
  oj.Assert.assertObject(instance);

  if (name)
  {
    // set new provider factory function clearing out the previously stored instance
    var props = {};
    props['instance'] = instance;
    oj.Validation._doImplementsCheck(instance, contractDef.type, contractDef.name);

    // Save to default and public factories
    factories[name.toLowerCase()] = props;
  }
};

/**
 * Contract for a ConverterFactory that provides a factory method to create a converter instance for 
 * the requested type. JET provides 2 factory implementations for number and datetime types that 
 * implement this contract. Customers can register custom converter factories for the supported 
 * types or create and register factories for new types altogether.
 * 
 * @name oj.ConverterFactory
 * @abstract
 * @class
 * @export
 * @see oj.Validation
 * @see oj.NumberConverterFactory
 * @see oj.DateTimeConverterFactory
 */
oj.ConverterFactory = 
{
  /**
   * Default type for a factory used to create number converters. This type is passed to the 
   * [Validation.converterFactory]{@link oj.Validation#converterFactory} method to retrieve the 
   * number converter factory of type {@link oj.NumberConverterFactory}. 
   * @expose
   * @const
   * @member
   * @type {string}
   */
  "CONVERTER_TYPE_NUMBER" : 'number',
  
  /**
   * Default type for a factory used to create datetime converters. This type is passed to the 
   * [Validation.converterFactory]{@link oj.Validation#converterFactory} method to retrieve the 
   * datetime converter factory of type {@link oj.DateTimeConverterFactory}. 
   * @expose
   * @const
   * @member
   * @type {string}
   */
  "CONVERTER_TYPE_DATETIME" : 'datetime',

  /**
   * Creates an immutable converter instance of the type the factory implements. 
   * 
   * @param {(Object|null)} options an object literal containing properties required by the converter 
   * for its initialization. The properties provided in the options is implementation specific.
   * 
   * @return {Object} a converter instance.
   * @throws {TypeError} if an unrecognized type was provided 
   * @expose
   */
  createConverter : function(options) {}  
};

/**
 * Contract for a ValidatorFactory that provides a factory method to create a validator instance for 
 * the requested type. JET provides several factory implementations that implement this contract - 
 * for example dateRestriction, dateTimeRange, numberRange, length, required, regexp. Customers can 
 * register custom validator factories for the supported types or create and register factories for 
 * new types altogether.
 * 
 * @name oj.ValidatorFactory
 * @abstract
 * @class
 * @export
 * @see oj.Validation
 * @see oj.DateRestrictionValidatorFactory
 * @see oj.DateTimeRangeValidatorFactory
 * @see oj.LengthValidatorFactory
 * @see oj.NumberRangeValidatorFactory
 * @see oj.RegExpValidatorFactory
 * @see oj.RequiredValidatorFactory
 */
oj.ValidatorFactory = 
{
  /**
   * Default type for a factory used to create required validators. This type is passed to the 
   * [Validation.validatorFactory]{@link oj.Validation#validatorFactory} method to retrieve the 
   * required validator factory of type {@link oj.RequiredValidatorFactory}. 
   * @expose
   * @const
   * @member
   * @type {string}
   */
  "VALIDATOR_TYPE_REQUIRED" : 'required',
          
  /**
   * Default type for a factory used to create regExp validators. This type is passed to the 
   * [Validation.validatorFactory]{@link oj.Validation#validatorFactory} method to retrieve the 
   * regExp validator factory of type {@link oj.RegExpValidatorFactory}. 
   * @expose
   * @const
   * @member
   * @type {string}
   */
  "VALIDATOR_TYPE_REGEXP" : 'regexp',

  /**
   * Default type for a factory used to create numberRange validators. This type is passed to the 
   * [Validation.validatorFactory]{@link oj.Validation#validatorFactory} method to retrieve the 
   * numberRange validator factory of type {@link oj.NumberRangeValidatorFactory}. 
   * @expose
   * @const
   * @member
   * @type {string}
   */
  "VALIDATOR_TYPE_NUMBERRANGE" : 'numberRange',

  /**
   * Default type for a factory used to create length validators. This type is passed to the 
   * [Validation.validatorFactory]{@link oj.Validation#validatorFactory} method to retrieve the 
   * length validator factory of type {@link oj.LengthValidatorFactory}. 
   * @expose
   * @const
   * @member
   * @type {string}
   */
  "VALIDATOR_TYPE_LENGTH" : 'length',

  /**
   * Default type for a factory used to create required validators. This type is passed to the 
   * [Validation.validatorFactory]{@link oj.Validation#validatorFactory} method to retrieve the 
   * dateTimeRange validator factory of type {@link oj.DateTimeRangeValidatorFactory}. 
   * @expose
   * @const
   * @member
   * @type {string}
   */
  "VALIDATOR_TYPE_DATETIMERANGE" : 'dateTimeRange',
  
  /**
   * Default type for a factory used to create date restriction validators. This type is passed to 
   * the [Validation.validatorFactory]{@link oj.Validation#validatorFactory} method to retrieve the 
   * dateRestriction validator factory of type {@link oj.DateRestrictionValidatorFactory}. 
   * @expose
   * @const
   * @member
   * @type {string}
   */
  "VALIDATOR_TYPE_DATERESTRICTION" : 'dateRestriction',
          
  /**
   * Creates an immutable validator instance of the type the factory implements. 
   * 
   * @param {(Object|null)} options an object literal containing properties required by the validator 
   * for its initialization. The properties provided in the options is implementation specific.
   * 
   * @return {Object} a validator instance.
   * @throws {TypeError} if an unrecognized type was provided 
   * @expose
   */
  createValidator : function(options) {}  
};