aboutsummaryrefslogtreecommitdiff
path: root/src/shared/java/net/minecraftforge/artifactural
diff options
context:
space:
mode:
Diffstat (limited to 'src/shared/java/net/minecraftforge/artifactural')
-rw-r--r--src/shared/java/net/minecraftforge/artifactural/base/artifact/ArtifactBase.java72
-rw-r--r--src/shared/java/net/minecraftforge/artifactural/base/artifact/SimpleArtifactIdentifier.java70
-rw-r--r--src/shared/java/net/minecraftforge/artifactural/base/artifact/SimpleArtifactMetadata.java86
-rw-r--r--src/shared/java/net/minecraftforge/artifactural/base/artifact/StreamableArtifact.java111
-rw-r--r--src/shared/java/net/minecraftforge/artifactural/base/cache/ArtifactCacheBase.java129
-rw-r--r--src/shared/java/net/minecraftforge/artifactural/base/cache/LocatedArtifactCache.java69
-rw-r--r--src/shared/java/net/minecraftforge/artifactural/base/repository/ArtifactProviderBuilder.java100
-rw-r--r--src/shared/java/net/minecraftforge/artifactural/base/repository/SimpleRepository.java44
-rw-r--r--src/shared/java/net/minecraftforge/artifactural/base/util/HashFunction.java135
-rw-r--r--src/shared/java/net/minecraftforge/artifactural/base/util/PatternReplace.java159
10 files changed, 975 insertions, 0 deletions
diff --git a/src/shared/java/net/minecraftforge/artifactural/base/artifact/ArtifactBase.java b/src/shared/java/net/minecraftforge/artifactural/base/artifact/ArtifactBase.java
new file mode 100644
index 0000000..ddcc8a5
--- /dev/null
+++ b/src/shared/java/net/minecraftforge/artifactural/base/artifact/ArtifactBase.java
@@ -0,0 +1,72 @@
+/*
+ * Artifactural
+ * Copyright (c) 2018-2021.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package net.minecraftforge.artifactural.base.artifact;
+
+import net.minecraftforge.artifactural.api.artifact.Artifact;
+import net.minecraftforge.artifactural.api.artifact.ArtifactIdentifier;
+import net.minecraftforge.artifactural.api.artifact.ArtifactMetadata;
+import net.minecraftforge.artifactural.api.artifact.ArtifactType;
+import net.minecraftforge.artifactural.api.cache.ArtifactCache;
+import net.minecraftforge.artifactural.api.transform.ArtifactTransformer;
+
+public abstract class ArtifactBase implements Artifact {
+
+ private final ArtifactIdentifier identifier;
+ private final ArtifactType type;
+ private final ArtifactMetadata metadata;
+
+ public ArtifactBase(ArtifactIdentifier identifier, ArtifactType type, ArtifactMetadata metadata) {
+ this.identifier = identifier;
+ this.type = type;
+ this.metadata = metadata;
+ }
+
+ @Override
+ public ArtifactIdentifier getIdentifier() {
+ return identifier;
+ }
+
+ @Override
+ public ArtifactType getType() {
+ return type;
+ }
+
+ @Override
+ public ArtifactMetadata getMetadata() {
+ return metadata;
+ }
+
+ @Override
+ public Artifact apply(ArtifactTransformer transformer) {
+ if (!transformer.appliesTo(this)) return this;
+ return transformer.transform(this);
+ }
+
+ @Override
+ public Artifact.Cached cache(ArtifactCache cache) {
+ return cache.store(this);
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "(" + identifier + ", " + type +", " + metadata;
+ }
+
+}
diff --git a/src/shared/java/net/minecraftforge/artifactural/base/artifact/SimpleArtifactIdentifier.java b/src/shared/java/net/minecraftforge/artifactural/base/artifact/SimpleArtifactIdentifier.java
new file mode 100644
index 0000000..5ab60f6
--- /dev/null
+++ b/src/shared/java/net/minecraftforge/artifactural/base/artifact/SimpleArtifactIdentifier.java
@@ -0,0 +1,70 @@
+/*
+ * Artifactural
+ * Copyright (c) 2018-2021.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package net.minecraftforge.artifactural.base.artifact;
+
+import net.minecraftforge.artifactural.api.artifact.ArtifactIdentifier;
+
+public class SimpleArtifactIdentifier implements ArtifactIdentifier {
+
+ private final String group, name, version, classifier, extension;
+
+ public SimpleArtifactIdentifier(String group, String name, String version, String classifier, String extension) {
+ this.group = group;
+ this.name = name;
+ this.version = version;
+ this.classifier = classifier;
+ this.extension = extension;
+ }
+
+ @Override
+ public String getGroup() {
+ return group;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getVersion() {
+ return version;
+ }
+
+ @Override
+ public String getClassifier() {
+ return classifier;
+ }
+
+ @Override
+ public String getExtension() {
+ return extension;
+ }
+
+ @Override
+ public String toString() {
+ String ret = getGroup() + ':' + getName() + ':' + getVersion();
+ if (classifier != null)
+ ret += ':' + getClassifier();
+ if ("jar".equals(extension))
+ ret += '@' + getExtension();
+ return ret;
+ }
+}
diff --git a/src/shared/java/net/minecraftforge/artifactural/base/artifact/SimpleArtifactMetadata.java b/src/shared/java/net/minecraftforge/artifactural/base/artifact/SimpleArtifactMetadata.java
new file mode 100644
index 0000000..21a1f20
--- /dev/null
+++ b/src/shared/java/net/minecraftforge/artifactural/base/artifact/SimpleArtifactMetadata.java
@@ -0,0 +1,86 @@
+/*
+ * Artifactural
+ * Copyright (c) 2018-2021.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package net.minecraftforge.artifactural.base.artifact;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import net.minecraftforge.artifactural.api.artifact.ArtifactMetadata;
+
+public class SimpleArtifactMetadata implements ArtifactMetadata {
+
+ private final List<Entry> entries = new LinkedList<>();
+ private String hash = null;
+
+ public SimpleArtifactMetadata() {
+ }
+
+ private SimpleArtifactMetadata(SimpleArtifactMetadata parent, Entry entry) {
+ this.entries.addAll(parent.entries);
+ this.entries.add(entry);
+ }
+
+ @Override
+ public ArtifactMetadata with(String key, String value) {
+ return new SimpleArtifactMetadata(this, new Entry(key, value));
+ }
+
+ @Override
+ public String getHash() {
+ if (hash != null) return hash;
+ try {
+ String str = entries.stream().map(Entry::toString).collect(Collectors.joining("\n"));
+ MessageDigest digest = MessageDigest.getInstance("SHA-1");
+ byte[] hashBytes = digest.digest(str.getBytes());
+ StringBuilder hashBuilder = new StringBuilder();
+ for (byte b : hashBytes) {
+ hashBuilder.append(String.format("%02x", b));
+ }
+ return hash = hashBuilder.toString();
+ } catch (NoSuchAlgorithmException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "SimpleArtifactMetadata(" + entries.toString() + ", " + getHash() + ")";
+ }
+
+ private static class Entry {
+
+ private final String key, value;
+
+ private Entry(String key, String value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return '[' + key + ',' + value + ']';
+ }
+
+ }
+
+}
diff --git a/src/shared/java/net/minecraftforge/artifactural/base/artifact/StreamableArtifact.java b/src/shared/java/net/minecraftforge/artifactural/base/artifact/StreamableArtifact.java
new file mode 100644
index 0000000..0428572
--- /dev/null
+++ b/src/shared/java/net/minecraftforge/artifactural/base/artifact/StreamableArtifact.java
@@ -0,0 +1,111 @@
+/*
+ * Artifactural
+ * Copyright (c) 2018-2021.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package net.minecraftforge.artifactural.base.artifact;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+import net.minecraftforge.artifactural.api.artifact.Artifact;
+import net.minecraftforge.artifactural.api.artifact.ArtifactIdentifier;
+import net.minecraftforge.artifactural.api.artifact.ArtifactMetadata;
+import net.minecraftforge.artifactural.api.artifact.ArtifactType;
+import net.minecraftforge.artifactural.api.artifact.MissingArtifactException;
+import net.minecraftforge.artifactural.api.artifact.Streamable;
+
+public class StreamableArtifact extends ArtifactBase {
+
+ public static Artifact ofFile(ArtifactIdentifier identifier, ArtifactType type, File file) {
+ return new StreamableFileArtifact(identifier, type, file);
+ }
+
+ public static Artifact ofURL(ArtifactIdentifier identifier, ArtifactType type, URL url) {
+ return new StreamableArtifact(identifier, type, url::openStream);
+ }
+
+ public static Artifact ofBytes(ArtifactIdentifier identifier, ArtifactType type, byte[] bytes) {
+ return new StreamableArtifact(identifier, type, () -> new ByteArrayInputStream(bytes));
+ }
+
+ public static Artifact ofStreamable(ArtifactIdentifier identifier, ArtifactType type, Streamable streamable) {
+ return new StreamableArtifact(identifier, type, streamable);
+ }
+
+ private final Streamable streamable;
+
+ private StreamableArtifact(ArtifactIdentifier identifier, ArtifactType type, Streamable streamable) {
+ this(identifier, type, new SimpleArtifactMetadata(), streamable);
+ }
+
+ private StreamableArtifact(ArtifactIdentifier identifier, ArtifactType type, ArtifactMetadata metadata, Streamable streamable) {
+ super(identifier, type, metadata);
+ this.streamable = streamable;
+ }
+
+ @Override
+ public Artifact withMetadata(ArtifactMetadata metadata) {
+ return new StreamableArtifact(getIdentifier(), getType(), metadata, streamable);
+ }
+
+ @Override
+ public boolean isPresent() {
+ try (InputStream is = openStream()) {
+ is.close();
+ return true;
+ } catch (IOException ex) {
+ return false;
+ }
+ }
+
+ @Override
+ public InputStream openStream() throws IOException {
+ return streamable.openStream();
+ }
+
+ private static class StreamableFileArtifact extends StreamableArtifact implements Artifact.Cached {
+
+ private final File file;
+
+ private StreamableFileArtifact(ArtifactIdentifier identifier, ArtifactType type, File file) {
+ super(identifier, type, () -> new FileInputStream(file));
+ this.file = file;
+ }
+
+ @Override
+ public File asFile() throws MissingArtifactException {
+ return file;
+ }
+
+ @Override
+ public File getFileLocation() throws MissingArtifactException {
+ return file;
+ }
+
+ @Override
+ public String toString() {
+ return "StreamableFileArtifact(" + file + ")";
+ }
+
+ }
+
+}
diff --git a/src/shared/java/net/minecraftforge/artifactural/base/cache/ArtifactCacheBase.java b/src/shared/java/net/minecraftforge/artifactural/base/cache/ArtifactCacheBase.java
new file mode 100644
index 0000000..3268871
--- /dev/null
+++ b/src/shared/java/net/minecraftforge/artifactural/base/cache/ArtifactCacheBase.java
@@ -0,0 +1,129 @@
+/*
+ * Artifactural
+ * Copyright (c) 2018-2021.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package net.minecraftforge.artifactural.base.cache;
+
+import net.minecraftforge.artifactural.api.artifact.Artifact;
+import net.minecraftforge.artifactural.api.artifact.ArtifactIdentifier;
+import net.minecraftforge.artifactural.api.artifact.ArtifactMetadata;
+import net.minecraftforge.artifactural.api.artifact.ArtifactType;
+import net.minecraftforge.artifactural.api.artifact.MissingArtifactException;
+import net.minecraftforge.artifactural.api.cache.ArtifactCache;
+import net.minecraftforge.artifactural.api.transform.ArtifactTransformer;
+import net.minecraftforge.artifactural.base.artifact.StreamableArtifact;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public abstract class ArtifactCacheBase implements ArtifactCache {
+
+ Artifact.Cached doStore(File path, Artifact artifact) {
+ return wrap(
+ StreamableArtifact.ofStreamable(
+ artifact.getIdentifier(),
+ artifact.getType(),
+ () -> stream(path, artifact)
+ ).withMetadata(artifact.getMetadata()),
+ path
+ );
+ }
+
+ private InputStream stream(File path, Artifact artifact) throws IOException {
+ if (!path.exists()) {
+ path.getParentFile().mkdirs();
+ path.createNewFile();
+ FileOutputStream fos = new FileOutputStream(path);
+ InputStream is = artifact.openStream();
+ int read;
+ byte[] bytes = new byte[256];
+ while ((read = is.read(bytes)) > 0) {
+ fos.write(bytes, 0, read);
+ }
+ fos.close();
+ is.close();
+ }
+ return new FileInputStream(path);
+ }
+
+ public static Artifact.Cached wrap(Artifact artifact, File file) {
+ return new Artifact.Cached() {
+
+ @Override
+ public ArtifactIdentifier getIdentifier() {
+ return artifact.getIdentifier();
+ }
+
+ @Override
+ public ArtifactMetadata getMetadata() {
+ return artifact.getMetadata();
+ }
+
+ @Override
+ public ArtifactType getType() {
+ return artifact.getType();
+ }
+
+ @Override
+ public Artifact withMetadata(ArtifactMetadata metadata) {
+ return artifact.withMetadata(metadata);
+ }
+
+ @Override
+ public Artifact apply(ArtifactTransformer transformer) {
+ return artifact.apply(transformer);
+ }
+
+ @Override
+ public Artifact.Cached cache(ArtifactCache cache) {
+ return artifact.cache(cache);
+ }
+
+ @Override
+ public boolean isPresent() {
+ return artifact.isPresent();
+ }
+
+ @Override
+ public InputStream openStream() throws IOException, MissingArtifactException {
+ return artifact.openStream();
+ }
+
+ @Override
+ public File asFile() throws IOException, MissingArtifactException {
+ if(!file.exists()) {
+ artifact.openStream().close();
+ }
+ return file;
+ }
+
+ @Override
+ public File getFileLocation() throws MissingArtifactException {
+ return file;
+ }
+ @Override
+ public String toString() {
+ return "wrapped(" + artifact + ", " + file + ")";
+ }
+ };
+ }
+
+}
diff --git a/src/shared/java/net/minecraftforge/artifactural/base/cache/LocatedArtifactCache.java b/src/shared/java/net/minecraftforge/artifactural/base/cache/LocatedArtifactCache.java
new file mode 100644
index 0000000..5627b50
--- /dev/null
+++ b/src/shared/java/net/minecraftforge/artifactural/base/cache/LocatedArtifactCache.java
@@ -0,0 +1,69 @@
+/*
+ * Artifactural
+ * Copyright (c) 2018-2021.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package net.minecraftforge.artifactural.base.cache;
+
+import net.minecraftforge.artifactural.api.artifact.Artifact;
+import net.minecraftforge.artifactural.api.artifact.ArtifactIdentifier;
+import net.minecraftforge.artifactural.base.util.PatternReplace;
+
+import java.io.File;
+import java.util.AbstractMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class LocatedArtifactCache extends ArtifactCacheBase {
+ private static final String PATTERN = "[group]/[name](/[meta_hash])/[version]/[name]-[version](-[classifier])(-[specifier]).[extension]";
+ private final File path;
+
+ public LocatedArtifactCache(File path) {
+ this.path = path;
+ }
+
+ @Override
+ public Artifact.Cached store(Artifact artifact) {
+ return doStore(getPath(artifact), artifact);
+ }
+
+ public File getPath(Artifact artifact) {
+ ArtifactIdentifier identifier = artifact.getIdentifier();
+ Map<String, String> names = Stream.of(
+ entry("group", identifier.getGroup()),
+ entry("name", identifier.getName()),
+ entry("version", identifier.getVersion()),
+ entry("classifier", identifier.getClassifier()),
+ entry("extension", identifier.getExtension()),
+ //entry("specifier", specifier), /?
+ entry("meta_hash", artifact.getMetadata().getHash())
+ ).collect(Collectors.toMap(Entry::getKey, Entry::getValue));
+ return new File(path, PatternReplace.replace(PATTERN, names));
+ }
+
+ private static <K,V> Entry<K,V> entry(K key, V value) {
+ return new AbstractMap.SimpleEntry<>(key, value);
+ }
+
+ @Override
+ public String toString() {
+ return "LocatedArtifactCache(" + path + ")";
+ }
+
+}
diff --git a/src/shared/java/net/minecraftforge/artifactural/base/repository/ArtifactProviderBuilder.java b/src/shared/java/net/minecraftforge/artifactural/base/repository/ArtifactProviderBuilder.java
new file mode 100644
index 0000000..282f9b4
--- /dev/null
+++ b/src/shared/java/net/minecraftforge/artifactural/base/repository/ArtifactProviderBuilder.java
@@ -0,0 +1,100 @@
+/*
+ * Artifactural
+ * Copyright (c) 2018-2021.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package net.minecraftforge.artifactural.base.repository;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+import net.minecraftforge.artifactural.api.artifact.Artifact;
+import net.minecraftforge.artifactural.api.repository.ArtifactProvider;
+
+public class ArtifactProviderBuilder<S, I> implements ArtifactProvider.Builder<S, I> {
+
+ public static <I> ArtifactProviderBuilder<I, I> begin(Class<I> type) {
+ return new ArtifactProviderBuilder<>(Function.identity());
+ }
+
+ private final Function<S, I> mapper;
+ private final Set<Predicate<I>> filters = new HashSet<>();
+
+ private ArtifactProviderBuilder(Function<S, I> mapper) {
+ this.mapper = mapper;
+ }
+
+ @Override
+ public ArtifactProvider.Builder<S, I> filter(Predicate<I> filter) {
+ filters.add(filter);
+ return this;
+ }
+
+ @Override
+ public <D> ArtifactProvider.Builder<S, D> mapInfo(Function<I, D> mapper) {
+ if (filters.isEmpty()) {
+ return new ArtifactProviderBuilder<>(this.mapper.andThen(mapper));
+ }
+ return new ArtifactProviderBuilder<>((S info) -> {
+ I localInfo = this.mapper.apply(info);
+ if (localInfo == null) return null;
+ for (Predicate<I> filter : filters) {
+ if (!filter.test(localInfo)) {
+ return null;
+ }
+ }
+ return mapper.apply(localInfo);
+ });
+ }
+
+ @Override
+ public ArtifactProvider.Builder.Complete<S, I> provide(ArtifactProvider<I> provider) {
+ return new Complete<>(mapper).provide(provider);
+ }
+
+ private static class Complete<S, I> implements ArtifactProvider.Builder.Complete<S, I> {
+
+ private final Set<ArtifactProvider<I>> providers = new HashSet<>();
+ private final Function<S, I> mapper;
+
+ private Complete(Function<S, I> mapper) {
+ this.mapper = mapper;
+ }
+
+ @Override
+ public Builder.Complete<S, I> provide(ArtifactProvider<I> provider) {
+ providers.add(provider);
+ return this;
+ }
+
+ @Override
+ public Artifact getArtifact(S info) {
+ I localInfo = mapper.apply(info);
+ if (localInfo == null) return Artifact.none();
+
+ for (ArtifactProvider<I> provider : providers) {
+ Artifact artifact = provider.getArtifact(localInfo);
+ if (artifact.isPresent()) return artifact;
+ }
+ return Artifact.none();
+ }
+
+ }
+
+}
diff --git a/src/shared/java/net/minecraftforge/artifactural/base/repository/SimpleRepository.java b/src/shared/java/net/minecraftforge/artifactural/base/repository/SimpleRepository.java
new file mode 100644
index 0000000..3076b08
--- /dev/null
+++ b/src/shared/java/net/minecraftforge/artifactural/base/repository/SimpleRepository.java
@@ -0,0 +1,44 @@
+/*
+ * Artifactural
+ * Copyright (c) 2018-2021.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package net.minecraftforge.artifactural.base.repository;
+
+import net.minecraftforge.artifactural.api.artifact.Artifact;
+import net.minecraftforge.artifactural.api.artifact.ArtifactIdentifier;
+import net.minecraftforge.artifactural.api.repository.ArtifactProvider;
+import net.minecraftforge.artifactural.api.repository.Repository;
+
+public class SimpleRepository implements Repository {
+
+ public static Repository of(ArtifactProvider<ArtifactIdentifier> provider) {
+ return new SimpleRepository(provider);
+ }
+
+ private final ArtifactProvider<ArtifactIdentifier> provider;
+
+ private SimpleRepository(ArtifactProvider<ArtifactIdentifier> provider) {
+ this.provider = provider;
+ }
+
+ @Override
+ public Artifact getArtifact(ArtifactIdentifier identifier) {
+ return provider.getArtifact(identifier);
+ }
+
+}
diff --git a/src/shared/java/net/minecraftforge/artifactural/base/util/HashFunction.java b/src/shared/java/net/minecraftforge/artifactural/base/util/HashFunction.java
new file mode 100644
index 0000000..bcf00f9
--- /dev/null
+++ b/src/shared/java/net/minecraftforge/artifactural/base/util/HashFunction.java
@@ -0,0 +1,135 @@
+/*
+ * Artifactural
+ * Copyright (c) 2018-2021.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package net.minecraftforge.artifactural.base.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Locale;
+
+//These are all standard hashing functions the JRE is REQUIRED to have, so add a nice factory that doesnt require catching annoying exceptions;
+public enum HashFunction {
+ MD5("md5", 32),
+ SHA1("SHA-1", 40),
+ SHA256("SHA-256", 64);
+
+ private String algo;
+ private String pad;
+
+ private HashFunction(String algo, int length) {
+ this.algo = algo;
+ // must specify locale to get correct number formatting
+ this.pad = String.format(Locale.ENGLISH, "%0" + length + "d", 0);
+ }
+
+ public String getExtension() {
+ return this.name().toLowerCase(Locale.ENGLISH);
+ }
+
+ public Instance create() {
+ return new Instance();
+ }
+
+ public MessageDigest get() {
+ try {
+ return MessageDigest.getInstance(algo);
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e); //Never happens
+ }
+ }
+
+ public String hash(File file) throws IOException {
+ try (FileInputStream fin = new FileInputStream(file)) {
+ return hash(fin);
+ }
+ }
+
+ public String hash(Iterable<File> files) throws IOException {
+ MessageDigest hash = get();
+ byte[] buf = new byte[1024];
+
+ for (File file : files) {
+ if (!file.exists())
+ continue;
+
+ try (FileInputStream fin = new FileInputStream(file)) {
+ int count = -1;
+ while ((count = fin.read(buf)) != -1)
+ hash.update(buf, 0, count);
+ }
+ }
+ return pad(new BigInteger(1, hash.digest()).toString(16));
+ }
+
+ public String hash(String data) {
+ return hash(data.getBytes(StandardCharsets.UTF_8));
+ }
+
+ public String hash(InputStream stream) throws IOException {
+ MessageDigest hash = get();
+ byte[] buf = new byte[1024];
+ int count = -1;
+ while ((count = stream.read(buf)) != -1)
+ hash.update(buf, 0, count);
+ return pad(new BigInteger(1, hash.digest()).toString(16));
+ }
+
+ public String hash(byte[] data) {
+ return pad(new BigInteger(1, get().digest(data)).toString(16));
+ }
+
+ public String pad(String hash) {
+ return (pad + hash).substring(hash.length());
+ }
+
+ public class Instance {
+ private MessageDigest digest = HashFunction.this.get();
+ public void update(byte input) {
+ digest.update(input);
+ }
+ public void update(byte[] input) {
+ digest.update(input);
+ }
+ public void update(byte[] input, int offset, int length) {
+ digest.update(input, offset, length);
+ }
+ public void update(ByteBuffer input) {
+ digest.update(input);
+ }
+ public void update(String input) {
+ update(input.getBytes(StandardCharsets.UTF_8));
+ }
+ public void update(int input) {
+ update(new byte[] { (byte)((input & 0xFF000000) >> 24), (byte)((input & 0xFF000000) >> 16), (byte)((input & 0xFF000000) >> 8), (byte)((input & 0xFF000000))});
+ }
+ public byte[] digest() {
+ return digest.digest();
+ }
+ public String finish() {
+ return pad(new BigInteger(1, digest()).toString(16));
+ }
+ }
+}
diff --git a/src/shared/java/net/minecraftforge/artifactural/base/util/PatternReplace.java b/src/shared/java/net/minecraftforge/artifactural/base/util/PatternReplace.java
new file mode 100644
index 0000000..6d3cf86
--- /dev/null
+++ b/src/shared/java/net/minecraftforge/artifactural/base/util/PatternReplace.java
@@ -0,0 +1,159 @@
+/*
+ * Artifactural
+ * Copyright (c) 2018-2021.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation version 2.1
+ * of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+package net.minecraftforge.artifactural.base.util;
+
+import java.util.Map;
+
+public class PatternReplace {
+ /*
+ * Replaces a patterened string, with support for optional groups.
+ * Example:
+ * Values:
+ * group: net/minecraftforge
+ * name: forge
+ * version: 1.0
+ * ext: jar
+ *
+ * Example: [group]/[name]/[version]/[name]-[version](-[classifier]).[ext]
+ * {classifier: test} net/minecraftforge/forge/1.0/forge-1.0-test.jar
+ * {classifier: null} net/minecraftforge/forge/1.0/forge-1.0.jar
+ *
+ * Nested Optionals are supported:
+ * Example: [group]/[name]/[version]/[name]-[version](-[classifier](-[suffix])).[ext]
+ * {classifier: test, suffix: foo} net/minecraftforge/forge/1.0/forge-1.0-test-foo.jar
+ * {classifier: test, suffix: foo} net/minecraftforge/forge/1.0/forge-1.0-test.jar
+ * {classifier: null, suffix: foo} net/minecraftforge/forge/1.0/forge-1.0.jar
+ *
+ * Compound optionals are supported:
+ * Example: [group]/[name]/[version]/[name]-[version](-[classifier]-[suffix]).[ext]
+ * {classifier: test, suffix: foo} net/minecraftforge/forge/1.0/forge-1.0-test-foo.jar
+ * {classifier: test, suffix: null} net/minecraftforge/forge/1.0/forge-1.0.jar
+ * {classifier: null, suffix: foo} net/minecraftforge/forge/1.0/forge-1.0.jar
+ *
+ *
+ * TODO: Support nested names?
+ * Example: [group]/[name]/[version]/[name]-[version](-[classifier[suffix]]).[ext]
+ * {classifierFoo: test, suffix: Foo} net/minecraftforge/forge/1.0/forge-1.0-test.jar
+ * {classifierFoo: null, suffix: Foo} net/minecraftforge/forge/1.0/forge-1.0.jar
+ */
+ public static String replace(String pattern, Map<String, String> values) {
+ if (pattern == null) return null;
+ if (pattern.isEmpty()) return "";
+
+ Optional optional = null;
+ StringBuffer name = null;
+ StringBuffer ret = new StringBuffer();
+
+ char[] chars = pattern.toCharArray();
+ for (int x = 0; x < chars.length; x++) {
+ char c = chars[x];
+ if (c == '\\') {
+ if (x == chars.length -1)
+ throw new IllegalArgumentException("Escape character can not be end of pattern: " + pattern);
+ x++;
+ ret.append(chars[x]);
+ continue;
+ }
+ switch (c) {
+ case '[':
+ if (name != null)
+ throw new IllegalArgumentException("Nested names are not supported @ " + x + " : " + pattern);
+ name = new StringBuffer();
+ break;
+ case ']':
+ if (name == null)
+ throw new IllegalArgumentException("Name closing found without opening @ " + x + " : " + pattern);
+ String key = name.toString();
+ if (key.isEmpty())
+ throw new IllegalArgumentException("Name can not be empty @ " + x + ": " + pattern);
+ if (optional != null)
+ optional.setKey(key, values);
+ else
+ ret.append(values.get(key)); // appends 'null' if missing, if you want "" then use ([name])
+ // Should we have this default to not replacing at all if value is not set to allow chaining?
+ // Meaning: '[key]' == '[key]' if 'key' is not set.
+ // Current: '[key]' == 'null'
+ name = null;
+ break;
+ case '(':
+ optional = new Optional(optional);
+ break;
+ case ')':
+ if (optional == null)
+ throw new IllegalArgumentException("Optional closing found without opening @ " + x + ": " + pattern);
+ optional = optional.finish(x, pattern, ret);
+ break;
+ default:
+ if (name != null)
+ name.append(c);
+ else if (optional != null)
+ optional.append(c);
+ else
+ ret.append(c);
+ }
+ }
+ if (optional != null)
+ throw new IllegalArgumentException("Missing closing of optional value: " + pattern);
+ if (name != null)
+ throw new IllegalArgumentException("Missing closing of name entry: " + pattern);
+ return ret.toString();
+ }
+
+ public static String quote(String value) {
+ return value.replaceAll("\\", "\\\\")
+ .replaceAll("(", "\\(")
+ .replaceAll(")", "\\)")
+ .replaceAll("[", "\\[")
+ .replaceAll("]", "\\]");
+ }
+
+ private static class Optional {
+ private final Optional parent;
+ private final StringBuffer buf = new StringBuffer();
+ private boolean hadAll = true;
+ private boolean hadValue = false;
+
+ private Optional(Optional parent) {
+ this.parent = parent;
+ }
+
+ public void append(char c) {
+ buf.append(c);
+ }
+
+ private void setKey(String key, Map<String, String> values) {
+ hadValue = true;
+ String value = values.get(key);
+ if (value != null && !value.isEmpty()) {
+ hadAll &= true;
+ buf.append(value);
+ } else
+ hadAll = false;
+ }
+
+ public Optional finish(int position, String pattern, StringBuffer ret) {
+ if (!hadValue)
+ throw new IllegalArgumentException("Invalid optional, missing inner name @ " + position +": " + pattern);
+ if (hadAll)
+ (parent == null ? ret : parent.buf).append(buf);
+ return parent;
+ }
+ }
+}