/Source Code/SongDatabase/Export and Import/PrimaryFacility.vb
Visual Basic | 364 lines | 314 code | 26 blank | 24 comment | 0 complexity | 5a053277055b001bab7b2fbeca4d92ac MD5 | raw file
- Imports System.IO
- Imports System.IO.Path
- Imports System.IO.Compression
- Imports PluginSupport
- Imports PowerSong.SongDatabase.Items
-
- Namespace ExportImport
-
- ''' <summary>
- ''' Represents the import and export functionality of PowerSong databases.
- ''' </summary>
- Public Class PrimaryFacility
-
- Private FDatabase As Database = Nothing
-
- ''' <summary>
- ''' Initializes a new instance of the <see cref="PrimaryFacility" /> class.
- ''' </summary>
- ''' <param name="sourceDatabase">The source database to use for the export/import.</param>
- Public Sub New(ByVal sourceDatabase As Database)
- FDatabase = sourceDatabase
- End Sub
-
- ''' <summary>
- ''' Exports the entire song database into a single file.
- ''' </summary>
- ''' <param name="fileName">The name of the target archive file.</param>
- Public Sub Export(ByVal fileName As String, _
- ByVal exportSongs As Boolean, _
- ByVal includeCategories As Boolean, _
- ByVal exportPlugins As Boolean, _
- ByVal exportStyles As Boolean)
-
- Dim DS As Data = FDatabase.Data
-
- ' Create the output stream
- Dim File As New BinaryWriter(New MemoryStream)
-
- ' Write all files in this folder to the file
- If exportSongs Then
- For Each SourceFilename As String In Directory.GetFiles(FDatabase.Location, "*.song")
- WriteFileToStream(File, SourceFilename, GetFileName(SourceFilename))
- Next
- End If
-
- ' TODO: Allow importing of categories as well
- If includeCategories Then
-
- ' Create a dataset for exporting
- Dim CategoriesDataset As New Data
- For Each Row As Data.CategoriesRow In DS.Categories
- CategoriesDataset.Categories.ImportRow(Row)
- Next
- For Each Row As Data.SongsRow In DS.Songs
- CategoriesDataset.Songs.ImportRow(Row)
- Next
- For Each Row As Data.SongCategoriesRow In DS.SongCategories
- CategoriesDataset.SongCategories.ImportRow(Row)
- Next
-
- ' Write the dataset to the archive
- Dim Writer As New StringWriter
- CategoriesDataset.WriteXml(Writer)
- Writer.Close()
- WriteTextToStream(File, Writer.ToString, "SongCategories.xml")
-
- End If
-
- If exportPlugins Then
- For Each SourceFilename As String In Directory.GetFiles(FDatabase.Location, "*.plugin")
- WriteFileToStream(File, SourceFilename, GetFileName(SourceFilename))
- Next
- End If
-
- If exportStyles Then
-
- ' Create a dataset for exporting
- Dim StylesDataset As New Data
- For Each Row As Data.StylesRow In DS.Styles
- StylesDataset.Styles.ImportRow(Row)
- Next
- For Each Row As Data.ProjectletsRow In DS.Projectlets
- StylesDataset.Projectlets.ImportRow(Row)
- Next
-
- ' Write the dataset to the archive
- Dim Writer As New StringWriter
- StylesDataset.WriteXml(Writer)
- Writer.Close()
- WriteTextToStream(File, Writer.ToString, "Styles.xml")
-
- End If
-
- ' Get the data as a sequence of bytes
- Dim Data(File.BaseStream.Length) As Byte
- File.BaseStream.Position = 0
- File.BaseStream.Read(Data, 0, File.BaseStream.Length)
-
- ' Write to a compressed stream
- Dim CompressedFile As New GZipStream(New FileStream(fileName, FileMode.Create), CompressionMode.Compress)
- CompressedFile.Write(Data, 0, File.BaseStream.Length)
-
- File.Close()
- CompressedFile.Close()
-
- End Sub
-
- Public Function Import(ByVal fileStream As Stream, _
- ByVal pluginContext As PluginContextBase, _
- ByVal overwriteItems As Boolean) As List(Of String)
-
- ' Start a transaction
- Dim DS As Data = FDatabase.Data
- Dim Messages As New List(Of String)
- FDatabase.StartTransaction()
- Try
-
- ' Decompress all of the data in the given file into a memory stream
- Dim ZipStream As New GZipStream(fileStream, CompressionMode.Decompress)
- Dim MemoryStream As New MemoryStream
- Dim Buffer(4096) As Byte
- Dim BytesRead As Integer = ZipStream.Read(Buffer, 0, 4096)
- While BytesRead > 0
- MemoryStream.Write(Buffer, 0, BytesRead)
- BytesRead = ZipStream.Read(Buffer, 0, 4096)
- End While
-
- ' Start reading from the beginning of the memory stream
- MemoryStream.Position = 0
- Dim File As New BinaryReader(MemoryStream)
-
- ' Read each file in the stream
- While File.BaseStream.Position < File.BaseStream.Length
- Dim NextFileName As String = File.ReadString
- Dim FileLength As Integer = File.ReadInt32
- Dim FileContents As Byte() = File.ReadBytes(FileLength)
- Dim OutputFileName As String = FDatabase.Location + "\" + NextFileName
-
- ' Work with the current file in the stream
- Select Case GetExtension(NextFileName).ToUpper
-
- Case ".SONG"
- ImportSong(DS, FileContents, OutputFileName, overwriteItems, Messages)
-
- Case ".PLUGIN"
- ImportPlugin(FileContents, pluginContext, OutputFileName, overwriteItems, Messages)
-
- Case ".XML"
-
- Select Case NextFileName.ToUpper
-
- Case "STYLES.XML"
- ImportStyles(DS, FileContents, overwriteItems, Messages)
-
- Case "SONGCATEGORIES.XML"
- Messages.Add("Song Categories have been included in the selected file but they cannot yet be imported. It is likely that a future version of PowerSong will allow Song Categories to be imported from the specified file.")
-
- Case Else
- Messages.Add("Skipped over unknown file: '" + NextFileName + "'.")
-
- End Select
-
- Case Else
- Messages.Add("Skipped over unknown file: '" + NextFileName + "'.")
-
- End Select
-
- End While
-
- Finally
- FDatabase.EndTransaction()
- End Try
-
- ' Perform some checks on the database - this assigns a category to uncategorised songs
- FDatabase.Save()
- DatabaseUpgrade.CheckSongCategories(FDatabase)
-
- ' Recreate the index
- FDatabase.RecreateIndex()
-
- Return Messages
-
- End Function
-
- Private Sub ImportSong(ByVal dataSet As Data, _
- ByVal fileContents As Byte(), _
- ByVal destinationFileName As String, _
- ByVal overwriteExisting As Boolean, _
- ByVal messages As List(Of String))
-
- ' Determine whether or not to import/overwrite the song
- Dim ImportThisSong As Boolean = True
- If IO.File.Exists(destinationFileName) Then
- If Not overwriteExisting Then
- messages.Add("The song '" + destinationFileName + "' already exists.")
- ImportThisSong = False
- End If
- End If
-
- If ImportThisSong = True Then
-
- Try
-
- ' Write the song
- Dim OutputFile As New BinaryWriter(New FileStream(destinationFileName, FileMode.Create))
- OutputFile.Write(fileContents)
- OutputFile.Close()
-
- ' Add to the database if necessary
- Dim Song As SongItem = SongItem.Load(destinationFileName)
- Dim ExistingSong As Data.SongsRow = dataSet.Songs.FindBySongID(Song.SongID)
- If ExistingSong IsNot Nothing Then dataSet.Songs.RemoveSongsRow(ExistingSong)
- Dim SongRow As Data.SongsRow = dataSet.Songs.AddSongsRow(Song.SongID, Song.Title, Nothing)
-
- ' Display information about the song
- messages.Add("Added song: '" + Song.Title + "'.")
-
- Catch ex As Exception
- messages.Add("Song import error: " + ex.Message)
- End Try
-
- End If
-
- End Sub
-
- Private Sub ImportPlugin(ByVal fileContents As Byte(), _
- ByVal pluginContext As PluginSupport.PluginContextBase, _
- ByVal destinationFileName As String, _
- ByVal overwriteExisting As Boolean, _
- ByVal messages As List(Of String))
-
- If IO.File.Exists(destinationFileName) Then
- messages.Add("The plugin '" + destinationFileName + "' already exists.")
- Else
-
- Try
-
- ' Add the plugin to the active database, instantiate it and call its Install method
- AddExistingPlugin(destinationFileName, fileContents)
- messages.Add("Added plugin '" + GetFileName(destinationFileName) + "'.")
- Dim Plugin As IPlugin = FDatabase.InstantiatePlugin(GetFileName(destinationFileName), pluginContext)
- Plugin.Install()
- messages.Add("Installed plugin '" + GetFileName(destinationFileName) + "'.")
-
- Catch ex As Exception
- messages.Add("Plugin import error: " + ex.Message)
- End Try
-
- End If
-
- End Sub
-
- Private Sub ImportStyles(ByVal dataSet As Data, _
- ByVal fileContents As Byte(), _
- ByVal overwriteExisting As Boolean, _
- ByVal messages As List(Of String))
-
- Try
-
- ' Get the styles
- Dim StyleData As New Data
- Dim Reader As New Xml.XmlTextReader(New MemoryStream(fileContents))
- StyleData.ReadXml(Reader)
- Reader.Close()
-
- ' Import the styles
- For Each Row As Data.StylesRow In StyleData.Styles
-
- Try
- Dim ExistingStyle As Data.StylesRow() = dataSet.Styles.Select("Name='" + Row.Name + "'")
- If ExistingStyle.Length = 0 Then
-
- ' Add the new style
- dataSet.Styles.ImportRow(Row)
- For Each SubRow As Data.ProjectletsRow In StyleData.Projectlets.Select("StyleID='" + Row.StyleID.ToString + "'")
- dataSet.Projectlets.ImportRow(SubRow)
- Next
- messages.Add("Added style '" + Row.Name + "'.")
-
- ElseIf overwriteExisting Then
-
- ' Overwrite existing style
- dataSet.Styles.RemoveStylesRow(ExistingStyle(0))
- dataSet.Styles.ImportRow(Row)
- For Each SubRow As Data.ProjectletsRow In StyleData.Projectlets.Select("StyleID='" + Row.StyleID.ToString + "'")
- dataSet.Projectlets.ImportRow(SubRow)
- Next
- messages.Add("Replaced style '" + Row.Name + "'.")
-
- Else
- messages.Add("The style '" + Row.Name + "' already exists, therefore it has been ignored.")
- End If
-
- Catch ex As Exception
- messages.Add("Cannot import style '" + Row.Name + "' because it is incompatible.")
- End Try
-
- Next
-
- Catch ex As Exception
- messages.Add("The styles file contained in the archive is invalid.")
- End Try
-
- End Sub
-
- Public Function Import(ByVal fileName As String, _
- ByVal pluginContext As PluginContextBase, _
- ByVal overwriteExistingSongs As Boolean) As List(Of String)
-
- Dim Stream As New FileStream(fileName, FileMode.Open)
- Dim Result As New List(Of String)
-
- Try
- Result = Import(Stream, pluginContext, overwriteExistingSongs)
- Finally
- Stream.Close()
- End Try
-
- Return Result
-
- End Function
-
- Private Sub WriteFileToStream(ByVal stream As BinaryWriter, _
- ByVal fileName As String, _
- ByVal fileNameInStream As String)
-
- stream.Write(fileNameInStream)
- Dim File As New BinaryReader(New FileStream(fileName, FileMode.Open))
- Dim FileLength As Integer = File.BaseStream.Length
- Dim Contents As Byte() = File.ReadBytes(FileLength)
- stream.Write(FileLength)
- stream.Write(Contents)
- File.Close()
-
- End Sub
-
- Private Sub WriteTextToStream(ByVal stream As BinaryWriter, _
- ByVal text As String, _
- ByVal fileNameInStream As String)
-
- stream.Write(fileNameInStream)
- Dim Data As Byte() = System.Text.Encoding.Default.GetBytes(text)
- stream.Write(Data.Length)
- stream.Write(Data)
-
- End Sub
-
- Private Sub AddExistingPlugin(ByVal destinationFileName As String, ByVal fileContents As Byte())
-
- ' Check extension
- If GetExtension(destinationFileName).ToUpper <> ".PLUGIN" Then
- Throw New ApplicationException("Plugin must have an extension of .PLUGIN.")
- End If
-
- If File.Exists(destinationFileName) Then Throw New ApplicationException("A plugin with the same file name already exists in the database.")
- Dim OutputFile As New FileStream(destinationFileName, FileMode.Create)
- OutputFile.Write(fileContents, 0, fileContents.Length)
- OutputFile.Close()
-
- End Sub
-
- End Class
-
- End Namespace