function main()
{
// ...
o = waitForObject({"type": "Table"});
// Find first child where properties...
// "text" == "Kimberly"
var o1 = objTools.findChildPath(o, {"text": "Kimberly"});
// Find first child of above object's parent
// where properties...
// text == "Kimberly"
// row == "1"
// column == "1"
var o2 = objTools.findChildPath(o1[1], {"text": "Kimberly", "row": "1", "column": "1"});
// Find first child where properties...
// row == "2"
// column == "0"
var o1 = objTools.findChildPath(o, {"row": "2", "column": "0"});
// Find second child where properties...
// row == "2"
// column == "0"
//
// "occurrence" is an "artificial" property.
// The first occurrence of an object is "0".
var o2 = objTools.findChildPath(o, {"row": "2", "column": "0", "occurrence": "1"});
// Find first child where properties...
// type == "TableCell"
// row == "2"
// column == "0"
var o1 = objTools.findChildPath(o, {"type": "TableCell", "row": "2", "column": "0"});
// Find second child where properties...
// type == "TableCell"
// row == "2"
// column == "0"
//
// "occurrence" is an "artificial" property.
// The first occurrence of an object is "0".
var o2 = objTools.findChildPath(o, {"type": "TableCell", "row": "2", "column": "0", "occurrence": "1"});
}
var objTools = {
/**
* Find and return object with properties matching those
* specified in propertiesObject.
*
* "obj" can be any container or child object.
* When passing a child only the children of that child
* are searched.
*
* The return value is an array with the found object
* at index 0, and all its parents (if any), starting
* with the closest parent. For example:
*
* [aTableCellObject, aTableRowObject, aTableObject]
*
* Example calls:
*
* o = waitForObject("{...}");
* path = findChildPath(o, {"text": "Kimberly"});
* path = findChildPath(o, {"row": "0"});
* path = findChildPath(o, {"row": "0", "column": "0"});
*
* propertiesObj can also contain "occurrence". The first
* occurrence of an object has an occurrence value of 0.
*
* propertiesObj can also contain "type", just like in
* real names. (But Squish wrapper types are not
* supported, i.e.
* com.froglogic.squish.swt.TreeItemProxy, etc.).
*/
findChildPath: function(obj, propertiesObj, verbose)
{
if (verbose == null) {
var verbose = False;
}
var path = [obj];
var occurrenceCountDown = [0];
if ("occurrence" in propertiesObj) {
occurrenceCountDown[0] = parseInt(propertiesObj["occurrence"]);
}
if (this.findChildPath_impl(obj, propertiesObj, path, occurrenceCountDown, verbose)) {
path.reverse();
return path;
}
return [];
},
hasPropertiesAndValues: function(obj, propertiesObj, verbose) {
if (verbose) {
test.log("findChildPath: Checking properties of: " + this.classOrTypeName(obj));
}
// If this is an item proxied by a Squish object,
// check the actual item instead
if (("class" in obj)
&& ("item" in obj)
&& (obj.item != null)
&& (obj["class"].indexOf("com.froglogic.") != -1)) {
obj = obj.item;
}
for (propertyName in propertiesObj) {
// Ignore the occurrence property here (a special check
// for that (based on our own occurrence counting) is in
// findChildPath_impl())
if (propertyName == "occurrence") {
continue;
}
var propertyValue = propertiesObj[propertyName];
// Allow searching for "type" too, for consistency
// with real name properties
if (propertyName == "type") {
var t = this.classOrTypeName(obj);
if (t == propertyValue) {
continue;
}
if (verbose) {
test.log("findChildPath: \tProperty mismatch: type: Expected: " + propertyValue + "; Actual: " + t);
}
return false;
}
var propertyNames = propertyName.split(".");
if (!(propertyNames[0] in obj)) {
if (verbose) {
test.log("findChildPath: \tProperty not in obj: " + propertyNames[0]);
}
return false;
}
try {
var v = this.fetchPropertyValue(obj, propertyNames, verbose);
if (v != propertyValue) {
if (verbose) {
test.log("findChildPath: \tProperty mismatch: " + propertyName + ": Expected: " + propertyValue + ": Actual: " + v);
}
return false;
}
} catch (e) {
if (verbose) {
test.log("findChildPath: \tProperty not found: " + propertyName, ""+e);
}
}
}
return true;
},
fetchPropertyValue: function(obj, propertyNames, verbose) {
if (propertyNames.length == 1) {
return obj[propertyNames[0]] ;
}
var currentObj = obj;
var nameSoFar = ""
for (var i = 0; i < propertyNames.length - 1; i++) {
var n = propertyNames[i];
currentObj = currentObj[n];
nameSoFar = nameSoFar + "." + n;
if (verbose) {
test.log("findChildPath: \tProperty in obj: " + nameSoFar.substr(1));
}
}
var n = propertyNames[propertyNames.length-1];
nameSoFar = nameSoFar + "." + n;
var v = currentObj[n];
if (verbose) {
test.log("findChildPath: \tProperty in obj: " + nameSoFar.substr(1));
}
return v;
},
findChildPath_impl: function (obj, propertiesObj, path, occurrenceCountDown, verbose)
{
if (verbose) {
test.log("findChildPath: Iterating over children of: " + this.classOrTypeName(obj));
}
var children = object.children(obj);
for (var i = 0; i < children.length; i++) {
var c = children[i];
if (verbose) {
test.log("findChildPath: Next child's type: " + this.classOrTypeName(c));
}
if (!this.hasPropertiesAndValues(c, propertiesObj, verbose)) {
path.push(c);
if (this.findChildPath_impl(c, propertiesObj, path, occurrenceCountDown, verbose)) {
return true;
}
path.pop();
continue;
}
if ([occurrenceCountDown] <= 0) {
path.push(c);
return true;
}
occurrenceCountDown[0] = occurrenceCountDown[0] - 1;
if (this.findChildPath_impl(c, propertiesObj, path, occurrenceCountDown, verbose)) {
return true;
}
}
return false;
},
classOrTypeName: function (obj) {
if ("class" in obj) {
return obj["class"];
}
return typeName(obj);
}
};