From 4089917696fffbd4b818fb90958d20f0714f93fb Mon Sep 17 00:00:00 2001 From: LexManos Date: Sun, 17 Jan 2021 17:35:01 -0800 Subject: Move to net.minecraftforge package, and update license headers. --- LICENSE-header.txt | 2 +- build.gradle | 2 +- .../artifactural/api/artifact/Artifact.java | 65 ---- .../api/artifact/ArtifactIdentifier.java | 80 ----- .../api/artifact/ArtifactMetadata.java | 28 -- .../artifactural/api/artifact/ArtifactType.java | 24 -- .../artifactural/api/artifact/Internal.java | 137 --------- .../api/artifact/MissingArtifactException.java | 29 -- .../artifactural/api/artifact/Streamable.java | 30 -- .../artifactural/api/cache/ArtifactCache.java | 28 -- .../api/repository/ArtifactProvider.java | 47 --- .../artifactural/api/repository/Repository.java | 44 --- .../api/transform/ArtifactTransformer.java | 68 ----- .../artifactural/api/artifact/Artifact.java | 65 ++++ .../api/artifact/ArtifactIdentifier.java | 80 +++++ .../api/artifact/ArtifactMetadata.java | 28 ++ .../artifactural/api/artifact/ArtifactType.java | 24 ++ .../artifactural/api/artifact/Internal.java | 137 +++++++++ .../api/artifact/MissingArtifactException.java | 29 ++ .../artifactural/api/artifact/Streamable.java | 30 ++ .../artifactural/api/cache/ArtifactCache.java | 28 ++ .../api/repository/ArtifactProvider.java | 47 +++ .../artifactural/api/repository/Repository.java | 44 +++ .../api/transform/ArtifactTransformer.java | 68 +++++ .../artifactural/gradle/DependencyResolver.java | 124 -------- .../artifactural/gradle/GradleArtifact.java | 45 --- .../gradle/GradleRepositoryAdapter.java | 333 --------------------- .../artifactural/gradle/ModifierAccess.java | 55 ---- .../artifactural/gradle/ReflectionUtils.java | 112 ------- .../artifactural/gradle/DependencyResolver.java | 124 ++++++++ .../artifactural/gradle/GradleArtifact.java | 45 +++ .../gradle/GradleRepositoryAdapter.java | 333 +++++++++++++++++++++ .../artifactural/gradle/ModifierAccess.java | 55 ++++ .../artifactural/gradle/ReflectionUtils.java | 112 +++++++ .../artifactural/gradle/ModifierAccess.java | 57 ---- .../artifactural/gradle/ModifierAccess.java | 57 ++++ .../artifactural/base/artifact/ArtifactBase.java | 72 ----- .../base/artifact/SimpleArtifactIdentifier.java | 70 ----- .../base/artifact/SimpleArtifactMetadata.java | 86 ------ .../base/artifact/StreamableArtifact.java | 111 ------- .../artifactural/base/cache/ArtifactCacheBase.java | 129 -------- .../base/cache/LocatedArtifactCache.java | 69 ----- .../base/repository/ArtifactProviderBuilder.java | 100 ------- .../base/repository/SimpleRepository.java | 44 --- .../artifactural/base/util/HashFunction.java | 135 --------- .../artifactural/base/util/PatternReplace.java | 159 ---------- .../artifactural/base/artifact/ArtifactBase.java | 72 +++++ .../base/artifact/SimpleArtifactIdentifier.java | 70 +++++ .../base/artifact/SimpleArtifactMetadata.java | 86 ++++++ .../base/artifact/StreamableArtifact.java | 111 +++++++ .../artifactural/base/cache/ArtifactCacheBase.java | 129 ++++++++ .../base/cache/LocatedArtifactCache.java | 69 +++++ .../base/repository/ArtifactProviderBuilder.java | 100 +++++++ .../base/repository/SimpleRepository.java | 44 +++ .../artifactural/base/util/HashFunction.java | 135 +++++++++ .../artifactural/base/util/PatternReplace.java | 159 ++++++++++ 56 files changed, 2283 insertions(+), 2283 deletions(-) delete mode 100644 src/api/java/com/amadornes/artifactural/api/artifact/Artifact.java delete mode 100644 src/api/java/com/amadornes/artifactural/api/artifact/ArtifactIdentifier.java delete mode 100644 src/api/java/com/amadornes/artifactural/api/artifact/ArtifactMetadata.java delete mode 100644 src/api/java/com/amadornes/artifactural/api/artifact/ArtifactType.java delete mode 100644 src/api/java/com/amadornes/artifactural/api/artifact/Internal.java delete mode 100644 src/api/java/com/amadornes/artifactural/api/artifact/MissingArtifactException.java delete mode 100644 src/api/java/com/amadornes/artifactural/api/artifact/Streamable.java delete mode 100644 src/api/java/com/amadornes/artifactural/api/cache/ArtifactCache.java delete mode 100644 src/api/java/com/amadornes/artifactural/api/repository/ArtifactProvider.java delete mode 100644 src/api/java/com/amadornes/artifactural/api/repository/Repository.java delete mode 100644 src/api/java/com/amadornes/artifactural/api/transform/ArtifactTransformer.java create mode 100644 src/api/java/net/minecraftforge/artifactural/api/artifact/Artifact.java create mode 100644 src/api/java/net/minecraftforge/artifactural/api/artifact/ArtifactIdentifier.java create mode 100644 src/api/java/net/minecraftforge/artifactural/api/artifact/ArtifactMetadata.java create mode 100644 src/api/java/net/minecraftforge/artifactural/api/artifact/ArtifactType.java create mode 100644 src/api/java/net/minecraftforge/artifactural/api/artifact/Internal.java create mode 100644 src/api/java/net/minecraftforge/artifactural/api/artifact/MissingArtifactException.java create mode 100644 src/api/java/net/minecraftforge/artifactural/api/artifact/Streamable.java create mode 100644 src/api/java/net/minecraftforge/artifactural/api/cache/ArtifactCache.java create mode 100644 src/api/java/net/minecraftforge/artifactural/api/repository/ArtifactProvider.java create mode 100644 src/api/java/net/minecraftforge/artifactural/api/repository/Repository.java create mode 100644 src/api/java/net/minecraftforge/artifactural/api/transform/ArtifactTransformer.java delete mode 100644 src/gradlecomp/java/com/amadornes/artifactural/gradle/DependencyResolver.java delete mode 100644 src/gradlecomp/java/com/amadornes/artifactural/gradle/GradleArtifact.java delete mode 100644 src/gradlecomp/java/com/amadornes/artifactural/gradle/GradleRepositoryAdapter.java delete mode 100644 src/gradlecomp/java/com/amadornes/artifactural/gradle/ModifierAccess.java delete mode 100644 src/gradlecomp/java/com/amadornes/artifactural/gradle/ReflectionUtils.java create mode 100644 src/gradlecomp/java/net/minecraftforge/artifactural/gradle/DependencyResolver.java create mode 100644 src/gradlecomp/java/net/minecraftforge/artifactural/gradle/GradleArtifact.java create mode 100644 src/gradlecomp/java/net/minecraftforge/artifactural/gradle/GradleRepositoryAdapter.java create mode 100644 src/gradlecomp/java/net/minecraftforge/artifactural/gradle/ModifierAccess.java create mode 100644 src/gradlecomp/java/net/minecraftforge/artifactural/gradle/ReflectionUtils.java delete mode 100644 src/java9/com/amadornes/artifactural/gradle/ModifierAccess.java create mode 100644 src/java9/net/minecraftforge/artifactural/gradle/ModifierAccess.java delete mode 100644 src/shared/java/com/amadornes/artifactural/base/artifact/ArtifactBase.java delete mode 100644 src/shared/java/com/amadornes/artifactural/base/artifact/SimpleArtifactIdentifier.java delete mode 100644 src/shared/java/com/amadornes/artifactural/base/artifact/SimpleArtifactMetadata.java delete mode 100644 src/shared/java/com/amadornes/artifactural/base/artifact/StreamableArtifact.java delete mode 100644 src/shared/java/com/amadornes/artifactural/base/cache/ArtifactCacheBase.java delete mode 100644 src/shared/java/com/amadornes/artifactural/base/cache/LocatedArtifactCache.java delete mode 100644 src/shared/java/com/amadornes/artifactural/base/repository/ArtifactProviderBuilder.java delete mode 100644 src/shared/java/com/amadornes/artifactural/base/repository/SimpleRepository.java delete mode 100644 src/shared/java/com/amadornes/artifactural/base/util/HashFunction.java delete mode 100644 src/shared/java/com/amadornes/artifactural/base/util/PatternReplace.java create mode 100644 src/shared/java/net/minecraftforge/artifactural/base/artifact/ArtifactBase.java create mode 100644 src/shared/java/net/minecraftforge/artifactural/base/artifact/SimpleArtifactIdentifier.java create mode 100644 src/shared/java/net/minecraftforge/artifactural/base/artifact/SimpleArtifactMetadata.java create mode 100644 src/shared/java/net/minecraftforge/artifactural/base/artifact/StreamableArtifact.java create mode 100644 src/shared/java/net/minecraftforge/artifactural/base/cache/ArtifactCacheBase.java create mode 100644 src/shared/java/net/minecraftforge/artifactural/base/cache/LocatedArtifactCache.java create mode 100644 src/shared/java/net/minecraftforge/artifactural/base/repository/ArtifactProviderBuilder.java create mode 100644 src/shared/java/net/minecraftforge/artifactural/base/repository/SimpleRepository.java create mode 100644 src/shared/java/net/minecraftforge/artifactural/base/util/HashFunction.java create mode 100644 src/shared/java/net/minecraftforge/artifactural/base/util/PatternReplace.java diff --git a/LICENSE-header.txt b/LICENSE-header.txt index a6bee08..74e543c 100644 --- a/LICENSE-header.txt +++ b/LICENSE-header.txt @@ -1,5 +1,5 @@ Artifactural -Copyright (c) 2018. +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 diff --git a/build.gradle b/build.gradle index 00240bd..10b9def 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ group = 'net.minecraftforge' version = gitVersion() def gitVersion() { - def raw = grgit.describe(longDescr: true) + def raw = grgit.describe(longDescr: true, tags: true) def desc = (raw == null ? 'unknown-unknown-unknown' : raw).split('-') as List def hash = desc.remove(desc.size() - 1) def offset = desc.remove(desc.size() - 1) diff --git a/src/api/java/com/amadornes/artifactural/api/artifact/Artifact.java b/src/api/java/com/amadornes/artifactural/api/artifact/Artifact.java deleted file mode 100644 index 1da3c90..0000000 --- a/src/api/java/com/amadornes/artifactural/api/artifact/Artifact.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Artifactural - * Copyright (c) 2018. - * - * 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 com.amadornes.artifactural.api.artifact; - -import com.amadornes.artifactural.api.cache.ArtifactCache; -import com.amadornes.artifactural.api.transform.ArtifactTransformer; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; - -public interface Artifact { - - static Artifact none() { - return Internal.NO_ARTIFACT; - } - - ArtifactIdentifier getIdentifier(); - - ArtifactMetadata getMetadata(); - - ArtifactType getType(); - - Artifact withMetadata(ArtifactMetadata metadata); - - Artifact apply(ArtifactTransformer transformer); - - Artifact.Cached cache(ArtifactCache cache); - - default Artifact.Cached optionallyCache(ArtifactCache cache) { - return this instanceof Artifact.Cached ? (Artifact.Cached) this : cache(cache); - } - - boolean isPresent(); - - InputStream openStream() throws IOException, MissingArtifactException; - - interface Cached extends Artifact { - - // Gets the file location, AND writes the file to disc if it hasn't already. - File asFile() throws IOException, MissingArtifactException; - - // Gets the file location, but doesn't guarantee that it exists. As the wrapped Artifact may not of been written. What's the point of this? - File getFileLocation() throws IOException, MissingArtifactException; - - } - -} diff --git a/src/api/java/com/amadornes/artifactural/api/artifact/ArtifactIdentifier.java b/src/api/java/com/amadornes/artifactural/api/artifact/ArtifactIdentifier.java deleted file mode 100644 index e663c11..0000000 --- a/src/api/java/com/amadornes/artifactural/api/artifact/ArtifactIdentifier.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Artifactural - * Copyright (c) 2018. - * - * 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 com.amadornes.artifactural.api.artifact; - -import java.util.function.Predicate; - -public interface ArtifactIdentifier { - - static ArtifactIdentifier none() { - return Internal.NO_IDENTIFIER; - } - - String getGroup(); - - String getName(); - - String getVersion(); - - String getClassifier(); - - String getExtension(); - - static Predicate groupMatches(String group) { - return identifier -> identifier.getGroup().matches(group); - } - - static Predicate nameMatches(String name) { - return identifier -> identifier.getName().matches(name); - } - - static Predicate versionMatches(String version) { - return identifier -> identifier.getVersion().matches(version); - } - - static Predicate classifierMatches(String classifier) { - return identifier -> identifier.getClassifier().matches(classifier); - } - - static Predicate extensionMatches(String extension) { - return identifier -> identifier.getExtension().matches(extension); - } - - static Predicate groupEquals(String group) { - return identifier -> identifier.getGroup().equals(group); - } - - static Predicate nameEquals(String name) { - return identifier -> identifier.getName().equals(name); - } - - static Predicate versionEquals(String version) { - return identifier -> identifier.getVersion().equals(version); - } - - static Predicate classifierEquals(String classifier) { - return identifier -> identifier.getClassifier().equals(classifier); - } - - static Predicate extensionEquals(String extension) { - return identifier -> identifier.getExtension().equals(extension); - } - -} diff --git a/src/api/java/com/amadornes/artifactural/api/artifact/ArtifactMetadata.java b/src/api/java/com/amadornes/artifactural/api/artifact/ArtifactMetadata.java deleted file mode 100644 index af5e08a..0000000 --- a/src/api/java/com/amadornes/artifactural/api/artifact/ArtifactMetadata.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Artifactural - * Copyright (c) 2018. - * - * 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 com.amadornes.artifactural.api.artifact; - -public interface ArtifactMetadata { - - ArtifactMetadata with(String key, String value); - - String getHash(); - -} diff --git a/src/api/java/com/amadornes/artifactural/api/artifact/ArtifactType.java b/src/api/java/com/amadornes/artifactural/api/artifact/ArtifactType.java deleted file mode 100644 index 40e5aa6..0000000 --- a/src/api/java/com/amadornes/artifactural/api/artifact/ArtifactType.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Artifactural - * Copyright (c) 2018. - * - * 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 com.amadornes.artifactural.api.artifact; - -public enum ArtifactType { - BINARY, SOURCE, OTHER -} diff --git a/src/api/java/com/amadornes/artifactural/api/artifact/Internal.java b/src/api/java/com/amadornes/artifactural/api/artifact/Internal.java deleted file mode 100644 index 39bc983..0000000 --- a/src/api/java/com/amadornes/artifactural/api/artifact/Internal.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Artifactural - * Copyright (c) 2018. - * - * 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 com.amadornes.artifactural.api.artifact; - -import com.amadornes.artifactural.api.cache.ArtifactCache; -import com.amadornes.artifactural.api.transform.ArtifactTransformer; - -import java.io.File; -import java.io.InputStream; - -final class Internal { - - static final ArtifactIdentifier NO_IDENTIFIER = new ArtifactIdentifier() { - - @Override - public String getGroup() { - return "missing"; - } - - @Override - public String getName() { - return "missing"; - } - - @Override - public String getVersion() { - return "0.0.0"; - } - - @Override - public String getClassifier() { - return ""; - } - - @Override - public String getExtension() { - return "missing"; - } - - @Override - public String toString() { - return "NO_IDENTIFIER"; - } - - }; - - static final Artifact NO_ARTIFACT = new Artifact.Cached() { - @Override - public String toString() { - return "NO_ARTIFACT"; - } - - @Override - public ArtifactIdentifier getIdentifier() { - return ArtifactIdentifier.none(); - } - - @Override - public ArtifactMetadata getMetadata() { - return new ArtifactMetadata() { - @Override - public ArtifactMetadata with(String key, String value) { - throw new UnsupportedOperationException(); - } - - @Override - public String getHash() { - return "ERROR"; - } - - @Override - public String toString() { - return "NO_METADATA"; - } - }; - } - - @Override - public ArtifactType getType() { - return ArtifactType.OTHER; - } - - @Override - public Artifact withMetadata(ArtifactMetadata metadata) { - return this; - } - - @Override - public Artifact apply(ArtifactTransformer transformer) { - return this; - } - - @Override - public Artifact.Cached cache(ArtifactCache cache) { - return this; - } - - @Override - public boolean isPresent() { - return false; - } - - @Override - public InputStream openStream() throws MissingArtifactException { - throw new MissingArtifactException(getIdentifier()); - } - - @Override - public File asFile() throws MissingArtifactException { - throw new MissingArtifactException(getIdentifier()); - } - - @Override - public File getFileLocation() throws MissingArtifactException { - throw new MissingArtifactException(getIdentifier()); - } - - }; - -} diff --git a/src/api/java/com/amadornes/artifactural/api/artifact/MissingArtifactException.java b/src/api/java/com/amadornes/artifactural/api/artifact/MissingArtifactException.java deleted file mode 100644 index 3caa7ec..0000000 --- a/src/api/java/com/amadornes/artifactural/api/artifact/MissingArtifactException.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Artifactural - * Copyright (c) 2018. - * - * 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 com.amadornes.artifactural.api.artifact; - -public class MissingArtifactException extends RuntimeException { - private static final long serialVersionUID = 4902516963452435653L; - - public MissingArtifactException(ArtifactIdentifier identifier) { - super("Could not find artifact: " + identifier); - } - -} diff --git a/src/api/java/com/amadornes/artifactural/api/artifact/Streamable.java b/src/api/java/com/amadornes/artifactural/api/artifact/Streamable.java deleted file mode 100644 index b8fcdf6..0000000 --- a/src/api/java/com/amadornes/artifactural/api/artifact/Streamable.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Artifactural - * Copyright (c) 2018. - * - * 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 com.amadornes.artifactural.api.artifact; - -import java.io.IOException; -import java.io.InputStream; - -@FunctionalInterface -public interface Streamable { - - InputStream openStream() throws IOException; - -} diff --git a/src/api/java/com/amadornes/artifactural/api/cache/ArtifactCache.java b/src/api/java/com/amadornes/artifactural/api/cache/ArtifactCache.java deleted file mode 100644 index 00e2197..0000000 --- a/src/api/java/com/amadornes/artifactural/api/cache/ArtifactCache.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Artifactural - * Copyright (c) 2018. - * - * 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 com.amadornes.artifactural.api.cache; - -import com.amadornes.artifactural.api.artifact.Artifact; - -public interface ArtifactCache { - - Artifact.Cached store(Artifact artifact); - -} diff --git a/src/api/java/com/amadornes/artifactural/api/repository/ArtifactProvider.java b/src/api/java/com/amadornes/artifactural/api/repository/ArtifactProvider.java deleted file mode 100644 index d48856f..0000000 --- a/src/api/java/com/amadornes/artifactural/api/repository/ArtifactProvider.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Artifactural - * Copyright (c) 2018. - * - * 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 com.amadornes.artifactural.api.repository; - -import com.amadornes.artifactural.api.artifact.Artifact; - -import java.util.function.Function; -import java.util.function.Predicate; - -public interface ArtifactProvider { - - Artifact getArtifact(I info); - - interface Builder { - - Builder filter(Predicate filter); - - Builder mapInfo(Function mapper); - - Complete provide(ArtifactProvider provider); - - interface Complete extends ArtifactProvider { - - Complete provide(ArtifactProvider provider); - - } - - } - -} diff --git a/src/api/java/com/amadornes/artifactural/api/repository/Repository.java b/src/api/java/com/amadornes/artifactural/api/repository/Repository.java deleted file mode 100644 index 2e1de79..0000000 --- a/src/api/java/com/amadornes/artifactural/api/repository/Repository.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Artifactural - * Copyright (c) 2018. - * - * 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 com.amadornes.artifactural.api.repository; - -import java.io.File; - -import com.amadornes.artifactural.api.artifact.Artifact; -import com.amadornes.artifactural.api.artifact.ArtifactIdentifier; - -public interface Repository { - - Artifact getArtifact(ArtifactIdentifier identifier); - - /** - * Returns a file in maven-metadata.xml format for the specified artifact, - * this is used by gradle to list all known versions, so that it can resolve wildcard - * dependencies such as foo:bar:1.+ - * - * @param group Group - * @param name Artifact name - * @return maven-metadata.xml file listing all versions of the artifact this repo can provide. Or null if you don't want to list any. - */ - default File getMavenMetadata(String group, String name) { - return null; - } - -} diff --git a/src/api/java/com/amadornes/artifactural/api/transform/ArtifactTransformer.java b/src/api/java/com/amadornes/artifactural/api/transform/ArtifactTransformer.java deleted file mode 100644 index b129d5b..0000000 --- a/src/api/java/com/amadornes/artifactural/api/transform/ArtifactTransformer.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Artifactural - * Copyright (c) 2018. - * - * 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 com.amadornes.artifactural.api.transform; - -import com.amadornes.artifactural.api.artifact.Artifact; -import com.amadornes.artifactural.api.artifact.ArtifactMetadata; - -import java.util.function.UnaryOperator; - -public interface ArtifactTransformer { - - static ArtifactTransformer of(UnaryOperator operator) { - return new ArtifactTransformer() { - @Override - public Artifact transform(Artifact artifact) { - return operator.apply(artifact); - } - - @Override - public ArtifactMetadata withInfo(ArtifactMetadata metadata) { - return metadata; - } - }; - } - - default boolean appliesTo(Artifact artifact) { - return true; - } - - Artifact transform(Artifact artifact); - - ArtifactMetadata withInfo(ArtifactMetadata metadata); - - default ArtifactTransformer andThen(ArtifactTransformer other) { - ArtifactTransformer current = this; - return new ArtifactTransformer() { - - @Override - public Artifact transform(Artifact artifact) { - return other.transform(current.transform(artifact)); - } - - @Override - public ArtifactMetadata withInfo(ArtifactMetadata metadata) { - return other.withInfo(current.withInfo(metadata)); - } - - }; - } - -} diff --git a/src/api/java/net/minecraftforge/artifactural/api/artifact/Artifact.java b/src/api/java/net/minecraftforge/artifactural/api/artifact/Artifact.java new file mode 100644 index 0000000..3d444fa --- /dev/null +++ b/src/api/java/net/minecraftforge/artifactural/api/artifact/Artifact.java @@ -0,0 +1,65 @@ +/* + * 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.api.artifact; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +import net.minecraftforge.artifactural.api.cache.ArtifactCache; +import net.minecraftforge.artifactural.api.transform.ArtifactTransformer; + +public interface Artifact { + + static Artifact none() { + return Internal.NO_ARTIFACT; + } + + ArtifactIdentifier getIdentifier(); + + ArtifactMetadata getMetadata(); + + ArtifactType getType(); + + Artifact withMetadata(ArtifactMetadata metadata); + + Artifact apply(ArtifactTransformer transformer); + + Artifact.Cached cache(ArtifactCache cache); + + default Artifact.Cached optionallyCache(ArtifactCache cache) { + return this instanceof Artifact.Cached ? (Artifact.Cached) this : cache(cache); + } + + boolean isPresent(); + + InputStream openStream() throws IOException, MissingArtifactException; + + interface Cached extends Artifact { + + // Gets the file location, AND writes the file to disc if it hasn't already. + File asFile() throws IOException, MissingArtifactException; + + // Gets the file location, but doesn't guarantee that it exists. As the wrapped Artifact may not of been written. What's the point of this? + File getFileLocation() throws IOException, MissingArtifactException; + + } + +} diff --git a/src/api/java/net/minecraftforge/artifactural/api/artifact/ArtifactIdentifier.java b/src/api/java/net/minecraftforge/artifactural/api/artifact/ArtifactIdentifier.java new file mode 100644 index 0000000..6485a59 --- /dev/null +++ b/src/api/java/net/minecraftforge/artifactural/api/artifact/ArtifactIdentifier.java @@ -0,0 +1,80 @@ +/* + * 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.api.artifact; + +import java.util.function.Predicate; + +public interface ArtifactIdentifier { + + static ArtifactIdentifier none() { + return Internal.NO_IDENTIFIER; + } + + String getGroup(); + + String getName(); + + String getVersion(); + + String getClassifier(); + + String getExtension(); + + static Predicate groupMatches(String group) { + return identifier -> identifier.getGroup().matches(group); + } + + static Predicate nameMatches(String name) { + return identifier -> identifier.getName().matches(name); + } + + static Predicate versionMatches(String version) { + return identifier -> identifier.getVersion().matches(version); + } + + static Predicate classifierMatches(String classifier) { + return identifier -> identifier.getClassifier().matches(classifier); + } + + static Predicate extensionMatches(String extension) { + return identifier -> identifier.getExtension().matches(extension); + } + + static Predicate groupEquals(String group) { + return identifier -> identifier.getGroup().equals(group); + } + + static Predicate nameEquals(String name) { + return identifier -> identifier.getName().equals(name); + } + + static Predicate versionEquals(String version) { + return identifier -> identifier.getVersion().equals(version); + } + + static Predicate classifierEquals(String classifier) { + return identifier -> identifier.getClassifier().equals(classifier); + } + + static Predicate extensionEquals(String extension) { + return identifier -> identifier.getExtension().equals(extension); + } + +} diff --git a/src/api/java/net/minecraftforge/artifactural/api/artifact/ArtifactMetadata.java b/src/api/java/net/minecraftforge/artifactural/api/artifact/ArtifactMetadata.java new file mode 100644 index 0000000..6bdf854 --- /dev/null +++ b/src/api/java/net/minecraftforge/artifactural/api/artifact/ArtifactMetadata.java @@ -0,0 +1,28 @@ +/* + * 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.api.artifact; + +public interface ArtifactMetadata { + + ArtifactMetadata with(String key, String value); + + String getHash(); + +} diff --git a/src/api/java/net/minecraftforge/artifactural/api/artifact/ArtifactType.java b/src/api/java/net/minecraftforge/artifactural/api/artifact/ArtifactType.java new file mode 100644 index 0000000..e2d717c --- /dev/null +++ b/src/api/java/net/minecraftforge/artifactural/api/artifact/ArtifactType.java @@ -0,0 +1,24 @@ +/* + * 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.api.artifact; + +public enum ArtifactType { + BINARY, SOURCE, OTHER +} diff --git a/src/api/java/net/minecraftforge/artifactural/api/artifact/Internal.java b/src/api/java/net/minecraftforge/artifactural/api/artifact/Internal.java new file mode 100644 index 0000000..4e9a03c --- /dev/null +++ b/src/api/java/net/minecraftforge/artifactural/api/artifact/Internal.java @@ -0,0 +1,137 @@ +/* + * 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.api.artifact; + +import java.io.File; +import java.io.InputStream; + +import net.minecraftforge.artifactural.api.cache.ArtifactCache; +import net.minecraftforge.artifactural.api.transform.ArtifactTransformer; + +final class Internal { + + static final ArtifactIdentifier NO_IDENTIFIER = new ArtifactIdentifier() { + + @Override + public String getGroup() { + return "missing"; + } + + @Override + public String getName() { + return "missing"; + } + + @Override + public String getVersion() { + return "0.0.0"; + } + + @Override + public String getClassifier() { + return ""; + } + + @Override + public String getExtension() { + return "missing"; + } + + @Override + public String toString() { + return "NO_IDENTIFIER"; + } + + }; + + static final Artifact NO_ARTIFACT = new Artifact.Cached() { + @Override + public String toString() { + return "NO_ARTIFACT"; + } + + @Override + public ArtifactIdentifier getIdentifier() { + return ArtifactIdentifier.none(); + } + + @Override + public ArtifactMetadata getMetadata() { + return new ArtifactMetadata() { + @Override + public ArtifactMetadata with(String key, String value) { + throw new UnsupportedOperationException(); + } + + @Override + public String getHash() { + return "ERROR"; + } + + @Override + public String toString() { + return "NO_METADATA"; + } + }; + } + + @Override + public ArtifactType getType() { + return ArtifactType.OTHER; + } + + @Override + public Artifact withMetadata(ArtifactMetadata metadata) { + return this; + } + + @Override + public Artifact apply(ArtifactTransformer transformer) { + return this; + } + + @Override + public Artifact.Cached cache(ArtifactCache cache) { + return this; + } + + @Override + public boolean isPresent() { + return false; + } + + @Override + public InputStream openStream() throws MissingArtifactException { + throw new MissingArtifactException(getIdentifier()); + } + + @Override + public File asFile() throws MissingArtifactException { + throw new MissingArtifactException(getIdentifier()); + } + + @Override + public File getFileLocation() throws MissingArtifactException { + throw new MissingArtifactException(getIdentifier()); + } + + }; + +} diff --git a/src/api/java/net/minecraftforge/artifactural/api/artifact/MissingArtifactException.java b/src/api/java/net/minecraftforge/artifactural/api/artifact/MissingArtifactException.java new file mode 100644 index 0000000..18686a6 --- /dev/null +++ b/src/api/java/net/minecraftforge/artifactural/api/artifact/MissingArtifactException.java @@ -0,0 +1,29 @@ +/* + * 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.api.artifact; + +public class MissingArtifactException extends RuntimeException { + private static final long serialVersionUID = 4902516963452435653L; + + public MissingArtifactException(ArtifactIdentifier identifier) { + super("Could not find artifact: " + identifier); + } + +} diff --git a/src/api/java/net/minecraftforge/artifactural/api/artifact/Streamable.java b/src/api/java/net/minecraftforge/artifactural/api/artifact/Streamable.java new file mode 100644 index 0000000..fa073e5 --- /dev/null +++ b/src/api/java/net/minecraftforge/artifactural/api/artifact/Streamable.java @@ -0,0 +1,30 @@ +/* + * 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.api.artifact; + +import java.io.IOException; +import java.io.InputStream; + +@FunctionalInterface +public interface Streamable { + + InputStream openStream() throws IOException; + +} diff --git a/src/api/java/net/minecraftforge/artifactural/api/cache/ArtifactCache.java b/src/api/java/net/minecraftforge/artifactural/api/cache/ArtifactCache.java new file mode 100644 index 0000000..9681b0d --- /dev/null +++ b/src/api/java/net/minecraftforge/artifactural/api/cache/ArtifactCache.java @@ -0,0 +1,28 @@ +/* + * 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.api.cache; + +import net.minecraftforge.artifactural.api.artifact.Artifact; + +public interface ArtifactCache { + + Artifact.Cached store(Artifact artifact); + +} diff --git a/src/api/java/net/minecraftforge/artifactural/api/repository/ArtifactProvider.java b/src/api/java/net/minecraftforge/artifactural/api/repository/ArtifactProvider.java new file mode 100644 index 0000000..cdcfc1a --- /dev/null +++ b/src/api/java/net/minecraftforge/artifactural/api/repository/ArtifactProvider.java @@ -0,0 +1,47 @@ +/* + * 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.api.repository; + +import java.util.function.Function; +import java.util.function.Predicate; + +import net.minecraftforge.artifactural.api.artifact.Artifact; + +public interface ArtifactProvider { + + Artifact getArtifact(I info); + + interface Builder { + + Builder filter(Predicate filter); + + Builder mapInfo(Function mapper); + + Complete provide(ArtifactProvider provider); + + interface Complete extends ArtifactProvider { + + Complete provide(ArtifactProvider provider); + + } + + } + +} diff --git a/src/api/java/net/minecraftforge/artifactural/api/repository/Repository.java b/src/api/java/net/minecraftforge/artifactural/api/repository/Repository.java new file mode 100644 index 0000000..e9694d8 --- /dev/null +++ b/src/api/java/net/minecraftforge/artifactural/api/repository/Repository.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.api.repository; + +import java.io.File; + +import net.minecraftforge.artifactural.api.artifact.Artifact; +import net.minecraftforge.artifactural.api.artifact.ArtifactIdentifier; + +public interface Repository { + + Artifact getArtifact(ArtifactIdentifier identifier); + + /** + * Returns a file in maven-metadata.xml format for the specified artifact, + * this is used by gradle to list all known versions, so that it can resolve wildcard + * dependencies such as foo:bar:1.+ + * + * @param group Group + * @param name Artifact name + * @return maven-metadata.xml file listing all versions of the artifact this repo can provide. Or null if you don't want to list any. + */ + default File getMavenMetadata(String group, String name) { + return null; + } + +} diff --git a/src/api/java/net/minecraftforge/artifactural/api/transform/ArtifactTransformer.java b/src/api/java/net/minecraftforge/artifactural/api/transform/ArtifactTransformer.java new file mode 100644 index 0000000..86a43ca --- /dev/null +++ b/src/api/java/net/minecraftforge/artifactural/api/transform/ArtifactTransformer.java @@ -0,0 +1,68 @@ +/* + * 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.api.transform; + +import java.util.function.UnaryOperator; + +import net.minecraftforge.artifactural.api.artifact.Artifact; +import net.minecraftforge.artifactural.api.artifact.ArtifactMetadata; + +public interface ArtifactTransformer { + + static ArtifactTransformer of(UnaryOperator operator) { + return new ArtifactTransformer() { + @Override + public Artifact transform(Artifact artifact) { + return operator.apply(artifact); + } + + @Override + public ArtifactMetadata withInfo(ArtifactMetadata metadata) { + return metadata; + } + }; + } + + default boolean appliesTo(Artifact artifact) { + return true; + } + + Artifact transform(Artifact artifact); + + ArtifactMetadata withInfo(ArtifactMetadata metadata); + + default ArtifactTransformer andThen(ArtifactTransformer other) { + ArtifactTransformer current = this; + return new ArtifactTransformer() { + + @Override + public Artifact transform(Artifact artifact) { + return other.transform(current.transform(artifact)); + } + + @Override + public ArtifactMetadata withInfo(ArtifactMetadata metadata) { + return other.withInfo(current.withInfo(metadata)); + } + + }; + } + +} diff --git a/src/gradlecomp/java/com/amadornes/artifactural/gradle/DependencyResolver.java b/src/gradlecomp/java/com/amadornes/artifactural/gradle/DependencyResolver.java deleted file mode 100644 index 9040b63..0000000 --- a/src/gradlecomp/java/com/amadornes/artifactural/gradle/DependencyResolver.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Artifactural - * Copyright (c) 2018. - * - * 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 com.amadornes.artifactural.gradle; - -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; -import org.gradle.api.Project; -import org.gradle.api.artifacts.ClientModule; -import org.gradle.api.artifacts.Configuration; -import org.gradle.api.artifacts.Dependency; -import org.gradle.api.artifacts.DependencyArtifact; -import org.gradle.api.artifacts.FileCollectionDependency; -import org.gradle.api.artifacts.ModuleDependency; - -import java.io.File; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -public class DependencyResolver { - - private final Project project; - private final AtomicInteger counter = new AtomicInteger(0); - private final Cache>> resolved = CacheBuilder.newBuilder().expireAfterWrite(30, TimeUnit.MINUTES).build(); - - public DependencyResolver(Project project) { - this.project = project; - } - - /** - * Resolves a dependency, downloading the file and its transitives - * if not cached and returns the set of files. - */ - public Set resolveDependency(Dependency dependency) { - if (dependency instanceof FileCollectionDependency) { - return ((FileCollectionDependency) dependency).getFiles().getFiles(); - } - String name = dependency.getGroup() + ":" + dependency.getName() + ":" + dependency.getVersion(); - if (dependency instanceof ModuleDependency) { - Set artifacts = ((ModuleDependency) dependency).getArtifacts(); - if (!artifacts.isEmpty()) { - DependencyArtifact artifact = artifacts.iterator().next(); - name += ":" + artifact.getClassifier() + "@" + artifact.getExtension(); - } - } - - // If this dep is being resolved on another thread, let it do it - CompletableFuture> future; - boolean found = true; - synchronized (resolved) { - future = resolved.getIfPresent(name); - if (future == null) { - resolved.put(name, future = new CompletableFuture<>()); - found = false; - } - } - - if (found) { - try { - return future.get(); - } catch (InterruptedException | ExecutionException ex) { - throw new RuntimeException(ex); - } - } - - // No other thread is resolving this dep and we've claimed it, so let's go! - int currentID = counter.getAndIncrement(); - Configuration cfg = project.getConfigurations().maybeCreate("resolve_dep_" + currentID); - cfg.getDependencies().add(dependency); - Set files = cfg.resolve(); - project.getConfigurations().remove(cfg); - future.complete(files); - return files; - } - - /** - * Resolves a dependency, downloading the file and its transitives - * if not cached and returns the set of files. - */ - public Set resolveDependency(Object dependency) { - Dependency dep = project.getDependencies().create(dependency); - return resolveDependency(dep); - } - - /** - * Resolves a dependency, downloading the file and its transitives - * if not cached and returns the set of files. - */ - public Set resolveDependency(Object dependency, boolean transitive) { - Dependency dep = project.getDependencies().create(dependency); - if (dep instanceof ClientModule) { - dep = ((ClientModule) dep).copy().setTransitive(transitive); - } - return resolveDependency(dep); - } - - /** - * Resolves a single dependency without any of its transitives - * if not cached and returns the file. - */ - public File resolveSingleDependency(Object dependency) { - return resolveDependency(dependency, false).iterator().next(); - } - -} diff --git a/src/gradlecomp/java/com/amadornes/artifactural/gradle/GradleArtifact.java b/src/gradlecomp/java/com/amadornes/artifactural/gradle/GradleArtifact.java deleted file mode 100644 index b1fc537..0000000 --- a/src/gradlecomp/java/com/amadornes/artifactural/gradle/GradleArtifact.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Artifactural - * Copyright (c) 2018. - * - * 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 com.amadornes.artifactural.gradle; - -import com.amadornes.artifactural.api.artifact.Artifact; -import com.amadornes.artifactural.api.artifact.ArtifactIdentifier; -import com.amadornes.artifactural.api.artifact.ArtifactType; -import com.amadornes.artifactural.base.artifact.StreamableArtifact; - -import java.io.File; -import java.util.Set; - -public class GradleArtifact { - - public static Artifact maven(DependencyResolver resolver, ArtifactIdentifier identifier, ArtifactType type) { - Set files = resolver.resolveDependency( - identifier.getGroup() - + ":" + identifier.getName() - + ":" + identifier.getVersion() - + (identifier.getClassifier().isEmpty() ? "" : ":" + identifier.getClassifier()) - + (identifier.getExtension().isEmpty() ? "" : "@" + identifier.getExtension()), - false - ); - if (files.isEmpty()) return Artifact.none(); - return StreamableArtifact.ofFile(identifier, type, files.iterator().next()); - } - -} diff --git a/src/gradlecomp/java/com/amadornes/artifactural/gradle/GradleRepositoryAdapter.java b/src/gradlecomp/java/com/amadornes/artifactural/gradle/GradleRepositoryAdapter.java deleted file mode 100644 index 51def33..0000000 --- a/src/gradlecomp/java/com/amadornes/artifactural/gradle/GradleRepositoryAdapter.java +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Artifactural - * Copyright (c) 2018. - * - * 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 com.amadornes.artifactural.gradle; - -import com.amadornes.artifactural.api.artifact.Artifact; -import com.amadornes.artifactural.api.artifact.ArtifactIdentifier; -import com.amadornes.artifactural.api.artifact.MissingArtifactException; -import com.amadornes.artifactural.api.repository.Repository; -import com.amadornes.artifactural.base.artifact.SimpleArtifactIdentifier; -import com.amadornes.artifactural.base.cache.LocatedArtifactCache; - -import org.gradle.api.artifacts.ComponentMetadataSupplierDetails; -import org.gradle.api.artifacts.component.ComponentArtifactIdentifier; -import org.gradle.api.artifacts.component.ModuleComponentIdentifier; -import org.gradle.api.artifacts.dsl.RepositoryHandler; -import org.gradle.api.internal.artifacts.BaseRepositoryFactory; -import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ComponentResolvers; -import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ConfiguredModuleComponentRepository; -import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleComponentRepositoryAccess; -import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact; -import org.gradle.api.internal.artifacts.repositories.AbstractArtifactRepository; -import org.gradle.api.internal.artifacts.repositories.DefaultMavenLocalArtifactRepository; -import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository; -import org.gradle.api.internal.artifacts.repositories.descriptor.FlatDirRepositoryDescriptor; -import org.gradle.api.internal.artifacts.repositories.descriptor.RepositoryDescriptor; -import org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceArtifactResolver; -import org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceResolver; -import org.gradle.api.internal.artifacts.repositories.resolver.MavenResolver; -import org.gradle.api.internal.artifacts.repositories.resolver.MetadataFetchingCost; -import org.gradle.api.internal.component.ArtifactType; -import org.gradle.api.model.ObjectFactory; -import org.gradle.internal.action.InstantiatingAction; -import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata; -import org.gradle.internal.component.external.model.ModuleDependencyMetadata; -import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetadata; -import org.gradle.internal.component.model.ComponentArtifactMetadata; -import org.gradle.internal.component.model.ComponentOverrideMetadata; -import org.gradle.internal.component.model.ComponentResolveMetadata; -import org.gradle.internal.component.model.ConfigurationMetadata; -import org.gradle.internal.component.model.ModuleSources; -import org.gradle.internal.nativeintegration.filesystem.FileSystem; -import org.gradle.internal.nativeintegration.services.FileSystems; -import org.gradle.internal.reflect.Instantiator; -import org.gradle.internal.resolve.result.BuildableArtifactResolveResult; -import org.gradle.internal.resolve.result.BuildableArtifactSetResolveResult; -import org.gradle.internal.resolve.result.BuildableComponentArtifactsResolveResult; -import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult; -import org.gradle.internal.resolve.result.BuildableModuleVersionListingResolveResult; -import org.gradle.internal.resource.ExternalResourceName; -import org.gradle.internal.resource.ExternalResourceRepository; -import org.gradle.internal.resource.LocalBinaryResource; -import org.gradle.internal.resource.local.FileResourceRepository; -import org.gradle.internal.resource.local.LocalFileStandInExternalResource; -import org.gradle.internal.resource.local.LocallyAvailableExternalResource; -import org.gradle.internal.resource.metadata.ExternalResourceMetaData; -import org.gradle.internal.resource.transfer.DefaultCacheAwareExternalResourceAccessor; - -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.util.ArrayList; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class GradleRepositoryAdapter extends AbstractArtifactRepository implements ResolutionAwareRepository { - - private static final Pattern URL_PATTERN = Pattern.compile( - "^(?\\S+(?:/\\S+)*)/(?\\S+)/(?\\S+)/" + - "\\2-\\3(?:-(?[^.\\s]+))?\\.(?\\S+)$"); - - public static GradleRepositoryAdapter add(RepositoryHandler handler, String name, File local, Repository repository) { - BaseRepositoryFactory factory = ReflectionUtils.get(handler, "repositoryFactory"); // We reflect here and create it manually so it DOESN'T get attached. - DefaultMavenLocalArtifactRepository maven = (DefaultMavenLocalArtifactRepository)factory.createMavenLocalRepository(); // We use maven local because it bypasses the caching and coping to .m2 - maven.setUrl(local); - maven.setName(name); - - GradleRepositoryAdapter repo; - - repo = new GradleRepositoryAdapter(repository, maven); - repo.setName(name); - handler.add(repo); - return repo; - } - - private final Repository repository; - private final DefaultMavenLocalArtifactRepository local; - private final String root; - private final LocatedArtifactCache cache; - - - // This constructor is modified via bytecode manipulation in 'build.gradle' - // DO NOT change this without modifying 'build.gradle' - // This contructor is used on Gradle 4.9 and below - private GradleRepositoryAdapter(Repository repository, DefaultMavenLocalArtifactRepository local) { - // This is replaced with a call to 'super()', with no arguments - super(null); - this.repository = repository; - this.local = local; - this.root = cleanRoot(local.getUrl()); - this.cache = new LocatedArtifactCache(new File(root)); - } - - - // This constructor is used on Gradle 4.10 and above - GradleRepositoryAdapter(ObjectFactory objectFactory, Repository repository, DefaultMavenLocalArtifactRepository local) { - super(objectFactory); - // This duplication from the above two-argument constructor is unfortunate, - // but unavoidable - this.repository = repository; - this.local = local; - this.root = cleanRoot(local.getUrl()); - this.cache = new LocatedArtifactCache(new File(root)); - } - - @Override - public String getDisplayName() { - return local.getDisplayName(); - } - - @Override - public ConfiguredModuleComponentRepository createResolver() { - MavenResolver resolver = (MavenResolver)local.createResolver(); - - GeneratingFileResourceRepository repo = new GeneratingFileResourceRepository(); - ReflectionUtils.alter(resolver, "repository", prev -> repo); // ExternalResourceResolver.repository - //ReflectionUtils.alter(resolver, "metadataSources", ); //ExternalResourceResolver.metadataSources We need to fix these from returning 'missing' - // MavenResolver -> MavenMetadataLoader -> FileCacheAwareExternalResourceAccessor -> DefaultCacheAwareExternalResourceAccessor - DefaultCacheAwareExternalResourceAccessor accessor = ReflectionUtils.get(resolver, "mavenMetaDataLoader.cacheAwareExternalResourceAccessor.delegate"); - ReflectionUtils.alter(accessor, "delegate", prev -> repo); // DefaultCacheAwareExternalResourceAccessor.delegate - ReflectionUtils.alter(accessor, "fileResourceRepository", prev -> repo); // DefaultCacheAwareExternalResourceAccessor.fileResourceRepository - ExternalResourceArtifactResolver extResolver = ReflectionUtils.invoke(resolver, ExternalResourceResolver.class, "createArtifactResolver"); //Makes the resolver and caches it. - ReflectionUtils.alter(extResolver, "repository", prev -> repo); - //File transport references, Would be better to get a reference to the transport and work from there, but don't see it stored anywhere. - ReflectionUtils.alter(resolver, "cachingResourceAccessor.this$0.repository", prev -> repo); - ReflectionUtils.alter(resolver, "cachingResourceAccessor.delegate.delegate", prev -> repo); - - return new ConfiguredModuleComponentRepository() { - private final ModuleComponentRepositoryAccess local = wrap(resolver.getLocalAccess()); - private final ModuleComponentRepositoryAccess remote = wrap(resolver.getRemoteAccess()); - @Override public String getId() { return resolver.getId(); } - @Override public String getName() { return resolver.getName(); } - @Override public ModuleComponentRepositoryAccess getLocalAccess() { return local; } - @Override public ModuleComponentRepositoryAccess getRemoteAccess() { return remote; } - @Override public Map getArtifactCache() { return resolver.getArtifactCache(); } - @Override public InstantiatingAction getComponentMetadataSupplier() { return resolver.getComponentMetadataSupplier(); } - @Override public boolean isDynamicResolveMode() { return resolver.isDynamicResolveMode(); } - @Override public boolean isLocal() { return resolver.isLocal(); } - - @Override - public void setComponentResolvers(ComponentResolvers resolver) { } - @Override - public Instantiator getComponentMetadataInstantiator() { - return resolver.getComponentMetadataInstantiator(); - } - - private ModuleComponentRepositoryAccess wrap(ModuleComponentRepositoryAccess delegate) { - return new ModuleComponentRepositoryAccess() { - @Override - public void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult result) { - delegate.resolveComponentMetaData(moduleComponentIdentifier, requestMetaData, result); - if (result.getState() == BuildableModuleComponentMetaDataResolveResult.State.Resolved) { - ModuleComponentResolveMetadata meta = result.getMetaData(); - if (meta.isMissing()) { - MutableModuleComponentResolveMetadata mutable = meta.asMutable(); - mutable.setChanging(true); - mutable.setMissing(false); - result.resolved(mutable.asImmutable()); - } - } - } - - @Override - public void resolveArtifacts(ComponentResolveMetadata component, ConfigurationMetadata variant, BuildableComponentArtifactsResolveResult result) { - delegate.resolveArtifacts(component, variant, result); - } - - @Override - public void listModuleVersions(ModuleDependencyMetadata dependency, BuildableModuleVersionListingResolveResult result) { - delegate.listModuleVersions(dependency, result); - } - - @Override - public void resolveArtifactsWithType(ComponentResolveMetadata component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) { - delegate.resolveArtifactsWithType(component, artifactType, result); - } - - @Override - public void resolveArtifact(ComponentArtifactMetadata artifact, ModuleSources moduleSources, BuildableArtifactResolveResult result) { - delegate.resolveArtifact(artifact, moduleSources, result); - } - - - @Override - public MetadataFetchingCost estimateMetadataFetchingCost(ModuleComponentIdentifier moduleComponentIdentifier) { - return delegate.estimateMetadataFetchingCost(moduleComponentIdentifier); - } - }; - } - }; - } - - public RepositoryDescriptor getDescriptor() { - return new FlatDirRepositoryDescriptor("ArtifacturalRepository", new ArrayList<>()); - } - - - private static String cleanRoot(URI uri) { - String ret = uri.normalize().getPath().replace('\\', '/'); - if (!ret.endsWith("/")) ret += '/'; - return ret; - } - - private class GeneratingFileResourceRepository implements FileResourceRepository { - private final FileSystem fileSystem = FileSystems.getDefault(); - private void debug(String message) { - //System.out.println(message); - } - private void log(String message) { - System.out.println(message); - } - - @Override - public ExternalResourceRepository withProgressLogging() { - return this; - } - - @Override - public LocalBinaryResource localResource(File file) { - debug("localResource: " + file); - return null; - } - - @Override - public LocallyAvailableExternalResource resource(File file) { - debug("resource(File): " + file); - return findArtifact(file.getAbsolutePath().replace('\\', '/')); - } - - @Override - public LocallyAvailableExternalResource resource(ExternalResourceName location) { - return resource(location, false); - } - - @Override - public LocallyAvailableExternalResource resource(ExternalResourceName location, boolean revalidate) { - debug("resource(ExternalResourceName,boolean): " + location + ", " + revalidate); - return findArtifact(location.getUri().getPath().replace('\\', '/')); - } - - @Override - public LocallyAvailableExternalResource resource(File file, URI originUri, ExternalResourceMetaData originMetadata) { - debug("resource(File,URI,ExternalResourceMetaData): " + file + ", " + originUri + ", " + originMetadata); - return findArtifact(file.getAbsolutePath().replace('\\', '/')); - } - - private LocallyAvailableExternalResource findArtifact(String path) { - if (path.startsWith(root)) { - String relative = path.substring(root.length()); - debug(" Relative: " + relative); - Matcher matcher = URL_PATTERN.matcher(relative); - if (matcher.matches()) { - ArtifactIdentifier identifier = new SimpleArtifactIdentifier( - matcher.group("group").replace('/', '.'), - matcher.group("name"), - matcher.group("version"), - matcher.group("classifier"), - matcher.group("extension")); - Artifact artifact = repository.getArtifact(identifier); - return wrap(artifact, identifier); - } else if (relative.endsWith("maven-metadata.xml")) { - String tmp = relative.substring(0, relative.length() - "maven-metadata.xml".length() - 1); - int idx = tmp.lastIndexOf('/'); - if (idx != -1) { - File ret = repository.getMavenMetadata(tmp.substring(0, idx - 1), tmp.substring(idx)); - if (ret != null) { - return new LocalFileStandInExternalResource(ret, fileSystem); - } - } - } else if (relative.endsWith("/")) { - debug(" Directory listing not supported"); - } else { - log(" Matcher Failed: " + relative); - } - } else { - log("Unknown root: " + path); - } - return new LocalFileStandInExternalResource(new File(path), fileSystem); - } - - private LocallyAvailableExternalResource wrap(Artifact artifact, ArtifactIdentifier id) { - if (!artifact.isPresent()) - return new LocalFileStandInExternalResource(cache.getPath(artifact), fileSystem); - Artifact.Cached cached = artifact.optionallyCache(cache); - try { - return new LocalFileStandInExternalResource(cached.asFile(), fileSystem); - } catch (MissingArtifactException | IOException e) { - throw new RuntimeException(e); - } - } - } - - //TODO: Make this a artifact provider interface with a proper API so we dont have direct reference to GradleRepoAdapter in consumers. - public File getArtifact(ArtifactIdentifier identifier) { - Artifact art = repository.getArtifact(identifier); - if (!art.isPresent()) - return null; - - Artifact.Cached cached = art.optionallyCache(cache); - try { - return cached.asFile(); - } catch (MissingArtifactException | IOException e) { - throw new RuntimeException(e); - } - } -} diff --git a/src/gradlecomp/java/com/amadornes/artifactural/gradle/ModifierAccess.java b/src/gradlecomp/java/com/amadornes/artifactural/gradle/ModifierAccess.java deleted file mode 100644 index 0a84755..0000000 --- a/src/gradlecomp/java/com/amadornes/artifactural/gradle/ModifierAccess.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Artifactural - * Copyright (c) 2018. - * - * 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 com.amadornes.artifactural.gradle; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; - -public class ModifierAccess { - private static Field MODIFIER_ACCESS = null; - private static boolean accessAttempted = false; - - public static synchronized boolean definalize(Field target) { - if ((target.getModifiers() & Modifier.FINAL) == 0) { - return true; - } - - if (MODIFIER_ACCESS == null && !accessAttempted) { - try { - final Field modifiers = Field.class.getDeclaredField("modifiers"); - modifiers.setAccessible(true); - MODIFIER_ACCESS = modifiers; - } catch (NoSuchFieldException e) { - throw new RuntimeException("Could not access Field.modifiers to definalize reflection object. Use Java 8, current version: " + System.getProperty("java.version"), e); - } - accessAttempted = true; - } - if (MODIFIER_ACCESS != null) { - try { - MODIFIER_ACCESS.setInt(target, target.getModifiers() & ~Modifier.FINAL); - } catch (IllegalArgumentException | IllegalAccessException e) { - throw new RuntimeException("Could not definalize field " + target.getDeclaringClass().getName() + "." + target.getName(), e); - } - return true; - } - return false; - } - -} diff --git a/src/gradlecomp/java/com/amadornes/artifactural/gradle/ReflectionUtils.java b/src/gradlecomp/java/com/amadornes/artifactural/gradle/ReflectionUtils.java deleted file mode 100644 index 1bef580..0000000 --- a/src/gradlecomp/java/com/amadornes/artifactural/gradle/ReflectionUtils.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Artifactural - * Copyright (c) 2018. - * - * 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 com.amadornes.artifactural.gradle; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.function.UnaryOperator; - -public class ReflectionUtils { - - @SuppressWarnings("unchecked") - public static void alter(Object target, String name, UnaryOperator operator) { - try { - int idx = name.lastIndexOf('.'); - if (idx != -1) { - target = drillField(target, name.substring(0, idx)); - if (target == null) throw new IllegalStateException("Could not find field '" + name + "'"); - name = name.substring(idx + 1); - } - Field f = findField(target.getClass(), name); - if (f == null) throw new IllegalStateException("Could not find '" + name + "'"); - - T oldV = (T)f.get(target); - T newV = operator.apply(oldV); - f.set(target, newV); - - if (f.get(target) != newV) { - throw new IllegalStateException("Failed to set new value on " + f.getDeclaringClass().getName() + "." + f.getName()); - } - } catch (IllegalAccessException ex) { - throw new RuntimeException(ex); - } - } - - private static Object drillField(Object obj, String path) { - for (String name : path.split("\\.")) { - if (obj == null) return null; - Field f = findField(obj.getClass(), name); - if (f == null) return null; - try { - obj = f.get(obj); - } catch (IllegalAccessException e) { - return null; - } - } - return obj; - } - - private static Field findField(Class clazz, String name) { - while (clazz != Object.class) { - for (Field f : clazz.getDeclaredFields()) { - if (f.getName().equals(name)) { - f.setAccessible(true); - if (!ModifierAccess.definalize(f)) { - System.out.println("Could not definalize field " + f.getDeclaringClass().getName() + "." + f.getName() + " Exception ate, lets see if it works"); - } - return f; - } - } - clazz = clazz.getSuperclass(); - } - return null; - } - - /** - * Invokes a method (can be private). - */ - @SuppressWarnings("unchecked") - public static T invoke(Object target, Class type, String name, Object... args) { - try { - Method method = type.getDeclaredMethod(name); - method.setAccessible(true); - return (T) method.invoke(target, args); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - - @SuppressWarnings("unchecked") - public static T get(Object target, String name) { - try { - int idx = name.lastIndexOf('.'); - if (idx != -1) { - target = drillField(target, name.substring(0, idx)); - if (target == null) throw new IllegalStateException("Could not find field '" + name + "'"); - name = name.substring(idx + 1); - } - Field f = findField(target.getClass(), name); - if (f == null) throw new IllegalStateException("Could not find '" + name + "'"); - return (T)f.get(target); - } catch (IllegalAccessException ex) { - throw new RuntimeException(ex); - } - } -} diff --git a/src/gradlecomp/java/net/minecraftforge/artifactural/gradle/DependencyResolver.java b/src/gradlecomp/java/net/minecraftforge/artifactural/gradle/DependencyResolver.java new file mode 100644 index 0000000..a0d8a00 --- /dev/null +++ b/src/gradlecomp/java/net/minecraftforge/artifactural/gradle/DependencyResolver.java @@ -0,0 +1,124 @@ +/* + * 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.gradle; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import org.gradle.api.Project; +import org.gradle.api.artifacts.ClientModule; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.Dependency; +import org.gradle.api.artifacts.DependencyArtifact; +import org.gradle.api.artifacts.FileCollectionDependency; +import org.gradle.api.artifacts.ModuleDependency; + +import java.io.File; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +public class DependencyResolver { + + private final Project project; + private final AtomicInteger counter = new AtomicInteger(0); + private final Cache>> resolved = CacheBuilder.newBuilder().expireAfterWrite(30, TimeUnit.MINUTES).build(); + + public DependencyResolver(Project project) { + this.project = project; + } + + /** + * Resolves a dependency, downloading the file and its transitives + * if not cached and returns the set of files. + */ + public Set resolveDependency(Dependency dependency) { + if (dependency instanceof FileCollectionDependency) { + return ((FileCollectionDependency) dependency).getFiles().getFiles(); + } + String name = dependency.getGroup() + ":" + dependency.getName() + ":" + dependency.getVersion(); + if (dependency instanceof ModuleDependency) { + Set artifacts = ((ModuleDependency) dependency).getArtifacts(); + if (!artifacts.isEmpty()) { + DependencyArtifact artifact = artifacts.iterator().next(); + name += ":" + artifact.getClassifier() + "@" + artifact.getExtension(); + } + } + + // If this dep is being resolved on another thread, let it do it + CompletableFuture> future; + boolean found = true; + synchronized (resolved) { + future = resolved.getIfPresent(name); + if (future == null) { + resolved.put(name, future = new CompletableFuture<>()); + found = false; + } + } + + if (found) { + try { + return future.get(); + } catch (InterruptedException | ExecutionException ex) { + throw new RuntimeException(ex); + } + } + + // No other thread is resolving this dep and we've claimed it, so let's go! + int currentID = counter.getAndIncrement(); + Configuration cfg = project.getConfigurations().maybeCreate("resolve_dep_" + currentID); + cfg.getDependencies().add(dependency); + Set files = cfg.resolve(); + project.getConfigurations().remove(cfg); + future.complete(files); + return files; + } + + /** + * Resolves a dependency, downloading the file and its transitives + * if not cached and returns the set of files. + */ + public Set resolveDependency(Object dependency) { + Dependency dep = project.getDependencies().create(dependency); + return resolveDependency(dep); + } + + /** + * Resolves a dependency, downloading the file and its transitives + * if not cached and returns the set of files. + */ + public Set resolveDependency(Object dependency, boolean transitive) { + Dependency dep = project.getDependencies().create(dependency); + if (dep instanceof ClientModule) { + dep = ((ClientModule) dep).copy().setTransitive(transitive); + } + return resolveDependency(dep); + } + + /** + * Resolves a single dependency without any of its transitives + * if not cached and returns the file. + */ + public File resolveSingleDependency(Object dependency) { + return resolveDependency(dependency, false).iterator().next(); + } + +} diff --git a/src/gradlecomp/java/net/minecraftforge/artifactural/gradle/GradleArtifact.java b/src/gradlecomp/java/net/minecraftforge/artifactural/gradle/GradleArtifact.java new file mode 100644 index 0000000..e099c73 --- /dev/null +++ b/src/gradlecomp/java/net/minecraftforge/artifactural/gradle/GradleArtifact.java @@ -0,0 +1,45 @@ +/* + * 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.gradle; + +import net.minecraftforge.artifactural.api.artifact.Artifact; +import net.minecraftforge.artifactural.api.artifact.ArtifactIdentifier; +import net.minecraftforge.artifactural.api.artifact.ArtifactType; +import net.minecraftforge.artifactural.base.artifact.StreamableArtifact; + +import java.io.File; +import java.util.Set; + +public class GradleArtifact { + + public static Artifact maven(DependencyResolver resolver, ArtifactIdentifier identifier, ArtifactType type) { + Set files = resolver.resolveDependency( + identifier.getGroup() + + ":" + identifier.getName() + + ":" + identifier.getVersion() + + (identifier.getClassifier().isEmpty() ? "" : ":" + identifier.getClassifier()) + + (identifier.getExtension().isEmpty() ? "" : "@" + identifier.getExtension()), + false + ); + if (files.isEmpty()) return Artifact.none(); + return StreamableArtifact.ofFile(identifier, type, files.iterator().next()); + } + +} diff --git a/src/gradlecomp/java/net/minecraftforge/artifactural/gradle/GradleRepositoryAdapter.java b/src/gradlecomp/java/net/minecraftforge/artifactural/gradle/GradleRepositoryAdapter.java new file mode 100644 index 0000000..87cd246 --- /dev/null +++ b/src/gradlecomp/java/net/minecraftforge/artifactural/gradle/GradleRepositoryAdapter.java @@ -0,0 +1,333 @@ +/* + * 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.gradle; + +import net.minecraftforge.artifactural.api.artifact.Artifact; +import net.minecraftforge.artifactural.api.artifact.ArtifactIdentifier; +import net.minecraftforge.artifactural.api.artifact.MissingArtifactException; +import net.minecraftforge.artifactural.api.repository.Repository; +import net.minecraftforge.artifactural.base.artifact.SimpleArtifactIdentifier; +import net.minecraftforge.artifactural.base.cache.LocatedArtifactCache; + +import org.gradle.api.artifacts.ComponentMetadataSupplierDetails; +import org.gradle.api.artifacts.component.ComponentArtifactIdentifier; +import org.gradle.api.artifacts.component.ModuleComponentIdentifier; +import org.gradle.api.artifacts.dsl.RepositoryHandler; +import org.gradle.api.internal.artifacts.BaseRepositoryFactory; +import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ComponentResolvers; +import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ConfiguredModuleComponentRepository; +import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.ModuleComponentRepositoryAccess; +import org.gradle.api.internal.artifacts.ivyservice.resolveengine.artifact.ResolvableArtifact; +import org.gradle.api.internal.artifacts.repositories.AbstractArtifactRepository; +import org.gradle.api.internal.artifacts.repositories.DefaultMavenLocalArtifactRepository; +import org.gradle.api.internal.artifacts.repositories.ResolutionAwareRepository; +import org.gradle.api.internal.artifacts.repositories.descriptor.FlatDirRepositoryDescriptor; +import org.gradle.api.internal.artifacts.repositories.descriptor.RepositoryDescriptor; +import org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceArtifactResolver; +import org.gradle.api.internal.artifacts.repositories.resolver.ExternalResourceResolver; +import org.gradle.api.internal.artifacts.repositories.resolver.MavenResolver; +import org.gradle.api.internal.artifacts.repositories.resolver.MetadataFetchingCost; +import org.gradle.api.internal.component.ArtifactType; +import org.gradle.api.model.ObjectFactory; +import org.gradle.internal.action.InstantiatingAction; +import org.gradle.internal.component.external.model.ModuleComponentResolveMetadata; +import org.gradle.internal.component.external.model.ModuleDependencyMetadata; +import org.gradle.internal.component.external.model.MutableModuleComponentResolveMetadata; +import org.gradle.internal.component.model.ComponentArtifactMetadata; +import org.gradle.internal.component.model.ComponentOverrideMetadata; +import org.gradle.internal.component.model.ComponentResolveMetadata; +import org.gradle.internal.component.model.ConfigurationMetadata; +import org.gradle.internal.component.model.ModuleSources; +import org.gradle.internal.nativeintegration.filesystem.FileSystem; +import org.gradle.internal.nativeintegration.services.FileSystems; +import org.gradle.internal.reflect.Instantiator; +import org.gradle.internal.resolve.result.BuildableArtifactResolveResult; +import org.gradle.internal.resolve.result.BuildableArtifactSetResolveResult; +import org.gradle.internal.resolve.result.BuildableComponentArtifactsResolveResult; +import org.gradle.internal.resolve.result.BuildableModuleComponentMetaDataResolveResult; +import org.gradle.internal.resolve.result.BuildableModuleVersionListingResolveResult; +import org.gradle.internal.resource.ExternalResourceName; +import org.gradle.internal.resource.ExternalResourceRepository; +import org.gradle.internal.resource.LocalBinaryResource; +import org.gradle.internal.resource.local.FileResourceRepository; +import org.gradle.internal.resource.local.LocalFileStandInExternalResource; +import org.gradle.internal.resource.local.LocallyAvailableExternalResource; +import org.gradle.internal.resource.metadata.ExternalResourceMetaData; +import org.gradle.internal.resource.transfer.DefaultCacheAwareExternalResourceAccessor; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.util.ArrayList; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class GradleRepositoryAdapter extends AbstractArtifactRepository implements ResolutionAwareRepository { + + private static final Pattern URL_PATTERN = Pattern.compile( + "^(?\\S+(?:/\\S+)*)/(?\\S+)/(?\\S+)/" + + "\\2-\\3(?:-(?[^.\\s]+))?\\.(?\\S+)$"); + + public static GradleRepositoryAdapter add(RepositoryHandler handler, String name, File local, Repository repository) { + BaseRepositoryFactory factory = ReflectionUtils.get(handler, "repositoryFactory"); // We reflect here and create it manually so it DOESN'T get attached. + DefaultMavenLocalArtifactRepository maven = (DefaultMavenLocalArtifactRepository)factory.createMavenLocalRepository(); // We use maven local because it bypasses the caching and coping to .m2 + maven.setUrl(local); + maven.setName(name); + + GradleRepositoryAdapter repo; + + repo = new GradleRepositoryAdapter(repository, maven); + repo.setName(name); + handler.add(repo); + return repo; + } + + private final Repository repository; + private final DefaultMavenLocalArtifactRepository local; + private final String root; + private final LocatedArtifactCache cache; + + + // This constructor is modified via bytecode manipulation in 'build.gradle' + // DO NOT change this without modifying 'build.gradle' + // This contructor is used on Gradle 4.9 and below + private GradleRepositoryAdapter(Repository repository, DefaultMavenLocalArtifactRepository local) { + // This is replaced with a call to 'super()', with no arguments + super(null); + this.repository = repository; + this.local = local; + this.root = cleanRoot(local.getUrl()); + this.cache = new LocatedArtifactCache(new File(root)); + } + + + // This constructor is used on Gradle 4.10 and above + GradleRepositoryAdapter(ObjectFactory objectFactory, Repository repository, DefaultMavenLocalArtifactRepository local) { + super(objectFactory); + // This duplication from the above two-argument constructor is unfortunate, + // but unavoidable + this.repository = repository; + this.local = local; + this.root = cleanRoot(local.getUrl()); + this.cache = new LocatedArtifactCache(new File(root)); + } + + @Override + public String getDisplayName() { + return local.getDisplayName(); + } + + @Override + public ConfiguredModuleComponentRepository createResolver() { + MavenResolver resolver = (MavenResolver)local.createResolver(); + + GeneratingFileResourceRepository repo = new GeneratingFileResourceRepository(); + ReflectionUtils.alter(resolver, "repository", prev -> repo); // ExternalResourceResolver.repository + //ReflectionUtils.alter(resolver, "metadataSources", ); //ExternalResourceResolver.metadataSources We need to fix these from returning 'missing' + // MavenResolver -> MavenMetadataLoader -> FileCacheAwareExternalResourceAccessor -> DefaultCacheAwareExternalResourceAccessor + DefaultCacheAwareExternalResourceAccessor accessor = ReflectionUtils.get(resolver, "mavenMetaDataLoader.cacheAwareExternalResourceAccessor.delegate"); + ReflectionUtils.alter(accessor, "delegate", prev -> repo); // DefaultCacheAwareExternalResourceAccessor.delegate + ReflectionUtils.alter(accessor, "fileResourceRepository", prev -> repo); // DefaultCacheAwareExternalResourceAccessor.fileResourceRepository + ExternalResourceArtifactResolver extResolver = ReflectionUtils.invoke(resolver, ExternalResourceResolver.class, "createArtifactResolver"); //Makes the resolver and caches it. + ReflectionUtils.alter(extResolver, "repository", prev -> repo); + //File transport references, Would be better to get a reference to the transport and work from there, but don't see it stored anywhere. + ReflectionUtils.alter(resolver, "cachingResourceAccessor.this$0.repository", prev -> repo); + ReflectionUtils.alter(resolver, "cachingResourceAccessor.delegate.delegate", prev -> repo); + + return new ConfiguredModuleComponentRepository() { + private final ModuleComponentRepositoryAccess local = wrap(resolver.getLocalAccess()); + private final ModuleComponentRepositoryAccess remote = wrap(resolver.getRemoteAccess()); + @Override public String getId() { return resolver.getId(); } + @Override public String getName() { return resolver.getName(); } + @Override public ModuleComponentRepositoryAccess getLocalAccess() { return local; } + @Override public ModuleComponentRepositoryAccess getRemoteAccess() { return remote; } + @Override public Map getArtifactCache() { return resolver.getArtifactCache(); } + @Override public InstantiatingAction getComponentMetadataSupplier() { return resolver.getComponentMetadataSupplier(); } + @Override public boolean isDynamicResolveMode() { return resolver.isDynamicResolveMode(); } + @Override public boolean isLocal() { return resolver.isLocal(); } + + @Override + public void setComponentResolvers(ComponentResolvers resolver) { } + @Override + public Instantiator getComponentMetadataInstantiator() { + return resolver.getComponentMetadataInstantiator(); + } + + private ModuleComponentRepositoryAccess wrap(ModuleComponentRepositoryAccess delegate) { + return new ModuleComponentRepositoryAccess() { + @Override + public void resolveComponentMetaData(ModuleComponentIdentifier moduleComponentIdentifier, ComponentOverrideMetadata requestMetaData, BuildableModuleComponentMetaDataResolveResult result) { + delegate.resolveComponentMetaData(moduleComponentIdentifier, requestMetaData, result); + if (result.getState() == BuildableModuleComponentMetaDataResolveResult.State.Resolved) { + ModuleComponentResolveMetadata meta = result.getMetaData(); + if (meta.isMissing()) { + MutableModuleComponentResolveMetadata mutable = meta.asMutable(); + mutable.setChanging(true); + mutable.setMissing(false); + result.resolved(mutable.asImmutable()); + } + } + } + + @Override + public void resolveArtifacts(ComponentResolveMetadata component, ConfigurationMetadata variant, BuildableComponentArtifactsResolveResult result) { + delegate.resolveArtifacts(component, variant, result); + } + + @Override + public void listModuleVersions(ModuleDependencyMetadata dependency, BuildableModuleVersionListingResolveResult result) { + delegate.listModuleVersions(dependency, result); + } + + @Override + public void resolveArtifactsWithType(ComponentResolveMetadata component, ArtifactType artifactType, BuildableArtifactSetResolveResult result) { + delegate.resolveArtifactsWithType(component, artifactType, result); + } + + @Override + public void resolveArtifact(ComponentArtifactMetadata artifact, ModuleSources moduleSources, BuildableArtifactResolveResult result) { + delegate.resolveArtifact(artifact, moduleSources, result); + } + + + @Override + public MetadataFetchingCost estimateMetadataFetchingCost(ModuleComponentIdentifier moduleComponentIdentifier) { + return delegate.estimateMetadataFetchingCost(moduleComponentIdentifier); + } + }; + } + }; + } + + public RepositoryDescriptor getDescriptor() { + return new FlatDirRepositoryDescriptor("ArtifacturalRepository", new ArrayList<>()); + } + + + private static String cleanRoot(URI uri) { + String ret = uri.normalize().getPath().replace('\\', '/'); + if (!ret.endsWith("/")) ret += '/'; + return ret; + } + + private class GeneratingFileResourceRepository implements FileResourceRepository { + private final FileSystem fileSystem = FileSystems.getDefault(); + private void debug(String message) { + //System.out.println(message); + } + private void log(String message) { + System.out.println(message); + } + + @Override + public ExternalResourceRepository withProgressLogging() { + return this; + } + + @Override + public LocalBinaryResource localResource(File file) { + debug("localResource: " + file); + return null; + } + + @Override + public LocallyAvailableExternalResource resource(File file) { + debug("resource(File): " + file); + return findArtifact(file.getAbsolutePath().replace('\\', '/')); + } + + @Override + public LocallyAvailableExternalResource resource(ExternalResourceName location) { + return resource(location, false); + } + + @Override + public LocallyAvailableExternalResource resource(ExternalResourceName location, boolean revalidate) { + debug("resource(ExternalResourceName,boolean): " + location + ", " + revalidate); + return findArtifact(location.getUri().getPath().replace('\\', '/')); + } + + @Override + public LocallyAvailableExternalResource resource(File file, URI originUri, ExternalResourceMetaData originMetadata) { + debug("resource(File,URI,ExternalResourceMetaData): " + file + ", " + originUri + ", " + originMetadata); + return findArtifact(file.getAbsolutePath().replace('\\', '/')); + } + + private LocallyAvailableExternalResource findArtifact(String path) { + if (path.startsWith(root)) { + String relative = path.substring(root.length()); + debug(" Relative: " + relative); + Matcher matcher = URL_PATTERN.matcher(relative); + if (matcher.matches()) { + ArtifactIdentifier identifier = new SimpleArtifactIdentifier( + matcher.group("group").replace('/', '.'), + matcher.group("name"), + matcher.group("version"), + matcher.group("classifier"), + matcher.group("extension")); + Artifact artifact = repository.getArtifact(identifier); + return wrap(artifact, identifier); + } else if (relative.endsWith("maven-metadata.xml")) { + String tmp = relative.substring(0, relative.length() - "maven-metadata.xml".length() - 1); + int idx = tmp.lastIndexOf('/'); + if (idx != -1) { + File ret = repository.getMavenMetadata(tmp.substring(0, idx - 1), tmp.substring(idx)); + if (ret != null) { + return new LocalFileStandInExternalResource(ret, fileSystem); + } + } + } else if (relative.endsWith("/")) { + debug(" Directory listing not supported"); + } else { + log(" Matcher Failed: " + relative); + } + } else { + log("Unknown root: " + path); + } + return new LocalFileStandInExternalResource(new File(path), fileSystem); + } + + private LocallyAvailableExternalResource wrap(Artifact artifact, ArtifactIdentifier id) { + if (!artifact.isPresent()) + return new LocalFileStandInExternalResource(cache.getPath(artifact), fileSystem); + Artifact.Cached cached = artifact.optionallyCache(cache); + try { + return new LocalFileStandInExternalResource(cached.asFile(), fileSystem); + } catch (MissingArtifactException | IOException e) { + throw new RuntimeException(e); + } + } + } + + //TODO: Make this a artifact provider interface with a proper API so we dont have direct reference to GradleRepoAdapter in consumers. + public File getArtifact(ArtifactIdentifier identifier) { + Artifact art = repository.getArtifact(identifier); + if (!art.isPresent()) + return null; + + Artifact.Cached cached = art.optionallyCache(cache); + try { + return cached.asFile(); + } catch (MissingArtifactException | IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/gradlecomp/java/net/minecraftforge/artifactural/gradle/ModifierAccess.java b/src/gradlecomp/java/net/minecraftforge/artifactural/gradle/ModifierAccess.java new file mode 100644 index 0000000..fb68144 --- /dev/null +++ b/src/gradlecomp/java/net/minecraftforge/artifactural/gradle/ModifierAccess.java @@ -0,0 +1,55 @@ +/* + * 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.gradle; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +public class ModifierAccess { + private static Field MODIFIER_ACCESS = null; + private static boolean accessAttempted = false; + + public static synchronized boolean definalize(Field target) { + if ((target.getModifiers() & Modifier.FINAL) == 0) { + return true; + } + + if (MODIFIER_ACCESS == null && !accessAttempted) { + try { + final Field modifiers = Field.class.getDeclaredField("modifiers"); + modifiers.setAccessible(true); + MODIFIER_ACCESS = modifiers; + } catch (NoSuchFieldException e) { + throw new RuntimeException("Could not access Field.modifiers to definalize reflection object. Use Java 8, current version: " + System.getProperty("java.version"), e); + } + accessAttempted = true; + } + if (MODIFIER_ACCESS != null) { + try { + MODIFIER_ACCESS.setInt(target, target.getModifiers() & ~Modifier.FINAL); + } catch (IllegalArgumentException | IllegalAccessException e) { + throw new RuntimeException("Could not definalize field " + target.getDeclaringClass().getName() + "." + target.getName(), e); + } + return true; + } + return false; + } + +} diff --git a/src/gradlecomp/java/net/minecraftforge/artifactural/gradle/ReflectionUtils.java b/src/gradlecomp/java/net/minecraftforge/artifactural/gradle/ReflectionUtils.java new file mode 100644 index 0000000..4e18885 --- /dev/null +++ b/src/gradlecomp/java/net/minecraftforge/artifactural/gradle/ReflectionUtils.java @@ -0,0 +1,112 @@ +/* + * 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.gradle; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.function.UnaryOperator; + +public class ReflectionUtils { + + @SuppressWarnings("unchecked") + public static void alter(Object target, String name, UnaryOperator operator) { + try { + int idx = name.lastIndexOf('.'); + if (idx != -1) { + target = drillField(target, name.substring(0, idx)); + if (target == null) throw new IllegalStateException("Could not find field '" + name + "'"); + name = name.substring(idx + 1); + } + Field f = findField(target.getClass(), name); + if (f == null) throw new IllegalStateException("Could not find '" + name + "'"); + + T oldV = (T)f.get(target); + T newV = operator.apply(oldV); + f.set(target, newV); + + if (f.get(target) != newV) { + throw new IllegalStateException("Failed to set new value on " + f.getDeclaringClass().getName() + "." + f.getName()); + } + } catch (IllegalAccessException ex) { + throw new RuntimeException(ex); + } + } + + private static Object drillField(Object obj, String path) { + for (String name : path.split("\\.")) { + if (obj == null) return null; + Field f = findField(obj.getClass(), name); + if (f == null) return null; + try { + obj = f.get(obj); + } catch (IllegalAccessException e) { + return null; + } + } + return obj; + } + + private static Field findField(Class clazz, String name) { + while (clazz != Object.class) { + for (Field f : clazz.getDeclaredFields()) { + if (f.getName().equals(name)) { + f.setAccessible(true); + if (!ModifierAccess.definalize(f)) { + System.out.println("Could not definalize field " + f.getDeclaringClass().getName() + "." + f.getName() + " Exception ate, lets see if it works"); + } + return f; + } + } + clazz = clazz.getSuperclass(); + } + return null; + } + + /** + * Invokes a method (can be private). + */ + @SuppressWarnings("unchecked") + public static T invoke(Object target, Class type, String name, Object... args) { + try { + Method method = type.getDeclaredMethod(name); + method.setAccessible(true); + return (T) method.invoke(target, args); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + @SuppressWarnings("unchecked") + public static T get(Object target, String name) { + try { + int idx = name.lastIndexOf('.'); + if (idx != -1) { + target = drillField(target, name.substring(0, idx)); + if (target == null) throw new IllegalStateException("Could not find field '" + name + "'"); + name = name.substring(idx + 1); + } + Field f = findField(target.getClass(), name); + if (f == null) throw new IllegalStateException("Could not find '" + name + "'"); + return (T)f.get(target); + } catch (IllegalAccessException ex) { + throw new RuntimeException(ex); + } + } +} diff --git a/src/java9/com/amadornes/artifactural/gradle/ModifierAccess.java b/src/java9/com/amadornes/artifactural/gradle/ModifierAccess.java deleted file mode 100644 index 69e7dff..0000000 --- a/src/java9/com/amadornes/artifactural/gradle/ModifierAccess.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Artifactural - * Copyright (c) 2018. - * - * 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 com.amadornes.artifactural.gradle; - -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodHandles.Lookup; -import java.lang.invoke.VarHandle; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; - -public class ModifierAccess { - private static VarHandle MODIFIER_ACCESS = null; - private static boolean accessAttempted = false; - - public static synchronized boolean definalize(Field target) { - if ((target.getModifiers() & Modifier.FINAL) == 0) { - return true; - } - - if (MODIFIER_ACCESS == null && !accessAttempted) { - try { - Lookup lookup = MethodHandles.privateLookupIn(Field.class, MethodHandles.lookup()); - MODIFIER_ACCESS = lookup.findVarHandle(Field.class, "modifiers", int.class); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new RuntimeException("Could not access Field.modifiers to definalize reflection object. Use Java 8, current version: " + System.getProperty("java.version"), e); - } - accessAttempted = true; - } - if (MODIFIER_ACCESS != null) { - try { - MODIFIER_ACCESS.set(target, target.getModifiers() & ~Modifier.FINAL); - } catch (IllegalArgumentException e) { - throw new RuntimeException("Could not definalize field " + target.getDeclaringClass().getName() + "." + target.getName(), e); - } - return true; - } - return false; - } - -} diff --git a/src/java9/net/minecraftforge/artifactural/gradle/ModifierAccess.java b/src/java9/net/minecraftforge/artifactural/gradle/ModifierAccess.java new file mode 100644 index 0000000..cf6f65f --- /dev/null +++ b/src/java9/net/minecraftforge/artifactural/gradle/ModifierAccess.java @@ -0,0 +1,57 @@ +/* + * Artifactural + * Copyright (c) 2018. + * + * 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.gradle; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.VarHandle; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +public class ModifierAccess { + private static VarHandle MODIFIER_ACCESS = null; + private static boolean accessAttempted = false; + + public static synchronized boolean definalize(Field target) { + if ((target.getModifiers() & Modifier.FINAL) == 0) { + return true; + } + + if (MODIFIER_ACCESS == null && !accessAttempted) { + try { + Lookup lookup = MethodHandles.privateLookupIn(Field.class, MethodHandles.lookup()); + MODIFIER_ACCESS = lookup.findVarHandle(Field.class, "modifiers", int.class); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException("Could not access Field.modifiers to definalize reflection object. Use Java 8, current version: " + System.getProperty("java.version"), e); + } + accessAttempted = true; + } + if (MODIFIER_ACCESS != null) { + try { + MODIFIER_ACCESS.set(target, target.getModifiers() & ~Modifier.FINAL); + } catch (IllegalArgumentException e) { + throw new RuntimeException("Could not definalize field " + target.getDeclaringClass().getName() + "." + target.getName(), e); + } + return true; + } + return false; + } + +} diff --git a/src/shared/java/com/amadornes/artifactural/base/artifact/ArtifactBase.java b/src/shared/java/com/amadornes/artifactural/base/artifact/ArtifactBase.java deleted file mode 100644 index 0eaa66d..0000000 --- a/src/shared/java/com/amadornes/artifactural/base/artifact/ArtifactBase.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Artifactural - * Copyright (c) 2018. - * - * 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 com.amadornes.artifactural.base.artifact; - -import com.amadornes.artifactural.api.artifact.Artifact; -import com.amadornes.artifactural.api.artifact.ArtifactMetadata; -import com.amadornes.artifactural.api.artifact.ArtifactType; -import com.amadornes.artifactural.api.cache.ArtifactCache; -import com.amadornes.artifactural.api.artifact.ArtifactIdentifier; -import com.amadornes.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/com/amadornes/artifactural/base/artifact/SimpleArtifactIdentifier.java b/src/shared/java/com/amadornes/artifactural/base/artifact/SimpleArtifactIdentifier.java deleted file mode 100644 index f0a4189..0000000 --- a/src/shared/java/com/amadornes/artifactural/base/artifact/SimpleArtifactIdentifier.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Artifactural - * Copyright (c) 2018. - * - * 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 com.amadornes.artifactural.base.artifact; - -import com.amadornes.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/com/amadornes/artifactural/base/artifact/SimpleArtifactMetadata.java b/src/shared/java/com/amadornes/artifactural/base/artifact/SimpleArtifactMetadata.java deleted file mode 100644 index a42894b..0000000 --- a/src/shared/java/com/amadornes/artifactural/base/artifact/SimpleArtifactMetadata.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Artifactural - * Copyright (c) 2018. - * - * 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 com.amadornes.artifactural.base.artifact; - -import com.amadornes.artifactural.api.artifact.ArtifactMetadata; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.LinkedList; -import java.util.List; -import java.util.stream.Collectors; - -public class SimpleArtifactMetadata implements ArtifactMetadata { - - private final List 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/com/amadornes/artifactural/base/artifact/StreamableArtifact.java b/src/shared/java/com/amadornes/artifactural/base/artifact/StreamableArtifact.java deleted file mode 100644 index e43b7c9..0000000 --- a/src/shared/java/com/amadornes/artifactural/base/artifact/StreamableArtifact.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Artifactural - * Copyright (c) 2018. - * - * 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 com.amadornes.artifactural.base.artifact; - -import com.amadornes.artifactural.api.artifact.Artifact; -import com.amadornes.artifactural.api.artifact.ArtifactIdentifier; -import com.amadornes.artifactural.api.artifact.ArtifactMetadata; -import com.amadornes.artifactural.api.artifact.ArtifactType; -import com.amadornes.artifactural.api.artifact.MissingArtifactException; -import com.amadornes.artifactural.api.artifact.Streamable; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; - -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/com/amadornes/artifactural/base/cache/ArtifactCacheBase.java b/src/shared/java/com/amadornes/artifactural/base/cache/ArtifactCacheBase.java deleted file mode 100644 index 08a2840..0000000 --- a/src/shared/java/com/amadornes/artifactural/base/cache/ArtifactCacheBase.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Artifactural - * Copyright (c) 2018. - * - * 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 com.amadornes.artifactural.base.cache; - -import com.amadornes.artifactural.api.artifact.Artifact; -import com.amadornes.artifactural.api.artifact.ArtifactIdentifier; -import com.amadornes.artifactural.api.artifact.ArtifactMetadata; -import com.amadornes.artifactural.api.artifact.ArtifactType; -import com.amadornes.artifactural.api.artifact.MissingArtifactException; -import com.amadornes.artifactural.api.cache.ArtifactCache; -import com.amadornes.artifactural.api.transform.ArtifactTransformer; -import com.amadornes.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/com/amadornes/artifactural/base/cache/LocatedArtifactCache.java b/src/shared/java/com/amadornes/artifactural/base/cache/LocatedArtifactCache.java deleted file mode 100644 index 0aeab9c..0000000 --- a/src/shared/java/com/amadornes/artifactural/base/cache/LocatedArtifactCache.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Artifactural - * Copyright (c) 2018. - * - * 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 com.amadornes.artifactural.base.cache; - -import com.amadornes.artifactural.api.artifact.Artifact; -import com.amadornes.artifactural.api.artifact.ArtifactIdentifier; -import com.amadornes.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 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 Entry entry(K key, V value) { - return new AbstractMap.SimpleEntry<>(key, value); - } - - @Override - public String toString() { - return "LocatedArtifactCache(" + path + ")"; - } - -} diff --git a/src/shared/java/com/amadornes/artifactural/base/repository/ArtifactProviderBuilder.java b/src/shared/java/com/amadornes/artifactural/base/repository/ArtifactProviderBuilder.java deleted file mode 100644 index 638ed9d..0000000 --- a/src/shared/java/com/amadornes/artifactural/base/repository/ArtifactProviderBuilder.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Artifactural - * Copyright (c) 2018. - * - * 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 com.amadornes.artifactural.base.repository; - -import com.amadornes.artifactural.api.artifact.Artifact; -import com.amadornes.artifactural.api.repository.ArtifactProvider; - -import java.util.HashSet; -import java.util.Set; -import java.util.function.Function; -import java.util.function.Predicate; - -public class ArtifactProviderBuilder implements ArtifactProvider.Builder { - - public static ArtifactProviderBuilder begin(Class type) { - return new ArtifactProviderBuilder<>(Function.identity()); - } - - private final Function mapper; - private final Set> filters = new HashSet<>(); - - private ArtifactProviderBuilder(Function mapper) { - this.mapper = mapper; - } - - @Override - public ArtifactProvider.Builder filter(Predicate filter) { - filters.add(filter); - return this; - } - - @Override - public ArtifactProvider.Builder mapInfo(Function 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 filter : filters) { - if (!filter.test(localInfo)) { - return null; - } - } - return mapper.apply(localInfo); - }); - } - - @Override - public ArtifactProvider.Builder.Complete provide(ArtifactProvider provider) { - return new Complete<>(mapper).provide(provider); - } - - private static class Complete implements ArtifactProvider.Builder.Complete { - - private final Set> providers = new HashSet<>(); - private final Function mapper; - - private Complete(Function mapper) { - this.mapper = mapper; - } - - @Override - public Builder.Complete provide(ArtifactProvider 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 provider : providers) { - Artifact artifact = provider.getArtifact(localInfo); - if (artifact.isPresent()) return artifact; - } - return Artifact.none(); - } - - } - -} diff --git a/src/shared/java/com/amadornes/artifactural/base/repository/SimpleRepository.java b/src/shared/java/com/amadornes/artifactural/base/repository/SimpleRepository.java deleted file mode 100644 index 060780a..0000000 --- a/src/shared/java/com/amadornes/artifactural/base/repository/SimpleRepository.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Artifactural - * Copyright (c) 2018. - * - * 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 com.amadornes.artifactural.base.repository; - -import com.amadornes.artifactural.api.artifact.Artifact; -import com.amadornes.artifactural.api.artifact.ArtifactIdentifier; -import com.amadornes.artifactural.api.repository.ArtifactProvider; -import com.amadornes.artifactural.api.repository.Repository; - -public class SimpleRepository implements Repository { - - public static Repository of(ArtifactProvider provider) { - return new SimpleRepository(provider); - } - - private final ArtifactProvider provider; - - private SimpleRepository(ArtifactProvider provider) { - this.provider = provider; - } - - @Override - public Artifact getArtifact(ArtifactIdentifier identifier) { - return provider.getArtifact(identifier); - } - -} diff --git a/src/shared/java/com/amadornes/artifactural/base/util/HashFunction.java b/src/shared/java/com/amadornes/artifactural/base/util/HashFunction.java deleted file mode 100644 index 9fe4ea9..0000000 --- a/src/shared/java/com/amadornes/artifactural/base/util/HashFunction.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Artifactural - * Copyright (c) 2018. - * - * 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 com.amadornes.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 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/com/amadornes/artifactural/base/util/PatternReplace.java b/src/shared/java/com/amadornes/artifactural/base/util/PatternReplace.java deleted file mode 100644 index e81bd36..0000000 --- a/src/shared/java/com/amadornes/artifactural/base/util/PatternReplace.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Artifactural - * Copyright (c) 2018. - * - * 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 com.amadornes.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 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 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; - } - } -} 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 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 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 Entry 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 implements ArtifactProvider.Builder { + + public static ArtifactProviderBuilder begin(Class type) { + return new ArtifactProviderBuilder<>(Function.identity()); + } + + private final Function mapper; + private final Set> filters = new HashSet<>(); + + private ArtifactProviderBuilder(Function mapper) { + this.mapper = mapper; + } + + @Override + public ArtifactProvider.Builder filter(Predicate filter) { + filters.add(filter); + return this; + } + + @Override + public ArtifactProvider.Builder mapInfo(Function 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 filter : filters) { + if (!filter.test(localInfo)) { + return null; + } + } + return mapper.apply(localInfo); + }); + } + + @Override + public ArtifactProvider.Builder.Complete provide(ArtifactProvider provider) { + return new Complete<>(mapper).provide(provider); + } + + private static class Complete implements ArtifactProvider.Builder.Complete { + + private final Set> providers = new HashSet<>(); + private final Function mapper; + + private Complete(Function mapper) { + this.mapper = mapper; + } + + @Override + public Builder.Complete provide(ArtifactProvider 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 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 provider) { + return new SimpleRepository(provider); + } + + private final ArtifactProvider provider; + + private SimpleRepository(ArtifactProvider 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 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 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 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; + } + } +} -- cgit