You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2017/12/20 00:56:40 UTC
[43/49] groovy git commit: Move source files to proper packages
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/cli/TypedOption.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/cli/TypedOption.java b/src/main/groovy/groovy/cli/TypedOption.java
new file mode 100644
index 0000000..e669324
--- /dev/null
+++ b/src/main/groovy/groovy/cli/TypedOption.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package groovy.cli;
+
+import java.util.HashMap;
+
+public class TypedOption<T> extends HashMap<String, T> {
+ public T defaultValue() {
+ return (T) super.get("defaultValue");
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/cli/Unparsed.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/cli/Unparsed.java b/src/main/groovy/groovy/cli/Unparsed.java
new file mode 100644
index 0000000..a741413
--- /dev/null
+++ b/src/main/groovy/groovy/cli/Unparsed.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package groovy.cli;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that a method or property will contain the remaining arguments.
+ */
+@java.lang.annotation.Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.FIELD})
+public @interface Unparsed {
+ /**
+ * The description for the remaining non-option arguments
+ *
+ * @return the description for the remaining non-option arguments
+ */
+ String description() default "ARGUMENTS";
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/cli/UnparsedField.groovy
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/cli/UnparsedField.groovy b/src/main/groovy/groovy/cli/UnparsedField.groovy
new file mode 100644
index 0000000..b185431
--- /dev/null
+++ b/src/main/groovy/groovy/cli/UnparsedField.groovy
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package groovy.cli
+
+import groovy.transform.AnnotationCollector
+import groovy.transform.Field
+
+@Unparsed
+@Field
+@AnnotationCollector
+@interface UnparsedField { }
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/grape/GrabAnnotationTransformation.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/grape/GrabAnnotationTransformation.java b/src/main/groovy/groovy/grape/GrabAnnotationTransformation.java
new file mode 100644
index 0000000..dd55ff9
--- /dev/null
+++ b/src/main/groovy/groovy/grape/GrabAnnotationTransformation.java
@@ -0,0 +1,639 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package groovy.grape;
+
+import groovy.lang.Grab;
+import groovy.lang.GrabConfig;
+import groovy.lang.GrabExclude;
+import groovy.lang.GrabResolver;
+import groovy.lang.Grapes;
+import groovy.transform.CompilationUnitAware;
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.AnnotatedNode;
+import org.codehaus.groovy.ast.AnnotationNode;
+import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ImportNode;
+import org.codehaus.groovy.ast.ModuleNode;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.ListExpression;
+import org.codehaus.groovy.ast.expr.MapExpression;
+import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.CompilePhase;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.control.io.StringReaderSource;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+import org.codehaus.groovy.tools.GrapeUtil;
+import org.codehaus.groovy.transform.ASTTransformation;
+import org.codehaus.groovy.transform.ASTTransformationVisitor;
+import org.codehaus.groovy.transform.AbstractASTTransformation;
+import org.codehaus.groovy.transform.GroovyASTTransformation;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static org.codehaus.groovy.ast.tools.GeneralUtils.args;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.callThisX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.callX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.constX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.eqX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.ifS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
+import static org.codehaus.groovy.transform.AbstractASTTransformation.getMemberStringValue;
+
+/**
+ * Transformation for declarative dependency management.
+ */
+@GroovyASTTransformation(phase=CompilePhase.CONVERSION)
+public class GrabAnnotationTransformation extends ClassCodeVisitorSupport implements ASTTransformation, CompilationUnitAware {
+ private static final String GRAB_CLASS_NAME = Grab.class.getName();
+ private static final String GRAB_DOT_NAME = GRAB_CLASS_NAME.substring(GRAB_CLASS_NAME.lastIndexOf("."));
+ private static final String GRAB_SHORT_NAME = GRAB_DOT_NAME.substring(1);
+
+ private static final String GRABEXCLUDE_CLASS_NAME = GrabExclude.class.getName();
+ private static final String GRABEXCLUDE_DOT_NAME = dotName(GRABEXCLUDE_CLASS_NAME);
+ private static final String GRABEXCLUDE_SHORT_NAME = shortName(GRABEXCLUDE_DOT_NAME);
+
+ private static final String GRABCONFIG_CLASS_NAME = GrabConfig.class.getName();
+ private static final String GRABCONFIG_DOT_NAME = dotName(GRABCONFIG_CLASS_NAME);
+ private static final String GRABCONFIG_SHORT_NAME = shortName(GRABCONFIG_DOT_NAME);
+
+ private static final String GRAPES_CLASS_NAME = Grapes.class.getName();
+ private static final String GRAPES_DOT_NAME = dotName(GRAPES_CLASS_NAME);
+ private static final String GRAPES_SHORT_NAME = shortName(GRAPES_DOT_NAME);
+
+ private static final String GRABRESOLVER_CLASS_NAME = GrabResolver.class.getName();
+ private static final String GRABRESOLVER_DOT_NAME = dotName(GRABRESOLVER_CLASS_NAME);
+ private static final String GRABRESOLVER_SHORT_NAME = shortName(GRABRESOLVER_DOT_NAME);
+
+ private static final ClassNode THREAD_CLASSNODE = ClassHelper.make(Thread.class);
+ private static final ClassNode SYSTEM_CLASSNODE = ClassHelper.make(System.class);
+
+ private static final List<String> GRABEXCLUDE_REQUIRED = Arrays.asList("group", "module");
+ private static final List<String> GRABRESOLVER_REQUIRED = Arrays.asList("name", "root");
+ private static final List<String> GRAB_REQUIRED = Arrays.asList("group", "module", "version");
+ private static final List<String> GRAB_OPTIONAL = Arrays.asList("classifier", "transitive", "conf", "ext", "type", "changing", "force", "initClass");
+ private static final List<String> GRAB_BOOLEAN = Arrays.asList("transitive", "changing", "force", "initClass");
+ private static final Collection<String> GRAB_ALL = DefaultGroovyMethods.plus(GRAB_REQUIRED, GRAB_OPTIONAL);
+ private static final Pattern IVY_PATTERN = Pattern.compile("([a-zA-Z0-9-/._+=]+)#([a-zA-Z0-9-/._+=]+)(;([a-zA-Z0-9-/.\\(\\)\\[\\]\\{\\}_+=,:@][a-zA-Z0-9-/.\\(\\)\\]\\{\\}_+=,:@]*))?(\\[([a-zA-Z0-9-/._+=,]*)\\])?");
+ private static final Pattern ATTRIBUTES_PATTERN = Pattern.compile("(.*;|^)([a-zA-Z0-9]+)=([a-zA-Z0-9.*\\[\\]\\-\\(\\),]*)$");
+
+ private static final String AUTO_DOWNLOAD_SETTING = Grape.AUTO_DOWNLOAD_SETTING;
+ private static final String DISABLE_CHECKSUMS_SETTING = Grape.DISABLE_CHECKSUMS_SETTING;
+ private static final String SYSTEM_PROPERTIES_SETTING = Grape.SYSTEM_PROPERTIES_SETTING;
+
+ private static String dotName(String className) {
+ return className.substring(className.lastIndexOf("."));
+ }
+
+ private static String shortName(String className) {
+ return className.substring(1);
+ }
+
+ boolean allowShortGrab;
+ Set<String> grabAliases;
+ List<AnnotationNode> grabAnnotations;
+
+ boolean allowShortGrabExcludes;
+ Set<String> grabExcludeAliases;
+ List<AnnotationNode> grabExcludeAnnotations;
+
+ boolean allowShortGrabConfig;
+ Set<String> grabConfigAliases;
+ List<AnnotationNode> grabConfigAnnotations;
+
+ boolean allowShortGrapes;
+ Set<String> grapesAliases;
+ List<AnnotationNode> grapesAnnotations;
+
+ boolean allowShortGrabResolver;
+ Set<String> grabResolverAliases;
+ List<AnnotationNode> grabResolverAnnotations;
+
+ CompilationUnit compilationUnit;
+ SourceUnit sourceUnit;
+ ClassLoader loader;
+ boolean initContextClassLoader;
+ Boolean autoDownload;
+ Boolean disableChecksums;
+ Map<String, String> systemProperties;
+
+ public SourceUnit getSourceUnit() {
+ return sourceUnit;
+ }
+
+ public void setCompilationUnit(final CompilationUnit compilationUnit) {
+ this.compilationUnit = compilationUnit;
+ }
+
+ public void visit(ASTNode[] nodes, SourceUnit source) {
+ sourceUnit = source;
+ loader = null;
+ initContextClassLoader = false;
+
+ ModuleNode mn = (ModuleNode) nodes[0];
+
+ allowShortGrab = true;
+ allowShortGrabExcludes = true;
+ allowShortGrabConfig = true;
+ allowShortGrapes = true;
+ allowShortGrabResolver = true;
+ grabAliases = new HashSet<String>();
+ grabExcludeAliases = new HashSet<String>();
+ grabConfigAliases = new HashSet<String>();
+ grapesAliases = new HashSet<String>();
+ grabResolverAliases = new HashSet<String>();
+ for (ImportNode im : mn.getImports()) {
+ String alias = im.getAlias();
+ String className = im.getClassName();
+ if ((className.endsWith(GRAB_DOT_NAME) && ((alias == null) || (alias.length() == 0)))
+ || (GRAB_CLASS_NAME.equals(alias)))
+ {
+ allowShortGrab = false;
+ } else if (GRAB_CLASS_NAME.equals(className)) {
+ grabAliases.add(im.getAlias());
+ }
+ if ((className.endsWith(GRAPES_DOT_NAME) && ((alias == null) || (alias.length() == 0)))
+ || (GRAPES_CLASS_NAME.equals(alias)))
+ {
+ allowShortGrapes = false;
+ } else if (GRAPES_CLASS_NAME.equals(className)) {
+ grapesAliases.add(im.getAlias());
+ }
+ if ((className.endsWith(GRABRESOLVER_DOT_NAME) && ((alias == null) || (alias.length() == 0)))
+ || (GRABRESOLVER_CLASS_NAME.equals(alias)))
+ {
+ allowShortGrabResolver = false;
+ } else if (GRABRESOLVER_CLASS_NAME.equals(className)) {
+ grabResolverAliases.add(im.getAlias());
+ }
+ }
+
+ List<Map<String,Object>> grabMaps = new ArrayList<Map<String,Object>>();
+ List<Map<String,Object>> grabMapsInit = new ArrayList<Map<String,Object>>();
+ List<Map<String,Object>> grabExcludeMaps = new ArrayList<Map<String,Object>>();
+
+ for (ClassNode classNode : sourceUnit.getAST().getClasses()) {
+ grabAnnotations = new ArrayList<AnnotationNode>();
+ grabExcludeAnnotations = new ArrayList<AnnotationNode>();
+ grabConfigAnnotations = new ArrayList<AnnotationNode>();
+ grapesAnnotations = new ArrayList<AnnotationNode>();
+ grabResolverAnnotations = new ArrayList<AnnotationNode>();
+
+ visitClass(classNode);
+
+ ClassNode grapeClassNode = ClassHelper.make(Grape.class);
+
+ List<Statement> grabResolverInitializers = new ArrayList<Statement>();
+
+ if (!grapesAnnotations.isEmpty()) {
+ for (AnnotationNode node : grapesAnnotations) {
+ Expression init = node.getMember("initClass");
+ Expression value = node.getMember("value");
+ if (value instanceof ListExpression) {
+ for (Object o : ((ListExpression)value).getExpressions()) {
+ if (o instanceof ConstantExpression) {
+ extractGrab(init, (ConstantExpression) o);
+ }
+ }
+ } else if (value instanceof ConstantExpression) {
+ extractGrab(init, (ConstantExpression) value);
+ }
+ // don't worry if it's not a ListExpression, or AnnotationConstant, etc.
+ // the rest of GroovyC will flag it as a syntax error later, so we don't
+ // need to raise the error ourselves
+ }
+ }
+
+ if (!grabResolverAnnotations.isEmpty()) {
+ grabResolverAnnotationLoop:
+ for (AnnotationNode node : grabResolverAnnotations) {
+ Map<String, Object> grabResolverMap = new HashMap<String, Object>();
+ String sval = getMemberStringValue(node, "value");
+ if (sval != null && sval.length() > 0) {
+ for (String s : GRABRESOLVER_REQUIRED) {
+ String mval = getMemberStringValue(node, s);
+ if (mval != null && mval.isEmpty()) mval = null;
+ if (mval != null) {
+ addError("The attribute \"" + s + "\" conflicts with attribute 'value' in @" + node.getClassNode().getNameWithoutPackage() + " annotations", node);
+ continue grabResolverAnnotationLoop;
+ }
+ }
+ grabResolverMap.put("name", sval);
+ grabResolverMap.put("root", sval);
+ } else {
+ for (String s : GRABRESOLVER_REQUIRED) {
+ String mval = getMemberStringValue(node, s);
+ Expression member = node.getMember(s);
+ if (member == null || (mval != null && mval.isEmpty())) {
+ addError("The missing attribute \"" + s + "\" is required in @" + node.getClassNode().getNameWithoutPackage() + " annotations", node);
+ continue grabResolverAnnotationLoop;
+ } else if (mval == null) {
+ addError("Attribute \"" + s + "\" has value " + member.getText() + " but should be an inline constant String in @" + node.getClassNode().getNameWithoutPackage() + " annotations", node);
+ continue grabResolverAnnotationLoop;
+ }
+ grabResolverMap.put(s, mval);
+ }
+ }
+
+ // If no scheme is specified for the repository root,
+ // then turn it into a URI relative to that of the source file.
+ String root = (String) grabResolverMap.get("root");
+ if (root != null && !root.contains(":")) {
+ URI sourceURI = null;
+ // Since we use the data: scheme for StringReaderSources (which are fairly common)
+ // and those are not hierarchical we can't use them for making an absolute URI.
+ if (!(getSourceUnit().getSource() instanceof StringReaderSource)) {
+ // Otherwise let's trust the source to know where it is from.
+ // And actually InputStreamReaderSource doesn't know what to do and so returns null.
+ sourceURI = getSourceUnit().getSource().getURI();
+ }
+ // If source doesn't know how to get a reference to itself,
+ // then let's use the current working directory, since the repo can be relative to that.
+ if (sourceURI == null) {
+ sourceURI = new File(".").toURI();
+ }
+ try {
+ URI rootURI = sourceURI.resolve(new URI(root));
+ grabResolverMap.put("root", rootURI.toString());
+ } catch (URISyntaxException e) {
+ // We'll be silent here.
+ // If the URI scheme is unknown or not hierarchical, then we just can't help them and shouldn't cause any trouble either.
+ // addError("Attribute \"root\" has value '" + root + "' which can't be turned into a valid URI relative to it's source '" + getSourceUnit().getName() + "' @" + node.getClassNode().getNameWithoutPackage() + " annotations", node);
+ }
+ }
+
+ Grape.addResolver(grabResolverMap);
+ addGrabResolverAsStaticInitIfNeeded(grapeClassNode, node, grabResolverInitializers, grabResolverMap);
+ }
+ }
+
+ if (!grabConfigAnnotations.isEmpty()) {
+ for (AnnotationNode node : grabConfigAnnotations) {
+ checkForClassLoader(node);
+ checkForInitContextClassLoader(node);
+ checkForAutoDownload(node);
+ checkForSystemProperties(node);
+ checkForDisableChecksums(node);
+ }
+ addInitContextClassLoaderIfNeeded(classNode);
+ }
+
+ if (!grabExcludeAnnotations.isEmpty()) {
+ grabExcludeAnnotationLoop:
+ for (AnnotationNode node : grabExcludeAnnotations) {
+ Map<String, Object> grabExcludeMap = new HashMap<String, Object>();
+ checkForConvenienceForm(node, true);
+ for (String s : GRABEXCLUDE_REQUIRED) {
+ Expression member = node.getMember(s);
+ if (member == null) {
+ addError("The missing attribute \"" + s + "\" is required in @" + node.getClassNode().getNameWithoutPackage() + " annotations", node);
+ continue grabExcludeAnnotationLoop;
+ } else if (member != null && !(member instanceof ConstantExpression)) {
+ addError("Attribute \"" + s + "\" has value " + member.getText() + " but should be an inline constant in @" + node.getClassNode().getNameWithoutPackage() + " annotations", node);
+ continue grabExcludeAnnotationLoop;
+ }
+ grabExcludeMap.put(s, ((ConstantExpression)member).getValue());
+ }
+ grabExcludeMaps.add(grabExcludeMap);
+ }
+ }
+
+ if (!grabAnnotations.isEmpty()) {
+ grabAnnotationLoop:
+ for (AnnotationNode node : grabAnnotations) {
+ Map<String, Object> grabMap = new HashMap<String, Object>();
+ checkForConvenienceForm(node, false);
+ for (String s : GRAB_ALL) {
+ Expression member = node.getMember(s);
+ String mval = getMemberStringValue(node, s);
+ if (mval != null && mval.isEmpty()) member = null;
+ if (member == null && !GRAB_OPTIONAL.contains(s)) {
+ addError("The missing attribute \"" + s + "\" is required in @" + node.getClassNode().getNameWithoutPackage() + " annotations", node);
+ continue grabAnnotationLoop;
+ } else if (member != null && !(member instanceof ConstantExpression)) {
+ addError("Attribute \"" + s + "\" has value " + member.getText() + " but should be an inline constant in @" + node.getClassNode().getNameWithoutPackage() + " annotations", node);
+ continue grabAnnotationLoop;
+ }
+ if (node.getMember(s) != null) {
+ grabMap.put(s, ((ConstantExpression)member).getValue());
+ }
+ }
+ grabMaps.add(grabMap);
+ if ((node.getMember("initClass") == null) || (node.getMember("initClass") == ConstantExpression.TRUE)) {
+ grabMapsInit.add(grabMap);
+ }
+ }
+ callGrabAsStaticInitIfNeeded(classNode, grapeClassNode, grabMapsInit, grabExcludeMaps);
+ }
+
+ if (!grabResolverInitializers.isEmpty()) {
+ classNode.addStaticInitializerStatements(grabResolverInitializers, true);
+ }
+ }
+
+ if (!grabMaps.isEmpty()) {
+ Map<String, Object> basicArgs = new HashMap<String, Object>();
+ basicArgs.put("classLoader", loader != null ? loader : sourceUnit.getClassLoader());
+ if (!grabExcludeMaps.isEmpty()) basicArgs.put("excludes", grabExcludeMaps);
+ if (autoDownload != null) basicArgs.put(AUTO_DOWNLOAD_SETTING, autoDownload);
+ if (disableChecksums != null) basicArgs.put(DISABLE_CHECKSUMS_SETTING, disableChecksums);
+ if (systemProperties != null) basicArgs.put(SYSTEM_PROPERTIES_SETTING, systemProperties);
+
+ try {
+ Grape.grab(basicArgs, grabMaps.toArray(new Map[grabMaps.size()]));
+ // grab may have added more transformations through new URLs added to classpath, so do one more scan
+ if (compilationUnit!=null) {
+ ASTTransformationVisitor.addGlobalTransformsAfterGrab(compilationUnit.getASTTransformationsContext());
+ }
+ } catch (RuntimeException re) {
+ // Decided against syntax exception since this is not a syntax error.
+ // The down side is we lose line number information for the offending
+ // @Grab annotation.
+ source.addException(re);
+ }
+ }
+ }
+
+ private void callGrabAsStaticInitIfNeeded(ClassNode classNode, ClassNode grapeClassNode, List<Map<String,Object>> grabMapsInit, List<Map<String, Object>> grabExcludeMaps) {
+ List<Statement> grabInitializers = new ArrayList<Statement>();
+ MapExpression basicArgs = new MapExpression();
+ if (autoDownload != null) {
+ basicArgs.addMapEntryExpression(constX(AUTO_DOWNLOAD_SETTING), constX(autoDownload));
+ }
+
+ if (disableChecksums != null) {
+ basicArgs.addMapEntryExpression(constX(DISABLE_CHECKSUMS_SETTING), constX(disableChecksums));
+ }
+
+ if (systemProperties != null && !systemProperties.isEmpty()) {
+ BlockStatement block = new BlockStatement();
+ for(Map.Entry e : systemProperties.entrySet()) {
+ block.addStatement(stmt(callX(SYSTEM_CLASSNODE, "setProperty", args(constX(e.getKey()), constX(e.getValue())))));
+ }
+ StaticMethodCallExpression enabled = callX(SYSTEM_CLASSNODE, "getProperty", args(constX("groovy.grape.enable"), constX("true")));
+ grabInitializers.add(ifS(eqX(enabled, constX("true")), block));
+ }
+
+ if (!grabExcludeMaps.isEmpty()) {
+ ListExpression list = new ListExpression();
+ for (Map<String, Object> map : grabExcludeMaps) {
+ Set<Map.Entry<String, Object>> entries = map.entrySet();
+ MapExpression inner = new MapExpression();
+ for (Map.Entry<String, Object> entry : entries) {
+ inner.addMapEntryExpression(constX(entry.getKey()), constX(entry.getValue()));
+ }
+ list.addExpression(inner);
+ }
+ basicArgs.addMapEntryExpression(constX("excludes"), list);
+ }
+
+ List<Expression> argList = new ArrayList<Expression>();
+ argList.add(basicArgs);
+ if (grabMapsInit.isEmpty()) return;
+ for (Map<String, Object> grabMap : grabMapsInit) {
+ // add Grape.grab(excludeArgs, [group:group, module:module, version:version, classifier:classifier])
+ // or Grape.grab([group:group, module:module, version:version, classifier:classifier])
+ MapExpression dependencyArg = new MapExpression();
+ for (String s : GRAB_REQUIRED) {
+ dependencyArg.addMapEntryExpression(constX(s), constX(grabMap.get(s)));
+ }
+ for (String s : GRAB_OPTIONAL) {
+ if (grabMap.containsKey(s))
+ dependencyArg.addMapEntryExpression(constX(s), constX(grabMap.get(s)));
+ }
+ argList.add(dependencyArg);
+ }
+ grabInitializers.add(stmt(callX(grapeClassNode, "grab", args(argList))));
+
+ // insert at beginning so we have the classloader set up before the class is called
+ classNode.addStaticInitializerStatements(grabInitializers, true);
+ }
+
+ private static void addGrabResolverAsStaticInitIfNeeded(ClassNode grapeClassNode, AnnotationNode node,
+ List<Statement> grabResolverInitializers, Map<String, Object> grabResolverMap) {
+ if ((node.getMember("initClass") == null)
+ || (node.getMember("initClass") == ConstantExpression.TRUE))
+ {
+ MapExpression resolverArgs = new MapExpression();
+ for (Map.Entry<String, Object> next : grabResolverMap.entrySet()) {
+ resolverArgs.addMapEntryExpression(constX(next.getKey()), constX(next.getValue()));
+ }
+ grabResolverInitializers.add(stmt(callX(grapeClassNode, "addResolver", args(resolverArgs))));
+ }
+ }
+
+ private void addInitContextClassLoaderIfNeeded(ClassNode classNode) {
+ if (initContextClassLoader) {
+ Statement initStatement = stmt(callX(
+ callX(THREAD_CLASSNODE, "currentThread"),
+ "setContextClassLoader",
+ callX(callThisX("getClass"), "getClassLoader")
+ )
+ );
+ classNode.addObjectInitializerStatements(initStatement);
+ }
+ }
+
+ private void checkForClassLoader(AnnotationNode node) {
+ Object val = node.getMember("systemClassLoader");
+ if (val == null || !(val instanceof ConstantExpression)) return;
+ Object systemClassLoaderObject = ((ConstantExpression)val).getValue();
+ if (!(systemClassLoaderObject instanceof Boolean)) return;
+ Boolean systemClassLoader = (Boolean) systemClassLoaderObject;
+ if (systemClassLoader) loader = ClassLoader.getSystemClassLoader();
+ }
+
+ private void checkForInitContextClassLoader(AnnotationNode node) {
+ Object val = node.getMember("initContextClassLoader");
+ if (val == null || !(val instanceof ConstantExpression)) return;
+ Object initContextClassLoaderObject = ((ConstantExpression)val).getValue();
+ if (!(initContextClassLoaderObject instanceof Boolean)) return;
+ initContextClassLoader = (Boolean) initContextClassLoaderObject;
+ }
+
+ private void checkForAutoDownload(AnnotationNode node) {
+ Object val = node.getMember(AUTO_DOWNLOAD_SETTING);
+ if (val == null || !(val instanceof ConstantExpression)) return;
+ Object autoDownloadValue = ((ConstantExpression)val).getValue();
+ if (!(autoDownloadValue instanceof Boolean)) return;
+ autoDownload = (Boolean) autoDownloadValue;
+ }
+
+ private void checkForDisableChecksums(AnnotationNode node) {
+ Object val = node.getMember(DISABLE_CHECKSUMS_SETTING);
+ if (val == null || !(val instanceof ConstantExpression)) return;
+ Object disableChecksumsValue = ((ConstantExpression)val).getValue();
+ if (!(disableChecksumsValue instanceof Boolean)) return;
+ disableChecksums = (Boolean) disableChecksumsValue;
+ }
+
+ private void checkForSystemProperties(AnnotationNode node) {
+ systemProperties = new HashMap<String, String>();
+ List<String> nameValueList = AbstractASTTransformation.getMemberStringList(node, SYSTEM_PROPERTIES_SETTING);
+ if (nameValueList != null) {
+ for (String nameValue : nameValueList) {
+ int equalsDelim = nameValue.indexOf('=');
+ if (equalsDelim != -1) {
+ systemProperties.put(nameValue.substring(0, equalsDelim), nameValue.substring(equalsDelim + 1));
+ }
+ }
+ }
+ }
+
+ private static void checkForConvenienceForm(AnnotationNode node, boolean exclude) {
+ Object val = node.getMember("value");
+ if (val == null || !(val instanceof ConstantExpression)) return;
+ Object allParts = ((ConstantExpression)val).getValue();
+ if (!(allParts instanceof String)) return;
+ String allstr = (String) allParts;
+
+ // strip off trailing attributes
+ boolean done = false;
+ while (!done) {
+ Matcher attrs = ATTRIBUTES_PATTERN.matcher(allstr);
+ if (attrs.find()) {
+ String attrName = attrs.group(2);
+ String attrValue = attrs.group(3);
+ if (attrName == null || attrValue == null) continue;
+ boolean isBool = GRAB_BOOLEAN.contains(attrName);
+ ConstantExpression value = constX(isBool ? Boolean.valueOf(attrValue) : attrValue);
+ value.setSourcePosition(node);
+ node.addMember(attrName, value);
+ int lastSemi = allstr.lastIndexOf(';');
+ if (lastSemi == -1) {
+ allstr = "";
+ break;
+ }
+ allstr = allstr.substring(0, lastSemi);
+ } else {
+ done = true;
+ }
+ }
+
+ if (allstr.contains("#")) {
+ // see: http://ant.apache.org/ivy/history/latest-milestone/textual.html
+ Matcher m = IVY_PATTERN.matcher(allstr);
+ if (!m.find()) return;
+ if (m.group(1) == null || m.group(2) == null) return;
+ node.addMember("module", constX(m.group(2)));
+ node.addMember("group", constX(m.group(1)));
+ if (m.group(6) != null) node.addMember("conf", constX(m.group(6)));
+ if (m.group(4) != null) node.addMember("version", constX(m.group(4)));
+ else if (!exclude && node.getMember("version") == null) node.addMember("version", constX("*"));
+ node.getMembers().remove("value");
+ } else if (allstr.contains(":")) {
+ // assume gradle syntax
+ // see: http://www.gradle.org/latest/docs/userguide/dependency_management.html#sec:how_to_declare_your_dependencies
+ Map<String, Object> parts = GrapeUtil.getIvyParts(allstr);
+ for (Map.Entry<String, Object> entry : parts.entrySet()) {
+ String key = entry.getKey();
+ String value = entry.getValue().toString();
+ if (!key.equals("version") || !value.equals("*") || !exclude) {
+ node.addMember(key, constX(value));
+ }
+ }
+ node.getMembers().remove("value");
+ }
+ }
+
+ private void extractGrab(Expression init, ConstantExpression ce) {
+ if (ce.getValue() instanceof AnnotationNode) {
+ AnnotationNode annotation = (AnnotationNode) ce.getValue();
+ if ((init != null) && (annotation.getMember("initClass") != null)) {
+ annotation.setMember("initClass", init);
+ }
+ String name = annotation.getClassNode().getName();
+ if ((GRAB_CLASS_NAME.equals(name))
+ || (allowShortGrab && GRAB_SHORT_NAME.equals(name))
+ || (grabAliases.contains(name))) {
+ grabAnnotations.add(annotation);
+ }
+ if ((GRABEXCLUDE_CLASS_NAME.equals(name))
+ || (allowShortGrabExcludes && GRABEXCLUDE_SHORT_NAME.equals(name))
+ || (grabExcludeAliases.contains(name))) {
+ grabExcludeAnnotations.add(annotation);
+ }
+ if ((GRABCONFIG_CLASS_NAME.equals(name))
+ || (allowShortGrabConfig && GRABCONFIG_SHORT_NAME.equals(name))
+ || (grabConfigAliases.contains(name))) {
+ grabConfigAnnotations.add(annotation);
+ }
+ if ((GRABRESOLVER_CLASS_NAME.equals(name))
+ || (allowShortGrabResolver && GRABRESOLVER_SHORT_NAME.equals(name))
+ || (grabResolverAliases.contains(name))) {
+ grabResolverAnnotations.add(annotation);
+ }
+ }
+ }
+
+ /**
+ * Adds the annotation to the internal target list if a match is found.
+ *
+ * @param node the AST node we are processing
+ */
+ public void visitAnnotations(AnnotatedNode node) {
+ super.visitAnnotations(node);
+ for (AnnotationNode an : node.getAnnotations()) {
+ String name = an.getClassNode().getName();
+ if ((GRAB_CLASS_NAME.equals(name))
+ || (allowShortGrab && GRAB_SHORT_NAME.equals(name))
+ || (grabAliases.contains(name))) {
+ grabAnnotations.add(an);
+ }
+ if ((GRABEXCLUDE_CLASS_NAME.equals(name))
+ || (allowShortGrabExcludes && GRABEXCLUDE_SHORT_NAME.equals(name))
+ || (grabExcludeAliases.contains(name))) {
+ grabExcludeAnnotations.add(an);
+ }
+ if ((GRABCONFIG_CLASS_NAME.equals(name))
+ || (allowShortGrabConfig && GRABCONFIG_SHORT_NAME.equals(name))
+ || (grabConfigAliases.contains(name))) {
+ grabConfigAnnotations.add(an);
+ }
+ if ((GRAPES_CLASS_NAME.equals(name))
+ || (allowShortGrapes && GRAPES_SHORT_NAME.equals(name))
+ || (grapesAliases.contains(name))) {
+ grapesAnnotations.add(an);
+ }
+ if ((GRABRESOLVER_CLASS_NAME.equals(name))
+ || (allowShortGrabResolver && GRABRESOLVER_SHORT_NAME.equals(name))
+ || (grabResolverAliases.contains(name))) {
+ grabResolverAnnotations.add(an);
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/grape/Grape.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/grape/Grape.java b/src/main/groovy/groovy/grape/Grape.java
new file mode 100644
index 0000000..ad89a20
--- /dev/null
+++ b/src/main/groovy/groovy/grape/Grape.java
@@ -0,0 +1,236 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package groovy.grape;
+
+import java.net.URI;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Facade to GrapeEngine.
+ */
+public class Grape {
+
+ public static final String AUTO_DOWNLOAD_SETTING = "autoDownload";
+ public static final String DISABLE_CHECKSUMS_SETTING = "disableChecksums";
+ public static final String SYSTEM_PROPERTIES_SETTING = "systemProperties";
+
+ private static boolean enableGrapes = Boolean.valueOf(System.getProperty("groovy.grape.enable", "true"));
+ private static boolean enableAutoDownload = Boolean.valueOf(System.getProperty("groovy.grape.autoDownload", "true"));
+ private static boolean disableChecksums = Boolean.valueOf(System.getProperty("groovy.grape.disableChecksums", "false"));
+ protected static GrapeEngine instance;
+
+ /**
+ * This is a static access kill-switch. All of the static shortcut
+ * methods in this class will not work if this property is set to false.
+ * By default it is set to true.
+ */
+ public static boolean getEnableGrapes() {
+ return enableGrapes;
+ }
+
+ /**
+ * This is a static access kill-switch. All of the static shortcut
+ * methods in this class will not work if this property is set to false.
+ * By default it is set to true.
+ */
+ public static void setEnableGrapes(boolean enableGrapes) {
+ Grape.enableGrapes = enableGrapes;
+ }
+
+ /**
+ * This is a static access auto download enabler. It will set the
+ * 'autoDownload' value to the passed in arguments map if not already set.
+ * If 'autoDownload' is set the value will not be adjusted.
+ * <p>
+ * This applies to the grab and resolve calls.
+ * <p>
+ * If it is set to false, only previously downloaded grapes
+ * will be used. This may cause failure in the grape call
+ * if the library has not yet been downloaded
+ * <p>
+ * If it is set to true, then any jars not already downloaded will
+ * automatically be downloaded. Also, any versions expressed as a range
+ * will be checked for new versions and downloaded (with dependencies)
+ * if found.
+ * <p>
+ * By default it is set to true.
+ */
+ public static boolean getEnableAutoDownload() {
+ return enableAutoDownload;
+ }
+
+ /**
+ * This is a static access auto download enabler. It will set the
+ * 'autoDownload' value to the passed in arguments map if not already
+ * set. If 'autoDownload' is set the value will not be adjusted.
+ * <p>
+ * This applies to the grab and resolve calls.
+ * <p>
+ * If it is set to false, only previously downloaded grapes
+ * will be used. This may cause failure in the grape call
+ * if the library has not yet been downloaded.
+ * <p>
+ * If it is set to true, then any jars not already downloaded will
+ * automatically be downloaded. Also, any versions expressed as a range
+ * will be checked for new versions and downloaded (with dependencies)
+ * if found. By default it is set to true.
+ */
+ public static void setEnableAutoDownload(boolean enableAutoDownload) {
+ Grape.enableAutoDownload = enableAutoDownload;
+ }
+
+ /**
+ * Global flag to ignore checksums.
+ * By default it is set to false.
+ */
+ public static boolean getDisableChecksums() {
+ return disableChecksums;
+ }
+
+ /**
+ * Set global flag to ignore checksums.
+ * By default it is set to false.
+ */
+ public static void setDisableChecksums(boolean disableChecksums) {
+ Grape.disableChecksums = disableChecksums;
+ }
+
+ public static synchronized GrapeEngine getInstance() {
+ if (instance == null) {
+ try {
+ // by default use GrapeIvy
+ //TODO META-INF/services resolver?
+ instance = (GrapeEngine) Class.forName("groovy.grape.GrapeIvy").newInstance();
+ } catch (InstantiationException e) {
+ //LOGME
+ } catch (IllegalAccessException e) {
+ //LOGME
+ } catch (ClassNotFoundException e) {
+ //LOGME
+ }
+ }
+ return instance;
+ }
+
+ public static void grab(String endorsed) {
+ if (enableGrapes) {
+ GrapeEngine instance = getInstance();
+ if (instance != null) {
+ instance.grab(endorsed);
+ }
+ }
+ }
+
+ public static void grab(Map<String, Object> dependency) {
+ if (enableGrapes) {
+ GrapeEngine instance = getInstance();
+ if (instance != null) {
+ if (!dependency.containsKey(AUTO_DOWNLOAD_SETTING)) {
+ dependency.put(AUTO_DOWNLOAD_SETTING, enableAutoDownload);
+ }
+ if (!dependency.containsKey(DISABLE_CHECKSUMS_SETTING)) {
+ dependency.put(DISABLE_CHECKSUMS_SETTING, disableChecksums);
+ }
+ instance.grab(dependency);
+ }
+ }
+ }
+
+ public static void grab(Map<String, Object> args, Map... dependencies) {
+ if (enableGrapes) {
+ GrapeEngine instance = getInstance();
+ if (instance != null) {
+ if (!args.containsKey(AUTO_DOWNLOAD_SETTING)) {
+ args.put(AUTO_DOWNLOAD_SETTING, enableAutoDownload);
+ }
+ if (!args.containsKey(DISABLE_CHECKSUMS_SETTING)) {
+ args.put(DISABLE_CHECKSUMS_SETTING, disableChecksums);
+ }
+ instance.grab(args, dependencies);
+ }
+ }
+ }
+
+ public static Map<String, Map<String, List<String>>> enumerateGrapes() {
+ Map<String, Map<String, List<String>>> grapes = null;
+ if (enableGrapes) {
+ GrapeEngine instance = getInstance();
+ if (instance != null) {
+ grapes = instance.enumerateGrapes();
+ }
+ }
+ if (grapes == null) {
+ return Collections.emptyMap();
+ } else {
+ return grapes;
+ }
+ }
+
+ public static URI[] resolve(Map<String, Object> args, Map... dependencies) {
+ return resolve(args, null, dependencies);
+ }
+
+ public static URI[] resolve(Map<String, Object> args, List depsInfo, Map... dependencies) {
+ URI[] uris = null;
+ if (enableGrapes) {
+ GrapeEngine instance = getInstance();
+ if (instance != null) {
+ if (!args.containsKey(AUTO_DOWNLOAD_SETTING)) {
+ args.put(AUTO_DOWNLOAD_SETTING, enableAutoDownload);
+ }
+ if (!args.containsKey(DISABLE_CHECKSUMS_SETTING)) {
+ args.put(DISABLE_CHECKSUMS_SETTING, disableChecksums);
+ }
+ uris = instance.resolve(args, depsInfo, dependencies);
+ }
+ }
+ if (uris == null) {
+ return new URI[0];
+ } else {
+ return uris;
+ }
+ }
+
+ public static Map[] listDependencies(ClassLoader cl) {
+ Map[] maps = null;
+ if (enableGrapes) {
+ GrapeEngine instance = getInstance();
+ if (instance != null) {
+ maps = instance.listDependencies(cl);
+ }
+ }
+ if (maps == null) {
+ return new Map[0];
+ } else {
+ return maps;
+ }
+
+ }
+
+ public static void addResolver(Map<String, Object> args) {
+ if (enableGrapes) {
+ GrapeEngine instance = getInstance();
+ if (instance != null) {
+ instance.addResolver(args);
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/grape/GrapeEngine.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/grape/GrapeEngine.java b/src/main/groovy/groovy/grape/GrapeEngine.java
new file mode 100644
index 0000000..d98882f
--- /dev/null
+++ b/src/main/groovy/groovy/grape/GrapeEngine.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package groovy.grape;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Danno Ferrin
+ */
+public interface GrapeEngine {
+
+ Object grab(String endorsedModule);
+
+ Object grab(Map args);
+
+ Object grab(Map args, Map... dependencies);
+
+ Map<String, Map<String, List<String>>> enumerateGrapes();
+
+ URI[] resolve(Map args, Map... dependencies);
+
+ URI[] resolve(Map args, List depsInfo, Map... dependencies);
+
+ Map[] listDependencies(ClassLoader classLoader);
+
+ void addResolver(Map<String, Object> args);
+}
+
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/grape/GrapeIvy.groovy
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/grape/GrapeIvy.groovy b/src/main/groovy/groovy/grape/GrapeIvy.groovy
new file mode 100644
index 0000000..a2c22a8
--- /dev/null
+++ b/src/main/groovy/groovy/grape/GrapeIvy.groovy
@@ -0,0 +1,729 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package groovy.grape
+
+import org.apache.groovy.plugin.GroovyRunner
+import org.apache.groovy.plugin.GroovyRunnerRegistry
+import org.apache.ivy.Ivy
+import org.apache.ivy.core.cache.ResolutionCacheManager
+import org.apache.ivy.core.event.IvyListener
+import org.apache.ivy.core.event.download.PrepareDownloadEvent
+import org.apache.ivy.core.event.resolve.StartResolveEvent
+import org.apache.ivy.core.module.descriptor.Configuration
+import org.apache.ivy.core.module.descriptor.DefaultDependencyArtifactDescriptor
+import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor
+import org.apache.ivy.core.module.descriptor.DefaultExcludeRule
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor
+import org.apache.ivy.core.module.id.ArtifactId
+import org.apache.ivy.core.module.id.ModuleId
+import org.apache.ivy.core.module.id.ModuleRevisionId
+import org.apache.ivy.core.report.ArtifactDownloadReport
+import org.apache.ivy.core.report.ResolveReport
+import org.apache.ivy.core.resolve.ResolveOptions
+import org.apache.ivy.core.settings.IvySettings
+import org.apache.ivy.plugins.matcher.ExactPatternMatcher
+import org.apache.ivy.plugins.matcher.PatternMatcher
+import org.apache.ivy.plugins.resolver.ChainResolver
+import org.apache.ivy.plugins.resolver.IBiblioResolver
+import org.apache.ivy.util.DefaultMessageLogger
+import org.apache.ivy.util.Message
+import org.codehaus.groovy.reflection.CachedClass
+import org.codehaus.groovy.reflection.ClassInfo
+import org.codehaus.groovy.reflection.ReflectionUtils
+import org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl
+
+import javax.xml.parsers.DocumentBuilderFactory
+import java.util.jar.JarFile
+import java.util.regex.Pattern
+import java.util.zip.ZipEntry
+import java.util.zip.ZipException
+import java.util.zip.ZipFile
+
+/**
+ * @author Danno Ferrin
+ * @author Paul King
+ * @author Roshan Dawrani (roshandawrani)
+ */
+class GrapeIvy implements GrapeEngine {
+
+ static final int DEFAULT_DEPTH = 3
+
+ private static final String METAINF_PREFIX = 'META-INF/services/'
+ private static final String RUNNER_PROVIDER_CONFIG = GroovyRunner.class.getName()
+
+ private final exclusiveGrabArgs = [
+ ['group', 'groupId', 'organisation', 'organization', 'org'],
+ ['module', 'artifactId', 'artifact'],
+ ['version', 'revision', 'rev'],
+ ['conf', 'scope', 'configuration'],
+ ].inject([:], {m, g -> g.each {a -> m[a] = (g - a) as Set}; m})
+
+ boolean enableGrapes
+ Ivy ivyInstance
+ Set<String> resolvedDependencies
+ Set<String> downloadedArtifacts
+ // weak hash map so we don't leak loaders directly
+ Map<ClassLoader, Set<IvyGrabRecord>> loadedDeps = new WeakHashMap<ClassLoader, Set<IvyGrabRecord>>()
+ // set that stores the IvyGrabRecord(s) for all the dependencies in each grab() call
+ Set<IvyGrabRecord> grabRecordsForCurrDependencies = new LinkedHashSet<IvyGrabRecord>()
+ // we keep the settings so that addResolver can add to the resolver chain
+ IvySettings settings
+
+ public GrapeIvy() {
+ // if we are already initialized, quit
+ if (enableGrapes) return
+
+ // start ivy
+ Message.defaultLogger = new DefaultMessageLogger(System.getProperty("ivy.message.logger.level", "-1") as int)
+ settings = new IvySettings()
+
+ // configure settings
+ def grapeConfig = getLocalGrapeConfig()
+ if (!grapeConfig.exists()) {
+ grapeConfig = GrapeIvy.getResource("defaultGrapeConfig.xml")
+ }
+ try {
+ settings.load(grapeConfig) // exploit multi-methods for convenience
+ } catch (java.text.ParseException ex) {
+ def configLocation = grapeConfig instanceof File ? grapeConfig.canonicalPath : grapeConfig.toString()
+ System.err.println "Local Ivy config file '$configLocation' appears corrupt - ignoring it and using default config instead\nError was: " + ex.message
+ grapeConfig = GrapeIvy.getResource("defaultGrapeConfig.xml")
+ settings.load(grapeConfig)
+ }
+
+ // set up the cache dirs
+ settings.defaultCache = getGrapeCacheDir()
+
+ settings.setVariable("ivy.default.configuration.m2compatible", "true")
+ ivyInstance = Ivy.newInstance(settings)
+ org.apache.ivy.core.IvyContext.getContext().setIvy(ivyInstance);
+ resolvedDependencies = []
+ downloadedArtifacts = []
+
+ //TODO add grab to the DGM??
+
+ enableGrapes = true
+ }
+
+ public File getGroovyRoot() {
+ String root = System.getProperty("groovy.root")
+ def groovyRoot
+ if (root == null) {
+ groovyRoot = new File(System.getProperty("user.home"), ".groovy")
+ } else {
+ groovyRoot = new File(root)
+ }
+ try {
+ groovyRoot = groovyRoot.canonicalFile
+ } catch (IOException e) {
+ // skip canonicalization then, it may not exist yet
+ }
+ return groovyRoot
+ }
+
+ public File getLocalGrapeConfig() {
+ String grapeConfig = System.getProperty("grape.config")
+ if(grapeConfig) {
+ return new File(grapeConfig)
+ }
+ return new File(getGrapeDir(), 'grapeConfig.xml')
+ }
+
+ public File getGrapeDir() {
+ String root = System.getProperty("grape.root")
+ if(root == null) {
+ return getGroovyRoot()
+ }
+ File grapeRoot = new File(root)
+ try {
+ grapeRoot = grapeRoot.canonicalFile
+ } catch (IOException e) {
+ // skip canonicalization then, it may not exist yet
+ }
+ return grapeRoot
+ }
+
+ public File getGrapeCacheDir() {
+ File cache = new File(getGrapeDir(), 'grapes')
+ if (!cache.exists()) {
+ cache.mkdirs()
+ } else if (!cache.isDirectory()) {
+ throw new RuntimeException("The grape cache dir $cache is not a directory")
+ }
+ return cache
+ }
+
+ public def chooseClassLoader(Map args) {
+ def loader = args.classLoader
+ if (!isValidTargetClassLoader(loader)) {
+ loader = (args.refObject?.class
+ ?:ReflectionUtils.getCallingClass(args.calleeDepth?:1)
+ )?.classLoader
+ while (loader && !isValidTargetClassLoader(loader)) {
+ loader = loader.parent
+ }
+ //if (!isValidTargetClassLoader(loader)) {
+ // loader = Thread.currentThread().contextClassLoader
+ //}
+ //if (!isValidTargetClassLoader(loader)) {
+ // loader = GrapeIvy.class.classLoader
+ //}
+ if (!isValidTargetClassLoader(loader)) {
+ throw new RuntimeException("No suitable ClassLoader found for grab")
+ }
+ }
+ return loader
+ }
+
+ private boolean isValidTargetClassLoader(loader) {
+ return isValidTargetClassLoaderClass(loader?.class)
+ }
+
+ private boolean isValidTargetClassLoaderClass(Class loaderClass) {
+ return (loaderClass != null) &&
+ (
+ (loaderClass.name == 'groovy.lang.GroovyClassLoader') ||
+ (loaderClass.name == 'org.codehaus.groovy.tools.RootLoader') ||
+ isValidTargetClassLoaderClass(loaderClass.superclass)
+ )
+ }
+
+ public IvyGrabRecord createGrabRecord(Map deps) {
+ // parse the actual dependency arguments
+ String module = deps.module ?: deps.artifactId ?: deps.artifact
+ if (!module) {
+ throw new RuntimeException('grab requires at least a module: or artifactId: or artifact: argument')
+ }
+
+ String groupId = deps.group ?: deps.groupId ?: deps.organisation ?: deps.organization ?: deps.org ?: ''
+ String ext = deps.ext ?: deps.type ?: ''
+ String type = deps.type ?: ''
+
+ //TODO accept ranges and decode them? except '1.0.0'..<'2.0.0' won't work in groovy
+ String version = deps.version ?: deps.revision ?: deps.rev ?: '*'
+ if ('*' == version) version = 'latest.default'
+
+ ModuleRevisionId mrid = ModuleRevisionId.newInstance(groupId, module, version)
+
+ boolean force = deps.containsKey('force') ? deps.force : true
+ boolean changing = deps.containsKey('changing') ? deps.changing : false
+ boolean transitive = deps.containsKey('transitive') ? deps.transitive : true
+ def conf = deps.conf ?: deps.scope ?: deps.configuration ?: ['default']
+ if (conf instanceof String) {
+ if (conf.startsWith("[") && conf.endsWith("]")) conf = conf[1..-2]
+ conf = conf.split(",").toList()
+ }
+ def classifier = deps.classifier ?: null
+
+ return new IvyGrabRecord(mrid:mrid, conf:conf, changing:changing, transitive:transitive, force:force, classifier:classifier, ext:ext, type:type)
+ }
+
+ public grab(String endorsedModule) {
+ return grab(group:'groovy.endorsed', module:endorsedModule, version:GroovySystem.version)
+ }
+
+ public grab(Map args) {
+ args.calleeDepth = args.calleeDepth?:DEFAULT_DEPTH + 1
+ return grab(args, args)
+ }
+
+ public grab(Map args, Map... dependencies) {
+ ClassLoader loader = null
+ grabRecordsForCurrDependencies.clear()
+
+ try {
+ // identify the target classloader early, so we fail before checking repositories
+ loader = chooseClassLoader(
+ classLoader:args.remove('classLoader'),
+ refObject:args.remove('refObject'),
+ calleeDepth:args.calleeDepth?:DEFAULT_DEPTH,
+ )
+
+ // check for non-fail null.
+ // If we were in fail mode we would have already thrown an exception
+ if (!loader) return
+
+ def uris = resolve(loader, args, dependencies)
+ for (URI uri in uris) {
+ loader.addURL(uri.toURL())
+ }
+ boolean runnerServicesFound = false
+ for (URI uri in uris) {
+ //TODO check artifact type, jar vs library, etc
+ File file = new File(uri)
+ processCategoryMethods(loader, file)
+ Collection<String> services = processMetaInfServices(loader, file)
+ if (!runnerServicesFound) {
+ runnerServicesFound = services.contains(RUNNER_PROVIDER_CONFIG)
+ }
+ }
+ if (runnerServicesFound) {
+ GroovyRunnerRegistry.getInstance().load(loader)
+ }
+ } catch (Exception e) {
+ // clean-up the state first
+ Set<IvyGrabRecord> grabRecordsForCurrLoader = getLoadedDepsForLoader(loader)
+ grabRecordsForCurrLoader.removeAll(grabRecordsForCurrDependencies)
+ grabRecordsForCurrDependencies.clear()
+
+ if (args.noExceptions) {
+ return e
+ }
+ throw e
+ }
+ return null
+ }
+
+ private processCategoryMethods(ClassLoader loader, File file) {
+ // register extension methods if jar
+ if (file.name.toLowerCase().endsWith(".jar")) {
+ def mcRegistry = GroovySystem.metaClassRegistry
+ if (mcRegistry instanceof MetaClassRegistryImpl) {
+ try {
+ JarFile jar = new JarFile(file)
+ def entry = jar.getEntry(MetaClassRegistryImpl.MODULE_META_INF_FILE)
+ if (entry) {
+ Properties props = new Properties()
+ props.load(jar.getInputStream(entry))
+ Map<CachedClass, List<MetaMethod>> metaMethods = new HashMap<CachedClass, List<MetaMethod>>()
+ mcRegistry.registerExtensionModuleFromProperties(props, loader, metaMethods)
+ // add old methods to the map
+ metaMethods.each { CachedClass c, List<MetaMethod> methods ->
+ // GROOVY-5543: if a module was loaded using grab, there are chances that subclasses
+ // have their own ClassInfo, and we must change them as well!
+ Set<CachedClass> classesToBeUpdated = [c]
+ ClassInfo.onAllClassInfo { ClassInfo info ->
+ if (c.theClass.isAssignableFrom(info.cachedClass.theClass)) {
+ classesToBeUpdated << info.cachedClass
+ }
+ }
+ classesToBeUpdated*.addNewMopMethods(methods)
+ }
+ }
+ }
+ catch(ZipException zipException) {
+ throw new RuntimeException("Grape could not load jar '$file'", zipException)
+ }
+ }
+ }
+ }
+
+ void processOtherServices(ClassLoader loader, File f) {
+ processMetaInfServices(loader, f) // ignore result
+ }
+
+ /**
+ * Searches the given File for known service provider
+ * configuration files to process.
+ *
+ * @param loader used to locate service provider files
+ * @param f ZipFile in which to search for services
+ * @return a collection of service provider files that were found
+ */
+ private Collection<String> processMetaInfServices(ClassLoader loader, File f) {
+ List<String> services = new ArrayList<>()
+ try {
+ ZipFile zf = new ZipFile(f)
+ String providerConfig = 'org.codehaus.groovy.runtime.SerializedCategoryMethods'
+ ZipEntry serializedCategoryMethods = zf.getEntry(METAINF_PREFIX + providerConfig)
+ if (serializedCategoryMethods != null) {
+ services.add(providerConfig)
+ processSerializedCategoryMethods(zf.getInputStream(serializedCategoryMethods))
+ }
+ // TODO: remove in a future release (replaced by GroovyRunnerRegistry)
+ providerConfig = 'org.codehaus.groovy.plugins.Runners'
+ ZipEntry pluginRunners = zf.getEntry(METAINF_PREFIX + providerConfig)
+ if (pluginRunners != null) {
+ services.add(providerConfig)
+ processRunners(zf.getInputStream(pluginRunners), f.getName(), loader)
+ }
+ // GroovyRunners are loaded per ClassLoader using a ServiceLoader so here
+ // it only needs to be indicated that the service provider file was found
+ if (zf.getEntry(METAINF_PREFIX + RUNNER_PROVIDER_CONFIG) != null) {
+ services.add(RUNNER_PROVIDER_CONFIG)
+ }
+ } catch(ZipException ignore) {
+ // ignore files we can't process, e.g. non-jar/zip artifacts
+ // TODO log a warning
+ }
+ return services
+ }
+
+ void processSerializedCategoryMethods(InputStream is) {
+ is.text.readLines().each {
+ println it.trim() // TODO implement this or delete it
+ }
+ }
+
+ void processRunners(InputStream is, String name, ClassLoader loader) {
+ GroovyRunnerRegistry registry = GroovyRunnerRegistry.getInstance()
+ is.text.readLines()*.trim().findAll{ !it.isEmpty() && it[0] != '#' }.each {
+ try {
+ registry[name] = loader.loadClass(it).newInstance()
+ } catch (Exception ex) {
+ throw new IllegalStateException("Error registering runner class '" + it + "'", ex)
+ }
+ }
+ }
+
+ public ResolveReport getDependencies(Map args, IvyGrabRecord... grabRecords) {
+ ResolutionCacheManager cacheManager = ivyInstance.resolutionCacheManager
+
+ def millis = System.currentTimeMillis()
+ def md = new DefaultModuleDescriptor(ModuleRevisionId
+ .newInstance("caller", "all-caller", "working" + millis.toString()[-2..-1]), "integration", null, true)
+ md.addConfiguration(new Configuration('default'))
+ md.setLastModified(millis)
+
+ addExcludesIfNeeded(args, md)
+
+ for (IvyGrabRecord grabRecord : grabRecords) {
+ def conf = grabRecord.conf ?: ['*']
+ DefaultDependencyDescriptor dd = md.dependencies.find {it.dependencyRevisionId.equals(grabRecord.mrid)}
+ if (dd) {
+ createAndAddDependencyArtifactDescriptor(dd, grabRecord, conf)
+ } else {
+ dd = new DefaultDependencyDescriptor(md, grabRecord.mrid, grabRecord.force,
+ grabRecord.changing, grabRecord.transitive)
+ conf.each {dd.addDependencyConfiguration('default', it)}
+ createAndAddDependencyArtifactDescriptor(dd, grabRecord, conf)
+ md.addDependency(dd)
+ }
+ }
+
+ // resolve grab and dependencies
+ ResolveOptions resolveOptions = new ResolveOptions()\
+ .setConfs(['default'] as String[])\
+ .setOutputReport(false)\
+ .setValidate(args.containsKey('validate') ? args.validate : false)
+
+ ivyInstance.settings.defaultResolver = args.autoDownload ? 'downloadGrapes' : 'cachedGrapes'
+ if (args.disableChecksums) {
+ ivyInstance.settings.setVariable('ivy.checksums', '')
+ }
+ boolean reportDownloads = System.getProperty('groovy.grape.report.downloads', 'false') == 'true'
+ if (reportDownloads) {
+ ivyInstance.eventManager.addIvyListener([progress:{ ivyEvent -> switch(ivyEvent) {
+ case StartResolveEvent:
+ ivyEvent.moduleDescriptor.dependencies.each { it ->
+ def name = it.toString()
+ if (!resolvedDependencies.contains(name)) {
+ resolvedDependencies << name
+ System.err.println "Resolving " + name
+ }
+ }
+ break
+ case PrepareDownloadEvent:
+ ivyEvent.artifacts.each { it ->
+ def name = it.toString()
+ if (!downloadedArtifacts.contains(name)) {
+ downloadedArtifacts << name
+ System.err.println "Preparing to download artifact " + name
+ }
+ }
+ break
+ } } ] as IvyListener)
+ }
+
+ ResolveReport report = null
+ int attempt = 8 // max of 8 times
+ while (true) {
+ try {
+ report = ivyInstance.resolve(md, resolveOptions)
+ break
+ } catch(IOException ioe) {
+ if (attempt--) {
+ if (reportDownloads)
+ System.err.println "Grab Error: retrying..."
+ sleep attempt > 4 ? 350 : 1000
+ continue
+ }
+ throw new RuntimeException("Error grabbing grapes -- $ioe.message")
+ }
+ }
+
+ if (report.hasError()) {
+ throw new RuntimeException("Error grabbing Grapes -- $report.allProblemMessages")
+ }
+ if (report.downloadSize && reportDownloads) {
+ System.err.println "Downloaded ${report.downloadSize >> 10} Kbytes in ${report.downloadTime}ms:\n ${report.allArtifactsReports*.toString().join('\n ')}"
+ }
+ md = report.moduleDescriptor
+
+ if (!args.preserveFiles) {
+ cacheManager.getResolvedIvyFileInCache(md.moduleRevisionId).delete()
+ cacheManager.getResolvedIvyPropertiesInCache(md.moduleRevisionId).delete()
+ }
+
+ return report
+ }
+
+ private void createAndAddDependencyArtifactDescriptor(DefaultDependencyDescriptor dd, IvyGrabRecord grabRecord, List<String> conf) {
+ // TODO: find out "unknown" reason and change comment below - also, confirm conf[0] check vs conf.contains('optional')
+ if (conf[0]!="optional" || grabRecord.classifier) { // for some unknown reason optional dependencies should not have an artifactDescriptor
+ def dad = new DefaultDependencyArtifactDescriptor(dd,
+ grabRecord.mrid.name, grabRecord.type ?: 'jar', grabRecord.ext ?: 'jar', null, grabRecord.classifier ? [classifier: grabRecord.classifier] : null)
+ conf.each { dad.addConfiguration(it) }
+ dd.addDependencyArtifact('default', dad)
+ }
+ }
+
+ public void uninstallArtifact(String group, String module, String rev) {
+ // TODO consider transitive uninstall as an option
+ Pattern ivyFilePattern = ~/ivy-(.*)\.xml/ //TODO get pattern from ivy conf
+ grapeCacheDir.eachDir { File groupDir ->
+ if (groupDir.name == group) groupDir.eachDir { File moduleDir ->
+ if (moduleDir.name == module) moduleDir.eachFileMatch(ivyFilePattern) { File ivyFile ->
+ def m = ivyFilePattern.matcher(ivyFile.name)
+ if (m.matches() && m.group(1) == rev) {
+ // TODO handle other types? e.g. 'dlls'
+ def jardir = new File(moduleDir, 'jars')
+ if (!jardir.exists()) return
+ def dbf = DocumentBuilderFactory.newInstance()
+ def db = dbf.newDocumentBuilder()
+ def root = db.parse(ivyFile).documentElement
+ def publis = root.getElementsByTagName('publications')
+ for (int i=0; i<publis.length;i++) {
+ def artifacts = publis.item(i).getElementsByTagName('artifact')
+ for (int j=0; j<artifacts.length; j++) {
+ def artifact = artifacts.item(j)
+ def attrs = artifact.attributes
+ def name = attrs.getNamedItem('name').getTextContent() + "-$rev"
+ def classifier = attrs.getNamedItemNS("m", "classifier")?.getTextContent()
+ if (classifier) name += "-$classifier"
+ name += ".${attrs.getNamedItem('ext').getTextContent()}"
+ def jarfile = new File(jardir, name)
+ if (jarfile.exists()) {
+ println "Deleting ${jarfile.name}"
+ jarfile.delete()
+ }
+ }
+ }
+ ivyFile.delete()
+ }
+ }
+ }
+ }
+ }
+
+ private addExcludesIfNeeded(Map args, DefaultModuleDescriptor md) {
+ if (!args.containsKey('excludes')) return
+ args.excludes.each{ map ->
+ def excludeRule = new DefaultExcludeRule(new ArtifactId(
+ new ModuleId(map.group, map.module), PatternMatcher.ANY_EXPRESSION,
+ PatternMatcher.ANY_EXPRESSION,
+ PatternMatcher.ANY_EXPRESSION),
+ ExactPatternMatcher.INSTANCE, null)
+ excludeRule.addConfiguration('default')
+ md.addExcludeRule(excludeRule)
+ }
+ }
+
+ public Map<String, Map<String, List<String>>> enumerateGrapes() {
+ Map<String, Map<String, List<String>>> bunches = [:]
+ Pattern ivyFilePattern = ~/ivy-(.*)\.xml/ //TODO get pattern from ivy conf
+ grapeCacheDir.eachDir {File groupDir ->
+ Map<String, List<String>> grapes = [:]
+ bunches[groupDir.name] = grapes
+ groupDir.eachDir { File moduleDir ->
+ def versions = []
+ moduleDir.eachFileMatch(ivyFilePattern) {File ivyFile ->
+ def m = ivyFilePattern.matcher(ivyFile.name)
+ if (m.matches()) versions += m.group(1)
+ }
+ grapes[moduleDir.name] = versions
+ }
+ }
+ return bunches
+ }
+
+ public URI[] resolve(Map args, Map ... dependencies) {
+ resolve(args, null, dependencies)
+ }
+
+ public URI[] resolve(Map args, List depsInfo, Map ... dependencies) {
+ // identify the target classloader early, so we fail before checking repositories
+ def loader = chooseClassLoader(
+ classLoader: args.remove('classLoader'),
+ refObject: args.remove('refObject'),
+ calleeDepth: args.calleeDepth ?: DEFAULT_DEPTH,
+ )
+
+ // check for non-fail null.
+ // If we were in fail mode we would have already thrown an exception
+ if (!loader) return
+
+ resolve(loader, args, depsInfo, dependencies)
+ }
+
+ URI [] resolve(ClassLoader loader, Map args, Map... dependencies) {
+ return resolve(loader, args, null, dependencies)
+ }
+
+ URI [] resolve(ClassLoader loader, Map args, List depsInfo, Map... dependencies) {
+ // check for mutually exclusive arguments
+ Set keys = args.keySet()
+ keys.each {a ->
+ Set badArgs = exclusiveGrabArgs[a]
+ if (badArgs && !badArgs.disjoint(keys)) {
+ throw new RuntimeException("Mutually exclusive arguments passed into grab: ${keys.intersect(badArgs) + a}")
+ }
+ }
+
+ // check the kill switch
+ if (!enableGrapes) { return }
+
+ boolean populateDepsInfo = (depsInfo != null)
+
+ Set<IvyGrabRecord> localDeps = getLoadedDepsForLoader(loader)
+
+ dependencies.each {
+ IvyGrabRecord igr = createGrabRecord(it)
+ grabRecordsForCurrDependencies.add(igr)
+ localDeps.add(igr)
+ }
+ // the call to reverse ensures that the newest additions are in
+ // front causing existing dependencies to come last and thus
+ // claiming higher priority. Thus when module versions clash we
+ // err on the side of using the class already loaded into the
+ // classloader rather than adding another jar of the same module
+ // with a different version
+ ResolveReport report = null
+ try {
+ report = getDependencies(args, *localDeps.asList().reverse())
+ } catch (Exception e) {
+ // clean-up the state first
+ localDeps.removeAll(grabRecordsForCurrDependencies)
+ grabRecordsForCurrDependencies.clear()
+ throw e
+ }
+
+ List<URI> results = []
+ for (ArtifactDownloadReport adl in report.allArtifactsReports) {
+ //TODO check artifact type, jar vs library, etc
+ if (adl.localFile) {
+ results += adl.localFile.toURI()
+ }
+ }
+
+ if (populateDepsInfo) {
+ def deps = report.dependencies
+ deps.each { depNode ->
+ def id = depNode.id
+ depsInfo << ['group' : id.organisation, 'module' : id.name, 'revision' : id.revision]
+ }
+ }
+
+ return results as URI[]
+ }
+
+ private Set<IvyGrabRecord> getLoadedDepsForLoader(ClassLoader loader) {
+ Set<IvyGrabRecord> localDeps = loadedDeps.get(loader)
+ if (localDeps == null) {
+ // use a linked set to preserve initial insertion order
+ localDeps = new LinkedHashSet<IvyGrabRecord>()
+ loadedDeps.put(loader, localDeps)
+ }
+ return localDeps
+ }
+
+ public Map[] listDependencies (ClassLoader classLoader) {
+ if (loadedDeps.containsKey(classLoader)) {
+ List<Map> results = []
+ loadedDeps[classLoader].each { IvyGrabRecord grabbed ->
+ def dep = [
+ group : grabbed.mrid.organisation,
+ module : grabbed.mrid.name,
+ version : grabbed.mrid.revision
+ ]
+ if (grabbed.conf != ['default']) {
+ dep.conf = grabbed.conf
+ }
+ if (grabbed.changing) {
+ dep.changing = grabbed.changing
+ }
+ if (!grabbed.transitive) {
+ dep.transitive = grabbed.transitive
+ }
+ if (!grabbed.force) {
+ dep.force = grabbed.force
+ }
+ if (grabbed.classifier) {
+ dep.classifier = grabbed.classifier
+ }
+ if (grabbed.ext) {
+ dep.ext = grabbed.ext
+ }
+ if (grabbed.type) {
+ dep.type = grabbed.type
+ }
+ results << dep
+ }
+ return results
+ }
+ return null
+ }
+
+ public void addResolver(Map<String, Object> args) {
+ ChainResolver chainResolver = settings.getResolver("downloadGrapes")
+
+ IBiblioResolver resolver = new IBiblioResolver(name: args.name, root:args.root,
+ m2compatible:(args.m2Compatible ?: true), settings:settings)
+
+ chainResolver.add(resolver)
+
+ ivyInstance = Ivy.newInstance(settings)
+ resolvedDependencies = []
+ downloadedArtifacts = []
+ }
+}
+
+class IvyGrabRecord {
+ ModuleRevisionId mrid
+ List<String> conf
+ boolean changing
+ boolean transitive
+ boolean force
+ String classifier
+ String ext
+ String type
+
+ public int hashCode() {
+ return (mrid.hashCode() ^ conf.hashCode()
+ ^ (changing ? 0xaaaaaaaa : 0x55555555)
+ ^ (transitive ? 0xbbbbbbbb : 0x66666666)
+ ^ (force ? 0xcccccccc: 0x77777777)
+ ^ (classifier ? classifier.hashCode() : 0)
+ ^ (ext ? ext.hashCode() : 0)
+ ^ (type ? type.hashCode() : 0))
+ }
+
+ public boolean equals(Object o) {
+ return ((o.class == IvyGrabRecord)
+ && (changing == o.changing)
+ && (transitive == o.transitive)
+ && (force== o.force)
+ && (mrid == o.mrid)
+ && (conf == o.conf)
+ && (classifier == o.classifier)
+ && (ext == o.ext)
+ && (type == o.type))
+ }
+
+}