transient.ts 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. /**
  2. * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. import { Map as ImmutableMap, OrderedSet } from 'immutable';
  7. import { Transform } from '../transform';
  8. import { StateTree } from './immutable';
  9. import { StateTreeBuilder } from './builder';
  10. import { StateObjectCell } from 'mol-state/object';
  11. import { shallowEqual } from 'mol-util/object';
  12. export { TransientTree }
  13. class TransientTree implements StateTree {
  14. transforms = this.tree.transforms as ImmutableMap<Transform.Ref, Transform>;
  15. children = this.tree.children as ImmutableMap<Transform.Ref, OrderedSet<Transform.Ref>>;
  16. cellStates = this.tree.cellStates as ImmutableMap<Transform.Ref, StateObjectCell.State>;
  17. private changedNodes = false;
  18. private changedChildren = false;
  19. private changedStates = false;
  20. private _childMutations: Map<Transform.Ref, OrderedSet<Transform.Ref>> | undefined = void 0;
  21. private get childMutations() {
  22. if (this._childMutations) return this._childMutations;
  23. this._childMutations = new Map();
  24. return this._childMutations;
  25. }
  26. private changeStates() {
  27. if (this.changedStates) return;
  28. this.changedStates = true;
  29. this.cellStates = this.cellStates.asMutable();
  30. }
  31. private changeNodes() {
  32. if (this.changedNodes) return;
  33. this.changedNodes = true;
  34. this.transforms = this.transforms.asMutable();
  35. }
  36. private changeChildren() {
  37. if (this.changedChildren) return;
  38. this.changedChildren = true;
  39. this.children = this.children.asMutable();
  40. }
  41. get root() { return this.transforms.get(Transform.RootRef)! }
  42. build(): StateTreeBuilder.Root {
  43. return new StateTreeBuilder.Root(this);
  44. }
  45. asTransient() {
  46. return this.asImmutable().asTransient();
  47. }
  48. private addChild(parent: Transform.Ref, child: Transform.Ref) {
  49. this.changeChildren();
  50. if (this.childMutations.has(parent)) {
  51. this.childMutations.get(parent)!.add(child);
  52. } else {
  53. const set = (this.children.get(parent) as OrderedSet<Transform.Ref>).asMutable();
  54. set.add(child);
  55. this.children.set(parent, set);
  56. this.childMutations.set(parent, set);
  57. }
  58. }
  59. private removeChild(parent: Transform.Ref, child: Transform.Ref) {
  60. this.changeChildren();
  61. if (this.childMutations.has(parent)) {
  62. this.childMutations.get(parent)!.remove(child);
  63. } else {
  64. const set = (this.children.get(parent) as OrderedSet<Transform.Ref>).asMutable();
  65. set.remove(child);
  66. this.children.set(parent, set);
  67. this.childMutations.set(parent, set);
  68. }
  69. }
  70. private clearRoot() {
  71. const parent = Transform.RootRef;
  72. if (this.children.get(parent).size === 0) return;
  73. this.changeChildren();
  74. const set = OrderedSet<Transform.Ref>();
  75. this.children.set(parent, set);
  76. this.childMutations.set(parent, set);
  77. }
  78. add(transform: Transform, initialState?: Partial<StateObjectCell.State>) {
  79. const ref = transform.ref;
  80. if (this.transforms.has(transform.ref)) {
  81. const node = this.transforms.get(transform.ref);
  82. if (node.parent !== transform.parent) alreadyPresent(transform.ref);
  83. }
  84. const children = this.children.get(transform.parent);
  85. if (!children) parentNotPresent(transform.parent);
  86. if (!children.has(transform.ref)) {
  87. this.addChild(transform.parent, transform.ref);
  88. }
  89. if (!this.children.has(transform.ref)) {
  90. if (!this.changedChildren) {
  91. this.changedChildren = true;
  92. this.children = this.children.asMutable();
  93. }
  94. this.children.set(transform.ref, OrderedSet());
  95. }
  96. this.changeNodes();
  97. this.transforms.set(ref, transform);
  98. if (!this.cellStates.has(ref)) {
  99. this.changeStates();
  100. if (StateObjectCell.isStateChange(StateObjectCell.DefaultState, initialState)) {
  101. this.cellStates.set(ref, { ...StateObjectCell.DefaultState, ...initialState });
  102. } else {
  103. this.cellStates.set(ref, StateObjectCell.DefaultState);
  104. }
  105. }
  106. return this;
  107. }
  108. /** Calls Transform.definition.params.areEqual if available, otherwise uses shallowEqual to check if the params changed */
  109. setParams(ref: Transform.Ref, params: unknown) {
  110. ensurePresent(this.transforms, ref);
  111. const transform = this.transforms.get(ref)!;
  112. const def = transform.transformer.definition;
  113. if (def.params && def.params.areEqual) {
  114. if (def.params.areEqual(transform.params, params)) return false;
  115. } else {
  116. if (shallowEqual(transform.params, params)) {
  117. return false;
  118. }
  119. }
  120. if (!this.changedNodes) {
  121. this.changedNodes = true;
  122. this.transforms = this.transforms.asMutable();
  123. }
  124. this.transforms.set(transform.ref, Transform.withParams(transform, params));
  125. return true;
  126. }
  127. updateCellState(ref: Transform.Ref, state: Partial<StateObjectCell.State>) {
  128. ensurePresent(this.transforms, ref);
  129. const old = this.cellStates.get(ref);
  130. if (!StateObjectCell.isStateChange(old, state)) return false;
  131. this.changeStates();
  132. this.cellStates.set(ref, { ...old, ...state });
  133. return true;
  134. }
  135. remove(ref: Transform.Ref): Transform[] {
  136. const node = this.transforms.get(ref);
  137. if (!node) return [];
  138. const st = StateTree.subtreePostOrder(this, node);
  139. if (ref === Transform.RootRef) {
  140. st.pop();
  141. if (st.length === 0) return st;
  142. this.clearRoot();
  143. } else {
  144. if (st.length === 0) return st;
  145. this.removeChild(node.parent, node.ref);
  146. }
  147. this.changeNodes();
  148. this.changeChildren();
  149. this.changeStates();
  150. for (const n of st) {
  151. this.transforms.delete(n.ref);
  152. this.children.delete(n.ref);
  153. this.cellStates.delete(n.ref);
  154. if (this._childMutations) this._childMutations.delete(n.ref);
  155. }
  156. return st;
  157. }
  158. asImmutable() {
  159. if (!this.changedNodes && !this.changedChildren && !this.changedStates && !this._childMutations) return this.tree;
  160. if (this._childMutations) this._childMutations.forEach(fixChildMutations, this.children);
  161. return StateTree.create(
  162. this.changedNodes ? this.transforms.asImmutable() : this.transforms,
  163. this.changedChildren ? this.children.asImmutable() : this.children,
  164. this.changedStates ? this.cellStates.asImmutable() : this.cellStates);
  165. }
  166. constructor(private tree: StateTree) {
  167. }
  168. }
  169. function fixChildMutations(this: ImmutableMap<Transform.Ref, OrderedSet<Transform.Ref>>, m: OrderedSet<Transform.Ref>, k: Transform.Ref) { this.set(k, m.asImmutable()); }
  170. function alreadyPresent(ref: Transform.Ref) {
  171. throw new Error(`Transform '${ref}' is already present in the tree.`);
  172. }
  173. function parentNotPresent(ref: Transform.Ref) {
  174. throw new Error(`Parent '${ref}' must be present in the tree.`);
  175. }
  176. function ensurePresent(nodes: StateTree.Transforms, ref: Transform.Ref) {
  177. if (!nodes.has(ref)) {
  178. throw new Error(`Node '${ref}' is not present in the tree.`);
  179. }
  180. }