http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/grape/GrabAnnotationTransformation.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/grape/GrabAnnotationTransformation.java b/src/main/groovy/grape/GrabAnnotationTransformation.java deleted file mode 100644 index dd55ff9..0000000 --- a/src/main/groovy/grape/GrabAnnotationTransformation.java +++ /dev/null @@ -1,639 +0,0 @@ -/* - * 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/grape/Grape.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/grape/Grape.java b/src/main/groovy/grape/Grape.java deleted file mode 100644 index c534ded..0000000 --- a/src/main/groovy/grape/Grape.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * 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/grape/GrapeEngine.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/grape/GrapeEngine.java b/src/main/groovy/grape/GrapeEngine.java deleted file mode 100644 index 4ce171f..0000000 --- a/src/main/groovy/grape/GrapeEngine.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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/grape/GrapeIvy.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/grape/GrapeIvy.groovy b/src/main/groovy/grape/GrapeIvy.groovy deleted file mode 100644 index a2c22a8..0000000 --- a/src/main/groovy/grape/GrapeIvy.groovy +++ /dev/null @@ -1,729 +0,0 @@ -/* - * 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)) - } - -} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/beans/Bindable.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/beans/Bindable.java b/src/main/groovy/groovy/beans/Bindable.java new file mode 100644 index 0000000..e9bd292 --- /dev/null +++ b/src/main/groovy/groovy/beans/Bindable.java @@ -0,0 +1,116 @@ +/* + * 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.beans; + +import org.codehaus.groovy.transform.GroovyASTTransformationClass; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotates a groovy property or a class. + * + * When annotating a property it indicates that the property should be a + * bound property according to the JavaBeans spec, announcing to listeners + * that the value has changed. + * <p> + * When annotating a class it indicates that all groovy properties in that + * class should be bound as though each property had the annotation (even + * if it already has it explicitly). + * <p> + * It is a compilation error to place this annotation on a field (that is + * not a property, i.e. has scope visibility modifiers). + * <p> + * If a property with a user defined setter method is annotated the code + * block is wrapped with the needed code to fire off the event. + * <p> + * The following example shows how you can use this annotation on fields + * of a class: + * <pre> + * class Person { + * @groovy.beans.Bindable + * String firstName + * + * @groovy.beans.Bindable + * def zipCode + * } + * </pre> + * The above example will generate code that is similar to the next snippet. + * Notice the difference between a String property and a def/Object property: + * <pre> + * public class Person { + * @groovy.beans.Bindable + * private java.lang.String firstName + * @groovy.beans.Bindable + * private java.lang.Object zipCode + * final private java.beans.PropertyChangeSupport this$propertyChangeSupport + * + * public Person() { + * this$propertyChangeSupport = new java.beans.PropertyChangeSupport(this) + * } + * + * public void addPropertyChangeListener(java.beans.PropertyChangeListener listener) { + * this$propertyChangeSupport.addPropertyChangeListener(listener) + * } + * + * public void addPropertyChangeListener(java.lang.String name, java.beans.PropertyChangeListener listener) { + * this$propertyChangeSupport.addPropertyChangeListener(name, listener) + * } + * + * public void removePropertyChangeListener(java.beans.PropertyChangeListener listener) { + * this$propertyChangeSupport.removePropertyChangeListener(listener) + * } + * + * public void removePropertyChangeListener(java.lang.String name, java.beans.PropertyChangeListener listener) { + * this$propertyChangeSupport.removePropertyChangeListener(name, listener) + * } + * + * public void firePropertyChange(java.lang.String name, java.lang.Object oldValue, java.lang.Object newValue) { + * this$propertyChangeSupport.firePropertyChange(name, oldValue, newValue) + * } + * + * public java.beans.PropertyChangeListener[] getPropertyChangeListeners() { + * return this$propertyChangeSupport.getPropertyChangeListeners() + * } + * + * public java.beans.PropertyChangeListener[] getPropertyChangeListeners(java.lang.String name) { + * return this$propertyChangeSupport.getPropertyChangeListeners(name) + * } + * + * public void setFirstName(java.lang.String value) { + * this.firePropertyChange('firstName', firstName, firstName = value ) + * } + * + * public void setZipCode(java.lang.Object value) { + * this.firePropertyChange('zipCode', zipCode, zipCode = value ) + * } + * } + * </pre> + * + * @see BindableASTTransformation + * @author Danno Ferrin (shemnon) + */ [email protected] +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.FIELD, ElementType.TYPE}) +@GroovyASTTransformationClass("groovy.beans.BindableASTTransformation") +public @interface Bindable { +}
