15 internal class JarFile : IDisposable
18 const string META_INF =
"META-INF/";
19 const string META_INF_VERSIONS = META_INF +
"versions/";
20 const string MANIFEST_NAME = META_INF +
"MANIFEST.MF";
21 const string INDEX_NAME =
"META-INF/INDEX.LIST";
23 internal static readonly RuntimeVersion BASE_VERSION = RuntimeVersion.Parse(
"8");
24 internal static readonly
int BASE_VERSION_FEATURE = BASE_VERSION.Feature;
25 internal static readonly RuntimeVersion RUNTIME_VERSION = RuntimeVersion.Parse(
"8");
26 internal static readonly
bool MULTI_RELEASE_ENABLED =
false;
27 internal static readonly
bool MULTI_RELEASE_FORCED =
false;
29 readonly ZipArchive _archive;
30 readonly RuntimeVersion _version;
31 readonly
int _versionFeature;
32 readonly
bool _dispose;
35 bool _hasCheckedSpecialAttributes;
36 bool _hasClassPathAttribute;
45 public JarFile(ZipArchive archive,
bool dispose =
false) :
46 this(archive, RUNTIME_VERSION, dispose)
58 public JarFile(ZipArchive archive, RuntimeVersion version,
bool dispose =
false)
60 _archive = archive ??
throw new ArgumentNullException(nameof(archive));
63 if (MULTI_RELEASE_FORCED || version.Feature == RUNTIME_VERSION.Feature)
65 _version = RUNTIME_VERSION;
67 else if (version.Feature <= BASE_VERSION_FEATURE)
69 _version = BASE_VERSION;
73 _version = RuntimeVersion.Parse(version.Feature.ToString());
76 _versionFeature = _version.Feature;
84 public JarFile(Stream stream,
bool dispose =
true) :
85 this(stream, RUNTIME_VERSION, dispose)
96 public JarFile(Stream stream, RuntimeVersion releaseVersion,
bool dispose =
true) :
97 this(
new ZipArchive(stream, ZipArchiveMode.Read, dispose), releaseVersion, dispose)
106 public JarFile(
string path) :
107 this(path, RUNTIME_VERSION)
117 public JarFile(
string path, RuntimeVersion releaseVersion) :
118 this(File.OpenRead(path), releaseVersion,
true)
126 public RuntimeVersion Version => IsMultiRelease ? _version : BASE_VERSION;
131 public Manifest? Manifest => GetOrReadManifest();
137 Manifest? GetOrReadManifest()
139 if (_manifest is
null)
149 Manifest? ReadManifest()
152 var e = _archive.GetEntry(MANIFEST_NAME);
157 using var s = e.Open();
158 return new Manifest(s);
164 void CheckForSpecialAttributes()
166 if (_hasCheckedSpecialAttributes)
169 if (Manifest is not
null)
171 if (Manifest.MainAttributes.TryGetValue(
"Class-Path", out _))
172 _hasClassPathAttribute =
true;
174 if (Manifest.MainAttributes.TryGetValue(
"Multi-Release", out var s))
175 _isMultiRelease =
bool.Parse(s);
178 _hasCheckedSpecialAttributes =
true;
184 public bool HasClassPathAttribute
188 if (_hasClassPathAttribute)
191 CheckForSpecialAttributes();
192 return _hasClassPathAttribute;
199 public bool IsMultiRelease
206 CheckForSpecialAttributes();
207 return _isMultiRelease;
214 public IEnumerable<JarFileEntry> GetEntries()
216 var hs =
new HashSet<string>();
218 foreach (var entry
in _archive.Entries)
220 var bn = GetBaseName(entry.FullName);
226 var je = GetEntry(bn);
228 yield
return je.Value;
238 string? GetBaseName(
string name)
240 if (name.StartsWith(META_INF_VERSIONS))
242 int off = META_INF_VERSIONS.Length;
243 int idx = name.IndexOf(
'/', off);
247 if (idx == -1 || idx == (name.Length - 1) || (
int.TryParse(name[off..idx], out var i) && i > _versionFeature))
251 return name.Substring(idx + 1);
262 public JarFileEntry? GetEntry(
string name)
266 var ve = GetVersionedEntry(name,
null);
271 var je = _archive.GetEntry(name);
273 return new JarFileEntry(
this, je, name, _versionFeature);
284 JarFileEntry? GetVersionedEntry(
string name, JarFileEntry? defaultEntry)
286 if (BASE_VERSION_FEATURE < _versionFeature && name.StartsWith(META_INF) ==
false)
289 var version = _versionFeature;
292 while (version >= BASE_VERSION_FEATURE)
294 var entry = _archive.GetEntry(META_INF_VERSIONS + version +
"/" + name);
296 return new JarFileEntry(
this, entry, name, version);
308 public void Dispose()
310 if (_dispose && _archive !=
null)