예제 #1
0
        private static int ReportNotMappedItems(ConsoleArgs consoleArgs)
        {
            ChiliAutomation chili  = new ChiliAutomation();
            var             output = consoleArgs.Destination;

            if (!Path.IsPathRooted(output))
            {
                output = Path.Combine(Environment.CurrentDirectory, output);
            }

            return(chili.ReportNotMappedWikiFiles(output));
        }
예제 #2
0
        /// <summary>
        /// Publish items to chili page
        /// </summary>
        /// <param name="cmdArgs"></param>
        /// <returns></returns>
        private static int PublishChiliItems(ConsoleArgs cmdArgs)
        {
            var input = string.Empty;

            if (string.IsNullOrEmpty(cmdArgs.User))
            {
                WriteToOutput("Username not specified", OutputType.Error);
                return(3);
            }
            if (string.IsNullOrEmpty(cmdArgs.Password))
            {
                WriteToOutput("Password not specified", OutputType.Error);
                return(4);
            }
            if (string.IsNullOrEmpty(cmdArgs.Destination))
            {
                WriteToOutput("Input path not specified", OutputType.Error);
                return(6);
            }
            input = cmdArgs.Destination;
            if (!Path.IsPathRooted(input))
            {
                input = Path.Combine(Environment.CurrentDirectory, input);
            }

            var chili = new ChiliAutomation();

            if (!chili.Login(cmdArgs.User, cmdArgs.Password))
            {
                WriteToOutput("Login failed or maybe site down.", OutputType.Error);
                chili.logout();
                return(8);
            }

            chili.UpdateDocs(input);

            chili.logout();


            return(0);
        }
예제 #3
0
        public static int Main(string[] args)
        {
            var consoleArgs = new ConsoleArgs();
            var options = new OptionSet();
            options.Add("c|cmd=", "The command to run", v => { consoleArgs.Command = v; })
                .Add("s|src=", "The source for the command", v => { consoleArgs.Source = v; })
                .Add("o|out=", "The output destination for the command", v => { consoleArgs.Destination = v; })
                .Add("l|log=", "The log file to write any output to", v => { consoleArgs.LogFile = v; })
                .Add("u|usr="******"The username to use", v => { consoleArgs.User = v; })
                .Add("p|pwd=", "The password", v => { consoleArgs.Password = v; })
                .Add("xsd", "Generate XSD files", v => { consoleArgs.GenerateXsd = v != null; });
            options.Parse(args);

            var cmd = consoleArgs.Command ?? string.Empty;
            if (!string.IsNullOrEmpty(consoleArgs.LogFile))
            {
                var logPath = consoleArgs.LogFile;
                if (!Path.IsPathRooted(logPath))
                {
                    logPath = Path.Combine(Environment.CurrentDirectory, logPath);
                }

                if (File.Exists(logPath))
                {
                    File.Delete(logPath);
                }

                xmlLog = new StreamWriter(logPath);
                xmlLog.WriteLine("<docGen command=\"" + cmd + "\" time=\"" + DateTime.Now.ToString("o") + "\">");
            }

            var exitCode = 0;
            if (cmd.Length == 0)
            {
                WriteToOutput("No command specified", OutputType.Error);
                HelpScreen();
                exitCode = 11;
            }
            else
            {
                switch (cmd.ToLowerInvariant())
                {
                    case "generate":
                        exitCode = GenerateDocumentation(consoleArgs);
                        break;

                    case "list":
                        //exitCode = ListConfluenceItems(consoleArgs);
                        break;

                    case "publish":
                        exitCode = PublishChiliItems(consoleArgs);
                        break;

                    case "reportnotmapped":
                        exitCode = ReportNotMappedItems(consoleArgs);
                        break;

                    default:
                        exitCode = 1;
                        WriteToOutput("Unknown command: " + cmd, OutputType.Error);
                        HelpScreen();
                        break;
                }
            }

            if (xmlLog != null)
            {
                xmlLog.WriteLine("</docGen>");
                xmlLog.Close();
                xmlLog.Dispose();
            }

            // Pause when running within Visual Studio
            if (Debugger.IsAttached)
            {
                Console.WriteLine("Application finished, press any key to continue");
                Console.ReadKey(true);
            }

            return exitCode;
        }
예제 #4
0
        public static int GenerateDocumentation(ConsoleArgs args)
        {
            specialChars = new Regex(@"[\|\[\]\*+]", RegexOptions.Compiled);
            if (string.IsNullOrEmpty(args.Source))
            {
                WriteToOutput("No assembly specified", OutputType.Error);
                return 21;
            }

            var assemblyName = args.Source;
            if (!Path.IsPathRooted(assemblyName))
            {
                assemblyName = Path.Combine(Environment.CurrentDirectory, assemblyName);
            }

            if (!File.Exists(assemblyName))
            {
                WriteToOutput("Cannot find assembly: " + assemblyName, OutputType.Error);
                return 22;
            }

            var documentation = new XDocument();
            var documentationPath = Path.ChangeExtension(assemblyName, "xml");
            if (File.Exists(documentationPath))
            {
                documentation = XDocument.Load(documentationPath);
            }
            else
            {
                WriteToOutput("No XML file found for assembly: " + assemblyName, OutputType.Error);
                return 23;
            }

            var stopwatch = new Stopwatch();
            stopwatch.Start();
            WriteToOutput("Starting documentation generation for " + Path.GetFileName(assemblyName), OutputType.Info);
            problemList.Clear();

            if (args.GenerateXsd)
            {
                WriteToOutput("Generating XSD files", OutputType.Debug);
            }

            try
            {
                var baseFolder = Path.Combine(Environment.CurrentDirectory, DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture));
                if (!string.IsNullOrEmpty(args.Destination))
                {
                    baseFolder = args.Destination;
                    if (!Path.IsPathRooted(baseFolder))
                    {
                        baseFolder = Path.Combine(Environment.CurrentDirectory, baseFolder);
                    }
                }
                Debug.WriteLine("BaseFolder : " + baseFolder);

                Directory.CreateDirectory(baseFolder);
                var assembly = Assembly.LoadFrom(assemblyName);
                string versionNumber = null;
                var version = assembly.GetName().Version;
                if ((version.Major != 0) && (version.Minor != 0))
                {
                    versionNumber = version.ToString(4);
                }

                // Load the documentation for any dependencies
                LoadDependencyDocumentation(Path.GetDirectoryName(assemblyName), assembly, documentation);

                var publicTypesInAssembly = assembly.GetExportedTypes();
                foreach (var publicType in publicTypesInAssembly)
                {
                    var attributes = publicType.GetCustomAttributes(typeof(ReflectorTypeAttribute), true);
                    if (attributes.Length > 0)
                    {
                        Debug.WriteLine("Found reflector attributes in " + publicType.FullName);

                        // There can be only one!
                        var attribute = attributes[0] as ReflectorTypeAttribute;
                        var baseFileName = attribute.Name + ".wiki";
                        var fileName = Path.Combine(baseFolder, baseFileName);
                        WriteToOutput("Generating " + attribute.Name + ".wiki for " + publicType.FullName, OutputType.Info);
                        var itemStopwatch = new Stopwatch();
                        itemStopwatch.Start();


                        if (File.Exists(fileName))
                        {
                            //to be sure that the reflector type is unique in the entire ccnet solution!
                            throw new Exception(string.Format("Filename {0} coming from class {1} already exists", baseFileName, publicType.Name));
                        }

                        var typeElement = (from element in documentation.Descendants("member")
                                           where element.Attribute("name").Value == "T:" + publicType.FullName
                                           select element).SingleOrDefault();

                        if (typeElement == null)
                        {
                            WriteToOutput(
                                string.Format("XML docs for {0} are badly formed XML or missing.", publicType.FullName),
                                OutputType.Error);
                        }

                        var typeVersion = 1.0;

                        using (var output = new StreamWriter(fileName))
                        {
                            var elementName = attribute.Name;
                            if (HasTag(typeElement, "title"))
                            {
                                elementName = typeElement.Element("title").Value;
                            }

                            output.WriteLine("h1. " + elementName);
                            output.WriteLine();
                            if (HasTag(typeElement, "summary"))
                            {
                                WriteDocumentation(typeElement, "summary", output, documentation);
                                output.WriteLine();
                            }
                            else
                            {
                                problemList.Add("No Summary tag for " + publicType.FullName + " file " + fileName);
                            }

                            if (HasTag(typeElement, "version"))
                            {
                                output.WriteLine("h2. Version");
                                output.WriteLine();
                                output.Write("Available from version ");
                                WriteDocumentation(typeElement, "version", output, documentation);
                                output.WriteLine();
                                var versionEl = typeElement.Element("version");
                                double.TryParse(versionEl.Value, out typeVersion);
                            }

                            if (HasTag(typeElement, "example"))
                            {
                                output.WriteLine("h2. Examples");
                                output.WriteLine();
                                WriteDocumentation(typeElement, "example", output, documentation);
                                output.WriteLine();
                            }
                            else
                            {
                                if (publicType.IsClass)
                                {
                                    problemList.Add("No example tag for " + publicType.FullName + " file " + fileName);
                                }
                            }

                            output.WriteLine("h2. Configuration Elements");
                            output.WriteLine();
                            var elements = ListElements(publicType);
                            var keyElement = typeElement != null ? typeElement.Element("key") : null;
                            if ((elements.Count > 0) || (keyElement != null))
                            {
                                //Thoughtworks confluence style
                                //output.WriteLine("|| Element || Description || Type || Required || Default || Version ||");

                                // Redmine Style
                                // todo better header colors dodgerblue
                                output.WriteLine("| *Element* | *Description* | *Type* | *Required* | *Default* | *Version* |");
                                WriteElements(elements, output, documentation, typeElement, typeVersion);
                            }
                            else
                            {
                                output.WriteLine("There is no configuration for this plugin.");
                            }
                            output.WriteLine();

                            if (HasTag(typeElement, "remarks"))
                            {
                                output.WriteLine("h2. Notes");
                                output.WriteLine();
                                WriteDocumentation(typeElement, "remarks", output, documentation);
                                output.WriteLine();
                            }

                            //Thoughtworks confluence style                            
                            //output.WriteLine("{info:title=Automatically Generated}");

                            // Redmine Style
                            output.WriteLine();
                            output.WriteLine("h4. Automatically Generated");
                            output.WriteLine();

                            output.WriteLine(
                                "Documentation generated on " +
                                DateTime.Now.ToUniversalTime().ToString("dddd, d MMM yyyy", CultureInfo.InvariantCulture) +
                                " at " +
                                DateTime.Now.ToUniversalTime().ToString("h:mm:ss tt", CultureInfo.InvariantCulture));
                            if (versionNumber != null)
                            {
                                output.WriteLine("Using assembly version " + versionNumber);
                            }

                            //Thoughtworks confluence style                            
                            //output.WriteLine("{info}");

                            // Redmine Style




                            output.Flush();
                        }

                        itemStopwatch.Stop();
                        WriteToOutput(
                            "Documentation generated for " + attribute.Name + " in " + itemStopwatch.Elapsed.TotalSeconds.ToString("#,##0.00") + "s",
                            OutputType.Debug);

                        if (args.GenerateXsd)
                        {
                            itemStopwatch.Reset();
                            itemStopwatch.Start();
                            var xsdFile = Path.Combine(baseFolder, attribute.Name + ".xsd");
                            if (File.Exists(xsdFile))
                            {
                                File.Delete(xsdFile);
                            }

                            WriteXsdFile(xsdFile, attribute, publicType, documentation, typeElement);
                            WriteToOutput(
                                "XSD generated for " + attribute.Name + " in " + itemStopwatch.Elapsed.TotalSeconds.ToString("#,##0.00") + "s",
                                OutputType.Debug);
                        }
                    }
                }
            }
            catch (Exception error)
            {
                WriteToOutput(error.Message, OutputType.Error);
                return 24;
            }

            stopwatch.Stop();
            WriteToOutput(
                "Documentation generation finished in " + stopwatch.Elapsed.TotalSeconds.ToString("#,##0.00") + "s",
                OutputType.Info);

            if (problemList.Count > 0)
            {
                WriteToOutput("Problems encountered : " + problemList.Count.ToString(), OutputType.Warning);

                foreach (string s in problemList)
                {
                    WriteToOutput(s, OutputType.Warning);
                }
            }

            return 0;
        }
예제 #5
0
        /// <summary>
        /// Publish items to chili page
        /// </summary>
        /// <param name="cmdArgs"></param>
        /// <returns></returns>
        private static int PublishChiliItems(ConsoleArgs cmdArgs)
        {
            var input = string.Empty;
            if (string.IsNullOrEmpty(cmdArgs.User))
            {
                WriteToOutput("Username not specified", OutputType.Error);
                return 3;
            }
            if (string.IsNullOrEmpty(cmdArgs.Password))
            {
                WriteToOutput("Password not specified", OutputType.Error);
                return 4;
            }
            if (string.IsNullOrEmpty(cmdArgs.Destination))
            {
                WriteToOutput("Input path not specified", OutputType.Error);
                return 6;
            }
            input = cmdArgs.Destination;
            if (!Path.IsPathRooted(input))
            {
                input = Path.Combine(Environment.CurrentDirectory, input);
            }

            var chili = new ChiliAutomation();
            if (!chili.Login(cmdArgs.User, cmdArgs.Password))
            {
                WriteToOutput("Login failed or maybe site down.", OutputType.Error);
                chili.logout();
                return 8;
            }

            chili.UpdateDocs(input);

            chili.logout();


            return 0;
        }
예제 #6
0
        private static int ReportNotMappedItems(ConsoleArgs consoleArgs)
        {
            ChiliAutomation chili = new ChiliAutomation();
            var output = consoleArgs.Destination;
            if (!Path.IsPathRooted(output))
            {
                output = Path.Combine(Environment.CurrentDirectory, output);
            }

            return chili.ReportNotMappedWikiFiles(output);
        }
예제 #7
0
        /// <summary>
        /// Publishes items to Confluence.
        /// </summary>
        /// <param name="cmdArgs">The command-line args.</param>
        private static int PublishConfluenceItems(ConsoleArgs cmdArgs)
        {
            var exitCode = 0;
            var isValid = false;
            var input = string.Empty;
            if (string.IsNullOrEmpty(cmdArgs.Destination))
            {
                WriteToOutput("Confluence URL not specified", OutputType.Error);
                exitCode = 2;
            }
            else if (string.IsNullOrEmpty(cmdArgs.User))
            {
                WriteToOutput("Username not specified", OutputType.Error);
                exitCode = 3;
            }
            else if (string.IsNullOrEmpty(cmdArgs.Password))
            {
                WriteToOutput("Password not specified", OutputType.Error);
                exitCode = 4;
            }
            else if (string.IsNullOrEmpty(cmdArgs.Source))
            {
                WriteToOutput("Input path not specified", OutputType.Error);
                exitCode = 6;
            }
            else
            {
                isValid = true;
                input = cmdArgs.Source;
                if (!Path.IsPathRooted(input))
                {
                    input = Path.Combine(Environment.CurrentDirectory, input);
                }
            }

            if (isValid)
            {
                var stopwatch = new Stopwatch();
                stopwatch.Start();
                WriteToOutput("Publishing Confluence items to " + cmdArgs.Destination, OutputType.Info);
                var count = 0;

                try
                {
                    var binding = new BasicHttpBinding();
                    binding.MaxReceivedMessageSize = int.MaxValue;
                    binding.ReaderQuotas.MaxStringContentLength = int.MaxValue;
                    var client = new ConfluenceSoapServiceClient(
                        binding,
                        new EndpointAddress(cmdArgs.Destination));

                    try
                    {
                        var session = client.login(cmdArgs.User, cmdArgs.Password);
                        try
                        {
                            var inputDir = Path.GetDirectoryName(input);
                            var inputDoc = XDocument.Load(input);
                            foreach (var pageEl in inputDoc.Root.Elements())
                            {
                                var sourceAttr = pageEl.Attribute("source");
                                var titleAttr = pageEl.Attribute("title");
                                var idAttr = pageEl.Attribute("id");
                                var parentAttr = pageEl.Attribute("parentId");
                                if (sourceAttr != null)
                                {
                                    WriteToOutput(
                                        "Publishing " + sourceAttr.Value + " to " + titleAttr.Value + "[" + idAttr.Value + "]",
                                        OutputType.Debug);

                                    var sourcePath = sourceAttr.Value;
                                    if (!Path.IsPathRooted(sourcePath))
                                    {
                                        sourcePath = Path.Combine(inputDir, sourcePath);
                                    }

                                    if (!File.Exists(sourcePath))
                                    {
                                        WriteToOutput("Unable to find file " + sourcePath, OutputType.Warning);
                                    }
                                    else
                                    {
                                        var page = client.getPage(session, "CCNET", titleAttr.Value);
                                        var newContent = File.ReadAllText(sourcePath).Replace("\r\n", "\n");
                                        var autoPos = newContent.LastIndexOf("{info:title=Automatically Generated}");
                                        if (string.Compare(page.content, 0, newContent, 0, autoPos == -1 ? newContent.Length : autoPos) != 0)
                                        {
                                            page.content = newContent;
                                            client.storePage(session, page);
                                            WriteToOutput(titleAttr.Value + " updated", OutputType.Info);
                                            count++;
                                        }
                                        else
                                        {
                                            WriteToOutput(titleAttr.Value + " unchanged, publish skipped", OutputType.Debug);
                                        }
                                    }
                                }
                                else
                                {
                                    WriteToOutput(string.Format("Missing source attribute for {0}.", titleAttr),
                                                  OutputType.Warning);
                                }
                            }
                        }
                        finally
                        {
                            client.logout(session);
                        }
                    }
                    catch (Exception error)
                    {
                        WriteToOutput("ERROR: " + error.Message, OutputType.Error);
                        exitCode = 10;
                    }
                }
                catch (Exception error)
                {
                    WriteToOutput("ERROR: " + error.Message, OutputType.Error);
                    exitCode = 10;
                }

                stopwatch.Stop();
                WriteToOutput(
                    count.ToString() + " confluence items updated in " + stopwatch.Elapsed.TotalSeconds.ToString("#,##0.00") + "s",
                    OutputType.Info);
            }

            return exitCode;
        }
예제 #8
0
        /// <summary>
        /// Lists the confluence items.
        /// </summary>
        /// <param name="cmdArgs">The command-line args.</param>
        private static int ListConfluenceItems(ConsoleArgs cmdArgs)
        {
            var exitCode = 0;
            var isValid = false;
            var output = string.Empty;
            if (string.IsNullOrEmpty(cmdArgs.Source))
            {
                WriteToOutput("Confluence URL not specified", OutputType.Error);
                exitCode = 2;
            }
            else if (string.IsNullOrEmpty(cmdArgs.User))
            {
                WriteToOutput("Username not specified", OutputType.Error);
                exitCode = 3;
            }
            else if (string.IsNullOrEmpty(cmdArgs.Password))
            {
                WriteToOutput("Password not specified", OutputType.Error);
                exitCode = 4;
            }
            else if (string.IsNullOrEmpty(cmdArgs.Destination))
            {
                WriteToOutput("Output path not specified", OutputType.Error);
                exitCode = 5;
            }
            else
            {
                isValid = true;
                output = cmdArgs.Destination;
                if (!Path.IsPathRooted(output))
                {
                    output = Path.Combine(Environment.CurrentDirectory, output);
                }
            }

            if (isValid)
            {
                var stopwatch = new Stopwatch();
                stopwatch.Start();
                WriteToOutput("Retrieving Confluence items from " + cmdArgs.Source, OutputType.Info);
                var count = 0;

                try
                {
                    var binding = new BasicHttpBinding();
                    binding.MaxReceivedMessageSize = int.MaxValue;
                    var client = new ConfluenceSoapServiceClient(
                        binding,
                        new EndpointAddress(cmdArgs.Source));

                    try
                    {
                        var session = client.login(cmdArgs.User, cmdArgs.Password);
                        try
                        {
                            var pages = client.getPages(session, "CCNET");
                            var document = new XDocument();
                            if (File.Exists(output))
                            {
                                document = XDocument.Load(output);
                            }

                            // Make sure there is a root element
                            var rootEl = document.Root;
                            if (rootEl == null)
                            {
                                rootEl = new XElement(
                                    "confluence",
                                    new XAttribute("space", "CCNET"));
                                document.Add(rootEl);
                            }

                            foreach (var page in pages)
                            {
                                // Attempt to find each page
                                var pageId = page.id.ToString();
                                var pageEl = (from element in rootEl.Elements("page")
                                              where element.Attribute("id").Value == pageId
                                              select element).SingleOrDefault();
                                if (pageEl == null)
                                {
                                    // If the page does not exist, add it
                                    pageEl = new XElement(
                                        "page",
                                        new XAttribute("title", page.title),
                                        new XAttribute("id", pageId),
                                        new XAttribute("url", page.url),
                                        new XAttribute("parentId", page.parentId.ToString()));
                                    rootEl.Add(pageEl);
                                    count++;
                                    WriteToOutput("New item added: " + page.title, OutputType.Info);
                                }
                            }

                            if (count > 0)
                            {
                                // Order all the elements
                                var newDoc = new XDocument(
                                    new XElement("confluence",
                                        new XAttribute("space", "CCNET")));
                                newDoc.Root.Add(from element in document.Root.Elements()
                                                orderby element.Attribute("title").Value
                                                select element);
                                newDoc.Save(output);
                                WriteToOutput("Document updated", OutputType.Info);
                            }
                        }
                        finally
                        {
                            client.logout(session);
                        }
                    }
                    catch (Exception error)
                    {
                        WriteToOutput("ERROR: " + error.Message, OutputType.Error);
                        exitCode = 10;
                    }
                }
                catch (Exception error)
                {
                    WriteToOutput("ERROR: " + error.Message, OutputType.Error);
                    exitCode = 10;
                }

                stopwatch.Stop();
                WriteToOutput(
                    count.ToString() + " new confluence items retrieved in " + stopwatch.Elapsed.TotalSeconds.ToString("#,##0.00") + "s",
                    OutputType.Info);
            }

            return exitCode;
        }
예제 #9
0
        public static int Main(string[] args)
        {
            var consoleArgs = new ConsoleArgs();
            var options     = new OptionSet();

            options.Add("c|cmd=", "The command to run", v => { consoleArgs.Command = v; })
            .Add("s|src=", "The source for the command", v => { consoleArgs.Source = v; })
            .Add("o|out=", "The output destination for the command", v => { consoleArgs.Destination = v; })
            .Add("l|log=", "The log file to write any output to", v => { consoleArgs.LogFile = v; })
            .Add("u|usr="******"The username to use", v => { consoleArgs.User = v; })
            .Add("p|pwd=", "The password", v => { consoleArgs.Password = v; })
            .Add("xsd", "Generate XSD files", v => { consoleArgs.GenerateXsd = v != null; });
            options.Parse(args);

            var cmd = consoleArgs.Command ?? string.Empty;

            if (!string.IsNullOrEmpty(consoleArgs.LogFile))
            {
                var logPath = consoleArgs.LogFile;
                if (!Path.IsPathRooted(logPath))
                {
                    logPath = Path.Combine(Environment.CurrentDirectory, logPath);
                }

                if (File.Exists(logPath))
                {
                    File.Delete(logPath);
                }

                xmlLog = new StreamWriter(logPath);
                xmlLog.WriteLine("<docGen command=\"" + cmd + "\" time=\"" + DateTime.Now.ToString("o") + "\">");
            }

            var exitCode = 0;

            if (cmd.Length == 0)
            {
                WriteToOutput("No command specified", OutputType.Error);
                HelpScreen();
                exitCode = 11;
            }
            else
            {
                switch (cmd.ToLowerInvariant())
                {
                case "generate":
                    exitCode = GenerateDocumentation(consoleArgs);
                    break;

                case "list":
                    //exitCode = ListConfluenceItems(consoleArgs);
                    break;

                case "publish":
                    exitCode = PublishChiliItems(consoleArgs);
                    break;

                case "reportnotmapped":
                    exitCode = ReportNotMappedItems(consoleArgs);
                    break;

                default:
                    exitCode = 1;
                    WriteToOutput("Unknown command: " + cmd, OutputType.Error);
                    HelpScreen();
                    break;
                }
            }

            if (xmlLog != null)
            {
                xmlLog.WriteLine("</docGen>");
                xmlLog.Close();
                xmlLog.Dispose();
            }

            // Pause when running within Visual Studio
            if (Debugger.IsAttached)
            {
                Console.WriteLine("Application finished, press any key to continue");
                Console.ReadKey(true);
            }

            return(exitCode);
        }
예제 #10
0
        public static int GenerateDocumentation(ConsoleArgs args)
        {
            specialChars = new Regex(@"[\|\[\]\*+]", RegexOptions.Compiled);
            if (string.IsNullOrEmpty(args.Source))
            {
                WriteToOutput("No assembly specified", OutputType.Error);
                return(21);
            }

            var assemblyName = args.Source;

            if (!Path.IsPathRooted(assemblyName))
            {
                assemblyName = Path.Combine(Environment.CurrentDirectory, assemblyName);
            }

            if (!File.Exists(assemblyName))
            {
                WriteToOutput("Cannot find assembly: " + assemblyName, OutputType.Error);
                return(22);
            }

            var documentation     = new XDocument();
            var documentationPath = Path.ChangeExtension(assemblyName, "xml");

            if (File.Exists(documentationPath))
            {
                documentation = XDocument.Load(documentationPath);
            }
            else
            {
                WriteToOutput("No XML file found for assembly: " + assemblyName, OutputType.Error);
                return(23);
            }

            var stopwatch = new Stopwatch();

            stopwatch.Start();
            WriteToOutput("Starting documentation generation for " + Path.GetFileName(assemblyName), OutputType.Info);
            problemList.Clear();

            if (args.GenerateXsd)
            {
                WriteToOutput("Generating XSD files", OutputType.Debug);
            }

            try
            {
                var baseFolder = Path.Combine(Environment.CurrentDirectory, DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss", CultureInfo.InvariantCulture));
                if (!string.IsNullOrEmpty(args.Destination))
                {
                    baseFolder = args.Destination;
                    if (!Path.IsPathRooted(baseFolder))
                    {
                        baseFolder = Path.Combine(Environment.CurrentDirectory, baseFolder);
                    }
                }
                Debug.WriteLine("BaseFolder : " + baseFolder);

                Directory.CreateDirectory(baseFolder);
                var    assembly      = Assembly.LoadFrom(assemblyName);
                string versionNumber = null;
                var    version       = assembly.GetName().Version;
                if ((version.Major != 0) && (version.Minor != 0))
                {
                    versionNumber = version.ToString(4);
                }

                // Load the documentation for any dependencies
                LoadDependencyDocumentation(Path.GetDirectoryName(assemblyName), assembly, documentation);

                var publicTypesInAssembly = assembly.GetExportedTypes();
                foreach (var publicType in publicTypesInAssembly)
                {
                    var attributes = publicType.GetCustomAttributes(typeof(ReflectorTypeAttribute), true);
                    if (attributes.Length > 0)
                    {
                        Debug.WriteLine("Found reflector attributes in " + publicType.FullName);

                        // There can be only one!
                        var attribute    = attributes[0] as ReflectorTypeAttribute;
                        var baseFileName = attribute.Name + ".wiki";
                        var fileName     = Path.Combine(baseFolder, baseFileName);
                        WriteToOutput("Generating " + attribute.Name + ".wiki for " + publicType.FullName, OutputType.Info);
                        var itemStopwatch = new Stopwatch();
                        itemStopwatch.Start();


                        if (File.Exists(fileName))
                        {
                            //to be sure that the reflector type is unique in the entire ccnet solution!
                            throw new Exception(string.Format("Filename {0} coming from class {1} already exists", baseFileName, publicType.Name));
                        }

                        var typeElement = (from element in documentation.Descendants("member")
                                           where element.Attribute("name").Value == "T:" + publicType.FullName
                                           select element).SingleOrDefault();

                        if (typeElement == null)
                        {
                            WriteToOutput(
                                string.Format("XML docs for {0} are badly formed XML or missing.", publicType.FullName),
                                OutputType.Error);
                        }

                        var typeVersion = 1.0;

                        using (var output = new StreamWriter(fileName))
                        {
                            var elementName = attribute.Name;
                            if (HasTag(typeElement, "title"))
                            {
                                elementName = typeElement.Element("title").Value;
                            }

                            output.WriteLine("h1. " + elementName);
                            output.WriteLine();
                            if (HasTag(typeElement, "summary"))
                            {
                                WriteDocumentation(typeElement, "summary", output, documentation);
                                output.WriteLine();
                            }
                            else
                            {
                                problemList.Add("No Summary tag for " + publicType.FullName + " file " + fileName);
                            }

                            if (HasTag(typeElement, "version"))
                            {
                                output.WriteLine("h2. Version");
                                output.WriteLine();
                                output.Write("Available from version ");
                                WriteDocumentation(typeElement, "version", output, documentation);
                                output.WriteLine();
                                var versionEl = typeElement.Element("version");
                                double.TryParse(versionEl.Value, out typeVersion);
                            }

                            if (HasTag(typeElement, "example"))
                            {
                                output.WriteLine("h2. Examples");
                                output.WriteLine();
                                WriteDocumentation(typeElement, "example", output, documentation);
                                output.WriteLine();
                            }
                            else
                            {
                                if (publicType.IsClass)
                                {
                                    problemList.Add("No example tag for " + publicType.FullName + " file " + fileName);
                                }
                            }

                            output.WriteLine("h2. Configuration Elements");
                            output.WriteLine();
                            var elements   = ListElements(publicType);
                            var keyElement = typeElement != null?typeElement.Element("key") : null;

                            if ((elements.Count > 0) || (keyElement != null))
                            {
                                //Thoughtworks confluence style
                                //output.WriteLine("|| Element || Description || Type || Required || Default || Version ||");

                                // Redmine Style
                                // todo better header colors dodgerblue
                                output.WriteLine("| *Element* | *Description* | *Type* | *Required* | *Default* | *Version* |");
                                WriteElements(elements, output, documentation, typeElement, typeVersion);
                            }
                            else
                            {
                                output.WriteLine("There is no configuration for this plugin.");
                            }
                            output.WriteLine();

                            if (HasTag(typeElement, "remarks"))
                            {
                                output.WriteLine("h2. Notes");
                                output.WriteLine();
                                WriteDocumentation(typeElement, "remarks", output, documentation);
                                output.WriteLine();
                            }

                            //Thoughtworks confluence style
                            //output.WriteLine("{info:title=Automatically Generated}");

                            // Redmine Style
                            output.WriteLine();
                            output.WriteLine("h4. Automatically Generated");
                            output.WriteLine();

                            output.WriteLine(
                                "Documentation generated on " +
                                DateTime.Now.ToUniversalTime().ToString("dddd, d MMM yyyy", CultureInfo.InvariantCulture) +
                                " at " +
                                DateTime.Now.ToUniversalTime().ToString("h:mm:ss tt", CultureInfo.InvariantCulture));
                            if (versionNumber != null)
                            {
                                output.WriteLine("Using assembly version " + versionNumber);
                            }

                            //Thoughtworks confluence style
                            //output.WriteLine("{info}");

                            // Redmine Style



                            output.Flush();
                        }

                        itemStopwatch.Stop();
                        WriteToOutput(
                            "Documentation generated for " + attribute.Name + " in " + itemStopwatch.Elapsed.TotalSeconds.ToString("#,##0.00") + "s",
                            OutputType.Debug);

                        if (args.GenerateXsd)
                        {
                            itemStopwatch.Reset();
                            itemStopwatch.Start();
                            var xsdFile = Path.Combine(baseFolder, attribute.Name + ".xsd");
                            if (File.Exists(xsdFile))
                            {
                                File.Delete(xsdFile);
                            }

                            WriteXsdFile(xsdFile, attribute, publicType, documentation, typeElement);
                            WriteToOutput(
                                "XSD generated for " + attribute.Name + " in " + itemStopwatch.Elapsed.TotalSeconds.ToString("#,##0.00") + "s",
                                OutputType.Debug);
                        }
                    }
                }
            }
            catch (Exception error)
            {
                WriteToOutput(error.Message, OutputType.Error);
                return(24);
            }

            stopwatch.Stop();
            WriteToOutput(
                "Documentation generation finished in " + stopwatch.Elapsed.TotalSeconds.ToString("#,##0.00") + "s",
                OutputType.Info);

            if (problemList.Count > 0)
            {
                WriteToOutput("Problems encountered : " + problemList.Count.ToString(), OutputType.Warning);

                foreach (string s in problemList)
                {
                    WriteToOutput(s, OutputType.Warning);
                }
            }

            return(0);
        }