using System.Xml.Linq;
using System.Collections.Generic;
+using Monodoc.Ecma;
+
namespace Monodoc.Providers
{
+ public enum EcmaNodeType {
+ Invalid,
+ Namespace,
+ Type,
+ Member,
+ Meta, // A node that's here to serve as a header for other node
+ }
+
// Common functionality between ecma-provider and ecmauncompiled-provider
internal class EcmaDoc
{
+ static EcmaUrlParser parser = new EcmaUrlParser ();
+
public static void PopulateTreeFromIndexFile (string indexFilePath,
+ string idPrefix,
Tree tree,
IDocStorage storage,
Dictionary<string, XElement> nsSummaries,
nsElements.Add (ExtractClassSummary (typeFilePath));
var typeCaption = EcmaDoc.GetTypeCaptionFromIndex (type);
- var url = "ecma:" + id + '#' + typeCaption + '/';
+ var url = idPrefix + id + '#' + typeCaption + '/';
typeCaption = EcmaDoc.GetTypeCaptionFromIndex (type, true);
var typeNode = nsNode.CreateNode (typeCaption, url);
return caption;
}
+ public static Node MatchNodeWithEcmaUrl (string url, Tree tree)
+ {
+ Node result = null;
+ EcmaDesc desc;
+ if (!parser.TryParse (url, out desc))
+ return null;
+
+ // Namespace search
+ Node currentNode = tree.RootNode;
+ Node searchNode = new Node () { Caption = desc.Namespace };
+ int index = currentNode.Nodes.BinarySearch (searchNode, EcmaGenericNodeComparer.Instance);
+ if (index >= 0)
+ result = currentNode.Nodes[index];
+ if (desc.DescKind == EcmaDesc.Kind.Namespace || index < 0)
+ return result;
+
+ // Type search
+ currentNode = result;
+ result = null;
+ searchNode.Caption = desc.ToCompleteTypeName ();
+ index = currentNode.Nodes.BinarySearch (searchNode, EcmaTypeNodeComparer.Instance);
+ if (index >= 0)
+ result = currentNode.Nodes[index];
+ if ((desc.DescKind == EcmaDesc.Kind.Type && !desc.IsEtc) || index < 0)
+ return result;
+
+ // Member selection
+ currentNode = result;
+ result = null;
+ var caption = desc.IsEtc ? EtcKindToCaption (desc.Etc) : MemberKindToCaption (desc.DescKind);
+ currentNode = FindNodeForCaption (currentNode.Nodes, caption);
+ if (currentNode == null
+ || (desc.IsEtc && desc.DescKind == EcmaDesc.Kind.Type && string.IsNullOrEmpty (desc.EtcFilter)))
+ return currentNode;
+
+ // Member search
+ result = null;
+ var format = desc.DescKind == EcmaDesc.Kind.Constructor ? EcmaDesc.Format.WithArgs : EcmaDesc.Format.WithoutArgs;
+ searchNode.Caption = desc.ToCompleteMemberName (format);
+ index = currentNode.Nodes.BinarySearch (searchNode, EcmaGenericNodeComparer.Instance);
+ if (index < 0)
+ return null;
+ result = currentNode.Nodes[index];
+ if (result.Nodes.Count == 0 || desc.IsEtc)
+ return result;
+
+ // Overloads search
+ currentNode = result;
+ searchNode.Caption = desc.ToCompleteMemberName (EcmaDesc.Format.WithArgs);
+ index = currentNode.Nodes.BinarySearch (searchNode, EcmaGenericNodeComparer.Instance);
+ if (index < 0)
+ return result;
+ result = result.Nodes[index];
+
+ return result;
+ }
+
+ // This comparer returns the answer straight from caption comparison
+ class EcmaGenericNodeComparer : IComparer<Node>
+ {
+ public static readonly EcmaGenericNodeComparer Instance = new EcmaGenericNodeComparer ();
+
+ public int Compare (Node n1, Node n2)
+ {
+ return string.Compare (n1.Caption, n2.Caption, StringComparison.Ordinal);
+ }
+ }
+
+ // This comparer take into account the space in the caption
+ class EcmaTypeNodeComparer : IComparer<Node>
+ {
+ public static readonly EcmaTypeNodeComparer Instance = new EcmaTypeNodeComparer ();
+
+ public int Compare (Node n1, Node n2)
+ {
+ int length1 = CaptionLength (n1.Caption);
+ int length2 = CaptionLength (n2.Caption);
+
+ return string.Compare (n1.Caption, 0, n2.Caption, 0, Math.Max (length1, length2), StringComparison.Ordinal);
+ }
+
+ int CaptionLength (string caption)
+ {
+ var length = caption.LastIndexOf (' ');
+ return length == -1 ? caption.Length : length;
+ }
+ }
+
+ public static Dictionary<string, string> GetContextForEcmaNode (string hash, string sourceID, Node node)
+ {
+ var args = new Dictionary<string, string> ();
+
+ args["source-id"] = sourceID;
+
+ if (node != null) {
+ var nodeType = GetNodeType (node);
+ switch (nodeType) {
+ case EcmaNodeType.Namespace:
+ args["show"] = "namespace";
+ args["namespace"] = node.Element.Substring ("N:".Length);
+ break;
+ case EcmaNodeType.Type:
+ args["show"] = "typeoverview";
+ break;
+ case EcmaNodeType.Member:
+ case EcmaNodeType.Meta:
+ switch (GetNodeMemberTypeChar (node)){
+ case 'C':
+ args["membertype"] = "Constructor";
+ break;
+ case 'M':
+ args["membertype"] = "Method";
+ break;
+ case 'P':
+ args["membertype"] = "Property";
+ break;
+ case 'F':
+ args["membertype"] = "Field";
+ break;
+ case 'E':
+ args["membertype"] = "Event";
+ break;
+ case 'O':
+ args["membertype"] = "Operator";
+ break;
+ case 'X':
+ args["membertype"] = "ExtensionMethod";
+ break;
+ case '*':
+ args["membertype"] = "All";
+ break;
+ }
+
+ if (nodeType == EcmaNodeType.Meta) {
+ args["show"] = "members";
+ args["index"] = "all";
+ } else {
+ args["show"] = "member";
+ args["index"] = node.Element;
+ }
+ break;
+ }
+ }
+
+ if (!string.IsNullOrEmpty (hash))
+ args["hash"] = hash;
+
+ return args;
+ }
+
+ public static EcmaNodeType GetNodeType (Node node)
+ {
+ // We guess the node type by checking the depth level it's at in the tree
+ int level = GetNodeLevel (node);
+ switch (level) {
+ case 0:
+ return EcmaNodeType.Namespace;
+ case 1:
+ return EcmaNodeType.Type;
+ case 2:
+ return EcmaNodeType.Meta;
+ case 3: // Here it's either a member or, in case of overload, a meta
+ return node.IsLeaf ? EcmaNodeType.Member : EcmaNodeType.Meta;
+ case 4: // At this level, everything is necessarily a member
+ return EcmaNodeType.Member;
+ default:
+ return EcmaNodeType.Invalid;
+ }
+ }
+
+ public static char GetNodeMemberTypeChar (Node node)
+ {
+ int level = GetNodeLevel (node);
+ // We try to reach the member group node depending on node nested level
+ switch (level) {
+ case 2:
+ return node.Element[0];
+ case 3:
+ return node.Parent.Element[0];
+ case 4:
+ return node.Parent.Parent.Element[0];
+ default:
+ throw new ArgumentException ("node", "Couldn't determine member type of node `" + node.Caption + "'");
+ }
+ }
+
+ public static int GetNodeLevel (Node node)
+ {
+ int i = 0;
+ for (; !node.Element.StartsWith ("root:/", StringComparison.OrdinalIgnoreCase); i++)
+ node = node.Parent;
+ return i - 1;
+ }
+
+ public static string EtcKindToCaption (char etc)
+ {
+ switch (etc) {
+ case 'M':
+ return "Methods";
+ case 'P':
+ return "Properties";
+ case 'C':
+ return "Constructors";
+ case 'F':
+ return "Fields";
+ case 'E':
+ return "Events";
+ case 'O':
+ return "Operators";
+ case '*':
+ return "Members";
+ default:
+ return null;
+ }
+ }
+
+ public static string MemberKindToCaption (EcmaDesc.Kind kind)
+ {
+ switch (kind) {
+ case EcmaDesc.Kind.Method:
+ return "Methods";
+ case EcmaDesc.Kind.Property:
+ return "Properties";
+ case EcmaDesc.Kind.Constructor:
+ return "Constructors";
+ case EcmaDesc.Kind.Field:
+ return "Fields";
+ case EcmaDesc.Kind.Event:
+ return "Events";
+ case EcmaDesc.Kind.Operator:
+ return "Operators";
+ default:
+ return null;
+ }
+ }
+
+ public static Node FindNodeForCaption (List<Node> nodes, string caption)
+ {
+ foreach (var node in nodes)
+ if (node.Caption.Equals (caption, StringComparison.OrdinalIgnoreCase))
+ return node;
+ return null;
+ }
+
internal static string MakeOperatorSignature (XElement member, out string memberSignature)
{
string name = (string)member.Attribute ("MemberName");
namespace Monodoc.Providers
{
- public enum EcmaNodeType {
- Invalid,
- Namespace,
- Type,
- Member,
- Meta, // A node that's here to serve as a header for other node
- }
-
public class EcmaProvider : Provider
{
HashSet<string> directories = new HashSet<string> ();
continue;
}
- EcmaDoc.PopulateTreeFromIndexFile (indexFilePath, tree, storage, nsSummaries, _ => resID++.ToString ());
+ EcmaDoc.PopulateTreeFromIndexFile (indexFilePath, EcmaHelpSource.EcmaPrefix, tree, storage, nsSummaries, _ => resID++.ToString ());
}
foreach (var summary in nsSummaries)
public class EcmaHelpSource : HelpSource
{
- const string EcmaPrefix = "ecma:";
+ internal const string EcmaPrefix = "ecma:";
EcmaUrlParser parser = new EcmaUrlParser ();
LRUCache<string, Node> cache = new LRUCache<string, Node> (4);
public override string GetPublicUrl (Node node)
{
string url = string.Empty;
- var type = GetNodeType (node);
+ var type = EcmaDoc.GetNodeType (node);
//Console.WriteLine ("GetPublicUrl {0} : {1} [{2}]", node.Element, node.Caption, type.ToString ());
switch (type) {
case EcmaNodeType.Namespace:
case EcmaNodeType.Meta:
return MakeTypeNodeUrl (GetNodeTypeParent (node)) + GenerateMetaSuffix (node);
case EcmaNodeType.Member:
- var typeChar = GetNodeMemberTypeChar (node);
+ var typeChar = EcmaDoc.GetNodeMemberTypeChar (node);
var parentNode = GetNodeTypeParent (node);
var typeNode = MakeTypeNodeUrl (parentNode).Substring (2);
return typeChar + ":" + typeNode + MakeMemberNodeUrl (typeChar, node);
return "." + caption;
}
- EcmaNodeType GetNodeType (Node node)
- {
- // We guess the node type by checking the depth level it's at in the tree
- int level = GetNodeLevel (node);
- switch (level) {
- case 0:
- return EcmaNodeType.Namespace;
- case 1:
- return EcmaNodeType.Type;
- case 2:
- return EcmaNodeType.Meta;
- case 3: // Here it's either a member or, in case of overload, a meta
- return node.IsLeaf ? EcmaNodeType.Member : EcmaNodeType.Meta;
- case 4: // At this level, everything is necessarily a member
- return EcmaNodeType.Member;
- default:
- return EcmaNodeType.Invalid;
- }
- }
-
- int GetNodeLevel (Node node)
- {
- int i = 0;
- for (; !node.Element.StartsWith ("root:/", StringComparison.OrdinalIgnoreCase); i++) {
- //Console.WriteLine ("\tLevel {0} : {1} {2}", i, node.Element, node.Caption);
- node = node.Parent;
- }
- return i - 1;
- }
-
- char GetNodeMemberTypeChar (Node node)
- {
- int level = GetNodeLevel (node);
- // We try to reach the member group node depending on node nested level
- switch (level) {
- case 2:
- return node.Element[0];
- case 3:
- return node.Parent.Element[0];
- case 4:
- return node.Parent.Parent.Element[0];
- default:
- throw new ArgumentException ("node", "Couldn't determine member type of node `" + node.Caption + "'");
- }
- }
-
Node GetNodeTypeParent (Node node)
{
// Type nodes are always at level 2 so we just need to get there
{
string suffix = string.Empty;
// A meta node has always a type element to begin with
- while (GetNodeType (node) != EcmaNodeType.Type) {
+ while (EcmaDoc.GetNodeType (node) != EcmaNodeType.Type) {
suffix = '/' + node.Element + suffix;
node = node.Parent;
}
{
Node node = null;
if ((node = cache.Get (url)) == null) {
- node = InternalMatchNode (url);
+ node = EcmaDoc.MatchNodeWithEcmaUrl (url, Tree);
if (node != null)
cache.Put (url, node);
}
return node;
}
- public Node InternalMatchNode (string url)
- {
- Node result = null;
- EcmaDesc desc;
- if (!parser.TryParse (url, out desc))
- return null;
-
- // Namespace search
- Node currentNode = Tree.RootNode;
- Node searchNode = new Node () { Caption = desc.Namespace };
- int index = currentNode.Nodes.BinarySearch (searchNode, EcmaGenericNodeComparer.Instance);
- if (index >= 0)
- result = currentNode.Nodes[index];
- if (desc.DescKind == EcmaDesc.Kind.Namespace || index < 0)
- return result;
-
- // Type search
- currentNode = result;
- result = null;
- searchNode.Caption = desc.ToCompleteTypeName ();
- index = currentNode.Nodes.BinarySearch (searchNode, EcmaTypeNodeComparer.Instance);
- if (index >= 0)
- result = currentNode.Nodes[index];
- if ((desc.DescKind == EcmaDesc.Kind.Type && !desc.IsEtc) || index < 0)
- return result;
-
- // Member selection
- currentNode = result;
- result = null;
- var caption = desc.IsEtc ? EtcKindToCaption (desc.Etc) : MemberKindToCaption (desc.DescKind);
- currentNode = FindNodeForCaption (currentNode.Nodes, caption);
- if (currentNode == null
- || (desc.IsEtc && desc.DescKind == EcmaDesc.Kind.Type && string.IsNullOrEmpty (desc.EtcFilter)))
- return currentNode;
-
- // Member search
- result = null;
- var format = desc.DescKind == EcmaDesc.Kind.Constructor ? EcmaDesc.Format.WithArgs : EcmaDesc.Format.WithoutArgs;
- searchNode.Caption = desc.ToCompleteMemberName (format);
- index = currentNode.Nodes.BinarySearch (searchNode, EcmaGenericNodeComparer.Instance);
- if (index < 0)
- return null;
- result = currentNode.Nodes[index];
- if (result.Nodes.Count == 0 || desc.IsEtc)
- return result;
-
- // Overloads search
- currentNode = result;
- searchNode.Caption = desc.ToCompleteMemberName (EcmaDesc.Format.WithArgs);
- index = currentNode.Nodes.BinarySearch (searchNode, EcmaGenericNodeComparer.Instance);
- if (index < 0)
- return result;
- result = result.Nodes[index];
-
- return result;
- }
-
- // This comparer returns the answer straight from caption comparison
- class EcmaGenericNodeComparer : IComparer<Node>
- {
- public static readonly EcmaGenericNodeComparer Instance = new EcmaGenericNodeComparer ();
-
- public int Compare (Node n1, Node n2)
- {
- return string.Compare (n1.Caption, n2.Caption, StringComparison.Ordinal);
- }
- }
-
- // This comparer take into account the space in the caption
- class EcmaTypeNodeComparer : IComparer<Node>
- {
- public static readonly EcmaTypeNodeComparer Instance = new EcmaTypeNodeComparer ();
-
- public int Compare (Node n1, Node n2)
- {
- int length1 = CaptionLength (n1.Caption);
- int length2 = CaptionLength (n2.Caption);
-
- return string.Compare (n1.Caption, 0, n2.Caption, 0, Math.Max (length1, length2), StringComparison.Ordinal);
- }
-
- int CaptionLength (string caption)
- {
- var length = caption.LastIndexOf (' ');
- return length == -1 ? caption.Length : length;
- }
- }
-
- string EtcKindToCaption (char etc)
- {
- switch (etc) {
- case 'M':
- return "Methods";
- case 'P':
- return "Properties";
- case 'C':
- return "Constructors";
- case 'F':
- return "Fields";
- case 'E':
- return "Events";
- case 'O':
- return "Operators";
- case '*':
- return "Members";
- default:
- return null;
- }
- }
-
- string MemberKindToCaption (EcmaDesc.Kind kind)
- {
- switch (kind) {
- case EcmaDesc.Kind.Method:
- return "Methods";
- case EcmaDesc.Kind.Property:
- return "Properties";
- case EcmaDesc.Kind.Constructor:
- return "Constructors";
- case EcmaDesc.Kind.Field:
- return "Fields";
- case EcmaDesc.Kind.Event:
- return "Events";
- case EcmaDesc.Kind.Operator:
- return "Operators";
- default:
- return null;
- }
- }
-
- Node FindNodeForCaption (List<Node> nodes, string caption)
- {
- foreach (var node in nodes)
- if (node.Caption.Equals (caption, StringComparison.OrdinalIgnoreCase))
- return node;
- return null;
- }
-
- string GetArgs (string hash, Node node)
- {
- var args = new Dictionary<string, string> ();
-
- args["source-id"] = SourceID.ToString ();
-
- if (node != null) {
- var nodeType = GetNodeType (node);
- switch (nodeType) {
- case EcmaNodeType.Namespace:
- args["show"] = "namespace";
- args["namespace"] = node.Element.Substring ("N:".Length);
- break;
- case EcmaNodeType.Type:
- args["show"] = "typeoverview";
- break;
- case EcmaNodeType.Member:
- case EcmaNodeType.Meta:
- switch (GetNodeMemberTypeChar (node)){
- case 'C':
- args["membertype"] = "Constructor";
- break;
- case 'M':
- args["membertype"] = "Method";
- break;
- case 'P':
- args["membertype"] = "Property";
- break;
- case 'F':
- args["membertype"] = "Field";
- break;
- case 'E':
- args["membertype"] = "Event";
- break;
- case 'O':
- args["membertype"] = "Operator";
- break;
- case 'X':
- args["membertype"] = "ExtensionMethod";
- break;
- case '*':
- args["membertype"] = "All";
- break;
- }
-
- if (nodeType == EcmaNodeType.Meta) {
- args["show"] = "members";
- args["index"] = "all";
- } else {
- args["show"] = "member";
- args["index"] = node.Element;
- }
- break;
- }
- }
-
- if (!string.IsNullOrEmpty (hash))
- args["hash"] = hash;
-
- return "?" + string.Join ("&", args.Select (kvp => kvp.Key == kvp.Value ? kvp.Key : kvp.Key + '=' + kvp.Value));
- }
-
public override void PopulateIndex (IndexMaker index_maker)
{
foreach (Node ns_node in Tree.RootNode.Nodes){
}
SearchableDocument doc_nod = searchDoc.Reset ();
- doc_nod.Title = LargeName (nc) + " " + EtcKindToCaption (c.Caption[0]);
+ doc_nod.Title = LargeName (nc) + " " + EcmaDoc.EtcKindToCaption (c.Caption[0]);
doc_nod.FullTitle = ns_node.Caption + '.' + typename + "::" + nc.Caption;
doc_nod.HotText = string.Empty;