1
+ /**
2
+ * The `Inheritable` module delivers the core data structure for making type safe,
3
+ * single-typed-children, tree nodes.
4
+ *
5
+ * This abstract class, in order to support tree nodes of *any* kind, makes creative
6
+ * use of the `any` type in a few places – but don't be fooled! These typing holes
7
+ * are *required* to be plugged by any by any implementing class by virtue of the type
8
+ * generics they are forced to provide when extending. By the time this abstract class
9
+ * is exposed to the outside world, all typing holes have been plugged and we are left
10
+ * with a fully typed tree structure.
11
+ *
12
+ * @module Block/BlockTree/Inheritable
13
+ */
1
14
import { ObjectDictionary } from "@opticss/util" ;
2
15
import { whatever } from "@opticss/util" ;
3
16
17
+ import { SourceLocation } from "../../SourceLocation" ;
18
+
4
19
/* tslint:disable:prefer-whatever-to-any */
5
20
export type AnyNode = Inheritable < any , any , any , any > ;
6
21
7
22
export abstract class Inheritable <
8
23
Self extends Inheritable < Self , Root , Parent , Child > ,
9
- Root extends Inheritable < any , Root , null , any > | Self ,
10
- Parent extends Inheritable < any , Root , any , Self > | null ,
11
- Child extends Inheritable < any , Root , Self , any > | null
24
+ Root extends Inheritable < any , Root , null , AnyNode > | Self ,
25
+ Parent extends Inheritable < any , Root , AnyNode | null , Self > | null ,
26
+ Child extends Inheritable < any , Root , Self , AnyNode | null > | null
12
27
> {
13
28
/* tslint:enable:prefer-whatever-to-any */
29
+
14
30
protected _name : string ;
15
31
protected _base : Self | undefined ;
16
32
protected _root : Root | Self ;
@@ -23,17 +39,6 @@ export abstract class Inheritable<
23
39
*/
24
40
protected abstract newChild ( name : string ) : Child ;
25
41
26
- // // TODO: Currently only ever returns itself if is a style. Need to get it to look other things up.
27
- // public lookup(path: string | BlockPath): Self | Child | Descendants | undefined {
28
- // path = new BlockPath(path);
29
- // let res: Self | Child | Descendants | null = this.asSelf();
30
- // for (let part of path.tokens()) {
31
- // res = res.resolveChild(part);
32
- // if (!res) { break; }
33
- // }
34
- // return res ? res : undefined;
35
- // }
36
-
37
42
/**
38
43
* Inheritable constructor
39
44
* @param name Name for this Inheritable instance.
@@ -74,14 +79,28 @@ export abstract class Inheritable<
74
79
}
75
80
76
81
/**
77
- * traverse parents and return the base block object.
78
- * @returns The base block in this container tree.
82
+ * Traverse parents and return the base block object.
83
+ * @returns The base node in this tree.
79
84
*/
80
85
public get root ( ) : Root {
81
86
// This is a safe cast because we know root will only be set to `Self` for `Source` nodes.
82
87
return this . _root as Root ;
83
88
}
84
89
90
+ /**
91
+ * The `block` property is an alias for `root`. This isn't the dryest place to put
92
+ * this line, but every extension re-declared this interface itself and I wanted it
93
+ * in one place.
94
+ * @returns The base node in this tree.
95
+ */
96
+ public get block ( ) : Root { return this . root ; }
97
+
98
+ public abstract lookup ( path : string , errLoc ?: SourceLocation ) : AnyNode | undefined ;
99
+
100
+ /**
101
+ * Sets the base node that this node inherits from. Must be of same type.
102
+ * @prop base Self The new base node.
103
+ */
85
104
public setBase ( base : Self ) {
86
105
this . _base = base ;
87
106
}
@@ -92,6 +111,7 @@ export abstract class Inheritable<
92
111
* other relationships to this object.
93
112
*
94
113
* If nothing is inherited, this returns an empty array.
114
+ * @returns The array of nodes this node inherits from.
95
115
*/
96
116
public resolveInheritance ( ) : Self [ ] {
97
117
let inherited : Self [ ] = [ ] ;
@@ -107,6 +127,7 @@ export abstract class Inheritable<
107
127
* Resolves the child with the given name from this node's inheritance
108
128
* chain. Returns undefined if the child is not found.
109
129
* @param name The name of the child to resolve.
130
+ * @returns The child node, or `null`
110
131
*/
111
132
public resolveChild ( name : string ) : Child | null {
112
133
let state : Child | null = this . getChild ( name ) ;
@@ -118,32 +139,32 @@ export abstract class Inheritable<
118
139
return state || null ;
119
140
}
120
141
121
- // /**
122
- // * Find the closest common ancestor Block between two Styles
123
- // * TODO: I think there is a more efficient way to do this.
124
- // * @param relative Style to compare ancestry with.
125
- // * @returns The Style's common Block ancestor, or null.
126
- // */
127
- // commonAncestor(relative: Style<Child>): Style<Child> | null {
128
- // let blockChain = new Set(...this.block.rootClass.resolveInheritance()); // lol
129
- // blockChain.add(this.block.rootClass);
130
- // let common = [relative.block.rootClass, ...relative.block.rootClass.resolveInheritance()].filter(b => blockChain.has(b));
131
- // return common.length ? common[0] as Style<Child> : null;
132
- // }
133
-
134
142
/**
135
- * Given a parent that is a base class of this style, retrieve this style's
136
- * base style from it, if it exists. This method does not traverse into base styles.
143
+ * Retrieve a child node from this object at `key`.
144
+ * @param key string The key to fetch the child object from.
145
+ * @returns The child node.
137
146
*/
138
147
public getChild ( key : string ) : Child | null {
139
148
return this . _children . get ( key ) || null ;
140
149
}
141
150
151
+ /**
152
+ * Set a child node on this object at `key`.
153
+ * @param key string The key to set the child object to.
154
+ * @returns The child node.
155
+ */
142
156
public setChild ( key : string , value : Child ) : Child {
143
157
this . _children . set ( key , value ) ;
144
158
return value ;
145
159
}
146
160
161
+ /**
162
+ * Ensure a child node exists on this object at `key`. If it does not, create it.
163
+ * If `key` is not provided, use the child name as the key.
164
+ * @param name string The name of this object to ensure.
165
+ * @param key string The key at which this child object should be (optional)
166
+ * @returns The child node.
167
+ */
147
168
public ensureChild ( name : string , key ?: string ) : Child {
148
169
key = key !== undefined ? key : name ;
149
170
if ( ! this . _children . has ( name ) ) {
@@ -152,15 +173,27 @@ export abstract class Inheritable<
152
173
return this . _children . get ( key ) as Child ;
153
174
}
154
175
176
+ /**
177
+ * Returns an array of all children nodes in the order they were added.
178
+ * @returns The children array.
179
+ */
155
180
public children ( ) : Child [ ] {
156
181
return [ ...this . _children . values ( ) ] ;
157
182
}
158
183
184
+ /**
185
+ * Returns a map of all children nodes at the keys they are stored..
186
+ * @returns The children map.
187
+ */
159
188
public childrenMap ( ) : Map < string , Child > {
160
189
return new Map ( this . _children ) ;
161
190
}
162
191
163
- // TODO: Cache this maybe? Convert entire model to only use hash?...
192
+ /**
193
+ * Returns a hash of all children nodes at the keys they are stored..
194
+ * TODO: Cache this maybe? Convert entire model to only use hash?...
195
+ * @returns The children hash.
196
+ */
164
197
public childrenHash ( ) : ObjectDictionary < Child > {
165
198
let out = { } ;
166
199
for ( let [ key , value ] of this . _children . entries ( ) ) {
0 commit comments