Ejemplo n.º 1
0
        /* Function: Save
         * Saves the passed information in <Config.nd>.
         */
        public void Save(Path filename, Config.ProjectInfo projectInfo, List <Style> styles, List <FileSourceInfo> fileSourceInfoList)
        {
            using (BinaryFile binaryFile = new BinaryFile())
            {
                binaryFile.OpenForWriting(filename);

                // [String: Project Title or null]
                // [String: Project Subtitle or null]
                // [String: Project Copyright or null]
                // [String: Project Timestamp Code or null]

                binaryFile.WriteString(projectInfo.Title);
                binaryFile.WriteString(projectInfo.Subtitle);
                binaryFile.WriteString(projectInfo.Copyright);
                binaryFile.WriteString(projectInfo.TimestampCode);


                // [String: Style Path]
                //    (properties)
                // [String: Style Path]
                // ...
                // [String: null]

                foreach (var style in styles)
                {
                    if (style is Styles.CSSOnly)
                    {
                        binaryFile.WriteString((style as Styles.CSSOnly).CSSFile);
                    }
                    else if (style is Styles.Advanced)
                    {
                        binaryFile.WriteString((style as Styles.Advanced).ConfigFile);
                    }
                    else
                    {
                        throw new NotImplementedException();
                    }


                    // [String: Inherit] ... [String: null]

                    if (style.Inherits != null)
                    {
                        foreach (var inheritStatement in style.Inherits)
                        {
                            binaryFile.WriteString(inheritStatement.Name);
                        }
                    }

                    binaryFile.WriteString(null);


                    // [String: OnLoad] [Byte: Page Type] ... [String: null]

                    if (style.OnLoad != null)
                    {
                        foreach (var onLoadStatement in style.OnLoad)
                        {
                            binaryFile.WriteString(onLoadStatement.Statement);
                            binaryFile.WriteByte((byte)onLoadStatement.Type);
                        }
                    }

                    binaryFile.WriteString(null);


                    // [String: Link] [Byte: Page Type] ... [String: null]

                    if (style.Links != null)
                    {
                        foreach (var linkStatement in style.Links)
                        {
                            binaryFile.WriteString(linkStatement.File);
                            binaryFile.WriteByte((byte)linkStatement.Type);
                        }
                    }

                    binaryFile.WriteString(null);


                    // [String: Home Page or null]

                    binaryFile.WriteString(style.HomePage);
                }

                // End of style paths
                binaryFile.WriteString(null);


                // [Int32: Source FileSource Number] [String: Source FileSource UniqueIDString]
                // [Int32: Source FileSource Number] [String: Source FileSource UniqueIDString]
                // ...
                // [Int32: 0]

                foreach (FileSourceInfo fileSourceInfo in fileSourceInfoList)
                {
                    if (fileSourceInfo.Type == Files.InputType.Source)
                    {
                        binaryFile.WriteInt32(fileSourceInfo.Number);
                        binaryFile.WriteString(fileSourceInfo.UniqueIDString);
                    }
                }

                binaryFile.WriteInt32(0);

                // [Int32: Image FileSource Number] [String: Image FileSource UniqueIDString]
                // [Int32: Image FileSource Number] [String: Image FileSource UniqueIDString]
                // ...
                // [Int32: 0]

                foreach (FileSourceInfo fileSourceInfo in fileSourceInfoList)
                {
                    if (fileSourceInfo.Type == Files.InputType.Image)
                    {
                        binaryFile.WriteInt32(fileSourceInfo.Number);
                        binaryFile.WriteString(fileSourceInfo.UniqueIDString);
                    }
                }

                binaryFile.WriteInt32(0);
            }
        }
Ejemplo n.º 2
0
        /* Function: Load
         * Loads the information in <Config.nd> and returns whether it was successful.  If not all the out parameters will still
         * return objects, they will just be empty.
         */
        public bool Load(Path filename, out Config.ProjectInfo projectInfo, out List <Style> styles, out List <FileSourceInfo> fileSourceInfoList)
        {
            projectInfo        = new Config.ProjectInfo();
            styles             = new List <Style>();
            fileSourceInfoList = new List <FileSourceInfo>();

            BinaryFile binaryFile = new BinaryFile();
            bool       result     = true;

            try
            {
                if (binaryFile.OpenForReading(filename, "2.2") == false)
                {
                    result = false;
                }
                else
                {
                    // [String: Project Title or null]
                    // [String: Project Subtitle or null]
                    // [String: Project Copyright or null]
                    // [String: Project Timestamp Code or null]

                    projectInfo.Title = binaryFile.ReadString();
                    projectInfo.TitlePropertyLocation = Config.PropertySource.PreviousRun;

                    projectInfo.Subtitle = binaryFile.ReadString();
                    projectInfo.SubtitlePropertyLocation = Config.PropertySource.PreviousRun;

                    projectInfo.Copyright = binaryFile.ReadString();
                    projectInfo.CopyrightPropertyLocation = Config.PropertySource.PreviousRun;

                    projectInfo.TimestampCode = binaryFile.ReadString();
                    projectInfo.TimestampCodePropertyLocation = Config.PropertySource.PreviousRun;


                    // [String: Style Path]
                    //    (properties)
                    // [String: Style Path]
                    // ...
                    // [String: null]

                    string stylePath = binaryFile.ReadString();

                    while (stylePath != null)
                    {
                        Style style;

                        if (stylePath.EndsWith(".css", StringComparison.OrdinalIgnoreCase))
                        {
                            style = new Styles.CSSOnly(stylePath);
                        }
                        else
                        {
                            style = new Styles.Advanced(stylePath);
                        }

                        styles.Add(style);


                        // [String: Inherit] ... [String: null]

                        string inheritStatement = binaryFile.ReadString();

                        while (inheritStatement != null)
                        {
                            // Find the name in the list of styles so we can connect the objects together properly.  There should only
                            // be one style per name so we can just compare by name.  Also, this list is stored in the order in which
                            // they must be applied, which means inherited styles will appear before the ones that inherit from them,
                            // so we can search the list we've built so far instead of waiting until they're all loaded.
                            Style matchingStyle = null;

                            for (int i = 0; i < styles.Count; i++)
                            {
                                if (string.Compare(inheritStatement, styles[i].Name, StringComparison.OrdinalIgnoreCase) == 0)
                                {
                                    matchingStyle = styles[i];
                                    break;
                                }
                            }

                            // If there's no match just add it as null.
                            style.AddInheritedStyle(inheritStatement, Config.PropertySource.PreviousRun, matchingStyle);

                            inheritStatement = binaryFile.ReadString();
                        }


                        // [String: OnLoad] [Byte: Page Type] ... [String: null]

                        string onLoadStatement = binaryFile.ReadString();

                        while (onLoadStatement != null)
                        {
                            Engine.Styles.PageType pageType = (Engine.Styles.PageType)binaryFile.ReadByte();
                            style.AddOnLoad(onLoadStatement, Config.PropertySource.PreviousRun, pageType);

                            onLoadStatement = binaryFile.ReadString();
                        }


                        // [String: Link] [Byte: Page Type] ... [String: null]

                        string linkStatement = binaryFile.ReadString();

                        while (linkStatement != null)
                        {
                            Engine.Styles.PageType pageType = (Engine.Styles.PageType)binaryFile.ReadByte();
                            style.AddLinkedFile(linkStatement, Config.PropertySource.PreviousRun, pageType);

                            linkStatement = binaryFile.ReadString();
                        }


                        // [String: Home Page or null]

                        string homePage = binaryFile.ReadString();

                        if (homePage != null)
                        {
                            style.SetHomePage(homePage, Config.PropertySource.PreviousRun);
                        }


                        // Next style path
                        stylePath = binaryFile.ReadString();
                    }

                    projectInfo.StyleName = styles[styles.Count - 1].Name;
                    projectInfo.StyleNamePropertyLocation = Config.PropertySource.PreviousRun;


                    // [Int32: Source FileSource Number] [String: Source FileSource UniqueIDString]
                    // [Int32: Source FileSource Number] [String: Source FileSource UniqueIDString]
                    // ...
                    // [Int32: 0]

                    FileSourceInfo fileSourceInfo = new FileSourceInfo();
                    fileSourceInfo.Type = Files.InputType.Source;

                    for (;;)
                    {
                        fileSourceInfo.Number = binaryFile.ReadInt32();

                        if (fileSourceInfo.Number == 0)
                        {
                            break;
                        }

                        fileSourceInfo.UniqueIDString = binaryFile.ReadString();
                        fileSourceInfoList.Add(fileSourceInfo);
                    }

                    // [Int32: Image FileSource Number] [String: Image FileSource UniqueIDString]
                    // [Int32: Image FileSource Number] [String: Image FileSource UniqueIDString]
                    // ...
                    // [Int32: 0]

                    fileSourceInfo.Type = Files.InputType.Image;

                    for (;;)
                    {
                        fileSourceInfo.Number = binaryFile.ReadInt32();

                        if (fileSourceInfo.Number == 0)
                        {
                            break;
                        }

                        fileSourceInfo.UniqueIDString = binaryFile.ReadString();
                        fileSourceInfoList.Add(fileSourceInfo);
                    }
                }
            }
            catch
            { result = false; }
            finally
            { binaryFile.Dispose(); }

            if (result == false)
            {
                projectInfo = new Config.ProjectInfo();
                styles.Clear();
                fileSourceInfoList.Clear();
            }

            return(result);
        }
Ejemplo n.º 3
0
        public override bool Start(Errors.ErrorList errorList)
        {
            int           errors           = errorList.Count;
            StartupIssues newStartupIssues = StartupIssues.None;


            //
            // Validate the output folder.
            //

            if (System.IO.Directory.Exists(config.Folder) == false)
            {
                errorList.Add(Locale.Get("NaturalDocs.Engine", "Error.FolderDoesntExist(type, name)", "output", config.Folder));
                return(false);
            }


            //
            // Load and validate the styles, including any inherited styles.
            //

            string    styleName   = config.ProjectInfo.StyleName;
            Style_txt styleParser = new Style_txt();

            if (styleName == null)
            {
                style = EngineInstance.Styles.LoadStyle("Default", errorList, Config.PropertySource.SystemDefault);
            }

            else if (EngineInstance.Styles.StyleExists(styleName))
            {
                style = EngineInstance.Styles.LoadStyle(styleName, errorList, config.ProjectInfo.StyleNamePropertyLocation);
            }

            // Check if it's an empty folder we want to generate a default Style.txt for
            else if (System.IO.Directory.Exists(EngineInstance.Config.ProjectConfigFolder + '/' + styleName) &&
                     !System.IO.File.Exists(EngineInstance.Config.ProjectConfigFolder + '/' + styleName + "/Style.txt"))
            {
                style = new Styles.Advanced(EngineInstance.Config.ProjectConfigFolder + '/' + styleName + "/Style.txt");

                // Inherit Default so everything still works before it's filled out.
                style.AddInheritedStyle("Default", Config.PropertySource.SystemGenerated);

                if (!styleParser.Save((Styles.Advanced)style, errorList, false))
                {
                    return(false);
                }

                // Now we have to reload it so it loads the inherited style as well.
                style = EngineInstance.Styles.LoadStyle(styleName, errorList, config.ProjectInfo.StyleNamePropertyLocation);
            }

            else
            {
                errorList.Add(Locale.Get("NaturalDocs.Engine", "Style.txt.CantFindStyle(name)", styleName),
                              config.ProjectInfo.StyleNamePropertyLocation);
                return(false);
            }

            if (style == null)
            {
                return(false);
            }

            stylesWithInheritance = style.BuildInheritanceList();


            //
            // Load Config.nd
            //

            Config_nd binaryConfigParser = new Config_nd();

            Config.ProjectInfo    previousProjectInfo;
            List <Style>          previousStyles;
            List <FileSourceInfo> previousFileSourceInfoList;
            bool hasBinaryConfigFile = false;

            if (!EngineInstance.HasIssues(StartupIssues.NeedToStartFresh))
            {
                hasBinaryConfigFile = binaryConfigParser.Load(WorkingDataFolder + "/Config.nd", out previousProjectInfo,
                                                              out previousStyles, out previousFileSourceInfoList);
            }
            else             // start fresh
            {
                previousProjectInfo        = new Config.ProjectInfo();
                previousStyles             = new List <Style>();
                previousFileSourceInfoList = new List <FileSourceInfo>();
            }


            //
            // Compare to the previous project info
            //

            bool projectInfoChanged = (!hasBinaryConfigFile ||
                                       ProjectInfo.Title != previousProjectInfo.Title ||
                                       ProjectInfo.Subtitle != previousProjectInfo.Subtitle ||
                                       ProjectInfo.Copyright != previousProjectInfo.Copyright ||
                                       ProjectInfo.TimestampCode != previousProjectInfo.TimestampCode);


            //
            // Compare to the previous list of styles.
            //

            bool inPurgingOperation = false;
            bool hasStyleChanges    = false;

            if (!hasBinaryConfigFile)
            {
                // If we don't have the binary config file we have to purge every style folder because some of them may no longer be in
                // use and we won't know which.
                PurgeAllStyleFolders(ref inPurgingOperation);

                newStartupIssues |= StartupIssues.NeedToReparseStyleFiles;
                hasStyleChanges   = true;
            }

            else             // (hasBinaryConfigFile)
            {
                // Purge the style folders for any no longer in use.

                foreach (var previousStyle in previousStyles)
                {
                    bool stillExists = false;

                    foreach (var currentStyle in stylesWithInheritance)
                    {
                        if (currentStyle.IsSameFundamentalStyle(previousStyle))
                        {
                            stillExists = true;
                            break;
                        }
                    }

                    if (!stillExists)
                    {
                        hasStyleChanges = true;
                        PurgeStyleFolder(previousStyle.Name, ref inPurgingOperation);
                    }
                }

                // Reparse styles on anything new.  If a style is new we can't assume all its files are going to be sent to the
                // IChangeWatcher functions because another output target may have been using it, and thus they are already in
                // Files.Manager.

                foreach (var currentStyle in stylesWithInheritance)
                {
                    bool foundMatch = false;

                    foreach (var previousStyle in previousStyles)
                    {
                        if (previousStyle.IsSameFundamentalStyle(currentStyle))
                        {
                            foundMatch = true;
                            break;
                        }
                    }

                    if (!foundMatch)
                    {
                        hasStyleChanges   = true;
                        newStartupIssues |= StartupIssues.NeedToReparseStyleFiles;
                        break;
                    }
                }

                // Check if the list of styles is the same or there's any changes in the settings.

                if (stylesWithInheritance.Count != previousStyles.Count)
                {
                    hasStyleChanges = true;
                }

                if (!hasStyleChanges)
                {
                    for (int i = 0; i < stylesWithInheritance.Count; i++)
                    {
                        if (!stylesWithInheritance[i].IsSameStyleAndProperties(previousStyles[i], includeInheritedStyles: false))
                        {
                            hasStyleChanges = true;
                            break;
                        }
                    }
                }
            }


            //
            // Compare to the previous list of FileSources.
            //

            if (!hasBinaryConfigFile)
            {
                // If we don't have the binary config file we need to rebuild all the output because we don't know which FileSource was
                // previously set to which number, which determines which output folder they use, like /files vs /files2.
                newStartupIssues |= StartupIssues.NeedToRebuildAllOutput;

                // This also means we have to purge every source or image output folder because we won't know which changed or are
                // no longer in use.
                PurgeAllSourceAndImageFolders(ref inPurgingOperation);
            }

            else              // (hasBinaryConfigFile)
            {
                bool hasDeletions = false;
                bool hasAdditions = false;


                // Purge the output folders of any deleted FileSources

                foreach (var previousFileSourceInfo in previousFileSourceInfoList)
                {
                    bool stillExists = false;

                    foreach (var fileSource in EngineInstance.Files.FileSources)
                    {
                        if (previousFileSourceInfo.IsSameFundamentalFileSource(fileSource))
                        {
                            stillExists = true;
                            break;
                        }
                    }

                    if (!stillExists)
                    {
                        hasDeletions = true;
                        Path outputFolder;

                        if (previousFileSourceInfo.Type == InputType.Source)
                        {
                            outputFolder = Paths.SourceFile.OutputFolder(OutputFolder, previousFileSourceInfo.Number);
                        }
                        else if (previousFileSourceInfo.Type == InputType.Image)
                        {
                            outputFolder = Paths.Image.OutputFolder(OutputFolder, previousFileSourceInfo.Number, previousFileSourceInfo.Type);
                        }
                        else
                        {
                            throw new NotImplementedException();
                        }

                        PurgeFolder(outputFolder, ref inPurgingOperation);
                    }
                }


                // Check if any FileSources were added

                foreach (var fileSource in EngineInstance.Files.FileSources)
                {
                    if (fileSource.Type == InputType.Source || fileSource.Type == InputType.Image)
                    {
                        bool foundMatch = false;

                        foreach (var previousFileSourceInfo in previousFileSourceInfoList)
                        {
                            if (previousFileSourceInfo.IsSameFundamentalFileSource(fileSource))
                            {
                                foundMatch = true;
                                break;
                            }
                        }

                        if (!foundMatch)
                        {
                            hasAdditions = true;
                            break;
                        }
                    }
                }


                // If there were both additions and deletions, force a rebuild.  This covers if a FileSource was simply moved from one
                // number to another, in which case the rebuild is required to populate the new folder.  This also covers if a folder
                // FileSource is replaced by one for its parent folder, in which case a rebuild is required to recreate the output for the
                // files in the child folder.

                if (hasAdditions && hasDeletions)
                {
                    newStartupIssues |= StartupIssues.NeedToRebuildAllOutput;
                }
            }


            //
            // Load BuildState.nd
            //

            BuildState_nd buildStateParser        = new BuildState_nd();
            bool          hasBinaryBuildStateFile = false;

            if (!EngineInstance.HasIssues(StartupIssues.NeedToStartFresh |
                                          StartupIssues.FileIDsInvalidated |
                                          StartupIssues.CodeIDsInvalidated |
                                          StartupIssues.CommentIDsInvalidated))
            {
                hasBinaryBuildStateFile = buildStateParser.Load(WorkingDataFolder + "/BuildState.nd", out buildState, out unprocessedChanges);
            }
            else             // start fresh
            {
                buildState         = new BuildState();
                unprocessedChanges = new UnprocessedChanges();
            }

            if (!hasBinaryBuildStateFile)
            {
                // If we don't have a build state file we need to reparse all the source files because we need to know sourceFilesWithContent
                // and classesWithContent.  We also need to rebuild all the output because we don't know if there was anything left in
                // sourceFilesToRebuild from the last run.  But those two flags actually aren't enough, because it will reparse those files, send
                // them to CodeDB, and then CodeDB won't send topic updates if the underlying content hasn't changed.  We'd actually need
                // to add all files and classes to UnprocessedChanges, but the Files module isn't started yet.  So f**k it, blow it all up and start
                // over.
                newStartupIssues |= StartupIssues.NeedToStartFresh;

                // Purge everything so no stray files are left behind from the previous build.
                PurgeAllSourceAndImageFolders(ref inPurgingOperation);
                PurgeAllClassFolders(ref inPurgingOperation);
                PurgeAllDatabaseFolders(ref inPurgingOperation);
                PurgeAllStyleFolders(ref inPurgingOperation);
                PurgeAllMenuFolders(ref inPurgingOperation);
                PurgeAllSearchIndexFolders(ref inPurgingOperation);
            }


            //
            // We're done with anything that could purge.
            //

            FinishedPurging(ref inPurgingOperation);


            //
            // Resave the Style.txt-based styles.
            //

            foreach (var style in stylesWithInheritance)
            {
                if (style is Styles.Advanced)
                {
                    var  advancedStyle = style as Styles.Advanced;
                    bool isSystemStyle = EngineInstance.Config.SystemStyleFolder.Contains(advancedStyle.ConfigFile);

                    // No error on save for system styles.
                    styleParser.Save(advancedStyle, errorList, noErrorOnFail: isSystemStyle);
                }
            }


            //
            // Save Config.nd
            //

            if (!System.IO.Directory.Exists(WorkingDataFolder))
            {
                System.IO.Directory.CreateDirectory(WorkingDataFolder);
            }

            List <FileSourceInfo> fileSourceInfoList = new List <FileSourceInfo>();

            foreach (var fileSource in EngineInstance.Files.FileSources)
            {
                if (fileSource.Type == Files.InputType.Source || fileSource.Type == Files.InputType.Image)
                {
                    FileSourceInfo fileSourceInfo = new FileSourceInfo();
                    fileSourceInfo.CopyFrom(fileSource);
                    fileSourceInfoList.Add(fileSourceInfo);
                }
                ;
            }

            binaryConfigParser.Save(WorkingDataFolder + "/Config.nd", ProjectInfo, stylesWithInheritance, fileSourceInfoList);


            //
            // Determine our home page
            //

            Path newHomePage     = Style.HomePageOf(stylesWithInheritance);
            bool homePageChanged = (newHomePage != buildState.HomePage);

            buildState.HomePage = newHomePage;
            // HomePageUsesTimestamp will be determined when it's built.


            //
            // Generate our timestamp
            //

            // We do it here instead of as needed because there are two places it could be used (the frame page and the home page)
            // and we want to avoid the admittedly unlikely possibility that Natural Docs can be building around midnight and use one
            // date for one and another for the other.
            string newTimestamp     = ProjectInfo.MakeTimestamp();
            bool   timestampChanged = (newTimestamp != buildState.GeneratedTimestamp);

            buildState.GeneratedTimestamp = newTimestamp;


            //
            // Load up unprocessedChanges
            //

            if (EngineInstance.HasIssues(StartupIssues.NeedToRebuildAllOutput) ||
                (newStartupIssues & StartupIssues.NeedToRebuildAllOutput) != 0)
            {
                unprocessedChanges.AddSourceFiles(buildState.sourceFilesWithContent);
                unprocessedChanges.AddClasses(buildState.classesWithContent);
                unprocessedChanges.AddImageFiles(buildState.usedImageFiles);

                newStartupIssues |= StartupIssues.NeedToReparseStyleFiles;

                unprocessedChanges.AddMainStyleFiles();
                unprocessedChanges.AddMainSearchFiles();
                unprocessedChanges.AddFramePage();
                unprocessedChanges.AddHomePage();
                unprocessedChanges.AddMenu();

                // We'll handle search prefixes after starting SearchIndex
            }

            else
            {
                if (!hasBinaryConfigFile || projectInfoChanged || timestampChanged)
                {
                    unprocessedChanges.AddFramePage();
                }

                if (!hasBinaryConfigFile || hasStyleChanges)
                {
                    unprocessedChanges.AddMainStyleFiles();
                }

                if (!hasBinaryConfigFile || homePageChanged || projectInfoChanged ||
                    (timestampChanged && buildState.HomePageUsesTimestamp))
                {
                    unprocessedChanges.AddHomePage();
                }
            }


            //
            // Create the search index, watch other modules, and apply new StartupIssues
            //

            searchIndex = new SearchIndex.Manager(this);

            EngineInstance.CodeDB.AddChangeWatcher(this);
            EngineInstance.Files.AddChangeWatcher(this);
            searchIndex.AddChangeWatcher(this);
            EngineInstance.AddStartupWatcher(this);

            if (newStartupIssues != StartupIssues.None)
            {
                EngineInstance.AddStartupIssues(newStartupIssues, dontNotify: this);
            }

            searchIndex.Start(errorList);


            //
            // If we're rebuilding everything, add the search index prefixes now that that's started
            //

            if (EngineInstance.HasIssues(StartupIssues.NeedToRebuildAllOutput))
            {
                var usedPrefixes = searchIndex.UsedPrefixes();

                foreach (var searchPrefix in usedPrefixes)
                {
                    unprocessedChanges.AddSearchPrefix(searchPrefix);
                }
            }


            bool success = (errors == errorList.Count);

            started = success;
            return(success);
        }