-
Notifications
You must be signed in to change notification settings - Fork 499
/
Copy pathObject.cs
244 lines (199 loc) · 7.72 KB
/
Object.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace Ink.Runtime
{
/// <summary>
/// Base class for all ink runtime content.
/// </summary>
public /* TODO: abstract */ class Object
{
/// <summary>
/// Runtime.Objects can be included in the main Story as a hierarchy.
/// Usually parents are Container objects. (TODO: Always?)
/// </summary>
/// <value>The parent.</value>
public Runtime.Object parent { get; set; }
internal Runtime.DebugMetadata debugMetadata {
get {
if (_debugMetadata == null) {
if (parent) {
return parent.debugMetadata;
}
}
return _debugMetadata;
}
set {
_debugMetadata = value;
}
}
// TODO: Come up with some clever solution for not having
// to have debug metadata on the object itself, perhaps
// for serialisation purposes at least.
DebugMetadata _debugMetadata;
internal int? DebugLineNumberOfPath(Path path)
{
if (path == null)
return null;
// Try to get a line number from debug metadata
var root = this.rootContentContainer;
if (root) {
var targetContent = root.ContentAtPath (path);
if (targetContent) {
var dm = targetContent.debugMetadata;
if (dm != null) {
return dm.startLineNumber;
}
}
}
return null;
}
internal Path path
{
get
{
if (_path == null) {
if (parent == null) {
_path = new Path ();
} else {
// Maintain a Stack so that the order of the components
// is reversed when they're added to the Path.
// We're iterating up the hierarchy from the leaves/children to the root.
var comps = new Stack<Path.Component> ();
var child = this;
Container container = child.parent as Container;
while (container) {
var namedChild = child as INamedContent;
if (namedChild != null && namedChild.hasValidName) {
comps.Push (new Path.Component (namedChild.name));
} else {
comps.Push (new Path.Component (container.content.IndexOf(child)));
}
child = container;
container = container.parent as Container;
}
_path = new Path (comps);
}
}
return _path;
}
}
Path _path;
internal Runtime.Object ResolvePath(Path path)
{
if (path.isRelative) {
Container nearestContainer = this as Container;
if (!nearestContainer) {
Debug.Assert (this.parent != null, "Can't resolve relative path because we don't have a parent");
nearestContainer = this.parent as Container;
Debug.Assert (nearestContainer != null, "Expected parent to be a container");
Debug.Assert (path.components [0].isParent);
path = path.tail;
}
return nearestContainer.ContentAtPath (path);
} else {
return this.rootContentContainer.ContentAtPath (path);
}
}
internal Path ConvertPathToRelative(Path globalPath)
{
// 1. Find last shared ancestor
// 2. Drill up using ".." style (actually represented as "^")
// 3. Re-build downward chain from common ancestor
var ownPath = this.path;
int minPathLength = Math.Min (globalPath.components.Count, ownPath.components.Count);
int lastSharedPathCompIndex = -1;
for (int i = 0; i < minPathLength; ++i) {
var ownComp = ownPath.components [i];
var otherComp = globalPath.components [i];
if (ownComp.Equals (otherComp)) {
lastSharedPathCompIndex = i;
} else {
break;
}
}
// No shared path components, so just use global path
if (lastSharedPathCompIndex == -1)
return globalPath;
int numUpwardsMoves = (ownPath.components.Count-1) - lastSharedPathCompIndex;
var newPathComps = new List<Path.Component> ();
for(int up=0; up<numUpwardsMoves; ++up)
newPathComps.Add (Path.Component.ToParent ());
for (int down = lastSharedPathCompIndex + 1; down < globalPath.components.Count; ++down)
newPathComps.Add (globalPath.components [down]);
var relativePath = new Path (newPathComps, relative:true);
return relativePath;
}
// Find most compact representation for a path, whether relative or global
internal string CompactPathString(Path otherPath)
{
string globalPathStr = null;
string relativePathStr = null;
if (otherPath.isRelative) {
relativePathStr = otherPath.componentsString;
globalPathStr = this.path.PathByAppendingPath(otherPath).componentsString;
} else {
var relativePath = ConvertPathToRelative (otherPath);
relativePathStr = relativePath.componentsString;
globalPathStr = otherPath.componentsString;
}
if (relativePathStr.Length < globalPathStr.Length)
return relativePathStr;
else
return globalPathStr;
}
internal Container rootContentContainer
{
get
{
Runtime.Object ancestor = this;
while (ancestor.parent) {
ancestor = ancestor.parent;
}
return ancestor as Container;
}
}
internal Object ()
{
}
public virtual Object Copy()
{
throw new System.NotImplementedException (GetType ().Name + " doesn't support copying");
}
internal void SetChild<T>(ref T obj, T value) where T : Runtime.Object
{
if (obj)
obj.parent = null;
obj = value;
if( obj )
obj.parent = this;
}
/// Allow implicit conversion to bool so you don't have to do:
/// if( myObj != null ) ...
public static implicit operator bool (Object obj)
{
var isNull = object.ReferenceEquals (obj, null);
return !isNull;
}
/// Required for implicit bool comparison
public static bool operator ==(Object a, Object b)
{
return object.ReferenceEquals (a, b);
}
/// Required for implicit bool comparison
public static bool operator !=(Object a, Object b)
{
return !(a == b);
}
/// Required for implicit bool comparison
public override bool Equals (object obj)
{
return object.ReferenceEquals (obj, this);
}
/// Required for implicit bool comparison
public override int GetHashCode ()
{
return base.GetHashCode ();
}
}
}