Example #1
0
        /* Function: GenerateJSON
         * Generates JSON for all the entries in the passed container.
         */
        protected void GenerateJSON(MenuEntries.Base.Container container)
        {
            ContainerExtraData containerExtraData = new ContainerExtraData(container);

            container.ExtraData = containerExtraData;

            containerExtraData.GenerateJSON(HTMLBuilder, this);

            foreach (var member in container.Members)
            {
                if (member is MenuEntries.Base.Target)
                {
                    TargetExtraData targetExtraData = new TargetExtraData((MenuEntries.Base.Target)member);
                    member.ExtraData = targetExtraData;

                    targetExtraData.GenerateJSON(HTMLBuilder, this);
                }
                else if (member is MenuEntries.Base.Container)
                {
                    GenerateJSON((MenuEntries.Base.Container)member);
                }
            }
        }
Example #2
0
        /* Function: Build
         * Generates JSON files for all entries in the menu.  It returns a <StringTable> mapping the file type strings ("files",
         * "classes", etc.) to a <IDObjects.NumberSet> representing all the files that were generated.  So "files.js", "files2.js",
         * and "files3.js" would map to "files" -> {1-3}.
         */
        public StringTable <IDObjects.NumberSet> Build()
        {
            try
            {
                // This will create multiple subdirectories if needed, and will not throw an exception if it already exists.
                System.IO.Directory.CreateDirectory(HTMLBuilder.Menu_DataFolder);
            }
            catch (Exception e)
            {
                throw new Exceptions.UserFriendly(
                          Locale.Get("NaturalDocs.Engine", "Error.CouldNotCreateOutputFolder(name, exception)",
                                     HTMLBuilder.Menu_DataFolder, e.Message)
                          );
            }


            // Build menu files

            StringTable <IDObjects.NumberSet> outputFiles = new StringTable <IDObjects.NumberSet>();

            if (RootFileMenu != null)
            {
                GenerateJSON(RootFileMenu);
                SegmentMenu(RootFileMenu, "files", ref outputFiles);
                BuildOutput(RootFileMenu);
            }

            if (RootClassMenu != null)
            {
                GenerateJSON(RootClassMenu);
                SegmentMenu(RootClassMenu, "classes", ref outputFiles);
                BuildOutput(RootClassMenu);
            }

            if (RootDatabaseMenu != null)
            {
                GenerateJSON(RootDatabaseMenu);
                SegmentMenu(RootDatabaseMenu, "database", ref outputFiles);
                BuildOutput(RootDatabaseMenu);
            }


            // Build tab information file

            StringBuilder tabInformation = new StringBuilder("NDMenu.OnTabsLoaded([");

            if (!EngineInstance.Config.ShrinkFiles)
            {
                tabInformation.Append('\n');
            }

            List <MenuEntries.Base.Container> tabContainers = new List <MenuEntries.Base.Container>();
            List <string> tabTypes = new List <string>();

            // DEPENDENCY: tabTypes must use the same strings as the NDLocation JavaScript class.
            // DEPENDENCY: tabTypes must use strings safe for including in CSS names.

            if (RootFileMenu != null)
            {
                tabContainers.Add(RootFileMenu);
                tabTypes.Add("File");
            }
            if (RootClassMenu != null)
            {
                tabContainers.Add(RootClassMenu);
                tabTypes.Add("Class");
            }
            if (RootDatabaseMenu != null)
            {
                tabContainers.Add(RootDatabaseMenu);
                tabTypes.Add("Database");
            }

            for (int i = 0; i < tabContainers.Count; i++)
            {
                ContainerExtraData extraData = (ContainerExtraData)tabContainers[i].ExtraData;

                if (!EngineInstance.Config.ShrinkFiles)
                {
                    tabInformation.Append(' ', IndentSpaces);
                }

                tabInformation.Append("[\"");
                tabInformation.Append(tabTypes[i]);
                tabInformation.Append("\",");

                if (tabContainers[i].CondensedTitles == null)
                {
                    tabInformation.Append('"');
                    tabInformation.StringEscapeAndAppend(tabContainers[i].Title.ToHTML());
                    tabInformation.Append('"');
                }
                else
                {
                    tabInformation.Append("[\"");
                    tabInformation.StringEscapeAndAppend(tabContainers[i].Title.ToHTML());
                    tabInformation.Append('"');

                    foreach (var condensedTitle in tabContainers[i].CondensedTitles)
                    {
                        tabInformation.Append(",\"");
                        tabInformation.StringEscapeAndAppend(condensedTitle.ToHTML());
                        tabInformation.Append('"');
                    }

                    tabInformation.Append(']');
                }

                tabInformation.Append(',');

                if (extraData.HashPath != null)
                {
                    tabInformation.Append('"');
                    tabInformation.StringEscapeAndAppend(extraData.HashPath);
                    tabInformation.Append('"');
                }
                // Otherwise leave an empty spot before the comma.  We don't have to write out "undefined".

                tabInformation.Append(",\"");
                tabInformation.StringEscapeAndAppend(extraData.DataFileName);
                tabInformation.Append("\"]");

                if (i < tabContainers.Count - 1)
                {
                    tabInformation.Append(',');
                }

                if (!EngineInstance.Config.ShrinkFiles)
                {
                    tabInformation.Append('\n');
                }
            }

            if (!EngineInstance.Config.ShrinkFiles)
            {
                tabInformation.Append(' ', IndentSpaces);
            }

            tabInformation.Append("]);");

            System.IO.File.WriteAllText(HTMLBuilder.Menu_DataFile("tabs", 1), tabInformation.ToString());


            return(outputFiles);
        }
Example #3
0
        /* Function: AppendMembers
         * A support function for <BuildOutput()>.  Appends the output of the container's members to the string, recursively
         * going through sub-containers as well.  This will not include the surrounding brackets, only the comma-separated
         * member entries.  If it finds any sub-containers that start a new data file, it will add them to containersToBuild.
         */
        protected void AppendMembers(MenuEntries.Base.Container container, StringBuilder output, int indent,
                                     Stack <MenuEntries.Base.Container> containersToBuild)
        {
            for (int i = 0; i < container.Members.Count; i++)
            {
                var member = container.Members[i];

                if (!EngineInstance.Config.ShrinkFiles)
                {
                    output.Append(' ', indent * IndentSpaces);
                }

                if (member is MenuEntries.Base.Target)
                {
                    TargetExtraData targetExtraData = (TargetExtraData)member.ExtraData;
                    output.Append(targetExtraData.JSON);
                }
                else if (member is MenuEntries.Base.Container)
                {
                    ContainerExtraData containerExtraData = (ContainerExtraData)member.ExtraData;
                    output.Append(containerExtraData.JSONBeforeMembers);

                    if (containerExtraData.StartsNewDataFile)
                    {
                        output.Append('"');
                        output.StringEscapeAndAppend(containerExtraData.DataFileName);
                        output.Append('"');

                        containersToBuild.Push((MenuEntries.Base.Container)member);
                    }
                    else
                    {
                        output.Append('[');

                        if (!EngineInstance.Config.ShrinkFiles)
                        {
                            output.AppendLine();
                        }

                        AppendMembers((MenuEntries.Base.Container)member, output, indent + 1, containersToBuild);

                        if (!EngineInstance.Config.ShrinkFiles)
                        {
                            output.Append(' ', (indent + 1) * IndentSpaces);
                        }

                        output.Append(']');
                    }

                    output.Append(containerExtraData.JSONAfterMembers);
                }
                                #if DEBUG
                else
                {
                    throw new Exception("Can't append JSON for menu entry " + member.Title + ".");
                }
                                #endif

                if (i < container.Members.Count - 1)
                {
                    output.Append(',');
                }

                if (!EngineInstance.Config.ShrinkFiles)
                {
                    output.AppendLine();
                }
            }
        }
Example #4
0
        /* Function: SegmentMenu
         * Segments the menu into smaller pieces and generates data file names.
         */
        protected void SegmentMenu(MenuEntries.Base.Container container, string dataFileType,
                                   ref StringTable <IDObjects.NumberSet> usedDataFiles)
        {
            // Generate the data file name for this container.

            IDObjects.NumberSet usedDataFileNumbers = usedDataFiles[dataFileType];

            if (usedDataFileNumbers == null)
            {
                usedDataFileNumbers = new IDObjects.NumberSet();
                usedDataFiles.Add(dataFileType, usedDataFileNumbers);
            }

            int dataFileNumber = usedDataFileNumbers.LowestAvailable;

            usedDataFileNumbers.Add(dataFileNumber);

            ContainerExtraData extraData = (ContainerExtraData)container.ExtraData;

            extraData.DataFileName = HTMLBuilder.Menu_DataFileNameOnly(dataFileType, dataFileNumber);


            // The data file has to include all the members in this container no matter what.

            int containerJSONSize = extraData.JSONBeforeMembers.Length + extraData.JSONAfterMembers.Length +
                                    extraData.JSONLengthOfMembers;

            List <MenuEntries.Base.Container> subContainers = null;

            foreach (var member in container.Members)
            {
                if (member is MenuEntries.Base.Container)
                {
                    if (subContainers == null)
                    {
                        subContainers = new List <MenuEntries.Base.Container>();
                    }

                    subContainers.Add((MenuEntries.Base.Container)member);
                }
            }


            // Now start including the contents of subcontainers until we reach the size limit.  We're going breadth-first instead of
            // depth first.

            List <MenuEntries.Base.Container> nextSubContainers = null;

            for (;;)
            {
                if (subContainers == null || subContainers.Count == 0)
                {
                    if (nextSubContainers == null || nextSubContainers.Count == 0)
                    {
                        break;
                    }
                    else
                    {
                        subContainers     = nextSubContainers;
                        nextSubContainers = null;
                    }
                }

                // Add subcontainers to the file in the order from smallest to largest.  This prevents one very large container early
                // in the list from causing all the other ones to be broken out into separate files.
                // DEPENDENCY: ContainerExtraData.JSONLengthOfMembers must cache its value for this algorithm to be efficient.

                int smallestSubContainerIndex = 0;
                int smallestSubContainerSize  = (subContainers[0].ExtraData as ContainerExtraData).JSONLengthOfMembers;

                for (int i = 1; i < subContainers.Count; i++)
                {
                    if ((subContainers[i].ExtraData as ContainerExtraData).JSONLengthOfMembers < smallestSubContainerSize)
                    {
                        smallestSubContainerIndex = i;
                        smallestSubContainerSize  = (subContainers[i].ExtraData as ContainerExtraData).JSONLengthOfMembers;
                    }
                }

                containerJSONSize += smallestSubContainerSize;

                if (containerJSONSize > SegmentLength)
                {
                    break;
                }

                foreach (var member in subContainers[smallestSubContainerIndex].Members)
                {
                    if (member is MenuEntries.Base.Container)
                    {
                        if (nextSubContainers == null)
                        {
                            nextSubContainers = new List <MenuEntries.Base.Container>();
                        }

                        nextSubContainers.Add((MenuEntries.Base.Container)member);
                    }
                }

                subContainers.RemoveAt(smallestSubContainerIndex);
            }


            // Now recurse through any remaining subcontainers so they get their own files.

            if (subContainers != null)
            {
                foreach (var subContainer in subContainers)
                {
                    SegmentMenu(subContainer, dataFileType, ref usedDataFiles);
                }
            }

            if (nextSubContainers != null)
            {
                foreach (var subContainer in nextSubContainers)
                {
                    SegmentMenu(subContainer, dataFileType, ref usedDataFiles);
                }
            }
        }