Major overhaul

Many things still need to be added, but this should be already useable
This commit is contained in:
Timo Ley 2020-07-26 15:47:05 +02:00
parent 8da44daa99
commit cf456421d4
12 changed files with 391 additions and 264 deletions

View File

@ -16,5 +16,7 @@ dependencies {
compile 'com.google.code.gson:gson:+'
compile 'org.python:jython-standalone:2.7.0'
compile "com.github.TheRandomLabs:CurseAPI:master-SNAPSHOT"
compile 'com.google.code.findbugs:jsr305:3.0.2'
testCompile group: 'junit', name: 'junit', version: '4.12'
}

View File

@ -5,10 +5,8 @@ import com.therandomlabs.curseapi.CurseException;
import com.therandomlabs.curseapi.file.CurseFile;
import com.therandomlabs.curseapi.project.CurseProject;
import ley.anvil.addonscript.util.IRepository;
import ley.anvil.addonscript.v1.AddonscriptJSON;
import ley.anvil.addonscript.wrapper.MetaData;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Optional;
public class CurseforgeRepository implements IRepository {
@ -23,23 +21,18 @@ public class CurseforgeRepository implements IRepository {
}
@Override
public AddonscriptJSON.Meta getMeta(String artifact) {
AddonscriptJSON.Meta meta = null;
public MetaData getMeta(String artifact) {
MetaData meta = null;
CurseFile file = getFile(artifact);
if (file != null) {
meta = new AddonscriptJSON.Meta();
meta = new MetaData();
try {
CurseProject project = file.project();
meta.name = project.name();
String desc = project.descriptionPlainText();
meta.description = Arrays.asList(desc.split("\n"));
meta.description = desc.split("\n");
meta.website = project.url().toString();
meta.contributors = new ArrayList<>();
AddonscriptJSON.Contributor owner = new AddonscriptJSON.Contributor();
owner.roles = new ArrayList<>();
owner.roles.add("owner");
owner.name = project.author().name();
meta.contributors.add(owner);
meta.contributors.put(project.author().name(), new String[]{"owner"});
meta.icon = project.logo().url().toString();
} catch (CurseException e) {
e.printStackTrace();

View File

@ -1,10 +1,11 @@
package ley.anvil.addonscript.maven;
import ley.anvil.addonscript.util.IRepository;
import ley.anvil.addonscript.v1.AddonscriptJSON;
import ley.anvil.addonscript.wrapper.MetaData;
public class MavenRepository implements IRepository {
//TODO Complete this
String url;
public MavenRepository(String url) {
@ -18,7 +19,7 @@ public class MavenRepository implements IRepository {
}
@Override
public AddonscriptJSON.Meta getMeta(String artifact) {
public MetaData getMeta(String artifact) {
return null;
}
}

View File

@ -1,44 +0,0 @@
package ley.anvil.addonscript.python;
import ley.anvil.addonscript.installer.IInstaller;
import org.python.util.PythonInterpreter;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
public class PythonInstaller implements IInstaller {
String script;
PythonInterpreter python;
public PythonInstaller(String scriptlink) {
script = getScript(scriptlink);
python = new PythonInterpreter();
}
@Override
public void install(String[] params, File input) {
python.set("input", input.getAbsolutePath());
python.set("params", params);
python.exec(script);
}
public String getScript(String url) {
try {
URL scripturl = new URL(url);
BufferedReader reader = new BufferedReader(new InputStreamReader(scripturl.openStream()));
StringBuilder script = new StringBuilder();
String inputLine;
while ((inputLine = reader.readLine()) != null)
script.append(inputLine);
reader.close();
return script.toString();
} catch (IOException e) {
e.printStackTrace();
return "";
}
}
}

View File

@ -0,0 +1,7 @@
package ley.anvil.addonscript.util;
/**
* This indicates, that a method can cause a HTTPRequest and therefore it could take some time
*/
public @interface HTTPRequest {
}

View File

@ -1,6 +1,6 @@
package ley.anvil.addonscript.util;
import ley.anvil.addonscript.v1.AddonscriptJSON;
import ley.anvil.addonscript.wrapper.MetaData;
/**
* Interface for all repository types
@ -12,6 +12,7 @@ public interface IRepository {
* @param artifact The artifact without the repo ID prefix
* @return The URL of the file
*/
@HTTPRequest
String getFileURL(String artifact);
/**
@ -20,6 +21,9 @@ public interface IRepository {
* @param artifact The artifact without the repo ID prefix
* @return A Meta object with meta information
*/
AddonscriptJSON.Meta getMeta(String artifact);
@HTTPRequest
MetaData getMeta(String artifact);
}

View File

@ -2,6 +2,8 @@ package ley.anvil.addonscript.util;
import ley.anvil.addonscript.v1.AddonscriptJSON;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
@ -17,4 +19,12 @@ public class Utils {
}
}
public static boolean notEmpty(@Nullable String s) {
return s != null && !s.equals("");
}
public static String slashEnd(@Nonnull String s) {
return s + (s.endsWith("/") ? "" : "/");
}
}

View File

@ -2,27 +2,15 @@ package ley.anvil.addonscript.v1;
import com.google.gson.annotations.Expose;
import ley.anvil.addonscript.curse.CurseforgeRepository;
import ley.anvil.addonscript.forge.ForgeMeta;
import ley.anvil.addonscript.installer.IInstaller;
import ley.anvil.addonscript.installer.InternalDirInstaller;
import ley.anvil.addonscript.maven.MavenRepository;
import ley.anvil.addonscript.python.PythonInstaller;
import ley.anvil.addonscript.util.ASBase;
import ley.anvil.addonscript.util.IRepository;
import ley.anvil.addonscript.util.Indexes;
import ley.anvil.addonscript.util.Utils;
import ley.anvil.addonscript.wrapper.LinkInstPair;
import org.python.jline.internal.Nullable;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
public class AddonscriptJSON extends ASBase {
public Indexes indexes;
public boolean loaded;
public static AddonscriptJSON fromJSON(String json) {
return fromJSON(json, AddonscriptJSON.class);
}
@ -36,53 +24,6 @@ public class AddonscriptJSON extends ASBase {
return as;
}
public AddonscriptJSON() {
indexes = new Indexes();
loaded = false;
}
public void load() {
indexes.INSTALLERS.put("internal.dir", new InternalDirInstaller());
for (Repository r : repositories) {
indexes.REPOSITORIES.put(r.id, r.getRepository());
}
for (IndexEntry e : index) {
if (e.type != null && e.type.equals("addon"))
indexes.ADDONS.put(e.id, Utils.getFromURL(e.link));
else if (e.type != null && e.type.equals("version"))
indexes.VERSIONS.put(e.versionid, Utils.getFromURL(e.link).getDefaultVersion());
}
for (Version v : versions) {
if (!indexes.VERSIONS.containsKey(v.versionid))
indexes.VERSIONS.put(v.versionid, v);
}
loaded = true;
}
public Version getDefaultVersion() {
for (Version v : versions) {
if (v.versionid == -1)
return v;
}
if (versions.size() > 0)
return versions.get(0);
return new Version();
}
public Version getVersion(int versionid) {
if (indexes.VERSIONS.containsKey(versionid))
return indexes.VERSIONS.get(versionid);
return null;
}
public Version getVersion(String versionCondition) {
return null; //TODO Interpret version range
}
//JSON Parts
/**
* The ID of the addon
*/
@ -111,11 +52,6 @@ public class AddonscriptJSON extends ASBase {
@Expose
public List<Repository> repositories;
/**
* A list of external installers this file uses
*/
public List<Installer> installers;
/**
* Optional
* Meta information for this addon
@ -124,35 +60,6 @@ public class AddonscriptJSON extends ASBase {
public Meta meta;
public static class Version implements Comparable<Version> {
public List<Relation> getRelations(String side, boolean optionals, @Nullable String edition) {
List<Relation> list = new ArrayList<>();
for (Relation r : relations) {
if (r.hasOption(side) && (r.hasOption("required") || (optionals && r.hasOption("optional")) || r.hasOption("edition:" + edition)))
list.add(r);
}
return list;
}
public List<LinkInstPair> getRelLinks(Indexes indexes, String side, boolean optionals, @Nullable String installer, @Nullable String edition) {
List<LinkInstPair> list = new ArrayList<>();
for (Relation r : getRelations(side, optionals, edition)) {
list.addAll(r.getLinks(indexes, side, optionals, installer, edition));
}
return list;
}
public List<LinkInstPair> getLinks(Indexes indexes, String side, boolean optionals, @Nullable String installer, @Nullable String edition) {
List<LinkInstPair> list = new ArrayList<>();
for (File file : files) {
if (file != null && file.hasOption(side) && (installer == null || installer.equals(file.installer) || installer.equals(file.installer.split(":")[0])) && ((file.hasOption("optional") && optionals) || file.hasOption("edition:" + edition) || file.hasOption("required")))
list.add(new LinkInstPair(file.getLink(indexes), file.installer));
}
return list;
}
/**
* The name of this version
* (for example: 1.0, 1.1, 2.0)
@ -283,22 +190,6 @@ public class AddonscriptJSON extends ASBase {
}
public static class Installer {
/**
* The ID of this installer
* Must be unique to this file
*/
public String id;
/**
* A link or relative path to a python file, which is an installer
*/
public String link;
public IInstaller getInstaller() {
return new PythonInstaller(link);
}
}
public static class File {
/**
* The ID of this file.
@ -341,85 +232,10 @@ public class AddonscriptJSON extends ASBase {
@Expose
public List<String> options;
public boolean hasOption(String option) {
if (options != null)
return option.contains(option);
else {
switch (option) {
case "client":
case "server":
case "required": return true;
default: return false;
}
}
}
public String getLink(Indexes indexes) {
if (link != null && !link.equals("")) {
return link;
}
else if (artifact != null && !artifact.equals("")) {
for (IRepository repo : indexes.REPOSITORIES.values()) {
String url = repo.getFileURL(artifact);
if (!url.equals(""))
return url;
}
}
return "";
}
public Meta getMeta(Indexes indexes) {
if (artifact != null && !artifact.equals("")) {
for (IRepository repo : indexes.REPOSITORIES.values()) {
Meta meta = repo.getMeta(artifact);
if (meta != null) {
return meta;
}
}
}
Meta meta = new Meta();
meta.name = link;
return meta;
}
}
public static class Relation {
public Meta getMeta(Indexes indexes) {
if(indexes.ADDONS.values().contains(id) && indexes.ADDONS.get(id).meta != null)
return indexes.ADDONS.get(id).meta;
else if(meta != null)
return meta;
else if (type != null && type.equals("modloader") && id != null && id.equals("forge"))
return new ForgeMeta();
else
return file.getMeta(indexes);
}
public List<LinkInstPair> getLinks(Indexes indexes, String side, boolean optionals, @Nullable String installer, @Nullable String edition) {
List<LinkInstPair> list = new ArrayList<>();
if (indexes.ADDONS.containsKey(id)) {
AddonscriptJSON addon = indexes.ADDONS.get(id);
if (!addon.loaded)
addon.load();
Version version = addon.getVersion(versions);
if (version != null) {
for (File f : version.files) {
if (f != null && f.hasOption(side) && (installer == null || installer.equals(f.installer) || installer.equals(f.installer.split(":")[0])) && ((f.hasOption("optional") && optionals) || f.hasOption("edition:" + edition) || f.hasOption("required")))
list.add(new LinkInstPair(f.getLink(indexes), f.installer));
}
}
}
if (file != null && hasOption(side) && (installer == null || installer.equals(file.installer) || installer.equals(file.installer.split(":")[0])) && ((hasOption("optional") && optionals) || hasOption("edition:" + edition) || hasOption("required")))
list.add(new LinkInstPair(file.getLink(indexes), file.installer));
return list;
}
/**
* The ID of the relation
* This should be unique in this Addonscript file
@ -472,21 +288,6 @@ public class AddonscriptJSON extends ASBase {
*/
@Expose
public List<String> options;
public boolean hasOption(String option) {
if (options != null)
return option.contains(option);
else {
switch (option) {
case "client":
case "server":
case "required": return true;
default: return false;
}
}
}
}
public static class IndexEntry {

View File

@ -0,0 +1,259 @@
package ley.anvil.addonscript.wrapper;
import jdk.nashorn.api.scripting.URLReader;
import ley.anvil.addonscript.installer.InternalDirInstaller;
import ley.anvil.addonscript.util.HTTPRequest;
import ley.anvil.addonscript.util.Utils;
import ley.anvil.addonscript.v1.AddonscriptJSON;
import javax.annotation.Nullable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ASWrapper {
AddonscriptJSON json;
Indexes indexes;
RepoManager repoManager;
public ASWrapper(AddonscriptJSON json) {
this.json = json;
indexes = new Indexes();
repoManager = new RepoManager();
indexes.INSTALLERS.put("internal.dir", new InternalDirInstaller());
if (json.repositories != null) {
for (AddonscriptJSON.Repository r : json.repositories) {
repoManager.addRepository(r.id, r.getRepository());
}
}
if (json.index != null) {
for (AddonscriptJSON.IndexEntry e : json.index) {
if (e.type != null && e.type.equals("addon"))
indexes.ADDONS.put(e.id, Utils.getFromURL(e.link));
//TODO external versions
}
}
if (json.versions != null) {
for (AddonscriptJSON.Version v : json.versions) {
if (!indexes.VERSIONS.containsKey(v.versionid))
indexes.VERSIONS.put(v.versionid, new VersionWrapper(v));
}
}
}
public ASWrapper(File file) throws FileNotFoundException {
this(AddonscriptJSON.read(new FileReader(file)));
}
public ASWrapper(URL url) {
this(AddonscriptJSON.read(new URLReader(url)));
}
public AddonscriptJSON getJson() {
return json;
}
@Override
public String toString() {
return json.toJSON();
}
//Options
public List<String> defaultOptions() {
List<String> list = new ArrayList<>();
list.add("client");
list.add("server");
list.add("required");
return list;
}
//Versions
public VersionWrapper getDefaultVersion() {
for (AddonscriptJSON.Version v : json.versions) {
if (v.versionid == -1)
return new VersionWrapper(v);
}
if (json.versions.size() > 0)
return new VersionWrapper(json.versions.get(0));
return new VersionWrapper();
}
public VersionWrapper getVersion(int versionid) {
if (indexes.VERSIONS.containsKey(versionid))
return indexes.VERSIONS.get(versionid);
return new VersionWrapper();
}
public VersionWrapper getVersion(String versionCondition) {
return null; //TODO Interpret version range
}
public class VersionWrapper {
AddonscriptJSON.Version version;
public VersionWrapper(AddonscriptJSON.Version version) {
this.version = version;
}
public VersionWrapper() {
this.version = null;
}
public boolean exists() {
return version != null;
}
public List<RelationWrapper> getRelations(String[] conditions, @Nullable String type) {
List<RelationWrapper> list = new ArrayList<>();
if (exists() && version.relations != null) {
for (AddonscriptJSON.Relation r : version.relations) {
List<String> opt;
if (r.options != null)
opt = r.options;
else
opt = defaultOptions();
if (opt.containsAll(Arrays.asList(conditions)) && (type== null || r.type.equals(type))) {
list.add(new RelationWrapper(r));
}
}
}
return list;
}
public List<FileWrapper> getFiles(String[] conditions, @Nullable String installer) {
List<FileWrapper> list = new ArrayList<>();
if (exists() && version.files != null) {
for (AddonscriptJSON.File f : version.files) {
List<String> opt;
if (f.options != null)
opt = f.options;
else
opt = defaultOptions();
if (opt.containsAll(Arrays.asList(conditions)) && (installer == null || f.installer.startsWith(installer))) {
list.add(new FileWrapper(f));
}
}
}
return list;
}
public String getVersionName() {
if (exists() && Utils.notEmpty(version.version))
return version.version;
return "";
}
}
public class FileWrapper {
AddonscriptJSON.File file;
String link;
public FileWrapper(AddonscriptJSON.File file) {
this.file = file;
if (Utils.notEmpty(file.link)) {
link = file.link;
}
}
public boolean isURL() {
if (link != null && (link.startsWith("http://") || link.startsWith("https://"))) {
return true;
}
if (Utils.notEmpty(file.artifact)) {
return true;
}
return false;
}
public boolean isFile() {
return Utils.notEmpty(link) && link.startsWith("file://");
}
public File asFile(String jsonDir) {
if (isFile()) {
return new File(Utils.slashEnd(jsonDir) + link.replace("file://", ""));
}
throw new RuntimeException("This is not a local file, try to check it before calling it.");
}
@HTTPRequest
public String getLink() {
if (!Utils.notEmpty(link) && Utils.notEmpty(file.artifact)) {
String l = repoManager.resolveArtifact(file.artifact);
if (Utils.notEmpty(l))
link = l;
}
if (Utils.notEmpty(link))
return link;
throw new RuntimeException("JSON File broken");
}
public String getArtifact() {
if (Utils.notEmpty(file.artifact))
return file.artifact;
return "";
}
@Deprecated
public String getInstaller() {
return file.installer;
}
}
public class RelationWrapper {
AddonscriptJSON.Relation relation;
public RelationWrapper(AddonscriptJSON.Relation relation) {
this.relation = relation;
}
public boolean hasFile() {
return relation != null && relation.file != null;
}
public FileWrapper getFile() {
if (hasFile())
return new FileWrapper(relation.file);
throw new RuntimeException("Relation has no file");
}
public boolean isInIndex() {
return indexes.ADDONS.containsKey(relation.id);
}
public boolean isModloader() {
if (Utils.notEmpty(relation.id) && relation.id.equals("forge")) {
return true;
}
return Utils.notEmpty(relation.loaderfile);
}
public boolean hasLocalMeta() {
return relation.meta != null;
}
public MetaData getLocalMeta() {
if (hasLocalMeta())
return new MetaData(relation.meta);
return new MetaData();
}
}
}

View File

@ -1,4 +1,4 @@
package ley.anvil.addonscript.util;
package ley.anvil.addonscript.wrapper;
import ley.anvil.addonscript.installer.IInstaller;
import ley.anvil.addonscript.v1.AddonscriptJSON;
@ -10,14 +10,12 @@ public class Indexes {
public Indexes() {
INSTALLERS = new HashMap<>();
REPOSITORIES = new HashMap<>();
ADDONS = new HashMap<>();
VERSIONS = new HashMap<>();
}
public Map<String, IInstaller> INSTALLERS;
public Map<String, IRepository> REPOSITORIES;
public Map<String, AddonscriptJSON> ADDONS;
public Map<Integer, AddonscriptJSON.Version> VERSIONS;
public Map<Integer, ASWrapper.VersionWrapper> VERSIONS;
}

View File

@ -0,0 +1,50 @@
package ley.anvil.addonscript.wrapper;
import ley.anvil.addonscript.util.Utils;
import ley.anvil.addonscript.v1.AddonscriptJSON;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
public class MetaData {
public MetaData() {
contributors = new HashMap<>();
}
public MetaData(AddonscriptJSON.Meta meta) {
this();
name = meta.name;
icon = meta.icon;
website = meta.website;
if (meta.description != null)
description = meta.description.toArray(new String[0]);
if (meta.contributors != null) {
for (AddonscriptJSON.Contributor c : meta.contributors) {
String[] roles;
if (c.roles != null) {
roles = c.roles.toArray(new String[0]);
} else {
roles = new String[0];
}
if (Utils.notEmpty(c.name))
contributors.put(c.name, roles);
}
}
}
@Nullable
public String name;
@Nullable
public String icon;
@Nullable
public String website;
@Nullable
public String[] description;
@Nonnull
public Map<String, String[]> contributors;
}

View File

@ -0,0 +1,46 @@
package ley.anvil.addonscript.wrapper;
import ley.anvil.addonscript.util.HTTPRequest;
import ley.anvil.addonscript.util.IRepository;
import ley.anvil.addonscript.util.Utils;
import javax.annotation.Nonnull;
import java.util.HashMap;
import java.util.Map;
public class RepoManager {
HashMap<String, IRepository> repositories;
public RepoManager() {
repositories = new HashMap<>();
}
public void addRepository(@Nonnull String id, @Nonnull IRepository repo) {
repositories.put(id, repo);
}
@HTTPRequest
public String resolveArtifact(@Nonnull String artifact) {
for (IRepository repo : repositories.values()) {
String link = repo.getFileURL(artifact);
if (Utils.notEmpty(link))
return link;
}
throw new RuntimeException(artifact + " was not found in a known repository");
}
@HTTPRequest
public Map<String, MetaData> getMeta(@Nonnull String[] artifacts) {
Map<String, MetaData> metas = new HashMap<>();
for (String a : artifacts) {
for (IRepository r : repositories.values()) {
MetaData data = r.getMeta(a);
if (data != null)
metas.put(a, data);
}
}
return metas;
}
}