Ejemplo n.º 1
0
        private void recursively_build_all_files_list(List <SobekCM_File_Info> returnValue, List <Page_TreeNode> handledPages, abstract_TreeNode thisNode)
        {
            // Since this is pre-order, first 'visit' this
            if (thisNode.Page)
            {
                Page_TreeNode pageNode = (Page_TreeNode)thisNode;
                if (!handledPages.Contains(pageNode))
                {
                    foreach (SobekCM_File_Info file in pageNode.Files)
                    {
                        returnValue.Add(file);
                    }
                    handledPages.Add(pageNode);
                }
            }

            // is this a division node? .. which can have children ..
            if (!thisNode.Page)
            {
                Division_TreeNode thisDivNode = (Division_TreeNode)thisNode;

                // Do the same for all the children
                foreach (abstract_TreeNode childNode in thisDivNode.Nodes)
                {
                    recursively_build_all_files_list(returnValue, handledPages, childNode);
                }
            }
        }
Ejemplo n.º 2
0
        private void preorder_build(List <abstract_TreeNode> collection, abstract_TreeNode thisNode, bool only_add_pages)
        {
            // Since this is pre-order, first 'visit' this
            if (!only_add_pages)
            {
                collection.Add(thisNode);
            }
            else
            {
                // If we are just getting pages, only add if it is not already added
                if ((thisNode.Page) && (!collection.Contains(thisNode)))
                {
                    collection.Add(thisNode);
                }
            }

            // is this a division node? .. which can have children ..
            if (!thisNode.Page)
            {
                Division_TreeNode thisDivNode = (Division_TreeNode)thisNode;

                // Do the same for all the children
                foreach (abstract_TreeNode childNode in thisDivNode.Nodes)
                {
                    preorder_build(collection, childNode, only_add_pages);
                }
            }
        }
Ejemplo n.º 3
0
        /// <summary> Adds a file  object (with the appropriate divisions and pages) to this tree </summary>
        /// <param name="New_File"> New file object to add </param>
        /// <param name="Label"> Label for the page containing this file, if it is a new page </param>
        /// <remarks> This is generally used to add just a single file.  To add many files, better logic should be implemented </remarks>
        public void Add_File(SobekCM_File_Info New_File, string Label)
        {
            // Determine the upper case name
            string systemname_upper = New_File.File_Name_Sans_Extension;

            // Look for a page/entity which has the same file name, else it will be added to the last division
            foreach (abstract_TreeNode rootNode in Roots)
            {
                if (recursively_add_file(rootNode, New_File, systemname_upper))
                {
                    return;
                }
            }

            // If not found, find the last division
            if (Roots.Count > 0)
            {
                if (!Roots[Roots.Count - 1].Page)
                {
                    // Get his last division
                    Division_TreeNode lastDivision = (Division_TreeNode)Roots[Roots.Count - 1];

                    // Find the last division then
                    while ((lastDivision.Nodes.Count > 0) && (!lastDivision.Nodes[lastDivision.Nodes.Count - 1].Page))
                    {
                        lastDivision = (Division_TreeNode)lastDivision.Nodes[lastDivision.Nodes.Count - 1];
                    }

                    // Add this as a new page on the last division
                    Page_TreeNode newPage = new Page_TreeNode(Label);
                    lastDivision.Add_Child(newPage);

                    // Now, add this file to the page
                    newPage.Files.Add(New_File);
                }
                else
                {
                    // No divisions at all, but pages exist at the top level, which is okay
                    Page_TreeNode pageNode = (Page_TreeNode)Roots[Roots.Count - 1];

                    // Now, add this file to the page
                    pageNode.Files.Add(New_File);
                }
            }
            else
            {
                // No nodes exist, so add a MAIN division node
                Division_TreeNode newDivNode = new Division_TreeNode("Main", String.Empty);
                Roots.Add(newDivNode);

                // Add this as a new page on the new division
                Page_TreeNode newPage = new Page_TreeNode(Label);
                newDivNode.Add_Child(newPage);

                // Now, add this file to the page
                newPage.Files.Add(New_File);
            }
        }
Ejemplo n.º 4
0
        private void preorder_build(List <abstract_TreeNode> Collection, abstract_TreeNode ThisNode, bool OnlyAddPages, bool PagesMustHaveFiles)
        {
            // Since this is pre-order, first 'visit' this
            if (!OnlyAddPages)
            {
                Collection.Add(ThisNode);
            }
            else
            {
                // If we are just getting pages, only add if it is not already added
                if ((ThisNode.Page) && (!Collection.Contains(ThisNode)))
                {
                    // If you just add all files, just add it here
                    if (!PagesMustHaveFiles)
                    {
                        Collection.Add(ThisNode);
                    }
                    else
                    {
                        // Must ensure this page has files to add it
                        Page_TreeNode asPage = ThisNode as Page_TreeNode;
                        if (asPage != null)
                        {
                            if ((asPage.Files != null) && (asPage.Files.Count > 0))
                            {
                                Collection.Add(asPage);
                            }
                        }
                    }
                }
            }

            // is this a division node? .. which can have children ..
            if (!ThisNode.Page)
            {
                Division_TreeNode thisDivNode = (Division_TreeNode)ThisNode;

                // Do the same for all the children
                foreach (abstract_TreeNode childNode in thisDivNode.Nodes)
                {
                    preorder_build(Collection, childNode, OnlyAddPages, PagesMustHaveFiles);
                }
            }
        }
Ejemplo n.º 5
0
        private bool recursively_add_file(abstract_TreeNode Node, SobekCM_File_Info New_File, string SystemName_Upper)
        {
            // If this is a page, check for a match first
            if (Node.Page)
            {
                Page_TreeNode pageNode = (Page_TreeNode)Node;
                if (pageNode.Files.Count >= 1)
                {
                    if (pageNode.Files[0].File_Name_Sans_Extension == SystemName_Upper)
                    {
                        // Belongs to this page.  Now, just make sure it doesn't already exist
                        foreach (SobekCM_File_Info thisFile in pageNode.Files)
                        {
                            if (thisFile.System_Name.ToUpper() == New_File.System_Name.ToUpper())
                            {
                                return(true);
                            }
                        }

                        // Not found, so add it to this page
                        pageNode.Files.Add(New_File);
                        return(true);
                    }
                }
            }

            // If this was a division, check all pages
            if (!Node.Page)
            {
                Division_TreeNode divNode = (Division_TreeNode)Node;
                foreach (abstract_TreeNode childNodes in divNode.Nodes)
                {
                    if (recursively_add_file(childNodes, New_File, SystemName_Upper))
                    {
                        return(true);
                    }
                }
            }

            // If nothing found that matches under this node, return false
            return(false);
        }
Ejemplo n.º 6
0
        private bool recursively_check_for_any_files(abstract_TreeNode Node)
        {
            if (Node.Page)
            {
                if (((Page_TreeNode)Node).Files.Count > 0)
                {
                    return(true);
                }
            }

            if (!Node.Page)
            {
                Division_TreeNode divNode = (Division_TreeNode)Node;
                foreach (abstract_TreeNode TreeNode in divNode.Nodes)
                {
                    if (recursively_check_for_any_files(TreeNode))
                    {
                        return(true);
                    }
                }
            }

            return(false);
        }
        /// <summary> Reads metadata from an open stream and saves to the provided item/package </summary>
        /// <param name="Input_Stream"> Open stream to read metadata from </param>
        /// <param name="Return_Package"> Package into which to read the metadata </param>
        /// <param name="Options"> Dictionary of any options which this metadata reader/writer may utilize </param>
        /// <param name="Error_Message">[OUTPUT] Explanation of the error, if an error occurs during reading </param>
        /// <returns>TRUE if successful, otherwise FALSE </returns>
        /// <remarks> Accepts two options: (1) 'METS_File_ReaderWriter:Minimize_File_Info' which tells whether the reader 
        /// should just skip the file reading portion completely, and just read the bibliographic data ( Default is FALSE).
        /// (2) 'METS_File_ReaderWriter:Support_Divisional_dmdSec_amdSec' </remarks>
        public bool Read_Metadata(Stream Input_Stream, SobekCM_Item Return_Package, Dictionary<string, object> Options, out string Error_Message)
        {
            Error_Message = String.Empty;

            // Read the options from the dictionary of options
            bool minimizeFileInfo = false;
            if (Options != null)
            {
                if (Options.ContainsKey("METS_File_ReaderWriter:Minimize_File_Info"))
                    bool.TryParse(Options["METS_File_ReaderWriter:Minimize_File_Info"].ToString(), out minimizeFileInfo);

                if (Options.ContainsKey("METS_File_ReaderWriter:Support_Divisional_dmdSec_amdSec"))
                {
                    bool supportDivisionalDmdSecAmdSec;
                    bool.TryParse(Options["METS_File_ReaderWriter:Support_Divisional_dmdSec_amdSec"].ToString(), out supportDivisionalDmdSecAmdSec);
                }
            }

            // Keep a list of all the files created, by file id, as additional data is gathered
            // from the different locations ( amdSec, fileSec, structmap )
            Dictionary<string, SobekCM_File_Info> files_by_fileid = new Dictionary<string, SobekCM_File_Info>();

            // For now, to do support for old way of doing downloads, build a list to hold
            // the deprecated download files
            List<Download_Info_DEPRECATED> deprecatedDownloads = new List<Download_Info_DEPRECATED>();

            // Need to store the unanalyzed sections of dmdSec and amdSec until we determine if 
            // the scope is the whole package, or the top-level div.  We use lists as the value since
            // several sections may have NO id and the METS may even (incorrectly) have multiple sections
            // with the same ID
            Dictionary<string, List<Unanalyzed_METS_Section>> dmdSec = new Dictionary<string, List<Unanalyzed_METS_Section>>();
            Dictionary<string, List<Unanalyzed_METS_Section>> amdSec = new Dictionary<string, List<Unanalyzed_METS_Section>>();

            // Dictionaries store the link between dmdSec and amdSec id's to single divisions
            Dictionary<string, abstract_TreeNode> division_dmdids = new Dictionary<string, abstract_TreeNode>();
            Dictionary<string, abstract_TreeNode> division_amdids = new Dictionary<string, abstract_TreeNode>();


            try
            {
                // Try to read the XML
                XmlReader r = new XmlTextReader(Input_Stream);

                // Begin stepping through each of the XML nodes
                while (r.Read())
                {
                    #region Handle some processing instructions requested by Florida SUS's / FLVC (hope to deprecate)

                    // Handle some processing instructions requested by Florida SUS's / FLVC
                    if (r.NodeType == XmlNodeType.ProcessingInstruction)
                    {
                        if (r.Name.ToLower() == "fcla")
                        {
                            string value = r.Value.ToLower();
                            if (value.IndexOf("fda=\"yes\"") >= 0)
                            {
                                DAITSS_Info daitssInfo = Return_Package.Get_Metadata_Module(GlobalVar.DAITSS_METADATA_MODULE_KEY) as DAITSS_Info;
                                if (daitssInfo == null)
                                {
                                    daitssInfo = new DAITSS_Info();
                                    Return_Package.Add_Metadata_Module(GlobalVar.DAITSS_METADATA_MODULE_KEY, daitssInfo);
                                }
                                daitssInfo.toArchive = true;
                            }
                            if (value.IndexOf("fda=\"no\"") >= 0)
                            {
                                DAITSS_Info daitssInfo2 = Return_Package.Get_Metadata_Module(GlobalVar.DAITSS_METADATA_MODULE_KEY) as DAITSS_Info;
                                if (daitssInfo2 == null)
                                {
                                    daitssInfo2 = new DAITSS_Info();
                                    Return_Package.Add_Metadata_Module(GlobalVar.DAITSS_METADATA_MODULE_KEY, daitssInfo2);
                                }
                                daitssInfo2.toArchive = false;
                            }
                        }
                    }

                    #endregion

                    if (r.NodeType == XmlNodeType.Element)
                    {
                        switch (r.Name.Replace("METS:", ""))
                        {
                            case "mets":
                                if (r.MoveToAttribute("OBJID"))
                                    Return_Package.METS_Header.ObjectID = r.Value;
                                break;

                            case "metsHdr":
                                read_mets_header(r.ReadSubtree(), Return_Package);
                                break;

                            case "dmdSec":
                            case "dmdSecFedora":
                                Unanalyzed_METS_Section thisDmdSec = store_dmd_sec(r.ReadSubtree());
                                if ( dmdSec.ContainsKey(thisDmdSec.ID))
                                    dmdSec[thisDmdSec.ID].Add(thisDmdSec);
                                else
                                {
                                    List<Unanalyzed_METS_Section> newDmdSecList = new List<Unanalyzed_METS_Section>();
                                    newDmdSecList.Add(thisDmdSec);
                                    dmdSec[thisDmdSec.ID] = newDmdSecList;
                                }
                                
                                break;

                            case "amdSec":
                                Unanalyzed_METS_Section thisAmdSec = store_amd_sec(r.ReadSubtree());
                                if (amdSec.ContainsKey(thisAmdSec.ID))
                                    amdSec[thisAmdSec.ID].Add(thisAmdSec);
                                else
                                {
                                    List<Unanalyzed_METS_Section> newAmdSecList = new List<Unanalyzed_METS_Section> {thisAmdSec};
                                    amdSec[thisAmdSec.ID] = newAmdSecList;
                                }
                                break;

                            case "fileSec":
                                read_file_sec(r.ReadSubtree(), minimizeFileInfo, files_by_fileid);
                                break;

                            case "structMap":
                                if (!r.IsEmptyElement)
                                {
                                    read_struct_map(r.ReadSubtree(), Return_Package, files_by_fileid, division_dmdids, division_amdids);
                                }
                                break;

                            case "behaviorSec":
                                read_behavior_sec(r.ReadSubtree(), Return_Package);
                                break;
                        }
                    }
                }

                // writer.Close();
                r.Close();

            }
            catch 
            {
                // Do nothinh
            }

            Input_Stream.Close();

            // Load some options for interoperability
            Dictionary<string, object> options = new Dictionary<string, object>();
            options.Add("SobekCM_FileInfo_METS_amdSec_ReaderWriter:Files_By_FileID", files_by_fileid);

            #region Process the previously stored dmd sections

            // Now, process the previously stored dmd sections
            foreach (string thisDmdSecId in dmdSec.Keys)
            {
                // Could be multiple stored sections with the same (or no) ID
                foreach (Unanalyzed_METS_Section metsSection in dmdSec[thisDmdSecId])
                {
                    XmlReader reader = XmlReader.Create(new StringReader(metsSection.Inner_XML));
                    string mdtype = String.Empty;
                    string othermdtype = String.Empty;
                    while (reader.Read())
                    {
                        if (reader.NodeType == XmlNodeType.Element)
                        {
                            if (reader.Name.ToLower().Replace("mets:", "") == "mdwrap")
                            {

                                if (reader.MoveToAttribute("MDTYPE"))
                                    mdtype = reader.Value;
                                if (reader.MoveToAttribute("OTHERMDTYPE"))
                                    othermdtype = reader.Value;

                                // NOt crazy about this part, but sometimes people do not use the OTHERMDTYPE
                                // tag correctly, and just use the LABEL to differentiate the types
                                if ((mdtype == "OTHER") && (othermdtype.Length == 0) && (reader.MoveToAttribute("LABEL")))
                                    othermdtype = reader.Value;

                                // Now, determine if this was a division-level read, or a package-wide
                                if (division_dmdids.ContainsKey(thisDmdSecId))
                                {
                                    // Division level dmdSec
                                    // Get the division
                                    abstract_TreeNode node = division_dmdids[thisDmdSecId];

                                    // Get an appropriate reader from the metadata configuration
                                    iDivision_dmdSec_ReaderWriter rw = ResourceObjectSettings.MetadataConfig.Get_Division_DmdSec_ReaderWriter(mdtype, othermdtype);

                                    // Is this dmdSec analyzable? (i.e., did we find an appropriate reader/writer?)
                                    if (rw == null)
                                    {
                                        node.Add_Unanalyzed_DMDSEC(metsSection);
                                    }
                                    else
                                    {
                                        rw.Read_dmdSec(reader, node, options);
                                    }
                                }
                                else
                                {
                                    // Package-level dmdSec 
                                    // Get an appropriate reader from the metadata configuration
                                    iPackage_dmdSec_ReaderWriter rw = ResourceObjectSettings.MetadataConfig.Get_Package_DmdSec_ReaderWriter(mdtype, othermdtype);

                                    // Is this dmdSec analyzable? (i.e., did we find an appropriate reader/writer?)
                                    if (rw == null)
                                    {
                                        Return_Package.Add_Unanalyzed_DMDSEC(metsSection);
                                    }
                                    else
                                    {
                                        rw.Read_dmdSec(reader, Return_Package, options);
                                    }
                                }
                            }
                        }
                    }
                }
            }

            #endregion

            #region Process the previously stored amd sections

            // Now, process the previously stored amd sections
            foreach (string thisAmdSecId in amdSec.Keys)
            {
                // Could be multiple stored sections with the same (or no) ID
                foreach (Unanalyzed_METS_Section metsSection in amdSec[thisAmdSecId])
                {
                    XmlReader reader = XmlReader.Create(new StringReader(metsSection.Inner_XML));
                    string mdtype = String.Empty;
                    string othermdtype = String.Empty;
                    while (reader.Read())
                    {
                        if (reader.NodeType == XmlNodeType.Element)
                        {
                            if (reader.Name.ToLower().Replace("mets:", "") == "mdwrap")
                            {

                                if (reader.MoveToAttribute("MDTYPE"))
                                    mdtype = reader.Value;
                                if (reader.MoveToAttribute("OTHERMDTYPE"))
                                    othermdtype = reader.Value;

                                // Package-level amdSec 
                                // Get an appropriate reader from the metadata configuration
                                iPackage_amdSec_ReaderWriter rw = ResourceObjectSettings.MetadataConfig.Get_Package_AmdSec_ReaderWriter(mdtype, othermdtype);

                                // Is this amdSec analyzable? (i.e., did we find an appropriate reader/writer?)
                                if (rw == null)
                                {
                                    Return_Package.Add_Unanalyzed_AMDSEC(metsSection);
                                }
                                else
                                {
                                    rw.Read_amdSec(reader, Return_Package, options);
                                }
                            }
                        }
                    }
                }
            }

            #endregion

            #region Special code used for moving downloads into the structure map system, and out of the old SobekCM METS section 

            // For backward compatability, move from the old download system to the
            // new structure.  This has to happen here at the end so that we have access

            // Were there some downloads added here?
            if (deprecatedDownloads.Count > 0)
            {
                // Get the list of downloads from the download tree
                List<SobekCM_File_Info> newStructureDownloads = Return_Package.Divisions.Download_Tree.All_Files;

                // Step through each download in the old system
                foreach (Download_Info_DEPRECATED thisDownload in deprecatedDownloads)
                {
                    // Get the label (if there is one)
                    string label = thisDownload.Label;
                    string filename = thisDownload.FileName;
                    bool found = false;
                    if ((filename.Length == 0) && (thisDownload.File_ID.Length > 0))
                    {
                        if (files_by_fileid.ContainsKey(thisDownload.File_ID))
                        {
                            SobekCM_File_Info thisDownloadFile = files_by_fileid[thisDownload.File_ID];
                            filename = thisDownloadFile.System_Name;

                            // Ensure a file of this name doesn't already exist
                            foreach (SobekCM_File_Info existingFile in newStructureDownloads)
                            {
                                if (existingFile.System_Name.ToUpper().Trim() == filename.ToUpper().Trim())
                                {
                                    found = true;
                                    break;
                                }
                            }

                            // Not found, so add it
                            if (!found)
                            {
                                // Determine the label if it was missing or identical to file name
                                if ((label.Length == 0) || (label == filename))
                                {
                                    label = filename;
                                    int first_period_index = label.IndexOf('.');
                                    if (first_period_index > 0)
                                    {
                                        label = label.Substring(0, first_period_index);
                                    }
                                }

                                // Add the root to the download tree, if not existing
                                Division_TreeNode newRoot;
                                if (Return_Package.Divisions.Download_Tree.Roots.Count == 0)
                                {
                                    newRoot = new Division_TreeNode("Main", String.Empty);
                                    Return_Package.Divisions.Download_Tree.Roots.Add(newRoot);
                                }
                                else
                                {
                                    newRoot = (Division_TreeNode) Return_Package.Divisions.Download_Tree.Roots[0];
                                }

                                // Add a page for this, with the provided label if there was one
                                Page_TreeNode newPage = new Page_TreeNode(label);
                                newRoot.Nodes.Add(newPage);

                                // Now, add this existing file
                                newPage.Files.Add(thisDownloadFile);

                                // Add to the list of files added (in case it appears twice)
                                newStructureDownloads.Add(thisDownloadFile);
                            }
                        }
                    }
                    else
                    {
                        // Ensure a file of this name doesn't already exist
                        foreach (SobekCM_File_Info existingFile in newStructureDownloads)
                        {
                            if (existingFile.System_Name.ToUpper().Trim() == filename.ToUpper().Trim())
                            {
                                found = true;
                                break;
                            }
                        }

                        // Not found, so add it
                        if (!found)
                        {
                            // Determine the label if it was missing or identical to file name
                            if ((label.Length == 0) || (label == filename))
                            {
                                label = filename;
                                int first_period_index = label.IndexOf('.');
                                if (first_period_index > 0)
                                {
                                    label = label.Substring(0, first_period_index);
                                }
                            }

                            // Add the root to the download tree, if not existing
                            Division_TreeNode newRoot;
                            if (Return_Package.Divisions.Download_Tree.Roots.Count == 0)
                            {
                                newRoot = new Division_TreeNode("Main", String.Empty);
                                Return_Package.Divisions.Download_Tree.Roots.Add(newRoot);
                            }
                            else
                            {
                                newRoot = (Division_TreeNode) Return_Package.Divisions.Download_Tree.Roots[0];
                            }

                            // Add a page for this, with the provided label if there was one
                            Page_TreeNode newPage = new Page_TreeNode(label);
                            newRoot.Nodes.Add(newPage);

                            // Now, add this existing file
                            SobekCM_File_Info thisDownloadFile = new SobekCM_File_Info(filename);
                            newPage.Files.Add(thisDownloadFile);

                            // Add to the list of files added (in case it appears twice)
                            newStructureDownloads.Add(thisDownloadFile);
                        }
                    }
                }
            }

            #endregion

            #region Special code for distributing any page-level coordinate information read from the old SobekCM coordinate metadata

            // Get the geospatial data
            GeoSpatial_Information geoSpatial = Return_Package.Get_Metadata_Module(GlobalVar.GEOSPATIAL_METADATA_MODULE_KEY) as GeoSpatial_Information;
            if ((geoSpatial != null) && ( geoSpatial.Polygon_Count > 0 ))
            {
                // See if any has the page sequence filled out, which means it came from the old metadata system
                bool redistribute = false;
                foreach (Coordinate_Polygon thisPolygon in geoSpatial.Polygons)
                {
                    if (thisPolygon.Page_Sequence > 0)
                    {
                        redistribute = true;
                        break;
                    }
                }

                // If we need to redistribute, get started!
                if (redistribute)
                {
                    // Get the pages, by sequence
                    List<abstract_TreeNode> pagesBySequence = Return_Package.Divisions.Physical_Tree.Pages_PreOrder;
                    List<Coordinate_Polygon> polygonsToRemove = new List<Coordinate_Polygon>();

                    // Step through each polygon
                    foreach (Coordinate_Polygon thisPolygon in geoSpatial.Polygons)
                    {
                        if ((thisPolygon.Page_Sequence > 0) && ( thisPolygon.Page_Sequence <= pagesBySequence.Count ))
                        {
                            // Get the page
                            abstract_TreeNode thisPageFromSequence = pagesBySequence[thisPolygon.Page_Sequence - 1];

                            // We can assume this page does not already have the coordiantes
                            GeoSpatial_Information thisPageCoord = new GeoSpatial_Information();
                            thisPageFromSequence.Add_Metadata_Module( GlobalVar.GEOSPATIAL_METADATA_MODULE_KEY, thisPageCoord );
                            thisPageCoord.Add_Polygon( thisPolygon);

                            // Remove this from the package-level coordinates
                            polygonsToRemove.Add(thisPolygon);
                        }
                    }

                    // Now, remove all polygons flagged to be removed
                    foreach (Coordinate_Polygon thisPolygon in polygonsToRemove)
                    {
                        geoSpatial.Remove_Polygon(thisPolygon);
                    }
                }
            }

            #endregion

            #region Copy any serial hierarchy in the Behaviors.Serial_Info part into the bib portion, if not there

            // Do some final cleanup on the SERIAL HIERARCHY
            if ((Return_Package.Behaviors.hasSerialInformation) && (Return_Package.Behaviors.Serial_Info.Count > 0))
            {
                if ((Return_Package.Bib_Info.Series_Part_Info.Enum1.Length == 0) && (Return_Package.Bib_Info.Series_Part_Info.Year.Length == 0))
                {
                    if (Return_Package.Bib_Info.SobekCM_Type == TypeOfResource_SobekCM_Enum.Newspaper)
                    {
                        Return_Package.Bib_Info.Series_Part_Info.Year = Return_Package.Behaviors.Serial_Info[0].Display;
                        Return_Package.Bib_Info.Series_Part_Info.Year_Index = Return_Package.Behaviors.Serial_Info[0].Order;

                        if (Return_Package.Behaviors.Serial_Info.Count > 1)
                        {
                            Return_Package.Bib_Info.Series_Part_Info.Month = Return_Package.Behaviors.Serial_Info[1].Display;
                            Return_Package.Bib_Info.Series_Part_Info.Month_Index = Return_Package.Behaviors.Serial_Info[1].Order;
                        }
                    }

                    if (Return_Package.Behaviors.Serial_Info.Count > 2)
                    {
                        Return_Package.Bib_Info.Series_Part_Info.Day = Return_Package.Behaviors.Serial_Info[2].Display;
                        Return_Package.Bib_Info.Series_Part_Info.Day_Index = Return_Package.Behaviors.Serial_Info[2].Order;
                    }
                }
                else
                {
                    Return_Package.Bib_Info.Series_Part_Info.Enum1 = Return_Package.Behaviors.Serial_Info[0].Display;
                    Return_Package.Bib_Info.Series_Part_Info.Enum1_Index = Return_Package.Behaviors.Serial_Info[0].Order;

                    if (Return_Package.Behaviors.Serial_Info.Count > 1)
                    {
                        Return_Package.Bib_Info.Series_Part_Info.Enum2 = Return_Package.Behaviors.Serial_Info[1].Display;
                        Return_Package.Bib_Info.Series_Part_Info.Enum2_Index = Return_Package.Behaviors.Serial_Info[1].Order;
                    }

                    if (Return_Package.Behaviors.Serial_Info.Count > 2)
                    {
                        Return_Package.Bib_Info.Series_Part_Info.Enum3 = Return_Package.Behaviors.Serial_Info[2].Display;
                        Return_Package.Bib_Info.Series_Part_Info.Enum3_Index = Return_Package.Behaviors.Serial_Info[2].Order;
                    }
                }
            }

            #endregion

            return true;
        }
        private void read_struct_map(XmlReader r, SobekCM_Item package, Dictionary<string, SobekCM_File_Info> files_by_fileid, Dictionary<string, abstract_TreeNode> division_dmdids, Dictionary<string, abstract_TreeNode> division_amdids )
        {
            Stack<abstract_TreeNode> parentNodes = new Stack<abstract_TreeNode>();
            Dictionary<string, abstract_TreeNode> divisions_by_id = new Dictionary<string, abstract_TreeNode>();

            string divID;
            string divType = String.Empty;
            string divLabel = String.Empty;
            string fileID = String.Empty;
            string dmdid = String.Empty;
            string amdid = String.Empty;
            ushort divOrder = 0;
            bool mainDivisionFound = false;
            abstract_TreeNode parentNode;
            Division_Tree thisDivTree = null;

            // Loop through reading each XML node
            do
            {
                // get the right division information based on node type
                switch (r.NodeType)
                {
                        // if EndElement, move up tree
                    case XmlNodeType.EndElement:
                        if (r.Name == "METS:structMap")
                        {
                            return;
                        }

                        if (r.Name == "METS:div")
                        {
                            // If there are more than one parent on the "parent stack" pop one off
                            if (parentNodes.Count > 0)
                                parentNodes.Pop();
                        }
                        break;

                        // if new element, add name and traverse tree
                    case XmlNodeType.Element:

                        // Is this the beginning of a structure map
                        if (r.Name == "METS:structMap")
                        {
                            thisDivTree = package.Divisions.Physical_Tree;
                            if (r.MoveToAttribute("TYPE"))
                            {
                                if (r.Value.ToUpper() == "OTHER")
                                    thisDivTree = package.Divisions.Download_Tree;
                            }
                        }

                        // Is this a new division?
                        if ((r.Name == "METS:div") && (r.HasAttributes))
                        {
                            // Since this is a new division, get all the possible attribute values or set to empty string
                            dmdid = (r.MoveToAttribute("DMDID") ? r.Value : String.Empty);
                            amdid = (r.MoveToAttribute("AMDID") ? r.Value : String.Empty);
                            divID = (r.MoveToAttribute("DMDID") ? r.Value : String.Empty);
                            divType = (r.MoveToAttribute("TYPE") ? r.Value : String.Empty);
                            divLabel = (r.MoveToAttribute("LABEL") ? r.Value : String.Empty);

                            // Get the order
                            if (r.MoveToAttribute("ORDER"))
                            {
                                if (!UInt16.TryParse(r.Value, out divOrder)) divOrder = 0;
                            }
                            else if (r.MoveToAttribute("ORDERLABEL"))
                            {
                                if (!UInt16.TryParse(r.Value, out divOrder)) divOrder = 0;
                            }
                            else
                            {
                                divOrder = 0;
                            }

                            // Was this an outer division, or the main division?
                            if (!mainDivisionFound) 
                            {
                                // This is an outer wrapper and NOT the MAIN division, so save this as an
                                // outer division (division greater than current digital resources), such as 
                                // used sometimes for serials or journals.
                                if (divType.ToUpper() != "MAIN")
                                {
                                    if (!package.Divisions.Contains_Outer_Division(divLabel, divType))
                                    {
                                        package.Divisions.Add_Outer_Division(divLabel, divOrder, divType);
                                    }
                                }
                                else
                                {
                                    mainDivisionFound = true;
                                }
                            }
                            else
                            {
                                // Get the parent node, if there is one
                                parentNode = parentNodes.Count > 0 ? parentNodes.Peek() : null;

                                // Create this division
                                abstract_TreeNode bibNode;
                                if (divType.ToUpper() == "PAGE")
                                    bibNode = new Page_TreeNode(divLabel);
                                else
                                    bibNode = new Division_TreeNode(divType, divLabel);

                                // Check to make sure no repeat here                               
                                if (divID.IndexOf("_repeat") > 0)
                                {
                                    divID = divID.Substring(0, divID.IndexOf("_repeat"));
                                    if (divisions_by_id.ContainsKey(divID))
                                    {
                                        bibNode = divisions_by_id[divID];
                                    }
                                }
                                
                                // Get the DMD sec or AMD sec's 
                                if (dmdid.Length > 0)
                                {
                                    string[] divDmdSecIds = dmdid.Split(" ".ToCharArray());
                                    foreach( string thisId in divDmdSecIds )
                                    {
                                        division_dmdids[thisId] = bibNode;
                                    }                                        
                                }
                                if (amdid.Length > 0)
                                {
                                    string[] divAmdSecIds = amdid.Split(" ".ToCharArray());
                                    foreach (string thisId in divAmdSecIds)
                                    {
                                        division_amdids[thisId] = bibNode;
                                    }
                                }

                                // If there is a parent, add to it
                                if (parentNode != null)
                                {
                                    ((Division_TreeNode) parentNode).Nodes.Add(bibNode);
                                }
                                else
                                {
                                    // No parent, so add this to the root
                                    thisDivTree.Roots.Add(bibNode);
                                }

                                // Now, add this to the end of the parent list, in case it has children
                                if (!r.IsEmptyElement)
                                {
                                    parentNodes.Push(bibNode);
                                }
                            }

                            r.MoveToElement();
                        }

                        // Is this a new file pointer applying to the last division?
                        if ((r.Name == "METS:fptr") && (r.MoveToAttribute("FILEID")))
                        {
                            // Get this file id
                            fileID = r.Value;

                            // Get the file from the files by id dictionary
                            if (files_by_fileid.ContainsKey(fileID))
                            {
                                SobekCM_File_Info thisFile = files_by_fileid[fileID];

                                abstract_TreeNode pageParentNode = null;
                                if (parentNodes.Count > 0)
                                    pageParentNode = parentNodes.Peek();


                                if ((pageParentNode != null) && (pageParentNode.Page))
                                {
                                    Page_TreeNode asPageNode = (Page_TreeNode) pageParentNode;
                                    if (!asPageNode.Files.Contains(thisFile))
                                        asPageNode.Files.Add(thisFile);
                                }
                                else
                                {
                                    if (pageParentNode == null)
                                    {
                                        thisDivTree.Add_File(thisFile);
                                    }
                                    else
                                    {
                                        Division_TreeNode asDivNode = (Division_TreeNode) pageParentNode;

                                        Page_TreeNode newPage = new Page_TreeNode();
                                        asDivNode.Add_Child(newPage);

                                        //parentNodes.Push(newPage);

                                        newPage.Files.Add(thisFile);
                                    }
                                }
                            }
                        }
                        break;
                } // end switch
            } while (r.Read());
        }
        /// <summary> Adds a file  object (with the appropriate divisions and pages) to this tree </summary>
        /// <param name="New_File"> New file object to add </param>
        /// <param name="Label"> Label for the page containing this file, if it is a new page </param>
        /// <remarks> This is generally used to add just a single file.  To add many files, better logic should be implemented </remarks>
        public void Add_File(SobekCM_File_Info New_File, string Label)
        {
            // Determine the upper case name
            string systemname_upper = New_File.File_Name_Sans_Extension;

            // Look for a page/entity which has the same file name, else it will be added to the last division
            foreach (abstract_TreeNode rootNode in Roots)
            {
                if (recursively_add_file(rootNode, New_File, systemname_upper))
                {
                    return;
                }
            }

            // If not found, find the last division
            if (Roots.Count > 0)
            {
                if (!Roots[Roots.Count - 1].Page)
                {
                    // Get his last division
                    Division_TreeNode lastDivision = (Division_TreeNode) Roots[Roots.Count - 1];

                    // Find the last division then
                    while ((lastDivision.Nodes.Count > 0) && (!lastDivision.Nodes[lastDivision.Nodes.Count - 1].Page))
                    {
                        lastDivision = (Division_TreeNode) lastDivision.Nodes[lastDivision.Nodes.Count - 1];
                    }

                    // Add this as a new page on the last division
                    Page_TreeNode newPage = new Page_TreeNode(Label);
                    lastDivision.Add_Child(newPage);

                    // Now, add this file to the page
                    newPage.Files.Add(New_File);
                }
                else
                {
                    // No divisions at all, but pages exist at the top level, which is okay
                    Page_TreeNode pageNode = (Page_TreeNode) Roots[Roots.Count - 1];

                    // Now, add this file to the page
                    pageNode.Files.Add(New_File);
                }
            }
            else
            {
                // No nodes exist, so add a MAIN division node
                Division_TreeNode newDivNode = new Division_TreeNode("Main", String.Empty);
                Roots.Add(newDivNode);

                // Add this as a new page on the new division
                Page_TreeNode newPage = new Page_TreeNode(Label);
                newDivNode.Add_Child(newPage);

                // Now, add this file to the page
                newPage.Files.Add(New_File);
            }
        }
        /// <summary> Adds all of the file information to a digital resource package by analyzing the directory </summary>
        /// <param name="BIBPackage">Digital resource package to enrich</param>
        /// <param name="FilesFilter"> Files to be added as page image files ( such as "*.tif|*.jpg|*.jp2" )</param>
        /// <param name="RecursivelyIncludeSubfolders"> Flag indicates if all files in subfolders should also be added </param>
        /// <param name="PageImagesInSeperateFoldersCanBeSamePage"> If two images with the same root are found in subfolders, should </param>
        public static void Add_All_Files(SobekCM_Item BIBPackage, string FilesFilter, bool RecursivelyIncludeSubfolders, bool PageImagesInSeperateFoldersCanBeSamePage)
        {
            // Get the set of file filters within a list
            List<string> file_filters = new List<string>();
            if (FilesFilter.IndexOf("|") < 0)
            {
                file_filters.Add(FilesFilter.ToUpper());
            }
            else
            {
                string[] splitter = FilesFilter.Split("|".ToCharArray());
                foreach (string thisFilter in splitter)
                {
                    file_filters.Add(thisFilter.ToUpper());
                }
            }

            // Get the files from the current directory (or recursive directories)
            Builder_Page_File_Collection fileCollection = new Builder_Page_File_Collection();
            get_files_from_current_directory(fileCollection, file_filters, BIBPackage.Source_Directory, String.Empty, RecursivelyIncludeSubfolders);

            // Now, determine which files are already in the METS file.
            // Build a collection of file objects from the METS
            List<SobekCM_File_Info> metsFiles = new List<SobekCM_File_Info>();
            Builder_Page_File_Collection metsFileCollection = new Builder_Page_File_Collection();
            Dictionary<SobekCM_File_Info, Page_TreeNode> fileToPage = new Dictionary<SobekCM_File_Info, Page_TreeNode>();
            Dictionary<Page_TreeNode, Division_TreeNode> pageToDiv = new Dictionary<Page_TreeNode, Division_TreeNode>();

            foreach (abstract_TreeNode rootNode in BIBPackage.Divisions.Physical_Tree.Roots)
            {
                recursively_add_all_METS_files(rootNode, metsFiles, metsFileCollection, fileToPage, pageToDiv, file_filters);
            }

            // Determine which files to delete from the METS package
            List<SobekCM_File_Info> deletes = new List<SobekCM_File_Info>();
            foreach (SobekCM_File_Info thisFile in metsFiles)
            {
                if ((thisFile.METS_LocType == SobekCM_File_Info_Type_Enum.SYSTEM) && (!File.Exists(BIBPackage.Source_Directory + "//" + thisFile.System_Name)))
                {
                    deletes.Add(thisFile);
                }
            }

            // Delete the files, and related pages
            foreach (SobekCM_File_Info thisFile in deletes)
            {
                metsFiles.Remove(thisFile);

                Page_TreeNode thisPage = fileToPage[thisFile];
                if (thisPage != null)
                {
                    thisPage.Files.Remove(thisFile);
                    Division_TreeNode thisDiv = pageToDiv[thisPage];
                    if (thisDiv != null)
                    {
                        thisDiv.Nodes.Remove(thisPage);
                    }
                }

                // Remove this from the other mets list
                int index = 0;
                int deleteIndex = -1;
                foreach (Builder_Page_File thisPageFile in metsFileCollection)
                {
                    if (thisPageFile.FullName.ToUpper() == thisFile.System_Name.ToUpper())
                    {
                        deleteIndex = index;
                        break;
                    }

                    index++;
                }
                if (deleteIndex >= 0)
                {
                    metsFileCollection.RemoveAt(deleteIndex);
                }
            }

            // Now, recursively check each division and remove empty divisions
            int rootNodeCounter = 0;
            while (rootNodeCounter < BIBPackage.Divisions.Physical_Tree.Roots.Count)
            {
                abstract_TreeNode rootNode = BIBPackage.Divisions.Physical_Tree.Roots[rootNodeCounter];
                if (recursively_remove_empty_divisions(rootNode))
                    BIBPackage.Divisions.Physical_Tree.Roots.Remove(rootNode);
                else
                    rootNodeCounter++;
            }

            // Build the list of all the remaining files
            Hashtable filesPresent = new Hashtable();
            foreach (SobekCM_File_Info thisFile in metsFiles)
            {
                filesPresent[thisFile.System_Name] = thisFile;
            }

            // Determine which files need to be added
            Builder_Page_File_Collection addFiles = new Builder_Page_File_Collection();
            foreach (Builder_Page_File thisFile in fileCollection)
            {
                if (!filesPresent.Contains(thisFile.FullName_With_Relative_Directory))
                {
                    addFiles.Add(thisFile);
                }
            }

            // Add files that need to be added
            if (addFiles.Count > 0)
            {
                // Make sure there is at least one division
                if (BIBPackage.Divisions.Physical_Tree.Roots.Count == 0)
                {
                    Division_TreeNode newRootNode = new Division_TreeNode("Main", String.Empty);
                    BIBPackage.Divisions.Physical_Tree.Roots.Add(newRootNode);
                }

                // Create the map of file names to pages
                Dictionary<string, Page_TreeNode> file_to_page_hash = new Dictionary<string, Page_TreeNode>();
                List<abstract_TreeNode> pageNodes = BIBPackage.Divisions.Physical_Tree.Pages_PreOrder;
                foreach (Page_TreeNode pageNode in pageNodes)
                {
                    if (pageNode.Files.Count > 0)
                    {
                        string first_page_name = pageNode.Files[0].File_Name_Sans_Extension;

                        if (first_page_name.IndexOf(".") > 0)
                            first_page_name = first_page_name.Substring(0, first_page_name.IndexOf("."));

                        if ((PageImagesInSeperateFoldersCanBeSamePage) || (pageNode.Files[0].METS_LocType == SobekCM_File_Info_Type_Enum.URL))
                        {
                            if (first_page_name.IndexOf("\\") > 0)
                            {
                                string[] slash_splitter = first_page_name.Split("\\".ToCharArray());
                                first_page_name = slash_splitter[slash_splitter.Length - 1];
                            }
                        }

                        if (!file_to_page_hash.ContainsKey(first_page_name.ToUpper()))
                        {
                            file_to_page_hash[first_page_name.ToUpper()] = pageNode;
                        }
                    }
                }

                // If there are no existing pages, this can be easily assembled
                if (metsFiles.Count == 0)
                {
                    try
                    {
                        // Get the first division
                        Division_TreeNode firstDiv = (Division_TreeNode) BIBPackage.Divisions.Physical_Tree.Roots[0];

                        // Add each file
                        foreach (Builder_Page_File thisFile in addFiles)
                        {
                            // Create the new METS file object
                            SobekCM_File_Info newFileForMETS = new SobekCM_File_Info(thisFile.FullName_With_Relative_Directory);

                            // Get the root of this file, to put all files of the same root on the same page
                            string thisFileShort = newFileForMETS.File_Name_Sans_Extension;
                            if (PageImagesInSeperateFoldersCanBeSamePage)
                            {
                                if (thisFileShort.IndexOf("\\") > 0)
                                {
                                    string[] slash_splitter = thisFileShort.Split("\\".ToCharArray());
                                    thisFileShort = slash_splitter[slash_splitter.Length - 1];
                                }
                            }

                            // Is this a pre-existing root ( therefore pre-existing page )?
                            if (file_to_page_hash.ContainsKey(thisFileShort))
                            {
                                // Just add this file to the pre-existing page
                                file_to_page_hash[thisFileShort].Files.Add(newFileForMETS);
                            }
                            else
                            {
                                // This needs a new page then
                                Page_TreeNode newPage = new Page_TreeNode();
                                newPage.Files.Add(newFileForMETS);
                                firstDiv.Nodes.Add(newPage);

                                // Add this page to the hash, so it is not added again later
                                file_to_page_hash[thisFileShort] = newPage;
                            }
                        }
                    }
                    catch
                    {
                    }
                }
                else
                {
                    // Configure the initial pointers
                    Builder_Page_File previous_file = null;
                    Builder_Page_File next_file = metsFileCollection[0];
                    Builder_Page_File new_file = addFiles[0];
                    int new_file_counter = 1;
                    int next_file_counter = 1;

                    // Loop through each file to be added
                    while (new_file != null)
                    {
                        // Create the new METS file object
                        SobekCM_File_Info newFileForMETS = new SobekCM_File_Info(new_file.FullName_With_Relative_Directory);

                        // Get the root of this file, to put all files of the same root on the same page
                        string thisFileShort = newFileForMETS.File_Name_Sans_Extension;
                        if (PageImagesInSeperateFoldersCanBeSamePage)
                        {
                            if (thisFileShort.IndexOf("\\") > 0)
                            {
                                string[] slash_splitter = thisFileShort.Split("\\".ToCharArray());
                                thisFileShort = slash_splitter[slash_splitter.Length - 1];
                            }
                        }

                        // First, ensure that we have not already added a page for this
                        if (file_to_page_hash.ContainsKey(thisFileShort))
                        {
                            // Just add this file to the pre-existing page
                            file_to_page_hash[thisFileShort].Files.Add(newFileForMETS);
                        }
                        else
                        {
                            // Move to the right part of the existing files list
                            while ((new_file.CompareTo(next_file) > 0) && (next_file != null))
                            {
                                previous_file = next_file;
                                if (next_file_counter < metsFileCollection.Count)
                                {
                                    next_file = metsFileCollection[next_file_counter++];
                                }
                                else
                                {
                                    next_file = null;
                                }
                            }

                            // Add the page for this and link the new file
                            Page_TreeNode newPage = new Page_TreeNode();
                            newPage.Files.Add(newFileForMETS);
                            file_to_page_hash[thisFileShort] = newPage;

                            // Get the parent division and add this page in the right place
                            // Check there was a previous page, otherwise this inserts at the very beginning
                            if (previous_file == null)
                            {
                                abstract_TreeNode abstractNode = BIBPackage.Divisions.Physical_Tree.Roots[0];
                                Division_TreeNode lastDivNode = (Division_TreeNode) abstractNode;
                                while (!abstractNode.Page)
                                {
                                    lastDivNode = (Division_TreeNode) abstractNode;
                                    if (lastDivNode.Nodes.Count > 0)
                                    {
                                        abstractNode = lastDivNode.Nodes[0];
                                    }
                                    else
                                    {
                                        break;
                                    }
                                }
                                lastDivNode.Nodes.Insert(0, newPage);
                                metsFileCollection.Insert(new_file);
                                new_file.METS_Division = lastDivNode;
                                new_file.METS_Page = newPage;
                                next_file = metsFileCollection[0];
                            }
                            else
                            {
                                Division_TreeNode parentDivNode = previous_file.METS_Division;
                                Page_TreeNode previousPageNode = previous_file.METS_Page;
                                int previousFileIndex = parentDivNode.Nodes.IndexOf(previousPageNode);
                                if (previousFileIndex + 1 >= parentDivNode.Nodes.Count)
                                    parentDivNode.Nodes.Add(newPage);
                                else
                                    parentDivNode.Nodes.Insert(previousFileIndex + 1, newPage);
                                next_file = previous_file;
                                next_file_counter--;
                                new_file.METS_Division = parentDivNode;
                                new_file.METS_Page = newPage;
                                metsFileCollection.Insert(new_file);
                            }
                        }

                        // Move to the next new file
                        if (new_file_counter < addFiles.Count)
                        {
                            new_file = addFiles[new_file_counter++];
                        }
                        else
                        {
                            new_file = null;
                        }
                    }
                }
            }
        }
 private void recurse_through_and_find_child_parent_relationship( Division_TreeNode parentNode )
 {
     foreach (abstract_TreeNode childNode in parentNode.Nodes)
     {
         if (childNode.Page)
         {
             childToParent[(Page_TreeNode) childNode] = parentNode;
         }
         else
         {
             recurse_through_and_find_child_parent_relationship((Division_TreeNode) childNode);
         }
     }
 }
        private void recurse_through_tree( Division_TreeNode parentNode, TreeNode parentViewNode, List<TreeNode> nodes, List<TreeNode> selectedNodes, List<TreeNode> pathNodes, ref int sequence )
        {
            foreach (abstract_TreeNode absNode in parentNode.Nodes )
            {
                if ( absNode.Page )
                {
                    sequence++;

                    foreach( TreeNode thisNode in nodes )
                    {
                        thisNode.Value = sequence.ToString();
                    }
                    if (sequence >= currentMode.Page)
                    {
                        if (!tocSelectedComplete)
                        {
                            selectedNodes.AddRange(pathNodes);
                            tocSelectedComplete = true;
                        }
                        else
                        {
                            if (sequence == currentMode.Page)
                            {
                                selectedNodes.AddRange(pathNodes);
                            }
                        }
                    }
                    nodes.Clear();
                }
                else
                {
                    Division_TreeNode divNode = (Division_TreeNode) absNode;
                    TreeNode treeViewNode = new TreeNode
                                                { Text = string.Format("<span class=\"SobekTocTreeViewItem\" Title='{0}'>{1}</span>", divNode.Display_Label, divNode.Display_Short_Label) };
                    parentViewNode.ChildNodes.Add( treeViewNode );
                    nodes.Add(treeViewNode);
                    List<TreeNode> pathNodes2 = new List<TreeNode> {treeViewNode};
                    recurse_through_tree(divNode, treeViewNode, nodes, selectedNodes, pathNodes2, ref sequence );
                }
            }
        }
        /// <summary> Saves the data rendered by this element to the provided bibliographic object during postback </summary>
        /// <param name="Bib"> Object into which to save the user's data, entered into the html rendered by this element </param>
        /// <remarks> This currently does not to anything, as this element is not fully implemented </remarks>
        public override void Save_To_Bib(SobekCM_Item Bib)
        {
            // Collect the list of download_files and download_labels from the form
            string[] getKeys = HttpContext.Current.Request.Form.AllKeys;
            string filename = String.Empty;
            List<string> download_files = new List<string>();
            List<string> download_labels = new List<string>();
            foreach (string thisKey in getKeys)
            {
                if (thisKey.IndexOf(html_element_name.Replace("_", "") + "_select") == 0)
                {
                    filename = HttpContext.Current.Request.Form[thisKey];
                }

                if (thisKey.IndexOf(html_element_name.Replace("_", "") + "_text") == 0)
                {
                    if (filename.Length > 0)
                    {
                        string label = HttpContext.Current.Request.Form[thisKey];
                        download_files.Add(filename.Replace(".*",""));
                        download_labels.Add(label);
                    }
                    filename = String.Empty;
                }
            }

            // Collect the list of download files and download labels from the package
            List<string> existing_files = new List<string>();
            List<string> existing_labels = new List<string>();
            List<abstract_TreeNode> downloadGroups = Bib.Divisions.Download_Tree.Pages_PreOrder;
            foreach (Page_TreeNode thisDownload in downloadGroups)
            {
                if (thisDownload.Files.Count > 0)
                {
                    string base_file = thisDownload.Files[0].File_Name_Sans_Extension;
                    if (!existing_files.Contains(base_file))
                    {
                        existing_files.Add(base_file );
                        existing_labels.Add(thisDownload.Label);
                    }
                }
            }

            // Now, compare the current list of downloads to the new list
            bool different = false;
            if (download_files.Count != existing_files.Count)
            {
                different = true;
            }
            else
            {
                // Same number of downloads, so step through and compare the files and labels
                for (int i = 0; i < download_files.Count; i++)
                {
                    // Get the index of this on the existing list
                    int index = existing_files.IndexOf(download_files[i].ToUpper());
                    if (index < 0)
                    {
                        different = true;
                        break;
                    }
                    if (existing_labels[index].Trim() != download_labels[i].Trim())
                    {
                        different = true;
                        break;
                    }
                }
            }

            // If this was different clear the existing downloads and load the new ones
            if (different)
            {
                // Get the directory for this package
                string directory = SobekCM_Library_Settings.Image_Server_Network + Bib.Web.AssocFilePath;

                // Clear existing
                Bib.Divisions.Download_Tree.Clear();

                // No nodes exist, so add a MAIN division node
                Division_TreeNode newDivNode = new Division_TreeNode("Main", String.Empty);
                Bib.Divisions.Download_Tree.Roots.Add(newDivNode);

                // Add a page for each
                for (int i = 0; i < download_files.Count; i++)
                {
                    // Get the list of matching files
                    string[] files = Directory.GetFiles(directory, download_files[i] + ".*");
                    if (files.Length > 0)
                    {
                        // Add this as a new page on the new division
                        Page_TreeNode newPage = new Page_TreeNode(download_labels[i]);
                        newDivNode.Add_Child(newPage);

                        // Add all the files next
                        foreach (SobekCM_File_Info newFile in files.Select(thisFile => (new FileInfo(thisFile)).Name).Select(add_filename => new SobekCM_File_Info(add_filename)))
                        {
                            newPage.Files.Add(newFile);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Clears all the page labels, division types and names, and reorders the pages by filename
        /// </summary>
        private void Clear_Pagination_And_Reorder_Pages()
        {
            SortedDictionary<string, Page_TreeNode> nodeToFilename = new SortedDictionary<string, Page_TreeNode>();
            int newPageCount = 0;

            // Add each page node to a sorted list/dictionary and clear the label
            foreach (Page_TreeNode thisNode in qc_item.Divisions.Physical_Tree.Pages_PreOrder)
            {
                thisNode.Label = String.Empty;
                string file_sans = "missing" + newPageCount;
                if (( thisNode.Files != null ) && ( thisNode.Files.Count > 0 ))
                    file_sans = thisNode.Files[0].File_Name_Sans_Extension;
                if (!nodeToFilename.ContainsKey(file_sans))
                {
                    nodeToFilename[file_sans] = thisNode;
                    newPageCount++;
                }

            }

            // Clear the physical (TOC) tree
            qc_item.Divisions.Physical_Tree.Clear();

            // Add the main node to the physical (TOC) division tree
            Division_TreeNode mainNode = new Division_TreeNode("Main", String.Empty);
            qc_item.Divisions.Physical_Tree.Roots.Add(mainNode);

            //Update the web Page count for this item
            qc_item.Web.Clear_Pages_By_Sequence();

            // Add back each page, in order by filename (sans extension)
            for (int i = 0; i < nodeToFilename.Count; i++)
            {
                mainNode.Add_Child(nodeToFilename.ElementAt(i).Value);
                qc_item.Web.Add_Pages_By_Sequence(nodeToFilename.ElementAt(i).Value);
            }

            //Update the QC web page count as well
            qc_item.Web.Static_PageCount = newPageCount;

            // Save the updated item to the session
            HttpContext.Current.Session[qc_item.BibID + "_" + qc_item.VID + " QC Work"] = qc_item;

            // Save to the temporary QC work section
            try
            {
                // Ensure the directory exists under the user's temporary mySobek InProcess folder
                if (!Directory.Exists(userInProcessDirectory))
                    Directory.CreateDirectory(userInProcessDirectory);

                // Save the METS
                qc_item.Save_METS(metsInProcessFile);
            }
            catch (Exception)
            {
                throw;
            }
        }
        private static void read_struct_map(XmlReader R, SobekCM_Item Package, Dictionary<string, SobekCM_File_Info> FilesByFileid, Dictionary<string, abstract_TreeNode> DivisionDmdids, Dictionary<string, abstract_TreeNode> DivisionAmdids )
        {
            Stack<abstract_TreeNode> parentNodes = new Stack<abstract_TreeNode>();
            Dictionary<string, abstract_TreeNode> divisions_by_id = new Dictionary<string, abstract_TreeNode>();

            bool mainDivisionFound = false;
            Division_Tree thisDivTree = null;

            // Loop through reading each XML node
            do
            {
                // get the right division information based on node type
                switch (R.NodeType)
                {
                        // if EndElement, move up tree
                    case XmlNodeType.EndElement:
                        if ((R.Name == "METS:structMap") || ( R.Name == "structMap" ))
                        {
                            return;
                        }

                        if ((R.Name == "METS:div") || (R.Name == "div"))
                        {
                            // If there are more than one parent on the "parent stack" pop one off
                            if (parentNodes.Count > 0)
                                parentNodes.Pop();
                        }
                        break;

                        // if new element, add name and traverse tree
                    case XmlNodeType.Element:

                        // Is this the beginning of a structure map
                        if ((R.Name == "METS:structMap") || (R.Name == "structMap"))
                        {
                            thisDivTree = Package.Divisions.Physical_Tree;
                            if (R.MoveToAttribute("TYPE"))
                            {
                                if (R.Value.ToUpper() == "OTHER")
                                    thisDivTree = Package.Divisions.Download_Tree;
                            }
                        }

                        // Is this a new division?
                        if (((R.Name == "METS:div") || ( R.Name == "div" )) && (R.HasAttributes))
                        {
                            // Since this is a new division, get all the possible attribute values or set to empty string
                            string dmdid = (R.MoveToAttribute("DMDID") ? R.Value : String.Empty);
                            string amdid = (R.MoveToAttribute("AMDID") ? R.Value : String.Empty);
                            string divID = (R.MoveToAttribute("DMDID") ? R.Value : String.Empty);
                            string divType = (R.MoveToAttribute("TYPE") ? R.Value : String.Empty);
                            string divLabel = (R.MoveToAttribute("LABEL") ? R.Value : String.Empty);

                            // Get the order
                            ushort divOrder;
                            if (R.MoveToAttribute("ORDER"))
                            {
                                if (!UInt16.TryParse(R.Value, out divOrder)) divOrder = 0;
                            }
                            else if (R.MoveToAttribute("ORDERLABEL"))
                            {
                                if (!UInt16.TryParse(R.Value, out divOrder)) divOrder = 0;
                            }
                            else
                            {
                                divOrder = 0;
                            }

                            // Was this an outer division, or the main division?
                            if (!mainDivisionFound) 
                            {
                                // This is an outer wrapper and NOT the MAIN division, so save this as an
                                // outer division (division greater than current digital resources), such as 
                                // used sometimes for serials or journals.
                                if (divType.ToUpper() != "MAIN")
                                {
                                    if (!Package.Divisions.Contains_Outer_Division(divLabel, divType))
                                    {
                                        Package.Divisions.Add_Outer_Division(divLabel, divOrder, divType);
                                    }
                                }
                                else
                                {
                                    mainDivisionFound = true;
                                }
                            }
                            else
                            {
                                // Get the parent node, if there is one
                                abstract_TreeNode parentNode = parentNodes.Count > 0 ? parentNodes.Peek() : null;

                                // Create this division
                                abstract_TreeNode bibNode;
                                if (divType.ToUpper() == "PAGE")
                                    bibNode = new Page_TreeNode(divLabel);
                                else
                                    bibNode = new Division_TreeNode(divType, divLabel);

                                // Check to make sure no repeat here                               
                                if (divID.IndexOf("_repeat") > 0)
                                {
                                    divID = divID.Substring(0, divID.IndexOf("_repeat"));
                                    if (divisions_by_id.ContainsKey(divID))
                                    {
                                        bibNode = divisions_by_id[divID];
                                    }
                                }
                                
                                // Get the DMD sec or AMD sec's 
                                if (dmdid.Length > 0)
                                {
                                    string[] divDmdSecIds = dmdid.Split(" ".ToCharArray());
                                    foreach( string thisId in divDmdSecIds )
                                    {
                                        DivisionDmdids[thisId] = bibNode;
                                    }                                        
                                }
                                if (amdid.Length > 0)
                                {
                                    string[] divAmdSecIds = amdid.Split(" ".ToCharArray());
                                    foreach (string thisId in divAmdSecIds)
                                    {
                                        DivisionAmdids[thisId] = bibNode;
                                    }
                                }

                                // If there is a parent, add to it
                                if (parentNode != null)
                                {
                                    ((Division_TreeNode) parentNode).Nodes.Add(bibNode);
                                }
                                else
                                {
                                    // No parent, so add this to the root
                                    thisDivTree.Roots.Add(bibNode);
                                }

                                // Now, add this to the end of the parent list, in case it has children
                                if (!R.IsEmptyElement)
                                {
                                    parentNodes.Push(bibNode);
                                }
                            }

                            R.MoveToElement();
                        }

                        // Is this a new file pointer applying to the last division?
                        if (((R.Name == "METS:fptr") || (R.Name == "fptr")) && (R.MoveToAttribute("FILEID")))
                        {
                            // Get this file id
                            string fileID = R.Value;

                            // Get the file from the files by id dictionary
                            if (FilesByFileid.ContainsKey(fileID))
                            {
                                SobekCM_File_Info thisFile = FilesByFileid[fileID];

                                abstract_TreeNode pageParentNode = null;
                                if (parentNodes.Count > 0)
                                    pageParentNode = parentNodes.Peek();


                                if ((pageParentNode != null) && (pageParentNode.Page))
                                {
                                    Page_TreeNode asPageNode = (Page_TreeNode) pageParentNode;
                                    if (!asPageNode.Files.Contains(thisFile))
                                        asPageNode.Files.Add(thisFile);
                                }
                                else
                                {
                                    if (pageParentNode == null)
                                    {
                                        thisDivTree.Add_File(thisFile);
                                    }
                                    else
                                    {
                                        Division_TreeNode asDivNode = (Division_TreeNode) pageParentNode;

                                        Page_TreeNode newPage = new Page_TreeNode();
                                        asDivNode.Add_Child(newPage);

                                        //parentNodes.Push(newPage);

                                        newPage.Files.Add(thisFile);
                                    }
                                }
                            }
                        }

                        // Is this a new METS pointer, often seen when importing from DSpace
                        if ((R.Name == "METS:mptr") || (R.Name == "mptr")) 
                        {
                            // Get the parent label
                            string parentLabel = String.Empty;

                            // Look for a parent node (should be one)
                            if (parentNodes.Count > 0)
                            {
                                abstract_TreeNode pageParentNode = parentNodes.Peek();
                                parentLabel = pageParentNode.Label;
                            }
                            else if (Package.Divisions.Outer_Division_Count > 0)
                            {
                                parentLabel = Package.Divisions.Outer_Divisions[Package.Divisions.Outer_Division_Count - 1].Label;
                            }

                            if ((!String.IsNullOrEmpty(parentLabel)) && (String.Compare(parentLabel, "Parent of this DSpace Object", StringComparison.OrdinalIgnoreCase) == 0))
                            {
                                // Is this a HANDLE reference?
                                string locType = (R.MoveToAttribute("LOCTYPE") ? R.Value : String.Empty).ToUpper();
                                string href = (R.MoveToAttribute("xlink:href") ? R.Value : String.Empty);

                                // If this has a handle href, make that the aggregation
                                if ((locType == "HANDLE") && (href.Length > 0))
                                {
                                    Package.Behaviors.Add_Aggregation(href);
                                }
                            }
                        }

                        break;

                } // end switch
            } while (R.Read());
        }
        /// <summary> Create a test digital resource item  </summary>       
        /// <param name="directory">Directory for the package source directory</param>
        /// <returns>Fully built test bib package</returns>
        public static SobekCM_Item Create(string directory)
        {
            SobekCM_Item testPackage = new SobekCM_Item();

            // Add all the METS header information
            testPackage.METS_Header.Create_Date = new DateTime(2007, 1, 1);
            testPackage.METS_Header.Modify_Date = DateTime.Now;
            testPackage.METS_Header.Creator_Individual = "Mark Sullivan";
            testPackage.METS_Header.Add_Creator_Individual_Notes("Programmer of new SobekCM.Resource_Object");
            testPackage.METS_Header.Add_Creator_Individual_Notes("Adding coordinates");
            testPackage.METS_Header.Creator_Organization = "University of Florida";
            testPackage.METS_Header.Creator_Software = "SobekCM Bib Package Test";
            testPackage.METS_Header.RecordStatus_Enum = METS_Record_Status.COMPLETE;
            testPackage.METS_Header.Add_Creator_Org_Notes("This test package was done to test DLCs new METS package");

            // Add all the MODS elements
            Abstract_Info testAbstract = testPackage.Bib_Info.Add_Abstract("This is a sample abstract", "en");
            testPackage.Bib_Info.Add_Abstract("Tämä on esimerkki abstrakteja", "fin");
            testAbstract.Display_Label = "Summary Abstract";
            testAbstract.Type = "summary";

            testPackage.Bib_Info.Access_Condition.Text = "All rights are reserved by source institution.";
            testPackage.Bib_Info.Access_Condition.Language = "en";
            testPackage.Bib_Info.Access_Condition.Type = "restrictions on use";
            testPackage.Bib_Info.Access_Condition.Display_Label = "Rights";

            testPackage.Bib_Info.Add_Identifier("000123234", "OCLC", "Electronic OCLC");
            testPackage.Bib_Info.Add_Identifier("182-asdsd-28k", "DOI");

            testPackage.Bib_Info.Add_Language("English", String.Empty, "en");
            testPackage.Bib_Info.Add_Language("Finnish");
            testPackage.Bib_Info.Add_Language(String.Empty, "ita", String.Empty);

            testPackage.Bib_Info.Location.Holding_Code = "MVS";
            testPackage.Bib_Info.Location.Holding_Name = "From the Private Library of Mark Sullivan";
            testPackage.Bib_Info.Location.PURL = "http://www.uflib.ufl.edu/ufdc/?b=CA00000000";
            testPackage.Bib_Info.Location.Other_URL = "http://www.fnhm.edu";
            testPackage.Bib_Info.Location.Other_URL_Display_Label = "Specimen Information";
            testPackage.Bib_Info.Location.Other_URL_Note = "Specimen FLAS 125342 Database";
            testPackage.Bib_Info.Location.EAD_URL = "http://digital.uflib.ufl.edu/";
            testPackage.Bib_Info.Location.EAD_Name = "Digital Library Center Finding Guide";

            testPackage.Bib_Info.Main_Entity_Name.Name_Type = Name_Info_Type_Enum.Personal;
            testPackage.Bib_Info.Main_Entity_Name.Full_Name = "Brown, B.F.";
            testPackage.Bib_Info.Main_Entity_Name.Terms_Of_Address = "Dr.";
            testPackage.Bib_Info.Main_Entity_Name.Display_Form = "B.F. Brown";
            testPackage.Bib_Info.Main_Entity_Name.Affiliation = "Chemistry Dept., American University";
            testPackage.Bib_Info.Main_Entity_Name.Description = "Chemistry Professor Emeritus";
            testPackage.Bib_Info.Main_Entity_Name.Add_Role("Author");

            Zoological_Taxonomy_Info taxonInfo = new Zoological_Taxonomy_Info();
            testPackage.Add_Metadata_Module(GlobalVar.ZOOLOGICAL_TAXONOMY_METADATA_MODULE_KEY, taxonInfo);
            taxonInfo.Scientific_Name = "Ctenomys sociabilis";
            taxonInfo.Higher_Classification = "Animalia; Chordata; Vertebrata; Mammalia; Theria; Eutheria; Rodentia; Hystricognatha; Hystricognathi; Ctenomyidae; Ctenomyini; Ctenomys";
            taxonInfo.Kingdom = "Animalia";
            taxonInfo.Phylum = "Chordata";
            taxonInfo.Class = "Mammalia";
            taxonInfo.Order = "Rodentia";
            taxonInfo.Family = "Ctenomyidae";
            taxonInfo.Genus = "Ctenomys";
            taxonInfo.Specific_Epithet = "sociabilis";
            taxonInfo.Taxonomic_Rank = "species";
            taxonInfo.Common_Name = "Social Tuco-Tuco";

            Name_Info name1 = new Name_Info();
            name1.Name_Type = Name_Info_Type_Enum.Personal;
            name1.Given_Name = "John Paul";
            name1.Terms_Of_Address = "Pope; II";
            name1.Dates = "1920-2002";
            name1.User_Submitted = true;
            testPackage.Bib_Info.Add_Named_Entity(name1);

            Name_Info name2 = new Name_Info();
            name2.Name_Type = Name_Info_Type_Enum.Conference;
            name2.Full_Name = "Paris Peace Conference (1919-1920)";
            name2.Dates = "1919-1920";
            testPackage.Bib_Info.Add_Named_Entity(name2);

            Name_Info name3 = new Name_Info();
            name3.Name_Type = Name_Info_Type_Enum.Corporate;
            name3.Full_Name = "United States -- Court of Appeals (2nd Court)";
            testPackage.Bib_Info.Add_Named_Entity(name3);

            Name_Info name4 = new Name_Info();
            name4.Name_Type = Name_Info_Type_Enum.Personal;
            name4.Full_Name = "Wilson, Mary";
            name4.Display_Form = "Mary 'Weels' Wilson";
            name4.Given_Name = "Mary";
            name4.Family_Name = "Wilson";
            name4.ID = "NAM4";
            name4.Terms_Of_Address = "2nd";
            name4.Add_Role("illustrator");
            name4.Add_Role("cartographer");
            testPackage.Bib_Info.Add_Named_Entity(name4);

            Name_Info donor = new Name_Info();
            donor.Name_Type = Name_Info_Type_Enum.Personal;
            donor.Full_Name = "Livingston, Arthur";
            donor.Description = "Gift in honor of Arthur Livingston";
            donor.Terms_Of_Address = "3rd";
            donor.Add_Role("honoree", String.Empty);
            testPackage.Bib_Info.Donor = donor;

            testPackage.Bib_Info.Main_Title.NonSort = "The ";
            testPackage.Bib_Info.Main_Title.Title = "Man Who Would Be King";
            testPackage.Bib_Info.Main_Title.Subtitle = "The story of succession in England";

            Title_Info title1 = new Title_Info("homme qui voulut être roi", Title_Type_Enum.Translated);
            title1.NonSort = "L'";
            title1.Language = "fr";
            testPackage.Bib_Info.Add_Other_Title(title1);

            Title_Info title2 = new Title_Info();
            title2.Title = "Man Who Be King";
            title2.Display_Label = "also known as";
            title2.NonSort = "The";
            title2.Title_Type = Title_Type_Enum.Alternative;
            testPackage.Bib_Info.Add_Other_Title(title2);

            Title_Info title3 = new Title_Info();
            title3.Title = "Great works of England";
            title3.Authority = "naf";
            title3.Add_Part_Name("Second Portion");
            title3.Add_Part_Number("2nd");
            title3.Title_Type = Title_Type_Enum.Uniform;
            title3.User_Submitted = true;
            testPackage.Bib_Info.Add_Other_Title(title3);

            testPackage.Bib_Info.Add_Note("Funded by the NEH", Note_Type_Enum.Funding);
            testPackage.Bib_Info.Add_Note("Based on a play which originally appeared in France as \"Un peu plus tard, un peu plus tôt\"").User_Submitted = true;
            testPackage.Bib_Info.Add_Note("Anne Baxter (Louise), Maria Perschy (Angela), Gustavo Rojo (Bill), Reginald Gilliam (Mr. Johnson), [Catherine Elliot?] (Aunt Sallie), Ben Tatar (waiter)", Note_Type_Enum.Performers, "Performed By");

            testPackage.Bib_Info.Origin_Info.Add_Place("New York", "nyu", "usa");
            testPackage.Bib_Info.Origin_Info.Date_Issued = "1992";
            testPackage.Bib_Info.Origin_Info.MARC_DateIssued_Start = "1992";
            testPackage.Bib_Info.Origin_Info.MARC_DateIssued_End = "1993";
            testPackage.Bib_Info.Origin_Info.Date_Copyrighted = "1999";
            testPackage.Bib_Info.Origin_Info.Edition = "2nd";

            Publisher_Info newPub = testPackage.Bib_Info.Add_Publisher("Published for the American Vacuum Society by the American Institute of Physics");
            newPub.Add_Place("New York, New York");
            newPub.User_Submitted = true;
            testPackage.Bib_Info.Add_Publisher("University of Florida Press House").Add_Place("Gainesville, FL");
            testPackage.Bib_Info.Add_Manufacturer("Addison Randly Publishing House");

            testPackage.Bib_Info.Original_Description.Extent = "1 sound disc (56 min.) : digital ; 3/4 in.";
            testPackage.Bib_Info.Original_Description.Add_Note("The sleeve of this sound disc was damaged in a fire");
            testPackage.Bib_Info.Original_Description.Add_Note("The disc has a moderate amount of scratches, but still plays");

            testPackage.Bib_Info.Series_Part_Info.Day = "18";
            testPackage.Bib_Info.Series_Part_Info.Day_Index = 18;
            testPackage.Bib_Info.Series_Part_Info.Month = "Syyskuu";
            testPackage.Bib_Info.Series_Part_Info.Month_Index = 9;
            testPackage.Bib_Info.Series_Part_Info.Year = "1992";
            testPackage.Bib_Info.Series_Part_Info.Year_Index = 1992;

            testPackage.Bib_Info.Series_Part_Info.Enum1 = "Volume 12";
            testPackage.Bib_Info.Series_Part_Info.Enum1_Index = 12;
            testPackage.Bib_Info.Series_Part_Info.Enum2 = "Issue 3";
            testPackage.Bib_Info.Series_Part_Info.Enum2_Index = 3;
            testPackage.Bib_Info.Series_Part_Info.Enum3 = "Part 1";
            testPackage.Bib_Info.Series_Part_Info.Enum3_Index = 1;

            testPackage.Behaviors.Serial_Info.Add_Hierarchy(1, 1992, "1992");
            testPackage.Behaviors.Serial_Info.Add_Hierarchy(2, 9, "Syyskuu");
            testPackage.Behaviors.Serial_Info.Add_Hierarchy(3, 18, "18");

            testPackage.Bib_Info.SeriesTitle.Title = "Shakespeare's most famous musicals";

            testPackage.Bib_Info.Add_Target_Audience("young adults");
            testPackage.Bib_Info.Add_Target_Audience("adolescent", "marctarget");

            testPackage.Bib_Info.SobekCM_Type = TypeOfResource_SobekCM_Enum.Newspaper;

            // Add cartographic subject
            Subject_Info_Cartographics newCartographics = testPackage.Bib_Info.Add_Cartographics_Subject();
            newCartographics.Scale = "1:2000";
            newCartographics.Projection = "Conical Projection";
            newCartographics.Coordinates = "E 72°--E 148°/N 13°--N 18°";

            // Add hierarchical geographic subject
            Subject_Info_HierarchicalGeographic hierarchical = testPackage.Bib_Info.Add_Hierarchical_Geographic_Subject();
            hierarchical.Continent = "North America";
            hierarchical.Country = "United States of America";
            hierarchical.State = "Kansas";
            hierarchical.County = "Butler";
            hierarchical.City = "Augusta";

            // Add hierarchical geographic subject
            Subject_Info_HierarchicalGeographic hierarchical2 = testPackage.Bib_Info.Add_Hierarchical_Geographic_Subject();
            hierarchical2.Region = "Arctic Ocean";

            // Add hierarchical geographic subject
            Subject_Info_HierarchicalGeographic hierarchical3 = testPackage.Bib_Info.Add_Hierarchical_Geographic_Subject();
            hierarchical3.Island = "Puerto Rico";
            hierarchical3.Language = "English";
            hierarchical3.Province = "Provincial";
            hierarchical3.Territory = "Puerto Rico";
            hierarchical3.Area = "Intercontinental areas (Western Hemisphere)";

            // Add a name subject
            Subject_Info_Name subjname1 = testPackage.Bib_Info.Add_Name_Subject();
            subjname1.Authority = "lcsh";
            subjname1.Full_Name = "Garcia Lorca, Federico";
            subjname1.Dates = "1898-1936";
            subjname1.Add_Geographic("Russia");
            subjname1.Add_Geographic("Moscow");
            subjname1.Add_Genre("maps");
            subjname1.User_Submitted = true;

            // Add a title information subject
            Subject_Info_TitleInfo subjtitle1 = testPackage.Bib_Info.Add_Title_Subject();
            subjtitle1.Title_Type = Title_Type_Enum.Uniform;
            subjtitle1.Authority = "naf";
            subjtitle1.Title = "Missale Carnotense";

            // Add a standard subject
            Subject_Info_Standard subject1 = testPackage.Bib_Info.Add_Subject();
            subject1.Authority = "lcsh";
            subject1.Add_Topic("Real property");
            subject1.Add_Geographic("Mississippi");
            subject1.Add_Geographic("Tippah County");
            subject1.Add_Genre("Maps");

            // Add a standard subject
            Subject_Info_Standard subject2 = testPackage.Bib_Info.Add_Subject();
            subject2.Add_Occupation("Migrant laborers");
            subject2.Add_Genre("School district case files");

            // Add a standard subject
            Subject_Info_Standard subject3 = testPackage.Bib_Info.Add_Subject();
            subject3.Authority = "lctgm";
            subject3.Add_Topic("Educational buildings");
            subject3.Add_Geographic("Washington (D.C.)");
            subject3.Add_Temporal("1890-1910");

            // Add a standard subject
            Subject_Info_Standard subject4 = testPackage.Bib_Info.Add_Subject();
            subject4.Authority = "rvm";
            subject4.Language = "french";
            subject4.Add_Topic("Église catholique");
            subject4.Add_Topic("Histoire");
            subject4.Add_Temporal("20e siècle");

            // Add record information
            testPackage.Bib_Info.Record.Add_Catalog_Language(new Language_Info("English", "eng", "en"));
            testPackage.Bib_Info.Record.Add_Catalog_Language(new Language_Info("French", "fre", "fr"));
            testPackage.Bib_Info.Record.MARC_Creation_Date = "080303";
            testPackage.Bib_Info.Record.Add_MARC_Record_Content_Sources("FUG");
            testPackage.Bib_Info.Record.Record_Origin = "Imported from (OCLC)001213124";

            // Test the items which are in the non-MODS portion of the Bib_Info object
            testPackage.BibID = "MVS0000001";
            testPackage.VID = "00001";
            testPackage.Bib_Info.SortDate = 1234;
            testPackage.Bib_Info.SortTitle = "MAN WHO WOULD BE KING";
            testPackage.Bib_Info.Add_Temporal_Subject(1990, 2002, "Recent history");
            testPackage.Bib_Info.Add_Temporal_Subject(1990, 2002, "Lähihistoria");
            testPackage.Bib_Info.Source.Code = "UF";
            testPackage.Bib_Info.Source.Statement = "University of Florida";

            // Add an affiliation
            Affiliation_Info affiliation1 = new Affiliation_Info();
            affiliation1.University = "University of Florida";
            affiliation1.Campus = "Gainesville Campus";
            affiliation1.College = "College of Engineering";
            affiliation1.Department = "Computer Engineering Department";
            affiliation1.Unit = "Robotics";
            affiliation1.Name_Reference = "NAM4";
            testPackage.Bib_Info.Add_Affiliation(affiliation1);

            // Add a related item
            Related_Item_Info relatedItem1 = new Related_Item_Info();
            relatedItem1.SobekCM_ID = "UF00001234";
            relatedItem1.Relationship = Related_Item_Type_Enum.Preceding;
            relatedItem1.Publisher = "Gainesville Sun Publishing House";
            relatedItem1.Add_Note(new Note_Info("Digitized with funding from NEH", Note_Type_Enum.Funding));
            relatedItem1.Add_Note(new Note_Info("Gainesville Bee was the precursor to this item"));
            relatedItem1.Main_Title.NonSort = "The";
            relatedItem1.Main_Title.Title = "Gainesville Bee";
            relatedItem1.Add_Identifier("01234353", "oclc");
            relatedItem1.Add_Identifier("002232311", "aleph");
            Name_Info ri_name = new Name_Info();
            ri_name.Full_Name = "Hills, Bryan";
            ri_name.Terms_Of_Address = "Mr.";
            ri_name.Name_Type = Name_Info_Type_Enum.Personal;
            ri_name.Add_Role("author");
            relatedItem1.Add_Name(ri_name);
            relatedItem1.URL = @"http://www.uflib.ufl.edu/ufdc/?b=UF00001234";
            relatedItem1.URL_Display_Label = "Full Text";
            testPackage.Bib_Info.Add_Related_Item(relatedItem1);

            // Add another related item
            Related_Item_Info relatedItem2 = new Related_Item_Info();
            relatedItem2.Relationship = Related_Item_Type_Enum.Succeeding;
            relatedItem2.SobekCM_ID = "UF00009999";
            relatedItem2.Main_Title.NonSort = "The";
            relatedItem2.Main_Title.Title = "Daily Sun";
            relatedItem2.Add_Identifier("0125437", "oclc");
            relatedItem2.Add_Note("Name change occured in Fall 1933");
            relatedItem2.Start_Date = "Fall 1933";
            relatedItem2.End_Date = "December 31, 1945";
            testPackage.Bib_Info.Add_Related_Item(relatedItem2);

            // Add some processing parameters
            testPackage.Behaviors.Add_Aggregation("JUV");
            testPackage.Behaviors.Add_Aggregation("DLOC");
            testPackage.Behaviors.Add_Aggregation("DLOSA1");
            testPackage.Behaviors.Add_Aggregation("ALICE");
            testPackage.Behaviors.Add_Aggregation("ARTE");

            testPackage.Web.GUID = "GUID!";
            testPackage.Behaviors.Add_Wordmark("DLOC");
            testPackage.Behaviors.Add_Wordmark("UFSPEC");
            testPackage.Behaviors.Main_Thumbnail = "00001thm.jpg";

            // Add some downloads
            testPackage.Divisions.Download_Tree.Add_File("MVS_Complete.PDF");
            testPackage.Divisions.Download_Tree.Add_File("MVS_Complete.MP2");
            testPackage.Divisions.Download_Tree.Add_File("MVS_Part1.MP2");
            testPackage.Divisions.Download_Tree.Add_File("MVS_Part1.PDF");

            // Add some coordinate information
            GeoSpatial_Information geoSpatial = new GeoSpatial_Information();
            testPackage.Add_Metadata_Module(GlobalVar.GEOSPATIAL_METADATA_MODULE_KEY, geoSpatial);
            geoSpatial.Add_Point(29.530151, -82.301459, "Lake Wauberg");
            geoSpatial.Add_Point(29.634352, -82.350640, "Veterinary School");
            Coordinate_Polygon polygon = new Coordinate_Polygon();
            polygon.Label = "University of Florida Campus";
            polygon.Add_Edge_Point(new Coordinate_Point(29.651435, -82.339869, String.Empty));
            polygon.Add_Edge_Point(new Coordinate_Point(29.641216, -82.340298, String.Empty));
            polygon.Add_Edge_Point(new Coordinate_Point(29.629503, -82.371969, String.Empty));
            polygon.Add_Edge_Point(new Coordinate_Point(29.649645, -82.371712, String.Empty));
            polygon.Add_Inner_Point(29.649794, -82.351971, "Stadium");
            polygon.Add_Inner_Point(29.650988, -82.341156, "Library");
            geoSpatial.Add_Polygon(polygon);
            Coordinate_Line line = new Coordinate_Line();
            line.Label = "Waldo Road";
            line.Add_Point(29.652852, -82.310944, "Gainesville");
            line.Add_Point(29.716681, -82.268372, String.Empty);
            line.Add_Point(29.791494, -82.167778, "Waldo");
            geoSpatial.Add_Line(line);

            // Add some performing arts information
            Performing_Arts_Info partInfo = new Performing_Arts_Info();
            testPackage.Add_Metadata_Module("PerformingArts", partInfo);
            partInfo.Performance = "Hamlet";
            partInfo.Performance_Date = "August 12, 1923";
            Performer performer1 = partInfo.Add_Performer("Sullivan, Mark");
            performer1.Sex = "M";
            performer1.LifeSpan = "1873-";
            performer1.Occupation = "actor";
            performer1.Title = "Mr.";

            Performer performer2 = partInfo.Add_Performer("Waldbart, Julia");
            performer2.Sex = "F";
            performer2.LifeSpan = "1876-";
            performer2.Occupation = "actress";
            performer2.Title = "Mrs.";

            // Add some oral history information
            Oral_Interview_Info oralInfo = new Oral_Interview_Info();
            testPackage.Add_Metadata_Module(  "OralInterview", oralInfo);
            oralInfo.Interviewee = "Edwards, Herm";
            oralInfo.Interviewer = "Proctor, Samual";

            // Add some learning object resource information
            LearningObjectMetadata lomInfo = new LearningObjectMetadata();
            testPackage.Add_Metadata_Module( GlobalVar.IEEE_LOM_METADATA_MODULE_KEY, lomInfo );
            lomInfo.AggregationLevel = AggregationLevelEnum.level3;
            lomInfo.Status = StatusEnum.draft;
            LOM_System_Requirements lomReq1 = new LOM_System_Requirements();
            lomReq1.RequirementType = RequirementTypeEnum.operating_system;
            lomReq1.Name.Value = "Windows";
            lomReq1.MinimumVersion = "Windows XP";
            lomReq1.MaximumVersion = "Windows 7";
            lomInfo.Add_SystemRequirements(lomReq1);
            LOM_System_Requirements lomReq2 = new LOM_System_Requirements();
            lomReq2.RequirementType = RequirementTypeEnum.software;
            lomReq2.Name.Value = "Java SDK";
            lomReq2.MinimumVersion = "1.7.1";
            lomReq2.MaximumVersion = "2.09";
            lomInfo.Add_SystemRequirements(lomReq2);
            lomInfo.InteractivityType = InteractivityTypeEnum.mixed;
            lomInfo.Add_LearningResourceType("exercise");
            lomInfo.Add_LearningResourceType("Tutorials", "encdlwebpedagogicaltype");
            lomInfo.InteractivityLevel = InteractivityLevelEnum.high;
            lomInfo.Add_IntendedEndUserRole(IntendedEndUserRoleEnum.learner);
            lomInfo.Add_Context("Undergraduate lower division", "enclearningcontext");
            lomInfo.Add_Context("15", "grade");
            lomInfo.Add_Context("16", "grade");
            lomInfo.Add_Context("5", "group");
            lomInfo.Add_TypicalAgeRange("suitable for children over 7", "en");
            lomInfo.Add_TypicalAgeRange("2-8");
            lomInfo.DifficultyLevel = DifficultyLevelEnum.medium;
            lomInfo.TypicalLearningTime = "PT45M";

            LOM_Classification lomClassification1 = new LOM_Classification();
            lomInfo.Add_Classification(lomClassification1);
            lomClassification1.Purpose.Value = "Discipline";
            LOM_TaxonPath lomTaxonPath1 = new LOM_TaxonPath();
            lomClassification1.Add_TaxonPath(lomTaxonPath1);
            lomTaxonPath1.Add_SourceName("ARIADNE");
            LOM_Taxon lomTaxon1 = new LOM_Taxon();
            lomTaxonPath1.Add_Taxon(lomTaxon1);
            lomTaxon1.ID = "BF120";
            lomTaxon1.Add_Entry("Work_History", "en");
            lomTaxon1.Add_Entry("Historie", "nl");
            LOM_Taxon lomTaxon2 = new LOM_Taxon();
            lomTaxonPath1.Add_Taxon(lomTaxon2);
            lomTaxon2.ID = "BF120.1";
            lomTaxon2.Add_Entry("American Work_History", "en");
            LOM_Taxon lomTaxon3 = new LOM_Taxon();
            lomTaxonPath1.Add_Taxon(lomTaxon3);
            lomTaxon3.ID = "BF120.1.4";
            lomTaxon3.Add_Entry("American Civil War", "en");

            LOM_Classification lomClassification2 = new LOM_Classification();
            lomInfo.Add_Classification(lomClassification2);
            lomClassification2.Purpose.Value = "Educational Objective";

            LOM_TaxonPath lomTaxonPath2 = new LOM_TaxonPath();
            lomClassification2.Add_TaxonPath(lomTaxonPath2);
            lomTaxonPath2.Add_SourceName("Common Core Standards", "en");
            LOM_Taxon lomTaxon4 = new LOM_Taxon();
            lomTaxonPath2.Add_Taxon(lomTaxon4);
            lomTaxon4.ID = "CCS.Math.Content";
            LOM_Taxon lomTaxon5 = new LOM_Taxon();
            lomTaxonPath2.Add_Taxon(lomTaxon5);
            lomTaxon5.ID = "3";
            lomTaxon5.Add_Entry("Grade 3", "en");
            LOM_Taxon lomTaxon6 = new LOM_Taxon();
            lomTaxonPath2.Add_Taxon(lomTaxon6);
            lomTaxon6.ID = "OA";
            lomTaxon6.Add_Entry("Operations and Algebraic Thinking", "en");
            LOM_Taxon lomTaxon7 = new LOM_Taxon();
            lomTaxonPath2.Add_Taxon(lomTaxon7);
            lomTaxon7.ID = "A";
            lomTaxon7.Add_Entry("Represent and solve problems involving multiplication and division.", "en");
            LOM_Taxon lomTaxon8 = new LOM_Taxon();
            lomTaxonPath2.Add_Taxon(lomTaxon8);
            lomTaxon8.ID = "3";
            lomTaxon8.Add_Entry("Use multiplication and division within 100 to solve word problems in situations involving equal groups, arrays, and measurement quantities, e.g., by using drawings and equations with a symbol for the unknown number to represent the problem.", "en");

            LOM_TaxonPath lomTaxonPath3 = new LOM_TaxonPath();
            lomClassification2.Add_TaxonPath(lomTaxonPath3);
            lomTaxonPath3.Add_SourceName("Common Core Standards", "en");
            LOM_Taxon lomTaxon14 = new LOM_Taxon();
            lomTaxonPath3.Add_Taxon(lomTaxon14);
            lomTaxon14.ID = "CCS.Math.Content";
            LOM_Taxon lomTaxon15 = new LOM_Taxon();
            lomTaxonPath3.Add_Taxon(lomTaxon15);
            lomTaxon15.ID = "3";
            lomTaxon15.Add_Entry("Grade 3", "en");
            LOM_Taxon lomTaxon16 = new LOM_Taxon();
            lomTaxonPath3.Add_Taxon(lomTaxon16);
            lomTaxon16.ID = "OA";
            lomTaxon16.Add_Entry("Operations and Algebraic Thinking", "en");
            LOM_Taxon lomTaxon17 = new LOM_Taxon();
            lomTaxonPath3.Add_Taxon(lomTaxon17);
            lomTaxon17.ID = "A";
            lomTaxon17.Add_Entry("Represent and solve problems involving multiplication and division.", "en");
            LOM_Taxon lomTaxon18 = new LOM_Taxon();
            lomTaxonPath3.Add_Taxon(lomTaxon18);
            lomTaxon18.ID = "4";
            lomTaxon18.Add_Entry("Determine the unknown whole number in a multiplication or division equation relating three whole numbers. For example, determine the unknown number that makes the equation true in each of the equations 8 × ? = 48, 5 = _ ÷ 3, 6 × 6 = ?", "en");

            // Add some views and interfaces
            testPackage.Behaviors.Clear_Web_Skins();
            testPackage.Behaviors.Add_Web_Skin("dLOC");
            testPackage.Behaviors.Add_Web_Skin("UFDC");
            testPackage.Behaviors.Add_View(View_Enum.JPEG2000);
            testPackage.Behaviors.Add_View(View_Enum.JPEG);
            testPackage.Behaviors.Add_View(View_Enum.RELATED_IMAGES);
            testPackage.Behaviors.Add_View(View_Enum.HTML, "Full Document", "MVS001214.html");

            // Create the chapters and pages and link them
            Division_TreeNode chapter1 = new Division_TreeNode("Chapter", "First Chapter");
            Page_TreeNode page1 = new Page_TreeNode("First Page");
            Page_TreeNode page2 = new Page_TreeNode("Page 2");
            chapter1.Nodes.Add(page1);
            chapter1.Nodes.Add(page2);
            Division_TreeNode chapter2 = new Division_TreeNode("Chapter", "Last Chapter");
            Page_TreeNode page3 = new Page_TreeNode("Page 3");
            Page_TreeNode page4 = new Page_TreeNode("Last Page");
            chapter2.Nodes.Add(page3);
            chapter2.Nodes.Add(page4);
            testPackage.Divisions.Physical_Tree.Roots.Add(chapter1);
            testPackage.Divisions.Physical_Tree.Roots.Add(chapter2);

            // Create the files
            SobekCM_File_Info file1_1 = new SobekCM_File_Info("2000626_0001.jp2", 2120, 1100);
            SobekCM_File_Info file1_2 = new SobekCM_File_Info("2000626_0001.jpg", 630, 330);
            SobekCM_File_Info file1_3 = new SobekCM_File_Info("2000626_0001.tif");
            SobekCM_File_Info file2_1 = new SobekCM_File_Info("2000626_0002.jp2", 1754, 2453);
            SobekCM_File_Info file2_2 = new SobekCM_File_Info("2000626_0002.jpg", 630, 832);
            SobekCM_File_Info file2_3 = new SobekCM_File_Info("2000626_0002.tif");
            SobekCM_File_Info file3_1 = new SobekCM_File_Info("2000626_0003.jp2", 2321, 1232);
            SobekCM_File_Info file3_2 = new SobekCM_File_Info("2000626_0003.jpg", 630, 342);
            SobekCM_File_Info file3_3 = new SobekCM_File_Info("2000626_0003.tif");
            SobekCM_File_Info file4_1 = new SobekCM_File_Info("2000626_0004.jp2", 2145, 1024);
            SobekCM_File_Info file4_2 = new SobekCM_File_Info("2000626_0004.jpg", 630, 326);
            SobekCM_File_Info file4_3 = new SobekCM_File_Info("2000626_0004.tif");

            // Link the files to the pages
            page1.Files.Add(file1_1);
            page1.Files.Add(file1_2);
            page1.Files.Add(file1_3);
            page2.Files.Add(file2_1);
            page2.Files.Add(file2_2);
            page2.Files.Add(file2_3);
            page3.Files.Add(file3_1);
            page3.Files.Add(file3_2);
            page3.Files.Add(file3_3);
            page4.Files.Add(file4_1);
            page4.Files.Add(file4_2);
            page4.Files.Add(file4_3);

            // Add the DAITSS information
            DAITSS_Info daitssInfo = new DAITSS_Info();
            daitssInfo.Account = "FTU";
            daitssInfo.SubAccount = "CLAS";
            daitssInfo.Project = "UFDC";
            daitssInfo.toArchive = true;
            testPackage.Add_Metadata_Module(GlobalVar.DAITSS_METADATA_MODULE_KEY, daitssInfo);

            // Save this package
            testPackage.Source_Directory = directory;
            return testPackage;
        }
        /// <summary> Save all the data from form post-back into the item in memory, and 
        /// return all the page information for those pages which are CHECKED (with the checkbox) </summary>
        /// <returns> Returns TRUE if successful, otherwise FALSE </returns>
        private bool Save_From_Form_Request_To_Item(string FilenameToMoveAfter, string FilenameToOmit, out List<QC_Viewer_Page_Division_Info> Selected_Page_Div_From_Form)
        {
            bool returnValue = true;

            // Get the current page number
            int current_qc_viewer_page_num = 1;
            if (CurrentRequest.ViewerCode.Replace("qc", "").Length > 0)
                Int32.TryParse(CurrentRequest.ViewerCode.Replace("qc", ""), out current_qc_viewer_page_num);

            // First, build a dictionary of all the pages ( filename --> page division object )
            Dictionary<Page_TreeNode, Division_TreeNode> pages_to_division = new Dictionary<Page_TreeNode, Division_TreeNode>();
            Dictionary<string, Page_TreeNode> pages_by_name = new Dictionary<string, Page_TreeNode>();
            List<Page_TreeNode> page_list = new List<Page_TreeNode>();
            List<string> page_filename_list = new List<string>();
            Division_TreeNode lastDivision = null;

            //Autonumber the remaining pages based on the selected option
            if (autonumber_mode_from_form == 0 || autonumber_mode_from_form == 1)
            {
                autonumber_mode = autonumber_mode_from_form;
                bool reached_last_page = false;
                bool reached_next_div = false;
                int number = 0;
                if (autonumber_number_system == "decimal")
                    number = Int32.Parse(autonumber_number_only) + 1;
                else if (autonumber_number_system.ToLower() == "roman")
                    //number = RomanToNumber(autonumber_number_only) + 1;
                    number = Int32.Parse(autonumber_number_only) + 1;

                //Do the autonumbering first
                foreach (abstract_TreeNode thisNode in qc_item.Divisions.Physical_Tree.Divisions_PreOrder)
                {
                    //Is this a division or a page node?
                    if (thisNode.Page)
                    {
                        Page_TreeNode thisPage = (Page_TreeNode)thisNode;

                        //Verify the page
                        if (thisPage.Files.Count > 0)
                        {
                            string filename = thisPage.Files[0].File_Name_Sans_Extension;

                            if (filename == hidden_autonumber_filename)
                            {
                                reached_last_page = true;
                            }
                            //if the last page displayed on the screen has been reached
                            else if (reached_last_page == true)
                            {
                                //Mode "0": Autonumber all pages of current division
                                //Mode "1": Autonumber all pages of the entire document
                                if ((autonumber_mode_from_form == 0 && reached_next_div == false) || (autonumber_mode_from_form == 1))
                                {
                                    if (autonumber_number_system == "decimal")
                                        thisPage.Label = autonumber_text_only + number.ToString();
                                    else if (autonumber_number_system == "ROMAN")
                                    {
                                        thisPage.Label = autonumber_text_only + NumberToRoman(number).ToUpper();
                                    }
                                    else
                                    {
                                        thisPage.Label = autonumber_text_only + NumberToRoman(number).ToLower();
                                    }
                                    number++;
                                }
                            }
                        }
                    }
                    else if (reached_last_page)
                    {
                        reached_next_div = true;
                    }
                }
            }

            //Move/Delete Pages as appropriate
            foreach (abstract_TreeNode thisNode in qc_item.Divisions.Physical_Tree.Divisions_PreOrder)
            {
                // Is this a division, or page node?
                if (thisNode.Page)
                {
                    Page_TreeNode thisPage = (Page_TreeNode)thisNode;
                    // Verify the page
                    if (thisPage.Files.Count > 0)
                    {
                        string filename = thisPage.Files[0].File_Name_Sans_Extension;
                        pages_by_name[filename] = thisPage;
                        page_filename_list.Add(filename);
                    }

                    // Add to the list of pages
                    page_list.Add(thisPage);

                    // Save the link from the page, up to the division
                    pages_to_division[thisPage] = lastDivision;
                }
                else
                {
                    lastDivision = (Division_TreeNode)thisNode;
                }
            }

            // Step through and collect all the form data
            List<QC_Viewer_Page_Division_Info> page_div_from_form = new List<QC_Viewer_Page_Division_Info>();
            List<Page_TreeNode> existing_pages_in_window = new List<Page_TreeNode>();

            //Get the list of pages to be moved
            Selected_Page_Div_From_Form = new List<QC_Viewer_Page_Division_Info>();

            try
            {
                // Now, step through each of the pages in the return
                string[] keysFromForm = HttpContext.Current.Request.Form.AllKeys;

                foreach (string thisKey in keysFromForm)
                {
                    // Has this gotten to the next page?
                    if ((thisKey.IndexOf("filename") == 0) && (thisKey.Length > 8))
                    {
                        // Create the qc viewer page information, and assign the filename
                        QC_Viewer_Page_Division_Info thisInfo = new QC_Viewer_Page_Division_Info { Filename = HttpContext.Current.Request.Form[thisKey] };

                        // Get the index to use for all the other keys
                        string thisIndex = thisKey.Substring(8);

                        // Get the page name
                        thisInfo.Page_Label = HttpContext.Current.Request.Form["textbox" + thisIndex];

                        // Was this page selected with the checkbox?  (for bulk delete or move)
                        //Get this info only if the move/delete operations are explicitly triggered
                        if (hidden_request == "delete_page" || hidden_request == "delete_selected_pages" || hidden_request == "move_selected_pages")
                        {
                            if ((HttpContext.Current.Request.Form["chkMoveThumbnail" + thisIndex] != null) || (thisInfo.Filename == FilenameToOmit))
                            {
                                thisInfo.Checkbox_Selected = true;
                                Selected_Page_Div_From_Form.Add(thisInfo);
                            }
                        }
                        // Is this a new division?
                        if (HttpContext.Current.Request.Form["newdiv" + thisIndex] != null)
                        {
                            thisInfo.New_Division = true;

                            // Get the new division type/label
                            thisInfo.Division_Type = HttpContext.Current.Request.Form["selectDivType" + thisIndex].Trim().Replace("!", "");
                            thisInfo.Division_Label = String.Empty;
                            if (HttpContext.Current.Request.Form["txtDivName" + thisIndex] != null)
                                thisInfo.Division_Label = HttpContext.Current.Request.Form["txtDivName" + thisIndex].Trim();
                            if (thisInfo.Division_Type.Length == 0)
                                thisInfo.Division_Type = "Chapter";

                            // Get the division config, based on the division type
                            if (qc_profile[thisInfo.Division_Type] != null)
                            {
                                QualityControl_Division_Config divInfo = qc_profile[thisInfo.Division_Type];

                                if (divInfo.BaseTypeName.Length > 0)
                                {
                                    thisInfo.Division_Label = thisInfo.Division_Type;
                                    thisInfo.Division_Type = divInfo.BaseTypeName;
                                }
                                else if (!divInfo.isNameable)
                                {
                                    thisInfo.Division_Label = String.Empty;
                                }
                            }
                        }
                        else
                        {
                            thisInfo.New_Division = false;
                        }

                        // Add this page to the collection
                        page_div_from_form.Add(thisInfo);

                        // Also, collect the page node from the mets/resource for clearing later
                        if (pages_by_name.ContainsKey(thisInfo.Filename))
                        {
                            Page_TreeNode existing_pagenode = pages_by_name[thisInfo.Filename];
                            existing_pages_in_window.Add(existing_pagenode);
                        }
                    }
                }

                // Determine the "window" that the user was seeing
                int window_first_page_index = page_filename_list.Count - 1;
                int window_last_page_index = 0;
                foreach (QC_Viewer_Page_Division_Info thisInfo in page_div_from_form)
                {
                    // Get the filename and then get the order
                    int page_order = page_filename_list.IndexOf(thisInfo.Filename);
                    if (page_order < window_first_page_index)
                        window_first_page_index = page_order;
                    if (page_order > window_last_page_index)
                        window_last_page_index = page_order;
                }

                // TODO: Do some sanity checks here to ensure it worked

                // Determine if the first page was part of an existing division (and not the first page in that division)
                Page_TreeNode window_first_page = page_list[window_first_page_index];
                Division_TreeNode window_first_division = pages_to_division[window_first_page];
                Division_TreeNode existing_division_containing_first_page = null;
                if (window_first_division.Nodes.IndexOf(window_first_page) > 0)
                    existing_division_containing_first_page = window_first_division;

                // Collect any additional, non-cleared pages from the division which contained the last page originally
                List<Page_TreeNode> remnant_pages = new List<Page_TreeNode>();
                Page_TreeNode window_last_page = page_list[window_last_page_index];
                Division_TreeNode window_last_division = pages_to_division[window_last_page];
                if (window_last_division.Nodes.IndexOf(window_last_page) < window_last_division.Nodes.Count)
                {
                    for (int i = window_last_division.Nodes.IndexOf(window_last_page) + 1;
                         i < window_last_division.Nodes.Count;
                         i++)
                    {
                        remnant_pages.Add((Page_TreeNode)window_last_division.Nodes[i]);
                    }
                }

                // Clear the window pages completely, including the remnant pages, which we add back at the end
                int index_within_chapter_roots_to_begin_insert = qc_item.Divisions.Physical_Tree.Roots.Count;
                foreach (Page_TreeNode thisNode in existing_pages_in_window)
                {
                    // Get the parent division, to clear this
                    Division_TreeNode parentNode = pages_to_division[thisNode];
                    parentNode.Nodes.Remove(thisNode);

                    // What is the index within the list of chapters
                    int this_root_index = qc_item.Divisions.Physical_Tree.Roots.IndexOf(parentNode) + 1;

                    // Does this clear out the chapter completely?
                    if (parentNode.Nodes.Count == 0)
                    {
                        qc_item.Divisions.Physical_Tree.Roots.Remove(parentNode);
                        this_root_index--;
                    }

                    // If this insert point is prior to the previously collected insert point, use this one for the first chapter
                    if (this_root_index < index_within_chapter_roots_to_begin_insert)
                        index_within_chapter_roots_to_begin_insert = this_root_index;
                }
                foreach (Page_TreeNode thisNode in remnant_pages)
                {
                    // Get the parent division, to clear this
                    window_last_division.Nodes.Remove(thisNode);

                    // Does this clear out the chapter completely?
                    if (window_last_division.Nodes.Count == 0)
                    {
                        qc_item.Divisions.Physical_Tree.Roots.Remove(window_last_division);
                    }
                }

                int move_into_division_index = -1;
                int move_into_node_index = -1;

                // Add each page from the original form
                Division_TreeNode last_added_division = existing_division_containing_first_page;
                foreach (QC_Viewer_Page_Division_Info pageInfo in page_div_from_form)
                {
                    // Is this a new division?
                    if (pageInfo.New_Division)
                    {
                        // If there was a last division, ensure some pages were added and add to the METS
                        if (last_added_division != null)
                        {
                            // Were any pages added to this last div?
                            if (last_added_division.Nodes.Count > 0)
                            {
                                // Since there were pages, add this to the METS
                                qc_item.Divisions.Physical_Tree.Roots.Insert(index_within_chapter_roots_to_begin_insert++, last_added_division);
                            }
                        }

                        // Create the new division
                        last_added_division = new Division_TreeNode(pageInfo.Division_Type, pageInfo.Division_Label);
                    }

                    // Get the page tree node and assign the new page label
                    Page_TreeNode thisPage = pages_by_name[pageInfo.Filename];
                    thisPage.Label = pageInfo.Page_Label;

                    // Add this page to the last division (possibly just created above) assuming it is
                    // not marked for removal (either by mass delete or mass move)
                    if (!pageInfo.Checkbox_Selected)
                        last_added_division.Add_Child(thisPage);
                    else
                    {
                        // Save the built page node for later, in case they will be MOVED
                        pageInfo.METS_StructMap_Page_Node = thisPage;
                    }

                    // Were we involved in a mass move, in which case we are looking for the insertion point?
                    if ((FilenameToMoveAfter.Length > 0) && (move_into_division_index < 0) && (pageInfo.Filename == FilenameToMoveAfter))
                    {
                        move_into_division_index = index_within_chapter_roots_to_begin_insert;
                        move_into_node_index = last_added_division.Nodes.Count;
                    }
                }

                // Handle all the remnant by adding to the last division
                foreach (Page_TreeNode thisNode in remnant_pages)
                {
                    last_added_division.Add_Child(thisNode);
                }

                // Handle any unfinished divisions
                // If there was a last division, ensure some pages were added and add to the METS
                if (last_added_division != null)
                {
                    // Were any pages added to this last div?
                    if (last_added_division.Nodes.Count > 0)
                    {
                        // Since there were pages, add this to the METS
                        if (index_within_chapter_roots_to_begin_insert > qc_item.Divisions.Physical_Tree.Roots.Count)
                            index_within_chapter_roots_to_begin_insert = qc_item.Divisions.Physical_Tree.Roots.Count;

                        qc_item.Divisions.Physical_Tree.Roots.Insert(index_within_chapter_roots_to_begin_insert++, last_added_division);
                    }
                }

                // Insert any pages which were moved
                if ((FilenameToMoveAfter.Length > 0) && (Selected_Page_Div_From_Form.Count > 0))
                {
                    // TODO: Check for the lack of any divisions what-so-ever within the METS.  If so, add one.

                    Division_TreeNode divNodeToInsertWithin = null;

                    // Get the division
                    if (move_into_division_index >= 0)
                        divNodeToInsertWithin = (Division_TreeNode)qc_item.Divisions.Physical_Tree.Roots[move_into_division_index];
                    else if (FilenameToMoveAfter == "[BEFORE FIRST]")
                    {
                        divNodeToInsertWithin = (Division_TreeNode)qc_item.Divisions.Physical_Tree.Roots[0];
                        move_into_node_index = 0;
                    }

                    if (divNodeToInsertWithin != null)
                    {
                        // Insert each page in order
                        foreach (QC_Viewer_Page_Division_Info insertPage in Selected_Page_Div_From_Form)
                        {
                            divNodeToInsertWithin.Nodes.Insert(move_into_node_index++, insertPage.METS_StructMap_Page_Node);
                        }
                    }
                }

                // Save the updated to the session
                HttpContext.Current.Session[qc_item.BibID + "_" + qc_item.VID + " QC Work"] = qc_item;

                // Save to the temporary QC work section

                // Ensure the directory exists under the user's temporary mySobek InProcess folder
                if (!Directory.Exists(userInProcessDirectory))
                    Directory.CreateDirectory(userInProcessDirectory);

                // Save the METS
                qc_item.Save_METS(metsInProcessFile);

                // Determine the total size of the package before saving
                string[] all_files_final = Directory.GetFiles(userInProcessDirectory);
                double size = all_files_final.Aggregate<string, double>(0, (current, thisFile) => current + (((new FileInfo(thisFile)).Length) / 1024));
                qc_item.DiskSize_KB = size;
            }
            catch (Exception e)
            {
                string error_folder = UI_ApplicationCache_Gateway.Settings.Servers.Image_Server_Network + qc_item.Web.AssocFilePath + "\\sobek_files";
                if (!Directory.Exists(error_folder))
                    Directory.CreateDirectory(error_folder);
                string error_message_file = "qc_error_" + DateTime.Now.Year + "_" + DateTime.Now.Month.ToString().PadLeft(2, '0') + "_" + DateTime.Now.Day.ToString().PadLeft(2, '0') + "_" + DateTime.Now.Hour.ToString().PadLeft(2, '0') + "_" + DateTime.Now.Minute.ToString().PadLeft(2, '0') + "_" + DateTime.Now.Second.ToString().PadLeft(2, '0') + "_" + DateTime.Now.Millisecond + ".txt";
                StreamWriter writer = new StreamWriter(error_folder + "\\" + error_message_file, true);

                writer.WriteLine("EXCEPTION CAUGHT DURING SAVE_FROM_FORM_REQUEST_TO_ITEM METHOD (2nd spot)");
                writer.WriteLine();
                writer.WriteLine(e.Message);
                writer.WriteLine();
                writer.WriteLine(e.StackTrace);
                writer.WriteLine();
                try
                {
                    writer.WriteLine(qc_item.Divisions.Physical_Tree.Pages_PreOrder.Count + " PAGES");
                }
                catch
                {
                    writer.WriteLine("ERROR GETTING PAGE COUNT");
                }
                writer.Flush();
                writer.Close();

                // Also, send an email
                Email_Helper.SendEmail("*****@*****.**", "QC Error caught on " + qc_item.BibID + ":" + qc_item.VID, "EXCEPTION CAUGHT DURING SAVE_FROM_FORM_REQUEST_TO_ITEM METHOD\n\n" + e.Message + "\n\n" + e.StackTrace, false, UI_ApplicationCache_Gateway.Settings.System.System_Name);

                // Rethrow this error
                throw new ApplicationException(e.Message);
            }

            // Return the flag indicating success
            return returnValue;
        }
        private void recurse_through_tree(Division_TreeNode ParentNode, TreeNode ParentViewNode, List<TreeNode> Nodes, List<TreeNode> SelectedNodes, List<TreeNode> PathNodes, ref int Sequence)
        {
            foreach (abstract_TreeNode absNode in ParentNode.Nodes)
            {
                if (absNode.Page)
                {
                    Sequence++;

                    foreach (TreeNode thisNode in Nodes)
                    {
                        thisNode.Value = Sequence.ToString();
                    }
                    if (Sequence >= RequestSpecificValues.Current_Mode.Page)
                    {
                        if (!tocSelectedComplete)
                        {
                            SelectedNodes.AddRange(PathNodes);
                            tocSelectedComplete = true;
                        }
                        else
                        {
                            if (Sequence == RequestSpecificValues.Current_Mode.Page)
                            {
                                SelectedNodes.AddRange(PathNodes);
                            }
                        }
                    }
                    Nodes.Clear();
                }
                else
                {
                    Division_TreeNode divNode = (Division_TreeNode)absNode;
                    TreeNode treeViewNode = new TreeNode { Text = string.Format("<span class=\"SobekTocTreeViewItem\" Title='{0}'>{1}</span>", divNode.Display_Label, divNode.Display_Short_Label) };
                    ParentViewNode.ChildNodes.Add(treeViewNode);
                    Nodes.Add(treeViewNode);
                    List<TreeNode> pathNodes2 = new List<TreeNode> { treeViewNode };
                    recurse_through_tree(divNode, treeViewNode, Nodes, SelectedNodes, pathNodes2, ref Sequence);
                }
            }
        }
        /// <summary> This provides an opportunity for the viewer to perform any pre-display work
        /// which is necessary before entering any of the rendering portions </summary>
        /// <param name="Tracer"> Trace object keeps a list of each method executed and important milestones in rendering </param>
        public void Perform_PreDisplay_Work(Custom_Tracer Tracer)
        {
            //Get the item details from the DB
            Item_Detail = Engine_Database.Get_Item_Information(BriefItem.BibID, BriefItem.VID, Tracer);

            if (Item_Detail != null)
            {
                //get the main thumbnail & JPEG filenames from the database
                mainThumbnailFileName_from_db = Item_Detail["MainThumbnailFile"].ToString();
                mainJPGFileName_from_db = Item_Detail["MainJPEGFile"].ToString();

                //Get the filenames without the extensions
                int length_thumbnail = mainThumbnailFileName_from_db.IndexOf("thm.jpg");
                int lengthJpeg = mainJPGFileName_from_db.IndexOf(".jpg");

                if (length_thumbnail > 0)
                    mainThumbnailFileName_from_db = mainThumbnailFileName_from_db.Substring(0, length_thumbnail);

                if (lengthJpeg > 0)
                    mainJPGFileName_from_db = mainJPGFileName_from_db.Substring(0, lengthJpeg);

            }
            //Get the Drag & Drop setting from the user options
            makeSortable = CurrentUser.Get_Setting("QC_ItemViewer:SortableMode", 3);

            // Get the proper number of thumbnails per page
            // First, pull the thumbnails per page from the user options
            thumbnailsPerPage = CurrentUser.Get_Setting("QC_ItemViewer:ThumbnailsPerPage", 1000);

            // Or was there a new value in the URL?
            if (CurrentRequest.Thumbnails_Per_Page >= -1)
            {
                CurrentUser.Add_Setting("QC_ItemViewer:ThumbnailsPerPage", CurrentRequest.Thumbnails_Per_Page);
                thumbnailsPerPage = CurrentRequest.Thumbnails_Per_Page.HasValue ? CurrentRequest.Thumbnails_Per_Page.Value : -100;

                // Now, reset the value in the navigation object, since we won't need to set it again
                CurrentRequest.Thumbnails_Per_Page = -100;
            }

            // -1 means to display all thumbnails (which is now capped at 1000)
            if (thumbnailsPerPage == -1)
                thumbnailsPerPage = 1000;

            // Get the proper size of thumbnails per page
            // First, pull the thumbnails per page from the user options
            thumbnailSize = CurrentUser.Get_Setting("QC_ItemViewer:ThumbnailSize", 1);

            // Or was there a new value in the URL?
            if (CurrentRequest.Size_Of_Thumbnails > -1)
            {
                CurrentUser.Add_Setting("QC_ItemViewer:ThumbnailSize", CurrentRequest.Size_Of_Thumbnails);
                thumbnailSize = CurrentRequest.Size_Of_Thumbnails.HasValue ? CurrentRequest.Size_Of_Thumbnails.Value : -1;

                //Now reset the current mode value since we won't need to set it again
                CurrentRequest.Size_Of_Thumbnails = -1;
            }

            // Get the autonumbering mode
            // First, pull the autonumbering mode from the user options
            autonumber_mode = CurrentUser.Get_Setting("QC_ItemViewer:AutonumberingMode", 0);

            //Also pull the Sortable mode from the user options
            makeSortable = CurrentUser.Get_Setting("QC_ItemViewer:SortableMode", 3);

            // Ensure there are no pages directly under the item
            List<abstract_TreeNode> add_to_new_main = new List<abstract_TreeNode>();
            foreach (abstract_TreeNode rootNode in qc_item.Divisions.Physical_Tree.Roots)
            {
                if (rootNode.Page)
                {
                    add_to_new_main.Add(rootNode);
                }
            }
            if (add_to_new_main.Count > 0)
            {
                Division_TreeNode newMain = new Division_TreeNode("Main", String.Empty);
                qc_item.Divisions.Physical_Tree.Roots.Add(newMain);
                foreach (abstract_TreeNode thisNode in add_to_new_main)
                    newMain.Add_Child(thisNode);
            }

            // Now, build a list from child node to parent node
            childToParent = new Dictionary<Page_TreeNode, Division_TreeNode>();
            foreach (abstract_TreeNode rootNode in qc_item.Divisions.Physical_Tree.Roots)
            {
                if (!rootNode.Page)
                {
                    recurse_through_and_find_child_parent_relationship((Division_TreeNode)rootNode);
                }
            }
        }