--- /dev/null
+using System;
+using System.Linq;
+using System.Text;
+using System.Collections.Generic;
+
+namespace MonkeyDoc.Ecma
+{
+ /* Some properties might not be filled/meaningful depending on kind
+ * like a namespace EcmaUrl won't have a valid TypeName
+ */
+ public class EcmaDesc : IEquatable<EcmaDesc>
+ {
+ public enum Kind
+ {
+ Type,
+ Constructor,
+ Method,
+ Namespace,
+ Field,
+ Property,
+ Event,
+ Operator
+ }
+
+ public enum Mod
+ {
+ Normal,
+ Pointer,
+ Ref,
+ Out
+ }
+
+ public enum Format
+ {
+ WithArgs,
+ WithoutArgs
+ }
+
+ public Kind DescKind {
+ get;
+ set;
+ }
+
+ public Mod DescModifier {
+ get;
+ set;
+ }
+
+ public string Namespace {
+ get;
+ set;
+ }
+
+ public string TypeName {
+ get;
+ set;
+ }
+
+ public string MemberName {
+ get;
+ set;
+ }
+
+ public EcmaDesc NestedType {
+ get;
+ set;
+ }
+
+ /* A list of the array dimensions attached to this type.
+ * The list count corresponds to the number of recursive
+ * array definition (jagged arrays) the value of the
+ * corresponding list item is the number of dimension
+ * attached to that array definition instance
+ */
+ public IList<int> ArrayDimensions {
+ get;
+ set;
+ }
+
+ /* Depending on the form of the url, we might not have the type
+ * of the argument but only how many the type/member has i.e.
+ * when such number is specified with a backtick
+ */
+ public IList<EcmaDesc> GenericTypeArguments {
+ get;
+ set;
+ }
+
+ public IList<EcmaDesc> GenericMemberArguments {
+ get;
+ set;
+ }
+
+ public IList<EcmaDesc> MemberArguments {
+ get;
+ set;
+ }
+
+ /* This indicates that we actually want an inner part of the ecmadesc
+ * i.e. in case of T: we could want the members (*), ctor (C), methods (M), ...
+ */
+ public char Etc {
+ get;
+ set;
+ }
+
+ public bool IsEtc {
+ get {
+ return Etc != (char)0;
+ }
+ }
+
+ /* EtcFilter is only valid in some case of IsEtc when the inner part needs
+ * to be further filtered e.g. in case we want a listing of the type overloads
+ * Equals
+ */
+ public string EtcFilter {
+ get;
+ set;
+ }
+
+ /* When a member is an explicit implementation of an interface member, we register
+ * the member EcmaDesc with its interface parent here
+ */
+ public EcmaDesc ExplicitImplMember {
+ get;
+ set;
+ }
+
+ // Returns the TypeName and the generic/inner type information if existing
+ public string ToCompleteTypeName (char innerTypeSeparator = '.')
+ {
+ var result = TypeName;
+ if (GenericTypeArguments != null)
+ result += FormatGenericArgs (GenericTypeArguments);
+ if (NestedType != null)
+ result += innerTypeSeparator + NestedType.ToCompleteTypeName ();
+ if (ArrayDimensions != null && ArrayDimensions.Count > 0)
+ result += ArrayDimensions.Select (dim => "[" + new string (',', dim - 1) + "]").Aggregate (string.Concat);
+
+ return result;
+ }
+
+ // Returns the member name with its generic types if existing
+ public string ToCompleteMemberName (Format format)
+ {
+ /* We special process two cases:
+ * - Explicit member implementation which append a full type specification
+ * - Conversion operator which are exposed as normal method but have specific captioning in the end
+ */
+ if (ExplicitImplMember != null) {
+ var impl = ExplicitImplMember;
+ return impl.FormattedNamespace + impl.ToCompleteTypeName () + "." + impl.ToCompleteMemberName (format);
+ } else if (format == Format.WithArgs && DescKind == Kind.Operator && MemberName.EndsWith ("Conversion")) {
+ var type1 = MemberArguments[0].FormattedNamespace + MemberArguments[0].ToCompleteTypeName () + ModToString (MemberArguments[0]);
+ var type2 = MemberArguments[1].FormattedNamespace + MemberArguments[1].ToCompleteTypeName () + ModToString (MemberArguments[1]);
+ return type1 + " to " + type2;
+ }
+
+ var result = IsEtc && !string.IsNullOrEmpty (EtcFilter) ? EtcFilter : MemberName;
+
+ // Temporary hack for monodoc produced inner type ctor
+ //if (DescKind == Kind.Constructor && NestedType != null)
+ //result = ToCompleteTypeName ();
+
+ if (GenericMemberArguments != null)
+ result += FormatGenericArgs (GenericMemberArguments);
+
+ if (format == Format.WithArgs) {
+ result += '(';
+ if (MemberArguments != null && MemberArguments.Count > 0) {
+ var args = MemberArguments.Select (a => FormatNamespace (a) + a.ToCompleteTypeName ('+') + ModToString (a));
+ result += string.Join (",", args);
+ }
+ result += ')';
+ }
+
+ return result;
+ }
+
+ public string ToEcmaCref ()
+ {
+ var sb = new StringBuilder ();
+ // Cref type
+ sb.Append (DescKind.ToString ()[0]);
+ // Create the rest
+ ConstructCRef (sb);
+
+ return sb.ToString ();
+ }
+
+ void ConstructCRef (StringBuilder sb)
+ {
+ sb.Append (Namespace);
+ if (DescKind == Kind.Namespace)
+ return;
+
+ sb.Append ('.');
+ sb.Append (TypeName);
+ if (GenericTypeArguments != null) {
+ sb.Append ('<');
+ foreach (var t in GenericTypeArguments)
+ t.ConstructCRef (sb);
+ sb.Append ('>');
+ }
+ if (NestedType != null) {
+ sb.Append ('+');
+ NestedType.ConstructCRef (sb);
+ }
+ if (ArrayDimensions != null && ArrayDimensions.Count > 0) {
+ for (int i = 0; i < ArrayDimensions.Count; i++) {
+ sb.Append ('[');
+ sb.Append (new string (',', ArrayDimensions[i] - 1));
+ sb.Append (']');
+ }
+ }
+ if (DescKind == Kind.Type)
+ return;
+
+ if (MemberArguments != null) {
+
+ }
+ }
+
+ public override string ToString ()
+ {
+ return string.Format ("({8}) {0}::{1}{2}{3}{7} {4}{5}{6} {9} {10}",
+ Namespace,
+ TypeName,
+ FormatGenericArgsFull (GenericTypeArguments),
+ NestedType != null ? "+" + NestedType.ToString () : string.Empty,
+ MemberName ?? string.Empty,
+ FormatGenericArgsFull (GenericMemberArguments),
+ MemberArguments != null ? "(" + string.Join (",", MemberArguments.Select (m => m.ToString ())) + ")" : string.Empty,
+ ArrayDimensions != null && ArrayDimensions.Count > 0 ? ArrayDimensions.Select (dim => "[" + new string (',', dim - 1) + "]").Aggregate (string.Concat) : string.Empty,
+ DescKind.ToString ()[0],
+ Etc != 0 ? '(' + Etc.ToString () + ')' : string.Empty,
+ ExplicitImplMember != null ? "$" + ExplicitImplMember.ToString () : string.Empty);
+
+ }
+
+ public override bool Equals (object other)
+ {
+ var otherDesc = other as EcmaDesc;
+ return otherDesc != null && Equals (otherDesc);
+ }
+
+ public bool Equals (EcmaDesc other)
+ {
+ if (other == null)
+ return false;
+
+ if (NestedType == null ^ other.NestedType == null
+ || ArrayDimensions == null ^ other.ArrayDimensions == null
+ || GenericTypeArguments == null ^ other.GenericTypeArguments == null
+ || GenericMemberArguments == null ^ other.GenericMemberArguments == null
+ || MemberArguments == null ^ other.MemberArguments == null
+ || ExplicitImplMember == null ^ other.ExplicitImplMember == null)
+ return false;
+
+ return other != null
+ && DescKind == other.DescKind
+ && TypeName == other.TypeName
+ && Namespace == other.Namespace
+ && MemberName == other.MemberName
+ && (NestedType == null || NestedType.Equals (other.NestedType))
+ && (ArrayDimensions == null || ArrayDimensions.SequenceEqual (other.ArrayDimensions))
+ && (GenericTypeArguments == null || GenericTypeArguments.SequenceEqual (other.GenericTypeArguments))
+ && (GenericMemberArguments == null || GenericMemberArguments.SequenceEqual (other.GenericMemberArguments))
+ && (MemberArguments == null || MemberArguments.SequenceEqual (other.MemberArguments))
+ && Etc == other.Etc
+ && EtcFilter == other.EtcFilter
+ && (ExplicitImplMember == null || ExplicitImplMember.Equals (other.ExplicitImplMember));
+ }
+
+ public override int GetHashCode ()
+ {
+ return DescKind.GetHashCode ()
+ ^ TypeName.GetHashCode ()
+ ^ Namespace.GetHashCode ()
+ ^ MemberName.GetHashCode ();
+ }
+
+ bool What (bool input)
+ {
+ if (!input)
+ throw new Exception ("Not equal");
+ return input;
+ }
+
+ bool WhatT (bool input)
+ {
+ if (input)
+ throw new Exception ("Not equal");
+ return input;
+ }
+
+ string FormatNamespace (EcmaDesc desc)
+ {
+ return string.IsNullOrEmpty (desc.Namespace) ? string.Empty : desc.Namespace + ".";
+ }
+
+ string FormatGenericArgs (IEnumerable<EcmaDesc> genericArgs)
+ {
+ return genericArgs != null ? "<" + string.Join (",", genericArgs.Select (t => FormatNamespace (t) + t.ToCompleteTypeName ())) + ">" : string.Empty;
+ }
+
+ string FormatGenericArgsFull (IEnumerable<EcmaDesc> genericArgs)
+ {
+ return genericArgs != null ? "<" + string.Join (",", genericArgs.Select (t => t.ToString ())) + ">" : string.Empty;
+ }
+
+ string ModToString (EcmaDesc desc)
+ {
+ switch (desc.DescModifier) {
+ case Mod.Pointer:
+ return "*";
+ case Mod.Ref:
+ return "&";
+ case Mod.Out:
+ return "@";
+ default:
+ return string.Empty;
+ }
+ }
+
+ string FormattedNamespace {
+ get {
+ return !string.IsNullOrEmpty (Namespace) ? Namespace + "." : string.Empty;
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+%{
+using System.Text;
+using System.IO;
+using System;
+using System.Linq;
+using System.Collections.Generic;
+
+namespace MonkeyDoc.Ecma
+{
+ public class EcmaUrlParser
+ {
+ int yacc_verbose_flag = 0;
+
+ public void IsValid (string input)
+ {
+ var lexer = new EcmaUrlTokenizer (input);
+ this.yyparse (lexer);
+ }
+
+ public EcmaDesc Parse (string input)
+ {
+ var lexer = new EcmaUrlTokenizer (input);
+ return (EcmaDesc)this.yyparse (lexer);
+ }
+
+ public bool TryParse (string input, out EcmaDesc desc)
+ {
+ desc = null;
+ try {
+ desc = Parse (input);
+ } catch {
+ return false;
+ }
+ return true;
+ }
+
+ EcmaDesc SetEcmaDescType (object result, EcmaDesc.Kind kind)
+ {
+ var desc = result as EcmaDesc;
+ desc.DescKind = kind;
+ return desc;
+ }
+
+ List<T> SafeReverse<T> (List<T> input)
+ {
+ if (input == null)
+ return null;
+ input.Reverse ();
+ return input;
+ }
+%}
+
+%token ERROR
+%token IDENTIFIER
+%token DIGIT
+%token DOT
+%token COMMA
+%token COLON
+%token INNER_TYPE_SEPARATOR
+%token OP_GENERICS_LT
+%token OP_GENERICS_GT
+%token OP_GENERICS_BACKTICK
+%token OP_OPEN_PAREN
+%token OP_CLOSE_PAREN
+%token OP_ARRAY_OPEN
+%token OP_ARRAY_CLOSE
+%token SLASH_SEPARATOR
+%token STAR
+%token REF_ARG
+%token OUT_ARG
+%token EXPLICIT_IMPL_SEP
+
+%start expression
+
+%%
+
+expression
+ : 'T' COLON type_expression { $$ = SetEcmaDescType ($3, EcmaDesc.Kind.Type); }
+ | 'N' COLON namespace_expression { $$ = SetEcmaDescType ($3, EcmaDesc.Kind.Namespace); }
+ | 'M' COLON method_expression { $$ = SetEcmaDescType ($3, EcmaDesc.Kind.Method); }
+ | 'F' COLON simple_member_expression { $$ = SetEcmaDescType ($3, EcmaDesc.Kind.Field); }
+ | 'C' COLON constructor_expression { $$ = SetEcmaDescType ($3, EcmaDesc.Kind.Constructor); }
+ | 'P' COLON property_expression { $$ = SetEcmaDescType ($3, EcmaDesc.Kind.Property); }
+ | 'E' COLON simple_member_expression { $$ = SetEcmaDescType ($3, EcmaDesc.Kind.Event); }
+ | 'O' COLON operator_expression { $$ = SetEcmaDescType ($3, EcmaDesc.Kind.Operator); }
+
+/* i.e. id.id.id or id */
+dot_expression
+ : IDENTIFIER { $$ = new List<string> { (string)$1 }; }
+ | IDENTIFIER DOT dot_expression { ((ICollection<string>)$3).Add ((string)$1); $$ = $3; }
+
+namespace_expression
+ : dot_expression { $$ = new EcmaDesc { Namespace = string.Join (".", ((IEnumerable<string>)$1).Reverse ()) }; }
+
+type_expression
+ : dot_expression type_expression_suffix {
+ var dotExpr = ((List<string>)$1);
+ dotExpr.Reverse ();
+ var desc = $2 as EcmaDesc;
+ desc.DescKind = EcmaDesc.Kind.Type;
+ desc.Namespace = string.Join (".", dotExpr.Take (dotExpr.Count - 1));
+ desc.TypeName = dotExpr.Last ();
+ $$ = desc;
+ }
+
+/* To be used in types with no namespaces attached to them like an inner type*/
+reduced_type_expression
+ : IDENTIFIER type_expression_suffix {
+ var desc = $2 as EcmaDesc;
+ desc.DescKind = EcmaDesc.Kind.Type;
+ desc.TypeName = $1 as string;
+ $$ = desc;
+ }
+
+type_expression_suffix
+ : opt_generic_type_suffix opt_inner_type_description opt_array_definition opt_etc {
+ bool nestedDescHasEtc = $2 != null && ((EcmaDesc)$2).IsEtc;
+ EcmaDesc nestedType = (EcmaDesc)$2;
+ $$ = new EcmaDesc {
+ GenericTypeArguments = $1 as List<EcmaDesc>,
+ NestedType = nestedType,
+ ArrayDimensions = SafeReverse ($3 as List<int>),
+ Etc = $4 != null ? ((Tuple<char, string>)$4).Item1 : nestedDescHasEtc ? nestedType.Etc : (char)0,
+ EtcFilter = $4 != null ? ((Tuple<char, string>)$4).Item2 : nestedDescHasEtc ? nestedType.EtcFilter : null
+ };
+ if (nestedDescHasEtc) {
+ nestedType.Etc = (char)0;
+ nestedType.EtcFilter = null;
+ }
+ }
+
+opt_inner_type_description
+ : /* empty */ { $$ = null; }
+ | INNER_TYPE_SEPARATOR reduced_type_expression { $$ = $2; }
+
+opt_generic_type_suffix
+ : /* empty */ { $$ = null; }
+ | OP_GENERICS_BACKTICK DIGIT { $$ = Enumerable.Repeat<string> (null, (int)$2).ToList (); }
+ | OP_GENERICS_LT generic_type_arg_list OP_GENERICS_GT { $$ = $2; }
+
+generic_type_arg_list
+ : type_expression { $$ = new List<EcmaDesc> () { (EcmaDesc)$1 }; }
+ | generic_type_arg_list COMMA type_expression { ((List<EcmaDesc>)$1).Add ((EcmaDesc)$3); $$ = $1; }
+
+opt_array_definition
+ : /* empty */ { $$ = null; }
+ | OP_ARRAY_OPEN opt_array_definition_list OP_ARRAY_CLOSE opt_array_definition {
+ var dims = ((IList<int>)$4) ?? new List<int> (2);
+ dims.Add ((int)$2);
+ $$ = dims;
+ }
+
+opt_array_definition_list
+ : /* empty */ { $$ = 1; }
+ | COMMA opt_array_definition_list { $$ = ((int)$2) + 1; }
+
+opt_etc
+ : /* empty */ { $$ = null; }
+ | SLASH_SEPARATOR etc_identifier { $$ = Tuple.Create<char, string> (((string)$2)[0], null); }
+ | SLASH_SEPARATOR etc_identifier SLASH_SEPARATOR reduced_member_expression { $$ = Tuple.Create<char, string> (((string)$2)[0], (string)$4); }
+/* | SLASH_SEPARATOR etc_identifier SLASH_SEPARATOR IDENTIFIER opt_generic_type_suffix { $$ = Tuple.Create<char, string> (((string)$2)[0], (string)$4 + ($5 == null ? string.Empty : "<" + string.Join (",", ((IEnumerable<EcmaDesc>)$5).Select (t => t.ToCompleteTypeName ())) + ">")); } */
+
+etc_identifier
+ : STAR { $$ = "*"; }
+ | IDENTIFIER { $$ = $1; }
+
+method_expression
+ : type_expression DOT IDENTIFIER opt_generic_type_suffix opt_arg_list_suffix {
+ var desc = $1 as EcmaDesc;
+ desc.MemberName = $3 as string;
+ desc.GenericMemberArguments = $4 as List<EcmaDesc>;
+ desc.MemberArguments = SafeReverse ($5 as List<EcmaDesc>);
+ $$ = desc;
+ }
+ | dot_expression opt_generic_type_suffix opt_arg_list_suffix {
+ var dotExpr = ((List<string>)$1);
+ $$ = new EcmaDesc {
+ Namespace = string.Join (".", dotExpr.Skip (2).DefaultIfEmpty (string.Empty).Reverse ()),
+ TypeName = dotExpr.Skip (1).First (),
+ MemberName = dotExpr.First (),
+ GenericMemberArguments = $2 as List<EcmaDesc>,
+ MemberArguments = SafeReverse ($3 as List<EcmaDesc>)
+ };
+ }
+ | type_expression EXPLICIT_IMPL_SEP method_expression {
+ var desc = $1 as EcmaDesc;
+ desc.ExplicitImplMember = $3 as EcmaDesc;
+ $$ = desc;
+ }
+
+/* To be used with members that may have no type/namespace attached */
+reduced_member_expression
+ : IDENTIFIER opt_generic_type_suffix { $$ = (string)$1 + ($2 == null ? string.Empty : "<" + string.Join (",", ((IEnumerable<EcmaDesc>)$2).Select (t => t.ToCompleteTypeName ())) + ">"); }
+ | IDENTIFIER opt_generic_type_suffix DOT reduced_member_expression {
+ var existing = $4 as string;
+ var expr = (string)$1 + ($2 == null ? string.Empty : "<" + string.Join (",", ((IEnumerable<EcmaDesc>)$2).Select (t => t.ToCompleteTypeName ())) + ">");
+ $$ = expr + "." + existing;
+ }
+
+arg_type_expression
+ : type_expression opt_arg_type_suffix { var desc = (EcmaDesc)$1; desc.DescModifier = (EcmaDesc.Mod)$2; $$ = desc; }
+
+opt_arg_type_suffix
+ : /* empty */ { $$ = EcmaDesc.Mod.Normal; }
+ | STAR { $$ = EcmaDesc.Mod.Pointer; }
+ | REF_ARG { $$ = EcmaDesc.Mod.Ref; }
+ | OUT_ARG { $$ = EcmaDesc.Mod.Out; }
+
+type_expression_list
+ : /* empty */ { $$ = null; }
+ | arg_type_expression { $$ = new List<EcmaDesc> () { (EcmaDesc)$1 }; }
+ | arg_type_expression COMMA type_expression_list { ((List<EcmaDesc>)$3).Add ((EcmaDesc)$1); $$ = $3; }
+
+simple_member_expression
+ : dot_expression {
+ var dotExpr = ((List<string>)$1);
+ dotExpr.Reverse ();
+
+ $$ = new EcmaDesc {
+ Namespace = dotExpr.Count > 2 ? string.Join (".", dotExpr.Take (dotExpr.Count - 2)) : string.Empty,
+ TypeName = dotExpr.Count > 1 ? dotExpr[dotExpr.Count - 2] : string.Empty,
+ MemberName = dotExpr[dotExpr.Count - 1]
+ };
+ }
+ | type_expression DOT IDENTIFIER {
+ var desc = $1 as EcmaDesc;
+ desc.MemberName = $3 as string;
+ $$ = desc;
+ }
+ | type_expression EXPLICIT_IMPL_SEP simple_member_expression {
+ var desc = $1 as EcmaDesc;
+ desc.ExplicitImplMember = $3 as EcmaDesc;
+ $$ = desc;
+ }
+
+constructor_expression
+ : method_expression { $$ = $1; }
+
+operator_expression
+ : method_expression { $$ = $1; }
+
+property_expression
+ : simple_member_expression opt_property_indexer {
+ var desc = $1 as EcmaDesc;
+ (desc.ExplicitImplMember ?? desc).MemberArguments = SafeReverse ($2 as List<EcmaDesc>);
+ $$ = desc;
+ }
+
+opt_property_indexer
+ : opt_arg_list_suffix { $$ = $1; }
+
+/*simple_member_expression opt_arg_list_suffix { $$ = CopyFromEcmaDesc (new EcmaDesc {
+ MemberArguments = SafeReverse ($2 as List<EcmaDesc>)
+ }, (EcmaDesc)$1);
+ }*/
+
+opt_arg_list_suffix
+ : /* empty */ { $$ = null; }
+ | OP_OPEN_PAREN type_expression_list OP_CLOSE_PAREN { $$ = $2; }
+
+%%
+
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.IO;
+
+namespace MonkeyDoc.Ecma
+{
+ public class EcmaUrlParserDriver
+ {
+ public static void Main (string[] args)
+ {
+ var input = new StringReader (args[0]);
+ var lexer = new EcmaUrlTokenizer (input);
+ var parser = new EcmaUrlParser ();
+
+ Console.WriteLine (parser.yyparse (lexer));
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Text;
+using System.Globalization;
+
+namespace MonkeyDoc.Ecma
+{
+ public class EcmaUrlTokenizer : yyParser.yyInput
+ {
+ const char EndOfStream = (char)0;
+ string input;
+ object val;
+ int current_token;
+ int current_pos;
+ int real_current_pos;
+ int identCount = 0;
+
+ public EcmaUrlTokenizer (string input)
+ {
+ this.input = input;
+ }
+
+ static bool is_identifier_start_character (char c)
+ {
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || Char.IsLetter (c);
+ }
+
+ static bool is_identifier_part_character (char c)
+ {
+ if (c >= 'a' && c <= 'z')
+ return true;
+
+ if (c >= 'A' && c <= 'Z')
+ return true;
+
+ if (c == '_' || (c >= '0' && c <= '9'))
+ return true;
+
+ if (c < 0x80)
+ return false;
+
+ return Char.IsLetter (c) || Char.GetUnicodeCategory (c) == UnicodeCategory.ConnectorPunctuation;
+ }
+
+ public bool advance ()
+ {
+ return Peek () != EndOfStream;
+ }
+
+ public Object Value {
+ get {
+ return val;
+ }
+ }
+
+ public Object value ()
+ {
+ return val;
+ }
+
+ public int token ()
+ {
+ int token = xtoken ();
+ //Console.WriteLine ("Current token {0} with value {1}", token, val == null ? "(none)" : val.ToString ());
+ if (token == Token.ERROR)
+ Console.WriteLine ("Problem at pos {0} after token {1}", current_pos, current_token);
+ current_token = token;
+ return token;
+ }
+
+ int xtoken ()
+ {
+ char next = Read ();
+ while (char.IsWhiteSpace (next))
+ next = Read ();
+ current_pos++;
+ val = null;
+
+ switch (next) {
+ case ',':
+ return Token.COMMA;
+ case '.':
+ return Token.DOT;
+ case '<':
+ return Token.OP_GENERICS_LT;
+ case '>':
+ return Token.OP_GENERICS_GT;
+ case '`':
+ return Token.OP_GENERICS_BACKTICK;
+ case '(':
+ return Token.OP_OPEN_PAREN;
+ case ')':
+ return Token.OP_CLOSE_PAREN;
+ case '+':
+ return Token.INNER_TYPE_SEPARATOR;
+ case ':':
+ return Token.COLON;
+ case '/':
+ return Token.SLASH_SEPARATOR;
+ case '[':
+ return Token.OP_ARRAY_OPEN;
+ case ']':
+ return Token.OP_ARRAY_CLOSE;
+ case '*':
+ return Token.STAR;
+ case '&':
+ return Token.REF_ARG;
+ case '@':
+ return Token.OUT_ARG;
+ case '$':
+ return Token.EXPLICIT_IMPL_SEP;
+ default:
+ return TokenizeIdentifierOrNumber (next);
+ }
+ }
+
+ int TokenizeIdentifierOrNumber (char current)
+ {
+ // We must first return the expression type which is a uppercase letter and a colon
+ if (current_pos < 2) {
+ val = null;
+ return (int)current;
+ }
+
+ if (is_identifier_start_character (current) || current == '*') {
+ unsafe {
+ // identifier length is artificially limited to 1024 bytes by implementations
+ char* pIdent = stackalloc char[512];
+ *pIdent = current;
+ identCount = 1;
+
+ char peek;
+ while ((peek = Peek ()) != EndOfStream && is_identifier_part_character (peek)) {
+ *(pIdent + identCount) = Read ();
+ ++current_pos;
+ ++identCount;
+ }
+
+ val = new string ((char*)pIdent, 0, identCount);
+ return Token.IDENTIFIER;
+ }
+ } else if (char.IsDigit (current)) {
+ val = current - '0';
+ return Token.DIGIT;
+ } else {
+ val = null;
+ return Token.ERROR;
+ }
+ }
+
+ char Read ()
+ {
+ try {
+ return input[real_current_pos++];
+ } catch {
+ return EndOfStream;
+ }
+ }
+
+ char Peek ()
+ {
+ try {
+ return input[real_current_pos];
+ } catch {
+ return EndOfStream;
+ }
+ }
+ }
+}
--- /dev/null
+using System;
+using System.IO;
+using System.Linq;
+using System.Xml;
+using System.Diagnostics;
+using System.Collections.Generic;
+
+using Mono.Utilities;
+using Lucene.Net.Index;
+
+namespace MonkeyDoc
+{
+ //
+ // The HelpSource class keeps track of the archived data, and its
+ // tree
+ //
+ public class HelpSource
+ {
+ static int id;
+
+ //
+ // The unique ID for this HelpSource.
+ //
+ int source_id;
+
+ // The name of the HelpSource, used by all the file (.tree, .zip, ...) used by it
+ string name;
+ // The full directory path where the HelpSource files are located
+ string basePath;
+
+ // The tree of this help source
+ Tree tree;
+ string treeFilePath;
+ RootTree rootTree;
+
+ IDocCache cache;
+ IDocStorage storage;
+
+ public HelpSource (string base_filename, bool create)
+ {
+ this.name = Path.GetFileName (base_filename);
+ this.basePath = Path.GetDirectoryName (base_filename);
+ this.treeFilePath = base_filename + ".tree";
+ this.storage = new MonkeyDoc.Storage.ZipStorage (base_filename + ".zip");
+ this.cache = DocCacheHelper.GetDefaultCache (Name);
+
+ tree = create ? new Tree (this, string.Empty, string.Empty) : new Tree (this, treeFilePath);
+
+ source_id = id++;
+ }
+
+ public HelpSource ()
+ {
+ tree = new Tree (this, "Blah", "Blah");
+ source_id = id++;
+ }
+
+ public int SourceID {
+ get {
+ return source_id;
+ }
+ }
+
+ public string Name {
+ get {
+ return name;
+ }
+ }
+
+ /* This gives the full path of the source/ directory */
+ public string BaseFilePath {
+ get {
+ return basePath;
+ }
+ }
+
+ public TraceLevel TraceLevel {
+ get;
+ set;
+ }
+
+ public string BaseDir {
+ get {
+ return basePath;
+ }
+ }
+
+ public Tree Tree {
+ get {
+ return tree;
+ }
+ }
+
+ public RootTree RootTree {
+ get {
+ return rootTree;
+ }
+ set {
+ rootTree = value;
+ }
+ }
+
+ public IDocCache Cache {
+ get {
+ return cache;
+ }
+ }
+
+ public IDocStorage Storage {
+ get {
+ return storage;
+ }
+ protected set {
+ storage = value;
+ }
+ }
+
+ // A HelpSource may have a common prefix to its URL, give it here
+ protected virtual string UriPrefix {
+ get {
+ return "dummy:";
+ }
+ }
+
+ /// <summary>
+ /// Returns a stream from the packaged help source archive
+ /// </summary>
+ public virtual Stream GetHelpStream (string id)
+ {
+ return storage.Retrieve (id);
+ }
+
+ public virtual Stream GetCachedHelpStream (string id)
+ {
+ if (string.IsNullOrEmpty (id))
+ throw new ArgumentNullException ("id");
+ if (!cache.CanCache (DocEntity.Text))
+ return GetHelpStream (id);
+ if (!cache.IsCached (id))
+ cache.CacheText (id, GetHelpStream (id));
+ return cache.GetCachedStream (id);
+ }
+
+ public XmlReader GetHelpXml (string id)
+ {
+ var url = "monodoc:///" + SourceID + "@" + Uri.EscapeDataString (id) + "@";
+ var stream = cache.IsCached (id) ? cache.GetCachedStream (id) : storage.Retrieve (id);
+
+ return stream == null ? null : new XmlTextReader (url, stream);
+ }
+
+ public virtual XmlDocument GetHelpXmlWithChanges (string id)
+ {
+ XmlDocument doc = new XmlDocument ();
+ if (!storage.SupportRevision) {
+ doc.Load (GetHelpXml (id));
+ } else {
+ var revManager = storage.RevisionManager;
+ doc.Load (revManager.RetrieveLatestRevision (id));
+ }
+ return doc;
+ }
+
+ public virtual string GetCachedText (string id)
+ {
+ if (!cache.CanCache (DocEntity.Text))
+ return GetText (id);
+ if (!cache.IsCached (id))
+ cache.CacheText (id, GetText (id));
+ return cache.GetCachedString (id);
+ }
+
+ public virtual string GetText (string id)
+ {
+ return new StreamReader (GetHelpStream (id)).ReadToEnd ();
+ }
+
+ // Tells if the result for the provided id is generated dynamically
+ // by the help source
+ public virtual bool IsGeneratedContent (string id)
+ {
+ return false;
+ }
+
+ // Tells if the content of the provided id is meant to be returned raw
+ public virtual bool IsRawContent (string id)
+ {
+ return false;
+ }
+
+ // Tells if provided id refers to a multi-content-type document if it's case
+ // tells the ids it's formed of
+ public virtual bool IsMultiPart (string id, out IEnumerable<string> parts)
+ {
+ parts = null;
+ return false;
+ }
+
+ /// <summary>
+ /// Saves the tree and the archive
+ /// </summary>
+ public void Save ()
+ {
+ tree.Save (treeFilePath);
+ storage.Dispose ();
+ }
+
+ public virtual void RenderPreviewDocs (XmlNode newNode, XmlWriter writer)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public virtual string GetPublicUrl (Node node)
+ {
+ return node.GetInternalUrl ();
+ }
+
+ public virtual bool CanHandleUrl (string url)
+ {
+ return url.StartsWith (UriPrefix, StringComparison.OrdinalIgnoreCase);
+ }
+
+ public virtual string GetInternalIdForUrl (string url, out Node node)
+ {
+ node = MatchNode (url);
+ return node == null ? null : url.Substring (UriPrefix.Length);
+ }
+
+ public virtual Node MatchNode (string url)
+ {
+ Node current = null;
+
+ var matchCache = LRUCache<string, Node>.Default;
+ if ((current = matchCache.Get (url)) != null)
+ return current;
+
+ current = Tree.RootNode;
+ var strippedUrl = url.StartsWith (UriPrefix, StringComparison.OrdinalIgnoreCase) ? url.Substring (UriPrefix.Length) : url;
+ var searchNode = new Node () { Element = strippedUrl };
+
+ do {
+ int index = current.Nodes.BinarySearch (searchNode, NodeElementComparer.Instance);
+ if (index >= 0) {
+ Node n = current.Nodes[index];
+ //Console.WriteLine ("Binarysearch success for {0} which fell on {1}", strippedUrl, n.Element);
+ matchCache.Put (url, n);
+ return n;
+ }
+ index = ~index;
+ if (index == current.Nodes.Count) {
+ //Console.WriteLine ("Match fail for {0}", strippedUrl);
+ //Console.WriteLine (current.Nodes.Select (n => n.Element).Aggregate ((e1, e2) => e1 + ", " + e2));
+ return SlowMatchNode (Tree.RootNode, matchCache, strippedUrl);
+ }
+ current = current.Nodes [index - 1];
+ //Console.WriteLine ("Binarysearch failed for {0}, next node check is {1}", strippedUrl, current.Element);
+ } while (true);
+
+ return null;
+ }
+
+ /* That slow path is mainly here to handle ecmaspec type of url which are composed of hard to sort numbers
+ * because they don't have the same amount of digit. We could use a regex to harmonise the various number
+ * parts but then it would be quite specific. Since in the case of ecmaspec the tree is well-formed enough
+ * the "Slow" match should still be fast enough
+ */
+ Node SlowMatchNode (Node current, LRUCache<string, Node> matchCache, string url)
+ {
+ //Console.WriteLine ("Entering slow path for {0} starting from {1}", url, current.Element);
+ while (current != null) {
+ bool stop = true;
+ foreach (Node n in current.Nodes) {
+ var element = n.Element.StartsWith (UriPrefix, StringComparison.OrdinalIgnoreCase) ? n.Element.Substring (UriPrefix.Length) : n.Element;
+ if (url == element) {
+ matchCache.Put (url, n);
+ return n;
+ } else if (url.StartsWith (element + ".", StringComparison.OrdinalIgnoreCase) && !n.IsLeaf) {
+ current = n;
+ stop = false;
+ break;
+ }
+ }
+ if (stop)
+ current = null;
+ }
+
+ return null;
+ }
+
+ class NodeElementComparer : IComparer<Node>
+ {
+ public static NodeElementComparer Instance = new NodeElementComparer ();
+
+ public int Compare (Node n1, Node n2)
+ {
+ return string.Compare (Cleanup (n1), Cleanup (n2), StringComparison.Ordinal);
+ }
+
+ string Cleanup (Node n)
+ {
+ var prefix = n.Tree != null && n.Tree.HelpSource != null ? n.Tree.HelpSource.UriPrefix : string.Empty;
+ var element = n.Element.StartsWith (prefix, StringComparison.OrdinalIgnoreCase) ? n.Element.Substring (prefix.Length) : n.Element;
+ if (char.IsDigit (element, 0)) {
+ var count = element.TakeWhile (char.IsDigit).Count ();
+ element = element.PadLeft (Math.Max (0, 3 - count) + element.Length, '0');
+ }
+ //Console.WriteLine ("Cleaned up {0} to {1}", n.Element, element);
+ return element;
+ }
+ }
+
+ public virtual DocumentType GetDocumentTypeForId (string id, out Dictionary<string, string> extraParams)
+ {
+ extraParams = null;
+ return DocumentType.PlainText;
+ }
+
+ public virtual Stream GetImage (string url)
+ {
+ return null;
+ }
+
+ //
+ // Populates the index.
+ //
+ public virtual void PopulateIndex (IndexMaker index_maker)
+ {
+ }
+
+ //
+ // Create different Documents for adding to Lucene search index
+ // The default action is do nothing. Subclasses should add the docs
+ //
+ public virtual void PopulateSearchableIndex (IndexWriter writer)
+ {
+
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.IO;
+using System.Text;
+using System.Linq;
+using System.Xml;
+using System.Collections.Generic;
+
+namespace MonkeyDoc
+{
+ public class Node : IComparable<Node>, IComparable
+ {
+ readonly Tree tree;
+ string caption, element, pubUrl;
+ public bool Documented;
+ bool loaded;
+ Node parent;
+ List<Node> nodes;
+ Dictionary<string, Node> childrenLookup;
+ /* 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;
+
+ 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.tree = tree;
+ this.caption = caption;
+ this.element = element;
+ }
+
+ /// <summary>
+ /// Creates a node from an on-disk representation
+ /// </summary>
+ internal Node (Node parent, int address) : this (parent.tree, address)
+ {
+ this.parent = parent;
+ }
+
+ internal Node (Tree tree, int address)
+ {
+ this.address = address;
+ this.tree = tree;
+ if (address > 0)
+ LoadNode ();
+ }
+
+ /* This is solely used for MatchNode to check for equality */
+ internal Node ()
+ {
+ }
+
+ void LoadNode ()
+ {
+ tree.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;
+ }
+
+ public List<Node> Nodes {
+ 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 tree;
+ }
+ }
+
+ internal int Address {
+ get {
+ return address;
+ }
+ }
+
+ /// <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);
+ }
+ }
+
+ 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 = tree.HelpSource != null ? tree.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 = caption;
+ var cap2 = 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);
+ }
+ }
+}
--- /dev/null
+using System;
+
+namespace MonkeyDoc
+{
+ public abstract class Provider
+ {
+ //
+ // This code is used to "tag" all the different sources
+ //
+ static short serial;
+
+ public int Code { get; set; }
+
+ public Provider ()
+ {
+ Code = serial++;
+ }
+
+ public abstract void PopulateTree (Tree tree);
+
+ //
+ // Called at shutdown time after the tree has been populated to perform
+ // any fixups or final tasks.
+ //
+ public abstract void CloseTree (HelpSource hs, Tree tree);
+ }
+}
--- /dev/null
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Configuration;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Xml;
+
+using MonkeyDoc.Providers;
+using Lucene.Net.Analysis.Standard;
+using Lucene.Net.Index;
+
+namespace MonkeyDoc
+{
+ public class RootTree : Tree
+ {
+ public const int MonodocVersion = 2;
+ const string RootNamespace = "root:/";
+ string basedir;
+ static List<string> uncompiledHelpSourcePaths = new List<string>();
+ HashSet<string> loadedSourceFiles = new HashSet<string>();
+ List<HelpSource> helpSources = new List<HelpSource>();
+ Dictionary<string, Node> nameToNode = new Dictionary<string, Node>();
+ Dictionary<string, HelpSource> nameToHelpSource = new Dictionary<string, HelpSource>();
+
+ public IList<HelpSource> HelpSources {
+ get {
+ return this.helpSources.AsReadOnly();
+ }
+ }
+
+ public DateTime LastHelpSourceTime {
+ get;
+ set;
+ }
+
+ static bool IsUnix {
+ get {
+ int platform = (int)Environment.OSVersion.Platform;
+ return platform == 4 || platform == 128 || platform == 6;
+ }
+ }
+
+ RootTree () : base (null, "Mono Documentation", "root:")
+ {
+ base.RootNode.EnsureNodes();
+ this.LastHelpSourceTime = DateTime.Now;
+ }
+
+ public static void AddUncompiledSource (string path)
+ {
+ uncompiledHelpSourcePaths.Add (path);
+ }
+
+ public static RootTree LoadTree ()
+ {
+ return RootTree.LoadTree (RootTree.ProbeBaseDirectories ());
+ }
+
+ static string ProbeBaseDirectories ()
+ {
+ string result = ".";
+ try {
+ result = Settings.Get ("docPath") ?? ".";
+ } catch {}
+
+ return result;
+ }
+
+ public static RootTree LoadTree (string basedir, bool includeExternal = true)
+ {
+ if (string.IsNullOrEmpty (basedir))
+ throw new ArgumentNullException ("basedir");
+ if (!Directory.Exists (basedir))
+ throw new ArgumentException ("basedir", string.Format ("Base documentation directory at '{0}' doesn't exist", basedir));
+
+ XmlDocument xmlDocument = new XmlDocument ();
+ string filename = Path.Combine (basedir, "monodoc.xml");
+ xmlDocument.Load (filename);
+ IEnumerable<string> sourceFiles = Directory.EnumerateFiles (Path.Combine (basedir, "sources"), "*.source");
+ if (includeExternal)
+ sourceFiles = sourceFiles.Concat (RootTree.ProbeExternalDirectorySources ());
+ return RootTree.LoadTree (basedir, xmlDocument, sourceFiles);
+ }
+
+ static IEnumerable<string> ProbeExternalDirectorySources ()
+ {
+ IEnumerable<string> enumerable = Enumerable.Empty<string> ();
+ try {
+ string path = Settings.Get ("docExternalPath");
+ enumerable = enumerable.Concat (System.IO.Directory.EnumerateFiles (path, "*.source"));
+ }
+ catch {}
+
+ if (Directory.Exists ("/Library/Frameworks/Mono.framework/External/monodoc"))
+ enumerable = enumerable.Concat (Directory.EnumerateFiles ("/Library/Frameworks/Mono.framework/External/monodoc", "*.source"));
+ return enumerable;
+ }
+
+ public static RootTree LoadTree (string indexDir, XmlDocument docTree, IEnumerable<string> sourceFiles)
+ {
+ if (docTree == null) {
+ docTree = new XmlDocument ();
+ using (Stream manifestResourceStream = typeof (RootTree).Assembly.GetManifestResourceStream ("monodoc.xml")) {
+ docTree.Load (manifestResourceStream);
+ }
+ }
+
+ sourceFiles = (sourceFiles ?? new string[0]);
+ RootTree rootTree = new RootTree ();
+ rootTree.basedir = indexDir;
+ XmlNodeList xml_node_list = docTree.SelectNodes ("/node/node");
+ rootTree.nameToNode["root"] = rootTree.RootNode;
+ rootTree.nameToNode["libraries"] = rootTree.RootNode;
+ rootTree.Populate (rootTree.RootNode, xml_node_list);
+
+ if (rootTree.LookupEntryPoint ("various") == null) {
+ Console.Error.WriteLine ("No 'various' doc node! Check monodoc.xml!");
+ Node rootNode = rootTree.RootNode;
+ }
+
+ foreach (string current in sourceFiles)
+ rootTree.AddSourceFile (current);
+
+ foreach (string path in uncompiledHelpSourcePaths) {
+ var hs = new Providers.EcmaUncompiledHelpSource (path);
+ hs.RootTree = rootTree;
+ rootTree.helpSources.Add (hs);
+ string epath = "extra-help-source-" + hs.Name;
+ Node hsn = rootTree.RootNode.CreateNode (hs.Name, "root:/" + epath);
+ rootTree.nameToHelpSource [epath] = hs;
+ hsn.EnsureNodes ();
+ foreach (Node n in hs.Tree.RootNode.Nodes)
+ hsn.AddNode (n);
+ }
+
+ RootTree.PurgeNode (rootTree.RootNode);
+ rootTree.RootNode.Sort ();
+ return rootTree;
+ }
+
+ public void AddSource (string sourcesDir)
+ {
+ IEnumerable<string> enumerable = Directory.EnumerateFiles (sourcesDir, "*.source");
+ foreach (string current in enumerable)
+ if (!this.AddSourceFile (current))
+ Console.Error.WriteLine ("Error: Could not load source file {0}", current);
+ }
+
+ public bool AddSourceFile (string sourceFile)
+ {
+ if (this.loadedSourceFiles.Contains (sourceFile))
+ return false;
+
+ Node node = this.LookupEntryPoint ("various") ?? base.RootNode;
+ XmlDocument xmlDocument = new XmlDocument ();
+ try {
+ xmlDocument.Load (sourceFile);
+ } catch {
+ bool result = false;
+ return result;
+ }
+
+ XmlNodeList extra_nodes = xmlDocument.SelectNodes ("/monodoc/node");
+ if (extra_nodes.Count > 0)
+ this.Populate (node, extra_nodes);
+
+ XmlNodeList sources = xmlDocument.SelectNodes ("/monodoc/source");
+ if (sources == null) {
+ Console.Error.WriteLine ("Error: No <source> section found in the {0} file", sourceFile);
+ return false;
+ }
+
+ loadedSourceFiles.Add (sourceFile);
+ foreach (XmlNode xmlNode in sources) {
+ XmlAttribute a = xmlNode.Attributes["provider"];
+ if (a == null) {
+ Console.Error.WriteLine ("Error: no provider in <source>");
+ continue;
+ }
+ string provider = a.InnerText;
+ a = xmlNode.Attributes["basefile"];
+ if (a == null) {
+ Console.Error.WriteLine ("Error: no basefile in <source>");
+ continue;
+ }
+ string basefile = a.InnerText;
+ a = xmlNode.Attributes["path"];
+ if (a == null) {
+ Console.Error.WriteLine ("Error: no path in <source>");
+ continue;
+ }
+ string path = a.InnerText;
+ string basefilepath = Path.Combine (Path.GetDirectoryName (sourceFile), basefile);
+ HelpSource helpSource = RootTree.GetHelpSource (provider, basefilepath);
+ if (helpSource != null) {
+ helpSource.RootTree = this;
+ this.helpSources.Add (helpSource);
+ this.nameToHelpSource[path] = helpSource;
+ Node node2 = this.LookupEntryPoint (path);
+ if (node2 == null) {
+ Console.Error.WriteLine ("node `{0}' is not defined on the documentation map", path);
+ node2 = node;
+ }
+ foreach (Node current in helpSource.Tree.RootNode.Nodes) {
+ node2.AddNode (current);
+ }
+ node2.Sort ();
+ }
+ }
+ return true;
+ }
+
+ static bool PurgeNode (Node node)
+ {
+ bool result = false;
+ if (!node.Documented)
+ {
+ List<Node> list = new List<Node> ();
+ foreach (Node current in node.Nodes)
+ {
+ bool flag = RootTree.PurgeNode (current);
+ if (flag)
+ {
+ list.Add (current);
+ }
+ }
+ result = (node.Nodes.Count == list.Count);
+ foreach (Node current2 in list)
+ {
+ node.DeleteNode (current2);
+ }
+ }
+ return result;
+ }
+
+ public static string[] GetSupportedFormats ()
+ {
+ return new string[]
+ {
+ "ecma",
+ "ecmaspec",
+ "error",
+ "man",
+ "xhtml"
+ };
+ }
+
+ public static HelpSource GetHelpSource (string provider, string basefilepath)
+ {
+ HelpSource result;
+ try {
+ switch (provider) {
+ case "xhtml":
+ case "hb":
+ result = new XhtmlHelpSource (basefilepath, false);
+ break;
+ case "man":
+ result = new ManHelpSource (basefilepath, false);
+ break;
+ case "error":
+ result = new ErrorHelpSource (basefilepath, false);
+ break;
+ case "ecmaspec":
+ result = new EcmaSpecHelpSource (basefilepath, false);
+ break;
+ case "ecma":
+ result = new EcmaHelpSource (basefilepath, false);
+ break;
+ default:
+ Console.Error.WriteLine ("Error: Unknown provider specified: {0}", provider);
+ result = null;
+ break;
+ }
+ } catch (FileNotFoundException) {
+ Console.Error.WriteLine ("Error: did not find one of the files in sources/" + basefilepath);
+ result = null;
+ }
+ return result;
+ }
+
+ public static Provider GetProvider (string provider, params string[] basefilepaths)
+ {
+ switch (provider) {
+ case "ecma":
+ return new EcmaProvider (basefilepaths[0]);
+ case "ecmaspec":
+ return new EcmaSpecProvider (basefilepaths[0]);
+ case "error":
+ return new ErrorProvider (basefilepaths[0]);
+ case "man":
+ return new ManProvider (basefilepaths);
+ case "xhml":
+ case "hb":
+ return new XhtmlProvider (basefilepaths[0]);
+ }
+
+ throw new NotSupportedException (provider);
+ }
+
+ void Populate (Node parent, XmlNodeList xml_node_list)
+ {
+ foreach (XmlNode xmlNode in xml_node_list) {
+ XmlAttribute e = xmlNode.Attributes["parent"];
+ Node parent2 = null;
+ if (e != null && this.nameToNode.TryGetValue (e.InnerText, out parent2)) {
+ xmlNode.Attributes.Remove (e);
+ Populate (parent2, xmlNode.SelectNodes ("."));
+ continue;
+ }
+ e = xmlNode.Attributes["label"];
+ if (e == null) {
+ Console.Error.WriteLine ("`label' attribute missing in <node>");
+ continue;
+ }
+ string label = e.InnerText;
+ e = xmlNode.Attributes["name"];
+ if (e == null) {
+ Console.Error.WriteLine ("`name' attribute missing in <node>");
+ continue;
+ }
+ string name = e.InnerText;
+ Node orCreateNode = parent.GetOrCreateNode (label, "root:/" + name);
+ orCreateNode.EnsureNodes ();
+ this.nameToNode[name] = orCreateNode;
+ XmlNodeList xmlNodeList = xmlNode.SelectNodes ("./node");
+ if (xmlNodeList != null) {
+ this.Populate (orCreateNode, xmlNodeList);
+ }
+ }
+ }
+
+ public Node LookupEntryPoint (string name)
+ {
+ Node result = null;
+ if (!this.nameToNode.TryGetValue (name, out result)) {
+ result = null;
+ }
+ return result;
+ }
+
+ public TOutput RenderUrl<TOutput> (string url, IDocGenerator<TOutput> generator, HelpSource hintSource = null)
+ {
+ Node dummy;
+ return RenderUrl<TOutput> (url, generator, out dummy, hintSource);
+ }
+
+ public TOutput RenderUrl<TOutput> (string url, IDocGenerator<TOutput> generator, out Node node, HelpSource hintSource = null)
+ {
+ node = null;
+ string internalId = null;
+ HelpSource hs = GetHelpSourceAndIdForUrl (url, hintSource, out internalId, out node);
+ return generator.Generate (hs, internalId);
+ }
+
+ public HelpSource GetHelpSourceAndIdForUrl (string url, out string internalId)
+ {
+ Node dummy;
+ return GetHelpSourceAndIdForUrl (url, out internalId, out dummy);
+ }
+
+ public HelpSource GetHelpSourceAndIdForUrl (string url, out string internalId, out Node node)
+ {
+ return GetHelpSourceAndIdForUrl (url, null, out internalId, out node);
+ }
+
+ public HelpSource GetHelpSourceAndIdForUrl (string url, HelpSource hintSource, out string internalId, out Node node)
+ {
+ node = null;
+ internalId = null;
+
+ if (url.StartsWith ("root:/", StringComparison.OrdinalIgnoreCase))
+ return this.GetHelpSourceAndIdFromName (url.Substring ("root:/".Length), out internalId, out node);
+
+ HelpSource helpSource = hintSource;
+ if (helpSource == null || string.IsNullOrEmpty (internalId = helpSource.GetInternalIdForUrl (url, out node))) {
+ helpSource = null;
+ foreach (var hs in helpSources.Where (h => h.CanHandleUrl (url))) {
+ if (!string.IsNullOrEmpty (internalId = hs.GetInternalIdForUrl (url, out node))) {
+ helpSource = hs;
+ break;
+ }
+ }
+ }
+
+ return helpSource;
+ }
+
+ public HelpSource GetHelpSourceAndIdFromName (string name, out string internalId, out Node node)
+ {
+ internalId = "root:";
+ node = this.LookupEntryPoint (name);
+
+ return node == null ? null : node.Nodes.Select (n => n.Tree.HelpSource).Where (hs => hs != null).Distinct ().FirstOrDefault ();
+ }
+
+ public HelpSource GetHelpSourceFromId (int id)
+ {
+ return (id < 0 || id >= this.helpSources.Count) ? null : this.helpSources[id];
+ }
+
+ public Stream GetImage (string url)
+ {
+ if (url.StartsWith ("source-id:", StringComparison.OrdinalIgnoreCase)) {
+ string text = url.Substring (10);
+ int num = text.IndexOf (":");
+ string text2 = text.Substring (0, num);
+ int id = 0;
+ try {
+ id = int.Parse (text2);
+ } catch {
+ Console.Error.WriteLine ("Failed to parse source-id url: {0} `{1}'", url, text2);
+ return null;
+ }
+ HelpSource helpSourceFromId = this.GetHelpSourceFromId (id);
+ return helpSourceFromId.GetImage (text.Substring (num + 1));
+ }
+ Assembly assembly = Assembly.GetAssembly (typeof (RootTree));
+ return assembly.GetManifestResourceStream (url);
+ }
+
+ public IndexReader GetIndex ()
+ {
+ try {
+ string text = Path.Combine (this.basedir, "monodoc.index");
+ if (File.Exists (text))
+ return IndexReader.Load (text);
+
+ text = Path.Combine (Settings.Get ("monodocIndexDirectory"), "monodoc.index");
+ return IndexReader.Load (text);
+ } catch {
+ return null;
+ }
+ }
+
+ public static void MakeIndex ()
+ {
+ RootTree rootTree = RootTree.LoadTree ();
+ rootTree.GenerateIndex ();
+ }
+
+ public void GenerateIndex ()
+ {
+ IndexMaker indexMaker = new IndexMaker ();
+ foreach (HelpSource current in this.helpSources)
+ current.PopulateIndex (indexMaker);
+ string text = Path.Combine (this.basedir, "monodoc.index");
+ try {
+ indexMaker.Save (text);
+ } catch (UnauthorizedAccessException) {
+ text = Path.Combine (Settings.Get ("docPath"), "monodoc.index");
+ try {
+ indexMaker.Save (text);
+ } catch (UnauthorizedAccessException) {
+ Console.WriteLine ("Unable to write index file in {0}", Path.Combine (Settings.Get ("docPath"), "monodoc.index"));
+ return;
+ }
+ }
+ if (RootTree.IsUnix)
+ RootTree.chmod (text, 420);
+
+ Console.WriteLine ("Documentation index at {0} updated", text);
+ }
+
+ public SearchableIndex GetSearchIndex ()
+ {
+ try {
+ string text = Path.Combine (this.basedir, "search_index");
+ if (System.IO.Directory.Exists (text))
+ return SearchableIndex.Load (text);
+ text = Path.Combine (Settings.Get ("docPath"), "search_index");
+ return SearchableIndex.Load (text);
+ } catch {
+ return null;
+ }
+ }
+
+ public static void MakeSearchIndex ()
+ {
+ RootTree rootTree = RootTree.LoadTree ();
+ rootTree.GenerateSearchIndex ();
+ }
+
+ public void GenerateSearchIndex ()
+ {
+ Console.WriteLine ("Loading the monodoc tree...");
+ string text = Path.Combine (this.basedir, "search_index");
+ IndexWriter indexWriter;
+ var analyzer = new StandardAnalyzer (Lucene.Net.Util.Version.LUCENE_CURRENT);
+ var directory = Lucene.Net.Store.FSDirectory.Open (text);
+
+ try {
+ if (!Directory.Exists (text))
+ Directory.CreateDirectory (text);
+ indexWriter = new IndexWriter (directory, analyzer, true, IndexWriter.MaxFieldLength.LIMITED);
+ } catch (UnauthorizedAccessException) {
+ try {
+ text = Path.Combine (Settings.Get ("docPath"), "search_index");
+ if (!Directory.Exists (text))
+ Directory.CreateDirectory (text);
+ indexWriter = new IndexWriter (directory, analyzer, true, IndexWriter.MaxFieldLength.LIMITED);
+ } catch (UnauthorizedAccessException) {
+ Console.WriteLine ("You don't have permissions to write on " + text);
+ return;
+ }
+ }
+ Console.WriteLine ("Collecting and adding documents...");
+ foreach (HelpSource current in this.helpSources) {
+ current.PopulateSearchableIndex (indexWriter);
+ }
+ Console.WriteLine ("Closing...");
+ indexWriter.Optimize ();
+ indexWriter.Close ();
+ }
+
+ [DllImport ("libc")]
+ static extern int chmod (string filename, int mode);
+ }
+}
--- /dev/null
+//
+//
+// SearchableDocument.cs: Abstracts our model of document from the Lucene Document
+//
+// Author: Mario Sopena
+//
+using Lucene.Net.Documents;
+
+namespace MonkeyDoc
+{
+ struct SearchableDocument
+ {
+ public string Title {
+ get; set;
+ }
+
+ public string Url {
+ get; set;
+ }
+
+ public string FullTitle {
+ get; set;
+ }
+
+ public string HotText {
+ get; set;
+ }
+
+ public string Text {
+ get; set;
+ }
+
+ public string Examples {
+ get; set;
+ }
+
+ public SearchableDocument Reset ()
+ {
+ Title = Url = FullTitle = HotText = Text = Examples = null;
+ return this;
+ }
+
+ public Document LuceneDoc {
+ get {
+ Document doc = new Document ();
+ doc.Add (UnIndexed ("title", Title));
+ doc.Add (UnIndexed ("url", Url));
+ doc.Add (UnIndexed ("fulltitle", FullTitle ?? string.Empty));
+ doc.Add (UnStored ("hottext", HotText));
+ doc.Add (UnStored ("text", Text));
+ doc.Add (UnStored ("examples", Examples));
+ return doc;
+ }
+ }
+
+ static Field UnIndexed(System.String name, System.String value_Renamed)
+ {
+ return new Field(name, value_Renamed, Field.Store.YES, Field.Index.NO);
+ }
+
+ static Field UnStored(System.String name, System.String value_Renamed)
+ {
+ return new Field(name, value_Renamed, Field.Store.NO, Field.Index.ANALYZED);
+ }
+ }
+}
--- /dev/null
+//
+//
+// SearchableIndex.cs: Index that uses Lucene to search through the docs
+//
+// Author: Mario Sopena
+//
+
+using System;
+using System.IO;
+using System.Collections.Generic;
+// Lucene imports
+using Lucene.Net.Index;
+using Lucene.Net.Documents;
+using Lucene.Net.Analysis;
+using Lucene.Net.Analysis.Standard;
+using Lucene.Net.Search;
+using Lucene.Net.QueryParsers;
+using Lucene.Net.Store;
+
+namespace MonkeyDoc
+{
+ public class SearchableIndex
+ {
+ const int maxSearchCount = 30;
+
+ IndexSearcher searcher;
+ string dir;
+
+ public string Dir {
+ get {
+ if (dir == null)
+ dir = "search_index";
+ return dir;
+ }
+ set { dir = value; }
+ }
+
+ public static SearchableIndex Load (string dir)
+ {
+ SearchableIndex s = new SearchableIndex ();
+ s.dir = dir;
+ try {
+ //s.searcher = new IndexSearcher (dir);
+ // TODO: parametrize that depending if we run on the desktop (low footprint) or the server (use RAMDirectory for instance)
+ s.searcher = new IndexSearcher (FSDirectory.Open (dir));
+ } catch (IOException) {
+ Console.WriteLine ("Index nonexistent or in bad format");
+ return null;
+ }
+ return s;
+ }
+
+ public Result Search (string term)
+ {
+ return Search (term, maxSearchCount);
+ }
+
+ public Result Search (string term, int count)
+ {
+ return Search (term, count, 0);
+ }
+
+ public Result Search (string term, int count, int start) {
+ try {
+ term = term.ToLower ();
+ Term htTerm = new Term ("hottext", term);
+ Query qq1 = new FuzzyQuery (htTerm);
+ Query qq2 = new TermQuery (htTerm);
+ qq2.Boost = 10f;
+ Query qq3 = new PrefixQuery (htTerm);
+ qq3.Boost = 10f;
+ DisjunctionMaxQuery q1 = new DisjunctionMaxQuery (0f);
+ q1.Add (qq1);
+ q1.Add (qq2);
+ q1.Add (qq3);
+ Query q2 = new TermQuery (new Term ("text", term));
+ q2.Boost = 3f;
+ Query q3 = new TermQuery (new Term ("examples", term));
+ q3.Boost = 3f;
+ DisjunctionMaxQuery q = new DisjunctionMaxQuery (0f);
+
+ q.Add (q1);
+ q.Add (q2);
+ q.Add (q3);
+
+ TopDocs top = SearchInternal (q, count, start);
+ Result r = new Result (term, searcher, top.ScoreDocs);
+ return r;
+ } catch (IOException) {
+ Console.WriteLine ("No index in {0}", dir);
+ return null;
+ }
+ }
+
+ TopDocs SearchInternal (Query q, int count, int start)
+ {
+ // Easy path that doesn't involve creating a Collector ourselves
+ // watch for Lucene.NET improvement on that (like searcher.SearchAfter)
+ if (start == 0)
+ return searcher.Search (q, count);
+
+ var weight = searcher.CreateWeight (q); // TODO: reuse weight instead of query
+ var collector = TopScoreDocCollector.Create (start + count + 1, false);
+ searcher.Search (q, collector);
+
+ return collector.TopDocs (start, count);
+ }
+
+ public Result FastSearch (string term, int number)
+ {
+ try {
+ term = term.ToLower ();
+ Query q1 = new TermQuery (new Term ("hottext", term));
+ Query q2 = new PrefixQuery (new Term ("hottext", term));
+ q2.Boost = 0.5f;
+ DisjunctionMaxQuery q = new DisjunctionMaxQuery (0f);
+ q.Add (q1);
+ q.Add (q2);
+ TopDocs top = searcher.Search (q, number);
+ return new Result (term, searcher, top.ScoreDocs);
+ } catch (IOException) {
+ Console.WriteLine ("No index in {0}", dir);
+ return null;
+ }
+ }
+ }
+
+ //
+ // An object representing the search term with the results
+ //
+ public class Result {
+ string term;
+ Searcher searcher;
+ ScoreDoc[] docs;
+
+ public string Term {
+ get { return term;}
+ }
+
+ public int Count {
+ get { return docs.Length; }
+ }
+
+ public Document this [int i] {
+ get { return searcher.Doc (docs[i].Doc); }
+ }
+
+ public string GetTitle (int i)
+ {
+ Document d = this[i];
+ return d == null ? string.Empty : d.Get ("title");
+ }
+
+ public string GetUrl (int i)
+ {
+ Document d = this[i];
+ return d == null ? string.Empty : d.Get ("url");
+ }
+
+ public string GetFullTitle (int i)
+ {
+ Document d = this[i];
+ return d == null ? string.Empty : d.Get ("fulltitle");
+ }
+
+ public float Score (int i)
+ {
+ return docs[i].Score;
+ }
+
+ public Result (string Term, Searcher searcher, ScoreDoc[] docs)
+ {
+ this.term = Term;
+ this.searcher = searcher;
+ this.docs = docs;
+ }
+ }
+}
+
--- /dev/null
+using System;
+using System.IO;
+using System.Text;
+using System.Linq;
+using System.Xml;
+using System.Collections.Generic;
+
+namespace MonkeyDoc
+{
+ /// <summary>
+ /// This tree is populated by the documentation providers, or populated
+ /// from a binary encoding of the tree. The format of the tree is designed
+ /// to minimize the need to load it in full.
+ /// </summary>
+
+ /* Ideally this class should also be abstracted to let user have something
+ * else than a file as a backing store, a database for instance
+ */
+ public class Tree
+ {
+ public readonly HelpSource HelpSource;
+
+ FileStream InputStream;
+ BinaryReader InputReader;
+
+ // This is the node which contains all the other node of the tree
+ Node rootNode;
+
+ /// <summary>
+ /// Load from file constructor
+ /// </summary>
+ public Tree (HelpSource hs, string filename)
+ {
+ Encoding utf8 = new UTF8Encoding (false, true);
+
+ if (!File.Exists (filename)){
+ throw new FileNotFoundException ();
+ }
+
+ InputStream = File.OpenRead (filename);
+ InputReader = new BinaryReader (InputStream, utf8);
+ byte [] sig = InputReader.ReadBytes (4);
+
+ if (!GoodSig (sig))
+ throw new Exception ("Invalid file format");
+
+ InputStream.Position = 4;
+ var position = InputReader.ReadInt32 ();
+ rootNode = new Node (this, position);
+ InflateNode (rootNode);
+
+ HelpSource = hs;
+ }
+
+ /// <summary>
+ /// Tree creation and merged tree constructor
+ /// </summary>
+ public Tree (HelpSource hs, string caption, string url) : this (hs, null, caption, url)
+ {
+ }
+
+ public Tree (HelpSource hs, Node parent, string caption, string element)
+ {
+ HelpSource = hs;
+ rootNode = parent == null ? new Node (this, caption, element) : new Node (parent, caption, element);
+ }
+
+ /// <summary>
+ /// Saves the tree into the specified file using the help file format.
+ /// </summary>
+ public void Save (string file)
+ {
+ Encoding utf8 = new UTF8Encoding (false, true);
+ using (FileStream output = File.OpenWrite (file)){
+ // Skip over the pointer to the first node.
+ output.Position = 8;
+
+ using (BinaryWriter writer = new BinaryWriter (output, utf8)) {
+ // Recursively dump
+ rootNode.Serialize (output, writer);
+
+ output.Position = 0;
+ writer.Write (new byte [] { (byte) 'M', (byte) 'o', (byte) 'H', (byte) 'P' });
+ writer.Write (rootNode.Address);
+ }
+ }
+ }
+
+ public Node RootNode {
+ get {
+ return rootNode;
+ }
+ }
+
+ static bool GoodSig (byte [] sig)
+ {
+ if (sig.Length != 4)
+ return false;
+ return sig [0] == (byte) 'M'
+ && sig [1] == (byte) 'o'
+ && sig [2] == (byte) 'H'
+ && sig [3] == (byte) 'P';
+ }
+
+ public void InflateNode (Node baseNode)
+ {
+ var address = baseNode.Address;
+ if (address < 0)
+ address = -address;
+
+ InputStream.Position = address;
+ baseNode.Deserialize (InputReader);
+ }
+ }
+
+ public static class TreeDumper
+ {
+ static int indent;
+
+ static void Indent ()
+ {
+ for (int i = 0; i < indent; i++)
+ Console.Write (" ");
+ }
+
+ public static void PrintTree (Node node)
+ {
+ Indent ();
+ Console.WriteLine ("{0},{1}\t[PublicUrl: {2}]", node.Element, node.Caption, node.PublicUrl);
+ if (node.Nodes.Count == 0)
+ return;
+
+ indent++;
+ foreach (Node n in node.Nodes)
+ PrintTree (n);
+ indent--;
+ }
+
+ public static string ExportToTocXml (Node root, string title, string desc)
+ {
+ if (root == null)
+ throw new ArgumentNullException ("root");
+ // Return a toc index of sub-nodes
+ StringBuilder buf = new StringBuilder ();
+ var writer = XmlWriter.Create (buf);
+ writer.WriteStartElement ("toc");
+ writer.WriteAttributeString ("title", title ?? string.Empty);
+ writer.WriteElementString ("description", desc ?? string.Empty);
+ writer.WriteStartElement ("list");
+ foreach (Node n in root.Nodes) {
+ writer.WriteStartElement ("item");
+ writer.WriteAttributeString ("url", n.Element);
+ writer.WriteValue (n.Caption);
+ writer.WriteEndElement ();
+ }
+ writer.WriteEndElement ();
+ writer.WriteEndElement ();
+ writer.Flush ();
+ writer.Close ();
+
+ return buf.ToString ();
+ }
+ }
+}
--- /dev/null
+using System;
+
+namespace MonkeyDoc
+{
+ public static class TypeUtils
+ {
+ public static bool GetNamespaceAndType (string url, out string ns, out string type)
+ {
+ int nsidx = -1;
+ int numLt = 0;
+ for (int i = 0; i < url.Length; ++i) {
+ char c = url [i];
+ switch (c) {
+ case '<':
+ case '{':
+ ++numLt;
+ break;
+ case '>':
+ case '}':
+ --numLt;
+ break;
+ case '.':
+ if (numLt == 0)
+ nsidx = i;
+ break;
+ }
+ }
+
+ if (nsidx == -1) {
+ ns = null;
+ type = null;
+ return false;
+ }
+ ns = url.Substring (0, nsidx);
+ type = url.Substring (nsidx + 1);
+
+ return true;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Linq;
+using System.IO;
+using System.Configuration;
+using System.Collections.Specialized;
+using MonkeyDoc.Caches;
+
+namespace MonkeyDoc
+{
+ public enum DocEntity
+ {
+ Text,
+ Blob
+ }
+
+ public interface IDocCache : IDisposable
+ {
+ bool IsCached (string id);
+ bool CanCache (DocEntity entity);
+
+ Stream GetCachedStream (string id);
+ string GetCachedString (string id);
+
+ void CacheText (string id, string content);
+ void CacheText (string id, Stream stream);
+
+ void CacheBlob (string id, byte[] data);
+ void CacheBlob (string id, Stream stream);
+ }
+
+ public static class DocCacheHelper
+ {
+ static string cacheBaseDirectory;
+
+ static DocCacheHelper ()
+ {
+ try {
+ var cacheValues = Settings.Get ("cache").Split (',');
+ if (cacheValues.Length == 2 && cacheValues[0].Equals ("file", StringComparison.Ordinal))
+ cacheBaseDirectory = cacheValues[1].Replace ("~", Environment.GetFolderPath (Environment.SpecialFolder.Personal));
+ } catch {}
+ }
+
+ // Use configuration option to query for cache directory, if it doesn't exist we instantiate a nullcache
+ public static IDocCache GetDefaultCache (string name)
+ {
+ if (cacheBaseDirectory == null)
+ return new NullCache ();
+
+ return new FileCache (Path.Combine (cacheBaseDirectory, name));
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.IO;
+
+namespace MonkeyDoc.Caches
+{
+ public class FileCache : IDocCache
+ {
+ string baseCacheDir;
+
+ public FileCache (string baseCacheDir)
+ {
+ this.baseCacheDir = baseCacheDir;
+ if (!Directory.Exists (baseCacheDir))
+ Directory.CreateDirectory (baseCacheDir);
+ }
+
+ public bool IsCached (string id)
+ {
+ return File.Exists (MakePath (id));
+ }
+
+ public bool CanCache (DocEntity entity)
+ {
+ return true;
+ }
+
+ public Stream GetCachedStream (string id)
+ {
+ return File.OpenRead (MakePath (id));
+ }
+
+ public string GetCachedString (string id)
+ {
+ return File.ReadAllText (MakePath (id));
+ }
+
+ public void CacheText (string id, string content)
+ {
+ File.WriteAllText (MakePath (id), content);
+ }
+
+ public void CacheText (string id, Stream stream)
+ {
+ using (var file = File.OpenWrite (MakePath (id)))
+ stream.CopyTo (file);
+ }
+
+ public void CacheBlob (string id, byte[] data)
+ {
+ File.WriteAllBytes (MakePath (id), data);
+ }
+
+ public void CacheBlob (string id, Stream stream)
+ {
+ using (var file = File.OpenWrite (MakePath (id)))
+ stream.CopyTo (file);
+ }
+
+ string MakePath (string id)
+ {
+ id = id.Replace (Path.DirectorySeparatorChar, '_');
+ return Path.Combine (baseCacheDir, id);
+ }
+
+ public void Dispose ()
+ {
+ if (!Directory.Exists (baseCacheDir))
+ return;
+
+ try {
+ Directory.Delete (baseCacheDir, true);
+ } catch {}
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.IO;
+
+namespace MonkeyDoc.Caches
+{
+ // This is basically a no-cache implementation
+ public class NullCache : IDocCache
+ {
+ public bool IsCached (string id)
+ {
+ return false;
+ }
+
+ public bool CanCache (DocEntity entity)
+ {
+ return false;
+ }
+
+ public Stream GetCachedStream (string id)
+ {
+ return null;
+ }
+
+ public string GetCachedString (string id)
+ {
+ return null;
+ }
+
+ public void CacheText (string id, string content)
+ {
+
+ }
+
+ public void CacheText (string id, Stream stream)
+ {
+
+ }
+
+ public void CacheBlob (string id, byte[] data)
+ {
+
+ }
+
+ public void CacheBlob (string id, Stream stream)
+ {
+
+ }
+
+ public void Dispose ()
+ {
+
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+
+namespace MonkeyDoc
+{
+ // All type of documents that a generator may find as input
+ public enum DocumentType {
+ EcmaXml, // Our main monodoc format
+ EcmaSpecXml,
+ Man,
+ AddinXml,
+ MonoBook, // This is mostly XHTML already, just need a tiny bit of processing
+ Html,
+ TocXml, // Used by help source displaying some kind of toc of the content they host
+ PlainText,
+ ErrorXml
+ }
+
+ /* This interface defines a set of transformation engine
+ * that convert multiple documentation source to a single output format
+ */
+ public interface IDocGenerator<TOutput>
+ {
+ // This method is responsible for finding out the documentation type
+ // for the given ID and use the right engine internally
+ TOutput Generate (HelpSource hs, string internalId);
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.IO;
+using System.Text;
+using System.Linq;
+using System.Collections.Generic;
+
+using MonkeyDoc;
+
+namespace MonkeyDoc.Generators
+{
+ using Html;
+
+ interface IHtmlExporter
+ {
+ string CssCode { get; }
+ string Export (Stream input, Dictionary<string, string> extras);
+ string Export (string input, Dictionary<string, string> extras);
+ }
+
+ public class HtmlGenerator : IDocGenerator<string>
+ {
+ const string cachePrefix = "htmlcached#";
+
+ static string css_code;
+
+ IDocCache defaultCache;
+ static Dictionary<DocumentType, IHtmlExporter> converters;
+
+ static HtmlGenerator ()
+ {
+ converters = new Dictionary<DocumentType, IHtmlExporter> {
+ { DocumentType.EcmaXml, new Ecma2Html () },
+ { DocumentType.Man, new Man2Html () },
+ { DocumentType.TocXml, new Toc2Html () },
+ { DocumentType.EcmaSpecXml, new Ecmaspec2Html () },
+ { DocumentType.ErrorXml, new Error2Html () },
+ { DocumentType.Html, new Idem () },
+ { DocumentType.MonoBook, new MonoBook2Html () },
+ { DocumentType.AddinXml, new Addin2Html () },
+ { DocumentType.PlainText, new Idem () },
+ };
+ }
+
+ public HtmlGenerator (IDocCache defaultCache)
+ {
+ this.defaultCache = defaultCache;
+ }
+
+ public string Generate (HelpSource hs, string id)
+ {
+ if (hs == null || string.IsNullOrEmpty (id))
+ return MakeHtmlError (string.Format ("Your request has found no candidate provider [hs=\"{0}\", id=\"{1}\"]",
+ hs == null ? "(null)" : hs.Name, id ?? "(null)"));
+ var cache = defaultCache ?? hs.Cache;
+ if (cache != null && cache.IsCached (MakeCacheKey (hs, id, null)))
+ return cache.GetCachedString (MakeCacheKey (hs, id, null));
+
+ IEnumerable<string> parts;
+ if (hs.IsMultiPart (id, out parts))
+ return GenerateMultiPart (hs, parts, id);
+
+ if (hs.IsRawContent (id))
+ return hs.GetText (id) ?? string.Empty;
+
+ Dictionary<string, string> extraParams = null;
+ DocumentType type = hs.GetDocumentTypeForId (id, out extraParams);
+ if (cache != null && extraParams != null && cache.IsCached (MakeCacheKey (hs, id, extraParams)))
+ return cache.GetCachedString (MakeCacheKey (hs, id, extraParams));
+
+ IHtmlExporter exporter;
+ if (!converters.TryGetValue (type, out exporter))
+ return MakeHtmlError (string.Format ("Input type '{0}' not supported",
+ type.ToString ()));
+ var result = hs.IsGeneratedContent (id) ?
+ exporter.Export (hs.GetCachedText (id), extraParams) :
+ exporter.Export (hs.GetCachedHelpStream (id), extraParams);
+
+ if (cache != null)
+ cache.CacheText (MakeCacheKey (hs, id, extraParams), result);
+ return result;
+ }
+
+ string GenerateMultiPart (HelpSource hs, IEnumerable<string> ids, string originalId)
+ {
+ var sb = new StringBuilder ();
+ foreach (var id in ids)
+ sb.AppendLine (Generate (hs, id));
+
+ var cache = defaultCache ?? hs.Cache;
+ if (cache != null)
+ cache.CacheText (MakeCacheKey (hs, originalId, null), sb.ToString ());
+ return sb.ToString ();
+ }
+
+ public static string InlineCss {
+ get {
+ if (css_code != null)
+ return css_code;
+
+ System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly (typeof (HtmlGenerator));
+ Stream str_css = assembly.GetManifestResourceStream ("base.css");
+ StringBuilder sb = new StringBuilder ((new StreamReader (str_css)).ReadToEnd());
+ sb.Replace ("@@FONT_FAMILY@@", "Sans Serif");
+ sb.Replace ("@@FONT_SIZE@@", "100%");
+ css_code = sb.ToString () + converters.Values
+ .Select (c => c.CssCode)
+ .Where (css => !string.IsNullOrEmpty (css))
+ .DefaultIfEmpty (string.Empty)
+ .Aggregate (string.Concat);
+ return css_code;
+ }
+ set {
+ css_code = value;
+ }
+ }
+
+ string MakeHtmlError (string error)
+ {
+ return string.Format ("<html><head></head><body><p>{0}</p></body></html>", error);
+ }
+
+ string MakeCacheKey (HelpSource hs, string page, IDictionary<string,string> extraParams)
+ {
+ var key = cachePrefix + hs.SourceID + page;
+ if (extraParams != null && extraParams.Count > 0) {
+ var paramPart = string.Join ("-", extraParams.Select (kvp => kvp.Key + kvp.Value));
+ key += '_' + paramPart;
+ }
+ return key;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.IO;
+using System.Text;
+using System.Linq;
+using System.Collections.Generic;
+
+using MonkeyDoc;
+
+namespace MonkeyDoc.Generators
+{
+ /// <summary>
+ /// This generators returns the raw content of the HelpSource without any transformation
+ /// </summary>
+ public class RawGenerator : IDocGenerator<string>
+ {
+ public string Generate (HelpSource hs, string id)
+ {
+ if (hs == null || string.IsNullOrEmpty (id))
+ return null;
+
+ IEnumerable<string> parts;
+ if (hs.IsMultiPart (id, out parts))
+ return GenerateMultiPart (hs, parts, id);
+
+ if (hs.IsRawContent (id))
+ return hs.GetText (id) ?? string.Empty;
+
+ var result = hs.IsGeneratedContent (id) ? hs.GetCachedText (id) : new StreamReader (hs.GetCachedHelpStream (id)).ReadToEnd ();
+
+ return result;
+ }
+
+ string GenerateMultiPart (HelpSource hs, IEnumerable<string> ids, string originalId)
+ {
+ var sb = new StringBuilder ();
+ foreach (var id in ids)
+ sb.AppendLine (Generate (hs, id));
+ return sb.ToString ();
+ }
+ }
+}
--- /dev/null
+using System;
+using System.IO;
+using System.Text;
+using System.Xml;
+using System.Xml.Xsl;
+using System.Xml.XPath;
+using System.Collections.Generic;
+
+namespace MonkeyDoc.Generators.Html
+{
+ public class Addin2Html : IHtmlExporter
+ {
+ public string CssCode {
+ get {
+ return string.Empty;
+ }
+ }
+
+ public string Export (Stream stream, Dictionary<string, string> extraArgs)
+ {
+ using (var reader = new StreamReader (stream))
+ return Htmlize (GetAddin (reader, extraArgs["AddinID"]),
+ extraArgs["show"],
+ extraArgs["AddinID"],
+ extraArgs["FileID"],
+ extraArgs["NodeID"]);
+ }
+
+ public string Export (string input, Dictionary<string, string> extraArgs)
+ {
+ return Htmlize (GetAddin (new StringReader (input), extraArgs["AddinID"]),
+ extraArgs["show"],
+ extraArgs["AddinID"],
+ extraArgs["FileID"],
+ extraArgs["NodeID"]);
+ }
+
+ XmlElement GetAddin (TextReader reader, string addinId)
+ {
+ XmlDocument doc = new XmlDocument ();
+ doc.Load (reader);
+ XmlElement addin = (XmlElement) doc.SelectSingleNode ("Addins/Addin[@fullId='" + addinId + "']");
+ return addin != null ? addin : null;
+ }
+
+ public string Htmlize (XmlElement addin, string urlType, string addinId, string fileId, string path)
+ {
+ if (urlType == MonkeyDoc.Providers.AddinsHelpSource.AddinPrefix)
+ return GetAddinTextFromUrl (addin, addinId, fileId);
+ else if (urlType == MonkeyDoc.Providers.AddinsHelpSource.ExtensionPrefix)
+ return GetExtensionTextFromUrl (addin, addinId, fileId, path);
+ else if (urlType == MonkeyDoc.Providers.AddinsHelpSource.ExtensionNodePrefix)
+ return GetExtensionNodeTextFromUrl (addin, addinId, fileId, path);
+
+ return null;
+ }
+
+ protected string GetAddinTextFromUrl (XmlElement addin, string addinId, string fileId)
+ {
+ if (addin == null)
+ return "<html>Add-in not found: " + addinId + "</html>";
+
+ StringBuilder sb = new StringBuilder ("<html>");
+ sb.Append ("<h1>").Append (addin.GetAttribute ("name")).Append ("</h1>");
+ XmlElement docs = (XmlElement) addin.SelectSingleNode ("Description");
+ if (docs != null)
+ sb.Append (docs.InnerText);
+
+ sb.Append ("<p><table border=\"1\" cellpadding=\"4\" cellspacing=\"0\">");
+ sb.AppendFormat ("<tr><td><b>Id</b></td><td>{0}</td></tr>", addin.GetAttribute ("addinId"));
+ sb.AppendFormat ("<tr><td><b>Namespace</b></td><td>{0}</td></tr>", addin.GetAttribute ("namespace"));
+ sb.AppendFormat ("<tr><td><b>Version</b></td><td>{0}</td></tr>", addin.GetAttribute ("version"));
+ sb.Append ("</table></p>");
+ sb.Append ("<p><b>Extension Points</b>:</p>");
+ sb.Append ("<ul>");
+
+ foreach (XmlElement ep in addin.SelectNodes ("ExtensionPoint")) {
+ sb.AppendFormat ("<li><a href=\"extension-point:{0}#{1}#{2}\">{3}</li>", fileId, addinId, ep.GetAttribute ("path"), ep.GetAttribute ("name"));
+ }
+ sb.Append ("</ul>");
+
+ sb.Append ("</html>");
+ return sb.ToString ();
+ }
+
+ protected string GetExtensionTextFromUrl (XmlElement addin, string addinId, string fileId, string path)
+ {
+ if (addin == null)
+ return "<html>Add-in not found: " + addinId + "</html>";
+
+ XmlElement ext = (XmlElement) addin.SelectSingleNode ("ExtensionPoint[@path='" + path + "']");
+ if (ext == null)
+ return "<html>Extension point not found: " + path + "</html>";
+
+ StringBuilder sb = new StringBuilder ("<html>");
+ sb.Append ("<h1>").Append (ext.GetAttribute ("name")).Append ("</h1>");
+
+ path = path.Replace ("/", " <b>/</b> ");
+ sb.Append ("<p><b>Path</b>: ").Append (path).Append ("</p>");
+ XmlElement desc = (XmlElement) ext.SelectSingleNode ("Description");
+ if (desc != null)
+ sb.Append (desc.InnerText);
+
+ sb.Append ("<p><b>Extension Nodes</b>:</p>");
+ sb.Append ("<table border=\"1\" cellpadding=\"4\" cellspacing=\"0\">");
+
+ foreach (XmlElement en in ext.SelectNodes ("ExtensionNode")) {
+ string nid = en.GetAttribute ("id");
+ string nname = en.GetAttribute ("name");
+ string sdesc = "";
+ desc = (XmlElement) en.SelectSingleNode ("Description");
+ if (desc != null)
+ sdesc = desc.InnerText;
+
+ sb.AppendFormat ("<tr><td><a href=\"extension-node:{0}#{1}#{2}\">{3}</td><td>{4}</td></tr>", fileId, addinId, nid, nname, sdesc);
+ }
+ sb.Append ("</table>");
+
+ sb.Append ("</html>");
+ return sb.ToString ();
+ }
+
+ protected string GetExtensionNodeTextFromUrl (XmlElement addin, string addinId, string fileId, string nodeId)
+ {
+ if (addin == null)
+ return "<html>Add-in not found: " + addinId + "</html>";
+
+ XmlElement node = (XmlElement) addin.SelectSingleNode ("ExtensionNodeType[@id='" + nodeId + "']");
+ if (node == null)
+ return "<html>Extension point not found: " + nodeId + "</html>";
+
+ StringBuilder sb = new StringBuilder ("<html>");
+ sb.Append ("<h1>").Append (node.GetAttribute ("name")).Append ("</h1>");
+ XmlElement desc = (XmlElement) node.SelectSingleNode ("Description");
+ if (desc != null)
+ sb.Append (desc.InnerText);
+
+ sb.Append ("<p><b>Attributes</b>:</p>");
+ sb.Append ("<table border=\"1\" cellpadding=\"4\" cellspacing=\"0\"><tr>");
+ sb.Append ("<td><b>Name</b></td>");
+ sb.Append ("<td><b>Type</b></td>");
+ sb.Append ("<td><b>Required</b></td>");
+ sb.Append ("<td><b>Localizable</b></td>");
+ sb.Append ("<td><b>Description</b></td>");
+ sb.Append ("<tr>");
+ sb.Append ("<td>id</td>");
+ sb.Append ("<td>System.String</td>");
+ sb.Append ("<td></td>");
+ sb.Append ("<td></td>");
+ sb.Append ("<td>Identifier of the node.</td>");
+ sb.Append ("</tr>");
+
+ foreach (XmlElement at in node.SelectNodes ("Attributes/Attribute")) {
+ sb.Append ("<tr>");
+ sb.AppendFormat ("<td>{0}</td>", at.GetAttribute ("name"));
+ sb.AppendFormat ("<td>{0}</td>", at.GetAttribute ("type"));
+ if (at.GetAttribute ("required") == "True")
+ sb.Append ("<td>Yes</td>");
+ else
+ sb.Append ("<td></td>");
+ if (at.GetAttribute ("localizable") == "True")
+ sb.Append ("<td>Yes</td>");
+ else
+ sb.Append ("<td></td>");
+ string sdesc = "";
+ desc = (XmlElement) at.SelectSingleNode ("Description");
+ if (desc != null)
+ sdesc = desc.InnerText;
+
+ sb.AppendFormat ("<td>{0}</td>", sdesc);
+ sb.Append ("</tr>");
+ }
+ sb.Append ("</table>");
+
+ XmlNodeList children = node.SelectNodes ("ChildNodes/ExtensionNode");
+ if (children.Count > 0) {
+ sb.Append ("<p><b>Child Nodes</b>:</p>");
+ sb.Append ("<table border=\"1\" cellpadding=\"4\" cellspacing=\"0\">");
+
+ foreach (XmlElement en in children) {
+ string nid = en.GetAttribute ("id");
+ string nname = en.GetAttribute ("name");
+ string sdesc = "";
+ desc = (XmlElement) en.SelectSingleNode ("Description");
+ if (desc != null)
+ sdesc = desc.InnerText;
+
+ sb.AppendFormat ("<tr><td><a href=\"extension-node:{0}#{1}#{2}\">{3}</td><td>{4}</td></tr>", fileId, addinId, nid, nname, sdesc);
+ }
+ sb.Append ("</table>");
+ }
+
+ sb.Append ("</html>");
+ return sb.ToString ();
+ }
+ }
+}
--- /dev/null
+using System;
+using System.IO;
+using System.Text;
+using System.Linq;
+using System.Xml;
+using System.Xml.Xsl;
+using System.Xml.XPath;
+using System.Collections.Generic;
+
+using Mono.Documentation;
+using BF = System.Reflection.BindingFlags;
+
+namespace MonkeyDoc.Generators.Html
+{
+ public class Ecma2Html : IHtmlExporter
+ {
+ static string css_ecma;
+ static string js;
+ static XslCompiledTransform ecma_transform;
+ readonly ExtensionObject ExtObject = new ExtensionObject ();
+
+ public Ecma2Html ()
+ {
+ }
+
+ public string CssCode {
+ get {
+ if (css_ecma != null)
+ return css_ecma;
+ var assembly = typeof(Ecma2Html).Assembly;
+ Stream str_css = assembly.GetManifestResourceStream ("mono-ecma.css");
+ css_ecma = (new StreamReader (str_css)).ReadToEnd();
+ return css_ecma;
+ }
+ }
+
+ public string JsCode {
+ get {
+ if (js != null)
+ return js;
+ var assembly = typeof(Ecma2Html).Assembly;
+ Stream str_js = assembly.GetManifestResourceStream ("helper.js");
+ js = (new StreamReader (str_js)).ReadToEnd();
+ return js;
+ }
+ }
+
+ public string Htmlize (XmlReader ecma_xml, Dictionary<string, string> extraArgs)
+ {
+ var args = new XsltArgumentList ();
+ args.AddExtensionObject("monodoc:///extensions", ExtObject);
+ foreach (var kvp in extraArgs)
+ args.AddParam (kvp.Key, string.Empty, kvp.Value);
+
+ return Htmlize(ecma_xml, args);
+ }
+
+ public string Htmlize (XmlReader ecma_xml, XsltArgumentList args)
+ {
+ EnsureTransform ();
+
+ var output = new StringBuilder ();
+ ecma_transform.Transform (ecma_xml,
+ args,
+ XmlWriter.Create (output, ecma_transform.OutputSettings),
+ CreateDocumentResolver ());
+ return output.ToString ();
+ }
+
+ protected virtual XmlResolver CreateDocumentResolver ()
+ {
+ // results in using XmlUrlResolver
+ return null;
+ }
+
+ public string Export (Stream stream, Dictionary<string, string> extraArgs)
+ {
+ return Htmlize (XmlReader.Create (stream), extraArgs);
+ }
+
+ public string Export (string input, Dictionary<string, string> extraArgs)
+ {
+ return Htmlize (XmlReader.Create (new StringReader (input)), extraArgs);
+ }
+
+ static void EnsureTransform ()
+ {
+ if (ecma_transform == null) {
+ ecma_transform = new XslCompiledTransform ();
+ var assembly = System.Reflection.Assembly.GetCallingAssembly ();
+
+ Stream stream = assembly.GetManifestResourceStream ("mono-ecma-css.xsl");
+ XmlReader xml_reader = new XmlTextReader (stream);
+ XmlResolver r = new ManifestResourceResolver (".");
+ ecma_transform.Load (xml_reader, XsltSettings.TrustedXslt, r);
+ }
+ }
+
+ public class ExtensionObject
+ {
+ bool quiet = true;
+
+ public string Colorize(string code, string lang)
+ {
+ return Mono.Utilities.Colorizer.Colorize(code,lang);
+ }
+
+ // Used by stylesheet to nicely reformat the <see cref=> tags.
+ public string MakeNiceSignature(string sig, string contexttype)
+ {
+ if (sig.Length < 3)
+ return sig;
+ if (sig[1] != ':')
+ return sig;
+
+ char s = sig[0];
+ sig = sig.Substring(2);
+
+ switch (s) {
+ case 'N': return sig;
+ case 'T': return ShortTypeName (sig, contexttype);
+
+ case 'C': case 'M': case 'P': case 'F': case 'E':
+ string type, mem, arg;
+
+ // Get arguments
+ int paren;
+ if (s == 'C' || s == 'M')
+ paren = sig.IndexOf("(");
+ else if (s == 'P')
+ paren = sig.IndexOf("[");
+ else
+ paren = 0;
+
+ if (paren > 0 && paren < sig.Length-1) {
+ string[] args = sig.Substring(paren+1, sig.Length-paren-2).Split(',');
+ for (int i = 0; i < args.Length; i++)
+ args[i] = ShortTypeName(args[i], contexttype);
+ arg = "(" + String.Join(", ", args) + ")";
+ sig = sig.Substring(0, paren);
+ } else {
+ arg = string.Empty;
+ }
+
+ // Get type and member names
+ int dot = sig.LastIndexOf(".");
+ if (s == 'C' || dot <= 0 || dot == sig.Length-1) {
+ mem = string.Empty;
+ type = sig;
+ } else {
+ type = sig.Substring(0, dot);
+ mem = sig.Substring(dot);
+ }
+
+ type = ShortTypeName(type, contexttype);
+
+ return type + mem + arg;
+
+ default:
+ return sig;
+ }
+ }
+
+ static string ShortTypeName(string name, string contexttype)
+ {
+ int dot = contexttype.LastIndexOf(".");
+ if (dot < 0) return name;
+ string contextns = contexttype.Substring(0, dot+1);
+
+ if (name == contexttype)
+ return name.Substring(dot+1);
+
+ if (name.StartsWith(contextns))
+ return name.Substring(contextns.Length);
+
+ return name.Replace("+", ".");
+ }
+
+ string MonoImpInfo(string assemblyname, string typename, string membername, string arglist, bool strlong)
+ {
+ if (quiet)
+ return string.Empty;
+
+ var a = new List<string> ();
+ if (!string.IsNullOrEmpty (arglist)) a.Add (arglist);
+ return MonoImpInfo(assemblyname, typename, membername, a, strlong);
+ }
+
+ string MonoImpInfo(string assemblyname, string typename, string membername, XPathNodeIterator itr, bool strlong)
+ {
+ if (quiet)
+ return string.Empty;
+
+ var rgs = itr.Cast<XPathNavigator> ().Select (nav => nav.Value).ToList ();
+
+ return MonoImpInfo (assemblyname, typename, membername, rgs, strlong);
+ }
+
+ string MonoImpInfo(string assemblyname, string typename, string membername, List<string> arglist, bool strlong)
+ {
+ try {
+ System.Reflection.Assembly assembly = null;
+
+ try {
+ assembly = System.Reflection.Assembly.LoadWithPartialName(assemblyname);
+ } catch (Exception) {
+ // nothing.
+ }
+
+ if (assembly == null) {
+ /*if (strlong) return "The assembly " + assemblyname + " is not available to MonoDoc.";
+ else return string.Empty;*/
+ return string.Empty; // silently ignore
+ }
+
+ Type t = assembly.GetType(typename, false);
+ if (t == null) {
+ if (strlong)
+ return typename + " has not been implemented.";
+ else
+ return "Not implemented.";
+ }
+
+ // The following code is flakey and fails to find existing members
+ return string.Empty;
+ } catch (Exception) {
+ return string.Empty;
+ }
+ }
+
+ string MonoImpInfo(System.Reflection.MemberInfo mi, string itemtype, bool strlong)
+ {
+ if (quiet)
+ return string.Empty;
+
+ string s = string.Empty;
+
+ object[] atts = mi.GetCustomAttributes(true);
+ int todoctr = 0;
+ foreach (object att in atts) if (att.GetType().Name == "MonoTODOAttribute") todoctr++;
+
+ if (todoctr > 0) {
+ if (strlong)
+ s = "This " + itemtype + " is marked as being unfinished.<BR/>\n";
+ else
+ s = "Unfinished.";
+ }
+
+ return s;
+ }
+
+ public string MonoImpInfo(string assemblyname, string typename, bool strlong)
+ {
+ if (quiet)
+ return string.Empty;
+
+ try {
+ if (assemblyname == string.Empty)
+ return string.Empty;
+
+ var assembly = System.Reflection.Assembly.LoadWithPartialName(assemblyname);
+ if (assembly == null)
+ return string.Empty;
+
+ Type t = assembly.GetType(typename, false);
+ if (t == null) {
+ if (strlong)
+ return typename + " has not been implemented.";
+ else
+ return "Not implemented.";
+ }
+
+ string s = MonoImpInfo(t, "type", strlong);
+
+ if (strlong) {
+ var mis = t.GetMembers (BF.Static | BF.Instance | BF.Public | BF.NonPublic);
+
+ // Scan members for MonoTODO attributes
+ int mctr = 0;
+ foreach (var mi in mis) {
+ string mii = MonoImpInfo(mi, null, false);
+ if (mii != string.Empty) mctr++;
+ }
+ if (mctr > 0) {
+ s += "This type has " + mctr + " members that are marked as unfinished.<BR/>";
+ }
+ }
+
+ return s;
+
+ } catch (Exception) {
+ return string.Empty;
+ }
+ }
+
+ public bool MonoEditing ()
+ {
+ return false;
+ }
+
+ public bool IsToBeAdded(string text)
+ {
+ return text.StartsWith ("To be added");
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.IO;
+using System.Xml;
+using System.Xml.Xsl;
+using System.Xml.XPath;
+using System.Collections.Generic;
+
+namespace MonkeyDoc.Generators.Html
+{
+ public class Ecmaspec2Html : IHtmlExporter
+ {
+ static string css_ecmaspec;
+ static XslTransform ecma_transform;
+ static XsltArgumentList args = new XsltArgumentList();
+
+ public string CssCode {
+ get {
+ if (css_ecmaspec != null)
+ return css_ecmaspec;
+ System.Reflection.Assembly assembly = System.Reflection.Assembly.GetCallingAssembly ();
+ Stream str_css = assembly.GetManifestResourceStream ("ecmaspec.css");
+ css_ecmaspec = (new StreamReader (str_css)).ReadToEnd ();
+ return css_ecmaspec;
+ }
+ }
+
+ class ExtObj
+ {
+ public string Colorize (string code, string lang)
+ {
+ return Mono.Utilities.Colorizer.Colorize (code, lang);
+ }
+ }
+
+ public string Export (Stream stream, Dictionary<string, string> extraArgs)
+ {
+ return Htmlize (new XPathDocument (stream));
+ }
+
+ public string Export (string input, Dictionary<string, string> extraArgs)
+ {
+ return Htmlize (new XPathDocument (new StringReader (input)));
+ }
+
+ static string Htmlize (XPathDocument ecma_xml)
+ {
+ if (ecma_transform == null){
+ ecma_transform = new XslTransform ();
+ System.Reflection.Assembly assembly = System.Reflection.Assembly.GetCallingAssembly ();
+ Stream stream;
+ stream = assembly.GetManifestResourceStream ("ecmaspec-html-css.xsl");
+
+ XmlReader xml_reader = new XmlTextReader (stream);
+ ecma_transform.Load (xml_reader, null, null);
+ args.AddExtensionObject ("monodoc:///extensions", new ExtObj ());
+ }
+
+ if (ecma_xml == null) return "";
+
+ StringWriter output = new StringWriter ();
+ ecma_transform.Transform (ecma_xml, args, output, null);
+
+ return output.ToString ();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.IO;
+using System.Linq;
+using System.Xml;
+using System.Xml.XPath;
+using System.Collections.Generic;
+
+namespace MonkeyDoc.Generators.Html
+{
+ public class Error2Html : IHtmlExporter
+ {
+ public string Export (string input, Dictionary<string, string> extraArgs)
+ {
+ return Htmlize (new XPathDocument (new StringReader (input)));
+ }
+
+ public string Export (Stream input, Dictionary<string, string> extraArgs)
+ {
+ return Htmlize (new XPathDocument (input));
+ }
+
+ public string CssCode {
+ get {
+ return @"
+ #error_ref {
+ background: #debcb0;
+ border: 2px solid #782609;
+ }
+ div.summary {
+ font-size: 110%;
+ font-weight: bolder;
+ }
+ div.details {
+ font-size: 110%;
+ font-weight: bolder;
+ }
+ div.code_example {
+ background: #f5f5dd;
+ border: 1px solid black;
+ padding-left: 1em;
+ padding-bottom: 1em;
+ margin-top: 1em;
+ white-space: pre;
+ margin-bottom: 1em;
+ }
+ div.code_ex_title {
+ position: relative;
+ top: -1em;
+ left: 30%;
+ background: #cdcd82;
+ border: 1px solid black;
+ color: black;
+ font-size: 65%;
+ text-transform: uppercase;
+ width: 40%;
+ padding: 0.3em;
+ text-align: center;
+ }";
+ }
+ }
+
+ public string Htmlize (IXPathNavigable doc)
+ {
+ var navigator = doc.CreateNavigator ();
+ var errorName = navigator.SelectSingleNode ("//ErrorDocumentation/ErrorName");
+ var details = navigator.SelectSingleNode ("//ErrorDocumentation/Details");
+
+ StringWriter sw = new StringWriter ();
+ XmlWriter w = new XmlTextWriter (sw);
+
+ WriteElementWithClass (w, "div", "header");
+ w.WriteAttributeString ("id", "error_ref");
+ WriteElementWithClass (w, "div", "subtitle", "Compiler Error Reference");
+ WriteElementWithClass (w, "div", "title", "Error " + (errorName == null ? string.Empty : errorName.Value));
+ w.WriteEndElement ();
+
+ if (details != null) {
+ WriteElementWithClass (w, "div", "summary", "Summary");
+
+ var summary = details.SelectSingleNode ("/Summary");
+ w.WriteValue (summary == null ? string.Empty : summary.Value);
+
+ WriteElementWithClass (w, "div", "details", "Details");
+ var de = details.SelectSingleNode ("/Details");
+ w.WriteValue (de == null ? string.Empty : de.Value);
+ }
+
+ foreach (XPathNavigator xmp in navigator.Select ("//ErrorDocumentation/Examples/string")) {
+ WriteElementWithClass (w, "div", "code_example");
+ WriteElementWithClass (w, "div", "code_ex_title", "Example");
+ w.WriteRaw (Mono.Utilities.Colorizer.Colorize (xmp.Value, "c#"));;
+ w.WriteEndElement ();
+ }
+
+ w.Close ();
+
+ return sw.ToString ();
+ }
+
+ void WriteElementWithClass (XmlWriter w, string element, string cls, string content = null)
+ {
+ w.WriteStartElement (element);
+ w.WriteAttributeString ("class", cls);
+ if (!string.IsNullOrEmpty (content)) {
+ w.WriteValue (content);
+ w.WriteEndElement ();
+ }
+ }
+ }
+}
--- /dev/null
+using System;
+using System.IO;
+using System.Text;
+using System.Collections.Generic;
+
+using MonkeyDoc;
+using MonkeyDoc.Generators;
+
+namespace MonkeyDoc.Generators.Html
+{
+ // Input is expected to be already HTML so just return it
+ public class Idem : IHtmlExporter
+ {
+ public string CssCode {
+ get {
+ return string.Empty;
+ }
+ }
+
+ public string Export (Stream input, Dictionary<string, string> extraArgs)
+ {
+ if (input == null)
+ return null;
+ return new StreamReader (input).ReadToEnd ();
+ }
+
+ public string Export (string input, Dictionary<string, string> extraArgs)
+ {
+ if (string.IsNullOrEmpty (input))
+ return null;
+ return input;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.IO;
+using System.Text;
+using System.Collections.Generic;
+
+using MonkeyDoc;
+using MonkeyDoc.Generators;
+
+namespace MonkeyDoc.Generators.Html
+{
+ public class Man2Html : IHtmlExporter
+ {
+ public string CssCode {
+ get {
+ return string.Empty;
+ }
+ }
+
+ public string Export (Stream input, Dictionary<string, string> extraArgs)
+ {
+ if (input == null)
+ return null;
+ return GetTextFromReader (new StreamReader (input));
+ }
+
+ public string Export (string input, Dictionary<string, string> extraArgs)
+ {
+ if (string.IsNullOrEmpty (input))
+ return null;
+ return GetTextFromReader (new StringReader (input));
+ }
+
+ public static string GetTextFromReader (TextReader file)
+ {
+ string line;
+ StateInfo s = new StateInfo ();
+
+ while ((line = file.ReadLine ()) != null)
+ ProcessLine (line, s);
+
+ return s.output.ToString ();
+ }
+
+ enum ListState {
+ None,
+ Start,
+ Title,
+ }
+
+ class StateInfo {
+ public ListState ls;
+ public Stack<string> tags = new Stack<string> ();
+ public StringBuilder output = new StringBuilder ();
+ }
+
+ static void ProcessLine (string line, StateInfo s)
+ {
+ string[] parts = SplitLine (line);
+ switch (parts [0]) {
+ case ".\\\"": // comments
+ case ".de": // define macro
+ case ".if": // if
+ case ".ne": // ???
+ case "..": // end macro
+ // ignore
+ break;
+ case ".I":
+ s.output.Append ("<i>");
+ Translate (parts, 1, s.output);
+ s.output.Append ("</i>");
+ break;
+ case ".B":
+ s.output.Append ("<b>");
+ Translate (parts, 1, s.output);
+ s.output.Append ("</b>");
+ break;
+ case ".br":
+ Translate (parts, 1, s.output);
+ s.output.Append ("<br />");
+ break;
+ case ".nf":
+ Expect (s, "</p>");
+ s.output.Append ("<pre>\n");
+ s.tags.Push ("</pre>");
+ break;
+ case ".fi":
+ Expect (s, "</pre>");
+ break;
+ case ".PP":
+ Expect (s, "</p>", "</dd>", "</dl>");
+ goto case ".Sp";
+ case ".Sp":
+ Expect (s, "</p>");
+ s.output.Append ("<p>");
+ Translate (parts, 1, s.output);
+ s.tags.Push ("</p>");
+ break;
+ case ".RS":
+ Expect (s, "</p>");
+ s.output.Append ("<blockquote>");
+ s.tags.Push ("</blockquote>");
+ break;
+ case ".RE":
+ ClearUntil (s, "</blockquote>");
+ break;
+ case ".SH":
+ ClearAll (s);
+ s.output.Append ("<h2>");
+ Translate (parts, 1, s.output);
+ s.output.Append ("</h2>")
+ .Append ("<blockquote>");
+ s.tags.Push ("</blockquote>");
+ break;
+ case ".SS":
+ s.output.Append ("<h3>");
+ Translate (parts, 1, s.output);
+ s.output.Append ("</h3>");
+ break;
+ case ".TH": {
+ ClearAll (s);
+ string name = "", extra = "";
+ if (parts.Length >= 4 && parts [2].Trim ().Length == 0) {
+ name = parts [1] + "(" + parts [3] + ")";
+ if (parts.Length > 4) {
+ int start = 4;
+ if (parts [start].Trim ().Length == 0)
+ ++start;
+ extra = string.Join ("", parts, start, parts.Length-start);
+ }
+ }
+ else
+ name = string.Join ("", parts, 1, parts.Length-1);
+ s.output.Append ("<table width=\"100%\" bgcolor=\"#b0c4da\">" +
+ "<tr colspan=\"2\"><td>Manual Pages</td></tr>\n" +
+ "<tr><td><h3>");
+ Translate (name, s.output);
+ s.output.Append ("</h3></td><td align=\"right\">");
+ Translate (extra, s.output);
+ s.output.Append ("</td></tr></table>");
+ break;
+ }
+ case ".TP":
+ Expect (s, "</p>");
+ if (s.tags.Count > 0 && s.tags.Peek ().ToString () != "</dd>") {
+ s.output.Append ("<dl>");
+ s.tags.Push ("</dl>");
+ }
+ else
+ Expect (s, "</dd>");
+ s.output.Append ("<dt>");
+ s.tags.Push ("</dt>");
+ s.ls = ListState.Start;
+ break;
+ default:
+ Translate (line, s.output);
+ break;
+ }
+ if (s.ls == ListState.Start)
+ s.ls = ListState.Title;
+ else if (s.ls == ListState.Title) {
+ Expect (s, "</dt>");
+ s.output.Append ("<dd>");
+ s.tags.Push ("</dd>");
+ s.ls = ListState.None;
+ }
+ s.output.Append ("\n");
+ }
+
+ static string[] SplitLine (string line)
+ {
+ if (line.Length > 1 && line [0] != '.')
+ return new string[]{null, line};
+
+ int i;
+ for (i = 0; i < line.Length; ++i) {
+ if (char.IsWhiteSpace (line, i))
+ break;
+ }
+
+ if (i == line.Length)
+ return new string[]{line};
+
+ var pieces = new List<string> ();
+ pieces.Add (line.Substring (0, i));
+ bool inQuotes = false;
+ bool prevWs = true;
+ ++i;
+ int start = i;
+ for ( ; i < line.Length; ++i) {
+ char c = line [i];
+ if (inQuotes) {
+ if (c == '"') {
+ Add (pieces, line, start, i);
+ start = i+1;
+ inQuotes = false;
+ }
+ }
+ else {
+ if (prevWs && c == '"') {
+ Add (pieces, line, start, i);
+ start = i+1;
+ inQuotes = true;
+ }
+ else if (char.IsWhiteSpace (c)) {
+ if (!prevWs) {
+ Add (pieces, line, start, i);
+ start = i;
+ }
+ prevWs = true;
+ }
+ else {
+ if (prevWs) {
+ Add (pieces, line, start, i);
+ start = i;
+ }
+ prevWs = false;
+ }
+ }
+ }
+ if (start > 0 && start != line.Length)
+ pieces.Add (line.Substring (start, line.Length-start));
+ return pieces.ToArray ();
+ }
+
+ static void Add (List<string> pieces, string line, int start, int end)
+ {
+ if (start == end)
+ return;
+ pieces.Add (line.Substring (start, end-start));
+ }
+
+ static void Expect (StateInfo s, params string[] expected)
+ {
+ string e;
+ while (s.tags.Count > 0 &&
+ Array.IndexOf (expected, (e = s.tags.Peek ().ToString ())) >= 0) {
+ s.output.Append (s.tags.Pop ().ToString ());
+ }
+ }
+
+ static void ClearUntil (StateInfo s, string required)
+ {
+ string e;
+ while (s.tags.Count > 0 &&
+ (e = s.tags.Peek ().ToString ()) != required) {
+ s.output.Append (s.tags.Pop ().ToString ());
+ }
+ if (e == required)
+ s.output.Append (s.tags.Pop ().ToString ());
+ }
+
+ static void ClearAll (StateInfo s)
+ {
+ while (s.tags.Count > 0)
+ s.output.Append (s.tags.Pop ().ToString ());
+ }
+
+ static void Translate (string[] lines, int startIndex, StringBuilder output)
+ {
+ if (lines.Length <= startIndex)
+ return;
+ do {
+ Translate (lines [startIndex++], output);
+ if (startIndex == lines.Length)
+ break;
+ } while (startIndex < lines.Length);
+ }
+
+ static void Translate (string line, StringBuilder output)
+ {
+ string span = null;
+ int start = output.Length;
+ for (int i = 0; i < line.Length; ++i) {
+ switch (line [i]) {
+ case '\\': {
+ if ((i+2) < line.Length && line [i+1] == 'f') {
+ if (line [i+2] == 'I') {
+ output.Append ("<i>");
+ span = "</i>";
+ }
+ else if (line [i+2] == 'B') {
+ output.Append ("<b>");
+ span = "</b>";
+ }
+ else if (line [i+2] == 'R' || line [i+2] == 'P') {
+ output.Append (span);
+ }
+ else
+ goto default;
+ i += 2;
+ }
+ else if ((i+1) < line.Length) {
+ output.Append (line [i+1]);
+ ++i;
+ }
+ else
+ goto default;
+ break;
+ }
+ case '<':
+ output.Append ("<");
+ break;
+ case '>':
+ output.Append (">");
+ break;
+ case '&':
+ output.Append ("&");
+ break;
+ default:
+ output.Append (line [i]);
+ break;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.IO;
+using System.Text;
+using System.Xml;
+using System.Collections.Generic;
+
+using MonkeyDoc;
+using MonkeyDoc.Generators;
+
+namespace MonkeyDoc.Generators.Html
+{
+ // Input is expected to be already HTML so just return it
+ public class MonoBook2Html : IHtmlExporter
+ {
+ public string CssCode {
+ get {
+ return @" h3 {
+ font-size: 18px;
+ padding-bottom: 4pt;
+ border-bottom: 2px solid #dddddd;
+ }
+
+ .api {
+ border: 1px solid;
+ padding: 10pt;
+ margin: 10pt;
+ }
+
+ .api-entry {
+ border-bottom: none;
+ font-size: 18px;
+ }
+
+ .prototype {
+ border: 1px solid;
+ background-color: #f2f2f2;
+ padding: 5pt;
+ margin-top: 5pt;
+ margin-bottom: 5pt;
+ }
+
+ .header {
+ border: 1px solid !important;
+ padding: 0 0 5pt 5pt !important;
+ margin: 10pt !important;
+ white-space: pre !important;
+ font-family: monospace !important;
+ font-weight: normal !important;
+ font-size: 1em !important;
+ }
+
+ .code {
+ border: 1px solid;
+ padding: 0 0 5pt 5pt;
+ margin: 10pt;
+ white-space: pre;
+ font-family: monospace;
+ }
+";
+ }
+ }
+
+ public string Export (Stream input, Dictionary<string, string> extraArgs)
+ {
+ if (input == null)
+ return null;
+ return FromXmlReader (XmlReader.Create (input));
+ }
+
+ public string Export (string input, Dictionary<string, string> extraArgs)
+ {
+ if (string.IsNullOrEmpty (input))
+ return null;
+ return FromXmlReader (XmlReader.Create (new StringReader (input)));
+ }
+
+ public string FromXmlReader (XmlReader reader)
+ {
+ if (!reader.ReadToDescendant ("head"))
+ return null;
+ if (!reader.ReadToNextSibling ("body"))
+ return null;
+
+ return reader.ReadInnerXml ();
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.IO;
+using System.Xml;
+using System.Xml.Xsl;
+using System.Xml.XPath;
+using System.Reflection;
+using System.Collections.Generic;
+
+namespace MonkeyDoc.Generators.Html
+{
+ public class Toc2Html : IHtmlExporter
+ {
+ XslTransform transform;
+
+ public Toc2Html ()
+ {
+ transform = new XslTransform ();
+ var assembly = Assembly.GetCallingAssembly ();
+ var stream = assembly.GetManifestResourceStream ("toc-html.xsl");
+ XmlReader xml_reader = new XmlTextReader (stream);
+ transform.Load (xml_reader, null, null);
+ }
+
+ public string Export (Stream input, Dictionary<string, string> extraArgs)
+ {
+ var output = new StringWriter ();
+ transform.Transform (new XPathDocument (input), null, output, null);
+ return output.ToString ();
+ }
+
+ public string Export (string input, Dictionary<string, string> extraArgs)
+ {
+ var output = new StringWriter ();
+ transform.Transform (new XPathDocument (new StringReader (input)), null, output, null);
+ return output.ToString ();
+ }
+
+ public string CssCode {
+ get {
+ return string.Empty;
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+//
+// index.cs: Handling of the index files
+//
+// Author:
+// Miguel de Icaza (miguel@xamarin.com)
+//
+// (C) 2003 Ximian, Inc.
+// Copyright 2003-2011 Novell Inc
+// Copyright 2011 Xamarin Inc.
+//
+// Possible file format optimizations:
+// * Do not use 4 bytes for each index entry, use 3 bytes
+// * Find a way of compressing strings, there are plenty of duplicates
+// Find common roots, and use an encoding that uses a root to compress data.
+// "System", "System.Data", "System.Data class"
+// 0: PLAIN: "System"
+// 1: PLAIN: " class"
+// 2: LINK0 PLAIN ".DATA"
+// 3: LINK0 LINK1
+//
+// Maybe split everything at spaces and dots, and encode that:
+// string-1-idx "System."
+// string-1-idx "Data"
+// 2-items [ string-1-idx string-2-idx]
+//
+// Other variations are possible; Like Archive "System", "System." when we
+// see "System.Data".
+//
+//
+
+using System;
+using System.IO;
+using System.Text;
+using System.Collections.Generic;
+
+namespace MonkeyDoc
+{
+ public class Topic
+ {
+ public readonly string Caption;
+ public readonly string SortKey;
+ public readonly string Url;
+
+ public Topic (string caption, string sort_key, string url)
+ {
+ Caption = caption;
+ SortKey = sort_key;
+ Url = url;
+ }
+ }
+
+ public class IndexEntry
+ {
+ List<Topic> topics;
+
+ public int Position {
+ get;
+ private set;
+ }
+
+ public IList<Topic> Topics {
+ get {
+ return topics.AsReadOnly ();
+ }
+ }
+
+ public int Count {
+ get;
+ private set;
+ }
+
+ public void Add (Topic t)
+ {
+ Count++;
+ topics.Add (t);
+ }
+
+ public Topic this [int idx] {
+ get {
+ if (idx < 0 || idx > topics.Count)
+ throw new ArgumentOutOfRangeException ("idx");
+ return topics[idx];
+ }
+ }
+
+ //
+ // Constructor from a stream
+ //
+ public IndexEntry (FileStream fs, BinaryReader reader, int position)
+ {
+ Count = reader.ReadInt32 ();
+ int caption_offset = reader.ReadInt32 ();
+ string caption;
+ topics = new List<Topic> (Count);
+
+ int [] offsets = new int [Count];
+ for (int i = 0; i < Count; i++)
+ offsets [i] = reader.ReadInt32 ();
+
+ fs.Position = caption_offset;
+ caption = reader.ReadString ();
+ for (int i = 0; i < Count; i++){
+ fs.Position = offsets [i];
+ string url = reader.ReadString ();
+ topics.Add (new Topic (caption, string.Empty, url));
+ }
+ }
+
+ //
+ // Regular constructor
+
+ public IndexEntry ()
+ {
+ topics = new List<Topic> ();
+ }
+
+ public void WriteTopics (IndexMaker maker, Stream stream, BinaryWriter writer)
+ {
+ //
+ // Convention: entries with the same SortKey should have the same Caption
+ //
+ Position = (int) stream.Position;
+ writer.Write (Count);
+
+ if (Count == 0)
+ return;
+
+ writer.Write (maker.GetCode (topics[0].Caption));
+ foreach (Topic t in topics)
+ writer.Write (maker.GetCode (t.Url));
+ }
+ }
+
+ public class IndexMaker
+ {
+ Dictionary<string, IndexEntry> entries = new Dictionary<string, IndexEntry> ();
+ Dictionary<string, int> all_strings = new Dictionary<string, int> ();
+ int index_position;
+
+ void AddString (string str)
+ {
+ if (!all_strings.ContainsKey (str))
+ all_strings.Add (str, 0);
+ }
+
+ public void AddTopic (Topic topic)
+ {
+ IndexEntry entry;
+ if (!entries.TryGetValue (topic.SortKey, out entry)) {
+ entry = new IndexEntry ();
+ entries[topic.SortKey] = entry;
+ }
+
+ AddString (topic.SortKey);
+ AddString (topic.Caption);
+ AddString (topic.Url);
+ entry.Add (topic);
+ }
+
+ public void Add (string caption, string sort_key, string url)
+ {
+ Topic t = new Topic (caption, sort_key, url);
+ AddTopic (t);
+ }
+
+ void SaveStringTable (Stream stream, BinaryWriter writer)
+ {
+ var keys = new List<string> (all_strings.Keys);
+ foreach (string s in keys) {
+ int pos = (int) stream.Position;
+ writer.Write (s);
+ all_strings [s] = pos;
+ }
+ }
+
+ public int GetCode (string s)
+ {
+ return all_strings [s];
+ }
+
+ void SaveTopics (Stream stream, BinaryWriter writer)
+ {
+ //
+ // Convention: entries with the same SortKey should have the same Caption
+ //
+ foreach (IndexEntry e in entries.Values)
+ e.WriteTopics (this, stream, writer);
+ }
+
+ void SaveIndexEntries (Stream stream, BinaryWriter writer)
+ {
+ index_position = (int) stream.Position;
+ writer.Write (entries.Count);
+ var keys = new List<string> (entries.Keys);
+ keys.Sort (StringComparer.OrdinalIgnoreCase);
+
+ foreach (string s in keys){
+ IndexEntry e = entries [s];
+ writer.Write (e.Position);
+ }
+ }
+
+ public void Save (string filename)
+ {
+ Encoding utf8 = new UTF8Encoding (false, true);
+
+ using (FileStream fs = File.OpenWrite (filename)){
+ BinaryWriter writer = new BinaryWriter (fs, utf8);
+ writer.Write (new byte [] { (byte) 'M',
+ (byte) 'o', (byte) 'i',
+ (byte) 'x'});
+
+ // Leave room for pointer
+ fs.Position = 8;
+
+ SaveStringTable (fs, writer);
+ SaveTopics (fs, writer);
+
+ // index_position is set here
+
+ SaveIndexEntries (fs, writer);
+
+ fs.Position = 4;
+ writer.Write (index_position);
+ }
+ }
+ }
+
+ public interface IListModel
+ {
+ int Rows { get; }
+ string GetValue (int row);
+ string GetDescription (int row);
+ }
+
+ public class IndexReader : IListModel
+ {
+ Encoding utf8 = new UTF8Encoding (false, true);
+ FileStream fs;
+ BinaryReader reader;
+
+ // The offset of the table of entries
+ int table_offset;
+ int entries;
+
+ static public IndexReader Load (string filename)
+ {
+ if (!File.Exists (filename))
+ return null;
+
+ try {
+ return new IndexReader (filename);
+ } catch {
+ return null;
+ }
+ }
+
+ IndexReader (string filename)
+ {
+ fs = File.OpenRead (filename);
+ reader = new BinaryReader (fs, utf8);
+
+ if (fs.ReadByte () != 'M' ||
+ fs.ReadByte () != 'o' ||
+ fs.ReadByte () != 'i' ||
+ fs.ReadByte () != 'x'){
+ throw new Exception ("Corrupt index");
+ }
+
+ // Seek to index_entries
+ fs.Position = reader.ReadInt32 ();
+
+ entries = reader.ReadInt32 ();
+
+ table_offset = (int) fs.Position;
+ }
+
+ public int Rows {
+ get {
+ return entries;
+ }
+ }
+
+ public string GetValue (int row)
+ {
+ fs.Position = row * 4 + table_offset;
+ fs.Position = reader.ReadInt32 () + 4;
+ int code = reader.ReadInt32 ();
+ fs.Position = code;
+ string caption = reader.ReadString ();
+
+ return caption;
+ }
+
+ public string GetDescription (int row)
+ {
+ return GetValue (row);
+ }
+
+ public IndexEntry GetIndexEntry (int row)
+ {
+ fs.Position = row * 4 + table_offset;
+ int entry_offset = reader.ReadInt32 ();
+ fs.Position = entry_offset;
+
+ return new IndexEntry (fs, reader, entry_offset);
+ }
+ }
+}
+
--- /dev/null
+using System;
+using System.Linq;
+using System.IO;
+using System.Text;
+using System.Xml;
+using System.Xml.Linq;
+using System.Collections.Generic;
+
+namespace MonkeyDoc.Providers
+{
+ // Common functionality between ecma-provider and ecmauncompiled-provider
+ internal class EcmaDoc
+ {
+ public static void PopulateTreeFromIndexFile (string indexFilePath,
+ Tree tree,
+ IDocStorage storage,
+ Dictionary<string, XElement> nsSummaries,
+ Func<XElement, string> indexGenerator = null)
+ {
+ var root = tree.RootNode;
+ int resID = 0;
+ var asm = Path.GetDirectoryName (indexFilePath);
+
+ storage = storage ?? new Storage.NullStorage ();
+ // nsSummaries is allowed to be null if the user doesn't care about it
+ nsSummaries = nsSummaries ?? new Dictionary<string, XElement> ();
+ // default index generator uses a counter
+ indexGenerator = indexGenerator ?? (_ => resID++.ToString ());
+
+ using (var reader = XmlReader.Create (File.OpenRead (indexFilePath))) {
+ reader.ReadToFollowing ("Types");
+ var types = XElement.Load (reader.ReadSubtree ());
+
+ foreach (var ns in types.Elements ("Namespace")) {
+ var nsName = (string)ns.Attribute ("Name");
+ nsName = !string.IsNullOrEmpty (nsName) ? nsName : "global";
+ var nsNode = root.GetOrCreateNode (nsName, "N:" + nsName);
+
+ XElement nsElements;
+ if (!nsSummaries.TryGetValue (nsName, out nsElements))
+ nsSummaries[nsName] = nsElements = new XElement ("elements",
+ new XElement ("summary"),
+ new XElement ("remarks"));
+
+ foreach (var type in ns.Elements ("Type")) {
+ // Add the XML file corresponding to the type to our storage
+ var id = indexGenerator (type);
+ string typeFilePath;
+ var typeDocument = EcmaDoc.LoadTypeDocument (asm, nsName, type.Attribute ("Name").Value, out typeFilePath);
+ if (typeDocument == null)
+ continue;
+ using (var file = File.OpenRead (typeFilePath))
+ storage.Store (id, file);
+ nsElements.Add (ExtractClassSummary (typeFilePath));
+
+ var typeCaption = EcmaDoc.GetTypeCaptionFromIndex (type);
+ var url = "ecma:" + id + '#' + typeCaption + '/';
+ typeCaption = EcmaDoc.GetTypeCaptionFromIndex (type, true);
+ var typeNode = nsNode.CreateNode (typeCaption, url);
+
+ // Add meta "Members" node
+ typeNode.CreateNode ("Members", "*");
+ var membersNode = typeDocument.Root.Element ("Members");
+ if (membersNode == null || !membersNode.Elements ().Any ())
+ continue;
+ var members = membersNode
+ .Elements ("Member")
+ .ToLookup (EcmaDoc.GetMemberType);
+
+ foreach (var memberType in members) {
+ // We pluralize the member type to get the caption and take the first letter as URL
+ var node = typeNode.CreateNode (EcmaDoc.PluralizeMemberType (memberType.Key), memberType.Key[0].ToString ());
+ var memberIndex = 0;
+
+ var isCtors = memberType.Key[0] == 'C';
+
+ // We do not escape much member name here
+ foreach (var memberGroup in memberType.GroupBy (m => MakeMemberCaption (m, isCtors))) {
+ if (memberGroup.Count () > 1) {
+ // Generate overload
+ var overloadCaption = MakeMemberCaption (memberGroup.First (), false);
+ var overloadNode = node.CreateNode (overloadCaption, overloadCaption);
+ foreach (var member in memberGroup)
+ overloadNode.CreateNode (MakeMemberCaption (member, true), (memberIndex++).ToString ());
+ overloadNode.Sort ();
+ } else {
+ // We treat constructor differently by showing their argument list in all cases
+ node.CreateNode (MakeMemberCaption (memberGroup.First (), isCtors), (memberIndex++).ToString ());
+ }
+ }
+ node.Sort ();
+ }
+ }
+
+ nsNode.Sort ();
+ }
+ root.Sort ();
+ }
+ }
+
+ // Utility methods
+
+ public static XDocument LoadTypeDocument (string basePath, string nsName, string typeName)
+ {
+ string dummy;
+ return LoadTypeDocument (basePath, nsName, typeName, out dummy);
+ }
+
+ public static XDocument LoadTypeDocument (string basePath, string nsName, string typeName, out string finalPath)
+ {
+ finalPath = Path.Combine (basePath, nsName, Path.ChangeExtension (typeName, ".xml"));
+ if (!File.Exists (finalPath)) {
+ Console.Error.WriteLine ("Warning: couldn't process type file `{0}' as it doesn't exist", finalPath);
+ return null;
+ }
+ return XDocument.Load (finalPath);
+ }
+
+ public static string GetTypeCaptionFromIndex (XElement typeNodeFromIndex, bool full = false)
+ {
+ var t = typeNodeFromIndex;
+ var c = ((string)(t.Attribute ("DisplayName") ?? t.Attribute ("Name"))).Replace ('+', '.');
+ if (full)
+ c += " " + (string)t.Attribute ("Kind");
+ return c;
+ }
+
+ public static string PluralizeMemberType (string memberType)
+ {
+ switch (memberType) {
+ case "Property":
+ return "Properties";
+ default:
+ return memberType + "s";
+ }
+ }
+
+ public static string GetMemberType (XElement m)
+ {
+ return m.Attribute ("MemberName").Value.StartsWith ("op_") ? "Operator" : m.Element ("MemberType").Value;
+ }
+
+ public static string MakeMemberCaption (XElement member, bool withArguments)
+ {
+ var caption = (string)member.Attribute ("MemberName");
+ // Use type name instead of .ctor for cosmetic sake
+ if (caption == ".ctor") {
+ caption = (string)member.Ancestors ("Type").First ().Attribute ("Name");
+ // If this is an inner type ctor, strip the parent type reference
+ var plusIndex = caption.LastIndexOf ('+');
+ if (plusIndex != -1)
+ caption = caption.Substring (plusIndex + 1);
+ }
+ if (caption.StartsWith ("op_")) {
+ string sig;
+ caption = MakeOperatorSignature (member, out sig);
+ caption = withArguments ? sig : caption;
+ return caption;
+ }
+ if (withArguments) {
+ var args = member.Element ("Parameters");
+ caption += '(';
+ if (args != null && args.Elements ("Parameter").Any ()) {
+ caption += args.Elements ("Parameter")
+ .Select (p => (string)p.Attribute ("Type"))
+ .Aggregate ((p1, p2) => p1 + "," + p2);
+ }
+ caption += ')';
+ }
+
+ return caption;
+ }
+
+ internal static string MakeOperatorSignature (XElement member, out string memberSignature)
+ {
+ string name = (string)member.Attribute ("MemberName");
+ var nicename = name.Substring(3);
+ memberSignature = null;
+
+ switch (name) {
+ // unary operators: no overloading possible [ECMA-335 §10.3.1]
+ case "op_UnaryPlus": // static R operator+ (T)
+ case "op_UnaryNegation": // static R operator- (T)
+ case "op_LogicalNot": // static R operator! (T)
+ case "op_OnesComplement": // static R operator~ (T)
+ case "op_Increment": // static R operator++ (T)
+ case "op_Decrement": // static R operator-- (T)
+ case "op_True": // static bool operator true (T)
+ case "op_False": // static bool operator false (T)
+ case "op_AddressOf": // static R operator& (T)
+ case "op_PointerDereference": // static R operator* (T)
+ memberSignature = nicename;
+ break;
+ // conversion operators: overloading based on parameter and return type [ECMA-335 §10.3.3]
+ case "op_Implicit": // static implicit operator R (T)
+ case "op_Explicit": // static explicit operator R (T)
+ nicename = name.EndsWith ("Implicit") ? "ImplicitConversion" : "ExplicitConversion";
+ string arg = (string)member.Element ("Parameters").Element ("Parameter").Attribute ("Type");
+ string ret = (string)member.Element ("ReturnValue").Element ("ReturnType");
+ memberSignature = arg + " to " + ret;
+ break;
+ // binary operators: overloading is possible [ECMA-335 §10.3.2]
+ default:
+ memberSignature =
+ nicename + "("
+ + string.Join (",", member.Element ("Parameters").Elements ("Parameter").Select (p => (string)p.Attribute ("Type")))
+ + ")";
+ break;
+ }
+
+ return nicename;
+ }
+
+ static XElement ExtractClassSummary (string typeFilePath)
+ {
+ using (var reader = XmlReader.Create (typeFilePath)) {
+ reader.ReadToFollowing ("Type");
+ var name = reader.GetAttribute ("Name");
+ var fullName = reader.GetAttribute ("FullName");
+ reader.ReadToFollowing ("AssemblyName");
+ var assemblyName = reader.ReadElementString ();
+ reader.ReadToFollowing ("summary");
+ var summary = reader.ReadInnerXml ();
+ reader.ReadToFollowing ("remarks");
+ var remarks = reader.ReadInnerXml ();
+
+ return new XElement ("class",
+ new XAttribute ("name", name ?? string.Empty),
+ new XAttribute ("fullname", fullName ?? string.Empty),
+ new XAttribute ("assembly", assemblyName ?? string.Empty),
+ new XElement ("summary", new XCData (summary)),
+ new XElement ("remarks", new XCData (remarks)));
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+// addins-provider.cs
+//
+// A provider to display Mono.Addins extension models
+//
+// Author:
+// Lluis Sanchez Gual <lluis@novell.com>
+//
+// Copyright (c) 2007 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+//
+
+using System;
+using System.Linq;
+using System.Diagnostics;
+using System.Text;
+using System.IO;
+using System.Xml;
+using System.Collections.Generic;
+
+namespace MonkeyDoc.Providers
+{
+ public class AddinsProvider : Provider
+ {
+ string file;
+
+ public AddinsProvider (string xmlModelFile)
+ {
+ file = xmlModelFile;
+
+ if (!File.Exists (file))
+ throw new FileNotFoundException (String.Format ("The file `{0}' does not exist", file));
+ }
+
+ public override void PopulateTree (Tree tree)
+ {
+ string fileId = Path.GetFileNameWithoutExtension (file);
+ using (var f = File.OpenRead (file))
+ tree.HelpSource.Storage.Store (fileId, f);
+
+ XmlDocument doc = new XmlDocument ();
+ doc.Load (file);
+
+ foreach (XmlElement addin in doc.SelectNodes ("Addins/Addin")) {
+
+ string addinId = addin.GetAttribute ("fullId");
+ Node newNode = tree.RootNode.CreateNode (addin.GetAttribute ("name"), "addin:" + fileId + "#" + addinId);
+
+ foreach (XmlElement node in addin.SelectNodes ("ExtensionPoint")) {
+ string target = "extension-point:" + fileId + "#" + addinId + "#" + node.GetAttribute ("path");
+ Node newExt = newNode.CreateNode (node.GetAttribute ("name"), target);
+
+ foreach (XmlElement en in node.SelectNodes ("ExtensionNode")) {
+ string nid = en.GetAttribute ("id");
+ string nname = en.GetAttribute ("name");
+ newExt.CreateNode (nname, "extension-node:" + fileId + "#" + addinId + "#" + nid);
+ }
+ }
+ }
+ }
+
+ public override void CloseTree (HelpSource hs, Tree tree)
+ {
+ }
+ }
+
+ public class AddinsHelpSource : HelpSource
+ {
+ public AddinsHelpSource (string base_file, bool create) : base (base_file, create)
+ {
+ }
+
+ internal protected const string AddinPrefix = "addin:";
+ internal protected const string ExtensionPrefix = "extension-point:";
+ internal protected const string ExtensionNodePrefix = "extension-node:";
+
+ public override bool CanHandleUrl (string url)
+ {
+ return url.StartsWith (AddinPrefix, StringComparison.OrdinalIgnoreCase)
+ || url.StartsWith (ExtensionPrefix, StringComparison.OrdinalIgnoreCase)
+ || url.StartsWith (ExtensionNodePrefix, StringComparison.OrdinalIgnoreCase);
+ }
+
+ protected override string UriPrefix {
+ get {
+ return AddinPrefix;
+ }
+ }
+
+ public override DocumentType GetDocumentTypeForId (string id, out Dictionary<string, string> extraArgs)
+ {
+ extraArgs = new Dictionary<string, string> ();
+ var idParts = id.Split ('#');
+ extraArgs["FileID"] = idParts[0];
+ extraArgs["AddinID"] = idParts[1];
+ extraArgs["NodeID"] = idParts[2];
+
+ return DocumentType.AddinXml;
+ }
+
+ public override Node MatchNode (string url)
+ {
+ var prefix = new[] { AddinPrefix, ExtensionPrefix, ExtensionNodePrefix }.First (p => url.StartsWith (p, StringComparison.OrdinalIgnoreCase));
+ return base.MatchNode (prefix != null ? url.Substring (prefix.Length) : url);
+ }
+
+ public override Stream GetHelpStream (string id)
+ {
+ var idParts = id.Split ('#');
+ return base.GetHelpStream (idParts[0]);
+ }
+
+ public override Stream GetCachedHelpStream (string id)
+ {
+ var idParts = id.Split ('#');
+ return base.GetHelpStream (idParts[0]);
+ }
+ }
+}
--- /dev/null
+//
+// The ecmaspec provider is for ECMA specifications
+//
+// Authors:
+// John Luke (jluke@cfl.rr.com)
+// Ben Maurer (bmaurer@users.sourceforge.net)
+//
+// Use like this:
+// mono assembler.exe --ecmaspec DIRECTORY --out name
+//
+
+using System;
+using System.Linq;
+using System.IO;
+using System.Text;
+using System.Xml;
+using System.Xml.Linq;
+using System.Collections.Generic;
+
+using Lucene.Net.Index;
+using Lucene.Net.Documents;
+
+using MonkeyDoc.Ecma;
+using Mono.Utilities;
+
+namespace MonkeyDoc.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> ();
+
+ public EcmaProvider ()
+ {
+ }
+
+ public EcmaProvider (string baseDir)
+ {
+ AddDirectory (baseDir);
+ }
+
+ public void AddDirectory (string directory)
+ {
+ if (string.IsNullOrEmpty (directory))
+ throw new ArgumentNullException ("directory");
+
+ directories.Add (directory);
+ }
+
+ public override void PopulateTree (Tree tree)
+ {
+ var storage = tree.HelpSource.Storage;
+ var nsSummaries = new Dictionary<string, XElement> ();
+ int resID = 0;
+
+ foreach (var asm in directories) {
+ var indexFilePath = Path.Combine (asm, "index.xml");
+ if (!File.Exists (indexFilePath)) {
+ Console.Error.WriteLine ("Warning: couldn't process directory `{0}' as it has no index.xml file", asm);
+ continue;
+ }
+
+ EcmaDoc.PopulateTreeFromIndexFile (indexFilePath, tree, storage, nsSummaries, _ => resID++.ToString ());
+ }
+
+ foreach (var summary in nsSummaries)
+ storage.Store ("xml.summary." + summary.Key, summary.Value.ToString ());
+
+ var masterSummary = new XElement ("elements",
+ directories
+ .SelectMany (d => Directory.EnumerateFiles (d, "ns-*.xml"))
+ .Select (ExtractNamespaceSummary));
+ storage.Store ("mastersummary.xml", masterSummary.ToString ());
+ }
+
+ XElement ExtractNamespaceSummary (string nsFile)
+ {
+ using (var reader = XmlReader.Create (nsFile)) {
+ reader.ReadToFollowing ("Namespace");
+ var name = reader.GetAttribute ("Name");
+ reader.ReadToFollowing ("summary");
+ var summary = reader.ReadInnerXml ();
+ reader.ReadToFollowing ("remarks");
+ var remarks = reader.ReadInnerXml ();
+
+ return new XElement ("namespace",
+ new XAttribute ("ns", name ?? string.Empty),
+ new XElement ("summary", new XCData (summary)),
+ new XElement ("remarks", new XCData (remarks)));
+ }
+ }
+
+ public override void CloseTree (HelpSource hs, Tree tree)
+ {
+ AddImages (hs);
+ AddExtensionMethods (hs);
+ }
+
+ void AddEcmaXml (HelpSource hs)
+ {
+ var xmls = directories
+ .SelectMany (Directory.EnumerateDirectories) // Assemblies
+ .SelectMany (Directory.EnumerateDirectories) // Namespaces
+ .SelectMany (Directory.EnumerateFiles)
+ .Where (f => f.EndsWith (".xml")); // Type XML files
+
+ int resID = 0;
+ foreach (var xml in xmls)
+ using (var file = File.OpenRead (xml))
+ hs.Storage.Store ((resID++).ToString (), file);
+ }
+
+ void AddImages (HelpSource hs)
+ {
+ var imgs = directories
+ .SelectMany (Directory.EnumerateDirectories)
+ .Select (d => Path.Combine (d, "_images"))
+ .Where (Directory.Exists)
+ .SelectMany (Directory.EnumerateFiles);
+
+ foreach (var img in imgs)
+ using (var file = File.OpenRead (img))
+ hs.Storage.Store (Path.GetFileName (img), file);
+ }
+
+ void AddExtensionMethods (HelpSource hs)
+ {
+ var extensionMethods = directories
+ .SelectMany (Directory.EnumerateDirectories)
+ .Select (d => Path.Combine (d, "index.xml"))
+ .Where (File.Exists)
+ .Select (f => {
+ using (var file = File.OpenRead (f)) {
+ var reader = XmlReader.Create (file);
+ reader.ReadToFollowing ("ExtensionMethods");
+ return reader.ReadInnerXml ();
+ }
+ })
+ .DefaultIfEmpty (string.Empty);
+
+ hs.Storage.Store ("ExtensionMethods.xml",
+ "<ExtensionMethods>" + extensionMethods.Aggregate (string.Concat) + "</ExtensionMethods>");
+ }
+
+ IEnumerable<string> GetEcmaXmls ()
+ {
+ return directories
+ .SelectMany (Directory.EnumerateDirectories) // Assemblies
+ .SelectMany (Directory.EnumerateDirectories) // Namespaces
+ .SelectMany (Directory.EnumerateFiles)
+ .Where (f => f.EndsWith (".xml")); // Type XML files
+ }
+ }
+
+ public class EcmaHelpSource : HelpSource
+ {
+ const string EcmaPrefix = "ecma:";
+ EcmaUrlParser parser = new EcmaUrlParser ();
+ LRUCache<string, Node> cache = new LRUCache<string, Node> (4);
+
+ public EcmaHelpSource (string base_file, bool create) : base (base_file, create)
+ {
+ }
+
+ protected EcmaHelpSource () : base ()
+ {
+ }
+
+ protected override string UriPrefix {
+ get {
+ return EcmaPrefix;
+ }
+ }
+
+ public override bool CanHandleUrl (string url)
+ {
+ if (url.Length > 2 && url[1] == ':') {
+ switch (url[0]) {
+ case 'T':
+ case 'M':
+ case 'C':
+ case 'P':
+ case 'E':
+ case 'F':
+ case 'N':
+ case 'O':
+ return true;
+ }
+ }
+ return base.CanHandleUrl (url);
+ }
+
+ // Clean the extra paramers in the id
+ public override Stream GetHelpStream (string id)
+ {
+ var idParts = id.Split ('?');
+ return base.GetHelpStream (idParts[0]);
+ }
+
+ public override Stream GetCachedHelpStream (string id)
+ {
+ var idParts = id.Split ('?');
+ return base.GetCachedHelpStream (idParts[0]);
+ }
+
+ public override DocumentType GetDocumentTypeForId (string id, out Dictionary<string, string> extraParams)
+ {
+ extraParams = null;
+ int interMark = id.LastIndexOf ('?');
+ if (interMark != -1)
+ extraParams = id.Substring (interMark)
+ .Split ('&')
+ .Select (nvp => {
+ var eqIdx = nvp.IndexOf ('=');
+ return new { Key = nvp.Substring (0, eqIdx < 0 ? nvp.Length : eqIdx), Value = nvp.Substring (eqIdx + 1) };
+ })
+ .ToDictionary (kvp => kvp.Key, kvp => kvp.Value );
+ return DocumentType.EcmaXml;
+ }
+
+ public override string GetPublicUrl (Node node)
+ {
+ string url = string.Empty;
+ var type = GetNodeType (node);
+ //Console.WriteLine ("GetPublicUrl {0} : {1} [{2}]", node.Element, node.Caption, type.ToString ());
+ switch (type) {
+ case EcmaNodeType.Namespace:
+ return node.Element; // A namespace node has already a well formated internal url
+ case EcmaNodeType.Type:
+ return MakeTypeNodeUrl (node);
+ case EcmaNodeType.Meta:
+ return MakeTypeNodeUrl (GetNodeTypeParent (node)) + GenerateMetaSuffix (node);
+ case EcmaNodeType.Member:
+ var typeChar = GetNodeMemberTypeChar (node);
+ var parentNode = GetNodeTypeParent (node);
+ var typeNode = MakeTypeNodeUrl (parentNode).Substring (2);
+ return typeChar + ":" + typeNode + MakeMemberNodeUrl (typeChar, node);
+ default:
+ return null;
+ }
+ }
+
+ string MakeTypeNodeUrl (Node node)
+ {
+ // A Type node has a Element property of the form: 'ecma:{number}#{typename}/'
+ var hashIndex = node.Element.IndexOf ('#');
+ var typeName = node.Element.Substring (hashIndex + 1, node.Element.Length - hashIndex - 2);
+ return "T:" + node.Parent.Caption + '.' + typeName.Replace ('.', '+');
+ }
+
+ string MakeMemberNodeUrl (char typeChar, Node node)
+ {
+ // We clean inner type ctor name which may contain the outer type name
+ var caption = node.Caption;
+
+ // Sanitize constructor caption of inner types
+ if (typeChar == 'C') {
+ int lastDot = -1;
+ for (int i = 0; i < caption.Length && caption[i] != '('; i++)
+ lastDot = caption[i] == '.' ? i : lastDot;
+ return lastDot == -1 ? '.' + caption : caption.Substring (lastDot);
+ }
+
+ /* We handle type conversion operator by checking if the name contains " to "
+ * (as in 'foo to bar') and we generate a corresponding conversion signature
+ */
+ if (typeChar == 'O' && caption.IndexOf (" to ") != -1) {
+ var parts = caption.Split (' ');
+ return "." + node.Parent.Caption + "(" + parts[0] + ", " + parts[2] + ")";
+ }
+
+ /* The goal here is to treat method which are explicit interface definition
+ * such as 'void IDisposable.Dispose ()' for which the caption is a dot
+ * expression thus colliding with the ecma parser.
+ * If the first non-alpha character in the caption is a dot then we have an
+ * explicit member implementation (we assume the interface has namespace)
+ */
+ var firstNonAlpha = caption.FirstOrDefault (c => !char.IsLetterOrDigit (c));
+ if (firstNonAlpha == '.')
+ return "$" + caption;
+
+ 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
+ while (node != null && node.Parent != null && !node.Parent.Parent.Element.StartsWith ("root:/", StringComparison.OrdinalIgnoreCase))
+ node = node.Parent;
+ return node;
+ }
+
+ string GenerateMetaSuffix (Node node)
+ {
+ string suffix = string.Empty;
+ // A meta node has always a type element to begin with
+ while (GetNodeType (node) != EcmaNodeType.Type) {
+ suffix = '/' + node.Element + suffix;
+ node = node.Parent;
+ }
+ return suffix;
+ }
+
+ public override string GetInternalIdForUrl (string url, out Node node)
+ {
+ var id = string.Empty;
+ node = null;
+
+ if (!url.StartsWith (UriPrefix, StringComparison.OrdinalIgnoreCase)) {
+ node = MatchNode (url);
+ if (node == null)
+ return null;
+ id = node.GetInternalUrl ();
+ }
+
+ string hash;
+ id = GetInternalIdForInternalUrl (id, out hash);
+
+ return id + GetArgs (hash, node);
+ }
+
+ public string GetInternalIdForInternalUrl (string internalUrl, out string hash)
+ {
+ var id = internalUrl;
+ if (id.StartsWith (UriPrefix, StringComparison.OrdinalIgnoreCase))
+ id = id.Substring (UriPrefix.Length);
+ else if (id.StartsWith ("N:", StringComparison.OrdinalIgnoreCase))
+ id = "xml.summary." + id.Substring ("N:".Length);
+
+ var hashIndex = id.IndexOf ('#');
+ hash = string.Empty;
+ if (hashIndex != -1) {
+ hash = id.Substring (hashIndex + 1);
+ id = id.Substring (0, hashIndex);
+ }
+
+ return id;
+ }
+
+ public override Node MatchNode (string url)
+ {
+ Node node = null;
+ if ((node = cache.Get (url)) == null) {
+ node = InternalMatchNode (url);
+ 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){
+ foreach (Node type_node in ns_node.Nodes){
+ string typename = type_node.Caption.Substring (0, type_node.Caption.IndexOf (' '));
+ string full = ns_node.Caption + "." + typename;
+
+ string doc_tag = GetKindFromCaption (type_node.Caption);
+ string url = type_node.PublicUrl;
+
+ //
+ // Add MonoMac/MonoTouch [Export] attributes, those live only in classes
+ //
+ XDocument type_doc = null;
+ ILookup<string, XElement> prematchedMembers = null;
+ bool hasExports = doc_tag == "Class" && (ns_node.Caption.StartsWith ("MonoTouch") || ns_node.Caption.StartsWith ("MonoMac"));
+ if (hasExports) {
+ try {
+ string rest, hash;
+ var id = GetInternalIdForInternalUrl (type_node.GetInternalUrl (), out hash);
+ type_doc = XDocument.Load (GetHelpStream (id));
+ prematchedMembers = type_doc.Root.Element ("Members").Elements ("Member").ToLookup (n => (string)n.Attribute ("MemberName"), n => n);
+ } catch (Exception e) {
+ Console.WriteLine ("Problem processing {0} for MonoTouch/MonoMac exports\n\n{0}", e);
+ hasExports = false;
+ }
+ }
+
+ if (doc_tag == "Class" || doc_tag == "Structure" || doc_tag == "Interface"){
+ index_maker.Add (type_node.Caption, typename, url);
+ index_maker.Add (full + " " + doc_tag, full, url);
+
+ foreach (Node c in type_node.Nodes){
+ switch (c.Caption){
+ case "Constructors":
+ index_maker.Add (" constructors", typename+"0", url + "/C");
+ break;
+ case "Fields":
+ index_maker.Add (" fields", typename+"1", url + "/F");
+ break;
+ case "Events":
+ index_maker.Add (" events", typename+"2", url + "/E");
+ break;
+ case "Properties":
+ index_maker.Add (" properties", typename+"3", url + "/P");
+ break;
+ case "Methods":
+ index_maker.Add (" methods", typename+"4", url + "/M");
+ break;
+ case "Operators":
+ index_maker.Add (" operators", typename+"5", url + "/O");
+ break;
+ }
+ }
+
+ //
+ // Now repeat, but use a different sort key, to make sure we come after
+ // the summary data above, start the counter at 6
+ //
+ string keybase = typename + "6.";
+
+ foreach (Node c in type_node.Nodes){
+ var type = c.Caption[0];
+
+ foreach (Node nc in c.Nodes) {
+ string res = nc.Caption;
+ string nurl = nc.PublicUrl;
+
+ // Process exports
+ if (hasExports && (type == 'C' || type == 'M' || type == 'P')) {
+ try {
+ var member = GetMemberFromCaption (type_doc, type == 'C' ? ".ctor" : res, false, prematchedMembers);
+ var exports = member.Descendants ("AttributeName").Where (a => a.Value.Contains ("Foundation.Export"));
+ foreach (var exportNode in exports) {
+ var parts = exportNode.Value.Split ('"');
+ if (parts.Length != 3) {
+ Console.WriteLine ("Export attribute not found or not usable in {0}", exportNode);
+ } else {
+ var export = parts[1];
+ index_maker.Add (export + " selector", export, nurl);
+ }
+ }
+ } catch (Exception e) {
+ Console.WriteLine ("Problem processing {0}/{1} for MonoTouch/MonoMac exports\n\n{2}", nurl, res, e);
+ }
+ }
+
+ switch (type){
+ case 'C':
+ break;
+ case 'F':
+ index_maker.Add (String.Format ("{0}.{1} field", typename, res),
+ keybase + res, nurl);
+ index_maker.Add (String.Format ("{0} field", res), res, nurl);
+ break;
+ case 'E':
+ index_maker.Add (String.Format ("{0}.{1} event", typename, res),
+ keybase + res, nurl);
+ index_maker.Add (String.Format ("{0} event", res), res, nurl);
+ break;
+ case 'P':
+ index_maker.Add (String.Format ("{0}.{1} property", typename, res),
+ keybase + res, nurl);
+ index_maker.Add (String.Format ("{0} property", res), res, nurl);
+ break;
+ case 'M':
+ index_maker.Add (String.Format ("{0}.{1} method", typename, res),
+ keybase + res, nurl);
+ index_maker.Add (String.Format ("{0} method", res), res, nurl);
+ break;
+ case 'O':
+ index_maker.Add (String.Format ("{0}.{1} operator", typename, res),
+ keybase + res, nurl);
+ break;
+ }
+ }
+ }
+ } else if (doc_tag == "Enumeration"){
+ //
+ // Enumerations: add the enumeration values
+ //
+ index_maker.Add (type_node.Caption, typename, url);
+ index_maker.Add (full + " " + doc_tag, full, url);
+
+ // Now, pull the values.
+ string rest, hash;
+ var id = GetInternalIdForInternalUrl (type_node.GetInternalUrl (), out hash);
+ var xdoc = XDocument.Load (GetHelpStream (id));
+ if (xdoc == null)
+ continue;
+
+ var members = xdoc.Root.Element ("Members").Elements ("Members");
+ if (members == null)
+ continue;
+
+ foreach (var member_node in members){
+ string enum_value = member_node.Attribute ("MemberName").Value;
+ string caption = enum_value + " value";
+ index_maker.Add (caption, caption, url);
+ }
+ } else if (doc_tag == "Delegate"){
+ index_maker.Add (type_node.Caption, typename, url);
+ index_maker.Add (full + " " + doc_tag, full, url);
+ }
+ }
+ }
+ }
+
+
+ public override void PopulateSearchableIndex (IndexWriter writer)
+ {
+ StringBuilder text = new StringBuilder ();
+ SearchableDocument searchDoc = new SearchableDocument ();
+
+ foreach (Node ns_node in Tree.RootNode.Nodes) {
+ foreach (Node type_node in ns_node.Nodes) {
+ string typename = type_node.Caption.Substring (0, type_node.Caption.IndexOf (' '));
+ string full = ns_node.Caption + "." + typename;
+ string url = type_node.PublicUrl;
+ string doc_tag = GetKindFromCaption (type_node.Caption);
+ string rest, hash;
+ var id = GetInternalIdForInternalUrl (type_node.GetInternalUrl (), out hash);
+ var xdoc = XDocument.Load (GetHelpStream (id));
+ if (xdoc == null)
+ continue;
+ if (string.IsNullOrEmpty (doc_tag)) {
+ Console.WriteLine (type_node.Caption);
+ continue;
+ }
+
+ // For classes, structures or interfaces add a doc for the overview and
+ // add a doc for every constructor, method, event, ...
+ // doc_tag == "Class" || doc_tag == "Structure" || doc_tag == "Interface"
+ if (doc_tag[0] == 'C' || doc_tag[0] == 'S' || doc_tag[0] == 'I') {
+ // Adds a doc for every overview of every type
+ SearchableDocument doc = searchDoc.Reset ();
+ doc.Title = type_node.Caption;
+ doc.HotText = typename;
+ doc.Url = url;
+ doc.FullTitle = full;
+
+ var node_sel = xdoc.Root.Element ("Docs");
+ text.Clear ();
+ GetTextFromNode (node_sel, text);
+ doc.Text = text.ToString ();
+
+ text.Clear ();
+ GetExamples (node_sel, text);
+ doc.Examples = text.ToString ();
+
+ writer.AddDocument (doc.LuceneDoc);
+ var exportParsable = doc_tag[0] == 'C' && (ns_node.Caption.StartsWith ("MonoTouch") || ns_node.Caption.StartsWith ("MonoMac"));
+
+ //Add docs for contructors, methods, etc.
+ foreach (Node c in type_node.Nodes) { // c = Constructors || Fields || Events || Properties || Methods || Operators
+ if (c.Element == "*")
+ continue;
+ const float innerTypeBoost = 0.2f;
+
+ IEnumerable<Node> ncnodes = c.Nodes;
+ // The rationale is that we need to properly handle method overloads
+ // so for those method node which have children, flatten them
+ if (c.Caption == "Methods") {
+ ncnodes = ncnodes
+ .Where (n => n.Nodes == null || n.Nodes.Count == 0)
+ .Concat (ncnodes.Where (n => n.Nodes.Count > 0).SelectMany (n => n.Nodes));
+ } else if (c.Caption == "Operators") {
+ ncnodes = ncnodes
+ .Where (n => !n.Caption.EndsWith ("Conversion"))
+ .Concat (ncnodes.Where (n => n.Caption.EndsWith ("Conversion")).SelectMany (n => n.Nodes));
+ }
+
+ var prematchedMembers = xdoc.Root.Element ("Members").Elements ("Member").ToLookup (n => (string)n.Attribute ("MemberName"), n => n);
+
+ foreach (Node nc in ncnodes) {
+ var docsNode = GetDocsFromCaption (xdoc, c.Caption[0] == 'C' ? ".ctor" : nc.Caption, c.Caption[0] == 'O', prematchedMembers);
+ if (docsNode == null) {
+ Console.Error.WriteLine ("Problem: {0}", nc.PublicUrl);
+ continue;
+ }
+
+ SearchableDocument doc_nod = searchDoc.Reset ();
+ doc_nod.Title = LargeName (nc) + " " + EtcKindToCaption (c.Caption[0]);
+ doc_nod.FullTitle = ns_node.Caption + '.' + typename + "::" + nc.Caption;
+ doc_nod.HotText = string.Empty;
+
+ /* Disable constructors hottext indexing as it's often "polluting" search queries
+ because it has the same hottext than standard types */
+ if (c.Caption != "Constructors") {
+ //dont add the parameters to the hottext
+ int ppos = nc.Caption.IndexOf ('(');
+ doc_nod.HotText = ppos != -1 ? nc.Caption.Substring (0, ppos) : nc.Caption;
+ }
+
+ var urlnc = nc.PublicUrl;
+ doc_nod.Url = urlnc;
+
+ text.Clear ();
+ GetTextFromNode (docsNode, text);
+ doc_nod.Text = text.ToString ();
+
+ text.Clear ();
+ GetExamples (docsNode, text);
+ doc_nod.Examples = text.ToString ();
+
+ Document lucene_doc = doc_nod.LuceneDoc;
+ lucene_doc.Boost = innerTypeBoost;
+ writer.AddDocument (lucene_doc);
+
+ // Objective-C binding specific parsing of [Export] attributes
+ if (exportParsable) {
+ try {
+ var exports = docsNode.Parent.Elements ("Attributes").Elements ("Attribute").Elements ("AttributeName")
+ .Select (a => (string)a).Where (txt => txt.Contains ("Foundation.Export"));
+
+ foreach (var exportNode in exports) {
+ var parts = exportNode.Split ('"');
+ if (parts.Length != 3) {
+ Console.WriteLine ("Export attribute not found or not usable in {0}", exportNode);
+ continue;
+ }
+
+ var export = parts[1];
+ var export_node = searchDoc.Reset ();
+ export_node.Title = export + " Export";
+ export_node.FullTitle = ns_node.Caption + '.' + typename + "::" + export;
+ export_node.Url = urlnc;
+ export_node.HotText = export;
+ export_node.Text = string.Empty;
+ export_node.Examples = string.Empty;
+ lucene_doc = export_node.LuceneDoc;
+ lucene_doc.Boost = innerTypeBoost;
+ writer.AddDocument (lucene_doc);
+ }
+ } catch (Exception e){
+ Console.WriteLine ("Problem processing {0} for MonoTouch/MonoMac exports\n\n{0}", e);
+ }
+ }
+ }
+ }
+ // doc_tag == "Enumeration"
+ } else if (doc_tag[0] == 'E'){
+ var members = xdoc.Root.Element ("Members").Elements ("Member");
+ if (members == null)
+ continue;
+
+ text.Clear ();
+ foreach (var member_node in members) {
+ string enum_value = (string)member_node.Attribute ("MemberName");
+ text.Append (enum_value);
+ text.Append (" ");
+ GetTextFromNode (member_node.Element ("Docs"), text);
+ text.AppendLine ();
+ }
+
+ SearchableDocument doc = searchDoc.Reset ();
+
+ text.Clear ();
+ GetExamples (xdoc.Root.Element ("Docs"), text);
+ doc.Examples = text.ToString ();
+
+ doc.Title = type_node.Caption;
+ doc.HotText = (string)xdoc.Root.Attribute ("Name");
+ doc.FullTitle = full;
+ doc.Url = url;
+ doc.Text = text.ToString();
+ writer.AddDocument (doc.LuceneDoc);
+ // doc_tag == "Delegate"
+ } else if (doc_tag[0] == 'D'){
+ SearchableDocument doc = searchDoc.Reset ();
+ doc.Title = type_node.Caption;
+ doc.HotText = (string)xdoc.Root.Attribute ("Name");
+ doc.FullTitle = full;
+ doc.Url = url;
+
+ var node_sel = xdoc.Root.Element ("Docs");
+
+ text.Clear ();
+ GetTextFromNode (node_sel, text);
+ doc.Text = text.ToString();
+
+ text.Clear ();
+ GetExamples (node_sel, text);
+ doc.Examples = text.ToString();
+
+ writer.AddDocument (doc.LuceneDoc);
+ }
+ }
+ }
+ }
+
+ string GetKindFromCaption (string s)
+ {
+ int p = s.LastIndexOf (' ');
+ if (p > 0)
+ return s.Substring (p + 1);
+ return null;
+ }
+
+ // Extract the interesting text from the docs node
+ void GetTextFromNode (XElement n, StringBuilder sb)
+ {
+ // Include the text content of the docs
+ sb.AppendLine (n.Value);
+ foreach (var tag in n.Descendants ())
+ //include the url to which points the see tag and the name of the parameter
+ if ((tag.Name.LocalName.Equals ("see", StringComparison.Ordinal) || tag.Name.LocalName.Equals ("paramref", StringComparison.Ordinal))
+ && tag.HasAttributes)
+ sb.AppendLine ((string)tag.Attributes ().First ());
+ }
+
+ // Extract the code nodes from the docs
+ void GetExamples (XElement n, StringBuilder sb)
+ {
+ foreach (var code in n.Descendants ("code"))
+ sb.Append ((string)code);
+ }
+
+ // Extract a large name for the Node
+ static string LargeName (Node matched_node)
+ {
+ string[] parts = matched_node.GetInternalUrl ().Split('/', '#');
+ if (parts.Length == 3 && parts[2] != String.Empty) //List of Members, properties, events, ...
+ return parts[1] + ": " + matched_node.Caption;
+ else if(parts.Length >= 4) //Showing a concrete Member, property, ...
+ return parts[1] + "." + matched_node.Caption;
+ else
+ return matched_node.Caption;
+ }
+
+ XElement GetMemberFromCaption (XDocument xdoc, string caption, bool isOperator, ILookup<string, XElement> prematchedMembers)
+ {
+ string name;
+ IList<string> args;
+ var doc = xdoc.Root.Element ("Members").Elements ("Member");
+
+ if (isOperator) {
+ // The first case are explicit and implicit conversion operators which are grouped specifically
+ if (caption.IndexOf (" to ") != -1) {
+ var convArgs = caption.Split (new[] { " to " }, StringSplitOptions.None);
+ return doc
+ .First (n => (AttrEq (n, "MemberName", "op_Explicit") || AttrEq (n, "MemberName", "op_Implicit"))
+ && ((string)n.Element ("ReturnValue").Element ("ReturnType")).Equals (convArgs[1], StringComparison.Ordinal)
+ && AttrEq (n.Element ("Parameters").Element ("Parameter"), "Type", convArgs[0]));
+ } else {
+ return doc.First (m => AttrEq (m, "MemberName", "op_" + caption));
+ }
+ }
+
+ TryParseCaption (caption, out name, out args);
+
+ if (!string.IsNullOrEmpty (name)) { // Filter member by name
+ var prematched = prematchedMembers[name];
+ doc = prematched.Any () ? prematched : doc.Where (m => AttrEq (m, "MemberName", name));
+ }
+ if (args != null && args.Count > 0) // Filter member by its argument list
+ doc = doc.Where (m => m.Element ("Parameters").Elements ("Parameter").Attributes ("Type").Select (a => (string)a).SequenceEqual (args));
+
+ return doc.First ();
+ }
+
+ XElement GetDocsFromCaption (XDocument xdoc, string caption, bool isOperator, ILookup<string, XElement> prematchedMembers)
+ {
+ return GetMemberFromCaption (xdoc, caption, isOperator, prematchedMembers).Element ("Docs");
+ }
+
+ // A simple stack-based parser to detect single type definition separated by commas
+ IEnumerable<string> ExtractArguments (string rawArgList)
+ {
+ var sb = new System.Text.StringBuilder ();
+ int genericDepth = 0;
+ int arrayDepth = 0;
+
+ for (int i = 0; i < rawArgList.Length; i++) {
+ char c = rawArgList[i];
+ switch (c) {
+ case ',':
+ if (genericDepth == 0 && arrayDepth == 0) {
+ yield return sb.ToString ();
+ sb.Clear ();
+ continue;
+ }
+ break;
+ case '<':
+ genericDepth++;
+ break;
+ case '>':
+ genericDepth--;
+ break;
+ case '[':
+ arrayDepth++;
+ break;
+ case ']':
+ arrayDepth--;
+ break;
+ }
+ sb.Append (c);
+ }
+ if (sb.Length > 0)
+ yield return sb.ToString ();
+ }
+
+ void TryParseCaption (string caption, out string name, out IList<string> argList)
+ {
+ name = null;
+ argList = null;
+ int parenIdx = caption.IndexOf ('(');
+ // In case of simple name, there is no need for processing
+ if (parenIdx == -1) {
+ name = caption;
+ return;
+ }
+ name = caption.Substring (0, parenIdx);
+ // Now we gather the argument list if there is any
+ var rawArgList = caption.Substring (parenIdx + 1, caption.Length - parenIdx - 2); // Only take what's inside the parens
+ if (string.IsNullOrEmpty (rawArgList))
+ return;
+
+ argList = ExtractArguments (rawArgList).Select (arg => arg.Trim ()).ToList ();
+ }
+
+ bool AttrEq (XElement element, string attributeName, string expectedValue)
+ {
+ return ((string)element.Attribute (attributeName)).Equals (expectedValue, StringComparison.Ordinal);
+ }
+ }
+}
--- /dev/null
+//
+// The ecmaspec provider is for ECMA specifications
+//
+// Authors:
+// John Luke (jluke@cfl.rr.com)
+// Ben Maurer (bmaurer@users.sourceforge.net)
+//
+// Use like this:
+// mono assembler.exe --ecmaspec DIRECTORY --out name
+//
+
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Text;
+using System.Xml.XPath;
+using System.Xml.Xsl;
+using System.Xml;
+using System.Collections.Generic;
+using Lucene.Net.Index;
+using Lucene.Net.Documents;
+
+namespace MonkeyDoc.Providers
+{
+ public class EcmaSpecProvider : Provider
+ {
+ string basedir;
+
+ public EcmaSpecProvider (string base_directory)
+ {
+ basedir = base_directory;
+ if (!Directory.Exists (basedir))
+ throw new DirectoryNotFoundException (String.Format ("The directory `{0}' does not exist", basedir));
+ }
+
+ public override void PopulateTree (Tree tree)
+ {
+ XPathNavigator n = new XPathDocument (Path.Combine (basedir, "toc.xml")).CreateNavigator ();
+ n.MoveToRoot ();
+ n.MoveToFirstChild ();
+ PopulateNode (n.SelectChildren ("node", ""), tree.RootNode);
+ }
+
+ void PopulateNode (XPathNodeIterator nodes, Node treeNode)
+ {
+ foreach (XPathNavigator n in nodes) {
+ string secNumber = n.GetAttribute ("number", "");
+ string secName = n.GetAttribute ("name", "");
+
+ var storage = treeNode.Tree.HelpSource.Storage;
+ using (var file = File.OpenRead (Path.Combine (basedir, secNumber + ".xml")))
+ storage.Store (secNumber, file);
+
+ Node thisNode = treeNode.GetOrCreateNode (secNumber + ": " + secName, "ecmaspec:" + secNumber);
+
+ if (n.HasChildren)
+ PopulateNode (n.SelectChildren ("node", ""), thisNode);
+ }
+ }
+
+ public override void CloseTree (HelpSource hs, Tree tree)
+ {
+ }
+ }
+
+ public class EcmaSpecHelpSource : HelpSource
+ {
+ const string EcmaspecPrefix = "ecmaspec:";
+ const string TocPart = "%toc"; // What is returned as TocXml
+ const string SpecPart = "%spec"; // What is returned as Ecmaspec
+
+ public EcmaSpecHelpSource (string base_file, bool create) : base (base_file, create)
+ {
+ }
+
+ public override DocumentType GetDocumentTypeForId (string id, out Dictionary<string, string> extraParams)
+ {
+ extraParams = null;
+ return id.EndsWith (TocPart) ? DocumentType.TocXml : DocumentType.EcmaSpecXml;
+ }
+
+ public override bool IsGeneratedContent (string id)
+ {
+ return id == "root:" || id.EndsWith (TocPart);
+ }
+
+ public override bool IsMultiPart (string id, out IEnumerable<string> parts)
+ {
+ if (id == "root:" || id.EndsWith (TocPart) || id.EndsWith (SpecPart)) {
+ parts = null;
+ return false;
+ }
+ parts = MakeMultiPart (id);
+ return true;
+ }
+
+ IEnumerable<string> MakeMultiPart (string baseId)
+ {
+ yield return baseId + SpecPart;
+ yield return baseId + TocPart;
+ }
+
+ public override string GetText (string id)
+ {
+ Node n = id == "root:" ? Tree.RootNode : MatchNode (EcmaspecPrefix + id.Substring (0, id.Length - TocPart.Length));
+ if (n == null)
+ throw new ArgumentException ("id", string.Format ("{0} -> {1}", id, EcmaspecPrefix + id.Substring (0, id.Length - TocPart.Length)));
+ return TreeDumper.ExportToTocXml (n, "C# Language Specification", "In this section:");
+ }
+
+ public override Stream GetHelpStream (string id)
+ {
+ return id.EndsWith (SpecPart) ? base.GetHelpStream (id.Substring (0, id.IndexOf (SpecPart))) : base.GetHelpStream (id);
+ }
+
+ public override void PopulateSearchableIndex (IndexWriter writer)
+ {
+ foreach (Node n in Tree.RootNode.Nodes)
+ AddDocuments (writer, n);
+ }
+
+ protected override string UriPrefix {
+ get {
+ return EcmaspecPrefix;
+ }
+ }
+
+ void AddDocuments (IndexWriter writer, Node node)
+ {
+ string url = node.PublicUrl;
+ Stream file_stream = GetHelpStream (url.Substring (9));
+ if (file_stream == null) //Error
+ return;
+ XmlDocument xdoc = new XmlDocument ();
+ xdoc.Load (new XmlTextReader (file_stream));
+
+ //Obtain the title
+ XmlNode nelem = xdoc.DocumentElement;
+ string title = nelem.Attributes["number"].Value + ": " + nelem.Attributes["title"].Value;
+
+ //Obtain the text
+ StringBuilder s = new StringBuilder ();
+ GetTextNode (nelem, s);
+ string text = s.ToString ();
+
+ //Obtain the examples
+ StringBuilder s2 = new StringBuilder ();
+ GetExamples (nelem, s2);
+ string examples = s2.ToString ();
+
+ //Write to the Lucene Index all the parts
+ SearchableDocument doc = new SearchableDocument ();
+ doc.Title = title;
+ doc.HotText = title.Substring (title.IndexOf (':'));
+ doc.Url = url;
+ doc.Text = text;
+ doc.Examples = examples;
+ writer.AddDocument (doc.LuceneDoc);
+
+ if (node.IsLeaf)
+ return;
+
+ foreach (Node n in node.Nodes)
+ AddDocuments (writer, n);
+ }
+
+ void GetTextNode (XmlNode n, StringBuilder s)
+ {
+ //dont include c# code
+ if (n.Name == "code_example")
+ return;
+ //include all text from nodes
+ if (n.NodeType == XmlNodeType.Text)
+ s.Append (n.Value);
+
+ //recursively explore all nodes
+ if (n.HasChildNodes)
+ foreach (XmlNode n_child in n.ChildNodes)
+ GetTextNode (n_child, s);
+ }
+
+ void GetExamples (XmlNode n, StringBuilder s)
+ {
+ if (n.Name == "code_example") {
+ if (n.FirstChild.Name == "#cdata-section")
+ s.Append (n.FirstChild.Value);
+ } else {
+ if (n.HasChildNodes)
+ foreach (XmlNode n_child in n.ChildNodes)
+ GetExamples (n_child, s);
+ }
+ }
+ }
+}
--- /dev/null
+using System;
+using System.Linq;
+using System.IO;
+using System.Text;
+using System.Xml;
+using System.Xml.Linq;
+using System.Collections.Generic;
+
+using Lucene.Net.Index;
+using Lucene.Net.Documents;
+
+using MonkeyDoc.Ecma;
+using MonkeyDoc.Storage;
+using Mono.Utilities;
+
+namespace MonkeyDoc.Providers
+{
+ public class EcmaUncompiledHelpSource : EcmaHelpSource
+ {
+ readonly DirectoryInfo basedir;
+ readonly string basedoc;
+
+ public readonly string BasePath;
+
+ public new string Name {
+ get;
+ private set;
+ }
+
+ /* base_file: the directory containing the index.xml file, usually in Mono land .../Documentation/en
+ * markName: if true, we encase the node caption with [] to clearly mark it's from an uncompiled source
+ */
+ public EcmaUncompiledHelpSource (string base_file, bool markName = true) : base ()
+ {
+ basedir = new DirectoryInfo (base_file);
+ BasePath = basedir.FullName;
+
+ basedoc = Path.Combine (basedir.FullName, "index.xml");
+
+ Name = ((string)XDocument.Load (basedoc).Root.Element ("Title")) ?? "UnnamedUncompiledSource";
+ if (markName)
+ Name = '[' + Name + ']';
+ Tree.RootNode.Caption = Name;
+
+ Func<XElement, string> indexGenerator = type => {
+ var nsName = (string)type.Parent.Attribute ("Name");
+ var typeName = (string)type.Attribute ("Name");
+ return Path.ChangeExtension (nsName + '/' + typeName, ".xml");
+ };
+
+ this.Storage = new UncompiledDocStorage (BasePath);
+
+ EcmaDoc.PopulateTreeFromIndexFile (basedoc, Tree, null, null);
+ }
+
+ protected override string UriPrefix {
+ get {
+ return "uncompiled:";
+ }
+ }
+ }
+}
--- /dev/null
+//
+// error-provider.cs
+//
+// Author:
+// Ben Maurer (bmaurer@users.sourceforge.net)
+//
+// (C) 2003 Ben Maurer
+// Copyright 2003-2011 Novell
+// Copyright 2011 Xamarin Inc
+//
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Xml;
+using System.Xml.Serialization;
+using System.Linq;
+using Lucene.Net.Index;
+using Lucene.Net.Documents;
+
+namespace MonkeyDoc.Providers
+{
+ public class ErrorProviderConfig
+ {
+ public string FilesPath;
+ public string Match;
+ public int ErrorNumSubstringStart;
+ public int ErrorNumSubstringLength;
+ public string FriendlyFormatString;
+
+ public override string ToString ()
+ {
+ var sb = new StringBuilder ();
+ var w = new StringWriter (sb);
+
+ w.WriteLine ("FilesPath: {0}", FilesPath);
+ w.WriteLine ("Match: {0}", Match);
+ w.WriteLine ("Error Number Substring: {0} Length:{1}", ErrorNumSubstringStart, ErrorNumSubstringLength);
+ w.WriteLine ("FriendlyFormatString: {0}", FriendlyFormatString);
+
+ return w.ToString ();
+ }
+
+ public Dictionary<string, ErrorDocumentation> Compile (HelpSource hs)
+ {
+ string[] files = Directory.GetFiles (FilesPath, Match);
+ var ret = new Dictionary<string, ErrorDocumentation> ();
+
+ foreach (string s in files) {
+ ErrorDocumentation d;
+ int errorNum = 0;
+
+ try {
+ errorNum = int.Parse (Path.GetFileName (s).Substring (ErrorNumSubstringStart, ErrorNumSubstringLength));
+ } catch {
+ Console.WriteLine ("Ignoring file {0}", s);
+ }
+
+ string errorName = String.Format (FriendlyFormatString, errorNum);
+
+ if (!ret.TryGetValue (errorName, out d))
+ ret[errorName] = d = new ErrorDocumentation (errorName);
+
+ if (d.Details == null) {
+ string xmlFile = Path.ChangeExtension (s, "xml");
+ if (File.Exists (xmlFile)) {
+ XmlSerializer cfgRdr = new XmlSerializer (typeof (ErrorDetails));
+ d.Details = (ErrorDetails)cfgRdr.Deserialize (new XmlTextReader (xmlFile));
+ }
+ }
+ // Encoding is same as used in MCS, so we will be able to do all those files
+ using (StreamReader reader = new StreamReader (s, Encoding.GetEncoding (28591))) {
+ d.Examples.Add (reader.ReadToEnd ());
+ }
+ }
+
+ return ret;
+ }
+ }
+
+ public class ErrorDocumentation
+ {
+ public string ErrorName;
+ public ErrorDetails Details;
+ public List<string> Examples = new List<string> ();
+
+ public ErrorDocumentation () {}
+ public ErrorDocumentation (string ErrorName)
+ {
+ this.ErrorName = ErrorName;
+ }
+ }
+
+ public class ErrorDetails
+ {
+ public XmlNode Summary;
+ public XmlNode Details;
+ }
+
+ public class ErrorProvider : Provider
+ {
+ ErrorProviderConfig config;
+
+ public ErrorProvider (string configFile)
+ {
+ config = ReadConfig (configFile);
+ }
+
+ public static ErrorProviderConfig ReadConfig (string file)
+ {
+ XmlSerializer cfgRdr = new XmlSerializer (typeof (ErrorProviderConfig));
+ ErrorProviderConfig ret = (ErrorProviderConfig)cfgRdr.Deserialize (new XmlTextReader (file));
+ // handle path rel to the config file
+ ret.FilesPath = Path.Combine (Path.GetDirectoryName (file), ret.FilesPath);
+ return ret;
+ }
+
+ public override void PopulateTree (Tree tree)
+ {
+ // everything is done in CloseTree so we can pack
+ }
+
+ public override void CloseTree (HelpSource hs, Tree tree)
+ {
+ var entries = config.Compile (hs);
+ MemoryStream ms = new MemoryStream ();
+ XmlSerializer writer = new XmlSerializer (typeof (ErrorDocumentation));
+
+ foreach (var de in entries) {
+ ErrorDocumentation d = de.Value;
+ string s = de.Key;
+
+ tree.RootNode.GetOrCreateNode (s, "error:" + s);
+
+ writer.Serialize (ms, d);
+ ms.Position = 0;
+ hs.Storage.Store (s, ms);
+ ms.SetLength (0);
+ }
+
+ tree.RootNode.Sort ();
+ }
+ }
+
+ public class ErrorHelpSource : HelpSource
+ {
+ public ErrorHelpSource (string base_file, bool create) : base (base_file, create)
+ {
+ }
+
+ public override string GetText (string id)
+ {
+ return TreeDumper.ExportToTocXml (Tree.RootNode, "Compiler Error Reference", "In this section:");
+ }
+
+ protected override string UriPrefix {
+ get {
+ return "error:";
+ }
+ }
+
+ public override bool IsGeneratedContent (string id)
+ {
+ return id == "root:";
+ }
+
+ public override DocumentType GetDocumentTypeForId (string id, out Dictionary<string, string> extraParams)
+ {
+ extraParams = null;
+ return id == "root:" ? DocumentType.TocXml : DocumentType.ErrorXml;
+ }
+
+ public override string GetInternalIdForUrl (string url, out Node node)
+ {
+ var result = base.GetInternalIdForUrl (url, out node);
+ return result.ToLower ();
+ }
+
+ public override void PopulateIndex (IndexMaker index_maker)
+ {
+ foreach (Node n in Tree.RootNode.Nodes)
+ index_maker.Add (n.Caption, n.Caption, n.Element);
+ }
+
+ public override void PopulateSearchableIndex (IndexWriter writer)
+ {
+ foreach (Node n in Tree.RootNode.Nodes) {
+ XmlSerializer reader = new XmlSerializer (typeof (ErrorDocumentation));
+ ErrorDocumentation d = (ErrorDocumentation)reader.Deserialize (GetHelpStream (n.Element.Substring (6)));
+ SearchableDocument doc = new SearchableDocument ();
+ doc.Title = d.ErrorName;
+ doc.Url = n.Element;
+ doc.Text = d.Details != null ? d.Details.ToString () : string.Empty;
+ doc.Examples = d.Examples.Cast<string> ().Aggregate ((e1, e2) => e1 + Environment.NewLine + e2);
+ doc.HotText = d.ErrorName;
+ writer.AddDocument (doc.LuceneDoc);
+ }
+ }
+ }
+}
--- /dev/null
+//
+// A provider to display man pages
+//
+// Authors:
+// Johannes Roith <johannes@roith.de>
+// Jonathan Pryor <jpryor@novell.com>
+//
+// (C) 2008 Novell, Inc.
+
+using System;
+using System.IO;
+using System.Text;
+using System.Xml;
+using System.Linq;
+using System.Collections.Generic;
+
+namespace MonkeyDoc.Providers
+{
+ public class ManProvider : Provider
+ {
+ string[] tocFiles;
+
+ public ManProvider (string[] handbookTocFiles)
+ {
+ tocFiles = handbookTocFiles;
+
+ // huh...
+ if (!File.Exists (tocFiles[0]))
+ throw new FileNotFoundException (String.Format ("The table of contents, `{0}' does not exist", tocFiles[0]));
+ }
+
+ public override void PopulateTree (Tree tree)
+ {
+ foreach(string TocFile in tocFiles) {
+ XmlDocument doc = new XmlDocument();
+ doc.Load (TocFile);
+
+ XmlNodeList nodeList = doc.GetElementsByTagName("manpage");
+ Node nodeToAddChildrenTo = tree.RootNode;
+ var storage = nodeToAddChildrenTo.Tree.HelpSource.Storage;
+
+ foreach (XmlNode node in nodeList) {
+
+ XmlAttribute name = node.Attributes["name"];
+ XmlAttribute page = node.Attributes["page"];
+
+ if (name == null || page == null) continue;
+
+ if (!File.Exists (page.Value))
+ continue;
+
+ string target = "man:" + name.Value;
+ nodeToAddChildrenTo.CreateNode (name.Value, target);
+
+ if (File.Exists (page.Value))
+ storage.Store (name.Value, File.OpenRead (page.Value));
+ }
+ }
+ }
+
+ public override void CloseTree (HelpSource hs, Tree tree)
+ {
+ }
+ }
+
+ public class ManHelpSource : HelpSource
+ {
+ const string ManPrefix = "man:";
+ Dictionary<string, Node> nodesMap;
+
+ public ManHelpSource (string base_file, bool create) : base (base_file, create)
+ {
+ nodesMap = Tree.RootNode.Nodes.ToDictionary (n => n.Element);
+ }
+
+ // Since man always has a flat tree and rather small amount of item
+ // we store them in a dictionary
+ public override Node MatchNode (string url)
+ {
+ Node result;
+ return nodesMap.TryGetValue (url, out result) ? result : null;
+ }
+
+ public override DocumentType GetDocumentTypeForId (string id, out Dictionary<string, string> extraParams)
+ {
+ extraParams = null;
+ return id == "root:" ? DocumentType.TocXml : DocumentType.Man;
+ }
+
+ public override bool IsGeneratedContent (string id)
+ {
+ return id == "root:";
+ }
+
+ public override string GetText (string url)
+ {
+ return TreeDumper.ExportToTocXml (Tree.RootNode, "Mono Documentation Library", "Available man pages:");
+ }
+
+ protected override string UriPrefix {
+ get {
+ return ManPrefix;
+ }
+ }
+ }
+}
--- /dev/null
+//
+// The simple provider is an example provider
+//
+// Author:
+// Miguel de Icaza (miguel@ximian.com)
+//
+// Use like this:
+// mono assembler.exe --simple DIRECTORY --out name
+//
+// Then create a .source file in your sources directory, and copy
+// name.tree and name.zip to the sources directory.
+//
+// To view the tree generated, use:
+// mono dump.exe name.tree
+//
+namespace Monodoc {
+using System;
+using System.IO;
+using System.Text;
+
+//
+// The simple provider generates the information source
+//
+public class SimpleProvider : Provider {
+ string basedir;
+
+ public SimpleProvider (string base_directory)
+ {
+ basedir = base_directory;
+ if (!Directory.Exists (basedir))
+ throw new FileNotFoundException (String.Format ("The directory `{0}' does not exist", basedir));
+ }
+
+ public override void PopulateTree (Tree tree)
+ {
+ Node top = tree.LookupNode ("Directory at: " + basedir, "simple:");
+
+ foreach (string dir in Directory.GetDirectories (basedir)){
+ string url = Path.GetFileName (dir);
+ Node n = top.LookupNode ("Dir: " + url, url);
+ PopulateDir (n, dir);
+ }
+ }
+
+#pragma warning disable 219
+ void PopulateDir (Node me, string dir)
+ {
+ Console.WriteLine ("Adding: " + dir);
+ foreach (string child_dir in Directory.GetDirectories (dir)){
+ string url = Path.GetFileName (child_dir);
+ Node n = me.LookupNode ("Dir: " + url, "simple-directory:" + url);
+ PopulateDir (me, child_dir);
+ }
+
+ foreach (string file in Directory.GetFiles (dir)){
+ Console.WriteLine (" File: " + file);
+ string file_code = me.tree.HelpSource.PackFile (file);
+
+ //
+ // The url element encoded for the file is:
+ // originalfilename#CODE
+ //
+ // The code is assigned to us after the file has been packaged
+ // We use the original-filename later to render html or text files
+ //
+ Node n = me.LookupNode (Path.GetFileName (file), file + "#" + file_code);
+
+ }
+ }
+
+ public override void CloseTree (HelpSource hs, Tree tree)
+ {
+ }
+}
+
+//
+// The HelpSource is used during the rendering phase.
+//
+
+public class SimpleHelpSource : HelpSource {
+ Encoding enc;
+
+ public SimpleHelpSource (string base_file, bool create) : base (base_file, create)
+ {
+ enc = new UTF8Encoding (false, false);
+ }
+
+ public override string GetText (string url, out Node match_node)
+ {
+ match_node = null;
+
+ string c = GetCachedText (url);
+ if (c != null)
+ return c;
+
+ if (url.StartsWith ("simple:") || url.StartsWith ("simple-directory:"))
+ return GetTextFromUrl (url);
+
+ return null;
+ }
+
+ string GetTextFromUrl (string url)
+ {
+ // Remove "simple:" prefix
+ url = url.Substring (7);
+
+ if (url.StartsWith ("simple-directory:"))
+ return String.Format ("<html>This is a directory entry point: {0} </html>",
+ url.Substring (17));
+
+ // Otherwise the last element of the url is the file code we got.
+ int pound = url.LastIndexOf ("#");
+ string code;
+ if (pound == -1)
+ code = url;
+ else
+ code = url.Substring (pound+1);
+
+
+ Stream s = GetHelpStream (code);
+ if (s == null)
+ return String.Format ("<html>No stream for this node: {0} </html>", url);
+
+ //
+ // Now, get the file type
+ //
+ int slash = url.LastIndexOf ("/");
+ string fname = url.Substring (slash + 1, pound - slash - 1).ToLower ();
+
+ if (fname.EndsWith (".html") || fname.EndsWith (".htm")){
+ TextReader r = new StreamReader (s, enc);
+ return r.ReadToEnd ();
+ }
+
+ if (fname.EndsWith (".png") || fname.EndsWith (".jpg") ||
+ fname.EndsWith (".jpeg") || fname.EndsWith (".gif")){
+ return "<html>Image file, have not implemented rendering this yet</html>";
+ }
+
+ // Convert text to HTML
+ StringBuilder result = new StringBuilder ("<html>");
+ TextReader reader = new StreamReader (s, enc);
+ string line;
+
+ while ((line = reader.ReadLine ()) != null){
+ result.Append (line);
+ result.Append ("<br>");
+ }
+ result.Append ("<html>");
+ return result.ToString ();
+ }
+}
+}
--- /dev/null
+//
+// A provider that uses Windows help file xhtml TOC files and looks for the
+// referenced documents to create the help source.
+//
+// Authors:
+// Copyright 2003 Lee Mallabone <gnome@fonicmonkey.net>
+// Johannes Roith <johannes@roith.de>
+// Miguel de Icaza <miguel@ximian.com>
+
+using System;
+using System.IO;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Xml;
+
+namespace MonkeyDoc.Providers
+{
+ public class XhtmlProvider : Provider
+ {
+ string tocFile;
+
+ public XhtmlProvider (string handbookTocFile)
+ {
+ tocFile = handbookTocFile;
+ if (!File.Exists (tocFile))
+ throw new FileNotFoundException (String.Format ("The table of contents, `{0}' does not exist", tocFile));
+ }
+
+ public override void PopulateTree (Tree tree)
+ {
+ //new SimpleHandbookTOCParser(tree, tocFile);
+ // TODO: port it
+ }
+
+ public override void CloseTree (HelpSource hs, Tree tree)
+ {
+ }
+ }
+
+ public class XhtmlHelpSource : HelpSource
+ {
+ public XhtmlHelpSource (string base_file, bool create) : base (base_file, create)
+ {
+
+ }
+
+ const string XhtmlPrefix = "xhtml:";
+
+ protected override string UriPrefix {
+ get {
+ return XhtmlPrefix;
+ }
+ }
+
+ public override DocumentType GetDocumentTypeForId (string id, out Dictionary<string, string> extraArgs)
+ {
+ extraArgs = null;
+ return id == "root:" ? DocumentType.TocXml : DocumentType.MonoBook;
+ }
+
+ public override bool IsGeneratedContent (string id)
+ {
+ return id == "root:";
+ }
+
+ public override string GetText (string url)
+ {
+ return TreeDumper.ExportToTocXml (Tree.RootNode, "Mono Handbook", string.Empty);
+ }
+
+ public static string GetAbsoluteLink(string target, string url)
+ {
+
+ string value = null;
+
+ if (target.StartsWith ("#") ||
+ target.StartsWith ("T:") ||
+ target.StartsWith ("M:") ||
+ target.StartsWith ("P:") ||
+ target.StartsWith ("T:") ||
+ target.StartsWith ("E:") ||
+ target.StartsWith ("F:") ||
+ target.StartsWith ("O:") ||
+ target.StartsWith ("N:") ||
+ target.StartsWith ("api:"))
+ return null;
+
+ int endp = target.IndexOf(':');
+
+ if (endp == -1)
+ endp = 0;
+ string protocol = target.Substring(0, endp);
+ switch (protocol) {
+ case "mailto":
+ case "http":
+ case "https":
+ case "ftp":
+ case "news":
+ case "irc":
+ break;
+ default:
+ // handle absolute urls like: /html/en/images/empty.png
+ if (!target.StartsWith("/")) {
+
+ // url is something like "gnome/bindings/mono.html"
+ // This will get the path "gnome/bindings"
+
+ int slash = url.LastIndexOf ("/");
+ string tmpurl = url;
+
+ if (slash != -1)
+ tmpurl = url.Substring(0, slash);
+
+ // Count "../" in target and go one level down
+ // for each in tmpurl, eventually, then remove "../".
+
+ Regex reg1 = new Regex("../");
+ MatchCollection matches = reg1.Matches(target);
+
+ for(int i = 1; i < matches.Count; i++) {
+ slash = tmpurl.LastIndexOf ("/");
+ if (slash != -1)
+ tmpurl = tmpurl.Substring(0, slash);
+ }
+
+ target = target.Replace("../", "");
+
+ value = tmpurl + "/" + target;
+
+ } else {
+ value = target.Substring(1, target.Length - 1);
+ }
+ break;
+ }
+ return value;
+ }
+
+ XmlDocument RewriteLinks(XmlDocument docToProcess, string url)
+ {
+ XmlNodeList nodeList = docToProcess.GetElementsByTagName("a");
+
+ foreach(XmlNode node in nodeList) {
+
+ XmlElement element = (XmlElement) node;
+
+ if (element.HasAttribute("href") ){
+
+ XmlAttribute href = element.GetAttributeNode("href");
+ string target = href.Value;
+
+ target = GetAbsoluteLink(target, url);
+ if (target != null) {
+ string newtarget = String.Format ("source-id:{0}:xhtml:{1}", SourceID, target);
+ href.Value = newtarget;
+ }
+ }
+ }
+
+ nodeList = docToProcess.GetElementsByTagName("img");
+
+ foreach(XmlNode node in nodeList) {
+
+ XmlElement element = (XmlElement) node;
+
+ if (element.HasAttribute("src") ){
+
+ XmlAttribute href = element.GetAttributeNode("src");
+ string target = href.Value;
+
+ target = GetAbsoluteLink(target, url);
+ if (target != null) {
+ string newtarget = String.Format ("source-id:{0}:xhtml:{1}", SourceID, target);
+ href.Value = newtarget;
+ }
+ }
+ }
+
+ return docToProcess;
+ }
+
+ public override void PopulateIndex (IndexMaker index_maker)
+ {
+ PopulateIndexFromNodes (Tree.RootNode);
+ }
+
+ void PopulateIndexFromNodes (Node start)
+ {
+ /*var nodes = start.Nodes;
+
+ if (nodes != null) {
+ foreach (Node n in nodes)
+ PopulateIndexFromNodes (n);
+ }*/
+ }
+ }
+}
--- /dev/null
+using System;
+using System.Configuration;
+using System.Collections.Specialized;
+
+namespace MonkeyDoc
+{
+ public static class Settings
+ {
+ static KeyValueConfigurationCollection libConfig;
+ static KeyValueConfigurationCollection exeConfig;
+
+ static Settings ()
+ {
+ try {
+ var config = ConfigurationManager.OpenExeConfiguration (System.Reflection.Assembly.GetExecutingAssembly ().Location);
+ libConfig = config.AppSettings.Settings;
+ } catch {}
+
+ try {
+ exeConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).AppSettings.Settings;
+ } catch {}
+ }
+
+ public static string Get (string key) {
+ KeyValueConfigurationElement element = null;
+ // We check the configuration in order: app first and then library itself
+ if (exeConfig != null)
+ element = exeConfig[key];
+ if (element == null && libConfig != null)
+ element = libConfig[key];
+
+ return element == null ? null : element.Value;
+ }
+
+ public static KeyValueConfigurationCollection AppSettings {
+ get {
+ return exeConfig;
+ }
+ }
+
+ public static KeyValueConfigurationCollection LibSettings {
+ get {
+ return libConfig;
+ }
+ }
+ }
+}
--- /dev/null
+using System;
+using System.IO;
+using System.Collections.Generic;
+
+namespace MonkeyDoc
+{
+ // Define a storage mechanism for a help source
+ public interface IDocStorage : IDisposable
+ {
+ // Tell if the storage can store successive change to the doc as revision
+ bool SupportRevision { get; }
+ IDocRevisionManager RevisionManager { get; }
+
+ // Tell if the storage support modifying an existing data
+ bool SupportChange { get; }
+
+ /* Store data inside the storage backend
+ * if SupportChange is false and user try to store something with an existing id
+ * an exception will be thrown
+ * if id is null or empty, the storage will try to create an automatic id. In all
+ * case the id that has been used to store the content is returned by the method
+ */
+ string Store (string id, string text);
+ string Store (string id, byte[] data);
+ string Store (string id, Stream stream);
+
+ Stream Retrieve (string id);
+
+ IEnumerable<string> GetAvailableIds ();
+ }
+
+ public interface IDocRevisionManager
+ {
+ Stream RetrieveWithRevision (string id, string revision);
+
+ // This should be ordered by most recent first
+ IEnumerable<string> AvailableRevisionsForId (string id);
+ // This can simply be implemented with above property but it can also be
+ // a revision storage symbolic value like "HEAD"
+ string LatestRevisionForId (string id);
+
+ // A commit message for instance
+ string GetRevisionDescription (string revision);
+ }
+
+ public static class DocRevisionManagerExtensions
+ {
+ public static Stream RetrieveLatestRevision (this IDocRevisionManager revManager, string id)
+ {
+ return revManager.RetrieveWithRevision (id, revManager.LatestRevisionForId (id));
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.IO;
+using System.Xml;
+using System.Linq;
+using System.Collections.Generic;
+
+namespace MonkeyDoc.Storage
+{
+ // A storage that doesn't store
+ public class NullStorage : IDocStorage
+ {
+ public NullStorage ()
+ {
+ }
+
+ public bool SupportRevision {
+ get {
+ return false;
+ }
+ }
+
+ public IDocRevisionManager RevisionManager {
+ get {
+ return null;
+ }
+ }
+
+ public bool SupportChange {
+ get {
+ return true;
+ }
+ }
+
+ public string Store (string id, string text)
+ {
+ return id;
+ }
+
+ public string Store (string id, byte[] data)
+ {
+ return id;
+ }
+
+ public string Store (string id, Stream stream)
+ {
+ return id;
+ }
+
+ public Stream Retrieve (string id)
+ {
+ return null;
+ }
+
+ public IEnumerable<string> GetAvailableIds ()
+ {
+ return Enumerable.Empty<string> ();
+ }
+
+ public void Dispose ()
+ {
+ }
+ }
+}
--- /dev/null
+using System;
+using System.IO;
+using System.Linq;
+using System.Collections.Generic;
+
+namespace MonkeyDoc.Storage
+{
+ // A read-only storage to access ecma XML document based on a standard directory layout
+ // id are relative path inside the base doc directory
+ public class UncompiledDocStorage : IDocStorage
+ {
+ readonly string basePath;
+
+ public UncompiledDocStorage (string basePath)
+ {
+ this.basePath = basePath;
+ }
+
+ public bool SupportRevision {
+ get {
+ return false;
+ }
+ }
+
+ public IDocRevisionManager RevisionManager {
+ get {
+ return null;
+ }
+ }
+
+ public bool SupportChange {
+ get {
+ return false;
+ }
+ }
+
+ public string Store (string id, string text)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public string Store (string id, byte[] data)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public string Store (string id, Stream stream)
+ {
+ throw new NotSupportedException ();
+ }
+
+ public Stream Retrieve (string id)
+ {
+ var path = id;
+ if ('/' != Path.DirectorySeparatorChar)
+ path = path.Replace ('/', Path.DirectorySeparatorChar);
+ return File.OpenRead (Path.Combine (basePath, path));
+ }
+
+ public IEnumerable<string> GetAvailableIds ()
+ {
+ return Directory.EnumerateFiles (basePath, "*.xml", SearchOption.AllDirectories);
+ }
+
+ public void Dispose ()
+ {
+ }
+ }
+}
--- /dev/null
+using System;
+using System.IO;
+using System.Xml;
+using System.Linq;
+using System.Text;
+using System.Collections.Generic;
+
+using ICSharpCode.SharpZipLib.Zip;
+
+namespace MonkeyDoc.Storage
+{
+ public class ZipStorage : IDocStorage
+ {
+ string zipFileName;
+ int code;
+ ZipOutputStream zipOutput;
+ ZipFile zipFile;
+ // SharpZipLib use linear search to map name to index, correct that a bit
+ Dictionary<string, int> entries = new Dictionary<string, int> ();
+
+ public ZipStorage (string zipFileName)
+ {
+ this.zipFileName = zipFileName;
+ }
+
+ public bool SupportRevision {
+ get {
+ return false;
+ }
+ }
+
+ public IDocRevisionManager RevisionManager {
+ get {
+ return null;
+ }
+ }
+
+ public bool SupportChange {
+ get {
+ return true;
+ }
+ }
+
+ public string Store (string id, string text)
+ {
+ EnsureOutput ();
+ SetupEntry (zipOutput, ref id);
+ var writer = new StreamWriter (zipOutput);
+ writer.Write (text);
+ writer.Flush ();
+
+ return id;
+ }
+
+ public string Store (string id, byte[] data)
+ {
+ EnsureOutput ();
+ SetupEntry (zipOutput, ref id);
+ zipOutput.Write (data, 0, data.Length);
+ return id;
+ }
+
+ public string Store (string id, Stream stream)
+ {
+ EnsureOutput ();
+ SetupEntry (zipOutput, ref id);
+ stream.CopyTo (zipOutput);
+ return id;
+ }
+
+ void SetupEntry (ZipOutputStream zipOutput, ref string id)
+ {
+ if (string.IsNullOrEmpty (id))
+ id = GetNewCode ();
+
+ ZipEntry entry = new ZipEntry (id);
+ zipOutput.PutNextEntry (entry);
+ }
+
+ public Stream Retrieve (string id)
+ {
+ EnsureInput ();
+ int index;
+ ZipEntry entry;
+ if (!entries.TryGetValue (id, out index) || (entry = zipFile[index]) == null)
+ entry = zipFile.GetEntry (id);
+ if (entry != null)
+ return zipFile.GetInputStream (entry);
+ else
+ throw new ArgumentException ("id", string.Format ("'{0}' isn't a valid id for this storage", id));
+ }
+
+ public IEnumerable<string> GetAvailableIds ()
+ {
+ EnsureInput ();
+ return zipFile.Cast<ZipEntry> ().Select (ze => ze.Name);
+ }
+
+ void EnsureOutput ()
+ {
+ if (zipFile != null)
+ throw new InvalidOperationException ("This ZipStorage instance is already used in read-mode");
+ if (zipOutput != null)
+ return;
+ zipOutput = new ZipOutputStream (File.Create (zipFileName));
+ }
+
+ void EnsureInput ()
+ {
+ if (zipOutput != null)
+ throw new InvalidOperationException ("This ZipStorage instance is already used in write-mode");
+ if (zipFile != null)
+ return;
+ zipFile = new ZipFile (zipFileName);
+ entries = Enumerable.Range (0, zipFile.Size).ToDictionary (i => zipFile[i].Name, i => i);
+ }
+
+ public void Dispose ()
+ {
+ if (zipOutput != null)
+ zipOutput.Dispose ();
+ if (zipFile != null)
+ zipFile.Close ();
+ }
+
+ string GetNewCode ()
+ {
+ return String.Format ("{0}", code++);
+ }
+ }
+}
\ No newline at end of file
+++ /dev/null
-using System;
-using System.Linq;
-using System.Text;
-using System.Collections.Generic;
-
-namespace MonkeyDoc.Ecma
-{
- /* Some properties might not be filled/meaningful depending on kind
- * like a namespace EcmaUrl won't have a valid TypeName
- */
- public class EcmaDesc : IEquatable<EcmaDesc>
- {
- public enum Kind
- {
- Type,
- Constructor,
- Method,
- Namespace,
- Field,
- Property,
- Event,
- Operator
- }
-
- public enum Mod
- {
- Normal,
- Pointer,
- Ref,
- Out
- }
-
- public enum Format
- {
- WithArgs,
- WithoutArgs
- }
-
- public Kind DescKind {
- get;
- set;
- }
-
- public Mod DescModifier {
- get;
- set;
- }
-
- public string Namespace {
- get;
- set;
- }
-
- public string TypeName {
- get;
- set;
- }
-
- public string MemberName {
- get;
- set;
- }
-
- public EcmaDesc NestedType {
- get;
- set;
- }
-
- /* A list of the array dimensions attached to this type.
- * The list count corresponds to the number of recursive
- * array definition (jagged arrays) the value of the
- * corresponding list item is the number of dimension
- * attached to that array definition instance
- */
- public IList<int> ArrayDimensions {
- get;
- set;
- }
-
- /* Depending on the form of the url, we might not have the type
- * of the argument but only how many the type/member has i.e.
- * when such number is specified with a backtick
- */
- public IList<EcmaDesc> GenericTypeArguments {
- get;
- set;
- }
-
- public IList<EcmaDesc> GenericMemberArguments {
- get;
- set;
- }
-
- public IList<EcmaDesc> MemberArguments {
- get;
- set;
- }
-
- /* This indicates that we actually want an inner part of the ecmadesc
- * i.e. in case of T: we could want the members (*), ctor (C), methods (M), ...
- */
- public char Etc {
- get;
- set;
- }
-
- public bool IsEtc {
- get {
- return Etc != (char)0;
- }
- }
-
- /* EtcFilter is only valid in some case of IsEtc when the inner part needs
- * to be further filtered e.g. in case we want a listing of the type overloads
- * Equals
- */
- public string EtcFilter {
- get;
- set;
- }
-
- /* When a member is an explicit implementation of an interface member, we register
- * the member EcmaDesc with its interface parent here
- */
- public EcmaDesc ExplicitImplMember {
- get;
- set;
- }
-
- // Returns the TypeName and the generic/inner type information if existing
- public string ToCompleteTypeName (char innerTypeSeparator = '.')
- {
- var result = TypeName;
- if (GenericTypeArguments != null)
- result += FormatGenericArgs (GenericTypeArguments);
- if (NestedType != null)
- result += innerTypeSeparator + NestedType.ToCompleteTypeName ();
- if (ArrayDimensions != null && ArrayDimensions.Count > 0)
- result += ArrayDimensions.Select (dim => "[" + new string (',', dim - 1) + "]").Aggregate (string.Concat);
-
- return result;
- }
-
- // Returns the member name with its generic types if existing
- public string ToCompleteMemberName (Format format)
- {
- /* We special process two cases:
- * - Explicit member implementation which append a full type specification
- * - Conversion operator which are exposed as normal method but have specific captioning in the end
- */
- if (ExplicitImplMember != null) {
- var impl = ExplicitImplMember;
- return impl.FormattedNamespace + impl.ToCompleteTypeName () + "." + impl.ToCompleteMemberName (format);
- } else if (format == Format.WithArgs && DescKind == Kind.Operator && MemberName.EndsWith ("Conversion")) {
- var type1 = MemberArguments[0].FormattedNamespace + MemberArguments[0].ToCompleteTypeName () + ModToString (MemberArguments[0]);
- var type2 = MemberArguments[1].FormattedNamespace + MemberArguments[1].ToCompleteTypeName () + ModToString (MemberArguments[1]);
- return type1 + " to " + type2;
- }
-
- var result = IsEtc && !string.IsNullOrEmpty (EtcFilter) ? EtcFilter : MemberName;
-
- // Temporary hack for monodoc produced inner type ctor
- //if (DescKind == Kind.Constructor && NestedType != null)
- //result = ToCompleteTypeName ();
-
- if (GenericMemberArguments != null)
- result += FormatGenericArgs (GenericMemberArguments);
-
- if (format == Format.WithArgs) {
- result += '(';
- if (MemberArguments != null && MemberArguments.Count > 0) {
- var args = MemberArguments.Select (a => FormatNamespace (a) + a.ToCompleteTypeName ('+') + ModToString (a));
- result += string.Join (",", args);
- }
- result += ')';
- }
-
- return result;
- }
-
- public string ToEcmaCref ()
- {
- var sb = new StringBuilder ();
- // Cref type
- sb.Append (DescKind.ToString ()[0]);
- // Create the rest
- ConstructCRef (sb);
-
- return sb.ToString ();
- }
-
- void ConstructCRef (StringBuilder sb)
- {
- sb.Append (Namespace);
- if (DescKind == Kind.Namespace)
- return;
-
- sb.Append ('.');
- sb.Append (TypeName);
- if (GenericTypeArguments != null) {
- sb.Append ('<');
- foreach (var t in GenericTypeArguments)
- t.ConstructCRef (sb);
- sb.Append ('>');
- }
- if (NestedType != null) {
- sb.Append ('+');
- NestedType.ConstructCRef (sb);
- }
- if (ArrayDimensions != null && ArrayDimensions.Count > 0) {
- for (int i = 0; i < ArrayDimensions.Count; i++) {
- sb.Append ('[');
- sb.Append (new string (',', ArrayDimensions[i] - 1));
- sb.Append (']');
- }
- }
- if (DescKind == Kind.Type)
- return;
-
- if (MemberArguments != null) {
-
- }
- }
-
- public override string ToString ()
- {
- return string.Format ("({8}) {0}::{1}{2}{3}{7} {4}{5}{6} {9} {10}",
- Namespace,
- TypeName,
- FormatGenericArgsFull (GenericTypeArguments),
- NestedType != null ? "+" + NestedType.ToString () : string.Empty,
- MemberName ?? string.Empty,
- FormatGenericArgsFull (GenericMemberArguments),
- MemberArguments != null ? "(" + string.Join (",", MemberArguments.Select (m => m.ToString ())) + ")" : string.Empty,
- ArrayDimensions != null && ArrayDimensions.Count > 0 ? ArrayDimensions.Select (dim => "[" + new string (',', dim - 1) + "]").Aggregate (string.Concat) : string.Empty,
- DescKind.ToString ()[0],
- Etc != 0 ? '(' + Etc.ToString () + ')' : string.Empty,
- ExplicitImplMember != null ? "$" + ExplicitImplMember.ToString () : string.Empty);
-
- }
-
- public override bool Equals (object other)
- {
- var otherDesc = other as EcmaDesc;
- return otherDesc != null && Equals (otherDesc);
- }
-
- public bool Equals (EcmaDesc other)
- {
- if (other == null)
- return false;
-
- if (NestedType == null ^ other.NestedType == null
- || ArrayDimensions == null ^ other.ArrayDimensions == null
- || GenericTypeArguments == null ^ other.GenericTypeArguments == null
- || GenericMemberArguments == null ^ other.GenericMemberArguments == null
- || MemberArguments == null ^ other.MemberArguments == null
- || ExplicitImplMember == null ^ other.ExplicitImplMember == null)
- return false;
-
- return other != null
- && DescKind == other.DescKind
- && TypeName == other.TypeName
- && Namespace == other.Namespace
- && MemberName == other.MemberName
- && (NestedType == null || NestedType.Equals (other.NestedType))
- && (ArrayDimensions == null || ArrayDimensions.SequenceEqual (other.ArrayDimensions))
- && (GenericTypeArguments == null || GenericTypeArguments.SequenceEqual (other.GenericTypeArguments))
- && (GenericMemberArguments == null || GenericMemberArguments.SequenceEqual (other.GenericMemberArguments))
- && (MemberArguments == null || MemberArguments.SequenceEqual (other.MemberArguments))
- && Etc == other.Etc
- && EtcFilter == other.EtcFilter
- && (ExplicitImplMember == null || ExplicitImplMember.Equals (other.ExplicitImplMember));
- }
-
- public override int GetHashCode ()
- {
- return DescKind.GetHashCode ()
- ^ TypeName.GetHashCode ()
- ^ Namespace.GetHashCode ()
- ^ MemberName.GetHashCode ();
- }
-
- bool What (bool input)
- {
- if (!input)
- throw new Exception ("Not equal");
- return input;
- }
-
- bool WhatT (bool input)
- {
- if (input)
- throw new Exception ("Not equal");
- return input;
- }
-
- string FormatNamespace (EcmaDesc desc)
- {
- return string.IsNullOrEmpty (desc.Namespace) ? string.Empty : desc.Namespace + ".";
- }
-
- string FormatGenericArgs (IEnumerable<EcmaDesc> genericArgs)
- {
- return genericArgs != null ? "<" + string.Join (",", genericArgs.Select (t => FormatNamespace (t) + t.ToCompleteTypeName ())) + ">" : string.Empty;
- }
-
- string FormatGenericArgsFull (IEnumerable<EcmaDesc> genericArgs)
- {
- return genericArgs != null ? "<" + string.Join (",", genericArgs.Select (t => t.ToString ())) + ">" : string.Empty;
- }
-
- string ModToString (EcmaDesc desc)
- {
- switch (desc.DescModifier) {
- case Mod.Pointer:
- return "*";
- case Mod.Ref:
- return "&";
- case Mod.Out:
- return "@";
- default:
- return string.Empty;
- }
- }
-
- string FormattedNamespace {
- get {
- return !string.IsNullOrEmpty (Namespace) ? Namespace + "." : string.Empty;
- }
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-%{
-using System.Text;
-using System.IO;
-using System;
-using System.Linq;
-using System.Collections.Generic;
-
-namespace MonkeyDoc.Ecma
-{
- public class EcmaUrlParser
- {
- int yacc_verbose_flag = 0;
-
- public void IsValid (string input)
- {
- var lexer = new EcmaUrlTokenizer (input);
- this.yyparse (lexer);
- }
-
- public EcmaDesc Parse (string input)
- {
- var lexer = new EcmaUrlTokenizer (input);
- return (EcmaDesc)this.yyparse (lexer);
- }
-
- public bool TryParse (string input, out EcmaDesc desc)
- {
- desc = null;
- try {
- desc = Parse (input);
- } catch {
- return false;
- }
- return true;
- }
-
- EcmaDesc SetEcmaDescType (object result, EcmaDesc.Kind kind)
- {
- var desc = result as EcmaDesc;
- desc.DescKind = kind;
- return desc;
- }
-
- List<T> SafeReverse<T> (List<T> input)
- {
- if (input == null)
- return null;
- input.Reverse ();
- return input;
- }
-%}
-
-%token ERROR
-%token IDENTIFIER
-%token DIGIT
-%token DOT
-%token COMMA
-%token COLON
-%token INNER_TYPE_SEPARATOR
-%token OP_GENERICS_LT
-%token OP_GENERICS_GT
-%token OP_GENERICS_BACKTICK
-%token OP_OPEN_PAREN
-%token OP_CLOSE_PAREN
-%token OP_ARRAY_OPEN
-%token OP_ARRAY_CLOSE
-%token SLASH_SEPARATOR
-%token STAR
-%token REF_ARG
-%token OUT_ARG
-%token EXPLICIT_IMPL_SEP
-
-%start expression
-
-%%
-
-expression
- : 'T' COLON type_expression { $$ = SetEcmaDescType ($3, EcmaDesc.Kind.Type); }
- | 'N' COLON namespace_expression { $$ = SetEcmaDescType ($3, EcmaDesc.Kind.Namespace); }
- | 'M' COLON method_expression { $$ = SetEcmaDescType ($3, EcmaDesc.Kind.Method); }
- | 'F' COLON simple_member_expression { $$ = SetEcmaDescType ($3, EcmaDesc.Kind.Field); }
- | 'C' COLON constructor_expression { $$ = SetEcmaDescType ($3, EcmaDesc.Kind.Constructor); }
- | 'P' COLON property_expression { $$ = SetEcmaDescType ($3, EcmaDesc.Kind.Property); }
- | 'E' COLON simple_member_expression { $$ = SetEcmaDescType ($3, EcmaDesc.Kind.Event); }
- | 'O' COLON operator_expression { $$ = SetEcmaDescType ($3, EcmaDesc.Kind.Operator); }
-
-/* i.e. id.id.id or id */
-dot_expression
- : IDENTIFIER { $$ = new List<string> { (string)$1 }; }
- | IDENTIFIER DOT dot_expression { ((ICollection<string>)$3).Add ((string)$1); $$ = $3; }
-
-namespace_expression
- : dot_expression { $$ = new EcmaDesc { Namespace = string.Join (".", ((IEnumerable<string>)$1).Reverse ()) }; }
-
-type_expression
- : dot_expression type_expression_suffix {
- var dotExpr = ((List<string>)$1);
- dotExpr.Reverse ();
- var desc = $2 as EcmaDesc;
- desc.DescKind = EcmaDesc.Kind.Type;
- desc.Namespace = string.Join (".", dotExpr.Take (dotExpr.Count - 1));
- desc.TypeName = dotExpr.Last ();
- $$ = desc;
- }
-
-/* To be used in types with no namespaces attached to them like an inner type*/
-reduced_type_expression
- : IDENTIFIER type_expression_suffix {
- var desc = $2 as EcmaDesc;
- desc.DescKind = EcmaDesc.Kind.Type;
- desc.TypeName = $1 as string;
- $$ = desc;
- }
-
-type_expression_suffix
- : opt_generic_type_suffix opt_inner_type_description opt_array_definition opt_etc {
- bool nestedDescHasEtc = $2 != null && ((EcmaDesc)$2).IsEtc;
- EcmaDesc nestedType = (EcmaDesc)$2;
- $$ = new EcmaDesc {
- GenericTypeArguments = $1 as List<EcmaDesc>,
- NestedType = nestedType,
- ArrayDimensions = SafeReverse ($3 as List<int>),
- Etc = $4 != null ? ((Tuple<char, string>)$4).Item1 : nestedDescHasEtc ? nestedType.Etc : (char)0,
- EtcFilter = $4 != null ? ((Tuple<char, string>)$4).Item2 : nestedDescHasEtc ? nestedType.EtcFilter : null
- };
- if (nestedDescHasEtc) {
- nestedType.Etc = (char)0;
- nestedType.EtcFilter = null;
- }
- }
-
-opt_inner_type_description
- : /* empty */ { $$ = null; }
- | INNER_TYPE_SEPARATOR reduced_type_expression { $$ = $2; }
-
-opt_generic_type_suffix
- : /* empty */ { $$ = null; }
- | OP_GENERICS_BACKTICK DIGIT { $$ = Enumerable.Repeat<string> (null, (int)$2).ToList (); }
- | OP_GENERICS_LT generic_type_arg_list OP_GENERICS_GT { $$ = $2; }
-
-generic_type_arg_list
- : type_expression { $$ = new List<EcmaDesc> () { (EcmaDesc)$1 }; }
- | generic_type_arg_list COMMA type_expression { ((List<EcmaDesc>)$1).Add ((EcmaDesc)$3); $$ = $1; }
-
-opt_array_definition
- : /* empty */ { $$ = null; }
- | OP_ARRAY_OPEN opt_array_definition_list OP_ARRAY_CLOSE opt_array_definition {
- var dims = ((IList<int>)$4) ?? new List<int> (2);
- dims.Add ((int)$2);
- $$ = dims;
- }
-
-opt_array_definition_list
- : /* empty */ { $$ = 1; }
- | COMMA opt_array_definition_list { $$ = ((int)$2) + 1; }
-
-opt_etc
- : /* empty */ { $$ = null; }
- | SLASH_SEPARATOR etc_identifier { $$ = Tuple.Create<char, string> (((string)$2)[0], null); }
- | SLASH_SEPARATOR etc_identifier SLASH_SEPARATOR reduced_member_expression { $$ = Tuple.Create<char, string> (((string)$2)[0], (string)$4); }
-/* | SLASH_SEPARATOR etc_identifier SLASH_SEPARATOR IDENTIFIER opt_generic_type_suffix { $$ = Tuple.Create<char, string> (((string)$2)[0], (string)$4 + ($5 == null ? string.Empty : "<" + string.Join (",", ((IEnumerable<EcmaDesc>)$5).Select (t => t.ToCompleteTypeName ())) + ">")); } */
-
-etc_identifier
- : STAR { $$ = "*"; }
- | IDENTIFIER { $$ = $1; }
-
-method_expression
- : type_expression DOT IDENTIFIER opt_generic_type_suffix opt_arg_list_suffix {
- var desc = $1 as EcmaDesc;
- desc.MemberName = $3 as string;
- desc.GenericMemberArguments = $4 as List<EcmaDesc>;
- desc.MemberArguments = SafeReverse ($5 as List<EcmaDesc>);
- $$ = desc;
- }
- | dot_expression opt_generic_type_suffix opt_arg_list_suffix {
- var dotExpr = ((List<string>)$1);
- $$ = new EcmaDesc {
- Namespace = string.Join (".", dotExpr.Skip (2).DefaultIfEmpty (string.Empty).Reverse ()),
- TypeName = dotExpr.Skip (1).First (),
- MemberName = dotExpr.First (),
- GenericMemberArguments = $2 as List<EcmaDesc>,
- MemberArguments = SafeReverse ($3 as List<EcmaDesc>)
- };
- }
- | type_expression EXPLICIT_IMPL_SEP method_expression {
- var desc = $1 as EcmaDesc;
- desc.ExplicitImplMember = $3 as EcmaDesc;
- $$ = desc;
- }
-
-/* To be used with members that may have no type/namespace attached */
-reduced_member_expression
- : IDENTIFIER opt_generic_type_suffix { $$ = (string)$1 + ($2 == null ? string.Empty : "<" + string.Join (",", ((IEnumerable<EcmaDesc>)$2).Select (t => t.ToCompleteTypeName ())) + ">"); }
- | IDENTIFIER opt_generic_type_suffix DOT reduced_member_expression {
- var existing = $4 as string;
- var expr = (string)$1 + ($2 == null ? string.Empty : "<" + string.Join (",", ((IEnumerable<EcmaDesc>)$2).Select (t => t.ToCompleteTypeName ())) + ">");
- $$ = expr + "." + existing;
- }
-
-arg_type_expression
- : type_expression opt_arg_type_suffix { var desc = (EcmaDesc)$1; desc.DescModifier = (EcmaDesc.Mod)$2; $$ = desc; }
-
-opt_arg_type_suffix
- : /* empty */ { $$ = EcmaDesc.Mod.Normal; }
- | STAR { $$ = EcmaDesc.Mod.Pointer; }
- | REF_ARG { $$ = EcmaDesc.Mod.Ref; }
- | OUT_ARG { $$ = EcmaDesc.Mod.Out; }
-
-type_expression_list
- : /* empty */ { $$ = null; }
- | arg_type_expression { $$ = new List<EcmaDesc> () { (EcmaDesc)$1 }; }
- | arg_type_expression COMMA type_expression_list { ((List<EcmaDesc>)$3).Add ((EcmaDesc)$1); $$ = $3; }
-
-simple_member_expression
- : dot_expression {
- var dotExpr = ((List<string>)$1);
- dotExpr.Reverse ();
-
- $$ = new EcmaDesc {
- Namespace = dotExpr.Count > 2 ? string.Join (".", dotExpr.Take (dotExpr.Count - 2)) : string.Empty,
- TypeName = dotExpr.Count > 1 ? dotExpr[dotExpr.Count - 2] : string.Empty,
- MemberName = dotExpr[dotExpr.Count - 1]
- };
- }
- | type_expression DOT IDENTIFIER {
- var desc = $1 as EcmaDesc;
- desc.MemberName = $3 as string;
- $$ = desc;
- }
- | type_expression EXPLICIT_IMPL_SEP simple_member_expression {
- var desc = $1 as EcmaDesc;
- desc.ExplicitImplMember = $3 as EcmaDesc;
- $$ = desc;
- }
-
-constructor_expression
- : method_expression { $$ = $1; }
-
-operator_expression
- : method_expression { $$ = $1; }
-
-property_expression
- : simple_member_expression opt_property_indexer {
- var desc = $1 as EcmaDesc;
- (desc.ExplicitImplMember ?? desc).MemberArguments = SafeReverse ($2 as List<EcmaDesc>);
- $$ = desc;
- }
-
-opt_property_indexer
- : opt_arg_list_suffix { $$ = $1; }
-
-/*simple_member_expression opt_arg_list_suffix { $$ = CopyFromEcmaDesc (new EcmaDesc {
- MemberArguments = SafeReverse ($2 as List<EcmaDesc>)
- }, (EcmaDesc)$1);
- }*/
-
-opt_arg_list_suffix
- : /* empty */ { $$ = null; }
- | OP_OPEN_PAREN type_expression_list OP_CLOSE_PAREN { $$ = $2; }
-
-%%
-
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using System.IO;
-
-namespace MonkeyDoc.Ecma
-{
- public class EcmaUrlParserDriver
- {
- public static void Main (string[] args)
- {
- var input = new StringReader (args[0]);
- var lexer = new EcmaUrlTokenizer (input);
- var parser = new EcmaUrlParser ();
-
- Console.WriteLine (parser.yyparse (lexer));
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using System.Text;
-using System.Globalization;
-
-namespace MonkeyDoc.Ecma
-{
- public class EcmaUrlTokenizer : yyParser.yyInput
- {
- const char EndOfStream = (char)0;
- string input;
- object val;
- int current_token;
- int current_pos;
- int real_current_pos;
- int identCount = 0;
-
- public EcmaUrlTokenizer (string input)
- {
- this.input = input;
- }
-
- static bool is_identifier_start_character (char c)
- {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || Char.IsLetter (c);
- }
-
- static bool is_identifier_part_character (char c)
- {
- if (c >= 'a' && c <= 'z')
- return true;
-
- if (c >= 'A' && c <= 'Z')
- return true;
-
- if (c == '_' || (c >= '0' && c <= '9'))
- return true;
-
- if (c < 0x80)
- return false;
-
- return Char.IsLetter (c) || Char.GetUnicodeCategory (c) == UnicodeCategory.ConnectorPunctuation;
- }
-
- public bool advance ()
- {
- return Peek () != EndOfStream;
- }
-
- public Object Value {
- get {
- return val;
- }
- }
-
- public Object value ()
- {
- return val;
- }
-
- public int token ()
- {
- int token = xtoken ();
- //Console.WriteLine ("Current token {0} with value {1}", token, val == null ? "(none)" : val.ToString ());
- if (token == Token.ERROR)
- Console.WriteLine ("Problem at pos {0} after token {1}", current_pos, current_token);
- current_token = token;
- return token;
- }
-
- int xtoken ()
- {
- char next = Read ();
- while (char.IsWhiteSpace (next))
- next = Read ();
- current_pos++;
- val = null;
-
- switch (next) {
- case ',':
- return Token.COMMA;
- case '.':
- return Token.DOT;
- case '<':
- return Token.OP_GENERICS_LT;
- case '>':
- return Token.OP_GENERICS_GT;
- case '`':
- return Token.OP_GENERICS_BACKTICK;
- case '(':
- return Token.OP_OPEN_PAREN;
- case ')':
- return Token.OP_CLOSE_PAREN;
- case '+':
- return Token.INNER_TYPE_SEPARATOR;
- case ':':
- return Token.COLON;
- case '/':
- return Token.SLASH_SEPARATOR;
- case '[':
- return Token.OP_ARRAY_OPEN;
- case ']':
- return Token.OP_ARRAY_CLOSE;
- case '*':
- return Token.STAR;
- case '&':
- return Token.REF_ARG;
- case '@':
- return Token.OUT_ARG;
- case '$':
- return Token.EXPLICIT_IMPL_SEP;
- default:
- return TokenizeIdentifierOrNumber (next);
- }
- }
-
- int TokenizeIdentifierOrNumber (char current)
- {
- // We must first return the expression type which is a uppercase letter and a colon
- if (current_pos < 2) {
- val = null;
- return (int)current;
- }
-
- if (is_identifier_start_character (current) || current == '*') {
- unsafe {
- // identifier length is artificially limited to 1024 bytes by implementations
- char* pIdent = stackalloc char[512];
- *pIdent = current;
- identCount = 1;
-
- char peek;
- while ((peek = Peek ()) != EndOfStream && is_identifier_part_character (peek)) {
- *(pIdent + identCount) = Read ();
- ++current_pos;
- ++identCount;
- }
-
- val = new string ((char*)pIdent, 0, identCount);
- return Token.IDENTIFIER;
- }
- } else if (char.IsDigit (current)) {
- val = current - '0';
- return Token.DIGIT;
- } else {
- val = null;
- return Token.ERROR;
- }
- }
-
- char Read ()
- {
- try {
- return input[real_current_pos++];
- } catch {
- return EndOfStream;
- }
- }
-
- char Peek ()
- {
- try {
- return input[real_current_pos];
- } catch {
- return EndOfStream;
- }
- }
- }
-}
+++ /dev/null
-using System;
-using System.IO;
-using System.Linq;
-using System.Xml;
-using System.Diagnostics;
-using System.Collections.Generic;
-
-using Mono.Utilities;
-using Lucene.Net.Index;
-
-namespace MonkeyDoc
-{
- //
- // The HelpSource class keeps track of the archived data, and its
- // tree
- //
- public class HelpSource
- {
- static int id;
-
- //
- // The unique ID for this HelpSource.
- //
- int source_id;
-
- // The name of the HelpSource, used by all the file (.tree, .zip, ...) used by it
- string name;
- // The full directory path where the HelpSource files are located
- string basePath;
-
- // The tree of this help source
- Tree tree;
- string treeFilePath;
- RootTree rootTree;
-
- IDocCache cache;
- IDocStorage storage;
-
- public HelpSource (string base_filename, bool create)
- {
- this.name = Path.GetFileName (base_filename);
- this.basePath = Path.GetDirectoryName (base_filename);
- this.treeFilePath = base_filename + ".tree";
- this.storage = new MonkeyDoc.Storage.ZipStorage (base_filename + ".zip");
- this.cache = DocCacheHelper.GetDefaultCache (Name);
-
- tree = create ? new Tree (this, string.Empty, string.Empty) : new Tree (this, treeFilePath);
-
- source_id = id++;
- }
-
- public HelpSource ()
- {
- tree = new Tree (this, "Blah", "Blah");
- source_id = id++;
- }
-
- public int SourceID {
- get {
- return source_id;
- }
- }
-
- public string Name {
- get {
- return name;
- }
- }
-
- /* This gives the full path of the source/ directory */
- public string BaseFilePath {
- get {
- return basePath;
- }
- }
-
- public TraceLevel TraceLevel {
- get;
- set;
- }
-
- public string BaseDir {
- get {
- return basePath;
- }
- }
-
- public Tree Tree {
- get {
- return tree;
- }
- }
-
- public RootTree RootTree {
- get {
- return rootTree;
- }
- set {
- rootTree = value;
- }
- }
-
- public IDocCache Cache {
- get {
- return cache;
- }
- }
-
- public IDocStorage Storage {
- get {
- return storage;
- }
- protected set {
- storage = value;
- }
- }
-
- // A HelpSource may have a common prefix to its URL, give it here
- protected virtual string UriPrefix {
- get {
- return "dummy:";
- }
- }
-
- /// <summary>
- /// Returns a stream from the packaged help source archive
- /// </summary>
- public virtual Stream GetHelpStream (string id)
- {
- return storage.Retrieve (id);
- }
-
- public virtual Stream GetCachedHelpStream (string id)
- {
- if (string.IsNullOrEmpty (id))
- throw new ArgumentNullException ("id");
- if (!cache.CanCache (DocEntity.Text))
- return GetHelpStream (id);
- if (!cache.IsCached (id))
- cache.CacheText (id, GetHelpStream (id));
- return cache.GetCachedStream (id);
- }
-
- public XmlReader GetHelpXml (string id)
- {
- var url = "monodoc:///" + SourceID + "@" + Uri.EscapeDataString (id) + "@";
- var stream = cache.IsCached (id) ? cache.GetCachedStream (id) : storage.Retrieve (id);
-
- return stream == null ? null : new XmlTextReader (url, stream);
- }
-
- public virtual XmlDocument GetHelpXmlWithChanges (string id)
- {
- XmlDocument doc = new XmlDocument ();
- if (!storage.SupportRevision) {
- doc.Load (GetHelpXml (id));
- } else {
- var revManager = storage.RevisionManager;
- doc.Load (revManager.RetrieveLatestRevision (id));
- }
- return doc;
- }
-
- public virtual string GetCachedText (string id)
- {
- if (!cache.CanCache (DocEntity.Text))
- return GetText (id);
- if (!cache.IsCached (id))
- cache.CacheText (id, GetText (id));
- return cache.GetCachedString (id);
- }
-
- public virtual string GetText (string id)
- {
- return new StreamReader (GetHelpStream (id)).ReadToEnd ();
- }
-
- // Tells if the result for the provided id is generated dynamically
- // by the help source
- public virtual bool IsGeneratedContent (string id)
- {
- return false;
- }
-
- // Tells if the content of the provided id is meant to be returned raw
- public virtual bool IsRawContent (string id)
- {
- return false;
- }
-
- // Tells if provided id refers to a multi-content-type document if it's case
- // tells the ids it's formed of
- public virtual bool IsMultiPart (string id, out IEnumerable<string> parts)
- {
- parts = null;
- return false;
- }
-
- /// <summary>
- /// Saves the tree and the archive
- /// </summary>
- public void Save ()
- {
- tree.Save (treeFilePath);
- storage.Dispose ();
- }
-
- public virtual void RenderPreviewDocs (XmlNode newNode, XmlWriter writer)
- {
- throw new NotImplementedException ();
- }
-
- public virtual string GetPublicUrl (Node node)
- {
- return node.GetInternalUrl ();
- }
-
- public virtual bool CanHandleUrl (string url)
- {
- return url.StartsWith (UriPrefix, StringComparison.OrdinalIgnoreCase);
- }
-
- public virtual string GetInternalIdForUrl (string url, out Node node)
- {
- node = MatchNode (url);
- return node == null ? null : url.Substring (UriPrefix.Length);
- }
-
- public virtual Node MatchNode (string url)
- {
- Node current = null;
-
- var matchCache = LRUCache<string, Node>.Default;
- if ((current = matchCache.Get (url)) != null)
- return current;
-
- current = Tree.RootNode;
- var strippedUrl = url.StartsWith (UriPrefix, StringComparison.OrdinalIgnoreCase) ? url.Substring (UriPrefix.Length) : url;
- var searchNode = new Node () { Element = strippedUrl };
-
- do {
- int index = current.Nodes.BinarySearch (searchNode, NodeElementComparer.Instance);
- if (index >= 0) {
- Node n = current.Nodes[index];
- //Console.WriteLine ("Binarysearch success for {0} which fell on {1}", strippedUrl, n.Element);
- matchCache.Put (url, n);
- return n;
- }
- index = ~index;
- if (index == current.Nodes.Count) {
- //Console.WriteLine ("Match fail for {0}", strippedUrl);
- //Console.WriteLine (current.Nodes.Select (n => n.Element).Aggregate ((e1, e2) => e1 + ", " + e2));
- return SlowMatchNode (Tree.RootNode, matchCache, strippedUrl);
- }
- current = current.Nodes [index - 1];
- //Console.WriteLine ("Binarysearch failed for {0}, next node check is {1}", strippedUrl, current.Element);
- } while (true);
-
- return null;
- }
-
- /* That slow path is mainly here to handle ecmaspec type of url which are composed of hard to sort numbers
- * because they don't have the same amount of digit. We could use a regex to harmonise the various number
- * parts but then it would be quite specific. Since in the case of ecmaspec the tree is well-formed enough
- * the "Slow" match should still be fast enough
- */
- Node SlowMatchNode (Node current, LRUCache<string, Node> matchCache, string url)
- {
- //Console.WriteLine ("Entering slow path for {0} starting from {1}", url, current.Element);
- while (current != null) {
- bool stop = true;
- foreach (Node n in current.Nodes) {
- var element = n.Element.StartsWith (UriPrefix, StringComparison.OrdinalIgnoreCase) ? n.Element.Substring (UriPrefix.Length) : n.Element;
- if (url == element) {
- matchCache.Put (url, n);
- return n;
- } else if (url.StartsWith (element + ".", StringComparison.OrdinalIgnoreCase) && !n.IsLeaf) {
- current = n;
- stop = false;
- break;
- }
- }
- if (stop)
- current = null;
- }
-
- return null;
- }
-
- class NodeElementComparer : IComparer<Node>
- {
- public static NodeElementComparer Instance = new NodeElementComparer ();
-
- public int Compare (Node n1, Node n2)
- {
- return string.Compare (Cleanup (n1), Cleanup (n2), StringComparison.Ordinal);
- }
-
- string Cleanup (Node n)
- {
- var prefix = n.Tree != null && n.Tree.HelpSource != null ? n.Tree.HelpSource.UriPrefix : string.Empty;
- var element = n.Element.StartsWith (prefix, StringComparison.OrdinalIgnoreCase) ? n.Element.Substring (prefix.Length) : n.Element;
- if (char.IsDigit (element, 0)) {
- var count = element.TakeWhile (char.IsDigit).Count ();
- element = element.PadLeft (Math.Max (0, 3 - count) + element.Length, '0');
- }
- //Console.WriteLine ("Cleaned up {0} to {1}", n.Element, element);
- return element;
- }
- }
-
- public virtual DocumentType GetDocumentTypeForId (string id, out Dictionary<string, string> extraParams)
- {
- extraParams = null;
- return DocumentType.PlainText;
- }
-
- public virtual Stream GetImage (string url)
- {
- return null;
- }
-
- //
- // Populates the index.
- //
- public virtual void PopulateIndex (IndexMaker index_maker)
- {
- }
-
- //
- // Create different Documents for adding to Lucene search index
- // The default action is do nothing. Subclasses should add the docs
- //
- public virtual void PopulateSearchableIndex (IndexWriter writer)
- {
-
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using System.IO;
-using System.Text;
-using System.Linq;
-using System.Xml;
-using System.Collections.Generic;
-
-namespace MonkeyDoc
-{
- public class Node : IComparable<Node>, IComparable
- {
- readonly Tree tree;
- string caption, element, pubUrl;
- public bool Documented;
- bool loaded;
- Node parent;
- List<Node> nodes;
- Dictionary<string, Node> childrenLookup;
- /* 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;
-
- 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.tree = tree;
- this.caption = caption;
- this.element = element;
- }
-
- /// <summary>
- /// Creates a node from an on-disk representation
- /// </summary>
- internal Node (Node parent, int address) : this (parent.tree, address)
- {
- this.parent = parent;
- }
-
- internal Node (Tree tree, int address)
- {
- this.address = address;
- this.tree = tree;
- if (address > 0)
- LoadNode ();
- }
-
- /* This is solely used for MatchNode to check for equality */
- internal Node ()
- {
- }
-
- void LoadNode ()
- {
- tree.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;
- }
-
- public List<Node> Nodes {
- 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 tree;
- }
- }
-
- internal int Address {
- get {
- return address;
- }
- }
-
- /// <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);
- }
- }
-
- 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 = tree.HelpSource != null ? tree.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 = caption;
- var cap2 = 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);
- }
- }
-}
+++ /dev/null
-using System;
-
-namespace MonkeyDoc
-{
- public abstract class Provider
- {
- //
- // This code is used to "tag" all the different sources
- //
- static short serial;
-
- public int Code { get; set; }
-
- public Provider ()
- {
- Code = serial++;
- }
-
- public abstract void PopulateTree (Tree tree);
-
- //
- // Called at shutdown time after the tree has been populated to perform
- // any fixups or final tasks.
- //
- public abstract void CloseTree (HelpSource hs, Tree tree);
- }
-}
+++ /dev/null
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Collections.Specialized;
-using System.Configuration;
-using System.IO;
-using System.Linq;
-using System.Reflection;
-using System.Runtime.InteropServices;
-using System.Xml;
-
-using MonkeyDoc.Providers;
-using Lucene.Net.Analysis.Standard;
-using Lucene.Net.Index;
-
-namespace MonkeyDoc
-{
- public class RootTree : Tree
- {
- public const int MonodocVersion = 2;
- const string RootNamespace = "root:/";
- string basedir;
- static List<string> uncompiledHelpSourcePaths = new List<string>();
- HashSet<string> loadedSourceFiles = new HashSet<string>();
- List<HelpSource> helpSources = new List<HelpSource>();
- Dictionary<string, Node> nameToNode = new Dictionary<string, Node>();
- Dictionary<string, HelpSource> nameToHelpSource = new Dictionary<string, HelpSource>();
-
- public IList<HelpSource> HelpSources {
- get {
- return this.helpSources.AsReadOnly();
- }
- }
-
- public DateTime LastHelpSourceTime {
- get;
- set;
- }
-
- static bool IsUnix {
- get {
- int platform = (int)Environment.OSVersion.Platform;
- return platform == 4 || platform == 128 || platform == 6;
- }
- }
-
- RootTree () : base (null, "Mono Documentation", "root:")
- {
- base.RootNode.EnsureNodes();
- this.LastHelpSourceTime = DateTime.Now;
- }
-
- public static void AddUncompiledSource (string path)
- {
- uncompiledHelpSourcePaths.Add (path);
- }
-
- public static RootTree LoadTree ()
- {
- return RootTree.LoadTree (RootTree.ProbeBaseDirectories ());
- }
-
- static string ProbeBaseDirectories ()
- {
- string result = ".";
- try {
- result = Settings.Get ("docPath") ?? ".";
- } catch {}
-
- return result;
- }
-
- public static RootTree LoadTree (string basedir, bool includeExternal = true)
- {
- if (string.IsNullOrEmpty (basedir))
- throw new ArgumentNullException ("basedir");
- if (!Directory.Exists (basedir))
- throw new ArgumentException ("basedir", string.Format ("Base documentation directory at '{0}' doesn't exist", basedir));
-
- XmlDocument xmlDocument = new XmlDocument ();
- string filename = Path.Combine (basedir, "monodoc.xml");
- xmlDocument.Load (filename);
- IEnumerable<string> sourceFiles = Directory.EnumerateFiles (Path.Combine (basedir, "sources"), "*.source");
- if (includeExternal)
- sourceFiles = sourceFiles.Concat (RootTree.ProbeExternalDirectorySources ());
- return RootTree.LoadTree (basedir, xmlDocument, sourceFiles);
- }
-
- static IEnumerable<string> ProbeExternalDirectorySources ()
- {
- IEnumerable<string> enumerable = Enumerable.Empty<string> ();
- try {
- string path = Settings.Get ("docExternalPath");
- enumerable = enumerable.Concat (System.IO.Directory.EnumerateFiles (path, "*.source"));
- }
- catch {}
-
- if (Directory.Exists ("/Library/Frameworks/Mono.framework/External/monodoc"))
- enumerable = enumerable.Concat (Directory.EnumerateFiles ("/Library/Frameworks/Mono.framework/External/monodoc", "*.source"));
- return enumerable;
- }
-
- public static RootTree LoadTree (string indexDir, XmlDocument docTree, IEnumerable<string> sourceFiles)
- {
- if (docTree == null) {
- docTree = new XmlDocument ();
- using (Stream manifestResourceStream = typeof (RootTree).Assembly.GetManifestResourceStream ("monodoc.xml")) {
- docTree.Load (manifestResourceStream);
- }
- }
-
- sourceFiles = (sourceFiles ?? new string[0]);
- RootTree rootTree = new RootTree ();
- rootTree.basedir = indexDir;
- XmlNodeList xml_node_list = docTree.SelectNodes ("/node/node");
- rootTree.nameToNode["root"] = rootTree.RootNode;
- rootTree.nameToNode["libraries"] = rootTree.RootNode;
- rootTree.Populate (rootTree.RootNode, xml_node_list);
-
- if (rootTree.LookupEntryPoint ("various") == null) {
- Console.Error.WriteLine ("No 'various' doc node! Check monodoc.xml!");
- Node rootNode = rootTree.RootNode;
- }
-
- foreach (string current in sourceFiles)
- rootTree.AddSourceFile (current);
-
- foreach (string path in uncompiledHelpSourcePaths) {
- var hs = new Providers.EcmaUncompiledHelpSource (path);
- hs.RootTree = rootTree;
- rootTree.helpSources.Add (hs);
- string epath = "extra-help-source-" + hs.Name;
- Node hsn = rootTree.RootNode.CreateNode (hs.Name, "root:/" + epath);
- rootTree.nameToHelpSource [epath] = hs;
- hsn.EnsureNodes ();
- foreach (Node n in hs.Tree.RootNode.Nodes)
- hsn.AddNode (n);
- }
-
- RootTree.PurgeNode (rootTree.RootNode);
- rootTree.RootNode.Sort ();
- return rootTree;
- }
-
- public void AddSource (string sourcesDir)
- {
- IEnumerable<string> enumerable = Directory.EnumerateFiles (sourcesDir, "*.source");
- foreach (string current in enumerable)
- if (!this.AddSourceFile (current))
- Console.Error.WriteLine ("Error: Could not load source file {0}", current);
- }
-
- public bool AddSourceFile (string sourceFile)
- {
- if (this.loadedSourceFiles.Contains (sourceFile))
- return false;
-
- Node node = this.LookupEntryPoint ("various") ?? base.RootNode;
- XmlDocument xmlDocument = new XmlDocument ();
- try {
- xmlDocument.Load (sourceFile);
- } catch {
- bool result = false;
- return result;
- }
-
- XmlNodeList extra_nodes = xmlDocument.SelectNodes ("/monodoc/node");
- if (extra_nodes.Count > 0)
- this.Populate (node, extra_nodes);
-
- XmlNodeList sources = xmlDocument.SelectNodes ("/monodoc/source");
- if (sources == null) {
- Console.Error.WriteLine ("Error: No <source> section found in the {0} file", sourceFile);
- return false;
- }
-
- loadedSourceFiles.Add (sourceFile);
- foreach (XmlNode xmlNode in sources) {
- XmlAttribute a = xmlNode.Attributes["provider"];
- if (a == null) {
- Console.Error.WriteLine ("Error: no provider in <source>");
- continue;
- }
- string provider = a.InnerText;
- a = xmlNode.Attributes["basefile"];
- if (a == null) {
- Console.Error.WriteLine ("Error: no basefile in <source>");
- continue;
- }
- string basefile = a.InnerText;
- a = xmlNode.Attributes["path"];
- if (a == null) {
- Console.Error.WriteLine ("Error: no path in <source>");
- continue;
- }
- string path = a.InnerText;
- string basefilepath = Path.Combine (Path.GetDirectoryName (sourceFile), basefile);
- HelpSource helpSource = RootTree.GetHelpSource (provider, basefilepath);
- if (helpSource != null) {
- helpSource.RootTree = this;
- this.helpSources.Add (helpSource);
- this.nameToHelpSource[path] = helpSource;
- Node node2 = this.LookupEntryPoint (path);
- if (node2 == null) {
- Console.Error.WriteLine ("node `{0}' is not defined on the documentation map", path);
- node2 = node;
- }
- foreach (Node current in helpSource.Tree.RootNode.Nodes) {
- node2.AddNode (current);
- }
- node2.Sort ();
- }
- }
- return true;
- }
-
- static bool PurgeNode (Node node)
- {
- bool result = false;
- if (!node.Documented)
- {
- List<Node> list = new List<Node> ();
- foreach (Node current in node.Nodes)
- {
- bool flag = RootTree.PurgeNode (current);
- if (flag)
- {
- list.Add (current);
- }
- }
- result = (node.Nodes.Count == list.Count);
- foreach (Node current2 in list)
- {
- node.DeleteNode (current2);
- }
- }
- return result;
- }
-
- public static string[] GetSupportedFormats ()
- {
- return new string[]
- {
- "ecma",
- "ecmaspec",
- "error",
- "man",
- "xhtml"
- };
- }
-
- public static HelpSource GetHelpSource (string provider, string basefilepath)
- {
- HelpSource result;
- try {
- switch (provider) {
- case "xhtml":
- case "hb":
- result = new XhtmlHelpSource (basefilepath, false);
- break;
- case "man":
- result = new ManHelpSource (basefilepath, false);
- break;
- case "error":
- result = new ErrorHelpSource (basefilepath, false);
- break;
- case "ecmaspec":
- result = new EcmaSpecHelpSource (basefilepath, false);
- break;
- case "ecma":
- result = new EcmaHelpSource (basefilepath, false);
- break;
- default:
- Console.Error.WriteLine ("Error: Unknown provider specified: {0}", provider);
- result = null;
- break;
- }
- } catch (FileNotFoundException) {
- Console.Error.WriteLine ("Error: did not find one of the files in sources/" + basefilepath);
- result = null;
- }
- return result;
- }
-
- public static Provider GetProvider (string provider, params string[] basefilepaths)
- {
- switch (provider) {
- case "ecma":
- return new EcmaProvider (basefilepaths[0]);
- case "ecmaspec":
- return new EcmaSpecProvider (basefilepaths[0]);
- case "error":
- return new ErrorProvider (basefilepaths[0]);
- case "man":
- return new ManProvider (basefilepaths);
- case "xhml":
- case "hb":
- return new XhtmlProvider (basefilepaths[0]);
- }
-
- throw new NotSupportedException (provider);
- }
-
- void Populate (Node parent, XmlNodeList xml_node_list)
- {
- foreach (XmlNode xmlNode in xml_node_list) {
- XmlAttribute e = xmlNode.Attributes["parent"];
- Node parent2 = null;
- if (e != null && this.nameToNode.TryGetValue (e.InnerText, out parent2)) {
- xmlNode.Attributes.Remove (e);
- Populate (parent2, xmlNode.SelectNodes ("."));
- continue;
- }
- e = xmlNode.Attributes["label"];
- if (e == null) {
- Console.Error.WriteLine ("`label' attribute missing in <node>");
- continue;
- }
- string label = e.InnerText;
- e = xmlNode.Attributes["name"];
- if (e == null) {
- Console.Error.WriteLine ("`name' attribute missing in <node>");
- continue;
- }
- string name = e.InnerText;
- Node orCreateNode = parent.GetOrCreateNode (label, "root:/" + name);
- orCreateNode.EnsureNodes ();
- this.nameToNode[name] = orCreateNode;
- XmlNodeList xmlNodeList = xmlNode.SelectNodes ("./node");
- if (xmlNodeList != null) {
- this.Populate (orCreateNode, xmlNodeList);
- }
- }
- }
-
- public Node LookupEntryPoint (string name)
- {
- Node result = null;
- if (!this.nameToNode.TryGetValue (name, out result)) {
- result = null;
- }
- return result;
- }
-
- public TOutput RenderUrl<TOutput> (string url, IDocGenerator<TOutput> generator, HelpSource hintSource = null)
- {
- Node dummy;
- return RenderUrl<TOutput> (url, generator, out dummy, hintSource);
- }
-
- public TOutput RenderUrl<TOutput> (string url, IDocGenerator<TOutput> generator, out Node node, HelpSource hintSource = null)
- {
- node = null;
- string internalId = null;
- HelpSource hs = GetHelpSourceAndIdForUrl (url, hintSource, out internalId, out node);
- return generator.Generate (hs, internalId);
- }
-
- public HelpSource GetHelpSourceAndIdForUrl (string url, out string internalId)
- {
- Node dummy;
- return GetHelpSourceAndIdForUrl (url, out internalId, out dummy);
- }
-
- public HelpSource GetHelpSourceAndIdForUrl (string url, out string internalId, out Node node)
- {
- return GetHelpSourceAndIdForUrl (url, null, out internalId, out node);
- }
-
- public HelpSource GetHelpSourceAndIdForUrl (string url, HelpSource hintSource, out string internalId, out Node node)
- {
- node = null;
- internalId = null;
-
- if (url.StartsWith ("root:/", StringComparison.OrdinalIgnoreCase))
- return this.GetHelpSourceAndIdFromName (url.Substring ("root:/".Length), out internalId, out node);
-
- HelpSource helpSource = hintSource;
- if (helpSource == null || string.IsNullOrEmpty (internalId = helpSource.GetInternalIdForUrl (url, out node))) {
- helpSource = null;
- foreach (var hs in helpSources.Where (h => h.CanHandleUrl (url))) {
- if (!string.IsNullOrEmpty (internalId = hs.GetInternalIdForUrl (url, out node))) {
- helpSource = hs;
- break;
- }
- }
- }
-
- return helpSource;
- }
-
- public HelpSource GetHelpSourceAndIdFromName (string name, out string internalId, out Node node)
- {
- internalId = "root:";
- node = this.LookupEntryPoint (name);
-
- return node == null ? null : node.Nodes.Select (n => n.Tree.HelpSource).Where (hs => hs != null).Distinct ().FirstOrDefault ();
- }
-
- public HelpSource GetHelpSourceFromId (int id)
- {
- return (id < 0 || id >= this.helpSources.Count) ? null : this.helpSources[id];
- }
-
- public Stream GetImage (string url)
- {
- if (url.StartsWith ("source-id:", StringComparison.OrdinalIgnoreCase)) {
- string text = url.Substring (10);
- int num = text.IndexOf (":");
- string text2 = text.Substring (0, num);
- int id = 0;
- try {
- id = int.Parse (text2);
- } catch {
- Console.Error.WriteLine ("Failed to parse source-id url: {0} `{1}'", url, text2);
- return null;
- }
- HelpSource helpSourceFromId = this.GetHelpSourceFromId (id);
- return helpSourceFromId.GetImage (text.Substring (num + 1));
- }
- Assembly assembly = Assembly.GetAssembly (typeof (RootTree));
- return assembly.GetManifestResourceStream (url);
- }
-
- public IndexReader GetIndex ()
- {
- try {
- string text = Path.Combine (this.basedir, "monodoc.index");
- if (File.Exists (text))
- return IndexReader.Load (text);
-
- text = Path.Combine (Settings.Get ("monodocIndexDirectory"), "monodoc.index");
- return IndexReader.Load (text);
- } catch {
- return null;
- }
- }
-
- public static void MakeIndex ()
- {
- RootTree rootTree = RootTree.LoadTree ();
- rootTree.GenerateIndex ();
- }
-
- public void GenerateIndex ()
- {
- IndexMaker indexMaker = new IndexMaker ();
- foreach (HelpSource current in this.helpSources)
- current.PopulateIndex (indexMaker);
- string text = Path.Combine (this.basedir, "monodoc.index");
- try {
- indexMaker.Save (text);
- } catch (UnauthorizedAccessException) {
- text = Path.Combine (Settings.Get ("docPath"), "monodoc.index");
- try {
- indexMaker.Save (text);
- } catch (UnauthorizedAccessException) {
- Console.WriteLine ("Unable to write index file in {0}", Path.Combine (Settings.Get ("docPath"), "monodoc.index"));
- return;
- }
- }
- if (RootTree.IsUnix)
- RootTree.chmod (text, 420);
-
- Console.WriteLine ("Documentation index at {0} updated", text);
- }
-
- public SearchableIndex GetSearchIndex ()
- {
- try {
- string text = Path.Combine (this.basedir, "search_index");
- if (System.IO.Directory.Exists (text))
- return SearchableIndex.Load (text);
- text = Path.Combine (Settings.Get ("docPath"), "search_index");
- return SearchableIndex.Load (text);
- } catch {
- return null;
- }
- }
-
- public static void MakeSearchIndex ()
- {
- RootTree rootTree = RootTree.LoadTree ();
- rootTree.GenerateSearchIndex ();
- }
-
- public void GenerateSearchIndex ()
- {
- Console.WriteLine ("Loading the monodoc tree...");
- string text = Path.Combine (this.basedir, "search_index");
- IndexWriter indexWriter;
- var analyzer = new StandardAnalyzer (Lucene.Net.Util.Version.LUCENE_CURRENT);
- var directory = Lucene.Net.Store.FSDirectory.Open (text);
-
- try {
- if (!Directory.Exists (text))
- Directory.CreateDirectory (text);
- indexWriter = new IndexWriter (directory, analyzer, true, IndexWriter.MaxFieldLength.LIMITED);
- } catch (UnauthorizedAccessException) {
- try {
- text = Path.Combine (Settings.Get ("docPath"), "search_index");
- if (!Directory.Exists (text))
- Directory.CreateDirectory (text);
- indexWriter = new IndexWriter (directory, analyzer, true, IndexWriter.MaxFieldLength.LIMITED);
- } catch (UnauthorizedAccessException) {
- Console.WriteLine ("You don't have permissions to write on " + text);
- return;
- }
- }
- Console.WriteLine ("Collecting and adding documents...");
- foreach (HelpSource current in this.helpSources) {
- current.PopulateSearchableIndex (indexWriter);
- }
- Console.WriteLine ("Closing...");
- indexWriter.Optimize ();
- indexWriter.Close ();
- }
-
- [DllImport ("libc")]
- static extern int chmod (string filename, int mode);
- }
-}
+++ /dev/null
-//
-//
-// SearchableDocument.cs: Abstracts our model of document from the Lucene Document
-//
-// Author: Mario Sopena
-//
-using Lucene.Net.Documents;
-
-namespace MonkeyDoc
-{
- struct SearchableDocument
- {
- public string Title {
- get; set;
- }
-
- public string Url {
- get; set;
- }
-
- public string FullTitle {
- get; set;
- }
-
- public string HotText {
- get; set;
- }
-
- public string Text {
- get; set;
- }
-
- public string Examples {
- get; set;
- }
-
- public SearchableDocument Reset ()
- {
- Title = Url = FullTitle = HotText = Text = Examples = null;
- return this;
- }
-
- public Document LuceneDoc {
- get {
- Document doc = new Document ();
- doc.Add (UnIndexed ("title", Title));
- doc.Add (UnIndexed ("url", Url));
- doc.Add (UnIndexed ("fulltitle", FullTitle ?? string.Empty));
- doc.Add (UnStored ("hottext", HotText));
- doc.Add (UnStored ("text", Text));
- doc.Add (UnStored ("examples", Examples));
- return doc;
- }
- }
-
- static Field UnIndexed(System.String name, System.String value_Renamed)
- {
- return new Field(name, value_Renamed, Field.Store.YES, Field.Index.NO);
- }
-
- static Field UnStored(System.String name, System.String value_Renamed)
- {
- return new Field(name, value_Renamed, Field.Store.NO, Field.Index.ANALYZED);
- }
- }
-}
+++ /dev/null
-//
-//
-// SearchableIndex.cs: Index that uses Lucene to search through the docs
-//
-// Author: Mario Sopena
-//
-
-using System;
-using System.IO;
-using System.Collections.Generic;
-// Lucene imports
-using Lucene.Net.Index;
-using Lucene.Net.Documents;
-using Lucene.Net.Analysis;
-using Lucene.Net.Analysis.Standard;
-using Lucene.Net.Search;
-using Lucene.Net.QueryParsers;
-using Lucene.Net.Store;
-
-namespace MonkeyDoc
-{
- public class SearchableIndex
- {
- const int maxSearchCount = 30;
-
- IndexSearcher searcher;
- string dir;
-
- public string Dir {
- get {
- if (dir == null)
- dir = "search_index";
- return dir;
- }
- set { dir = value; }
- }
-
- public static SearchableIndex Load (string dir)
- {
- SearchableIndex s = new SearchableIndex ();
- s.dir = dir;
- try {
- //s.searcher = new IndexSearcher (dir);
- // TODO: parametrize that depending if we run on the desktop (low footprint) or the server (use RAMDirectory for instance)
- s.searcher = new IndexSearcher (FSDirectory.Open (dir));
- } catch (IOException) {
- Console.WriteLine ("Index nonexistent or in bad format");
- return null;
- }
- return s;
- }
-
- public Result Search (string term)
- {
- return Search (term, maxSearchCount);
- }
-
- public Result Search (string term, int count)
- {
- return Search (term, count, 0);
- }
-
- public Result Search (string term, int count, int start) {
- try {
- term = term.ToLower ();
- Term htTerm = new Term ("hottext", term);
- Query qq1 = new FuzzyQuery (htTerm);
- Query qq2 = new TermQuery (htTerm);
- qq2.Boost = 10f;
- Query qq3 = new PrefixQuery (htTerm);
- qq3.Boost = 10f;
- DisjunctionMaxQuery q1 = new DisjunctionMaxQuery (0f);
- q1.Add (qq1);
- q1.Add (qq2);
- q1.Add (qq3);
- Query q2 = new TermQuery (new Term ("text", term));
- q2.Boost = 3f;
- Query q3 = new TermQuery (new Term ("examples", term));
- q3.Boost = 3f;
- DisjunctionMaxQuery q = new DisjunctionMaxQuery (0f);
-
- q.Add (q1);
- q.Add (q2);
- q.Add (q3);
-
- TopDocs top = SearchInternal (q, count, start);
- Result r = new Result (term, searcher, top.ScoreDocs);
- return r;
- } catch (IOException) {
- Console.WriteLine ("No index in {0}", dir);
- return null;
- }
- }
-
- TopDocs SearchInternal (Query q, int count, int start)
- {
- // Easy path that doesn't involve creating a Collector ourselves
- // watch for Lucene.NET improvement on that (like searcher.SearchAfter)
- if (start == 0)
- return searcher.Search (q, count);
-
- var weight = searcher.CreateWeight (q); // TODO: reuse weight instead of query
- var collector = TopScoreDocCollector.Create (start + count + 1, false);
- searcher.Search (q, collector);
-
- return collector.TopDocs (start, count);
- }
-
- public Result FastSearch (string term, int number)
- {
- try {
- term = term.ToLower ();
- Query q1 = new TermQuery (new Term ("hottext", term));
- Query q2 = new PrefixQuery (new Term ("hottext", term));
- q2.Boost = 0.5f;
- DisjunctionMaxQuery q = new DisjunctionMaxQuery (0f);
- q.Add (q1);
- q.Add (q2);
- TopDocs top = searcher.Search (q, number);
- return new Result (term, searcher, top.ScoreDocs);
- } catch (IOException) {
- Console.WriteLine ("No index in {0}", dir);
- return null;
- }
- }
- }
-
- //
- // An object representing the search term with the results
- //
- public class Result {
- string term;
- Searcher searcher;
- ScoreDoc[] docs;
-
- public string Term {
- get { return term;}
- }
-
- public int Count {
- get { return docs.Length; }
- }
-
- public Document this [int i] {
- get { return searcher.Doc (docs[i].Doc); }
- }
-
- public string GetTitle (int i)
- {
- Document d = this[i];
- return d == null ? string.Empty : d.Get ("title");
- }
-
- public string GetUrl (int i)
- {
- Document d = this[i];
- return d == null ? string.Empty : d.Get ("url");
- }
-
- public string GetFullTitle (int i)
- {
- Document d = this[i];
- return d == null ? string.Empty : d.Get ("fulltitle");
- }
-
- public float Score (int i)
- {
- return docs[i].Score;
- }
-
- public Result (string Term, Searcher searcher, ScoreDoc[] docs)
- {
- this.term = Term;
- this.searcher = searcher;
- this.docs = docs;
- }
- }
-}
-
+++ /dev/null
-using System;
-using System.IO;
-using System.Text;
-using System.Linq;
-using System.Xml;
-using System.Collections.Generic;
-
-namespace MonkeyDoc
-{
- /// <summary>
- /// This tree is populated by the documentation providers, or populated
- /// from a binary encoding of the tree. The format of the tree is designed
- /// to minimize the need to load it in full.
- /// </summary>
-
- /* Ideally this class should also be abstracted to let user have something
- * else than a file as a backing store, a database for instance
- */
- public class Tree
- {
- public readonly HelpSource HelpSource;
-
- FileStream InputStream;
- BinaryReader InputReader;
-
- // This is the node which contains all the other node of the tree
- Node rootNode;
-
- /// <summary>
- /// Load from file constructor
- /// </summary>
- public Tree (HelpSource hs, string filename)
- {
- Encoding utf8 = new UTF8Encoding (false, true);
-
- if (!File.Exists (filename)){
- throw new FileNotFoundException ();
- }
-
- InputStream = File.OpenRead (filename);
- InputReader = new BinaryReader (InputStream, utf8);
- byte [] sig = InputReader.ReadBytes (4);
-
- if (!GoodSig (sig))
- throw new Exception ("Invalid file format");
-
- InputStream.Position = 4;
- var position = InputReader.ReadInt32 ();
- rootNode = new Node (this, position);
- InflateNode (rootNode);
-
- HelpSource = hs;
- }
-
- /// <summary>
- /// Tree creation and merged tree constructor
- /// </summary>
- public Tree (HelpSource hs, string caption, string url) : this (hs, null, caption, url)
- {
- }
-
- public Tree (HelpSource hs, Node parent, string caption, string element)
- {
- HelpSource = hs;
- rootNode = parent == null ? new Node (this, caption, element) : new Node (parent, caption, element);
- }
-
- /// <summary>
- /// Saves the tree into the specified file using the help file format.
- /// </summary>
- public void Save (string file)
- {
- Encoding utf8 = new UTF8Encoding (false, true);
- using (FileStream output = File.OpenWrite (file)){
- // Skip over the pointer to the first node.
- output.Position = 8;
-
- using (BinaryWriter writer = new BinaryWriter (output, utf8)) {
- // Recursively dump
- rootNode.Serialize (output, writer);
-
- output.Position = 0;
- writer.Write (new byte [] { (byte) 'M', (byte) 'o', (byte) 'H', (byte) 'P' });
- writer.Write (rootNode.Address);
- }
- }
- }
-
- public Node RootNode {
- get {
- return rootNode;
- }
- }
-
- static bool GoodSig (byte [] sig)
- {
- if (sig.Length != 4)
- return false;
- return sig [0] == (byte) 'M'
- && sig [1] == (byte) 'o'
- && sig [2] == (byte) 'H'
- && sig [3] == (byte) 'P';
- }
-
- public void InflateNode (Node baseNode)
- {
- var address = baseNode.Address;
- if (address < 0)
- address = -address;
-
- InputStream.Position = address;
- baseNode.Deserialize (InputReader);
- }
- }
-
- public static class TreeDumper
- {
- static int indent;
-
- static void Indent ()
- {
- for (int i = 0; i < indent; i++)
- Console.Write (" ");
- }
-
- public static void PrintTree (Node node)
- {
- Indent ();
- Console.WriteLine ("{0},{1}\t[PublicUrl: {2}]", node.Element, node.Caption, node.PublicUrl);
- if (node.Nodes.Count == 0)
- return;
-
- indent++;
- foreach (Node n in node.Nodes)
- PrintTree (n);
- indent--;
- }
-
- public static string ExportToTocXml (Node root, string title, string desc)
- {
- if (root == null)
- throw new ArgumentNullException ("root");
- // Return a toc index of sub-nodes
- StringBuilder buf = new StringBuilder ();
- var writer = XmlWriter.Create (buf);
- writer.WriteStartElement ("toc");
- writer.WriteAttributeString ("title", title ?? string.Empty);
- writer.WriteElementString ("description", desc ?? string.Empty);
- writer.WriteStartElement ("list");
- foreach (Node n in root.Nodes) {
- writer.WriteStartElement ("item");
- writer.WriteAttributeString ("url", n.Element);
- writer.WriteValue (n.Caption);
- writer.WriteEndElement ();
- }
- writer.WriteEndElement ();
- writer.WriteEndElement ();
- writer.Flush ();
- writer.Close ();
-
- return buf.ToString ();
- }
- }
-}
+++ /dev/null
-using System;
-
-namespace MonkeyDoc
-{
- public static class TypeUtils
- {
- public static bool GetNamespaceAndType (string url, out string ns, out string type)
- {
- int nsidx = -1;
- int numLt = 0;
- for (int i = 0; i < url.Length; ++i) {
- char c = url [i];
- switch (c) {
- case '<':
- case '{':
- ++numLt;
- break;
- case '>':
- case '}':
- --numLt;
- break;
- case '.':
- if (numLt == 0)
- nsidx = i;
- break;
- }
- }
-
- if (nsidx == -1) {
- ns = null;
- type = null;
- return false;
- }
- ns = url.Substring (0, nsidx);
- type = url.Substring (nsidx + 1);
-
- return true;
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using System.Linq;
-using System.IO;
-using System.Configuration;
-using System.Collections.Specialized;
-using MonkeyDoc.Caches;
-
-namespace MonkeyDoc
-{
- public enum DocEntity
- {
- Text,
- Blob
- }
-
- public interface IDocCache : IDisposable
- {
- bool IsCached (string id);
- bool CanCache (DocEntity entity);
-
- Stream GetCachedStream (string id);
- string GetCachedString (string id);
-
- void CacheText (string id, string content);
- void CacheText (string id, Stream stream);
-
- void CacheBlob (string id, byte[] data);
- void CacheBlob (string id, Stream stream);
- }
-
- public static class DocCacheHelper
- {
- static string cacheBaseDirectory;
-
- static DocCacheHelper ()
- {
- try {
- var cacheValues = Settings.Get ("cache").Split (',');
- if (cacheValues.Length == 2 && cacheValues[0].Equals ("file", StringComparison.Ordinal))
- cacheBaseDirectory = cacheValues[1].Replace ("~", Environment.GetFolderPath (Environment.SpecialFolder.Personal));
- } catch {}
- }
-
- // Use configuration option to query for cache directory, if it doesn't exist we instantiate a nullcache
- public static IDocCache GetDefaultCache (string name)
- {
- if (cacheBaseDirectory == null)
- return new NullCache ();
-
- return new FileCache (Path.Combine (cacheBaseDirectory, name));
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using System.IO;
-
-namespace MonkeyDoc.Caches
-{
- public class FileCache : IDocCache
- {
- string baseCacheDir;
-
- public FileCache (string baseCacheDir)
- {
- this.baseCacheDir = baseCacheDir;
- if (!Directory.Exists (baseCacheDir))
- Directory.CreateDirectory (baseCacheDir);
- }
-
- public bool IsCached (string id)
- {
- return File.Exists (MakePath (id));
- }
-
- public bool CanCache (DocEntity entity)
- {
- return true;
- }
-
- public Stream GetCachedStream (string id)
- {
- return File.OpenRead (MakePath (id));
- }
-
- public string GetCachedString (string id)
- {
- return File.ReadAllText (MakePath (id));
- }
-
- public void CacheText (string id, string content)
- {
- File.WriteAllText (MakePath (id), content);
- }
-
- public void CacheText (string id, Stream stream)
- {
- using (var file = File.OpenWrite (MakePath (id)))
- stream.CopyTo (file);
- }
-
- public void CacheBlob (string id, byte[] data)
- {
- File.WriteAllBytes (MakePath (id), data);
- }
-
- public void CacheBlob (string id, Stream stream)
- {
- using (var file = File.OpenWrite (MakePath (id)))
- stream.CopyTo (file);
- }
-
- string MakePath (string id)
- {
- id = id.Replace (Path.DirectorySeparatorChar, '_');
- return Path.Combine (baseCacheDir, id);
- }
-
- public void Dispose ()
- {
- if (!Directory.Exists (baseCacheDir))
- return;
-
- try {
- Directory.Delete (baseCacheDir, true);
- } catch {}
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using System.IO;
-
-namespace MonkeyDoc.Caches
-{
- // This is basically a no-cache implementation
- public class NullCache : IDocCache
- {
- public bool IsCached (string id)
- {
- return false;
- }
-
- public bool CanCache (DocEntity entity)
- {
- return false;
- }
-
- public Stream GetCachedStream (string id)
- {
- return null;
- }
-
- public string GetCachedString (string id)
- {
- return null;
- }
-
- public void CacheText (string id, string content)
- {
-
- }
-
- public void CacheText (string id, Stream stream)
- {
-
- }
-
- public void CacheBlob (string id, byte[] data)
- {
-
- }
-
- public void CacheBlob (string id, Stream stream)
- {
-
- }
-
- public void Dispose ()
- {
-
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-
-namespace MonkeyDoc
-{
- // All type of documents that a generator may find as input
- public enum DocumentType {
- EcmaXml, // Our main monodoc format
- EcmaSpecXml,
- Man,
- AddinXml,
- MonoBook, // This is mostly XHTML already, just need a tiny bit of processing
- Html,
- TocXml, // Used by help source displaying some kind of toc of the content they host
- PlainText,
- ErrorXml
- }
-
- /* This interface defines a set of transformation engine
- * that convert multiple documentation source to a single output format
- */
- public interface IDocGenerator<TOutput>
- {
- // This method is responsible for finding out the documentation type
- // for the given ID and use the right engine internally
- TOutput Generate (HelpSource hs, string internalId);
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using System.IO;
-using System.Text;
-using System.Linq;
-using System.Collections.Generic;
-
-using MonkeyDoc;
-
-namespace MonkeyDoc.Generators
-{
- using Html;
-
- interface IHtmlExporter
- {
- string CssCode { get; }
- string Export (Stream input, Dictionary<string, string> extras);
- string Export (string input, Dictionary<string, string> extras);
- }
-
- public class HtmlGenerator : IDocGenerator<string>
- {
- const string cachePrefix = "htmlcached#";
-
- static string css_code;
-
- IDocCache defaultCache;
- static Dictionary<DocumentType, IHtmlExporter> converters;
-
- static HtmlGenerator ()
- {
- converters = new Dictionary<DocumentType, IHtmlExporter> {
- { DocumentType.EcmaXml, new Ecma2Html () },
- { DocumentType.Man, new Man2Html () },
- { DocumentType.TocXml, new Toc2Html () },
- { DocumentType.EcmaSpecXml, new Ecmaspec2Html () },
- { DocumentType.ErrorXml, new Error2Html () },
- { DocumentType.Html, new Idem () },
- { DocumentType.MonoBook, new MonoBook2Html () },
- { DocumentType.AddinXml, new Addin2Html () },
- { DocumentType.PlainText, new Idem () },
- };
- }
-
- public HtmlGenerator (IDocCache defaultCache)
- {
- this.defaultCache = defaultCache;
- }
-
- public string Generate (HelpSource hs, string id)
- {
- if (hs == null || string.IsNullOrEmpty (id))
- return MakeHtmlError (string.Format ("Your request has found no candidate provider [hs=\"{0}\", id=\"{1}\"]",
- hs == null ? "(null)" : hs.Name, id ?? "(null)"));
- var cache = defaultCache ?? hs.Cache;
- if (cache != null && cache.IsCached (MakeCacheKey (hs, id, null)))
- return cache.GetCachedString (MakeCacheKey (hs, id, null));
-
- IEnumerable<string> parts;
- if (hs.IsMultiPart (id, out parts))
- return GenerateMultiPart (hs, parts, id);
-
- if (hs.IsRawContent (id))
- return hs.GetText (id) ?? string.Empty;
-
- Dictionary<string, string> extraParams = null;
- DocumentType type = hs.GetDocumentTypeForId (id, out extraParams);
- if (cache != null && extraParams != null && cache.IsCached (MakeCacheKey (hs, id, extraParams)))
- return cache.GetCachedString (MakeCacheKey (hs, id, extraParams));
-
- IHtmlExporter exporter;
- if (!converters.TryGetValue (type, out exporter))
- return MakeHtmlError (string.Format ("Input type '{0}' not supported",
- type.ToString ()));
- var result = hs.IsGeneratedContent (id) ?
- exporter.Export (hs.GetCachedText (id), extraParams) :
- exporter.Export (hs.GetCachedHelpStream (id), extraParams);
-
- if (cache != null)
- cache.CacheText (MakeCacheKey (hs, id, extraParams), result);
- return result;
- }
-
- string GenerateMultiPart (HelpSource hs, IEnumerable<string> ids, string originalId)
- {
- var sb = new StringBuilder ();
- foreach (var id in ids)
- sb.AppendLine (Generate (hs, id));
-
- var cache = defaultCache ?? hs.Cache;
- if (cache != null)
- cache.CacheText (MakeCacheKey (hs, originalId, null), sb.ToString ());
- return sb.ToString ();
- }
-
- public static string InlineCss {
- get {
- if (css_code != null)
- return css_code;
-
- System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly (typeof (HtmlGenerator));
- Stream str_css = assembly.GetManifestResourceStream ("base.css");
- StringBuilder sb = new StringBuilder ((new StreamReader (str_css)).ReadToEnd());
- sb.Replace ("@@FONT_FAMILY@@", "Sans Serif");
- sb.Replace ("@@FONT_SIZE@@", "100%");
- css_code = sb.ToString () + converters.Values
- .Select (c => c.CssCode)
- .Where (css => !string.IsNullOrEmpty (css))
- .DefaultIfEmpty (string.Empty)
- .Aggregate (string.Concat);
- return css_code;
- }
- set {
- css_code = value;
- }
- }
-
- string MakeHtmlError (string error)
- {
- return string.Format ("<html><head></head><body><p>{0}</p></body></html>", error);
- }
-
- string MakeCacheKey (HelpSource hs, string page, IDictionary<string,string> extraParams)
- {
- var key = cachePrefix + hs.SourceID + page;
- if (extraParams != null && extraParams.Count > 0) {
- var paramPart = string.Join ("-", extraParams.Select (kvp => kvp.Key + kvp.Value));
- key += '_' + paramPart;
- }
- return key;
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using System.IO;
-using System.Text;
-using System.Linq;
-using System.Collections.Generic;
-
-using MonkeyDoc;
-
-namespace MonkeyDoc.Generators
-{
- /// <summary>
- /// This generators returns the raw content of the HelpSource without any transformation
- /// </summary>
- public class RawGenerator : IDocGenerator<string>
- {
- public string Generate (HelpSource hs, string id)
- {
- if (hs == null || string.IsNullOrEmpty (id))
- return null;
-
- IEnumerable<string> parts;
- if (hs.IsMultiPart (id, out parts))
- return GenerateMultiPart (hs, parts, id);
-
- if (hs.IsRawContent (id))
- return hs.GetText (id) ?? string.Empty;
-
- var result = hs.IsGeneratedContent (id) ? hs.GetCachedText (id) : new StreamReader (hs.GetCachedHelpStream (id)).ReadToEnd ();
-
- return result;
- }
-
- string GenerateMultiPart (HelpSource hs, IEnumerable<string> ids, string originalId)
- {
- var sb = new StringBuilder ();
- foreach (var id in ids)
- sb.AppendLine (Generate (hs, id));
- return sb.ToString ();
- }
- }
-}
+++ /dev/null
-using System;
-using System.IO;
-using System.Text;
-using System.Xml;
-using System.Xml.Xsl;
-using System.Xml.XPath;
-using System.Collections.Generic;
-
-namespace MonkeyDoc.Generators.Html
-{
- public class Addin2Html : IHtmlExporter
- {
- public string CssCode {
- get {
- return string.Empty;
- }
- }
-
- public string Export (Stream stream, Dictionary<string, string> extraArgs)
- {
- using (var reader = new StreamReader (stream))
- return Htmlize (GetAddin (reader, extraArgs["AddinID"]),
- extraArgs["show"],
- extraArgs["AddinID"],
- extraArgs["FileID"],
- extraArgs["NodeID"]);
- }
-
- public string Export (string input, Dictionary<string, string> extraArgs)
- {
- return Htmlize (GetAddin (new StringReader (input), extraArgs["AddinID"]),
- extraArgs["show"],
- extraArgs["AddinID"],
- extraArgs["FileID"],
- extraArgs["NodeID"]);
- }
-
- XmlElement GetAddin (TextReader reader, string addinId)
- {
- XmlDocument doc = new XmlDocument ();
- doc.Load (reader);
- XmlElement addin = (XmlElement) doc.SelectSingleNode ("Addins/Addin[@fullId='" + addinId + "']");
- return addin != null ? addin : null;
- }
-
- public string Htmlize (XmlElement addin, string urlType, string addinId, string fileId, string path)
- {
- if (urlType == MonkeyDoc.Providers.AddinsHelpSource.AddinPrefix)
- return GetAddinTextFromUrl (addin, addinId, fileId);
- else if (urlType == MonkeyDoc.Providers.AddinsHelpSource.ExtensionPrefix)
- return GetExtensionTextFromUrl (addin, addinId, fileId, path);
- else if (urlType == MonkeyDoc.Providers.AddinsHelpSource.ExtensionNodePrefix)
- return GetExtensionNodeTextFromUrl (addin, addinId, fileId, path);
-
- return null;
- }
-
- protected string GetAddinTextFromUrl (XmlElement addin, string addinId, string fileId)
- {
- if (addin == null)
- return "<html>Add-in not found: " + addinId + "</html>";
-
- StringBuilder sb = new StringBuilder ("<html>");
- sb.Append ("<h1>").Append (addin.GetAttribute ("name")).Append ("</h1>");
- XmlElement docs = (XmlElement) addin.SelectSingleNode ("Description");
- if (docs != null)
- sb.Append (docs.InnerText);
-
- sb.Append ("<p><table border=\"1\" cellpadding=\"4\" cellspacing=\"0\">");
- sb.AppendFormat ("<tr><td><b>Id</b></td><td>{0}</td></tr>", addin.GetAttribute ("addinId"));
- sb.AppendFormat ("<tr><td><b>Namespace</b></td><td>{0}</td></tr>", addin.GetAttribute ("namespace"));
- sb.AppendFormat ("<tr><td><b>Version</b></td><td>{0}</td></tr>", addin.GetAttribute ("version"));
- sb.Append ("</table></p>");
- sb.Append ("<p><b>Extension Points</b>:</p>");
- sb.Append ("<ul>");
-
- foreach (XmlElement ep in addin.SelectNodes ("ExtensionPoint")) {
- sb.AppendFormat ("<li><a href=\"extension-point:{0}#{1}#{2}\">{3}</li>", fileId, addinId, ep.GetAttribute ("path"), ep.GetAttribute ("name"));
- }
- sb.Append ("</ul>");
-
- sb.Append ("</html>");
- return sb.ToString ();
- }
-
- protected string GetExtensionTextFromUrl (XmlElement addin, string addinId, string fileId, string path)
- {
- if (addin == null)
- return "<html>Add-in not found: " + addinId + "</html>";
-
- XmlElement ext = (XmlElement) addin.SelectSingleNode ("ExtensionPoint[@path='" + path + "']");
- if (ext == null)
- return "<html>Extension point not found: " + path + "</html>";
-
- StringBuilder sb = new StringBuilder ("<html>");
- sb.Append ("<h1>").Append (ext.GetAttribute ("name")).Append ("</h1>");
-
- path = path.Replace ("/", " <b>/</b> ");
- sb.Append ("<p><b>Path</b>: ").Append (path).Append ("</p>");
- XmlElement desc = (XmlElement) ext.SelectSingleNode ("Description");
- if (desc != null)
- sb.Append (desc.InnerText);
-
- sb.Append ("<p><b>Extension Nodes</b>:</p>");
- sb.Append ("<table border=\"1\" cellpadding=\"4\" cellspacing=\"0\">");
-
- foreach (XmlElement en in ext.SelectNodes ("ExtensionNode")) {
- string nid = en.GetAttribute ("id");
- string nname = en.GetAttribute ("name");
- string sdesc = "";
- desc = (XmlElement) en.SelectSingleNode ("Description");
- if (desc != null)
- sdesc = desc.InnerText;
-
- sb.AppendFormat ("<tr><td><a href=\"extension-node:{0}#{1}#{2}\">{3}</td><td>{4}</td></tr>", fileId, addinId, nid, nname, sdesc);
- }
- sb.Append ("</table>");
-
- sb.Append ("</html>");
- return sb.ToString ();
- }
-
- protected string GetExtensionNodeTextFromUrl (XmlElement addin, string addinId, string fileId, string nodeId)
- {
- if (addin == null)
- return "<html>Add-in not found: " + addinId + "</html>";
-
- XmlElement node = (XmlElement) addin.SelectSingleNode ("ExtensionNodeType[@id='" + nodeId + "']");
- if (node == null)
- return "<html>Extension point not found: " + nodeId + "</html>";
-
- StringBuilder sb = new StringBuilder ("<html>");
- sb.Append ("<h1>").Append (node.GetAttribute ("name")).Append ("</h1>");
- XmlElement desc = (XmlElement) node.SelectSingleNode ("Description");
- if (desc != null)
- sb.Append (desc.InnerText);
-
- sb.Append ("<p><b>Attributes</b>:</p>");
- sb.Append ("<table border=\"1\" cellpadding=\"4\" cellspacing=\"0\"><tr>");
- sb.Append ("<td><b>Name</b></td>");
- sb.Append ("<td><b>Type</b></td>");
- sb.Append ("<td><b>Required</b></td>");
- sb.Append ("<td><b>Localizable</b></td>");
- sb.Append ("<td><b>Description</b></td>");
- sb.Append ("<tr>");
- sb.Append ("<td>id</td>");
- sb.Append ("<td>System.String</td>");
- sb.Append ("<td></td>");
- sb.Append ("<td></td>");
- sb.Append ("<td>Identifier of the node.</td>");
- sb.Append ("</tr>");
-
- foreach (XmlElement at in node.SelectNodes ("Attributes/Attribute")) {
- sb.Append ("<tr>");
- sb.AppendFormat ("<td>{0}</td>", at.GetAttribute ("name"));
- sb.AppendFormat ("<td>{0}</td>", at.GetAttribute ("type"));
- if (at.GetAttribute ("required") == "True")
- sb.Append ("<td>Yes</td>");
- else
- sb.Append ("<td></td>");
- if (at.GetAttribute ("localizable") == "True")
- sb.Append ("<td>Yes</td>");
- else
- sb.Append ("<td></td>");
- string sdesc = "";
- desc = (XmlElement) at.SelectSingleNode ("Description");
- if (desc != null)
- sdesc = desc.InnerText;
-
- sb.AppendFormat ("<td>{0}</td>", sdesc);
- sb.Append ("</tr>");
- }
- sb.Append ("</table>");
-
- XmlNodeList children = node.SelectNodes ("ChildNodes/ExtensionNode");
- if (children.Count > 0) {
- sb.Append ("<p><b>Child Nodes</b>:</p>");
- sb.Append ("<table border=\"1\" cellpadding=\"4\" cellspacing=\"0\">");
-
- foreach (XmlElement en in children) {
- string nid = en.GetAttribute ("id");
- string nname = en.GetAttribute ("name");
- string sdesc = "";
- desc = (XmlElement) en.SelectSingleNode ("Description");
- if (desc != null)
- sdesc = desc.InnerText;
-
- sb.AppendFormat ("<tr><td><a href=\"extension-node:{0}#{1}#{2}\">{3}</td><td>{4}</td></tr>", fileId, addinId, nid, nname, sdesc);
- }
- sb.Append ("</table>");
- }
-
- sb.Append ("</html>");
- return sb.ToString ();
- }
- }
-}
+++ /dev/null
-using System;
-using System.IO;
-using System.Text;
-using System.Linq;
-using System.Xml;
-using System.Xml.Xsl;
-using System.Xml.XPath;
-using System.Collections.Generic;
-
-using Mono.Documentation;
-using BF = System.Reflection.BindingFlags;
-
-namespace MonkeyDoc.Generators.Html
-{
- public class Ecma2Html : IHtmlExporter
- {
- static string css_ecma;
- static string js;
- static XslCompiledTransform ecma_transform;
- readonly ExtensionObject ExtObject = new ExtensionObject ();
-
- public Ecma2Html ()
- {
- }
-
- public string CssCode {
- get {
- if (css_ecma != null)
- return css_ecma;
- var assembly = typeof(Ecma2Html).Assembly;
- Stream str_css = assembly.GetManifestResourceStream ("mono-ecma.css");
- css_ecma = (new StreamReader (str_css)).ReadToEnd();
- return css_ecma;
- }
- }
-
- public string JsCode {
- get {
- if (js != null)
- return js;
- var assembly = typeof(Ecma2Html).Assembly;
- Stream str_js = assembly.GetManifestResourceStream ("helper.js");
- js = (new StreamReader (str_js)).ReadToEnd();
- return js;
- }
- }
-
- public string Htmlize (XmlReader ecma_xml, Dictionary<string, string> extraArgs)
- {
- var args = new XsltArgumentList ();
- args.AddExtensionObject("monodoc:///extensions", ExtObject);
- foreach (var kvp in extraArgs)
- args.AddParam (kvp.Key, string.Empty, kvp.Value);
-
- return Htmlize(ecma_xml, args);
- }
-
- public string Htmlize (XmlReader ecma_xml, XsltArgumentList args)
- {
- EnsureTransform ();
-
- var output = new StringBuilder ();
- ecma_transform.Transform (ecma_xml,
- args,
- XmlWriter.Create (output, ecma_transform.OutputSettings),
- CreateDocumentResolver ());
- return output.ToString ();
- }
-
- protected virtual XmlResolver CreateDocumentResolver ()
- {
- // results in using XmlUrlResolver
- return null;
- }
-
- public string Export (Stream stream, Dictionary<string, string> extraArgs)
- {
- return Htmlize (XmlReader.Create (stream), extraArgs);
- }
-
- public string Export (string input, Dictionary<string, string> extraArgs)
- {
- return Htmlize (XmlReader.Create (new StringReader (input)), extraArgs);
- }
-
- static void EnsureTransform ()
- {
- if (ecma_transform == null) {
- ecma_transform = new XslCompiledTransform ();
- var assembly = System.Reflection.Assembly.GetCallingAssembly ();
-
- Stream stream = assembly.GetManifestResourceStream ("mono-ecma-css.xsl");
- XmlReader xml_reader = new XmlTextReader (stream);
- XmlResolver r = new ManifestResourceResolver (".");
- ecma_transform.Load (xml_reader, XsltSettings.TrustedXslt, r);
- }
- }
-
- public class ExtensionObject
- {
- bool quiet = true;
-
- public string Colorize(string code, string lang)
- {
- return Mono.Utilities.Colorizer.Colorize(code,lang);
- }
-
- // Used by stylesheet to nicely reformat the <see cref=> tags.
- public string MakeNiceSignature(string sig, string contexttype)
- {
- if (sig.Length < 3)
- return sig;
- if (sig[1] != ':')
- return sig;
-
- char s = sig[0];
- sig = sig.Substring(2);
-
- switch (s) {
- case 'N': return sig;
- case 'T': return ShortTypeName (sig, contexttype);
-
- case 'C': case 'M': case 'P': case 'F': case 'E':
- string type, mem, arg;
-
- // Get arguments
- int paren;
- if (s == 'C' || s == 'M')
- paren = sig.IndexOf("(");
- else if (s == 'P')
- paren = sig.IndexOf("[");
- else
- paren = 0;
-
- if (paren > 0 && paren < sig.Length-1) {
- string[] args = sig.Substring(paren+1, sig.Length-paren-2).Split(',');
- for (int i = 0; i < args.Length; i++)
- args[i] = ShortTypeName(args[i], contexttype);
- arg = "(" + String.Join(", ", args) + ")";
- sig = sig.Substring(0, paren);
- } else {
- arg = string.Empty;
- }
-
- // Get type and member names
- int dot = sig.LastIndexOf(".");
- if (s == 'C' || dot <= 0 || dot == sig.Length-1) {
- mem = string.Empty;
- type = sig;
- } else {
- type = sig.Substring(0, dot);
- mem = sig.Substring(dot);
- }
-
- type = ShortTypeName(type, contexttype);
-
- return type + mem + arg;
-
- default:
- return sig;
- }
- }
-
- static string ShortTypeName(string name, string contexttype)
- {
- int dot = contexttype.LastIndexOf(".");
- if (dot < 0) return name;
- string contextns = contexttype.Substring(0, dot+1);
-
- if (name == contexttype)
- return name.Substring(dot+1);
-
- if (name.StartsWith(contextns))
- return name.Substring(contextns.Length);
-
- return name.Replace("+", ".");
- }
-
- string MonoImpInfo(string assemblyname, string typename, string membername, string arglist, bool strlong)
- {
- if (quiet)
- return string.Empty;
-
- var a = new List<string> ();
- if (!string.IsNullOrEmpty (arglist)) a.Add (arglist);
- return MonoImpInfo(assemblyname, typename, membername, a, strlong);
- }
-
- string MonoImpInfo(string assemblyname, string typename, string membername, XPathNodeIterator itr, bool strlong)
- {
- if (quiet)
- return string.Empty;
-
- var rgs = itr.Cast<XPathNavigator> ().Select (nav => nav.Value).ToList ();
-
- return MonoImpInfo (assemblyname, typename, membername, rgs, strlong);
- }
-
- string MonoImpInfo(string assemblyname, string typename, string membername, List<string> arglist, bool strlong)
- {
- try {
- System.Reflection.Assembly assembly = null;
-
- try {
- assembly = System.Reflection.Assembly.LoadWithPartialName(assemblyname);
- } catch (Exception) {
- // nothing.
- }
-
- if (assembly == null) {
- /*if (strlong) return "The assembly " + assemblyname + " is not available to MonoDoc.";
- else return string.Empty;*/
- return string.Empty; // silently ignore
- }
-
- Type t = assembly.GetType(typename, false);
- if (t == null) {
- if (strlong)
- return typename + " has not been implemented.";
- else
- return "Not implemented.";
- }
-
- // The following code is flakey and fails to find existing members
- return string.Empty;
- } catch (Exception) {
- return string.Empty;
- }
- }
-
- string MonoImpInfo(System.Reflection.MemberInfo mi, string itemtype, bool strlong)
- {
- if (quiet)
- return string.Empty;
-
- string s = string.Empty;
-
- object[] atts = mi.GetCustomAttributes(true);
- int todoctr = 0;
- foreach (object att in atts) if (att.GetType().Name == "MonoTODOAttribute") todoctr++;
-
- if (todoctr > 0) {
- if (strlong)
- s = "This " + itemtype + " is marked as being unfinished.<BR/>\n";
- else
- s = "Unfinished.";
- }
-
- return s;
- }
-
- public string MonoImpInfo(string assemblyname, string typename, bool strlong)
- {
- if (quiet)
- return string.Empty;
-
- try {
- if (assemblyname == string.Empty)
- return string.Empty;
-
- var assembly = System.Reflection.Assembly.LoadWithPartialName(assemblyname);
- if (assembly == null)
- return string.Empty;
-
- Type t = assembly.GetType(typename, false);
- if (t == null) {
- if (strlong)
- return typename + " has not been implemented.";
- else
- return "Not implemented.";
- }
-
- string s = MonoImpInfo(t, "type", strlong);
-
- if (strlong) {
- var mis = t.GetMembers (BF.Static | BF.Instance | BF.Public | BF.NonPublic);
-
- // Scan members for MonoTODO attributes
- int mctr = 0;
- foreach (var mi in mis) {
- string mii = MonoImpInfo(mi, null, false);
- if (mii != string.Empty) mctr++;
- }
- if (mctr > 0) {
- s += "This type has " + mctr + " members that are marked as unfinished.<BR/>";
- }
- }
-
- return s;
-
- } catch (Exception) {
- return string.Empty;
- }
- }
-
- public bool MonoEditing ()
- {
- return false;
- }
-
- public bool IsToBeAdded(string text)
- {
- return text.StartsWith ("To be added");
- }
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using System.IO;
-using System.Xml;
-using System.Xml.Xsl;
-using System.Xml.XPath;
-using System.Collections.Generic;
-
-namespace MonkeyDoc.Generators.Html
-{
- public class Ecmaspec2Html : IHtmlExporter
- {
- static string css_ecmaspec;
- static XslTransform ecma_transform;
- static XsltArgumentList args = new XsltArgumentList();
-
- public string CssCode {
- get {
- if (css_ecmaspec != null)
- return css_ecmaspec;
- System.Reflection.Assembly assembly = System.Reflection.Assembly.GetCallingAssembly ();
- Stream str_css = assembly.GetManifestResourceStream ("ecmaspec.css");
- css_ecmaspec = (new StreamReader (str_css)).ReadToEnd ();
- return css_ecmaspec;
- }
- }
-
- class ExtObj
- {
- public string Colorize (string code, string lang)
- {
- return Mono.Utilities.Colorizer.Colorize (code, lang);
- }
- }
-
- public string Export (Stream stream, Dictionary<string, string> extraArgs)
- {
- return Htmlize (new XPathDocument (stream));
- }
-
- public string Export (string input, Dictionary<string, string> extraArgs)
- {
- return Htmlize (new XPathDocument (new StringReader (input)));
- }
-
- static string Htmlize (XPathDocument ecma_xml)
- {
- if (ecma_transform == null){
- ecma_transform = new XslTransform ();
- System.Reflection.Assembly assembly = System.Reflection.Assembly.GetCallingAssembly ();
- Stream stream;
- stream = assembly.GetManifestResourceStream ("ecmaspec-html-css.xsl");
-
- XmlReader xml_reader = new XmlTextReader (stream);
- ecma_transform.Load (xml_reader, null, null);
- args.AddExtensionObject ("monodoc:///extensions", new ExtObj ());
- }
-
- if (ecma_xml == null) return "";
-
- StringWriter output = new StringWriter ();
- ecma_transform.Transform (ecma_xml, args, output, null);
-
- return output.ToString ();
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using System.IO;
-using System.Linq;
-using System.Xml;
-using System.Xml.XPath;
-using System.Collections.Generic;
-
-namespace MonkeyDoc.Generators.Html
-{
- public class Error2Html : IHtmlExporter
- {
- public string Export (string input, Dictionary<string, string> extraArgs)
- {
- return Htmlize (new XPathDocument (new StringReader (input)));
- }
-
- public string Export (Stream input, Dictionary<string, string> extraArgs)
- {
- return Htmlize (new XPathDocument (input));
- }
-
- public string CssCode {
- get {
- return @"
- #error_ref {
- background: #debcb0;
- border: 2px solid #782609;
- }
- div.summary {
- font-size: 110%;
- font-weight: bolder;
- }
- div.details {
- font-size: 110%;
- font-weight: bolder;
- }
- div.code_example {
- background: #f5f5dd;
- border: 1px solid black;
- padding-left: 1em;
- padding-bottom: 1em;
- margin-top: 1em;
- white-space: pre;
- margin-bottom: 1em;
- }
- div.code_ex_title {
- position: relative;
- top: -1em;
- left: 30%;
- background: #cdcd82;
- border: 1px solid black;
- color: black;
- font-size: 65%;
- text-transform: uppercase;
- width: 40%;
- padding: 0.3em;
- text-align: center;
- }";
- }
- }
-
- public string Htmlize (IXPathNavigable doc)
- {
- var navigator = doc.CreateNavigator ();
- var errorName = navigator.SelectSingleNode ("//ErrorDocumentation/ErrorName");
- var details = navigator.SelectSingleNode ("//ErrorDocumentation/Details");
-
- StringWriter sw = new StringWriter ();
- XmlWriter w = new XmlTextWriter (sw);
-
- WriteElementWithClass (w, "div", "header");
- w.WriteAttributeString ("id", "error_ref");
- WriteElementWithClass (w, "div", "subtitle", "Compiler Error Reference");
- WriteElementWithClass (w, "div", "title", "Error " + (errorName == null ? string.Empty : errorName.Value));
- w.WriteEndElement ();
-
- if (details != null) {
- WriteElementWithClass (w, "div", "summary", "Summary");
-
- var summary = details.SelectSingleNode ("/Summary");
- w.WriteValue (summary == null ? string.Empty : summary.Value);
-
- WriteElementWithClass (w, "div", "details", "Details");
- var de = details.SelectSingleNode ("/Details");
- w.WriteValue (de == null ? string.Empty : de.Value);
- }
-
- foreach (XPathNavigator xmp in navigator.Select ("//ErrorDocumentation/Examples/string")) {
- WriteElementWithClass (w, "div", "code_example");
- WriteElementWithClass (w, "div", "code_ex_title", "Example");
- w.WriteRaw (Mono.Utilities.Colorizer.Colorize (xmp.Value, "c#"));;
- w.WriteEndElement ();
- }
-
- w.Close ();
-
- return sw.ToString ();
- }
-
- void WriteElementWithClass (XmlWriter w, string element, string cls, string content = null)
- {
- w.WriteStartElement (element);
- w.WriteAttributeString ("class", cls);
- if (!string.IsNullOrEmpty (content)) {
- w.WriteValue (content);
- w.WriteEndElement ();
- }
- }
- }
-}
+++ /dev/null
-using System;
-using System.IO;
-using System.Text;
-using System.Collections.Generic;
-
-using MonkeyDoc;
-using MonkeyDoc.Generators;
-
-namespace MonkeyDoc.Generators.Html
-{
- // Input is expected to be already HTML so just return it
- public class Idem : IHtmlExporter
- {
- public string CssCode {
- get {
- return string.Empty;
- }
- }
-
- public string Export (Stream input, Dictionary<string, string> extraArgs)
- {
- if (input == null)
- return null;
- return new StreamReader (input).ReadToEnd ();
- }
-
- public string Export (string input, Dictionary<string, string> extraArgs)
- {
- if (string.IsNullOrEmpty (input))
- return null;
- return input;
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using System.IO;
-using System.Text;
-using System.Collections.Generic;
-
-using MonkeyDoc;
-using MonkeyDoc.Generators;
-
-namespace MonkeyDoc.Generators.Html
-{
- public class Man2Html : IHtmlExporter
- {
- public string CssCode {
- get {
- return string.Empty;
- }
- }
-
- public string Export (Stream input, Dictionary<string, string> extraArgs)
- {
- if (input == null)
- return null;
- return GetTextFromReader (new StreamReader (input));
- }
-
- public string Export (string input, Dictionary<string, string> extraArgs)
- {
- if (string.IsNullOrEmpty (input))
- return null;
- return GetTextFromReader (new StringReader (input));
- }
-
- public static string GetTextFromReader (TextReader file)
- {
- string line;
- StateInfo s = new StateInfo ();
-
- while ((line = file.ReadLine ()) != null)
- ProcessLine (line, s);
-
- return s.output.ToString ();
- }
-
- enum ListState {
- None,
- Start,
- Title,
- }
-
- class StateInfo {
- public ListState ls;
- public Stack<string> tags = new Stack<string> ();
- public StringBuilder output = new StringBuilder ();
- }
-
- static void ProcessLine (string line, StateInfo s)
- {
- string[] parts = SplitLine (line);
- switch (parts [0]) {
- case ".\\\"": // comments
- case ".de": // define macro
- case ".if": // if
- case ".ne": // ???
- case "..": // end macro
- // ignore
- break;
- case ".I":
- s.output.Append ("<i>");
- Translate (parts, 1, s.output);
- s.output.Append ("</i>");
- break;
- case ".B":
- s.output.Append ("<b>");
- Translate (parts, 1, s.output);
- s.output.Append ("</b>");
- break;
- case ".br":
- Translate (parts, 1, s.output);
- s.output.Append ("<br />");
- break;
- case ".nf":
- Expect (s, "</p>");
- s.output.Append ("<pre>\n");
- s.tags.Push ("</pre>");
- break;
- case ".fi":
- Expect (s, "</pre>");
- break;
- case ".PP":
- Expect (s, "</p>", "</dd>", "</dl>");
- goto case ".Sp";
- case ".Sp":
- Expect (s, "</p>");
- s.output.Append ("<p>");
- Translate (parts, 1, s.output);
- s.tags.Push ("</p>");
- break;
- case ".RS":
- Expect (s, "</p>");
- s.output.Append ("<blockquote>");
- s.tags.Push ("</blockquote>");
- break;
- case ".RE":
- ClearUntil (s, "</blockquote>");
- break;
- case ".SH":
- ClearAll (s);
- s.output.Append ("<h2>");
- Translate (parts, 1, s.output);
- s.output.Append ("</h2>")
- .Append ("<blockquote>");
- s.tags.Push ("</blockquote>");
- break;
- case ".SS":
- s.output.Append ("<h3>");
- Translate (parts, 1, s.output);
- s.output.Append ("</h3>");
- break;
- case ".TH": {
- ClearAll (s);
- string name = "", extra = "";
- if (parts.Length >= 4 && parts [2].Trim ().Length == 0) {
- name = parts [1] + "(" + parts [3] + ")";
- if (parts.Length > 4) {
- int start = 4;
- if (parts [start].Trim ().Length == 0)
- ++start;
- extra = string.Join ("", parts, start, parts.Length-start);
- }
- }
- else
- name = string.Join ("", parts, 1, parts.Length-1);
- s.output.Append ("<table width=\"100%\" bgcolor=\"#b0c4da\">" +
- "<tr colspan=\"2\"><td>Manual Pages</td></tr>\n" +
- "<tr><td><h3>");
- Translate (name, s.output);
- s.output.Append ("</h3></td><td align=\"right\">");
- Translate (extra, s.output);
- s.output.Append ("</td></tr></table>");
- break;
- }
- case ".TP":
- Expect (s, "</p>");
- if (s.tags.Count > 0 && s.tags.Peek ().ToString () != "</dd>") {
- s.output.Append ("<dl>");
- s.tags.Push ("</dl>");
- }
- else
- Expect (s, "</dd>");
- s.output.Append ("<dt>");
- s.tags.Push ("</dt>");
- s.ls = ListState.Start;
- break;
- default:
- Translate (line, s.output);
- break;
- }
- if (s.ls == ListState.Start)
- s.ls = ListState.Title;
- else if (s.ls == ListState.Title) {
- Expect (s, "</dt>");
- s.output.Append ("<dd>");
- s.tags.Push ("</dd>");
- s.ls = ListState.None;
- }
- s.output.Append ("\n");
- }
-
- static string[] SplitLine (string line)
- {
- if (line.Length > 1 && line [0] != '.')
- return new string[]{null, line};
-
- int i;
- for (i = 0; i < line.Length; ++i) {
- if (char.IsWhiteSpace (line, i))
- break;
- }
-
- if (i == line.Length)
- return new string[]{line};
-
- var pieces = new List<string> ();
- pieces.Add (line.Substring (0, i));
- bool inQuotes = false;
- bool prevWs = true;
- ++i;
- int start = i;
- for ( ; i < line.Length; ++i) {
- char c = line [i];
- if (inQuotes) {
- if (c == '"') {
- Add (pieces, line, start, i);
- start = i+1;
- inQuotes = false;
- }
- }
- else {
- if (prevWs && c == '"') {
- Add (pieces, line, start, i);
- start = i+1;
- inQuotes = true;
- }
- else if (char.IsWhiteSpace (c)) {
- if (!prevWs) {
- Add (pieces, line, start, i);
- start = i;
- }
- prevWs = true;
- }
- else {
- if (prevWs) {
- Add (pieces, line, start, i);
- start = i;
- }
- prevWs = false;
- }
- }
- }
- if (start > 0 && start != line.Length)
- pieces.Add (line.Substring (start, line.Length-start));
- return pieces.ToArray ();
- }
-
- static void Add (List<string> pieces, string line, int start, int end)
- {
- if (start == end)
- return;
- pieces.Add (line.Substring (start, end-start));
- }
-
- static void Expect (StateInfo s, params string[] expected)
- {
- string e;
- while (s.tags.Count > 0 &&
- Array.IndexOf (expected, (e = s.tags.Peek ().ToString ())) >= 0) {
- s.output.Append (s.tags.Pop ().ToString ());
- }
- }
-
- static void ClearUntil (StateInfo s, string required)
- {
- string e;
- while (s.tags.Count > 0 &&
- (e = s.tags.Peek ().ToString ()) != required) {
- s.output.Append (s.tags.Pop ().ToString ());
- }
- if (e == required)
- s.output.Append (s.tags.Pop ().ToString ());
- }
-
- static void ClearAll (StateInfo s)
- {
- while (s.tags.Count > 0)
- s.output.Append (s.tags.Pop ().ToString ());
- }
-
- static void Translate (string[] lines, int startIndex, StringBuilder output)
- {
- if (lines.Length <= startIndex)
- return;
- do {
- Translate (lines [startIndex++], output);
- if (startIndex == lines.Length)
- break;
- } while (startIndex < lines.Length);
- }
-
- static void Translate (string line, StringBuilder output)
- {
- string span = null;
- int start = output.Length;
- for (int i = 0; i < line.Length; ++i) {
- switch (line [i]) {
- case '\\': {
- if ((i+2) < line.Length && line [i+1] == 'f') {
- if (line [i+2] == 'I') {
- output.Append ("<i>");
- span = "</i>";
- }
- else if (line [i+2] == 'B') {
- output.Append ("<b>");
- span = "</b>";
- }
- else if (line [i+2] == 'R' || line [i+2] == 'P') {
- output.Append (span);
- }
- else
- goto default;
- i += 2;
- }
- else if ((i+1) < line.Length) {
- output.Append (line [i+1]);
- ++i;
- }
- else
- goto default;
- break;
- }
- case '<':
- output.Append ("<");
- break;
- case '>':
- output.Append (">");
- break;
- case '&':
- output.Append ("&");
- break;
- default:
- output.Append (line [i]);
- break;
- }
- }
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using System.IO;
-using System.Text;
-using System.Xml;
-using System.Collections.Generic;
-
-using MonkeyDoc;
-using MonkeyDoc.Generators;
-
-namespace MonkeyDoc.Generators.Html
-{
- // Input is expected to be already HTML so just return it
- public class MonoBook2Html : IHtmlExporter
- {
- public string CssCode {
- get {
- return @" h3 {
- font-size: 18px;
- padding-bottom: 4pt;
- border-bottom: 2px solid #dddddd;
- }
-
- .api {
- border: 1px solid;
- padding: 10pt;
- margin: 10pt;
- }
-
- .api-entry {
- border-bottom: none;
- font-size: 18px;
- }
-
- .prototype {
- border: 1px solid;
- background-color: #f2f2f2;
- padding: 5pt;
- margin-top: 5pt;
- margin-bottom: 5pt;
- }
-
- .header {
- border: 1px solid !important;
- padding: 0 0 5pt 5pt !important;
- margin: 10pt !important;
- white-space: pre !important;
- font-family: monospace !important;
- font-weight: normal !important;
- font-size: 1em !important;
- }
-
- .code {
- border: 1px solid;
- padding: 0 0 5pt 5pt;
- margin: 10pt;
- white-space: pre;
- font-family: monospace;
- }
-";
- }
- }
-
- public string Export (Stream input, Dictionary<string, string> extraArgs)
- {
- if (input == null)
- return null;
- return FromXmlReader (XmlReader.Create (input));
- }
-
- public string Export (string input, Dictionary<string, string> extraArgs)
- {
- if (string.IsNullOrEmpty (input))
- return null;
- return FromXmlReader (XmlReader.Create (new StringReader (input)));
- }
-
- public string FromXmlReader (XmlReader reader)
- {
- if (!reader.ReadToDescendant ("head"))
- return null;
- if (!reader.ReadToNextSibling ("body"))
- return null;
-
- return reader.ReadInnerXml ();
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using System.IO;
-using System.Xml;
-using System.Xml.Xsl;
-using System.Xml.XPath;
-using System.Reflection;
-using System.Collections.Generic;
-
-namespace MonkeyDoc.Generators.Html
-{
- public class Toc2Html : IHtmlExporter
- {
- XslTransform transform;
-
- public Toc2Html ()
- {
- transform = new XslTransform ();
- var assembly = Assembly.GetCallingAssembly ();
- var stream = assembly.GetManifestResourceStream ("toc-html.xsl");
- XmlReader xml_reader = new XmlTextReader (stream);
- transform.Load (xml_reader, null, null);
- }
-
- public string Export (Stream input, Dictionary<string, string> extraArgs)
- {
- var output = new StringWriter ();
- transform.Transform (new XPathDocument (input), null, output, null);
- return output.ToString ();
- }
-
- public string Export (string input, Dictionary<string, string> extraArgs)
- {
- var output = new StringWriter ();
- transform.Transform (new XPathDocument (new StringReader (input)), null, output, null);
- return output.ToString ();
- }
-
- public string CssCode {
- get {
- return string.Empty;
- }
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-//
-// index.cs: Handling of the index files
-//
-// Author:
-// Miguel de Icaza (miguel@xamarin.com)
-//
-// (C) 2003 Ximian, Inc.
-// Copyright 2003-2011 Novell Inc
-// Copyright 2011 Xamarin Inc.
-//
-// Possible file format optimizations:
-// * Do not use 4 bytes for each index entry, use 3 bytes
-// * Find a way of compressing strings, there are plenty of duplicates
-// Find common roots, and use an encoding that uses a root to compress data.
-// "System", "System.Data", "System.Data class"
-// 0: PLAIN: "System"
-// 1: PLAIN: " class"
-// 2: LINK0 PLAIN ".DATA"
-// 3: LINK0 LINK1
-//
-// Maybe split everything at spaces and dots, and encode that:
-// string-1-idx "System."
-// string-1-idx "Data"
-// 2-items [ string-1-idx string-2-idx]
-//
-// Other variations are possible; Like Archive "System", "System." when we
-// see "System.Data".
-//
-//
-
-using System;
-using System.IO;
-using System.Text;
-using System.Collections.Generic;
-
-namespace MonkeyDoc
-{
- public class Topic
- {
- public readonly string Caption;
- public readonly string SortKey;
- public readonly string Url;
-
- public Topic (string caption, string sort_key, string url)
- {
- Caption = caption;
- SortKey = sort_key;
- Url = url;
- }
- }
-
- public class IndexEntry
- {
- List<Topic> topics;
-
- public int Position {
- get;
- private set;
- }
-
- public IList<Topic> Topics {
- get {
- return topics.AsReadOnly ();
- }
- }
-
- public int Count {
- get;
- private set;
- }
-
- public void Add (Topic t)
- {
- Count++;
- topics.Add (t);
- }
-
- public Topic this [int idx] {
- get {
- if (idx < 0 || idx > topics.Count)
- throw new ArgumentOutOfRangeException ("idx");
- return topics[idx];
- }
- }
-
- //
- // Constructor from a stream
- //
- public IndexEntry (FileStream fs, BinaryReader reader, int position)
- {
- Count = reader.ReadInt32 ();
- int caption_offset = reader.ReadInt32 ();
- string caption;
- topics = new List<Topic> (Count);
-
- int [] offsets = new int [Count];
- for (int i = 0; i < Count; i++)
- offsets [i] = reader.ReadInt32 ();
-
- fs.Position = caption_offset;
- caption = reader.ReadString ();
- for (int i = 0; i < Count; i++){
- fs.Position = offsets [i];
- string url = reader.ReadString ();
- topics.Add (new Topic (caption, string.Empty, url));
- }
- }
-
- //
- // Regular constructor
-
- public IndexEntry ()
- {
- topics = new List<Topic> ();
- }
-
- public void WriteTopics (IndexMaker maker, Stream stream, BinaryWriter writer)
- {
- //
- // Convention: entries with the same SortKey should have the same Caption
- //
- Position = (int) stream.Position;
- writer.Write (Count);
-
- if (Count == 0)
- return;
-
- writer.Write (maker.GetCode (topics[0].Caption));
- foreach (Topic t in topics)
- writer.Write (maker.GetCode (t.Url));
- }
- }
-
- public class IndexMaker
- {
- Dictionary<string, IndexEntry> entries = new Dictionary<string, IndexEntry> ();
- Dictionary<string, int> all_strings = new Dictionary<string, int> ();
- int index_position;
-
- void AddString (string str)
- {
- if (!all_strings.ContainsKey (str))
- all_strings.Add (str, 0);
- }
-
- public void AddTopic (Topic topic)
- {
- IndexEntry entry;
- if (!entries.TryGetValue (topic.SortKey, out entry)) {
- entry = new IndexEntry ();
- entries[topic.SortKey] = entry;
- }
-
- AddString (topic.SortKey);
- AddString (topic.Caption);
- AddString (topic.Url);
- entry.Add (topic);
- }
-
- public void Add (string caption, string sort_key, string url)
- {
- Topic t = new Topic (caption, sort_key, url);
- AddTopic (t);
- }
-
- void SaveStringTable (Stream stream, BinaryWriter writer)
- {
- var keys = new List<string> (all_strings.Keys);
- foreach (string s in keys) {
- int pos = (int) stream.Position;
- writer.Write (s);
- all_strings [s] = pos;
- }
- }
-
- public int GetCode (string s)
- {
- return all_strings [s];
- }
-
- void SaveTopics (Stream stream, BinaryWriter writer)
- {
- //
- // Convention: entries with the same SortKey should have the same Caption
- //
- foreach (IndexEntry e in entries.Values)
- e.WriteTopics (this, stream, writer);
- }
-
- void SaveIndexEntries (Stream stream, BinaryWriter writer)
- {
- index_position = (int) stream.Position;
- writer.Write (entries.Count);
- var keys = new List<string> (entries.Keys);
- keys.Sort (StringComparer.OrdinalIgnoreCase);
-
- foreach (string s in keys){
- IndexEntry e = entries [s];
- writer.Write (e.Position);
- }
- }
-
- public void Save (string filename)
- {
- Encoding utf8 = new UTF8Encoding (false, true);
-
- using (FileStream fs = File.OpenWrite (filename)){
- BinaryWriter writer = new BinaryWriter (fs, utf8);
- writer.Write (new byte [] { (byte) 'M',
- (byte) 'o', (byte) 'i',
- (byte) 'x'});
-
- // Leave room for pointer
- fs.Position = 8;
-
- SaveStringTable (fs, writer);
- SaveTopics (fs, writer);
-
- // index_position is set here
-
- SaveIndexEntries (fs, writer);
-
- fs.Position = 4;
- writer.Write (index_position);
- }
- }
- }
-
- public interface IListModel
- {
- int Rows { get; }
- string GetValue (int row);
- string GetDescription (int row);
- }
-
- public class IndexReader : IListModel
- {
- Encoding utf8 = new UTF8Encoding (false, true);
- FileStream fs;
- BinaryReader reader;
-
- // The offset of the table of entries
- int table_offset;
- int entries;
-
- static public IndexReader Load (string filename)
- {
- if (!File.Exists (filename))
- return null;
-
- try {
- return new IndexReader (filename);
- } catch {
- return null;
- }
- }
-
- IndexReader (string filename)
- {
- fs = File.OpenRead (filename);
- reader = new BinaryReader (fs, utf8);
-
- if (fs.ReadByte () != 'M' ||
- fs.ReadByte () != 'o' ||
- fs.ReadByte () != 'i' ||
- fs.ReadByte () != 'x'){
- throw new Exception ("Corrupt index");
- }
-
- // Seek to index_entries
- fs.Position = reader.ReadInt32 ();
-
- entries = reader.ReadInt32 ();
-
- table_offset = (int) fs.Position;
- }
-
- public int Rows {
- get {
- return entries;
- }
- }
-
- public string GetValue (int row)
- {
- fs.Position = row * 4 + table_offset;
- fs.Position = reader.ReadInt32 () + 4;
- int code = reader.ReadInt32 ();
- fs.Position = code;
- string caption = reader.ReadString ();
-
- return caption;
- }
-
- public string GetDescription (int row)
- {
- return GetValue (row);
- }
-
- public IndexEntry GetIndexEntry (int row)
- {
- fs.Position = row * 4 + table_offset;
- int entry_offset = reader.ReadInt32 ();
- fs.Position = entry_offset;
-
- return new IndexEntry (fs, reader, entry_offset);
- }
- }
-}
-
+++ /dev/null
-using System;
-using System.Linq;
-using System.IO;
-using System.Text;
-using System.Xml;
-using System.Xml.Linq;
-using System.Collections.Generic;
-
-namespace MonkeyDoc.Providers
-{
- // Common functionality between ecma-provider and ecmauncompiled-provider
- internal class EcmaDoc
- {
- public static void PopulateTreeFromIndexFile (string indexFilePath,
- Tree tree,
- IDocStorage storage,
- Dictionary<string, XElement> nsSummaries,
- Func<XElement, string> indexGenerator = null)
- {
- var root = tree.RootNode;
- int resID = 0;
- var asm = Path.GetDirectoryName (indexFilePath);
-
- storage = storage ?? new Storage.NullStorage ();
- // nsSummaries is allowed to be null if the user doesn't care about it
- nsSummaries = nsSummaries ?? new Dictionary<string, XElement> ();
- // default index generator uses a counter
- indexGenerator = indexGenerator ?? (_ => resID++.ToString ());
-
- using (var reader = XmlReader.Create (File.OpenRead (indexFilePath))) {
- reader.ReadToFollowing ("Types");
- var types = XElement.Load (reader.ReadSubtree ());
-
- foreach (var ns in types.Elements ("Namespace")) {
- var nsName = (string)ns.Attribute ("Name");
- nsName = !string.IsNullOrEmpty (nsName) ? nsName : "global";
- var nsNode = root.GetOrCreateNode (nsName, "N:" + nsName);
-
- XElement nsElements;
- if (!nsSummaries.TryGetValue (nsName, out nsElements))
- nsSummaries[nsName] = nsElements = new XElement ("elements",
- new XElement ("summary"),
- new XElement ("remarks"));
-
- foreach (var type in ns.Elements ("Type")) {
- // Add the XML file corresponding to the type to our storage
- var id = indexGenerator (type);
- string typeFilePath;
- var typeDocument = EcmaDoc.LoadTypeDocument (asm, nsName, type.Attribute ("Name").Value, out typeFilePath);
- if (typeDocument == null)
- continue;
- using (var file = File.OpenRead (typeFilePath))
- storage.Store (id, file);
- nsElements.Add (ExtractClassSummary (typeFilePath));
-
- var typeCaption = EcmaDoc.GetTypeCaptionFromIndex (type);
- var url = "ecma:" + id + '#' + typeCaption + '/';
- typeCaption = EcmaDoc.GetTypeCaptionFromIndex (type, true);
- var typeNode = nsNode.CreateNode (typeCaption, url);
-
- // Add meta "Members" node
- typeNode.CreateNode ("Members", "*");
- var membersNode = typeDocument.Root.Element ("Members");
- if (membersNode == null || !membersNode.Elements ().Any ())
- continue;
- var members = membersNode
- .Elements ("Member")
- .ToLookup (EcmaDoc.GetMemberType);
-
- foreach (var memberType in members) {
- // We pluralize the member type to get the caption and take the first letter as URL
- var node = typeNode.CreateNode (EcmaDoc.PluralizeMemberType (memberType.Key), memberType.Key[0].ToString ());
- var memberIndex = 0;
-
- var isCtors = memberType.Key[0] == 'C';
-
- // We do not escape much member name here
- foreach (var memberGroup in memberType.GroupBy (m => MakeMemberCaption (m, isCtors))) {
- if (memberGroup.Count () > 1) {
- // Generate overload
- var overloadCaption = MakeMemberCaption (memberGroup.First (), false);
- var overloadNode = node.CreateNode (overloadCaption, overloadCaption);
- foreach (var member in memberGroup)
- overloadNode.CreateNode (MakeMemberCaption (member, true), (memberIndex++).ToString ());
- overloadNode.Sort ();
- } else {
- // We treat constructor differently by showing their argument list in all cases
- node.CreateNode (MakeMemberCaption (memberGroup.First (), isCtors), (memberIndex++).ToString ());
- }
- }
- node.Sort ();
- }
- }
-
- nsNode.Sort ();
- }
- root.Sort ();
- }
- }
-
- // Utility methods
-
- public static XDocument LoadTypeDocument (string basePath, string nsName, string typeName)
- {
- string dummy;
- return LoadTypeDocument (basePath, nsName, typeName, out dummy);
- }
-
- public static XDocument LoadTypeDocument (string basePath, string nsName, string typeName, out string finalPath)
- {
- finalPath = Path.Combine (basePath, nsName, Path.ChangeExtension (typeName, ".xml"));
- if (!File.Exists (finalPath)) {
- Console.Error.WriteLine ("Warning: couldn't process type file `{0}' as it doesn't exist", finalPath);
- return null;
- }
- return XDocument.Load (finalPath);
- }
-
- public static string GetTypeCaptionFromIndex (XElement typeNodeFromIndex, bool full = false)
- {
- var t = typeNodeFromIndex;
- var c = ((string)(t.Attribute ("DisplayName") ?? t.Attribute ("Name"))).Replace ('+', '.');
- if (full)
- c += " " + (string)t.Attribute ("Kind");
- return c;
- }
-
- public static string PluralizeMemberType (string memberType)
- {
- switch (memberType) {
- case "Property":
- return "Properties";
- default:
- return memberType + "s";
- }
- }
-
- public static string GetMemberType (XElement m)
- {
- return m.Attribute ("MemberName").Value.StartsWith ("op_") ? "Operator" : m.Element ("MemberType").Value;
- }
-
- public static string MakeMemberCaption (XElement member, bool withArguments)
- {
- var caption = (string)member.Attribute ("MemberName");
- // Use type name instead of .ctor for cosmetic sake
- if (caption == ".ctor") {
- caption = (string)member.Ancestors ("Type").First ().Attribute ("Name");
- // If this is an inner type ctor, strip the parent type reference
- var plusIndex = caption.LastIndexOf ('+');
- if (plusIndex != -1)
- caption = caption.Substring (plusIndex + 1);
- }
- if (caption.StartsWith ("op_")) {
- string sig;
- caption = MakeOperatorSignature (member, out sig);
- caption = withArguments ? sig : caption;
- return caption;
- }
- if (withArguments) {
- var args = member.Element ("Parameters");
- caption += '(';
- if (args != null && args.Elements ("Parameter").Any ()) {
- caption += args.Elements ("Parameter")
- .Select (p => (string)p.Attribute ("Type"))
- .Aggregate ((p1, p2) => p1 + "," + p2);
- }
- caption += ')';
- }
-
- return caption;
- }
-
- internal static string MakeOperatorSignature (XElement member, out string memberSignature)
- {
- string name = (string)member.Attribute ("MemberName");
- var nicename = name.Substring(3);
- memberSignature = null;
-
- switch (name) {
- // unary operators: no overloading possible [ECMA-335 §10.3.1]
- case "op_UnaryPlus": // static R operator+ (T)
- case "op_UnaryNegation": // static R operator- (T)
- case "op_LogicalNot": // static R operator! (T)
- case "op_OnesComplement": // static R operator~ (T)
- case "op_Increment": // static R operator++ (T)
- case "op_Decrement": // static R operator-- (T)
- case "op_True": // static bool operator true (T)
- case "op_False": // static bool operator false (T)
- case "op_AddressOf": // static R operator& (T)
- case "op_PointerDereference": // static R operator* (T)
- memberSignature = nicename;
- break;
- // conversion operators: overloading based on parameter and return type [ECMA-335 §10.3.3]
- case "op_Implicit": // static implicit operator R (T)
- case "op_Explicit": // static explicit operator R (T)
- nicename = name.EndsWith ("Implicit") ? "ImplicitConversion" : "ExplicitConversion";
- string arg = (string)member.Element ("Parameters").Element ("Parameter").Attribute ("Type");
- string ret = (string)member.Element ("ReturnValue").Element ("ReturnType");
- memberSignature = arg + " to " + ret;
- break;
- // binary operators: overloading is possible [ECMA-335 §10.3.2]
- default:
- memberSignature =
- nicename + "("
- + string.Join (",", member.Element ("Parameters").Elements ("Parameter").Select (p => (string)p.Attribute ("Type")))
- + ")";
- break;
- }
-
- return nicename;
- }
-
- static XElement ExtractClassSummary (string typeFilePath)
- {
- using (var reader = XmlReader.Create (typeFilePath)) {
- reader.ReadToFollowing ("Type");
- var name = reader.GetAttribute ("Name");
- var fullName = reader.GetAttribute ("FullName");
- reader.ReadToFollowing ("AssemblyName");
- var assemblyName = reader.ReadElementString ();
- reader.ReadToFollowing ("summary");
- var summary = reader.ReadInnerXml ();
- reader.ReadToFollowing ("remarks");
- var remarks = reader.ReadInnerXml ();
-
- return new XElement ("class",
- new XAttribute ("name", name ?? string.Empty),
- new XAttribute ("fullname", fullName ?? string.Empty),
- new XAttribute ("assembly", assemblyName ?? string.Empty),
- new XElement ("summary", new XCData (summary)),
- new XElement ("remarks", new XCData (remarks)));
- }
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-// addins-provider.cs
-//
-// A provider to display Mono.Addins extension models
-//
-// Author:
-// Lluis Sanchez Gual <lluis@novell.com>
-//
-// Copyright (c) 2007 Novell, Inc (http://www.novell.com)
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-//
-
-using System;
-using System.Linq;
-using System.Diagnostics;
-using System.Text;
-using System.IO;
-using System.Xml;
-using System.Collections.Generic;
-
-namespace MonkeyDoc.Providers
-{
- public class AddinsProvider : Provider
- {
- string file;
-
- public AddinsProvider (string xmlModelFile)
- {
- file = xmlModelFile;
-
- if (!File.Exists (file))
- throw new FileNotFoundException (String.Format ("The file `{0}' does not exist", file));
- }
-
- public override void PopulateTree (Tree tree)
- {
- string fileId = Path.GetFileNameWithoutExtension (file);
- using (var f = File.OpenRead (file))
- tree.HelpSource.Storage.Store (fileId, f);
-
- XmlDocument doc = new XmlDocument ();
- doc.Load (file);
-
- foreach (XmlElement addin in doc.SelectNodes ("Addins/Addin")) {
-
- string addinId = addin.GetAttribute ("fullId");
- Node newNode = tree.RootNode.CreateNode (addin.GetAttribute ("name"), "addin:" + fileId + "#" + addinId);
-
- foreach (XmlElement node in addin.SelectNodes ("ExtensionPoint")) {
- string target = "extension-point:" + fileId + "#" + addinId + "#" + node.GetAttribute ("path");
- Node newExt = newNode.CreateNode (node.GetAttribute ("name"), target);
-
- foreach (XmlElement en in node.SelectNodes ("ExtensionNode")) {
- string nid = en.GetAttribute ("id");
- string nname = en.GetAttribute ("name");
- newExt.CreateNode (nname, "extension-node:" + fileId + "#" + addinId + "#" + nid);
- }
- }
- }
- }
-
- public override void CloseTree (HelpSource hs, Tree tree)
- {
- }
- }
-
- public class AddinsHelpSource : HelpSource
- {
- public AddinsHelpSource (string base_file, bool create) : base (base_file, create)
- {
- }
-
- internal protected const string AddinPrefix = "addin:";
- internal protected const string ExtensionPrefix = "extension-point:";
- internal protected const string ExtensionNodePrefix = "extension-node:";
-
- public override bool CanHandleUrl (string url)
- {
- return url.StartsWith (AddinPrefix, StringComparison.OrdinalIgnoreCase)
- || url.StartsWith (ExtensionPrefix, StringComparison.OrdinalIgnoreCase)
- || url.StartsWith (ExtensionNodePrefix, StringComparison.OrdinalIgnoreCase);
- }
-
- protected override string UriPrefix {
- get {
- return AddinPrefix;
- }
- }
-
- public override DocumentType GetDocumentTypeForId (string id, out Dictionary<string, string> extraArgs)
- {
- extraArgs = new Dictionary<string, string> ();
- var idParts = id.Split ('#');
- extraArgs["FileID"] = idParts[0];
- extraArgs["AddinID"] = idParts[1];
- extraArgs["NodeID"] = idParts[2];
-
- return DocumentType.AddinXml;
- }
-
- public override Node MatchNode (string url)
- {
- var prefix = new[] { AddinPrefix, ExtensionPrefix, ExtensionNodePrefix }.First (p => url.StartsWith (p, StringComparison.OrdinalIgnoreCase));
- return base.MatchNode (prefix != null ? url.Substring (prefix.Length) : url);
- }
-
- public override Stream GetHelpStream (string id)
- {
- var idParts = id.Split ('#');
- return base.GetHelpStream (idParts[0]);
- }
-
- public override Stream GetCachedHelpStream (string id)
- {
- var idParts = id.Split ('#');
- return base.GetHelpStream (idParts[0]);
- }
- }
-}
+++ /dev/null
-//
-// The ecmaspec provider is for ECMA specifications
-//
-// Authors:
-// John Luke (jluke@cfl.rr.com)
-// Ben Maurer (bmaurer@users.sourceforge.net)
-//
-// Use like this:
-// mono assembler.exe --ecmaspec DIRECTORY --out name
-//
-
-using System;
-using System.Linq;
-using System.IO;
-using System.Text;
-using System.Xml;
-using System.Xml.Linq;
-using System.Collections.Generic;
-
-using Lucene.Net.Index;
-using Lucene.Net.Documents;
-
-using MonkeyDoc.Ecma;
-using Mono.Utilities;
-
-namespace MonkeyDoc.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> ();
-
- public EcmaProvider ()
- {
- }
-
- public EcmaProvider (string baseDir)
- {
- AddDirectory (baseDir);
- }
-
- public void AddDirectory (string directory)
- {
- if (string.IsNullOrEmpty (directory))
- throw new ArgumentNullException ("directory");
-
- directories.Add (directory);
- }
-
- public override void PopulateTree (Tree tree)
- {
- var storage = tree.HelpSource.Storage;
- var nsSummaries = new Dictionary<string, XElement> ();
- int resID = 0;
-
- foreach (var asm in directories) {
- var indexFilePath = Path.Combine (asm, "index.xml");
- if (!File.Exists (indexFilePath)) {
- Console.Error.WriteLine ("Warning: couldn't process directory `{0}' as it has no index.xml file", asm);
- continue;
- }
-
- EcmaDoc.PopulateTreeFromIndexFile (indexFilePath, tree, storage, nsSummaries, _ => resID++.ToString ());
- }
-
- foreach (var summary in nsSummaries)
- storage.Store ("xml.summary." + summary.Key, summary.Value.ToString ());
-
- var masterSummary = new XElement ("elements",
- directories
- .SelectMany (d => Directory.EnumerateFiles (d, "ns-*.xml"))
- .Select (ExtractNamespaceSummary));
- storage.Store ("mastersummary.xml", masterSummary.ToString ());
- }
-
- XElement ExtractNamespaceSummary (string nsFile)
- {
- using (var reader = XmlReader.Create (nsFile)) {
- reader.ReadToFollowing ("Namespace");
- var name = reader.GetAttribute ("Name");
- reader.ReadToFollowing ("summary");
- var summary = reader.ReadInnerXml ();
- reader.ReadToFollowing ("remarks");
- var remarks = reader.ReadInnerXml ();
-
- return new XElement ("namespace",
- new XAttribute ("ns", name ?? string.Empty),
- new XElement ("summary", new XCData (summary)),
- new XElement ("remarks", new XCData (remarks)));
- }
- }
-
- public override void CloseTree (HelpSource hs, Tree tree)
- {
- AddImages (hs);
- AddExtensionMethods (hs);
- }
-
- void AddEcmaXml (HelpSource hs)
- {
- var xmls = directories
- .SelectMany (Directory.EnumerateDirectories) // Assemblies
- .SelectMany (Directory.EnumerateDirectories) // Namespaces
- .SelectMany (Directory.EnumerateFiles)
- .Where (f => f.EndsWith (".xml")); // Type XML files
-
- int resID = 0;
- foreach (var xml in xmls)
- using (var file = File.OpenRead (xml))
- hs.Storage.Store ((resID++).ToString (), file);
- }
-
- void AddImages (HelpSource hs)
- {
- var imgs = directories
- .SelectMany (Directory.EnumerateDirectories)
- .Select (d => Path.Combine (d, "_images"))
- .Where (Directory.Exists)
- .SelectMany (Directory.EnumerateFiles);
-
- foreach (var img in imgs)
- using (var file = File.OpenRead (img))
- hs.Storage.Store (Path.GetFileName (img), file);
- }
-
- void AddExtensionMethods (HelpSource hs)
- {
- var extensionMethods = directories
- .SelectMany (Directory.EnumerateDirectories)
- .Select (d => Path.Combine (d, "index.xml"))
- .Where (File.Exists)
- .Select (f => {
- using (var file = File.OpenRead (f)) {
- var reader = XmlReader.Create (file);
- reader.ReadToFollowing ("ExtensionMethods");
- return reader.ReadInnerXml ();
- }
- })
- .DefaultIfEmpty (string.Empty);
-
- hs.Storage.Store ("ExtensionMethods.xml",
- "<ExtensionMethods>" + extensionMethods.Aggregate (string.Concat) + "</ExtensionMethods>");
- }
-
- IEnumerable<string> GetEcmaXmls ()
- {
- return directories
- .SelectMany (Directory.EnumerateDirectories) // Assemblies
- .SelectMany (Directory.EnumerateDirectories) // Namespaces
- .SelectMany (Directory.EnumerateFiles)
- .Where (f => f.EndsWith (".xml")); // Type XML files
- }
- }
-
- public class EcmaHelpSource : HelpSource
- {
- const string EcmaPrefix = "ecma:";
- EcmaUrlParser parser = new EcmaUrlParser ();
- LRUCache<string, Node> cache = new LRUCache<string, Node> (4);
-
- public EcmaHelpSource (string base_file, bool create) : base (base_file, create)
- {
- }
-
- protected EcmaHelpSource () : base ()
- {
- }
-
- protected override string UriPrefix {
- get {
- return EcmaPrefix;
- }
- }
-
- public override bool CanHandleUrl (string url)
- {
- if (url.Length > 2 && url[1] == ':') {
- switch (url[0]) {
- case 'T':
- case 'M':
- case 'C':
- case 'P':
- case 'E':
- case 'F':
- case 'N':
- case 'O':
- return true;
- }
- }
- return base.CanHandleUrl (url);
- }
-
- // Clean the extra paramers in the id
- public override Stream GetHelpStream (string id)
- {
- var idParts = id.Split ('?');
- return base.GetHelpStream (idParts[0]);
- }
-
- public override Stream GetCachedHelpStream (string id)
- {
- var idParts = id.Split ('?');
- return base.GetCachedHelpStream (idParts[0]);
- }
-
- public override DocumentType GetDocumentTypeForId (string id, out Dictionary<string, string> extraParams)
- {
- extraParams = null;
- int interMark = id.LastIndexOf ('?');
- if (interMark != -1)
- extraParams = id.Substring (interMark)
- .Split ('&')
- .Select (nvp => {
- var eqIdx = nvp.IndexOf ('=');
- return new { Key = nvp.Substring (0, eqIdx < 0 ? nvp.Length : eqIdx), Value = nvp.Substring (eqIdx + 1) };
- })
- .ToDictionary (kvp => kvp.Key, kvp => kvp.Value );
- return DocumentType.EcmaXml;
- }
-
- public override string GetPublicUrl (Node node)
- {
- string url = string.Empty;
- var type = GetNodeType (node);
- //Console.WriteLine ("GetPublicUrl {0} : {1} [{2}]", node.Element, node.Caption, type.ToString ());
- switch (type) {
- case EcmaNodeType.Namespace:
- return node.Element; // A namespace node has already a well formated internal url
- case EcmaNodeType.Type:
- return MakeTypeNodeUrl (node);
- case EcmaNodeType.Meta:
- return MakeTypeNodeUrl (GetNodeTypeParent (node)) + GenerateMetaSuffix (node);
- case EcmaNodeType.Member:
- var typeChar = GetNodeMemberTypeChar (node);
- var parentNode = GetNodeTypeParent (node);
- var typeNode = MakeTypeNodeUrl (parentNode).Substring (2);
- return typeChar + ":" + typeNode + MakeMemberNodeUrl (typeChar, node);
- default:
- return null;
- }
- }
-
- string MakeTypeNodeUrl (Node node)
- {
- // A Type node has a Element property of the form: 'ecma:{number}#{typename}/'
- var hashIndex = node.Element.IndexOf ('#');
- var typeName = node.Element.Substring (hashIndex + 1, node.Element.Length - hashIndex - 2);
- return "T:" + node.Parent.Caption + '.' + typeName.Replace ('.', '+');
- }
-
- string MakeMemberNodeUrl (char typeChar, Node node)
- {
- // We clean inner type ctor name which may contain the outer type name
- var caption = node.Caption;
-
- // Sanitize constructor caption of inner types
- if (typeChar == 'C') {
- int lastDot = -1;
- for (int i = 0; i < caption.Length && caption[i] != '('; i++)
- lastDot = caption[i] == '.' ? i : lastDot;
- return lastDot == -1 ? '.' + caption : caption.Substring (lastDot);
- }
-
- /* We handle type conversion operator by checking if the name contains " to "
- * (as in 'foo to bar') and we generate a corresponding conversion signature
- */
- if (typeChar == 'O' && caption.IndexOf (" to ") != -1) {
- var parts = caption.Split (' ');
- return "." + node.Parent.Caption + "(" + parts[0] + ", " + parts[2] + ")";
- }
-
- /* The goal here is to treat method which are explicit interface definition
- * such as 'void IDisposable.Dispose ()' for which the caption is a dot
- * expression thus colliding with the ecma parser.
- * If the first non-alpha character in the caption is a dot then we have an
- * explicit member implementation (we assume the interface has namespace)
- */
- var firstNonAlpha = caption.FirstOrDefault (c => !char.IsLetterOrDigit (c));
- if (firstNonAlpha == '.')
- return "$" + caption;
-
- 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
- while (node != null && node.Parent != null && !node.Parent.Parent.Element.StartsWith ("root:/", StringComparison.OrdinalIgnoreCase))
- node = node.Parent;
- return node;
- }
-
- string GenerateMetaSuffix (Node node)
- {
- string suffix = string.Empty;
- // A meta node has always a type element to begin with
- while (GetNodeType (node) != EcmaNodeType.Type) {
- suffix = '/' + node.Element + suffix;
- node = node.Parent;
- }
- return suffix;
- }
-
- public override string GetInternalIdForUrl (string url, out Node node)
- {
- var id = string.Empty;
- node = null;
-
- if (!url.StartsWith (UriPrefix, StringComparison.OrdinalIgnoreCase)) {
- node = MatchNode (url);
- if (node == null)
- return null;
- id = node.GetInternalUrl ();
- }
-
- string hash;
- id = GetInternalIdForInternalUrl (id, out hash);
-
- return id + GetArgs (hash, node);
- }
-
- public string GetInternalIdForInternalUrl (string internalUrl, out string hash)
- {
- var id = internalUrl;
- if (id.StartsWith (UriPrefix, StringComparison.OrdinalIgnoreCase))
- id = id.Substring (UriPrefix.Length);
- else if (id.StartsWith ("N:", StringComparison.OrdinalIgnoreCase))
- id = "xml.summary." + id.Substring ("N:".Length);
-
- var hashIndex = id.IndexOf ('#');
- hash = string.Empty;
- if (hashIndex != -1) {
- hash = id.Substring (hashIndex + 1);
- id = id.Substring (0, hashIndex);
- }
-
- return id;
- }
-
- public override Node MatchNode (string url)
- {
- Node node = null;
- if ((node = cache.Get (url)) == null) {
- node = InternalMatchNode (url);
- 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){
- foreach (Node type_node in ns_node.Nodes){
- string typename = type_node.Caption.Substring (0, type_node.Caption.IndexOf (' '));
- string full = ns_node.Caption + "." + typename;
-
- string doc_tag = GetKindFromCaption (type_node.Caption);
- string url = type_node.PublicUrl;
-
- //
- // Add MonoMac/MonoTouch [Export] attributes, those live only in classes
- //
- XDocument type_doc = null;
- ILookup<string, XElement> prematchedMembers = null;
- bool hasExports = doc_tag == "Class" && (ns_node.Caption.StartsWith ("MonoTouch") || ns_node.Caption.StartsWith ("MonoMac"));
- if (hasExports) {
- try {
- string rest, hash;
- var id = GetInternalIdForInternalUrl (type_node.GetInternalUrl (), out hash);
- type_doc = XDocument.Load (GetHelpStream (id));
- prematchedMembers = type_doc.Root.Element ("Members").Elements ("Member").ToLookup (n => (string)n.Attribute ("MemberName"), n => n);
- } catch (Exception e) {
- Console.WriteLine ("Problem processing {0} for MonoTouch/MonoMac exports\n\n{0}", e);
- hasExports = false;
- }
- }
-
- if (doc_tag == "Class" || doc_tag == "Structure" || doc_tag == "Interface"){
- index_maker.Add (type_node.Caption, typename, url);
- index_maker.Add (full + " " + doc_tag, full, url);
-
- foreach (Node c in type_node.Nodes){
- switch (c.Caption){
- case "Constructors":
- index_maker.Add (" constructors", typename+"0", url + "/C");
- break;
- case "Fields":
- index_maker.Add (" fields", typename+"1", url + "/F");
- break;
- case "Events":
- index_maker.Add (" events", typename+"2", url + "/E");
- break;
- case "Properties":
- index_maker.Add (" properties", typename+"3", url + "/P");
- break;
- case "Methods":
- index_maker.Add (" methods", typename+"4", url + "/M");
- break;
- case "Operators":
- index_maker.Add (" operators", typename+"5", url + "/O");
- break;
- }
- }
-
- //
- // Now repeat, but use a different sort key, to make sure we come after
- // the summary data above, start the counter at 6
- //
- string keybase = typename + "6.";
-
- foreach (Node c in type_node.Nodes){
- var type = c.Caption[0];
-
- foreach (Node nc in c.Nodes) {
- string res = nc.Caption;
- string nurl = nc.PublicUrl;
-
- // Process exports
- if (hasExports && (type == 'C' || type == 'M' || type == 'P')) {
- try {
- var member = GetMemberFromCaption (type_doc, type == 'C' ? ".ctor" : res, false, prematchedMembers);
- var exports = member.Descendants ("AttributeName").Where (a => a.Value.Contains ("Foundation.Export"));
- foreach (var exportNode in exports) {
- var parts = exportNode.Value.Split ('"');
- if (parts.Length != 3) {
- Console.WriteLine ("Export attribute not found or not usable in {0}", exportNode);
- } else {
- var export = parts[1];
- index_maker.Add (export + " selector", export, nurl);
- }
- }
- } catch (Exception e) {
- Console.WriteLine ("Problem processing {0}/{1} for MonoTouch/MonoMac exports\n\n{2}", nurl, res, e);
- }
- }
-
- switch (type){
- case 'C':
- break;
- case 'F':
- index_maker.Add (String.Format ("{0}.{1} field", typename, res),
- keybase + res, nurl);
- index_maker.Add (String.Format ("{0} field", res), res, nurl);
- break;
- case 'E':
- index_maker.Add (String.Format ("{0}.{1} event", typename, res),
- keybase + res, nurl);
- index_maker.Add (String.Format ("{0} event", res), res, nurl);
- break;
- case 'P':
- index_maker.Add (String.Format ("{0}.{1} property", typename, res),
- keybase + res, nurl);
- index_maker.Add (String.Format ("{0} property", res), res, nurl);
- break;
- case 'M':
- index_maker.Add (String.Format ("{0}.{1} method", typename, res),
- keybase + res, nurl);
- index_maker.Add (String.Format ("{0} method", res), res, nurl);
- break;
- case 'O':
- index_maker.Add (String.Format ("{0}.{1} operator", typename, res),
- keybase + res, nurl);
- break;
- }
- }
- }
- } else if (doc_tag == "Enumeration"){
- //
- // Enumerations: add the enumeration values
- //
- index_maker.Add (type_node.Caption, typename, url);
- index_maker.Add (full + " " + doc_tag, full, url);
-
- // Now, pull the values.
- string rest, hash;
- var id = GetInternalIdForInternalUrl (type_node.GetInternalUrl (), out hash);
- var xdoc = XDocument.Load (GetHelpStream (id));
- if (xdoc == null)
- continue;
-
- var members = xdoc.Root.Element ("Members").Elements ("Members");
- if (members == null)
- continue;
-
- foreach (var member_node in members){
- string enum_value = member_node.Attribute ("MemberName").Value;
- string caption = enum_value + " value";
- index_maker.Add (caption, caption, url);
- }
- } else if (doc_tag == "Delegate"){
- index_maker.Add (type_node.Caption, typename, url);
- index_maker.Add (full + " " + doc_tag, full, url);
- }
- }
- }
- }
-
-
- public override void PopulateSearchableIndex (IndexWriter writer)
- {
- StringBuilder text = new StringBuilder ();
- SearchableDocument searchDoc = new SearchableDocument ();
-
- foreach (Node ns_node in Tree.RootNode.Nodes) {
- foreach (Node type_node in ns_node.Nodes) {
- string typename = type_node.Caption.Substring (0, type_node.Caption.IndexOf (' '));
- string full = ns_node.Caption + "." + typename;
- string url = type_node.PublicUrl;
- string doc_tag = GetKindFromCaption (type_node.Caption);
- string rest, hash;
- var id = GetInternalIdForInternalUrl (type_node.GetInternalUrl (), out hash);
- var xdoc = XDocument.Load (GetHelpStream (id));
- if (xdoc == null)
- continue;
- if (string.IsNullOrEmpty (doc_tag)) {
- Console.WriteLine (type_node.Caption);
- continue;
- }
-
- // For classes, structures or interfaces add a doc for the overview and
- // add a doc for every constructor, method, event, ...
- // doc_tag == "Class" || doc_tag == "Structure" || doc_tag == "Interface"
- if (doc_tag[0] == 'C' || doc_tag[0] == 'S' || doc_tag[0] == 'I') {
- // Adds a doc for every overview of every type
- SearchableDocument doc = searchDoc.Reset ();
- doc.Title = type_node.Caption;
- doc.HotText = typename;
- doc.Url = url;
- doc.FullTitle = full;
-
- var node_sel = xdoc.Root.Element ("Docs");
- text.Clear ();
- GetTextFromNode (node_sel, text);
- doc.Text = text.ToString ();
-
- text.Clear ();
- GetExamples (node_sel, text);
- doc.Examples = text.ToString ();
-
- writer.AddDocument (doc.LuceneDoc);
- var exportParsable = doc_tag[0] == 'C' && (ns_node.Caption.StartsWith ("MonoTouch") || ns_node.Caption.StartsWith ("MonoMac"));
-
- //Add docs for contructors, methods, etc.
- foreach (Node c in type_node.Nodes) { // c = Constructors || Fields || Events || Properties || Methods || Operators
- if (c.Element == "*")
- continue;
- const float innerTypeBoost = 0.2f;
-
- IEnumerable<Node> ncnodes = c.Nodes;
- // The rationale is that we need to properly handle method overloads
- // so for those method node which have children, flatten them
- if (c.Caption == "Methods") {
- ncnodes = ncnodes
- .Where (n => n.Nodes == null || n.Nodes.Count == 0)
- .Concat (ncnodes.Where (n => n.Nodes.Count > 0).SelectMany (n => n.Nodes));
- } else if (c.Caption == "Operators") {
- ncnodes = ncnodes
- .Where (n => !n.Caption.EndsWith ("Conversion"))
- .Concat (ncnodes.Where (n => n.Caption.EndsWith ("Conversion")).SelectMany (n => n.Nodes));
- }
-
- var prematchedMembers = xdoc.Root.Element ("Members").Elements ("Member").ToLookup (n => (string)n.Attribute ("MemberName"), n => n);
-
- foreach (Node nc in ncnodes) {
- var docsNode = GetDocsFromCaption (xdoc, c.Caption[0] == 'C' ? ".ctor" : nc.Caption, c.Caption[0] == 'O', prematchedMembers);
- if (docsNode == null) {
- Console.Error.WriteLine ("Problem: {0}", nc.PublicUrl);
- continue;
- }
-
- SearchableDocument doc_nod = searchDoc.Reset ();
- doc_nod.Title = LargeName (nc) + " " + EtcKindToCaption (c.Caption[0]);
- doc_nod.FullTitle = ns_node.Caption + '.' + typename + "::" + nc.Caption;
- doc_nod.HotText = string.Empty;
-
- /* Disable constructors hottext indexing as it's often "polluting" search queries
- because it has the same hottext than standard types */
- if (c.Caption != "Constructors") {
- //dont add the parameters to the hottext
- int ppos = nc.Caption.IndexOf ('(');
- doc_nod.HotText = ppos != -1 ? nc.Caption.Substring (0, ppos) : nc.Caption;
- }
-
- var urlnc = nc.PublicUrl;
- doc_nod.Url = urlnc;
-
- text.Clear ();
- GetTextFromNode (docsNode, text);
- doc_nod.Text = text.ToString ();
-
- text.Clear ();
- GetExamples (docsNode, text);
- doc_nod.Examples = text.ToString ();
-
- Document lucene_doc = doc_nod.LuceneDoc;
- lucene_doc.Boost = innerTypeBoost;
- writer.AddDocument (lucene_doc);
-
- // Objective-C binding specific parsing of [Export] attributes
- if (exportParsable) {
- try {
- var exports = docsNode.Parent.Elements ("Attributes").Elements ("Attribute").Elements ("AttributeName")
- .Select (a => (string)a).Where (txt => txt.Contains ("Foundation.Export"));
-
- foreach (var exportNode in exports) {
- var parts = exportNode.Split ('"');
- if (parts.Length != 3) {
- Console.WriteLine ("Export attribute not found or not usable in {0}", exportNode);
- continue;
- }
-
- var export = parts[1];
- var export_node = searchDoc.Reset ();
- export_node.Title = export + " Export";
- export_node.FullTitle = ns_node.Caption + '.' + typename + "::" + export;
- export_node.Url = urlnc;
- export_node.HotText = export;
- export_node.Text = string.Empty;
- export_node.Examples = string.Empty;
- lucene_doc = export_node.LuceneDoc;
- lucene_doc.Boost = innerTypeBoost;
- writer.AddDocument (lucene_doc);
- }
- } catch (Exception e){
- Console.WriteLine ("Problem processing {0} for MonoTouch/MonoMac exports\n\n{0}", e);
- }
- }
- }
- }
- // doc_tag == "Enumeration"
- } else if (doc_tag[0] == 'E'){
- var members = xdoc.Root.Element ("Members").Elements ("Member");
- if (members == null)
- continue;
-
- text.Clear ();
- foreach (var member_node in members) {
- string enum_value = (string)member_node.Attribute ("MemberName");
- text.Append (enum_value);
- text.Append (" ");
- GetTextFromNode (member_node.Element ("Docs"), text);
- text.AppendLine ();
- }
-
- SearchableDocument doc = searchDoc.Reset ();
-
- text.Clear ();
- GetExamples (xdoc.Root.Element ("Docs"), text);
- doc.Examples = text.ToString ();
-
- doc.Title = type_node.Caption;
- doc.HotText = (string)xdoc.Root.Attribute ("Name");
- doc.FullTitle = full;
- doc.Url = url;
- doc.Text = text.ToString();
- writer.AddDocument (doc.LuceneDoc);
- // doc_tag == "Delegate"
- } else if (doc_tag[0] == 'D'){
- SearchableDocument doc = searchDoc.Reset ();
- doc.Title = type_node.Caption;
- doc.HotText = (string)xdoc.Root.Attribute ("Name");
- doc.FullTitle = full;
- doc.Url = url;
-
- var node_sel = xdoc.Root.Element ("Docs");
-
- text.Clear ();
- GetTextFromNode (node_sel, text);
- doc.Text = text.ToString();
-
- text.Clear ();
- GetExamples (node_sel, text);
- doc.Examples = text.ToString();
-
- writer.AddDocument (doc.LuceneDoc);
- }
- }
- }
- }
-
- string GetKindFromCaption (string s)
- {
- int p = s.LastIndexOf (' ');
- if (p > 0)
- return s.Substring (p + 1);
- return null;
- }
-
- // Extract the interesting text from the docs node
- void GetTextFromNode (XElement n, StringBuilder sb)
- {
- // Include the text content of the docs
- sb.AppendLine (n.Value);
- foreach (var tag in n.Descendants ())
- //include the url to which points the see tag and the name of the parameter
- if ((tag.Name.LocalName.Equals ("see", StringComparison.Ordinal) || tag.Name.LocalName.Equals ("paramref", StringComparison.Ordinal))
- && tag.HasAttributes)
- sb.AppendLine ((string)tag.Attributes ().First ());
- }
-
- // Extract the code nodes from the docs
- void GetExamples (XElement n, StringBuilder sb)
- {
- foreach (var code in n.Descendants ("code"))
- sb.Append ((string)code);
- }
-
- // Extract a large name for the Node
- static string LargeName (Node matched_node)
- {
- string[] parts = matched_node.GetInternalUrl ().Split('/', '#');
- if (parts.Length == 3 && parts[2] != String.Empty) //List of Members, properties, events, ...
- return parts[1] + ": " + matched_node.Caption;
- else if(parts.Length >= 4) //Showing a concrete Member, property, ...
- return parts[1] + "." + matched_node.Caption;
- else
- return matched_node.Caption;
- }
-
- XElement GetMemberFromCaption (XDocument xdoc, string caption, bool isOperator, ILookup<string, XElement> prematchedMembers)
- {
- string name;
- IList<string> args;
- var doc = xdoc.Root.Element ("Members").Elements ("Member");
-
- if (isOperator) {
- // The first case are explicit and implicit conversion operators which are grouped specifically
- if (caption.IndexOf (" to ") != -1) {
- var convArgs = caption.Split (new[] { " to " }, StringSplitOptions.None);
- return doc
- .First (n => (AttrEq (n, "MemberName", "op_Explicit") || AttrEq (n, "MemberName", "op_Implicit"))
- && ((string)n.Element ("ReturnValue").Element ("ReturnType")).Equals (convArgs[1], StringComparison.Ordinal)
- && AttrEq (n.Element ("Parameters").Element ("Parameter"), "Type", convArgs[0]));
- } else {
- return doc.First (m => AttrEq (m, "MemberName", "op_" + caption));
- }
- }
-
- TryParseCaption (caption, out name, out args);
-
- if (!string.IsNullOrEmpty (name)) { // Filter member by name
- var prematched = prematchedMembers[name];
- doc = prematched.Any () ? prematched : doc.Where (m => AttrEq (m, "MemberName", name));
- }
- if (args != null && args.Count > 0) // Filter member by its argument list
- doc = doc.Where (m => m.Element ("Parameters").Elements ("Parameter").Attributes ("Type").Select (a => (string)a).SequenceEqual (args));
-
- return doc.First ();
- }
-
- XElement GetDocsFromCaption (XDocument xdoc, string caption, bool isOperator, ILookup<string, XElement> prematchedMembers)
- {
- return GetMemberFromCaption (xdoc, caption, isOperator, prematchedMembers).Element ("Docs");
- }
-
- // A simple stack-based parser to detect single type definition separated by commas
- IEnumerable<string> ExtractArguments (string rawArgList)
- {
- var sb = new System.Text.StringBuilder ();
- int genericDepth = 0;
- int arrayDepth = 0;
-
- for (int i = 0; i < rawArgList.Length; i++) {
- char c = rawArgList[i];
- switch (c) {
- case ',':
- if (genericDepth == 0 && arrayDepth == 0) {
- yield return sb.ToString ();
- sb.Clear ();
- continue;
- }
- break;
- case '<':
- genericDepth++;
- break;
- case '>':
- genericDepth--;
- break;
- case '[':
- arrayDepth++;
- break;
- case ']':
- arrayDepth--;
- break;
- }
- sb.Append (c);
- }
- if (sb.Length > 0)
- yield return sb.ToString ();
- }
-
- void TryParseCaption (string caption, out string name, out IList<string> argList)
- {
- name = null;
- argList = null;
- int parenIdx = caption.IndexOf ('(');
- // In case of simple name, there is no need for processing
- if (parenIdx == -1) {
- name = caption;
- return;
- }
- name = caption.Substring (0, parenIdx);
- // Now we gather the argument list if there is any
- var rawArgList = caption.Substring (parenIdx + 1, caption.Length - parenIdx - 2); // Only take what's inside the parens
- if (string.IsNullOrEmpty (rawArgList))
- return;
-
- argList = ExtractArguments (rawArgList).Select (arg => arg.Trim ()).ToList ();
- }
-
- bool AttrEq (XElement element, string attributeName, string expectedValue)
- {
- return ((string)element.Attribute (attributeName)).Equals (expectedValue, StringComparison.Ordinal);
- }
- }
-}
+++ /dev/null
-//
-// The ecmaspec provider is for ECMA specifications
-//
-// Authors:
-// John Luke (jluke@cfl.rr.com)
-// Ben Maurer (bmaurer@users.sourceforge.net)
-//
-// Use like this:
-// mono assembler.exe --ecmaspec DIRECTORY --out name
-//
-
-using System;
-using System.Diagnostics;
-using System.IO;
-using System.Text;
-using System.Xml.XPath;
-using System.Xml.Xsl;
-using System.Xml;
-using System.Collections.Generic;
-using Lucene.Net.Index;
-using Lucene.Net.Documents;
-
-namespace MonkeyDoc.Providers
-{
- public class EcmaSpecProvider : Provider
- {
- string basedir;
-
- public EcmaSpecProvider (string base_directory)
- {
- basedir = base_directory;
- if (!Directory.Exists (basedir))
- throw new DirectoryNotFoundException (String.Format ("The directory `{0}' does not exist", basedir));
- }
-
- public override void PopulateTree (Tree tree)
- {
- XPathNavigator n = new XPathDocument (Path.Combine (basedir, "toc.xml")).CreateNavigator ();
- n.MoveToRoot ();
- n.MoveToFirstChild ();
- PopulateNode (n.SelectChildren ("node", ""), tree.RootNode);
- }
-
- void PopulateNode (XPathNodeIterator nodes, Node treeNode)
- {
- foreach (XPathNavigator n in nodes) {
- string secNumber = n.GetAttribute ("number", "");
- string secName = n.GetAttribute ("name", "");
-
- var storage = treeNode.Tree.HelpSource.Storage;
- using (var file = File.OpenRead (Path.Combine (basedir, secNumber + ".xml")))
- storage.Store (secNumber, file);
-
- Node thisNode = treeNode.GetOrCreateNode (secNumber + ": " + secName, "ecmaspec:" + secNumber);
-
- if (n.HasChildren)
- PopulateNode (n.SelectChildren ("node", ""), thisNode);
- }
- }
-
- public override void CloseTree (HelpSource hs, Tree tree)
- {
- }
- }
-
- public class EcmaSpecHelpSource : HelpSource
- {
- const string EcmaspecPrefix = "ecmaspec:";
- const string TocPart = "%toc"; // What is returned as TocXml
- const string SpecPart = "%spec"; // What is returned as Ecmaspec
-
- public EcmaSpecHelpSource (string base_file, bool create) : base (base_file, create)
- {
- }
-
- public override DocumentType GetDocumentTypeForId (string id, out Dictionary<string, string> extraParams)
- {
- extraParams = null;
- return id.EndsWith (TocPart) ? DocumentType.TocXml : DocumentType.EcmaSpecXml;
- }
-
- public override bool IsGeneratedContent (string id)
- {
- return id == "root:" || id.EndsWith (TocPart);
- }
-
- public override bool IsMultiPart (string id, out IEnumerable<string> parts)
- {
- if (id == "root:" || id.EndsWith (TocPart) || id.EndsWith (SpecPart)) {
- parts = null;
- return false;
- }
- parts = MakeMultiPart (id);
- return true;
- }
-
- IEnumerable<string> MakeMultiPart (string baseId)
- {
- yield return baseId + SpecPart;
- yield return baseId + TocPart;
- }
-
- public override string GetText (string id)
- {
- Node n = id == "root:" ? Tree.RootNode : MatchNode (EcmaspecPrefix + id.Substring (0, id.Length - TocPart.Length));
- if (n == null)
- throw new ArgumentException ("id", string.Format ("{0} -> {1}", id, EcmaspecPrefix + id.Substring (0, id.Length - TocPart.Length)));
- return TreeDumper.ExportToTocXml (n, "C# Language Specification", "In this section:");
- }
-
- public override Stream GetHelpStream (string id)
- {
- return id.EndsWith (SpecPart) ? base.GetHelpStream (id.Substring (0, id.IndexOf (SpecPart))) : base.GetHelpStream (id);
- }
-
- public override void PopulateSearchableIndex (IndexWriter writer)
- {
- foreach (Node n in Tree.RootNode.Nodes)
- AddDocuments (writer, n);
- }
-
- protected override string UriPrefix {
- get {
- return EcmaspecPrefix;
- }
- }
-
- void AddDocuments (IndexWriter writer, Node node)
- {
- string url = node.PublicUrl;
- Stream file_stream = GetHelpStream (url.Substring (9));
- if (file_stream == null) //Error
- return;
- XmlDocument xdoc = new XmlDocument ();
- xdoc.Load (new XmlTextReader (file_stream));
-
- //Obtain the title
- XmlNode nelem = xdoc.DocumentElement;
- string title = nelem.Attributes["number"].Value + ": " + nelem.Attributes["title"].Value;
-
- //Obtain the text
- StringBuilder s = new StringBuilder ();
- GetTextNode (nelem, s);
- string text = s.ToString ();
-
- //Obtain the examples
- StringBuilder s2 = new StringBuilder ();
- GetExamples (nelem, s2);
- string examples = s2.ToString ();
-
- //Write to the Lucene Index all the parts
- SearchableDocument doc = new SearchableDocument ();
- doc.Title = title;
- doc.HotText = title.Substring (title.IndexOf (':'));
- doc.Url = url;
- doc.Text = text;
- doc.Examples = examples;
- writer.AddDocument (doc.LuceneDoc);
-
- if (node.IsLeaf)
- return;
-
- foreach (Node n in node.Nodes)
- AddDocuments (writer, n);
- }
-
- void GetTextNode (XmlNode n, StringBuilder s)
- {
- //dont include c# code
- if (n.Name == "code_example")
- return;
- //include all text from nodes
- if (n.NodeType == XmlNodeType.Text)
- s.Append (n.Value);
-
- //recursively explore all nodes
- if (n.HasChildNodes)
- foreach (XmlNode n_child in n.ChildNodes)
- GetTextNode (n_child, s);
- }
-
- void GetExamples (XmlNode n, StringBuilder s)
- {
- if (n.Name == "code_example") {
- if (n.FirstChild.Name == "#cdata-section")
- s.Append (n.FirstChild.Value);
- } else {
- if (n.HasChildNodes)
- foreach (XmlNode n_child in n.ChildNodes)
- GetExamples (n_child, s);
- }
- }
- }
-}
+++ /dev/null
-using System;
-using System.Linq;
-using System.IO;
-using System.Text;
-using System.Xml;
-using System.Xml.Linq;
-using System.Collections.Generic;
-
-using Lucene.Net.Index;
-using Lucene.Net.Documents;
-
-using MonkeyDoc.Ecma;
-using MonkeyDoc.Storage;
-using Mono.Utilities;
-
-namespace MonkeyDoc.Providers
-{
- public class EcmaUncompiledHelpSource : EcmaHelpSource
- {
- readonly DirectoryInfo basedir;
- readonly string basedoc;
-
- public readonly string BasePath;
-
- public new string Name {
- get;
- private set;
- }
-
- /* base_file: the directory containing the index.xml file, usually in Mono land .../Documentation/en
- * markName: if true, we encase the node caption with [] to clearly mark it's from an uncompiled source
- */
- public EcmaUncompiledHelpSource (string base_file, bool markName = true) : base ()
- {
- basedir = new DirectoryInfo (base_file);
- BasePath = basedir.FullName;
-
- basedoc = Path.Combine (basedir.FullName, "index.xml");
-
- Name = ((string)XDocument.Load (basedoc).Root.Element ("Title")) ?? "UnnamedUncompiledSource";
- if (markName)
- Name = '[' + Name + ']';
- Tree.RootNode.Caption = Name;
-
- Func<XElement, string> indexGenerator = type => {
- var nsName = (string)type.Parent.Attribute ("Name");
- var typeName = (string)type.Attribute ("Name");
- return Path.ChangeExtension (nsName + '/' + typeName, ".xml");
- };
-
- this.Storage = new UncompiledDocStorage (BasePath);
-
- EcmaDoc.PopulateTreeFromIndexFile (basedoc, Tree, null, null);
- }
-
- protected override string UriPrefix {
- get {
- return "uncompiled:";
- }
- }
- }
-}
+++ /dev/null
-//
-// error-provider.cs
-//
-// Author:
-// Ben Maurer (bmaurer@users.sourceforge.net)
-//
-// (C) 2003 Ben Maurer
-// Copyright 2003-2011 Novell
-// Copyright 2011 Xamarin Inc
-//
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Text;
-using System.Xml;
-using System.Xml.Serialization;
-using System.Linq;
-using Lucene.Net.Index;
-using Lucene.Net.Documents;
-
-namespace MonkeyDoc.Providers
-{
- public class ErrorProviderConfig
- {
- public string FilesPath;
- public string Match;
- public int ErrorNumSubstringStart;
- public int ErrorNumSubstringLength;
- public string FriendlyFormatString;
-
- public override string ToString ()
- {
- var sb = new StringBuilder ();
- var w = new StringWriter (sb);
-
- w.WriteLine ("FilesPath: {0}", FilesPath);
- w.WriteLine ("Match: {0}", Match);
- w.WriteLine ("Error Number Substring: {0} Length:{1}", ErrorNumSubstringStart, ErrorNumSubstringLength);
- w.WriteLine ("FriendlyFormatString: {0}", FriendlyFormatString);
-
- return w.ToString ();
- }
-
- public Dictionary<string, ErrorDocumentation> Compile (HelpSource hs)
- {
- string[] files = Directory.GetFiles (FilesPath, Match);
- var ret = new Dictionary<string, ErrorDocumentation> ();
-
- foreach (string s in files) {
- ErrorDocumentation d;
- int errorNum = 0;
-
- try {
- errorNum = int.Parse (Path.GetFileName (s).Substring (ErrorNumSubstringStart, ErrorNumSubstringLength));
- } catch {
- Console.WriteLine ("Ignoring file {0}", s);
- }
-
- string errorName = String.Format (FriendlyFormatString, errorNum);
-
- if (!ret.TryGetValue (errorName, out d))
- ret[errorName] = d = new ErrorDocumentation (errorName);
-
- if (d.Details == null) {
- string xmlFile = Path.ChangeExtension (s, "xml");
- if (File.Exists (xmlFile)) {
- XmlSerializer cfgRdr = new XmlSerializer (typeof (ErrorDetails));
- d.Details = (ErrorDetails)cfgRdr.Deserialize (new XmlTextReader (xmlFile));
- }
- }
- // Encoding is same as used in MCS, so we will be able to do all those files
- using (StreamReader reader = new StreamReader (s, Encoding.GetEncoding (28591))) {
- d.Examples.Add (reader.ReadToEnd ());
- }
- }
-
- return ret;
- }
- }
-
- public class ErrorDocumentation
- {
- public string ErrorName;
- public ErrorDetails Details;
- public List<string> Examples = new List<string> ();
-
- public ErrorDocumentation () {}
- public ErrorDocumentation (string ErrorName)
- {
- this.ErrorName = ErrorName;
- }
- }
-
- public class ErrorDetails
- {
- public XmlNode Summary;
- public XmlNode Details;
- }
-
- public class ErrorProvider : Provider
- {
- ErrorProviderConfig config;
-
- public ErrorProvider (string configFile)
- {
- config = ReadConfig (configFile);
- }
-
- public static ErrorProviderConfig ReadConfig (string file)
- {
- XmlSerializer cfgRdr = new XmlSerializer (typeof (ErrorProviderConfig));
- ErrorProviderConfig ret = (ErrorProviderConfig)cfgRdr.Deserialize (new XmlTextReader (file));
- // handle path rel to the config file
- ret.FilesPath = Path.Combine (Path.GetDirectoryName (file), ret.FilesPath);
- return ret;
- }
-
- public override void PopulateTree (Tree tree)
- {
- // everything is done in CloseTree so we can pack
- }
-
- public override void CloseTree (HelpSource hs, Tree tree)
- {
- var entries = config.Compile (hs);
- MemoryStream ms = new MemoryStream ();
- XmlSerializer writer = new XmlSerializer (typeof (ErrorDocumentation));
-
- foreach (var de in entries) {
- ErrorDocumentation d = de.Value;
- string s = de.Key;
-
- tree.RootNode.GetOrCreateNode (s, "error:" + s);
-
- writer.Serialize (ms, d);
- ms.Position = 0;
- hs.Storage.Store (s, ms);
- ms.SetLength (0);
- }
-
- tree.RootNode.Sort ();
- }
- }
-
- public class ErrorHelpSource : HelpSource
- {
- public ErrorHelpSource (string base_file, bool create) : base (base_file, create)
- {
- }
-
- public override string GetText (string id)
- {
- return TreeDumper.ExportToTocXml (Tree.RootNode, "Compiler Error Reference", "In this section:");
- }
-
- protected override string UriPrefix {
- get {
- return "error:";
- }
- }
-
- public override bool IsGeneratedContent (string id)
- {
- return id == "root:";
- }
-
- public override DocumentType GetDocumentTypeForId (string id, out Dictionary<string, string> extraParams)
- {
- extraParams = null;
- return id == "root:" ? DocumentType.TocXml : DocumentType.ErrorXml;
- }
-
- public override string GetInternalIdForUrl (string url, out Node node)
- {
- var result = base.GetInternalIdForUrl (url, out node);
- return result.ToLower ();
- }
-
- public override void PopulateIndex (IndexMaker index_maker)
- {
- foreach (Node n in Tree.RootNode.Nodes)
- index_maker.Add (n.Caption, n.Caption, n.Element);
- }
-
- public override void PopulateSearchableIndex (IndexWriter writer)
- {
- foreach (Node n in Tree.RootNode.Nodes) {
- XmlSerializer reader = new XmlSerializer (typeof (ErrorDocumentation));
- ErrorDocumentation d = (ErrorDocumentation)reader.Deserialize (GetHelpStream (n.Element.Substring (6)));
- SearchableDocument doc = new SearchableDocument ();
- doc.Title = d.ErrorName;
- doc.Url = n.Element;
- doc.Text = d.Details != null ? d.Details.ToString () : string.Empty;
- doc.Examples = d.Examples.Cast<string> ().Aggregate ((e1, e2) => e1 + Environment.NewLine + e2);
- doc.HotText = d.ErrorName;
- writer.AddDocument (doc.LuceneDoc);
- }
- }
- }
-}
+++ /dev/null
-//
-// A provider to display man pages
-//
-// Authors:
-// Johannes Roith <johannes@roith.de>
-// Jonathan Pryor <jpryor@novell.com>
-//
-// (C) 2008 Novell, Inc.
-
-using System;
-using System.IO;
-using System.Text;
-using System.Xml;
-using System.Linq;
-using System.Collections.Generic;
-
-namespace MonkeyDoc.Providers
-{
- public class ManProvider : Provider
- {
- string[] tocFiles;
-
- public ManProvider (string[] handbookTocFiles)
- {
- tocFiles = handbookTocFiles;
-
- // huh...
- if (!File.Exists (tocFiles[0]))
- throw new FileNotFoundException (String.Format ("The table of contents, `{0}' does not exist", tocFiles[0]));
- }
-
- public override void PopulateTree (Tree tree)
- {
- foreach(string TocFile in tocFiles) {
- XmlDocument doc = new XmlDocument();
- doc.Load (TocFile);
-
- XmlNodeList nodeList = doc.GetElementsByTagName("manpage");
- Node nodeToAddChildrenTo = tree.RootNode;
- var storage = nodeToAddChildrenTo.Tree.HelpSource.Storage;
-
- foreach (XmlNode node in nodeList) {
-
- XmlAttribute name = node.Attributes["name"];
- XmlAttribute page = node.Attributes["page"];
-
- if (name == null || page == null) continue;
-
- if (!File.Exists (page.Value))
- continue;
-
- string target = "man:" + name.Value;
- nodeToAddChildrenTo.CreateNode (name.Value, target);
-
- if (File.Exists (page.Value))
- storage.Store (name.Value, File.OpenRead (page.Value));
- }
- }
- }
-
- public override void CloseTree (HelpSource hs, Tree tree)
- {
- }
- }
-
- public class ManHelpSource : HelpSource
- {
- const string ManPrefix = "man:";
- Dictionary<string, Node> nodesMap;
-
- public ManHelpSource (string base_file, bool create) : base (base_file, create)
- {
- nodesMap = Tree.RootNode.Nodes.ToDictionary (n => n.Element);
- }
-
- // Since man always has a flat tree and rather small amount of item
- // we store them in a dictionary
- public override Node MatchNode (string url)
- {
- Node result;
- return nodesMap.TryGetValue (url, out result) ? result : null;
- }
-
- public override DocumentType GetDocumentTypeForId (string id, out Dictionary<string, string> extraParams)
- {
- extraParams = null;
- return id == "root:" ? DocumentType.TocXml : DocumentType.Man;
- }
-
- public override bool IsGeneratedContent (string id)
- {
- return id == "root:";
- }
-
- public override string GetText (string url)
- {
- return TreeDumper.ExportToTocXml (Tree.RootNode, "Mono Documentation Library", "Available man pages:");
- }
-
- protected override string UriPrefix {
- get {
- return ManPrefix;
- }
- }
- }
-}
+++ /dev/null
-//
-// The simple provider is an example provider
-//
-// Author:
-// Miguel de Icaza (miguel@ximian.com)
-//
-// Use like this:
-// mono assembler.exe --simple DIRECTORY --out name
-//
-// Then create a .source file in your sources directory, and copy
-// name.tree and name.zip to the sources directory.
-//
-// To view the tree generated, use:
-// mono dump.exe name.tree
-//
-namespace Monodoc {
-using System;
-using System.IO;
-using System.Text;
-
-//
-// The simple provider generates the information source
-//
-public class SimpleProvider : Provider {
- string basedir;
-
- public SimpleProvider (string base_directory)
- {
- basedir = base_directory;
- if (!Directory.Exists (basedir))
- throw new FileNotFoundException (String.Format ("The directory `{0}' does not exist", basedir));
- }
-
- public override void PopulateTree (Tree tree)
- {
- Node top = tree.LookupNode ("Directory at: " + basedir, "simple:");
-
- foreach (string dir in Directory.GetDirectories (basedir)){
- string url = Path.GetFileName (dir);
- Node n = top.LookupNode ("Dir: " + url, url);
- PopulateDir (n, dir);
- }
- }
-
-#pragma warning disable 219
- void PopulateDir (Node me, string dir)
- {
- Console.WriteLine ("Adding: " + dir);
- foreach (string child_dir in Directory.GetDirectories (dir)){
- string url = Path.GetFileName (child_dir);
- Node n = me.LookupNode ("Dir: " + url, "simple-directory:" + url);
- PopulateDir (me, child_dir);
- }
-
- foreach (string file in Directory.GetFiles (dir)){
- Console.WriteLine (" File: " + file);
- string file_code = me.tree.HelpSource.PackFile (file);
-
- //
- // The url element encoded for the file is:
- // originalfilename#CODE
- //
- // The code is assigned to us after the file has been packaged
- // We use the original-filename later to render html or text files
- //
- Node n = me.LookupNode (Path.GetFileName (file), file + "#" + file_code);
-
- }
- }
-
- public override void CloseTree (HelpSource hs, Tree tree)
- {
- }
-}
-
-//
-// The HelpSource is used during the rendering phase.
-//
-
-public class SimpleHelpSource : HelpSource {
- Encoding enc;
-
- public SimpleHelpSource (string base_file, bool create) : base (base_file, create)
- {
- enc = new UTF8Encoding (false, false);
- }
-
- public override string GetText (string url, out Node match_node)
- {
- match_node = null;
-
- string c = GetCachedText (url);
- if (c != null)
- return c;
-
- if (url.StartsWith ("simple:") || url.StartsWith ("simple-directory:"))
- return GetTextFromUrl (url);
-
- return null;
- }
-
- string GetTextFromUrl (string url)
- {
- // Remove "simple:" prefix
- url = url.Substring (7);
-
- if (url.StartsWith ("simple-directory:"))
- return String.Format ("<html>This is a directory entry point: {0} </html>",
- url.Substring (17));
-
- // Otherwise the last element of the url is the file code we got.
- int pound = url.LastIndexOf ("#");
- string code;
- if (pound == -1)
- code = url;
- else
- code = url.Substring (pound+1);
-
-
- Stream s = GetHelpStream (code);
- if (s == null)
- return String.Format ("<html>No stream for this node: {0} </html>", url);
-
- //
- // Now, get the file type
- //
- int slash = url.LastIndexOf ("/");
- string fname = url.Substring (slash + 1, pound - slash - 1).ToLower ();
-
- if (fname.EndsWith (".html") || fname.EndsWith (".htm")){
- TextReader r = new StreamReader (s, enc);
- return r.ReadToEnd ();
- }
-
- if (fname.EndsWith (".png") || fname.EndsWith (".jpg") ||
- fname.EndsWith (".jpeg") || fname.EndsWith (".gif")){
- return "<html>Image file, have not implemented rendering this yet</html>";
- }
-
- // Convert text to HTML
- StringBuilder result = new StringBuilder ("<html>");
- TextReader reader = new StreamReader (s, enc);
- string line;
-
- while ((line = reader.ReadLine ()) != null){
- result.Append (line);
- result.Append ("<br>");
- }
- result.Append ("<html>");
- return result.ToString ();
- }
-}
-}
+++ /dev/null
-//
-// A provider that uses Windows help file xhtml TOC files and looks for the
-// referenced documents to create the help source.
-//
-// Authors:
-// Copyright 2003 Lee Mallabone <gnome@fonicmonkey.net>
-// Johannes Roith <johannes@roith.de>
-// Miguel de Icaza <miguel@ximian.com>
-
-using System;
-using System.IO;
-using System.Collections.Generic;
-using System.Text;
-using System.Text.RegularExpressions;
-using System.Xml;
-
-namespace MonkeyDoc.Providers
-{
- public class XhtmlProvider : Provider
- {
- string tocFile;
-
- public XhtmlProvider (string handbookTocFile)
- {
- tocFile = handbookTocFile;
- if (!File.Exists (tocFile))
- throw new FileNotFoundException (String.Format ("The table of contents, `{0}' does not exist", tocFile));
- }
-
- public override void PopulateTree (Tree tree)
- {
- //new SimpleHandbookTOCParser(tree, tocFile);
- // TODO: port it
- }
-
- public override void CloseTree (HelpSource hs, Tree tree)
- {
- }
- }
-
- public class XhtmlHelpSource : HelpSource
- {
- public XhtmlHelpSource (string base_file, bool create) : base (base_file, create)
- {
-
- }
-
- const string XhtmlPrefix = "xhtml:";
-
- protected override string UriPrefix {
- get {
- return XhtmlPrefix;
- }
- }
-
- public override DocumentType GetDocumentTypeForId (string id, out Dictionary<string, string> extraArgs)
- {
- extraArgs = null;
- return id == "root:" ? DocumentType.TocXml : DocumentType.MonoBook;
- }
-
- public override bool IsGeneratedContent (string id)
- {
- return id == "root:";
- }
-
- public override string GetText (string url)
- {
- return TreeDumper.ExportToTocXml (Tree.RootNode, "Mono Handbook", string.Empty);
- }
-
- public static string GetAbsoluteLink(string target, string url)
- {
-
- string value = null;
-
- if (target.StartsWith ("#") ||
- target.StartsWith ("T:") ||
- target.StartsWith ("M:") ||
- target.StartsWith ("P:") ||
- target.StartsWith ("T:") ||
- target.StartsWith ("E:") ||
- target.StartsWith ("F:") ||
- target.StartsWith ("O:") ||
- target.StartsWith ("N:") ||
- target.StartsWith ("api:"))
- return null;
-
- int endp = target.IndexOf(':');
-
- if (endp == -1)
- endp = 0;
- string protocol = target.Substring(0, endp);
- switch (protocol) {
- case "mailto":
- case "http":
- case "https":
- case "ftp":
- case "news":
- case "irc":
- break;
- default:
- // handle absolute urls like: /html/en/images/empty.png
- if (!target.StartsWith("/")) {
-
- // url is something like "gnome/bindings/mono.html"
- // This will get the path "gnome/bindings"
-
- int slash = url.LastIndexOf ("/");
- string tmpurl = url;
-
- if (slash != -1)
- tmpurl = url.Substring(0, slash);
-
- // Count "../" in target and go one level down
- // for each in tmpurl, eventually, then remove "../".
-
- Regex reg1 = new Regex("../");
- MatchCollection matches = reg1.Matches(target);
-
- for(int i = 1; i < matches.Count; i++) {
- slash = tmpurl.LastIndexOf ("/");
- if (slash != -1)
- tmpurl = tmpurl.Substring(0, slash);
- }
-
- target = target.Replace("../", "");
-
- value = tmpurl + "/" + target;
-
- } else {
- value = target.Substring(1, target.Length - 1);
- }
- break;
- }
- return value;
- }
-
- XmlDocument RewriteLinks(XmlDocument docToProcess, string url)
- {
- XmlNodeList nodeList = docToProcess.GetElementsByTagName("a");
-
- foreach(XmlNode node in nodeList) {
-
- XmlElement element = (XmlElement) node;
-
- if (element.HasAttribute("href") ){
-
- XmlAttribute href = element.GetAttributeNode("href");
- string target = href.Value;
-
- target = GetAbsoluteLink(target, url);
- if (target != null) {
- string newtarget = String.Format ("source-id:{0}:xhtml:{1}", SourceID, target);
- href.Value = newtarget;
- }
- }
- }
-
- nodeList = docToProcess.GetElementsByTagName("img");
-
- foreach(XmlNode node in nodeList) {
-
- XmlElement element = (XmlElement) node;
-
- if (element.HasAttribute("src") ){
-
- XmlAttribute href = element.GetAttributeNode("src");
- string target = href.Value;
-
- target = GetAbsoluteLink(target, url);
- if (target != null) {
- string newtarget = String.Format ("source-id:{0}:xhtml:{1}", SourceID, target);
- href.Value = newtarget;
- }
- }
- }
-
- return docToProcess;
- }
-
- public override void PopulateIndex (IndexMaker index_maker)
- {
- PopulateIndexFromNodes (Tree.RootNode);
- }
-
- void PopulateIndexFromNodes (Node start)
- {
- /*var nodes = start.Nodes;
-
- if (nodes != null) {
- foreach (Node n in nodes)
- PopulateIndexFromNodes (n);
- }*/
- }
- }
-}
+++ /dev/null
-using System;
-using System.Configuration;
-using System.Collections.Specialized;
-
-namespace MonkeyDoc
-{
- public static class Settings
- {
- static KeyValueConfigurationCollection libConfig;
- static KeyValueConfigurationCollection exeConfig;
-
- static Settings ()
- {
- try {
- var config = ConfigurationManager.OpenExeConfiguration (System.Reflection.Assembly.GetExecutingAssembly ().Location);
- libConfig = config.AppSettings.Settings;
- } catch {}
-
- try {
- exeConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).AppSettings.Settings;
- } catch {}
- }
-
- public static string Get (string key) {
- KeyValueConfigurationElement element = null;
- // We check the configuration in order: app first and then library itself
- if (exeConfig != null)
- element = exeConfig[key];
- if (element == null && libConfig != null)
- element = libConfig[key];
-
- return element == null ? null : element.Value;
- }
-
- public static KeyValueConfigurationCollection AppSettings {
- get {
- return exeConfig;
- }
- }
-
- public static KeyValueConfigurationCollection LibSettings {
- get {
- return libConfig;
- }
- }
- }
-}
+++ /dev/null
-using System;
-using System.IO;
-using System.Collections.Generic;
-
-namespace MonkeyDoc
-{
- // Define a storage mechanism for a help source
- public interface IDocStorage : IDisposable
- {
- // Tell if the storage can store successive change to the doc as revision
- bool SupportRevision { get; }
- IDocRevisionManager RevisionManager { get; }
-
- // Tell if the storage support modifying an existing data
- bool SupportChange { get; }
-
- /* Store data inside the storage backend
- * if SupportChange is false and user try to store something with an existing id
- * an exception will be thrown
- * if id is null or empty, the storage will try to create an automatic id. In all
- * case the id that has been used to store the content is returned by the method
- */
- string Store (string id, string text);
- string Store (string id, byte[] data);
- string Store (string id, Stream stream);
-
- Stream Retrieve (string id);
-
- IEnumerable<string> GetAvailableIds ();
- }
-
- public interface IDocRevisionManager
- {
- Stream RetrieveWithRevision (string id, string revision);
-
- // This should be ordered by most recent first
- IEnumerable<string> AvailableRevisionsForId (string id);
- // This can simply be implemented with above property but it can also be
- // a revision storage symbolic value like "HEAD"
- string LatestRevisionForId (string id);
-
- // A commit message for instance
- string GetRevisionDescription (string revision);
- }
-
- public static class DocRevisionManagerExtensions
- {
- public static Stream RetrieveLatestRevision (this IDocRevisionManager revManager, string id)
- {
- return revManager.RetrieveWithRevision (id, revManager.LatestRevisionForId (id));
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-using System;
-using System.IO;
-using System.Xml;
-using System.Linq;
-using System.Collections.Generic;
-
-namespace MonkeyDoc.Storage
-{
- // A storage that doesn't store
- public class NullStorage : IDocStorage
- {
- public NullStorage ()
- {
- }
-
- public bool SupportRevision {
- get {
- return false;
- }
- }
-
- public IDocRevisionManager RevisionManager {
- get {
- return null;
- }
- }
-
- public bool SupportChange {
- get {
- return true;
- }
- }
-
- public string Store (string id, string text)
- {
- return id;
- }
-
- public string Store (string id, byte[] data)
- {
- return id;
- }
-
- public string Store (string id, Stream stream)
- {
- return id;
- }
-
- public Stream Retrieve (string id)
- {
- return null;
- }
-
- public IEnumerable<string> GetAvailableIds ()
- {
- return Enumerable.Empty<string> ();
- }
-
- public void Dispose ()
- {
- }
- }
-}
+++ /dev/null
-using System;
-using System.IO;
-using System.Linq;
-using System.Collections.Generic;
-
-namespace MonkeyDoc.Storage
-{
- // A read-only storage to access ecma XML document based on a standard directory layout
- // id are relative path inside the base doc directory
- public class UncompiledDocStorage : IDocStorage
- {
- readonly string basePath;
-
- public UncompiledDocStorage (string basePath)
- {
- this.basePath = basePath;
- }
-
- public bool SupportRevision {
- get {
- return false;
- }
- }
-
- public IDocRevisionManager RevisionManager {
- get {
- return null;
- }
- }
-
- public bool SupportChange {
- get {
- return false;
- }
- }
-
- public string Store (string id, string text)
- {
- throw new NotSupportedException ();
- }
-
- public string Store (string id, byte[] data)
- {
- throw new NotSupportedException ();
- }
-
- public string Store (string id, Stream stream)
- {
- throw new NotSupportedException ();
- }
-
- public Stream Retrieve (string id)
- {
- var path = id;
- if ('/' != Path.DirectorySeparatorChar)
- path = path.Replace ('/', Path.DirectorySeparatorChar);
- return File.OpenRead (Path.Combine (basePath, path));
- }
-
- public IEnumerable<string> GetAvailableIds ()
- {
- return Directory.EnumerateFiles (basePath, "*.xml", SearchOption.AllDirectories);
- }
-
- public void Dispose ()
- {
- }
- }
-}
+++ /dev/null
-using System;
-using System.IO;
-using System.Xml;
-using System.Linq;
-using System.Text;
-using System.Collections.Generic;
-
-using ICSharpCode.SharpZipLib.Zip;
-
-namespace MonkeyDoc.Storage
-{
- public class ZipStorage : IDocStorage
- {
- string zipFileName;
- int code;
- ZipOutputStream zipOutput;
- ZipFile zipFile;
- // SharpZipLib use linear search to map name to index, correct that a bit
- Dictionary<string, int> entries = new Dictionary<string, int> ();
-
- public ZipStorage (string zipFileName)
- {
- this.zipFileName = zipFileName;
- }
-
- public bool SupportRevision {
- get {
- return false;
- }
- }
-
- public IDocRevisionManager RevisionManager {
- get {
- return null;
- }
- }
-
- public bool SupportChange {
- get {
- return true;
- }
- }
-
- public string Store (string id, string text)
- {
- EnsureOutput ();
- SetupEntry (zipOutput, ref id);
- var writer = new StreamWriter (zipOutput);
- writer.Write (text);
- writer.Flush ();
-
- return id;
- }
-
- public string Store (string id, byte[] data)
- {
- EnsureOutput ();
- SetupEntry (zipOutput, ref id);
- zipOutput.Write (data, 0, data.Length);
- return id;
- }
-
- public string Store (string id, Stream stream)
- {
- EnsureOutput ();
- SetupEntry (zipOutput, ref id);
- stream.CopyTo (zipOutput);
- return id;
- }
-
- void SetupEntry (ZipOutputStream zipOutput, ref string id)
- {
- if (string.IsNullOrEmpty (id))
- id = GetNewCode ();
-
- ZipEntry entry = new ZipEntry (id);
- zipOutput.PutNextEntry (entry);
- }
-
- public Stream Retrieve (string id)
- {
- EnsureInput ();
- int index;
- ZipEntry entry;
- if (!entries.TryGetValue (id, out index) || (entry = zipFile[index]) == null)
- entry = zipFile.GetEntry (id);
- if (entry != null)
- return zipFile.GetInputStream (entry);
- else
- throw new ArgumentException ("id", string.Format ("'{0}' isn't a valid id for this storage", id));
- }
-
- public IEnumerable<string> GetAvailableIds ()
- {
- EnsureInput ();
- return zipFile.Cast<ZipEntry> ().Select (ze => ze.Name);
- }
-
- void EnsureOutput ()
- {
- if (zipFile != null)
- throw new InvalidOperationException ("This ZipStorage instance is already used in read-mode");
- if (zipOutput != null)
- return;
- zipOutput = new ZipOutputStream (File.Create (zipFileName));
- }
-
- void EnsureInput ()
- {
- if (zipOutput != null)
- throw new InvalidOperationException ("This ZipStorage instance is already used in write-mode");
- if (zipFile != null)
- return;
- zipFile = new ZipFile (zipFileName);
- entries = Enumerable.Range (0, zipFile.Size).ToDictionary (i => zipFile[i].Name, i => i);
- }
-
- public void Dispose ()
- {
- if (zipOutput != null)
- zipOutput.Dispose ();
- if (zipFile != null)
- zipFile.Close ();
- }
-
- string GetNewCode ()
- {
- return String.Format ("{0}", code++);
- }
- }
-}
\ No newline at end of file