Skip to content

Commit ca4205c

Browse files
committed
Case-insensitive ZIP loading
1 parent 0fbdf27 commit ca4205c

File tree

4 files changed

+124
-5
lines changed

4 files changed

+124
-5
lines changed

polymod/fs/SysZipFileSystem.hx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package polymod.fs;
22

3+
import polymod.util.insensitive.*;
34
import polymod.fs.ZipFileSystem.ZipFileSystemParams;
45
#if !sys
56
class SysZipFileSystem extends polymod.fs.StubFileSystem
@@ -33,7 +34,7 @@ class SysZipFileSystem extends SysFileSystem
3334
/**
3435
* Specifies the name of the ZIP that contains each file.
3536
*/
36-
var filesLocations:Map<String, String>;
37+
var filesLocations:InsensitiveMap<String>;
3738

3839
/**
3940
* Specifies the names of available directories within the ZIP files.
@@ -48,7 +49,7 @@ class SysZipFileSystem extends SysFileSystem
4849
public function new(params:ZipFileSystemParams)
4950
{
5051
super(params);
51-
filesLocations = new Map<String, String>();
52+
filesLocations = new InsensitiveMap();
5253
zipParsers = new Map<String, ZipParser>();
5354
fileDirectories = [];
5455

@@ -125,8 +126,9 @@ class SysZipFileSystem extends SysFileSystem
125126
return super.isDirectory(path);
126127
}
127128

128-
public override function readDirectory(path:String)
129+
public override function readDirectory(path:String):Array<String>
129130
{
131+
var path:InsensitiveString = path;
130132
// Remove trailing slash
131133
if (path.endsWith("/"))
132134
path = path.substring(0, path.length - 1);
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package polymod.util.insensitive;
2+
3+
import haxe.ds.StringMap;
4+
import haxe.Constraints.IMap;
5+
6+
/**
7+
* A string map which treats any letter cases the same (case insensitive)
8+
*/
9+
class InsensitiveMap<T> implements IMap<String, T> {
10+
var data:StringMap<T> = new StringMap();
11+
var originalKeys:Map<String, String> = new Map();
12+
13+
public function new() {}
14+
15+
public function set(key:String, value:T):Void {
16+
var lowerKey = key.toLowerCase();
17+
if (data.exists(key)) {
18+
trace('[WARNING] Key "$key" already exists and will be not be overwritten.');
19+
return;
20+
}
21+
22+
data.set(lowerKey, value);
23+
originalKeys.set(lowerKey, key);
24+
}
25+
26+
public inline function get(key:String):Null<T> {
27+
return data.get(key.toLowerCase());
28+
}
29+
30+
public inline function exists(key:String):Bool {
31+
return data.exists(key.toLowerCase());
32+
}
33+
34+
public function remove(key:String):Bool {
35+
var lowerKey = key.toLowerCase();
36+
originalKeys.remove(lowerKey);
37+
return data.remove(lowerKey);
38+
}
39+
40+
public function clear():Void {
41+
data.clear();
42+
originalKeys.clear();
43+
}
44+
45+
public function copy():InsensitiveMap<T> {
46+
var res = new InsensitiveMap();
47+
res.data = data.copy();
48+
res.originalKeys = originalKeys.copy();
49+
return res;
50+
}
51+
52+
public inline function keys():Iterator<String> {
53+
return originalKeys.iterator();
54+
}
55+
56+
public function keyValueIterator() {
57+
return {
58+
var it = originalKeys.keys();
59+
return {
60+
hasNext: function() return it.hasNext(),
61+
next: function() {
62+
var lowerKey = it.next();
63+
var originalKey = originalKeys.get(lowerKey);
64+
return { key: originalKey, value: data.get(lowerKey) };
65+
}
66+
};
67+
};
68+
}
69+
70+
public inline function iterator() {
71+
return data.iterator();
72+
}
73+
74+
public function toString():String {
75+
var parts = [];
76+
for (key in keys()) {
77+
parts.push('$key => ${get(key)}');
78+
}
79+
return '{' + parts.join(', ') + '}';
80+
}
81+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package polymod.util.insensitive;
2+
3+
using StringTools;
4+
5+
/**
6+
* A case-insensitive string. (NOTE: Doesn't work properly in containers (Map, Array, etc.))
7+
*/
8+
@:forward
9+
abstract InsensitiveString(String) from String to String {
10+
public inline function new(s:String) {
11+
this = s;
12+
}
13+
14+
// Operators
15+
@:op(A==B)
16+
inline function eq(b:InsensitiveString):Bool
17+
return this.toLowerCase() == b.toLowerCase();
18+
19+
@:op(A!=B)
20+
inline function notEq(b:InsensitiveString):Bool
21+
return this.toLowerCase() != b.toLowerCase();
22+
23+
// Overrides
24+
public inline function startsWith(s:InsensitiveString):Bool
25+
return this.toLowerCase().startsWith(s.toLowerCase());
26+
27+
public inline function endsWith(s:InsensitiveString):Bool
28+
return this.toLowerCase().endsWith(s.toLowerCase());
29+
30+
public inline function indexOf(s:String, ?startIndex:Int):Int
31+
return this.toLowerCase().indexOf(s.toLowerCase(), startIndex);
32+
33+
public inline function lastIndexOf(s:String, ?startIndex:Int):Int
34+
return this.toLowerCase().lastIndexOf(s.toLowerCase(), startIndex);
35+
}

polymod/util/zip/ZipParser.hx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package polymod.util.zip;
33
#if sys
44
import haxe.ds.StringMap;
55
import haxe.io.Bytes;
6+
import polymod.util.insensitive.InsensitiveMap;
67
import sys.io.File;
78
import sys.io.FileInput;
89

@@ -34,7 +35,7 @@ class ZipParser
3435
* The central directory records, as parsed from the central directory.
3536
* These contain metadata about each file in the archive.
3637
*/
37-
public var centralDirectoryRecords:StringMap<CentralDirectoryFileHeader>;
38+
public var centralDirectoryRecords:InsensitiveMap<CentralDirectoryFileHeader>;
3839

3940
public function new(fileName:String)
4041
{
@@ -68,7 +69,7 @@ class ZipParser
6869
*/
6970
function getAllCentralDirectoryHeaders():Void
7071
{
71-
this.centralDirectoryRecords = new StringMap();
72+
this.centralDirectoryRecords = new InsensitiveMap();
7273
fileHandle.seek(this.endOfCentralDirectoryRecord.cdrOffset, SeekBegin);
7374
for (_ in 0...this.endOfCentralDirectoryRecord.cdrsTotal)
7475
{

0 commit comments

Comments
 (0)