using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace Ink.Runtime
{
///
/// Base class for all ink runtime content.
///
public /* TODO: abstract */ class Object
{
///
/// Runtime.Objects can be included in the main Story as a hierarchy.
/// Usually parents are Container objects. (TODO: Always?)
///
/// The parent.
public Runtime.Object parent { get; set; }
public Runtime.DebugMetadata debugMetadata {
get {
if (_debugMetadata == null) {
if (parent) {
return parent.debugMetadata;
}
}
return _debugMetadata;
}
set {
_debugMetadata = value;
}
}
public Runtime.DebugMetadata ownDebugMetadata {
get {
return _debugMetadata;
}
}
// 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;
public int? DebugLineNumberOfPath(Path path)
{
if (path == null)
return null;
// Try to get a line number from debug metadata
var root = this.rootContentContainer;
if (root) {
Runtime.Object targetContent = root.ContentAtPath (path).obj;
if (targetContent) {
var dm = targetContent.debugMetadata;
if (dm != null) {
return dm.startLineNumber;
}
}
}
return null;
}
public 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 ();
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;
public SearchResult 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.GetComponent(0).isParent);
path = path.tail;
}
return nearestContainer.ContentAtPath (path);
} else {
return this.rootContentContainer.ContentAtPath (path);
}
}
public 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.length, ownPath.length);
int lastSharedPathCompIndex = -1;
for (int i = 0; i < minPathLength; ++i) {
var ownComp = ownPath.GetComponent(i);
var otherComp = globalPath.GetComponent(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.length-1) - lastSharedPathCompIndex;
var newPathComps = new List ();
for(int up=0; up(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 ();
}
}
}