extractModuleName static method
Extracts the module name from a JAR file
Attempts to determine the Java module name by examining:
- The JAR's file path for known module patterns
- module-info.class file
- META-INF/module-name or META-INF/automatic-module-name
- META-INF/MANIFEST.MF for Automatic-Module-Name
- Falls back to deriving a name from the filename using Java 9 automatic module naming rules
jarPath The path to the JAR file
Returns the module name if found, null otherwise
Implementation
static Future<String?> extractModuleName(String jarPath) async {
try {
final jarFile = File(jarPath);
if (!await jarFile.exists()) {
return null;
}
final bytes = await jarFile.readAsBytes();
final archive = ZipDecoder().decodeBytes(bytes);
final fileName = p.basename(jarPath);
final filePath = jarPath.replaceAll('\\', '/');
if (filePath.contains('net/minecraftforge/securemodules/')) {
return 'cpw.mods.securejarhandler';
} else if (filePath.contains('net/minecraftforge/unsafe/')) {
return 'net.minecraftforge.unsafe';
} else if (filePath.contains('net/minecraftforge/bootstrap-api/')) {
return 'net.minecraftforge.bootstrap.api';
} else if (filePath.contains('net/minecraftforge/bootstrap/')) {
return 'net.minecraftforge.bootstrap';
} else if (filePath.contains('net/minecraftforge/JarJarFileSystems/')) {
return 'JarJarFileSystems';
} else if (filePath.contains('com/google/guava/guava/')) {
return 'com.google.common';
} else if (filePath.contains('com/google/guava/failureaccess/')) {
return 'failureaccess';
} else if (filePath.contains('net/minecraftforge/accesstransformers/')) {
return 'accesstransformers';
} else if (filePath.contains('net/minecraftforge/eventbus/')) {
return 'net.minecraftforge.eventbus';
} else if (filePath.contains('net/jodah/typetools/')) {
return 'net.jodah.typetools';
} else if (filePath.contains('net/minecraftforge/forgespi/')) {
return 'net.minecraftforge.forgespi';
} else if (filePath.contains('net/minecraftforge/coremods/')) {
return 'net.minecraftforge.coremod';
} else if (filePath.contains('org/openjdk/nashorn/')) {
return 'org.openjdk.nashorn';
} else if (filePath.contains('net/minecraftforge/modlauncher/')) {
return 'cpw.mods.modlauncher';
} else if (filePath.contains('net/minecraftforge/mergetool-api/')) {
return 'net.minecraftforge.mergetool.api';
} else if (filePath.contains('com/electronwill/night-config/toml/')) {
return 'com.electronwill.nightconfig.toml';
} else if (filePath.contains('com/electronwill/night-config/core/')) {
return 'com.electronwill.nightconfig.core';
} else if (filePath.contains('org/apache/maven/maven-artifact/')) {
return 'maven.artifact';
} else if (filePath.contains('net/minecrell/terminalconsoleappender/')) {
return 'terminalconsoleappender';
} else if (filePath.contains('org/jline/jline-reader/')) {
return 'org.jline.reader';
} else if (filePath.contains('org/jline/jline-terminal/')) {
return 'org.jline.terminal';
}
if (filePath.contains('org/ow2/asm/')) {
if (fileName.startsWith('asm-')) {
final subModule = fileName.split('-')[1].split('.')[0];
return 'org.objectweb.asm.$subModule';
}
return 'org.objectweb.asm';
}
final moduleInfoFile = archive.files.firstWhere(
(file) => file.name == 'module-info.class',
orElse:
() => archive.files.firstWhere(
(file) => file.name.endsWith('/module-info.class'),
orElse:
() => archive.files.firstWhere(
(file) => file.name.toLowerCase() == 'meta-inf/module-name',
orElse:
() => archive.files.firstWhere(
(file) =>
file.name.toLowerCase() ==
'meta-inf/automatic-module-name',
orElse: () => ArchiveFile('', 0, []),
),
),
),
);
if (moduleInfoFile.size > 0) {
if (moduleInfoFile.name.toLowerCase() == 'meta-inf/module-name' ||
moduleInfoFile.name.toLowerCase() ==
'meta-inf/automatic-module-name') {
final content = String.fromCharCodes(
moduleInfoFile.content as List<int>,
);
return content.trim();
}
final fileName = p.basename(jarPath);
final moduleName = fileName.replaceAll(RegExp(r'\.jar$'), '');
return moduleName;
}
final manifestFile = archive.files.firstWhere(
(file) => file.name.toUpperCase() == 'META-INF/MANIFEST.MF',
orElse: () => ArchiveFile('', 0, []),
);
if (manifestFile.size > 0) {
final content = String.fromCharCodes(manifestFile.content as List<int>);
final lines = content.split('\n');
for (final line in lines) {
if (line.startsWith('Automatic-Module-Name:')) {
return line.substring('Automatic-Module-Name:'.length).trim();
}
}
}
final nameWithoutExtension = fileName.replaceAll(RegExp(r'\.jar$'), '');
final versionPattern = RegExp(r'-\d+(\.\d+)*(-[a-zA-Z0-9]+)?$');
final baseName = nameWithoutExtension.replaceAll(versionPattern, '');
String moduleName = baseName
.replaceAll(RegExp(r'[^a-zA-Z0-9]'), '.')
.replaceAll(RegExp(r'\.+'), '.')
.replaceAll(RegExp(r'^\.'), '')
.replaceAll(RegExp(r'\.$'), '');
return moduleName;
} catch (e) {
debugPrint('Error extracting module name from $jarPath: $e');
return null;
}
}