Newer
Older
TheVengeance-Project-IADE-Unity2D / Assets / Ink / InkLibs / InkCompiler / ParsedHierarchy / Object.cs
  1. using System.Collections.Generic;
  2. using System.Text;
  3. namespace Ink.Parsed
  4. {
  5. public abstract class Object
  6. {
  7. public Runtime.DebugMetadata debugMetadata {
  8. get {
  9. if (_debugMetadata == null) {
  10. if (parent) {
  11. return parent.debugMetadata;
  12. }
  13. }
  14. return _debugMetadata;
  15. }
  16. set {
  17. _debugMetadata = value;
  18. }
  19. }
  20. private Runtime.DebugMetadata _debugMetadata;
  21. public bool hasOwnDebugMetadata {
  22. get {
  23. return _debugMetadata != null;
  24. }
  25. }
  26. public virtual string typeName {
  27. get {
  28. return GetType().Name;
  29. }
  30. }
  31. public Parsed.Object parent { get; set; }
  32. public List<Parsed.Object> content { get; protected set; }
  33. public Parsed.Story story {
  34. get {
  35. Parsed.Object ancestor = this;
  36. while (ancestor.parent) {
  37. ancestor = ancestor.parent;
  38. }
  39. return ancestor as Parsed.Story;
  40. }
  41. }
  42. private Runtime.Object _runtimeObject;
  43. public Runtime.Object runtimeObject
  44. {
  45. get {
  46. if (_runtimeObject == null) {
  47. _runtimeObject = GenerateRuntimeObject ();
  48. if( _runtimeObject )
  49. _runtimeObject.debugMetadata = debugMetadata;
  50. }
  51. return _runtimeObject;
  52. }
  53. set {
  54. _runtimeObject = value;
  55. }
  56. }
  57. // virtual so that certian object types can return a different
  58. // path than just the path to the main runtimeObject.
  59. // e.g. a Choice returns a path to its content rather than
  60. // its outer container.
  61. public virtual Runtime.Path runtimePath
  62. {
  63. get {
  64. return runtimeObject.path;
  65. }
  66. }
  67. // When counting visits and turns since, different object
  68. // types may have different containers that needs to be counted.
  69. // For most it'll just be the object's main runtime object,
  70. // but for e.g. choices, it'll be the target container.
  71. public virtual Runtime.Container containerForCounting
  72. {
  73. get {
  74. return this.runtimeObject as Runtime.Container;
  75. }
  76. }
  77. public Parsed.Path PathRelativeTo(Parsed.Object otherObj)
  78. {
  79. var ownAncestry = ancestry;
  80. var otherAncestry = otherObj.ancestry;
  81. Parsed.Object highestCommonAncestor = null;
  82. int minLength = System.Math.Min (ownAncestry.Count, otherAncestry.Count);
  83. for (int i = 0; i < minLength; ++i) {
  84. var a1 = ancestry [i];
  85. var a2 = otherAncestry [i];
  86. if (a1 == a2)
  87. highestCommonAncestor = a1;
  88. else
  89. break;
  90. }
  91. FlowBase commonFlowAncestor = highestCommonAncestor as FlowBase;
  92. if (commonFlowAncestor == null)
  93. commonFlowAncestor = highestCommonAncestor.ClosestFlowBase ();
  94. var pathComponents = new List<Identifier> ();
  95. bool hasWeavePoint = false;
  96. FlowLevel baseFlow = FlowLevel.WeavePoint;
  97. var ancestor = this;
  98. while(ancestor && (ancestor != commonFlowAncestor) && !(ancestor is Story)) {
  99. if (ancestor == commonFlowAncestor)
  100. break;
  101. if (!hasWeavePoint) {
  102. var weavePointAncestor = ancestor as IWeavePoint;
  103. if (weavePointAncestor != null && weavePointAncestor.identifier != null) {
  104. pathComponents.Add (weavePointAncestor.identifier);
  105. hasWeavePoint = true;
  106. continue;
  107. }
  108. }
  109. var flowAncestor = ancestor as FlowBase;
  110. if (flowAncestor) {
  111. pathComponents.Add (flowAncestor.identifier);
  112. baseFlow = flowAncestor.flowLevel;
  113. }
  114. ancestor = ancestor.parent;
  115. }
  116. pathComponents.Reverse ();
  117. if (pathComponents.Count > 0) {
  118. return new Path (baseFlow, pathComponents);
  119. }
  120. return null;
  121. }
  122. public List<Parsed.Object> ancestry
  123. {
  124. get {
  125. var result = new List<Parsed.Object> ();
  126. var ancestor = this.parent;
  127. while(ancestor) {
  128. result.Add (ancestor);
  129. ancestor = ancestor.parent;
  130. }
  131. result.Reverse ();
  132. return result;
  133. }
  134. }
  135. public string descriptionOfScope
  136. {
  137. get {
  138. var locationNames = new List<string> ();
  139. Parsed.Object ancestor = this;
  140. while (ancestor) {
  141. var ancestorFlow = ancestor as FlowBase;
  142. if (ancestorFlow && ancestorFlow.identifier != null) {
  143. locationNames.Add ("'" + ancestorFlow.identifier + "'");
  144. }
  145. ancestor = ancestor.parent;
  146. }
  147. var scopeSB = new StringBuilder ();
  148. if (locationNames.Count > 0) {
  149. var locationsListStr = string.Join (", ", locationNames.ToArray());
  150. scopeSB.Append (locationsListStr);
  151. scopeSB.Append (" and ");
  152. }
  153. scopeSB.Append ("at top scope");
  154. return scopeSB.ToString ();
  155. }
  156. }
  157. // Return the object so that method can be chained easily
  158. public T AddContent<T>(T subContent) where T : Parsed.Object
  159. {
  160. if (content == null) {
  161. content = new List<Parsed.Object> ();
  162. }
  163. // Make resilient to content not existing, which can happen
  164. // in the case of parse errors where we've already reported
  165. // an error but still want a valid structure so we can
  166. // carry on parsing.
  167. if( subContent ) {
  168. subContent.parent = this;
  169. content.Add(subContent);
  170. }
  171. return subContent;
  172. }
  173. public void AddContent<T>(List<T> listContent) where T : Parsed.Object
  174. {
  175. foreach (var obj in listContent) {
  176. AddContent (obj);
  177. }
  178. }
  179. public T InsertContent<T>(int index, T subContent) where T : Parsed.Object
  180. {
  181. if (content == null) {
  182. content = new List<Parsed.Object> ();
  183. }
  184. subContent.parent = this;
  185. content.Insert (index, subContent);
  186. return subContent;
  187. }
  188. public delegate bool FindQueryFunc<T>(T obj);
  189. public T Find<T>(FindQueryFunc<T> queryFunc = null) where T : class
  190. {
  191. var tObj = this as T;
  192. if (tObj != null && (queryFunc == null || queryFunc (tObj) == true)) {
  193. return tObj;
  194. }
  195. if (content == null)
  196. return null;
  197. foreach (var obj in content) {
  198. var nestedResult = obj.Find (queryFunc);
  199. if (nestedResult != null)
  200. return nestedResult;
  201. }
  202. return null;
  203. }
  204. public List<T> FindAll<T>(FindQueryFunc<T> queryFunc = null) where T : class
  205. {
  206. var found = new List<T> ();
  207. FindAll (queryFunc, found);
  208. return found;
  209. }
  210. void FindAll<T>(FindQueryFunc<T> queryFunc, List<T> foundSoFar) where T : class
  211. {
  212. var tObj = this as T;
  213. if (tObj != null && (queryFunc == null || queryFunc (tObj) == true)) {
  214. foundSoFar.Add (tObj);
  215. }
  216. if (content == null)
  217. return;
  218. foreach (var obj in content) {
  219. obj.FindAll (queryFunc, foundSoFar);
  220. }
  221. }
  222. public abstract Runtime.Object GenerateRuntimeObject ();
  223. public virtual void ResolveReferences(Story context)
  224. {
  225. if (content != null) {
  226. foreach(var obj in content) {
  227. obj.ResolveReferences (context);
  228. }
  229. }
  230. }
  231. public FlowBase ClosestFlowBase()
  232. {
  233. var ancestor = this.parent;
  234. while (ancestor) {
  235. if (ancestor is FlowBase) {
  236. return (FlowBase)ancestor;
  237. }
  238. ancestor = ancestor.parent;
  239. }
  240. return null;
  241. }
  242. public virtual void Error(string message, Parsed.Object source = null, bool isWarning = false)
  243. {
  244. if (source == null) {
  245. source = this;
  246. }
  247. // Only allow a single parsed object to have a single error *directly* associated with it
  248. if (source._alreadyHadError && !isWarning) {
  249. return;
  250. }
  251. if (source._alreadyHadWarning && isWarning) {
  252. return;
  253. }
  254. if (this.parent) {
  255. this.parent.Error (message, source, isWarning);
  256. } else {
  257. throw new System.Exception ("No parent object to send error to: "+message);
  258. }
  259. if (isWarning) {
  260. source._alreadyHadWarning = true;
  261. } else {
  262. source._alreadyHadError = true;
  263. }
  264. }
  265. public void Warning(string message, Parsed.Object source = null)
  266. {
  267. Error (message, source, isWarning: true);
  268. }
  269. // Allow implicit conversion to bool so you don't have to do:
  270. // if( myObj != null ) ...
  271. public static implicit operator bool (Object obj)
  272. {
  273. var isNull = object.ReferenceEquals (obj, null);
  274. return !isNull;
  275. }
  276. public static bool operator ==(Object a, Object b)
  277. {
  278. return object.ReferenceEquals (a, b);
  279. }
  280. public static bool operator !=(Object a, Object b)
  281. {
  282. return !(a == b);
  283. }
  284. public override bool Equals (object obj)
  285. {
  286. return object.ReferenceEquals (obj, this);
  287. }
  288. public override int GetHashCode ()
  289. {
  290. return base.GetHashCode ();
  291. }
  292. bool _alreadyHadError;
  293. bool _alreadyHadWarning;
  294. }
  295. }