Search in sources :

Example 1 with VersionParsingException

use of net.fabricmc.loader.api.VersionParsingException in project quilt-loader by QuiltMC.

the class V1ModMetadataParser method parse.

/**
 * Reads a {@code fabric.mod.json} file of schema version {@code 1}.
 *
 * @param logger the logger to print warnings to
 * @param reader the json reader to read the file with
 * @return the metadata of this file, null if the file could not be parsed
 * @throws IOException         if there was any issue reading the file
 */
static FabricLoaderModMetadata parse(JsonReader reader) throws IOException, ParseMetadataException {
    List<ParseWarning> warnings = new ArrayList<>();
    // All the values the `fabric.mod.json` may contain:
    // Required
    String id = null;
    Version version = null;
    // Optional (id provides)
    List<String> provides = new ArrayList<>();
    // Optional (mod loading)
    // Default is always universal
    ModEnvironment environment = ModEnvironment.UNIVERSAL;
    Map<String, List<EntrypointMetadata>> entrypoints = new HashMap<>();
    List<NestedJarEntry> jars = new ArrayList<>();
    List<V1ModMetadataFabric.MixinEntry> mixins = new ArrayList<>();
    String accessWidener = null;
    // Optional (dependency resolution)
    List<ModDependency> dependencies = new ArrayList<>();
    // Happy little accidents
    boolean hasRequires = false;
    // Optional (metadata)
    String name = null;
    String description = null;
    List<Person> authors = new ArrayList<>();
    List<Person> contributors = new ArrayList<>();
    ContactInformation contact = null;
    List<String> license = new ArrayList<>();
    V1ModMetadataFabric.IconEntry icon = null;
    // Optional (language adapter providers)
    Map<String, String> languageAdapters = new HashMap<>();
    // Optional (custom values)
    Map<String, CustomValue> customValues = new HashMap<>();
    while (reader.hasNext()) {
        final String key = reader.nextName();
        // Work our way from required to entirely optional
        switch(key) {
            case "schemaVersion":
                // Duplicate field, make sure it matches our current schema version
                if (reader.peek() != JsonToken.NUMBER) {
                    throw new ParseMetadataException("Duplicate \"schemaVersion\" field is not a number", reader);
                }
                final int read = reader.nextInt();
                if (read != 1) {
                    throw new ParseMetadataException(String.format("Duplicate \"schemaVersion\" field does not match the predicted schema version of 1. Duplicate field value is %s", read), reader);
                }
                break;
            case "id":
                if (reader.peek() != JsonToken.STRING) {
                    throw new ParseMetadataException("Mod id must be a non-empty string with a length of 3-64 characters.", reader);
                }
                id = reader.nextString();
                break;
            case "version":
                if (reader.peek() != JsonToken.STRING) {
                    throw new ParseMetadataException("Version must be a non-empty string", reader);
                }
                try {
                    version = VersionParser.parse(reader.nextString(), false);
                } catch (VersionParsingException e) {
                    throw new ParseMetadataException("Failed to parse version", e);
                }
                break;
            case "provides":
                readProvides(reader, provides);
                break;
            case "environment":
                if (reader.peek() != JsonToken.STRING) {
                    throw new ParseMetadataException("Environment must be a string", reader);
                }
                environment = readEnvironment(reader);
                break;
            case "entrypoints":
                readEntrypoints(warnings, reader, entrypoints);
                break;
            case "jars":
                readNestedJarEntries(warnings, reader, jars);
                break;
            case "mixins":
                readMixinConfigs(warnings, reader, mixins);
                break;
            case "accessWidener":
                if (reader.peek() != JsonToken.STRING) {
                    throw new ParseMetadataException("Access Widener file must be a string", reader);
                }
                accessWidener = reader.nextString();
                break;
            case "depends":
                readDependenciesContainer(reader, ModDependency.Kind.DEPENDS, dependencies);
                break;
            case "recommends":
                readDependenciesContainer(reader, ModDependency.Kind.RECOMMENDS, dependencies);
                break;
            case "suggests":
                readDependenciesContainer(reader, ModDependency.Kind.SUGGESTS, dependencies);
                break;
            case "conflicts":
                readDependenciesContainer(reader, ModDependency.Kind.CONFLICTS, dependencies);
                break;
            case "breaks":
                readDependenciesContainer(reader, ModDependency.Kind.BREAKS, dependencies);
                break;
            case "requires":
                hasRequires = true;
                reader.skipValue();
                break;
            case "name":
                if (reader.peek() != JsonToken.STRING) {
                    throw new ParseMetadataException("Mod name must be a string", reader);
                }
                name = reader.nextString();
                break;
            case "description":
                if (reader.peek() != JsonToken.STRING) {
                    throw new ParseMetadataException("Mod description must be a string", reader);
                }
                description = reader.nextString();
                break;
            case "authors":
                readPeople(warnings, reader, authors);
                break;
            case "contributors":
                readPeople(warnings, reader, contributors);
                break;
            case "contact":
                contact = readContactInfo(reader);
                break;
            case "license":
                readLicense(reader, license);
                break;
            case "icon":
                icon = readIcon(reader);
                break;
            case "languageAdapters":
                readLanguageAdapters(reader, languageAdapters);
                break;
            case "custom":
                readCustomValues(reader, customValues);
                break;
            case "$schema":
                reader.skipValue();
                break;
            default:
                if (!FabricModMetadataReader.IGNORED_KEYS.contains(key)) {
                    warnings.add(new ParseWarning(reader.locationString(), key, "Unsupported root entry"));
                }
                reader.skipValue();
                break;
        }
    }
    // Validate all required fields are resolved
    if (id == null) {
        throw new ParseMetadataException.MissingField("id");
    }
    if (version == null) {
        throw new ParseMetadataException.MissingField("version");
    }
    FabricModMetadataReader.logWarningMessages(id, warnings);
    return new V1ModMetadataFabric(id, version, provides, environment, entrypoints, jars, mixins, accessWidener, dependencies, hasRequires, name, description, authors, contributors, contact, license, icon, languageAdapters, customValues);
}
Also used : HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) ModDependency(net.fabricmc.loader.api.metadata.ModDependency) CustomValue(net.fabricmc.loader.api.metadata.CustomValue) Version(net.fabricmc.loader.api.Version) ArrayList(java.util.ArrayList) List(java.util.List) ModEnvironment(net.fabricmc.loader.api.metadata.ModEnvironment) ContactInformation(net.fabricmc.loader.api.metadata.ContactInformation) VersionParsingException(net.fabricmc.loader.api.VersionParsingException) Person(net.fabricmc.loader.api.metadata.Person)

Example 2 with VersionParsingException

use of net.fabricmc.loader.api.VersionParsingException in project BCLib by paulevsGitch.

the class ModUtil method readJSON.

private static ModMetadata readJSON(InputStream is, String sourceFile) throws IOException {
    try (com.google.gson.stream.JsonReader reader = new JsonReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
        JsonObject data = new JsonParser().parse(reader).getAsJsonObject();
        Version ver;
        try {
            ver = new SemanticVersionImpl(data.get("version").getAsString(), false);
        } catch (VersionParsingException e) {
            BCLib.LOGGER.error("Unable to parse Version in " + sourceFile);
            return null;
        }
        if (data.get("id") == null) {
            BCLib.LOGGER.error("Unable to read ID in " + sourceFile);
            return null;
        }
        if (data.get("name") == null) {
            BCLib.LOGGER.error("Unable to read name in " + sourceFile);
            return null;
        }
        return new ModMetadata() {

            @Override
            public Version getVersion() {
                return ver;
            }

            @Override
            public String getType() {
                return "fabric";
            }

            @Override
            public String getId() {
                return data.get("id").getAsString();
            }

            @Override
            public Collection<String> getProvides() {
                return new ArrayList<>();
            }

            @Override
            public ModEnvironment getEnvironment() {
                JsonElement env = data.get("environment");
                if (env == null) {
                    BCLib.LOGGER.warning("No environment specified in " + sourceFile);
                // return ModEnvironment.UNIVERSAL;
                }
                final String environment = env == null ? "" : env.getAsString().toLowerCase(Locale.ROOT);
                if (environment.isEmpty() || environment.equals("*") || environment.equals("\"*\"") || environment.equals("common")) {
                    JsonElement entrypoints = data.get("entrypoints");
                    boolean hasClient = true;
                    // check if there is an actual client entrypoint
                    if (entrypoints != null && entrypoints.isJsonObject()) {
                        JsonElement client = entrypoints.getAsJsonObject().get("client");
                        if (client != null && client.isJsonArray()) {
                            hasClient = client.getAsJsonArray().size() > 0;
                        } else if (client == null || !client.isJsonPrimitive()) {
                            hasClient = false;
                        } else if (!client.getAsJsonPrimitive().isString()) {
                            hasClient = false;
                        }
                    }
                    // if (hasClient == false) return ModEnvironment.SERVER;
                    return ModEnvironment.UNIVERSAL;
                } else if (environment.equals("client")) {
                    return ModEnvironment.CLIENT;
                } else if (environment.equals("server")) {
                    return ModEnvironment.SERVER;
                } else {
                    BCLib.LOGGER.error("Unable to read environment in " + sourceFile);
                    return ModEnvironment.UNIVERSAL;
                }
            }

            @Override
            public Collection<ModDependency> getDepends() {
                return new ArrayList<>();
            }

            @Override
            public Collection<ModDependency> getRecommends() {
                return new ArrayList<>();
            }

            @Override
            public Collection<ModDependency> getSuggests() {
                return new ArrayList<>();
            }

            @Override
            public Collection<ModDependency> getConflicts() {
                return new ArrayList<>();
            }

            @Override
            public Collection<ModDependency> getBreaks() {
                return new ArrayList<>();
            }

            public Collection<ModDependency> getDependencies() {
                return new ArrayList<>();
            }

            @Override
            public String getName() {
                return data.get("name").getAsString();
            }

            @Override
            public String getDescription() {
                return "";
            }

            @Override
            public Collection<Person> getAuthors() {
                return new ArrayList<>();
            }

            @Override
            public Collection<Person> getContributors() {
                return new ArrayList<>();
            }

            @Override
            public ContactInformation getContact() {
                return null;
            }

            @Override
            public Collection<String> getLicense() {
                return new ArrayList<>();
            }

            @Override
            public Optional<String> getIconPath(int size) {
                return Optional.empty();
            }

            @Override
            public boolean containsCustomValue(String key) {
                return false;
            }

            @Override
            public CustomValue getCustomValue(String key) {
                return null;
            }

            @Override
            public Map<String, CustomValue> getCustomValues() {
                return new HashMap<>();
            }

            @Override
            public boolean containsCustomElement(String key) {
                return false;
            }

            public JsonElement getCustomElement(String key) {
                return null;
            }
        };
    }
}
Also used : SemanticVersionImpl(net.fabricmc.loader.util.version.SemanticVersionImpl) InputStreamReader(java.io.InputStreamReader) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) JsonObject(com.google.gson.JsonObject) ModDependency(net.fabricmc.loader.api.metadata.ModDependency) JsonReader(com.google.gson.stream.JsonReader) CustomValue(net.fabricmc.loader.api.metadata.CustomValue) VersionParsingException(net.fabricmc.loader.api.VersionParsingException) ModMetadata(net.fabricmc.loader.api.metadata.ModMetadata) Version(net.fabricmc.loader.api.Version) SemanticVersion(net.fabricmc.loader.api.SemanticVersion) JsonElement(com.google.gson.JsonElement) JsonReader(com.google.gson.stream.JsonReader) Person(net.fabricmc.loader.api.metadata.Person) JsonParser(com.google.gson.JsonParser)

Example 3 with VersionParsingException

use of net.fabricmc.loader.api.VersionParsingException in project fabric-loader by FabricMC.

the class Log4jLogHandler method needsLookupRemoval.

private static boolean needsLookupRemoval() {
    Manifest manifest;
    try {
        manifest = ManifestUtil.readManifest(LogManager.class);
    } catch (IOException | URISyntaxException e) {
        Log.warn(LogCategory.GAME_PROVIDER, "Can't read Log4J2 Manifest", e);
        return true;
    }
    if (manifest == null)
        return true;
    String title = ManifestUtil.getManifestValue(manifest, Name.IMPLEMENTATION_TITLE);
    if (title == null || !title.toLowerCase(Locale.ENGLISH).contains("log4j"))
        return true;
    String version = ManifestUtil.getManifestValue(manifest, Name.IMPLEMENTATION_VERSION);
    if (version == null)
        return true;
    try {
        // 2.15+ doesn't lookup by default, but we patch anything up to 2.16 just in case
        return Version.parse(version).compareTo(Version.parse("2.16")) < 0;
    } catch (VersionParsingException e) {
        Log.warn(LogCategory.GAME_PROVIDER, "Can't parse Log4J2 Manifest version %s", version, e);
        return true;
    }
}
Also used : VersionParsingException(net.fabricmc.loader.api.VersionParsingException) IOException(java.io.IOException) URISyntaxException(java.net.URISyntaxException) Manifest(java.util.jar.Manifest) LogManager(org.apache.logging.log4j.LogManager)

Example 4 with VersionParsingException

use of net.fabricmc.loader.api.VersionParsingException in project fabric-loader by FabricMC.

the class V1ModMetadataParser method parse.

/**
 * Reads a {@code fabric.mod.json} file of schema version {@code 1}.
 *
 * @param logger the logger to print warnings to
 * @param reader the json reader to read the file with
 * @return the metadata of this file, null if the file could not be parsed
 * @throws IOException         if there was any issue reading the file
 */
static LoaderModMetadata parse(JsonReader reader) throws IOException, ParseMetadataException {
    List<ParseWarning> warnings = new ArrayList<>();
    // All the values the `fabric.mod.json` may contain:
    // Required
    String id = null;
    Version version = null;
    // Optional (id provides)
    List<String> provides = new ArrayList<>();
    // Optional (mod loading)
    // Default is always universal
    ModEnvironment environment = ModEnvironment.UNIVERSAL;
    Map<String, List<EntrypointMetadata>> entrypoints = new HashMap<>();
    List<NestedJarEntry> jars = new ArrayList<>();
    List<V1ModMetadata.MixinEntry> mixins = new ArrayList<>();
    String accessWidener = null;
    // Optional (dependency resolution)
    List<ModDependency> dependencies = new ArrayList<>();
    // Happy little accidents
    boolean hasRequires = false;
    // Optional (metadata)
    String name = null;
    String description = null;
    List<Person> authors = new ArrayList<>();
    List<Person> contributors = new ArrayList<>();
    ContactInformation contact = null;
    List<String> license = new ArrayList<>();
    V1ModMetadata.IconEntry icon = null;
    // Optional (language adapter providers)
    Map<String, String> languageAdapters = new HashMap<>();
    // Optional (custom values)
    Map<String, CustomValue> customValues = new HashMap<>();
    while (reader.hasNext()) {
        final String key = reader.nextName();
        // Work our way from required to entirely optional
        switch(key) {
            case "schemaVersion":
                // Duplicate field, make sure it matches our current schema version
                if (reader.peek() != JsonToken.NUMBER) {
                    throw new ParseMetadataException("Duplicate \"schemaVersion\" field is not a number", reader);
                }
                final int read = reader.nextInt();
                if (read != 1) {
                    throw new ParseMetadataException(String.format("Duplicate \"schemaVersion\" field does not match the predicted schema version of 1. Duplicate field value is %s", read), reader);
                }
                break;
            case "id":
                if (reader.peek() != JsonToken.STRING) {
                    throw new ParseMetadataException("Mod id must be a non-empty string with a length of 3-64 characters.", reader);
                }
                id = reader.nextString();
                break;
            case "version":
                if (reader.peek() != JsonToken.STRING) {
                    throw new ParseMetadataException("Version must be a non-empty string", reader);
                }
                try {
                    version = VersionParser.parse(reader.nextString(), false);
                } catch (VersionParsingException e) {
                    throw new ParseMetadataException("Failed to parse version", e);
                }
                break;
            case "provides":
                readProvides(reader, provides);
                break;
            case "environment":
                if (reader.peek() != JsonToken.STRING) {
                    throw new ParseMetadataException("Environment must be a string", reader);
                }
                environment = readEnvironment(reader);
                break;
            case "entrypoints":
                readEntrypoints(warnings, reader, entrypoints);
                break;
            case "jars":
                readNestedJarEntries(warnings, reader, jars);
                break;
            case "mixins":
                readMixinConfigs(warnings, reader, mixins);
                break;
            case "accessWidener":
                if (reader.peek() != JsonToken.STRING) {
                    throw new ParseMetadataException("Access Widener file must be a string", reader);
                }
                accessWidener = reader.nextString();
                break;
            case "depends":
                readDependenciesContainer(reader, ModDependency.Kind.DEPENDS, dependencies);
                break;
            case "recommends":
                readDependenciesContainer(reader, ModDependency.Kind.RECOMMENDS, dependencies);
                break;
            case "suggests":
                readDependenciesContainer(reader, ModDependency.Kind.SUGGESTS, dependencies);
                break;
            case "conflicts":
                readDependenciesContainer(reader, ModDependency.Kind.CONFLICTS, dependencies);
                break;
            case "breaks":
                readDependenciesContainer(reader, ModDependency.Kind.BREAKS, dependencies);
                break;
            case "requires":
                hasRequires = true;
                reader.skipValue();
                break;
            case "name":
                if (reader.peek() != JsonToken.STRING) {
                    throw new ParseMetadataException("Mod name must be a string", reader);
                }
                name = reader.nextString();
                break;
            case "description":
                if (reader.peek() != JsonToken.STRING) {
                    throw new ParseMetadataException("Mod description must be a string", reader);
                }
                description = reader.nextString();
                break;
            case "authors":
                readPeople(warnings, reader, authors);
                break;
            case "contributors":
                readPeople(warnings, reader, contributors);
                break;
            case "contact":
                contact = readContactInfo(reader);
                break;
            case "license":
                readLicense(reader, license);
                break;
            case "icon":
                icon = readIcon(reader);
                break;
            case "languageAdapters":
                readLanguageAdapters(reader, languageAdapters);
                break;
            case "custom":
                readCustomValues(reader, customValues);
                break;
            default:
                if (!ModMetadataParser.IGNORED_KEYS.contains(key)) {
                    warnings.add(new ParseWarning(reader.getLineNumber(), reader.getColumn(), key, "Unsupported root entry"));
                }
                reader.skipValue();
                break;
        }
    }
    // Validate all required fields are resolved
    if (id == null) {
        throw new ParseMetadataException.MissingField("id");
    }
    if (version == null) {
        throw new ParseMetadataException.MissingField("version");
    }
    ModMetadataParser.logWarningMessages(id, warnings);
    return new V1ModMetadata(id, version, provides, environment, entrypoints, jars, mixins, accessWidener, dependencies, hasRequires, name, description, authors, contributors, contact, license, icon, languageAdapters, customValues);
}
Also used : HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) ModDependency(net.fabricmc.loader.api.metadata.ModDependency) CustomValue(net.fabricmc.loader.api.metadata.CustomValue) Version(net.fabricmc.loader.api.Version) ArrayList(java.util.ArrayList) List(java.util.List) ModEnvironment(net.fabricmc.loader.api.metadata.ModEnvironment) ContactInformation(net.fabricmc.loader.api.metadata.ContactInformation) VersionParsingException(net.fabricmc.loader.api.VersionParsingException) Person(net.fabricmc.loader.api.metadata.Person)

Example 5 with VersionParsingException

use of net.fabricmc.loader.api.VersionParsingException in project fabric-loader by FabricMC.

the class VersionPredicateParser method parse.

public static VersionPredicate parse(String predicate) throws VersionParsingException {
    List<SingleVersionPredicate> predicateList = new ArrayList<>();
    for (String s : predicate.split(" ")) {
        s = s.trim();
        if (s.isEmpty() || s.equals("*")) {
            continue;
        }
        VersionComparisonOperator operator = VersionComparisonOperator.EQUAL;
        for (VersionComparisonOperator op : OPERATORS) {
            if (s.startsWith(op.getSerialized())) {
                operator = op;
                s = s.substring(op.getSerialized().length());
                break;
            }
        }
        Version version = VersionParser.parse(s, true);
        if (version instanceof SemanticVersion) {
            SemanticVersion semVer = (SemanticVersion) version;
            if (semVer.hasWildcard()) {
                // .x version -> replace with conventional version by replacing the operator
                if (operator != VersionComparisonOperator.EQUAL) {
                    throw new VersionParsingException("Invalid predicate: " + predicate + ", version ranges with wildcards (.X) require using the equality operator or no operator at all!");
                }
                assert !semVer.getPrereleaseKey().isPresent();
                int compCount = semVer.getVersionComponentCount();
                assert compCount == 2 || compCount == 3;
                operator = compCount == 2 ? VersionComparisonOperator.SAME_TO_NEXT_MAJOR : VersionComparisonOperator.SAME_TO_NEXT_MINOR;
                int[] newComponents = new int[semVer.getVersionComponentCount() - 1];
                for (int i = 0; i < semVer.getVersionComponentCount() - 1; i++) {
                    newComponents[i] = semVer.getVersionComponent(i);
                }
                version = new SemanticVersionImpl(newComponents, "", semVer.getBuildKey().orElse(null));
            }
        } else if (!operator.isMinInclusive() && !operator.isMaxInclusive()) {
            // non-semver without inclusive bound
            throw new VersionParsingException("Invalid predicate: " + predicate + ", version ranges need to be semantic version compatible to use operators that exclude the bound!");
        } else {
            // non-semver with inclusive bound
            operator = VersionComparisonOperator.EQUAL;
        }
        predicateList.add(new SingleVersionPredicate(operator, version));
    }
    if (predicateList.isEmpty()) {
        return AnyVersionPredicate.INSTANCE;
    } else if (predicateList.size() == 1) {
        return predicateList.get(0);
    } else {
        return new MultiVersionPredicate(predicateList);
    }
}
Also used : VersionParsingException(net.fabricmc.loader.api.VersionParsingException) Version(net.fabricmc.loader.api.Version) SemanticVersion(net.fabricmc.loader.api.SemanticVersion) ArrayList(java.util.ArrayList) VersionComparisonOperator(net.fabricmc.loader.api.metadata.version.VersionComparisonOperator) SemanticVersion(net.fabricmc.loader.api.SemanticVersion)

Aggregations

VersionParsingException (net.fabricmc.loader.api.VersionParsingException)16 Version (net.fabricmc.loader.api.Version)9 ArrayList (java.util.ArrayList)7 SemanticVersion (net.fabricmc.loader.api.SemanticVersion)6 ModDependency (net.fabricmc.loader.api.metadata.ModDependency)5 Person (net.fabricmc.loader.api.metadata.Person)5 ContactInformation (net.fabricmc.loader.api.metadata.ContactInformation)4 ModEnvironment (net.fabricmc.loader.api.metadata.ModEnvironment)4 IOException (java.io.IOException)3 InputStreamReader (java.io.InputStreamReader)3 HashMap (java.util.HashMap)3 CustomValue (net.fabricmc.loader.api.metadata.CustomValue)3 JsonParser (com.google.gson.JsonParser)2 InputStream (java.io.InputStream)2 URISyntaxException (java.net.URISyntaxException)2 List (java.util.List)2 Manifest (java.util.jar.Manifest)2 ModMetadata (net.fabricmc.loader.api.metadata.ModMetadata)2 VersionComparisonOperator (net.fabricmc.loader.api.metadata.version.VersionComparisonOperator)2 VersionPredicate (net.fabricmc.loader.api.metadata.version.VersionPredicate)2