@Maciejasjmj said:
I don't know what kind of crack I was on when I wrote it
For interest sake, and because it does contain some LINQ, here is what code can look like when really written on drugs... (I was on meth when I wrote it.)
I tended to write horribly verbose code back then, and for some bizarre reason I thought it was hilarious to catch AggregateExceptions, always with a variable called ax, just so I could type: ax.Handle... (It seemed funnier when I was high.)
/// <summary>Populate the file and directory list from the zip file entries, keeping track of the current directory
/// in a class-level property. We always only display the files and directories contained by the current directory.
/// path == null indicates that the current directory is the root directory of the zip file.</summary>
private void Populate(string path = null)
{
CurrentZipDirectory = path;
// Use threadsafe collections to hold temporary lists of files and directories.
var directoryBag = new ConcurrentBag<ListViewItem>();
var fileBag = new ConcurrentBag<ListViewItem>();
// This list will be filtered for the current directory.
var zipFileEntries = Enumerable.Empty<ZipArchiveEntry>().ToList();
try
{
using (var zipArchive = ZipFile.Open(Filename, ZipArchiveMode.Read))
{
try
{
zipFileEntries = zipArchive.Entries.ToList();
}
catch (Exception ex)
{
ex.Log();
MessageBox.Show(Browser.Instance, string.Format("The zip file appears to be invalid and could not be read.\n{0}", ex.Message), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
// Discard everything that is not nested in/below the current directory
if (path != null)
zipFileEntries.RemoveAll(entry => !(entry.FullName.StartsWith(path + Romy.Core.Constants.DelimiterChars[0]) || entry.FullName.StartsWith(path + Romy.Core.Constants.DelimiterChars[1])));
/* Get the directories to display. Zip files sometimes contain entries for directories, but often
* do not, and when we parse the entries paths, there will be many recurrences of each directory.
* Thus use a dictionary to keep track of the directories we have already found. */
var directories = new ConcurrentDictionary<string, string>();
var directoryEntries = zipFileEntries.Where(e =>
{
// Filter for subdirectories of the current directory.
var filenameInZip = e.FullName;
/* Remove current directory from string, so that the same
* string-manipulation logic applies to all directries. */
if (path != null)
filenameInZip = filenameInZip.Substring(path.Length + 1);
/* With the current directory removed, any string that contains one slash (forward or
* backslash; whatever this zip file uses) is a sub-directory of the current directory. */
if (filenameInZip.Count(c => c == '\\' || c == '/') > 0)
{
var directory = filenameInZip.Substring(0, filenameInZip.IndexOfAny(Romy.Core.Constants.DelimiterChars));
if (!directories.Values.Contains(directory))
{
directories[e.FullName] = directory;
return true;
}
}
return false;
});
try
{
directoryEntries.AsParallel().ForAll(entry =>
{
var index = path == null ? 0 : path.Length + 1;
var relativePath = entry.FullName.Substring(0, entry.FullName.IndexOfAny(Romy.Core.Constants.DelimiterChars, index));
/* Use my SimpleFileInfo helper class to get the file or directory description and image
* index from the SystemImageList. (The SystemImagelist is used by the listview.) */
var simpleFileInfo = new SimpleFileInfo
{
Name = directories[entry.FullName],
FileSystemType = Romy.Core.FileSystemType.Directory,
//DateModified = entry.ModifyTime, // This date in this entry refers to a file, not the directory. As we built the
Size = 0 // dictionary of directories, we kind of hijacked a file entry to use for the directory.
};
directoryBag.Add(ShellListView.CreateListViewItem(new ZipEntryInfo
{
SimpleFileInfo = simpleFileInfo,
DisplayName = relativePath,
CompressedLength = entry.CompressedLength,
FullName = entry.FullName,
LastWriteTime = entry.LastWriteTime,
Length = entry.Length,
Name = entry.Name
}, simpleFileInfo));
});
}
catch (AggregateException ax)
{
var handled = false;
ax.Handle(ex => handled = ex is ArgumentOutOfRangeException);
if (!handled)
throw;
}
/* Get the files. The logic is almost identical to the directories. */
var fileEntries = zipFileEntries.Where(e =>
{
return path != null ? e.FullName.IndexOfAny(Romy.Core.Constants.DelimiterChars, path.Length + 1) == -1 : e.FullName.IndexOfAny(Romy.Core.Constants.DelimiterChars) == -1;
});
try
{
fileEntries.AsParallel().ForAll(entry =>
{
var relativePath = entry.FullName;
if (path != null)
relativePath = relativePath.IndexOf(Path.DirectorySeparatorChar) != -1 ? relativePath.Replace(path + Path.AltDirectorySeparatorChar, string.Empty) : relativePath.Replace(path + '/', string.Empty);
if (!string.IsNullOrEmpty(relativePath) && !(relativePath.EndsWith(Path.DirectorySeparatorChar.ToString()) && !(relativePath.EndsWith(Path.AltDirectorySeparatorChar.ToString()) && entry.Length == 0)))
{
var simpleFileInfo = new SimpleFileInfo
{
Name = relativePath,
FileSystemType = FileSystemType.File,
DateModified = entry.LastWriteTime.LocalDateTime,
Size = entry.Length
};
fileBag.Add(ShellListView.CreateListViewItem(new ZipEntryInfo
{
SimpleFileInfo = simpleFileInfo,
DisplayName = relativePath,
CompressedLength = entry.CompressedLength,
FullName = entry.FullName,
LastWriteTime = entry.LastWriteTime,
Length = entry.Length,
Name = entry.Name
}, simpleFileInfo));
}
});
}
catch (AggregateException ax)
{
var handled = false;
ax.Handle(ex => handled = ex is ArgumentOutOfRangeException);
if (!handled)
throw;
}
// Copy our ConcurrentBag lists to arrays, and sort them in parallel.
var directoryItemArray = directoryBag.ParallelSort(Comparer<ListViewItem>.Create((x, y) =>
{
return string.Compare(((ZipEntryInfo)x.Tag).DisplayName, ((ZipEntryInfo)y.Tag).DisplayName, StringComparison.InvariantCultureIgnoreCase);
})).ToArray();
var fileItemArray = fileBag.ParallelSort(Comparer<ListViewItem>.Create((x, y) =>
{
string xString = ((ZipEntryInfo)x.Tag).DisplayName;
string xExtension = Path.GetExtension(xString);
xString = Path.GetFileNameWithoutExtension(xString);
string yString = ((ZipEntryInfo)y.Tag).DisplayName;
string yExtension = Path.GetExtension(yString);
yString = Path.GetFileNameWithoutExtension(yString);
return (xExtension + xString).CompareIgnoreCultureAndCase(yExtension + yString);
})).ToArray();
listView.BeginUpdate();
try
{
listView.Items.Clear();
listView.Items.AddRange(directoryItemArray);
listView.Items.AddRange(fileItemArray);
if (fileItemArray.Length == 0)
{
sizeHeader.Text = string.Empty;
modifiedHeader.Text = string.Empty;
}
else
{
sizeHeader.Text = "Size";
modifiedHeader.Text = "Modified Date";
}
if (listView.Items.Count > 0)
listView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
}
finally { listView.EndUpdate(); }
}
validZipFile = true;
}
catch (InvalidDataException ex)
{
ex.Log();
MessageBox.Show(Browser.Instance, string.Format("The zip file appears to be invalid and could not be read.\n{0}", ex.Message), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
UpdateStatusStripInfo();
SelectLastDirectory();
// The vertical scrollbar is not being painted unless we mouse-over it or force a repaint.
// Thanks to writing my own crappy control instead of downloading a decent one.
listView.Invalidate(true);
listView.Update();
InitializeControls();
}