Xamarin Public Jenkins (auto-signing) 536cd135cc Imported Upstream version 5.4.0.167
Former-commit-id: 5624ac747d633e885131e8349322922b6a59baaa
2017-08-21 15:34:15 +00:00

385 lines
8.3 KiB
C#

using System;
using System.IO;
using System.Text;
using System.Linq;
using System.Xml;
using System.Collections;
using System.Collections.Generic;
namespace Monodoc
{
public
#if LEGACY_MODE
partial
#endif
class Node : IComparable<Node>, IComparable
{
readonly Tree parentTree;
string caption, element, pubUrl;
public bool Documented;
bool loaded;
Node parent;
List<Node> nodes;
#if LEGACY_MODE
ArrayList legacyNodes;
#endif
Dictionary<string, Node> childrenLookup;
bool elementSort;
/* Address has three types of value,
* _ 0 is for no on-disk representation
* _ >0 is a valid address that is loaded immediately
* _ <0 is a valid negated address to indicate lazy loading
*/
int address;
#if LEGACY_MODE
[Obsolete ("Tree inheriting Node is being phased out. Use the `Tree.RootNode' property instead")]
public Node (string caption, string element)
{
this.parentTree = (Tree) this;
this.caption = caption;
this.element = element;
parent = null;
}
#endif
public Node (Node parent, string caption, string element) : this (parent.Tree, caption, element)
{
this.parent = parent;
}
internal Node (Tree tree, string caption, string element)
{
this.parentTree = tree;
this.caption = caption;
this.element = element;
this.elementSort = parentTree.HelpSource != null && parentTree.HelpSource.SortType == SortType.Element;
}
/// <summary>
/// Creates a node from an on-disk representation
/// </summary>
internal Node (Node parent, int address) : this (parent.parentTree, address)
{
this.parent = parent;
}
internal Node (Tree tree, int address)
{
this.address = address;
this.parentTree = tree;
this.elementSort = parentTree.HelpSource != null && parentTree.HelpSource.SortType == SortType.Element;
if (address > 0)
LoadNode ();
}
/* This is solely used for MatchNode to check for equality */
internal Node ()
{
}
void LoadNode ()
{
parentTree.InflateNode (this);
if (parent != null)
parent.RegisterFullNode (this);
}
public void AddNode (Node n)
{
nodes.Add (n);
n.parent = this;
n.Documented = true;
RegisterFullNode (n);
}
public void DeleteNode (Node n)
{
nodes.Remove (n);
if (!string.IsNullOrEmpty (n.element))
childrenLookup.Remove (n.element);
}
// When a child node is inflated, it calls this method
// so that we can add it to our lookup for quick search
void RegisterFullNode (Node child)
{
if (childrenLookup == null)
childrenLookup = new Dictionary<string, Node> ();
if (!string.IsNullOrEmpty (child.element))
childrenLookup[child.element] = child;
}
#if LEGACY_MODE
[Obsolete ("Use ChildNodes")]
public ArrayList Nodes {
get {
if (legacyNodes == null)
legacyNodes = new ArrayList (ChildNodes as ICollection);
return legacyNodes;
}
}
#endif
public IList<Node> ChildNodes {
get {
EnsureLoaded ();
return nodes != null ? nodes : new List<Node> ();
}
}
public string Element {
get {
EnsureLoaded ();
return element;
}
set {
element = value;
}
}
public string Caption {
get {
EnsureLoaded ();
return caption;
}
internal set {
caption = value;
}
}
public Node Parent {
get {
return parent;
}
}
public Tree Tree {
get {
return parentTree;
}
}
internal int Address {
get {
return address;
}
#if LEGACY_MODE
set {
address = value;
}
#endif
}
/// <summary>
/// Creates a new node, in the locator entry point, and with
/// a user visible caption of @caption
/// </summary>
public Node CreateNode (string c_caption, string c_element)
{
EnsureNodes ();
if (string.IsNullOrEmpty (c_caption))
throw new ArgumentNullException ("c_caption");
if (string.IsNullOrEmpty (c_element))
throw new ArgumentNullException ("c_element");
Node t = new Node (this, c_caption, c_element);
nodes.Add (t);
childrenLookup[c_element] = t;
return t;
}
public Node GetOrCreateNode (string c_caption, string c_element)
{
if (nodes == null)
return CreateNode (c_caption, c_element);
if (childrenLookup.Count != nodes.Count || (nodes.Count == 0 && childrenLookup.Count != nodes.Capacity))
UpdateLookup ();
Node result;
if (!childrenLookup.TryGetValue (c_element, out result))
result = CreateNode (c_caption, c_element);
return result;
}
public void EnsureNodes ()
{
if (nodes == null) {
nodes = new List<Node> ();
childrenLookup = new Dictionary<string, Node> ();
}
}
public void EnsureLoaded ()
{
if (address < 0 && !loaded) {
LoadNode ();
loaded = true;
}
}
void UpdateLookup ()
{
foreach (var node in nodes)
childrenLookup[node.Element] = node;
}
public bool IsLeaf {
get {
return nodes == null || nodes.Count == 0;
}
}
void EncodeInt (BinaryWriter writer, int value)
{
do {
int high = (value >> 7) & 0x01ffffff;
byte b = (byte)(value & 0x7f);
if (high != 0) {
b = (byte)(b | 0x80);
}
writer.Write(b);
value = high;
} while(value != 0);
}
int DecodeInt (BinaryReader reader)
{
int ret = 0;
int shift = 0;
byte b;
do {
b = reader.ReadByte();
ret = ret | ((b & 0x7f) << shift);
shift += 7;
} while ((b & 0x80) == 0x80);
return ret;
}
internal void Deserialize (BinaryReader reader)
{
int count = DecodeInt (reader);
element = reader.ReadString ();
caption = reader.ReadString ();
if (count == 0)
return;
nodes = new List<Node> (count);
for (int i = 0; i < count; i++) {
int child_address = DecodeInt (reader);
Node t = new Node (this, -child_address);
nodes.Add (t);
}
if (parentTree.ForceResort)
nodes.Sort ();
}
internal void Serialize (FileStream output, BinaryWriter writer)
{
if (nodes != null)
foreach (Node child in nodes)
child.Serialize (output, writer);
address = (int) output.Position;
EncodeInt (writer, nodes == null ? 0 : (int) nodes.Count);
writer.Write (element);
writer.Write (caption);
if (nodes != null)
foreach (Node child in nodes)
EncodeInt (writer, child.address);
}
public void Sort ()
{
if (nodes != null)
nodes.Sort ();
}
internal string GetInternalUrl ()
{
EnsureLoaded ();
if (element.IndexOf (":") != -1 || parent == null)
return element;
var parentUrl = parent.GetInternalUrl ();
return parentUrl.EndsWith ("/") ? parentUrl + element : parentUrl + "/" + element;
}
public string PublicUrl {
get {
if (pubUrl != null)
return pubUrl;
return pubUrl = parentTree.HelpSource != null ? parentTree.HelpSource.GetPublicUrl (this) : GetInternalUrl ();
}
}
int IComparable.CompareTo (object obj)
{
Node other = obj as Node;
if (other == null)
return -1;
return CompareToInternal (other);
}
int IComparable<Node>.CompareTo (Node obj)
{
return CompareToInternal (obj);
}
int CompareToInternal (Node other)
{
EnsureLoaded ();
other.EnsureLoaded ();
var cap1 = elementSort ? element : caption;
var cap2 = elementSort ? other.element : other.caption;
/* Some node (notably from ecmaspec) have number prepended to them
* which we need to sort better by padding them to the same number
* of digits
*/
if (char.IsDigit (cap1[0]) && char.IsDigit (cap2[0])) {
int c1 = cap1.TakeWhile (char.IsDigit).Count ();
int c2 = cap2.TakeWhile (char.IsDigit).Count ();
if (c1 != c2) {
cap1 = cap1.PadLeft (cap1.Length + Math.Max (0, c2 - c1), '0');
cap2 = cap2.PadLeft (cap2.Length + Math.Max (0, c1 - c2), '0');
}
}
return string.Compare (cap1, cap2, StringComparison.Ordinal);
}
}
internal static class IListExtensions
{
// TODO: if the backing store ever change from List<T>, we need to tune these methods to have a fallback mechanism
public static int BinarySearch<T> (this IList<T> ilist, T item)
{
var list = ilist as List<T>;
if (list == null)
throw new NotSupportedException ();
return list.BinarySearch (item);
}
public static int BinarySearch<T> (this IList<T> ilist, T item, IComparer<T> comparer)
{
var list = ilist as List<T>;
if (list == null)
throw new NotSupportedException ();
return list.BinarySearch (item, comparer);
}
}
}