{
- children.push(transform_json(e, "" + i, `${stableId}[${i}]`, options));
- })
- } else if (typeof obj == 'string') {
- children.push(transform_json(null, obj, `${stableId}.${obj}`, options));
- } else if (typeof obj == 'number' || typeof obj == 'boolean') {
- children.push(transform_json(null, "" + obj, `${stableId}.${obj}`, options));
- } else if (obj && typeof obj == 'object') {
- Object.keys(obj).forEach((key) => {
- children.push(transform_json(obj[key], key, `${stableId}.${key}`, options));
- });
+class ObjectTransformer {
+ constructor(obj, rootName, stableId) {
+ this.obj = obj;
+ this.rootName = rootName;
+ this.stableId = stableId;
+ this.diff = false;
}
- if (children.length == 1 && !children[0].combined) {
- return Object.freeze({
- kind: "",
- name: name + ": " + children[0].name,
- stableId: stableId,
- children: children[0].children,
- combined: true
- });
+ setOptions(options) {
+ this.options = options;
+ return this;
}
- return Object.freeze({
- kind: "",
- name: name,
- stableId: stableId,
- children: children,
- });
+ withDiff(obj) {
+ this.diff = true;
+ this.compareWithObj = obj ?? new Terminal();
+ return this;
+ }
+
+ transform() {
+ const { formatter } = this.options;
+ if (!formatter) {
+ throw new Error("Missing formatter, please set with setOptions()");
+ }
+
+ return this._transform(this.obj, this.rootName, this.compareWithObj, this.rootName, this.stableId);
+ }
+
+ _transformKeys(obj) {
+ const { skip, formatter } = this.options;
+ const transformedObj = {};
+ let formatted = undefined;
+
+ if (skip && skip.includes(obj)) {
+ // skip
+ } else if ((formatted = formatter(obj))) {
+ // Obj has been formatted into a terminal node — has no children.
+ transformedObj[formatted] = new Terminal();
+ } else if (Array.isArray(obj)) {
+ obj.forEach((e, i) => {
+ transformedObj["" + i] = e;
+ });
+ } else if (typeof obj == 'string') {
+ // Object is a primitive type — has no children. Set to terminal
+ // to differentiate between null object and Terminal element.
+ transformedObj[obj] = new Terminal();
+ } else if (typeof obj == 'number' || typeof obj == 'boolean') {
+ // Similar to above — primitive type node has no children.
+ transformedObj["" + obj] = new Terminal();
+ } else if (obj && typeof obj == 'object') {
+ Object.keys(obj).forEach((key) => {
+ transformedObj[key] = obj[key];
+ });
+ } else if (obj === null) {
+ // Null object is a has no children — set to be terminal node.
+ transformedObj.null = new Terminal();
+ }
+
+ return transformedObj;
+ }
+
+ _transform(obj, name, compareWithObj, compareWithName, stableId) {
+ const children = [];
+
+ if (!isTerminal(obj)) {
+ obj = this._transformKeys(obj);
+ }
+ if (!isTerminal(compareWithObj)) {
+ compareWithObj = this._transformKeys(compareWithObj);
+ }
+
+ for (const key in obj) {
+ if (obj.hasOwnProperty(key)) {
+ let compareWithChild = new Terminal();
+ let compareWithName = new Terminal();
+ if (compareWithObj.hasOwnProperty(key)) {
+ compareWithChild = compareWithObj[key];
+ compareWithName = key;
+ }
+ children.push(this._transform(obj[key], key, compareWithChild, compareWithName, `${stableId}.${key}`));
+ }
+ }
+
+ // Takes care of adding deleted items to final tree
+ for (const key in compareWithObj) {
+ if (!obj.hasOwnProperty(key) && compareWithObj.hasOwnProperty(key)) {
+ children.push(this._transform(new Terminal(), new Terminal(), compareWithObj[key], key));
+ }
+ }
+
+ let transformedObj;
+ if (
+ children.length == 1 &&
+ children[0].children.length == 0 &&
+ !children[0].combined
+ ) {
+ // Merge leaf key value pairs.
+ const child = children[0];
+
+ transformedObj = {
+ kind: "",
+ name: name + ": " + child.name,
+ stableId: stableId,
+ children: child.children,
+ combined: true,
+ }
+
+ if (this.diff) {
+ transformedObj.diff = child.diff;
+ }
+ } else {
+ transformedObj = {
+ kind: "",
+ name,
+ stableId: stableId,
+ children,
+ };
+
+ if (this.diff) {
+ const diff = getDiff(name, compareWithName);
+ transformedObj.diff = diff;
+
+ if (diff.type == DiffType.DELETED) {
+ transformedObj.name = compareWithName;
+ }
+ }
+ }
+
+ return Object.freeze(transformedObj);
+ }
}
function nanos_to_string(elapsedRealtimeNanos) {
@@ -163,4 +274,4 @@ function get_visible_chip() {
return { short: 'V', long: "visible", class: 'default' };
}
-export { transform, transform_json, nanos_to_string, get_visible_chip };
+export { transform, ObjectTransformer, nanos_to_string, get_visible_chip };
diff --git a/tools/winscope/src/utils/diff.js b/tools/winscope/src/utils/diff.js
new file mode 100644
index 000000000..4482f0367
--- /dev/null
+++ b/tools/winscope/src/utils/diff.js
@@ -0,0 +1,10 @@
+const DiffType = Object.freeze({
+ NONE: 'none',
+ ADDED: 'added',
+ DELETED: 'deleted',
+ ADDED_MOVE: 'addedMove',
+ DELETED_MOVE: 'deletedMove',
+ MODIFIED: 'modified',
+});
+
+export { DiffType };
\ No newline at end of file