| /** | 
|  * Row data are defined as name and (immediate) parent only. | 
|  * DataHierarchyProvider class is able to query recursively to infer all ancestors. | 
|  */ | 
| export interface DataHierarchy { | 
|   Name: string; | 
|   ParentKey: string; | 
|   NameKey: string; | 
|   OndrawImageName?: string; | 
| } | 
|   | 
| /** | 
|  * Provider class to get all ancestor row names by passing in the target child row. | 
|  */ | 
| export class DataHierarchyProvider { | 
|   private readonly store: Map<string, DataHierarchy> = new Map(); | 
|   | 
|   public constructor(data: Record<string, DataHierarchy>) { | 
|     for (const t of Object.keys(data)) { | 
|       const a = data[t]; | 
|       this.store.set(a.NameKey, a); // Stores row data and its immediate parent | 
|     } | 
|   } | 
|   | 
|   /** | 
|    * Given a row name, infer the ancestor rows. | 
|    * | 
|    * @param key Row name. | 
|    * @returns Tuple containing row name and all ancestors. | 
|    */ | 
|   public getHierarchy(key: DataHierarchy): [DataHierarchy, DataHierarchy[]] { | 
|     // Recursive get all ancestor rows | 
|     const ancestor = this.store.get(key.NameKey); | 
|     let ancestorArr: DataHierarchy[] = []; | 
|     ancestorArr = this.recursiveGetAncestor(ancestor!, ancestorArr); | 
|     return [key, ancestorArr]; | 
|   } | 
|   | 
|   private recursiveGetAncestor(ancestor: DataHierarchy, ancestorArr: DataHierarchy[]): DataHierarchy[] { | 
|     const hasParent = ancestor !== undefined && ancestor.ParentKey !== ''; | 
|     if (hasParent) { | 
|       const ancestorName = this.store.get(ancestor.ParentKey); | 
|       if (ancestorName) { | 
|         ancestorArr = [ancestorName].concat(ancestorArr); // Put ancestor to the front of array | 
|         ancestorArr = this.recursiveGetAncestor(ancestorName, ancestorArr); | 
|       } else { | 
|         throw(TypeError(`No Ancestor: ${ancestor.ParentKey} to be found.`)); | 
|       } | 
|     } | 
|     return ancestorArr; | 
|   } | 
| } |