public bool Write_Metadata(TextWriter Output_Stream, SobekCM_Item Item_To_Save, Dictionary<string, object> Options, out string Error_Message)
{
// PERHAPS MAKE THESE OPTIONS?
List<string> mimes_to_exclude = new List<string>();
const bool MINIMIZE_FILE_SIZE = false;
// Get the METS writing profile
METS_Writing_Profile profile = ResourceObjectSettings.MetadataConfig.Default_METS_Writing_Profile;
// Set default error outpt message
Error_Message = String.Empty;
// Ensure the METS ID is set from BibID and VID
if (Item_To_Save.METS_Header.ObjectID.Length == 0)
{
Item_To_Save.METS_Header.ObjectID = Item_To_Save.BibID + "_" + Item_To_Save.VID;
if (Item_To_Save.VID.Length == 0)
Item_To_Save.METS_Header.ObjectID = Item_To_Save.BibID;
}
// If this is bib level, it is different
if ((Item_To_Save.VID == "*****") || (Item_To_Save.METS_Header.RecordStatus_Enum == METS_Record_Status.BIB_LEVEL))
Item_To_Save.METS_Header.ObjectID = Item_To_Save.BibID;
// If this is juat a project XML, do this differently
if (Item_To_Save.Bib_Info.SobekCM_Type == TypeOfResource_SobekCM_Enum.Project)
{
Item_To_Save.Bib_Info.Main_Title.Title = "Project level metadata for '" + Item_To_Save.BibID + "'";
Item_To_Save.METS_Header.ObjectID = Item_To_Save.BibID;
}
// Ensure the source code appears in the METS header correctly
Item_To_Save.METS_Header.Creator_Organization = Item_To_Save.Bib_Info.Source.Code;
if ((Item_To_Save.Bib_Info.Source.Statement.Length > 0) && (String.Compare(Item_To_Save.Bib_Info.Source.Statement, Item_To_Save.Bib_Info.Source.Code, StringComparison.OrdinalIgnoreCase) != 0))
{
Item_To_Save.METS_Header.Creator_Organization = Item_To_Save.Bib_Info.Source.Code + "," + Item_To_Save.Bib_Info.Source.XML_Safe_Statement;
}
// Get the list of divisions from the physical tree and the download/other file tre
List<abstract_TreeNode> physicalDivisions = Item_To_Save.Divisions.Physical_Tree.Divisions_PreOrder;
List<abstract_TreeNode> downloadDivisions = Item_To_Save.Divisions.Download_Tree.Divisions_PreOrder;
List<abstract_TreeNode> allDivisions = new List<abstract_TreeNode>();
List<SobekCM_File_Info> allFiles = new List<SobekCM_File_Info>();
#region Prepare the list of divisions and files to be written by assigning proper ID's and groupid's to all
// Create some private variables for this
int page_and_group_number = 1;
int division_number = 1;
bool hasPageFiles = false;
bool hasDownloadFiles = false;
Dictionary<string, List<SobekCM_File_Info>> mimeHash = new Dictionary<string, List<SobekCM_File_Info>>();
List<string> fileids_used = new List<string>();
// Clear any existing ID's here since the ID's are only used for writing METS files
foreach (abstract_TreeNode thisNode in physicalDivisions)
{
thisNode.ID = String.Empty;
thisNode.DMDID = String.Empty;
thisNode.ADMID = String.Empty;
}
// First, assign group numbers for all the files on each page (physical or other)
// Group numbers in the METS file correspond to files on the same page (physical or other)
// At the same time, we will build the list of all files and files by mime type
foreach (abstract_TreeNode thisNode in physicalDivisions)
{
// If this node was already hit (perhaps if a div has two parent divs),
// then skip it
if (thisNode.ID.Length > 0)
continue;
// Add this to the list of all nodes
allDivisions.Add(thisNode);
// Was this a PAGE or a DIVISION node?
if (thisNode.Page)
{
// Get this page node
Page_TreeNode pageNode = (Page_TreeNode) thisNode;
// Only do anything if there are actually any files here
if ((pageNode.Files != null) && (pageNode.Files.Count > 0))
{
// Set the page ID here
pageNode.ID = "PAGE" + page_and_group_number;
// Step through any files under this page
foreach (SobekCM_File_Info thisFile in pageNode.Files)
{
// Set the ADMID and DMDID to empty in preparation for METS writing
thisFile.ADMID = String.Empty;
thisFile.DMDID = String.Empty;
// If no file name, skip this
if (String.IsNullOrEmpty(thisFile.System_Name))
continue;
// Get this file extension and MIME type
string fileExtension = thisFile.File_Extension;
string mimetype = thisFile.MIME_Type(thisFile.File_Extension);
// If this is going to be excluded from appearing in the METS file, just skip
// it here as well.
if (!mimes_to_exclude.Contains(mimetype))
{
// Set the group number on this file
thisFile.Group_Number = "G" + page_and_group_number;
// Set the ID for this file as well
switch (mimetype)
{
case "image/tiff":
thisFile.ID = "TIF" + page_and_group_number;
break;
case "text/plain":
thisFile.ID = "TXT" + page_and_group_number;
break;
case "image/jpeg":
if (thisFile.System_Name.ToLower().IndexOf("thm.jp") > 0)
{
thisFile.ID = "THUMB" + page_and_group_number;
mimetype = mimetype + "-thumbnails";
}
else
thisFile.ID = "JPEG" + page_and_group_number;
break;
case "image/gif":
if (thisFile.System_Name.ToLower().IndexOf("thm.gif") > 0)
{
thisFile.ID = "THUMB" + page_and_group_number;
mimetype = mimetype + "-thumbnails";
}
else
thisFile.ID = "GIF" + page_and_group_number;
break;
case "image/jp2":
thisFile.ID = "JP2" + page_and_group_number;
break;
default:
if (fileExtension.Length > 0)
{
thisFile.ID = fileExtension + page_and_group_number;
}
else
{
thisFile.ID = "NOEXT" + page_and_group_number;
}
break;
}
// Ensure this fileid is really unique. It may not be if there are multiple
// files of the same mime-type in the same page. (such as 0001.jpg and 0001.qc.jpg)
if (fileids_used.Contains(thisFile.ID))
{
int count = 2;
while (fileids_used.Contains(thisFile.ID + "." + count))
count++;
thisFile.ID = thisFile.ID + "." + count;
}
// Save this file id
fileids_used.Add(thisFile.ID);
// Also add to the list of files
allFiles.Add(thisFile);
// Also ensure we know there are page image files
hasPageFiles = true;
// If this is a new MIME type, add it, else just save this file in the MIME hash
if (!mimeHash.ContainsKey(mimetype))
{
List<SobekCM_File_Info> newList = new List<SobekCM_File_Info> {thisFile};
mimeHash[mimetype] = newList;
}
else
{
mimeHash[mimetype].Add(thisFile);
}
}
}
// Prepare for the next page
page_and_group_number++;
}
else
{
// Page has no files, so it should be skipped when written
pageNode.ID = "SKIP";
}
}
else
{
// This node is a DIVISION (non-page)
thisNode.ID = "PDIV" + division_number;
division_number++;
}
}
// Clear any existing ID's here since the ID's are only used for writing METS files
foreach (abstract_TreeNode thisNode in downloadDivisions)
{
thisNode.ID = String.Empty;
thisNode.DMDID = String.Empty;
thisNode.ADMID = String.Empty;
}
// Now, do the same thing for the download/other files division tree
// Group numbers in the METS file correspond to files on the same page (physical or other)
// At the same time, we will build the list of all files and files by mime type
page_and_group_number = 1;
division_number = 1;
foreach (abstract_TreeNode thisNode in downloadDivisions)
{
// If this node was already hit (perhaps if a div has two parent divs),
// then skip it
if (thisNode.ID.Length > 0)
continue;
// Add this to the list of all nodes
allDivisions.Add(thisNode);
// Was this a PAGE or a DIVISION node?
if (thisNode.Page)
{
// Get this page node
Page_TreeNode pageNode = (Page_TreeNode) thisNode;
// Only do anything if there are actually any files here
if ((pageNode.Files != null) && (pageNode.Files.Count > 0))
{
// Set the page ID here
pageNode.ID = "FILES" + page_and_group_number;
// Step through any files under this page
foreach (SobekCM_File_Info thisFile in pageNode.Files)
{
// Set the ADMID and DMDID to empty in preparation for METS writing
thisFile.ADMID = String.Empty;
thisFile.DMDID = String.Empty;
// If no file name, skip this
if (String.IsNullOrEmpty(thisFile.System_Name))
continue;
// Get this file extension and MIME type
string fileExtension = thisFile.File_Extension;
string mimetype = thisFile.MIME_Type(thisFile.File_Extension);
// If this is going to be excluded from appearing in the METS file, just skip
// it here as well.
if (!mimes_to_exclude.Contains(mimetype))
{
// Set the group number on this file
thisFile.Group_Number = "G" + page_and_group_number;
// Set the ID for this file as well
switch (mimetype)
{
case "image/tiff":
thisFile.ID = "TIF" + page_and_group_number;
break;
case "text/plain":
thisFile.ID = "TXT" + page_and_group_number;
break;
case "image/jpeg":
if (thisFile.System_Name.ToLower().IndexOf("thm.jp") > 0)
{
thisFile.ID = "THUMB" + page_and_group_number;
mimetype = mimetype + "-thumbnails";
}
else
thisFile.ID = "JPEG" + page_and_group_number;
break;
case "image/gif":
if (thisFile.System_Name.ToLower().IndexOf("thm.gif") > 0)
{
thisFile.ID = "THUMB" + page_and_group_number;
mimetype = mimetype + "-thumbnails";
}
else
thisFile.ID = "GIF" + page_and_group_number;
break;
case "image/jp2":
thisFile.ID = "JP2" + page_and_group_number;
break;
default:
if (fileExtension.Length > 0)
{
thisFile.ID = fileExtension + page_and_group_number;
}
else
{
thisFile.ID = "NOEXT" + page_and_group_number;
}
break;
}
// Ensure this fileid is really unique. It may not be if there are multiple
// files of the same mime-type in the same page. (such as 0001.jpg and 0001.qc.jpg)
if (fileids_used.Contains(thisFile.ID))
{
int count = 2;
while (fileids_used.Contains(thisFile.ID + "." + count))
count++;
thisFile.ID = thisFile.ID + "." + count;
}
// Save this file id
fileids_used.Add(thisFile.ID);
// Also add to the list of files
allFiles.Add(thisFile);
// Also ensure we know there are page image files
hasDownloadFiles = true;
// If this is a new MIME type, add it, else just save this file in the MIME hash
if (!mimeHash.ContainsKey(mimetype))
{
List<SobekCM_File_Info> newList = new List<SobekCM_File_Info> {thisFile};
mimeHash[mimetype] = newList;
}
else
{
mimeHash[mimetype].Add(thisFile);
}
}
}
// Prepare for the next page
page_and_group_number++;
}
else
{
// Page has no files, so it should be skipped when written
pageNode.ID = "SKIP";
}
}
else
{
// This node is a DIVISION (non-page)
thisNode.ID = "ODIV" + division_number;
division_number++;
}
}
#endregion
// Add the XML declaration
Output_Stream.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>");
#region Write some processing instructions requested by Florida SUS's / FLVC (hope to deprecate)
// Some special code for setting processing parameters
bool daitssWriterIncluded = false;
foreach (METS_Section_ReaderWriter_Config thisConfig in profile.Package_Level_AmdSec_Writer_Configs)
{
if (thisConfig.Code_Class == "DAITSS_METS_amdSec_ReaderWriter")
{
daitssWriterIncluded = true;
break;
}
}
if (daitssWriterIncluded)
{
DAITSS_Info daitssInfo = Item_To_Save.Get_Metadata_Module(GlobalVar.DAITSS_METADATA_MODULE_KEY) as DAITSS_Info;
if (daitssInfo != null)
{
if ((daitssInfo.hasData) && (daitssInfo.toArchive))
{
Output_Stream.WriteLine("<?fcla fda=\"yes\"?>");
}
else
{
Output_Stream.WriteLine("<?fcla fda=\"no\"?>");
}
}
}
#endregion
// Add a remark here with the title and type
Output_Stream.WriteLine("<!-- " + Item_To_Save.Bib_Info.Main_Title.Title_XML.Replace("-", " ") + " ( " + Item_To_Save.Bib_Info.SobekCM_Type_String + " ) -->");
// Add the METS declaration information
Output_Stream.WriteLine("<METS:mets OBJID=\"" + Item_To_Save.METS_Header.ObjectID + "\"");
Output_Stream.WriteLine(" xmlns:METS=\"http://www.loc.gov/METS/\"");
Output_Stream.WriteLine(" xmlns:xlink=\"http://www.w3.org/1999/xlink\"");
Output_Stream.WriteLine(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
#region Add XMLNS and schema locations for active METS section reader/writers with data to write
// Collect all the xmlns and schema locations
List<string> xmlnsList = new List<string>();
List<string> schemaLocList = new List<string>();
foreach (METS_Section_ReaderWriter_Config thisRWconfig in profile.Package_Level_AmdSec_Writer_Configs)
{
iPackage_amdSec_ReaderWriter thisRw = (iPackage_amdSec_ReaderWriter) thisRWconfig.ReaderWriterObject;
if (thisRw.Schema_Reference_Required_Package(Item_To_Save))
{
string[] rwXmlns = thisRw.Schema_Namespace(Item_To_Save);
string[] rwSchema = thisRw.Schema_Location(Item_To_Save);
foreach (string thisValue in rwXmlns)
{
if (!xmlnsList.Contains(thisValue))
xmlnsList.Add(thisValue);
}
foreach (string thisValue in rwSchema)
{
if (!schemaLocList.Contains(thisValue))
schemaLocList.Add(thisValue);
}
}
}
foreach (METS_Section_ReaderWriter_Config thisRWconfig in profile.Package_Level_DmdSec_Writer_Configs)
{
iPackage_dmdSec_ReaderWriter thisRw = (iPackage_dmdSec_ReaderWriter) thisRWconfig.ReaderWriterObject;
if (thisRw.Schema_Reference_Required_Package(Item_To_Save))
{
string[] rwXmlns = thisRw.Schema_Namespace(Item_To_Save);
string[] rwSchema = thisRw.Schema_Location(Item_To_Save);
foreach (string thisValue in rwXmlns)
{
if (!xmlnsList.Contains(thisValue))
xmlnsList.Add(thisValue);
}
foreach (string thisValue in rwSchema)
{
if (!schemaLocList.Contains(thisValue))
schemaLocList.Add(thisValue);
}
}
}
foreach (METS_Section_ReaderWriter_Config thisRWconfig in profile.Division_Level_AmdSec_Writer_Configs)
{
iDivision_amdSec_ReaderWriter thisRw = (iDivision_amdSec_ReaderWriter)thisRWconfig.ReaderWriterObject;
foreach (abstract_TreeNode thisNode in physicalDivisions)
{
if (thisRw.Schema_Reference_Required_Division(thisNode))
{
string[] rwXmlns = thisRw.Schema_Namespace(Item_To_Save);
string[] rwSchema = thisRw.Schema_Location(Item_To_Save);
foreach (string thisValue in rwXmlns)
{
if (!xmlnsList.Contains(thisValue))
xmlnsList.Add(thisValue);
}
foreach (string thisValue in rwSchema)
{
if (!schemaLocList.Contains(thisValue))
schemaLocList.Add(thisValue);
}
break;
}
}
}
foreach (METS_Section_ReaderWriter_Config thisRWconfig in profile.Division_Level_DmdSec_Writer_Configs)
{
iDivision_dmdSec_ReaderWriter thisRw = (iDivision_dmdSec_ReaderWriter)thisRWconfig.ReaderWriterObject;
foreach (abstract_TreeNode thisNode in physicalDivisions)
{
if (thisRw.Schema_Reference_Required_Division(thisNode))
{
string[] rwXmlns = thisRw.Schema_Namespace(Item_To_Save);
string[] rwSchema = thisRw.Schema_Location(Item_To_Save);
foreach (string thisValue in rwXmlns)
{
if (!xmlnsList.Contains(thisValue))
xmlnsList.Add(thisValue);
}
foreach (string thisValue in rwSchema)
{
if (!schemaLocList.Contains(thisValue))
schemaLocList.Add(thisValue);
}
break;
}
}
}
// Add the namespaces
foreach (string namespaces in xmlnsList)
{
Output_Stream.WriteLine(" xmlns:" + namespaces);
}
Output_Stream.WriteLine(" xsi:schemaLocation=\"http://www.loc.gov/METS/");
Output_Stream.Write(" http://www.loc.gov/standards/mets/mets.xsd");
// Add the schema locations
foreach (string location in schemaLocList)
{
Output_Stream.WriteLine();
Output_Stream.Write(location);
}
#endregion
Output_Stream.WriteLine("\">");
// Add the METS declaration information and METS header
Item_To_Save.METS_Header.Add_METS(Item_To_Save, Output_Stream);
// Create the options dictionary for these writers
Dictionary<string, object> options = new Dictionary<string, object>();
options["SobekCM_FileInfo_METS_amdSec_ReaderWriter:All_Files"] = allFiles;
// Counters to keep track of the number of each section added
int dmdSec_counter = 1;
int digiProvMd = 1;
int rightsMd = 1;
int sourceMd = 1;
int techMd = 1;
#region Add all the package-level DMDSECs
// Prepare to add all the bibliographic section
StringBuilder dmd_secid_builder = new StringBuilder();
// Step through all the package level dmdSecs to be added
foreach (METS_Section_ReaderWriter_Config thisConfig in profile.Package_Level_DmdSec_Writer_Configs)
{
iPackage_dmdSec_ReaderWriter thisWriter = (iPackage_dmdSec_ReaderWriter) thisConfig.ReaderWriterObject;
if (thisWriter.Include_dmdSec(Item_To_Save, options))
{
// Save this DMD ID
dmd_secid_builder.Append("DMD" + dmdSec_counter + " ");
// Start this METS section
Output_Stream.WriteLine("<METS:dmdSec ID=\"DMD" + dmdSec_counter + "\">");
if (thisConfig.Default_Mapping.Other_MD_Type.Length > 0)
Output_Stream.WriteLine("<METS:mdWrap MDTYPE=\"" + thisConfig.Default_Mapping.MD_Type + "\" OTHERMDTYPE=\"" + thisConfig.Default_Mapping.Other_MD_Type + "\" MIMETYPE=\"text/xml\" LABEL=\"" + thisConfig.Default_Mapping.Label + "\">");
else
Output_Stream.WriteLine("<METS:mdWrap MDTYPE=\"" + thisConfig.Default_Mapping.MD_Type + "\" MIMETYPE=\"text/xml\" LABEL=\"" + thisConfig.Default_Mapping.Label + "\">");
Output_Stream.WriteLine("<METS:xmlData>");
// Add the amd section to the stream
thisWriter.Write_dmdSec(Output_Stream, Item_To_Save, options);
// Close this METS section
Output_Stream.WriteLine("</METS:xmlData>");
Output_Stream.WriteLine("</METS:mdWrap>");
Output_Stream.WriteLine("</METS:dmdSec>");
dmdSec_counter++;
}
}
// Now, add any unanalyzed DMD sections
if (( Item_To_Save.Unanalyzed_DMDSECs != null ) && ( Item_To_Save.Unanalyzed_DMDSECs.Count > 0 ))
{
foreach (Unanalyzed_METS_Section thisSection in Item_To_Save.Unanalyzed_DMDSECs)
{
// This wll be linked to the top-level
dmd_secid_builder.Append("DMD" + dmdSec_counter + " ");
// Add this to the output stream
Output_Stream.Write("<METS:dmdSec ID=\"DMD" + dmdSec_counter + "\"");
foreach (KeyValuePair<string, string> attribute in thisSection.Section_Attributes)
{
if (attribute.Key != "ID")
Output_Stream.Write(" " + attribute.Key + "=\"" + attribute.Value + "\"");
}
Output_Stream.WriteLine(">");
Output_Stream.WriteLine(thisSection.Inner_XML);
Output_Stream.WriteLine("</METS:dmdSec>");
dmdSec_counter++;
}
}
#endregion
#region Add all the division-level DMDSECs
// Should we add DMDSEC metadata sections for the divisions?
// Step through all the possible division level dmdSecs to be added
foreach (METS_Section_ReaderWriter_Config thisConfig in profile.Division_Level_DmdSec_Writer_Configs)
{
// Step through each division
foreach (abstract_TreeNode thisDivision in allDivisions)
{
iDivision_dmdSec_ReaderWriter thisWriter = (iDivision_dmdSec_ReaderWriter) thisConfig.ReaderWriterObject;
// Include the DMD Sec for this division?
if (thisWriter.Include_dmdSec(thisDivision, options))
{
// Save this DMD ID
thisDivision.DMDID = thisDivision.DMDID + "DMD" + dmdSec_counter + " ";
// Start this METS section
Output_Stream.WriteLine("<METS:dmdSec ID=\"DMD" + dmdSec_counter + "\">");
if (thisConfig.Default_Mapping.Other_MD_Type.Length > 0)
Output_Stream.WriteLine("<METS:mdWrap MDTYPE=\"" + thisConfig.Default_Mapping.MD_Type + "\" OTHERMDTYPE=\"" + thisConfig.Default_Mapping.Other_MD_Type + "\" MIMETYPE=\"text/xml\" LABEL=\"" + thisConfig.Default_Mapping.Label + "\">");
else
Output_Stream.WriteLine("<METS:mdWrap MDTYPE=\"" + thisConfig.Default_Mapping.MD_Type + "\" MIMETYPE=\"text/xml\" LABEL=\"" + thisConfig.Default_Mapping.Label + "\">");
Output_Stream.WriteLine("<METS:xmlData>");
// Add the amd section to the stream
thisWriter.Write_dmdSec(Output_Stream, thisDivision, options);
// Close this METS section
Output_Stream.WriteLine("</METS:xmlData>");
Output_Stream.WriteLine("</METS:mdWrap>");
Output_Stream.WriteLine("</METS:dmdSec>");
dmdSec_counter++;
}
}
}
#endregion
#region Add all the file-level DMDSECs
// Should we add DMDSEC metadata sections for the files?
// Step through all the possible file level dmdSecs to be added
foreach (METS_Section_ReaderWriter_Config thisConfig in profile.File_Level_DmdSec_Writer_Configs)
{
// Step through each file
foreach (SobekCM_File_Info thisFile in allFiles)
{
iFile_dmdSec_ReaderWriter thisWriter = (iFile_dmdSec_ReaderWriter)thisConfig.ReaderWriterObject;
// Include the DMD Sec for this file?
if (thisWriter.Include_dmdSec(thisFile, options))
{
// Save this DMD ID
thisFile.DMDID = thisFile.DMDID + "DMD" + dmdSec_counter + " ";
// Start this METS section
Output_Stream.WriteLine("<METS:dmdSec ID=\"DMD" + dmdSec_counter + "\">");
if (thisConfig.Default_Mapping.Other_MD_Type.Length > 0)
Output_Stream.WriteLine("<METS:mdWrap MDTYPE=\"" + thisConfig.Default_Mapping.MD_Type + "\" OTHERMDTYPE=\"" + thisConfig.Default_Mapping.Other_MD_Type + "\" MIMETYPE=\"text/xml\" LABEL=\"" + thisConfig.Default_Mapping.Label + "\">");
else
Output_Stream.WriteLine("<METS:mdWrap MDTYPE=\"" + thisConfig.Default_Mapping.MD_Type + "\" MIMETYPE=\"text/xml\" LABEL=\"" + thisConfig.Default_Mapping.Label + "\">");
Output_Stream.WriteLine("<METS:xmlData>");
// Add the amd section to the stream
thisWriter.Write_dmdSec(Output_Stream, thisFile, options);
// Close this METS section
Output_Stream.WriteLine("</METS:xmlData>");
Output_Stream.WriteLine("</METS:mdWrap>");
Output_Stream.WriteLine("</METS:dmdSec>");
dmdSec_counter++;
}
}
}
#endregion
#region Add all the package-level AMDSECs
// Prepare to add all the bibliographic section
StringBuilder amd_secid_builder = new StringBuilder();
// Step through all the package level amdSecs to be added
foreach (METS_Section_ReaderWriter_Config thisConfig in profile.Package_Level_AmdSec_Writer_Configs)
{
iPackage_amdSec_ReaderWriter thisWriter = (iPackage_amdSec_ReaderWriter)thisConfig.ReaderWriterObject;
if (thisWriter.Include_amdSec(Item_To_Save, options))
{
// Start this METS section
Output_Stream.WriteLine("<METS:amdSec>");
switch (thisConfig.AmdSecType)
{
case METS_amdSec_Type_Enum.DigiProvMD:
Output_Stream.WriteLine("<METS:digiprovMD ID=\"DIGIPROV" + digiProvMd + "\">");
amd_secid_builder.Append("DIGIPROV" + digiProvMd + " ");
digiProvMd++;
break;
case METS_amdSec_Type_Enum.RightsMD:
Output_Stream.WriteLine("<METS:rightsMD ID=\"RIGHTS" + rightsMd + "\">");
amd_secid_builder.Append("RIGHTS" + rightsMd + " ");
rightsMd++;
break;
case METS_amdSec_Type_Enum.SourceMD:
Output_Stream.WriteLine("<METS:sourceMD ID=\"SOURCE" + sourceMd + "\">");
amd_secid_builder.Append("SOURCE" + sourceMd + " ");
sourceMd++;
break;
case METS_amdSec_Type_Enum.TechMD:
Output_Stream.WriteLine("<METS:techMD ID=\"TECH" + techMd + "\">");
amd_secid_builder.Append("TECH" + techMd + " ");
techMd++;
break;
default:
Output_Stream.WriteLine("<METS:rightsMD ID=\"RIGHTS" + rightsMd + "\">");
amd_secid_builder.Append("RIGHTS" + rightsMd + " ");
rightsMd++;
break;
}
if (thisConfig.Default_Mapping.Other_MD_Type.Length > 0)
Output_Stream.WriteLine("<METS:mdWrap MDTYPE=\"" + thisConfig.Default_Mapping.MD_Type + "\" OTHERMDTYPE=\"" + thisConfig.Default_Mapping.Other_MD_Type + "\" MIMETYPE=\"text/xml\" LABEL=\"" + thisConfig.Default_Mapping.Label + "\">");
else
Output_Stream.WriteLine("<METS:mdWrap MDTYPE=\"" + thisConfig.Default_Mapping.MD_Type + "\" MIMETYPE=\"text/xml\" LABEL=\"" + thisConfig.Default_Mapping.Label + "\">");
Output_Stream.WriteLine("<METS:xmlData>");
// Add the amd section to the stream
thisWriter.Write_amdSec(Output_Stream, Item_To_Save, options);
// Close this METS section
Output_Stream.WriteLine("</METS:xmlData>");
Output_Stream.WriteLine("</METS:mdWrap>");
switch (thisConfig.AmdSecType)
{
case METS_amdSec_Type_Enum.DigiProvMD:
Output_Stream.WriteLine("</METS:digiprovMD>");
break;
case METS_amdSec_Type_Enum.RightsMD:
Output_Stream.WriteLine("</METS:rightsMD>");
break;
case METS_amdSec_Type_Enum.SourceMD:
Output_Stream.WriteLine("</METS:sourceMD>");
break;
case METS_amdSec_Type_Enum.TechMD:
Output_Stream.WriteLine("</METS:techMD>");
break;
default:
Output_Stream.WriteLine("</METS:rightsMD>");
break;
}
Output_Stream.WriteLine("</METS:amdSec>");
}
}
// Now, add any unanalyzed AMD sections
if (Item_To_Save.Unanalyzed_AMDSECs != null)
{
foreach (Unanalyzed_METS_Section thisSection in Item_To_Save.Unanalyzed_AMDSECs)
{
// Add this to the output stream
Output_Stream.Write("<METS:amdSec");
foreach (KeyValuePair<string, string> attribute in thisSection.Section_Attributes)
{
if (attribute.Key != "ID")
Output_Stream.Write(" " + attribute.Key + "=\"" + attribute.Value + "\"");
}
Output_Stream.WriteLine(">");
Output_Stream.WriteLine(thisSection.Inner_XML);
Output_Stream.WriteLine("</METS:amdSec>");
}
}
#endregion
#region Add all the division-level AMDSECs
// Should we add AMDSEC metadata sections for the divisions?
// Step through all the possible division level amdSecs to be added
foreach (METS_Section_ReaderWriter_Config thisConfig in profile.Division_Level_AmdSec_Writer_Configs)
{
// Step through each division
foreach (abstract_TreeNode thisDivision in allDivisions)
{
iDivision_amdSec_ReaderWriter thisWriter = (iDivision_amdSec_ReaderWriter) thisConfig.ReaderWriterObject;
// Include the DMD Sec for this division?
if (thisWriter.Include_amdSec(thisDivision, options))
{
// Start this METS section
Output_Stream.WriteLine("<METS:amdSec>");
switch (thisConfig.AmdSecType)
{
case METS_amdSec_Type_Enum.DigiProvMD:
Output_Stream.WriteLine("<METS:digiProvMD ID=\"DIGIPROV" + digiProvMd + "\">");
thisDivision.ADMID = thisDivision.ADMID + "DIGIPROV" + digiProvMd + " ";
digiProvMd++;
break;
case METS_amdSec_Type_Enum.RightsMD:
Output_Stream.WriteLine("<METS:rightsMD ID=\"RIGHTS" + rightsMd + "\">");
thisDivision.ADMID = thisDivision.ADMID + "RIGHTS" + rightsMd + " ";
rightsMd++;
break;
case METS_amdSec_Type_Enum.SourceMD:
Output_Stream.WriteLine("<METS:sourceMD ID=\"SOURCE" + sourceMd + "\">");
thisDivision.ADMID = thisDivision.ADMID + "SOURCE" + sourceMd + " ";
sourceMd++;
break;
case METS_amdSec_Type_Enum.TechMD:
Output_Stream.WriteLine("<METS:techMD ID=\"TECH" + techMd + "\">");
thisDivision.ADMID = thisDivision.ADMID + "TECH" + techMd + " ";
techMd++;
break;
default:
Output_Stream.WriteLine("<METS:rightsMD ID=\"RIGHTS" + rightsMd + "\">");
thisDivision.ADMID = thisDivision.ADMID + "RIGHTS" + rightsMd + " ";
rightsMd++;
break;
}
if (thisConfig.Default_Mapping.Other_MD_Type.Length > 0)
Output_Stream.WriteLine("<METS:mdWrap MDTYPE=\"" + thisConfig.Default_Mapping.MD_Type + "\" OTHERMDTYPE=\"" + thisConfig.Default_Mapping.Other_MD_Type + "\" MIMETYPE=\"text/xml\" LABEL=\"" + thisConfig.Default_Mapping.Label + "\">");
else
Output_Stream.WriteLine("<METS:mdWrap MDTYPE=\"" + thisConfig.Default_Mapping.MD_Type + "\" MIMETYPE=\"text/xml\" LABEL=\"" + thisConfig.Default_Mapping.Label + "\">");
Output_Stream.WriteLine("<METS:xmlData>");
Output_Stream.WriteLine("<METS:xmlData>");
// Add the amd section to the stream
thisWriter.Write_amdSec(Output_Stream, thisDivision, options);
// Close this METS section
Output_Stream.WriteLine("</METS:xmlData>");
Output_Stream.WriteLine("</METS:mdWrap>");
switch (thisConfig.AmdSecType)
{
case METS_amdSec_Type_Enum.DigiProvMD:
Output_Stream.WriteLine("</METS:digiProvMD>");
break;
case METS_amdSec_Type_Enum.RightsMD:
Output_Stream.WriteLine("</METS:rightsMD>");
break;
case METS_amdSec_Type_Enum.SourceMD:
Output_Stream.WriteLine("</METS:sourceMD>");
break;
case METS_amdSec_Type_Enum.TechMD:
Output_Stream.WriteLine("</METS:techMD>");
break;
default:
Output_Stream.WriteLine("</METS:rightsMD>");
break;
}
Output_Stream.WriteLine("</METS:amdSec>");
dmdSec_counter++;
}
}
}
#endregion
#region Add all the file-level AMDSECs
// Should we add AMDSEC metadata sections for the files?
// Step through all the possible file level amdSecs to be added
foreach (METS_Section_ReaderWriter_Config thisConfig in profile.File_Level_AmdSec_Writer_Configs)
{
// Step through each file
foreach (SobekCM_File_Info thisFile in allFiles)
{
iFile_amdSec_ReaderWriter thisWriter = (iFile_amdSec_ReaderWriter)thisConfig.ReaderWriterObject;
// Include the DMD Sec for this file?
if (thisWriter.Include_amdSec(thisFile, options))
{
// Start this METS section
Output_Stream.WriteLine("<METS:amdSec>");
switch (thisConfig.AmdSecType)
{
case METS_amdSec_Type_Enum.DigiProvMD:
Output_Stream.WriteLine("<METS:digiProvMD ID=\"DIGIPROV" + digiProvMd + "\">");
thisFile.ADMID = thisFile.ADMID + "DIGIPROV" + digiProvMd + " ";
digiProvMd++;
break;
case METS_amdSec_Type_Enum.RightsMD:
Output_Stream.WriteLine("<METS:rightsMD ID=\"RIGHTS" + rightsMd + "\">");
thisFile.ADMID = thisFile.ADMID + "RIGHTS" + rightsMd + " ";
rightsMd++;
break;
case METS_amdSec_Type_Enum.SourceMD:
Output_Stream.WriteLine("<METS:sourceMD ID=\"SOURCE" + sourceMd + "\">");
thisFile.ADMID = thisFile.ADMID + "SOURCE" + sourceMd + " ";
sourceMd++;
break;
case METS_amdSec_Type_Enum.TechMD:
Output_Stream.WriteLine("<METS:techMD ID=\"TECH" + techMd + "\">");
thisFile.ADMID = thisFile.ADMID + "TECH" + techMd + " ";
techMd++;
break;
default:
Output_Stream.WriteLine("<METS:rightsMD ID=\"RIGHTS" + rightsMd + "\">");
thisFile.ADMID = thisFile.ADMID + "RIGHTS" + rightsMd + " ";
rightsMd++;
break;
}
if (thisConfig.Default_Mapping.Other_MD_Type.Length > 0)
Output_Stream.WriteLine("<METS:mdWrap MDTYPE=\"" + thisConfig.Default_Mapping.MD_Type + "\" OTHERMDTYPE=\"" + thisConfig.Default_Mapping.Other_MD_Type + "\" MIMETYPE=\"text/xml\" LABEL=\"" + thisConfig.Default_Mapping.Label + "\">");
else
Output_Stream.WriteLine("<METS:mdWrap MDTYPE=\"" + thisConfig.Default_Mapping.MD_Type + "\" MIMETYPE=\"text/xml\" LABEL=\"" + thisConfig.Default_Mapping.Label + "\">");
Output_Stream.WriteLine("<METS:xmlData>");
Output_Stream.WriteLine("<METS:xmlData>");
// Add the amd section to the stream
thisWriter.Write_amdSec(Output_Stream, thisFile, options);
// Close this METS section
Output_Stream.WriteLine("</METS:xmlData>");
Output_Stream.WriteLine("</METS:mdWrap>");
switch (thisConfig.AmdSecType)
{
case METS_amdSec_Type_Enum.DigiProvMD:
Output_Stream.WriteLine("</METS:digiProvMD>");
break;
case METS_amdSec_Type_Enum.RightsMD:
Output_Stream.WriteLine("</METS:rightsMD>");
break;
case METS_amdSec_Type_Enum.SourceMD:
Output_Stream.WriteLine("</METS:sourceMD>");
break;
case METS_amdSec_Type_Enum.TechMD:
Output_Stream.WriteLine("</METS:techMD>");
break;
default:
Output_Stream.WriteLine("</METS:rightsMD>");
break;
}
Output_Stream.WriteLine("</METS:amdSec>");
dmdSec_counter++;
}
}
}
#endregion
// Add file and structure map sections
if ( allFiles.Count > 0 )
{
#region Add the files section
// Start the files section
Output_Stream.WriteLine("<METS:fileSec>");
// Step through each mime type
foreach (string thisMimeType in mimeHash.Keys)
{
List<SobekCM_File_Info> mimeCollection = mimeHash[thisMimeType];
// Start this file group section
Output_Stream.WriteLine(thisMimeType == "image/tiff" ? "<METS:fileGrp USE=\"archive\" >" : "<METS:fileGrp USE=\"reference\" >");
// Step through each file of this mime type and append the METS
foreach (SobekCM_File_Info thisFile in mimeCollection)
{
// Is the size and checksum information here?
if ((!MINIMIZE_FILE_SIZE) && ( !Item_To_Save.Divisions.Suppress_Checksum ))
{
if ((((String.IsNullOrEmpty(thisFile.Checksum)) || (thisFile.Size <= 0))) && (thisFile.METS_LocType == SobekCM_File_Info_Type_Enum.SYSTEM))
{
// Perform in a try catch
try
{
if (File.Exists(Item_To_Save.Source_Directory + "/" + thisFile.System_Name))
{
// Get the size first
if ( thisFile.Size < 0 )
{
FileInfo thisFileInfo = new FileInfo(Item_To_Save.Source_Directory + "/" + thisFile.System_Name);
thisFile.Size = thisFileInfo.Length;
}
// Get the checksum, if it doesn't exist
if (String.IsNullOrEmpty(thisFile.Checksum))
{
FileMD5 checksummer = new FileMD5(Item_To_Save.Source_Directory + "/" + thisFile.System_Name);
thisFile.Checksum = checksummer.Checksum;
// Set the checksum type
thisFile.Checksum_Type = "MD5";
}
}
}
catch
{
}
}
}
// Start out this file
Output_Stream.Write("<METS:file GROUPID=\"" + thisFile.Group_Number + "\" ID=\"" + thisFile.ID + "\" MIMETYPE=\"" + thisMimeType + "\"");
// Add links to dmd secs and amd secs
if (!String.IsNullOrEmpty(thisFile.DMDID))
{
Output_Stream.Write(" DMDID=\"" + thisFile.DMDID + "\"");
}
if (!String.IsNullOrEmpty(thisFile.ADMID))
{
Output_Stream.Write(" ADMID=\"" + thisFile.ADMID + "\"");
}
// Add the checksum it it exists
if ((!MINIMIZE_FILE_SIZE) && (!String.IsNullOrEmpty(thisFile.Checksum)))
{
Output_Stream.Write(" CHECKSUM=\"" + thisFile.Checksum + "\"");
// Add the checksum type if there was one (SHOULD be one here)
if (!String.IsNullOrEmpty(thisFile.Checksum_Type))
{
Output_Stream.Write(" CHECKSUMTYPE=\"" + thisFile.Checksum_Type + "\"");
}
}
// Add the size of the file, if it exists
if ((!MINIMIZE_FILE_SIZE) && (thisFile.Size > 0))
{
Output_Stream.Write(" SIZE=\"" + thisFile.Size + "\"");
}
// Close out this beginning file tag
Output_Stream.WriteLine(">");
// Include the system name or URL
if (thisFile.METS_LocType == SobekCM_File_Info_Type_Enum.URL)
{
Output_Stream.WriteLine("<METS:FLocat LOCTYPE=\"URL\" xlink:href=\"" + thisFile.System_Name.Replace(" ", "%20").Replace("&", "&") + "\" />");
}
else
{
Output_Stream.WriteLine("<METS:FLocat LOCTYPE=\"OTHER\" OTHERLOCTYPE=\"SYSTEM\" xlink:href=\"" + thisFile.System_Name.Replace(" ", "%20").Replace("&", "&").Replace("\\", "/") + "\" />");
}
// Add the closing file tag
Output_Stream.WriteLine("</METS:file>");
}
// Close out this file group section
Output_Stream.WriteLine("</METS:fileGrp>");
}
// Finish out the file section
Output_Stream.WriteLine("</METS:fileSec>");
#endregion
#region Add the structure map section
Dictionary<abstract_TreeNode, int> pages_to_appearances = new Dictionary<abstract_TreeNode, int>();
// May or may not be AMDSecs and DMDsec
string dmdSecIdString = dmd_secid_builder.ToString().Trim();
string amdSecIdString = amd_secid_builder.ToString().Trim();
// Add the physical page structure map first
if (hasPageFiles)
{
Output_Stream.WriteLine("<METS:structMap ID=\"STRUCT1\" TYPE=\"physical\">");
// Add any outer divisions here
if ( Item_To_Save.Divisions.Outer_Division_Count > 0)
{
foreach (Outer_Division_Info outerDiv in Item_To_Save.Divisions.Outer_Divisions)
{
Output_Stream.Write("<METS:div");
if (dmdSecIdString.Length > 0)
{
Output_Stream.Write(" DMDID=\"" + dmdSecIdString + "\"");
}
if (outerDiv.Label.Length > 0)
{
Output_Stream.Write(" LABEL=\"" + Convert_String_To_XML_Safe_Static(outerDiv.Label) + "\"");
}
if (outerDiv.OrderLabel > 0)
{
Output_Stream.Write(" ORDERLABEL=\"" + outerDiv.OrderLabel + "\"");
}
if (outerDiv.Type.Length > 0)
{
Output_Stream.Write(" TYPE=\"" + Convert_String_To_XML_Safe_Static(outerDiv.Type) + "\"");
}
Output_Stream.WriteLine(">");
}
}
else
{
// Start the main division information
Output_Stream.Write("<METS:div");
if (dmdSecIdString.Length > 0)
{
Output_Stream.Write(" DMDID=\"" + dmdSecIdString + "\"");
}
if (amdSecIdString.Length > 0)
{
Output_Stream.Write(" ADMID=\"" + amdSecIdString + "\"");
}
// Add the title, if one was provided
string title = Item_To_Save.Bib_Info.Main_Title.ToString();
if (title.Length > 0)
{
Output_Stream.Write(" LABEL=\"" + title + "\"");
}
// Finish out this first, main division tag
Output_Stream.WriteLine(" ORDER=\"0\" TYPE=\"main\">");
}
// Add all the divisions recursively
int order = 1;
foreach (abstract_TreeNode thisRoot in Item_To_Save.Divisions.Physical_Tree.Roots)
{
recursively_add_div_info(thisRoot, Output_Stream, pages_to_appearances, order++);
}
// Close any outer divisions here
if (Item_To_Save.Divisions.Outer_Division_Count > 0)
{
for (int index = 0; index < Item_To_Save.Divisions.Outer_Division_Count; index++)
{
Output_Stream.WriteLine("</METS:div>");
}
}
else
{
// Close out the main division tag
Output_Stream.WriteLine("</METS:div>");
}
// Close out this structure map portion
Output_Stream.WriteLine("</METS:structMap>");
}
if (hasDownloadFiles)
{
Output_Stream.WriteLine("<METS:structMap ID=\"STRUCT2\" TYPE=\"other\">");
// Add any outer divisions here
if ( Item_To_Save.Divisions.Outer_Division_Count > 0)
{
foreach (Outer_Division_Info outerDiv in Item_To_Save.Divisions.Outer_Divisions)
{
Output_Stream.Write("<METS:div");
if (dmdSecIdString.Length > 0)
{
Output_Stream.Write(" DMDID=\"" + dmdSecIdString + "\"");
}
if (outerDiv.Label.Length > 0)
{
Output_Stream.Write(" LABEL=\"" + Convert_String_To_XML_Safe_Static(outerDiv.Label) + "\"");
}
if (outerDiv.OrderLabel > 0)
{
Output_Stream.Write(" ORDERLABEL=\"" + outerDiv.OrderLabel + "\"");
}
if (outerDiv.Type.Length > 0)
{
Output_Stream.Write(" TYPE=\"" + Convert_String_To_XML_Safe_Static(outerDiv.Type) + "\"");
}
Output_Stream.WriteLine(">");
}
}
else
{
// Start the main division information
Output_Stream.Write("<METS:div");
if (dmdSecIdString.Length > 0)
{
Output_Stream.Write(" DMDID=\"" + dmdSecIdString + "\"");
}
if (amdSecIdString.Length > 0)
{
Output_Stream.Write(" ADMID=\"" + amdSecIdString + "\"");
}
// Add the title, if one was provided
string title = Item_To_Save.Bib_Info.Main_Title.ToString();
if (title.Length > 0)
{
Output_Stream.Write(" LABEL=\"" + title + "\"");
}
// Finish out this first, main division tag
Output_Stream.WriteLine(" ORDER=\"0\" TYPE=\"main\">");
}
// Add all the divisions recursively
int order = 1;
foreach (abstract_TreeNode thisRoot in Item_To_Save.Divisions.Download_Tree.Roots)
{
recursively_add_div_info(thisRoot, Output_Stream, pages_to_appearances, order++);
}
// Close any outer divisions here
if (Item_To_Save.Divisions.Outer_Division_Count > 0)
{
for (int index = 0; index < Item_To_Save.Divisions.Outer_Division_Count; index++)
{
Output_Stream.WriteLine("</METS:div>");
}
}
else
{
// Close out the main division tag
Output_Stream.WriteLine("</METS:div>");
}
// Close out this structure map portion
Output_Stream.WriteLine("</METS:structMap>");
}
#endregion
}
else
{
// Structure map is a required element for METS
Output_Stream.Write("<METS:structMap ID=\"STRUCT1\" > <METS:div /> </METS:structMap>\r\n");
}
// Add the behavior section for SobekCM views and interfaces
//if (embedded_metadata_types.Contains(Metadata_Type_Enum.SobekCM_Behaviors))
//{
// Item_To_Save.Behaviors.Add_BehaviorSec_METS(Output, Item_To_Save.Divisions.Physical_Tree.Has_Files);
//}
// Close out the METS file
Output_Stream.Write("</METS:mets>\r\n");
return true;
}