diff options
author | LexManos <LexManos@gmail.com> | 2021-01-17 17:35:01 -0800 |
---|---|---|
committer | LexManos <LexManos@gmail.com> | 2021-01-17 17:40:39 -0800 |
commit | 4089917696fffbd4b818fb90958d20f0714f93fb (patch) | |
tree | 1f5de4972d9afe0328d7d0ce929aac06c3bca15e /src/gradlecomp/java/net/minecraftforge | |
parent | 631cd05e726092c51e95b52bb8a8bb6a2ae2cc42 (diff) | |
download | Artifactural-4089917696fffbd4b818fb90958d20f0714f93fb.tar.gz Artifactural-4089917696fffbd4b818fb90958d20f0714f93fb.tar.bz2 Artifactural-4089917696fffbd4b818fb90958d20f0714f93fb.zip |
Move to net.minecraftforge package, and update license headers.
Diffstat (limited to 'src/gradlecomp/java/net/minecraftforge')
5 files changed, 669 insertions, 0 deletions
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<String, CompletableFuture<Set<File>>> 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<File> 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<DependencyArtifact> 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<Set<File>> 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<File> 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<File> 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<File> 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<File> 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( + "^(?<group>\\S+(?:/\\S+)*)/(?<name>\\S+)/(?<version>\\S+)/" + + "\\2-\\3(?:-(?<classifier>[^.\\s]+))?\\.(?<extension>\\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<ComponentArtifactIdentifier, ResolvableArtifact> getArtifactCache() { return resolver.getArtifactCache(); } + @Override public InstantiatingAction<ComponentMetadataSupplierDetails> 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 <T> void alter(Object target, String name, UnaryOperator<T> 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> 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> 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); + } + } +} |