Forum Discussion

BenoitB's avatar
BenoitB
Community Hero
4 years ago

Advanced search object for complex model

Hello all,

 

 

Please find here my implementation of advanced search of in-memory object for complex tree model.

 

Sometimes when the tested app is really complex (especially in heavy client application), the search for object could be too slow due to depth.

 

One of the solution is to do by finding intermediate object to narrow and guide the search. So you multiply findChildEx. To ease that i made the followg method :

 

 

 

 

/**
 * <a id="system.findContainer"></a>
 * Rechercher un objet TestComplete en mémoire de manière containérisé<br>
 * Si <i>system.debug</i> est à <b>true</b> alors des logs complémentaires sur la recherche sont inscrits
 * @memberof system
 * @function
 *  {Object} ParentObject - Objet parent porteur de l'objet à rechercher. Racine de départ de la recherche. Plus l'objet parent est de haut niveau, plus le temps de recherche peut être long
 *  {array} Props - Tableau des propriétés des objets à rechercher par container, si plusieurs propriétés recherchées pour un container donnée alors le séparateur est le |, exemple ["Visible|Name", "Name"] -> recherche des 2 propriétés Visble et Name pour le premier container et recherche de Name seulement pour le deuxième container
 *  {array} Values - Tableau des valeurs des propriétés des objets à rechercher
 *  {number|array} [Depth=4] - Limiter la recherche a <i>Depth>/i> profondeur depuis l'objet parent. Plus la profondeur est grande, plus le temps de recherche peut être long. Peut être un tableau pour définir un temps de rechercher par container
 * @returns {object} Renvoie l'objet s'il existe ou <b>null</b> le cas échéant ou bien en cas d'erreur
 */
system.findContainer = function(ParentObject = null, Props = null, Values = null, Depth = 4) {
  // Vérification des paramètres obligatoires
  if ((ParentObject == null) || (Props == null) || (Values == null)) {
    if (system.debug)
      Log.Message('findContainer() - Un paramètre obligatoire est non renseigné (ParentObject ou Props ou Values)', "", pmHigher, system.logWarning);
    return null;
  }
  if (ParentObject == "current") {
    if ((typeof system.container != 'undefined') && (system.container != null) && (system.container.Exists))
      ParentObject = system.container
    else
      return null;
  }
  if (system.debug) {
    let propsKey = typeof Props == "string" ? Props : Props.join("|");
    let valuesKey;
    switch (typeof Values) {
      case "string":
        valuesKey = Values;
        break;
      case "number":
      case "boolean":
        valuesKey = Values.toString();
        break;
      case "null":
        valuesKey = "null";
        break;
      default:
        valuesKey = Values.join("|");
        break;
    }
    Log.Message("findContainer(" + ParentObject.FullName + ", " + propsKey + ", " + valuesKey + ", " + Depth.toString()  + ") - Recherche d'objet", "", pmLowest, system.logDebug);
  }
  var objectfind = ParentObject;
  let currentProps;
  let currentValues;
  let currentDepth;
  try {
    for (let i=0;i<Props.length;i++) {
      currentProps  = Props[i].split('|');
      currentValues = new Array();
      currentDepth  = typeof Depth == 'number' ? Depth : Depth[i];
      for (let j=0;j<currentProps.length;j++) {
        currentValues.push(Values.shift());
      }  
      objectfind = objectfind.FindChildEx(currentProps, currentValues, currentDepth, true, system.time.medium);
      if ((typeof objectfind == 'undefined') || ((objectfind != null) && (!objectfind.Exists))) 
        break;
    }  
  }
  catch (e) {
    objectfind = null;
    if (system.debug)
      Log.Message("findContainer() - Une exception est apparue dans la recherche", e.message, pmHighest, system.logError);
  }
  finally {
    // Ne renvoyer que "null" sur non trouvé ou erreur
    if ((typeof objectfind == 'undefined') || ((objectfind != null) && (!objectfind.Exists)))
      objectfind = null;
    if (system.debug) {
      if (objectfind == null)
        Log.Message("findContainer() renvoie un objet null ou undefined", "", pmLowest, system.logDebug)
      else
        Log.Message('findContainer() a trouvé un objet', objectfind.FullName, pmLowest, system.logDebug);
    }
    return objectfind;
  }
}

 

 

 

 

It comes from my testing framework so everything starting by system. is specific but you should understand the use :

system.debug -> true to activate an additionnal level of log.

system.logDebug -> specific debug log attributes.

system.container -> a global variable that can hold a frequently used object in portion of test

system.time.medium -> this is 5 seconds (5000ms)

 

Samples usage :

 

 

 

let objectToTest = system.findContainer(SourceObject, ["Visible|Name", "Visible|Name"], [true,'dlmAccueil', true,'editionEnfants']);

 

 

 

Will search in SourceObject a visible object named 'editionEnfants' which is located inside another visible object named 'dlmAccueil'. The search will use a default max depth of 4 levels for both objects.

 

 

 

 

let objectToTest = system.findContainer(SourceObject, ["Visible|Name", "Name", "Header|Index|Visible"], [true, "Saisie", "DockingPanel"', "ILPaneGroup", 1, true], [2, 3, 2]);

 

 

 

Will search in SourceObject a visible object with property Index to 1 and property Header to 'ILPanelGroup' which is located inside another object named 'DockingPanel' which is located inside another visible object named 'Saisie'.  The search will use a depth of 2 levels for first object, 3 levels for second object and 2 levels for final object.

 

 

Hope it could help someone and feel free to discuss/improve it.

  • I'm thinking that this may be extremely useful for CEF frameworks just because of the depth of objects typically embedded within those applications. But I'm not sure if that would be considered "complex" to a point where this is "needed" but I would think this would still be helpful. 

    • BenoitB's avatar
      BenoitB
      Community Hero

       hkim5 i use it thoroughly in a complex WPF application and it save me a lot 😉

       

      Another side effect of using it is that is a little quicker than the sequential call of findChildEx()

       

      Here a simplified version without debug level and isolated from my framework :

       

       

      function findContainer(ParentObject = null, Props = null, Values = null, Depth = 4) {
        // Vérification des paramètres obligatoires
        if ((ParentObject == null) || (Props == null) || (Values == null))
          return null;
        var objectfind = ParentObject;
        let currentProps;
        let currentValues;
        let currentDepth;
        try {
          for (let i=0;i<Props.length;i++) {
            currentProps  = Props[i].split('|');
            currentValues = new Array();
            currentDepth  = typeof Depth == 'number' ? Depth : Depth[i];
            for (let j=0;j<currentProps.length;j++) {
              currentValues.push(Values.shift());
            }  
            objectfind = objectfind.FindChildEx(currentProps, currentValues, currentDepth, true, 5000);
            if ((typeof objectfind == 'undefined') || ((objectfind != null) && (!objectfind.Exists))) 
              break;
          }  
        }
        catch (e) {
          objectfind = null;
        }
        finally {
          // Ne renvoyer que "null" sur non trouvé ou erreur
          if ((typeof objectfind == 'undefined') || ((objectfind != null) && (!objectfind.Exists)))
            objectfind = null;
          return objectfind;
        }
      }