Merge pull request #1857 from slluis/fix-assembly-resolver
[mono.git] / mcs / class / System.IO.Compression / ZipArchive.cs
index c8f68f707f62b9835f5cc94916bf0d998ed95b0e..feb5e1b705dec780327d9b62586094ac7bfc1faa 100644 (file)
@@ -1,7 +1,8 @@
 //
-// ZipArchive.cs
+// ZipArchiveEntry.cs
 //
 // Author:
+//       Joao Matos <joao.matos@xamarin.com>
 //       Martin Baulig <martin.baulig@xamarin.com>
 //
 // Copyright (c) 2013 Xamarin Inc. (http://www.xamarin.com)
 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
-using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
+using System.Linq;
 using System.Text;
+using SharpCompress.Common;
 
 namespace System.IO.Compression
 {
-       [MonoTODO]
        public class ZipArchive : IDisposable
        {
+               internal Stream stream;
+               internal readonly bool leaveStreamOpen;
+               internal readonly ZipArchiveMode mode;
+               internal Encoding entryNameEncoding;
+               internal bool disposed;
+               internal Dictionary<string, ZipArchiveEntry> entries; 
+               internal SharpCompress.Archive.Zip.ZipArchive zipFile;
+
                public ZipArchive (Stream stream)
                {
-                       throw new NotImplementedException ();
+                       if (stream == null)
+                               throw new ArgumentNullException("stream");
+
+                       this.stream = stream;
+                       mode = ZipArchiveMode.Read;
+                       CreateZip(stream, mode);
                }
 
                public ZipArchive (Stream stream, ZipArchiveMode mode)
-                       : this (stream)
                {
+                       if (stream == null)
+                               throw new ArgumentNullException("stream");
+
+                       this.stream = stream;
+                       this.mode = mode;
+                       CreateZip(stream, mode);
                }
 
-               public ZipArchive (Stream stream, ZipArchiveMode mode,
-                                  bool leaveOpen)
-                       : this (stream, mode)
+               public ZipArchive (Stream stream, ZipArchiveMode mode, bool leaveOpen)
                {
+                       if (stream == null)
+                               throw new ArgumentNullException("stream");
+
+                       this.stream = stream;
+                       this.mode = mode;
+                       leaveStreamOpen = leaveOpen;
+                       CreateZip(stream, mode);
                }
 
-               public ZipArchive (Stream stream, ZipArchiveMode mode,
-                                  bool leaveOpen, Encoding entryNameEncoding)
-                       : this (stream, mode, leaveOpen)
+               public ZipArchive (Stream stream, ZipArchiveMode mode, bool leaveOpen, Encoding entryNameEncoding)
                {
+                       if (stream == null)
+                               throw new ArgumentNullException("stream");
+
+                       this.stream = stream;
+                       this.mode = mode;
+                       leaveStreamOpen = leaveOpen;
+                       this.entryNameEncoding = entryNameEncoding;
+                       CreateZip(stream, mode);
+               }
+
+               private void CreateZip(Stream stream, ZipArchiveMode mode)
+               {
+                       if (mode != ZipArchiveMode.Read && mode != ZipArchiveMode.Create && mode != ZipArchiveMode.Update)
+                               throw new ArgumentOutOfRangeException("mode");
+
+                       // If the mode parameter is set to Read, the stream must support reading.
+                       if (mode == ZipArchiveMode.Read && !stream.CanRead)
+                               throw new ArgumentException("Stream must support reading for Read archive mode");
+
+                       // If the mode parameter is set to Create, the stream must support writing.
+                       if (mode == ZipArchiveMode.Create && !stream.CanWrite)
+                               throw new ArgumentException("Stream must support writing for Create archive mode");
+
+                       // If the mode parameter is set to Update, the stream must support reading, writing, and seeking.
+                       if (mode == ZipArchiveMode.Update && (!stream.CanRead || !stream.CanWrite || !stream.CanSeek))
+                               throw new ArgumentException("Stream must support reading, writing and seeking for Update archive mode");
+
+                       try {
+                               zipFile = mode == ZipArchiveMode.Create ? 
+                                       SharpCompress.Archive.Zip.ZipArchive.Create() :
+                                       SharpCompress.Archive.Zip.ZipArchive.Open(stream);
+                       } catch (Exception e) {
+                               throw new InvalidDataException("The contents of the stream are not in the zip archive format.", e);
+                       }
+
+                       entries = new Dictionary<string, ZipArchiveEntry>();
+                       if (Mode != ZipArchiveMode.Create) {
+                               foreach (var entry in zipFile.Entries) {
+                                       var zipEntry = new ZipArchiveEntry(this, entry);
+                                       entries[entry.Key] = zipEntry;
+                               }
+                       }
                }
 
                public ReadOnlyCollection<ZipArchiveEntry> Entries {
                        get {
-                               throw new NotImplementedException ();
+                               if (disposed)
+                                       throw new ObjectDisposedException("The zip archive has been disposed.");
+
+                               if (Mode == ZipArchiveMode.Create)
+                                       throw new NotSupportedException("Cannot access entries in Create mode.");
+
+                               if (zipFile == null)
+                                       throw new InvalidDataException("The zip archive is corrupt, and its entries cannot be retrieved.");
+
+                               if (entries == null)
+                                       return new ReadOnlyCollection<ZipArchiveEntry>(new List<ZipArchiveEntry>());
+
+                               return new ReadOnlyCollection<ZipArchiveEntry>(entries.Values.ToList());
                        }
                }
 
                public ZipArchiveMode Mode {
                        get {
-                               throw new NotImplementedException ();
+                               if (disposed)
+                                       throw new ObjectDisposedException("The zip archive has been disposed.");
+
+                               return mode;
                        }
                }
 
                public ZipArchiveEntry CreateEntry (string entryName)
                {
-                       throw new NotImplementedException ();
+                       if (disposed)
+                               throw new ObjectDisposedException("The zip archive has been disposed.");
+
+                       return CreateEntry(entryName, CompressionLevel.Optimal);
                }
 
-               public ZipArchiveEntry CreateEntry (string entryName,
-                                                   CompressionLevel compressionLevel)
+               public ZipArchiveEntry CreateEntry (string entryName, CompressionLevel compressionLevel)
                {
-                       throw new NotImplementedException ();
+                       if (disposed)
+                               throw new ObjectDisposedException("The zip archive has been disposed.");
+
+                       if (entryName == string.Empty)
+                               throw new ArgumentException("Entry name cannot be empty.");
+
+                       if (entryName == null)
+                               throw new ArgumentNullException("entryName");
+
+                       if (mode != ZipArchiveMode.Create && mode != ZipArchiveMode.Update)
+                               throw new NotSupportedException("The zip archive does not support writing.");
+
+                       if (zipFile == null)
+                               throw new InvalidDataException("The zip archive is corrupt, and its entries cannot be retrieved.");
+
+                       var memoryStream = new MemoryStream();
+                       var entry = zipFile.AddEntry(entryName, memoryStream);
+                       var archiveEntry = new ZipArchiveEntry(this, entry);
+                       entries[entryName] = archiveEntry;
+
+                       return archiveEntry;
                }
 
                public ZipArchiveEntry GetEntry (string entryName)
                {
-                       throw new NotImplementedException ();
+                       if (disposed)
+                               throw new ObjectDisposedException("The zip archive has been disposed.");
+
+                       if (entryName == string.Empty)
+                               throw new ArgumentException("Entry name cannot be empty.");
+
+                       if (entryName == null)
+                               throw new ArgumentNullException("entryName");
+
+                       if (mode != ZipArchiveMode.Read && mode != ZipArchiveMode.Update)
+                               throw new NotSupportedException("The zip archive does not support reading.");
+
+                       if (zipFile == null)
+                               throw new InvalidDataException("The zip archive is corrupt, and its entries cannot be retrieved.");
+
+                       return entries.ContainsKey(entryName) ? entries[entryName] : null;
+               }
+
+               private void Save()
+               {
+                       using (var newZip = new MemoryStream()) {
+                               zipFile.SaveTo(newZip, CompressionType.Deflate);
+
+                               stream.SetLength(0);
+                               stream.Position = 0;
+                               newZip.Position = 0;
+                               newZip.CopyTo(stream);
+                       }
                }
 
                protected virtual void Dispose (bool disposing)
                {
-                       throw new NotImplementedException ();
+                       if (disposed)
+                               return;
+
+                       if (mode != ZipArchiveMode.Read)
+                               Save();
+
+                       disposed = true;
+
+                       if (leaveStreamOpen)
+                               return;
+
+                       if (stream != null) {
+                               stream.Dispose();
+                               stream = null;
+                       }
                }
 
                public void Dispose ()
                {
-                       throw new NotImplementedException ();
+                       Dispose(true);
+                       GC.SuppressFinalize(this);
                }
        }
 }