/**
  * Removes all classes from the class cache.
  *
  * @see #getClassCacheEntry(String)
  * @see #setClassCacheEntry(Class)
  * @see #removeClassCacheEntry(String)
  */
 public void clearCache() {
   synchronized (classCache) {
     classCache.clear();
   }
   synchronized (sourceCache) {
     sourceCache.clear();
   }
 }
 /**
  * Parses the given code source into a Java class. If there is a class file for the given code
  * source, then no parsing is done, instead the cached class is returned.
  *
  * @param shouldCacheSource if true then the generated class will be stored in the source cache
  * @return the main class defined in the given script
  */
 public Class parseClass(GroovyCodeSource codeSource, boolean shouldCacheSource)
     throws CompilationFailedException {
   synchronized (sourceCache) {
     Class answer = sourceCache.get(codeSource.getName());
     if (answer != null) return answer;
     answer = doParseClass(codeSource);
     if (shouldCacheSource) sourceCache.put(codeSource.getName(), answer);
     return answer;
   }
 }
 /**
  * (Re)Compiles the given source. This method starts the compilation of a given source, if the
  * source has changed since the class was created. For this isSourceNewer is called.
  *
  * @param source the source pointer for the compilation
  * @param className the name of the class to be generated
  * @param oldClass a possible former class
  * @return the old class if the source wasn't new enough, the new class else
  * @throws CompilationFailedException if the compilation failed
  * @throws IOException if the source is not readable
  * @see #isSourceNewer(URL, Class)
  */
 protected Class recompile(URL source, String className, Class oldClass)
     throws CompilationFailedException, IOException {
   if (source != null) {
     // found a source, compile it if newer
     if ((oldClass != null && isSourceNewer(source, oldClass)) || (oldClass == null)) {
       synchronized (sourceCache) {
         String name = source.toExternalForm();
         sourceCache.remove(name);
         if (isFile(source)) {
           try {
             return parseClass(
                 new GroovyCodeSource(new File(source.toURI()), config.getSourceEncoding()));
           } catch (URISyntaxException e) {
             // do nothing and fall back to the other version
           }
         }
         return parseClass(source.openStream(), name);
       }
     }
   }
   return oldClass;
 }
 /**
  * Returns all Groovy classes loaded by this class loader.
  *
  * @return all classes loaded by this class loader
  */
 public Class[] getLoadedClasses() {
   synchronized (classCache) {
     final Collection<Class> values = classCache.values();
     return values.toArray(new Class[values.size()]);
   }
 }
 /**
  * removes a class from the class cache.
  *
  * @param name of the class
  * @see #getClassCacheEntry(String)
  * @see #setClassCacheEntry(Class)
  * @see #clearCache()
  */
 protected void removeClassCacheEntry(String name) {
   synchronized (classCache) {
     classCache.remove(name);
   }
 }
 /**
  * sets an entry in the class cache.
  *
  * @param cls the class
  * @see #removeClassCacheEntry(String)
  * @see #getClassCacheEntry(String)
  * @see #clearCache()
  */
 protected void setClassCacheEntry(Class cls) {
   synchronized (classCache) {
     classCache.put(cls.getName(), cls);
   }
 }
 /**
  * gets a class from the class cache. This cache contains only classes loaded through this class
  * loader or an InnerLoader instance. If no class is stored for a specific name, then the method
  * should return null.
  *
  * @param name of the class
  * @return the class stored for the given name
  * @see #removeClassCacheEntry(String)
  * @see #setClassCacheEntry(Class)
  * @see #clearCache()
  */
 protected Class getClassCacheEntry(String name) {
   if (name == null) return null;
   synchronized (classCache) {
     return classCache.get(name);
   }
 }
 private static void addPhaseOperationsForGlobalTransforms(
     CompilationUnit compilationUnit, Map<String, URL> transformNames, boolean isFirstScan) {
   GroovyClassLoader transformLoader = compilationUnit.getTransformLoader();
   for (Map.Entry<String, URL> entry : transformNames.entrySet()) {
     try {
       Class gTransClass = transformLoader.loadClass(entry.getKey(), false, true, false);
       // no inspection unchecked
       GroovyASTTransformation transformAnnotation =
           (GroovyASTTransformation) gTransClass.getAnnotation(GroovyASTTransformation.class);
       if (transformAnnotation == null) {
         compilationUnit
             .getErrorCollector()
             .addWarning(
                 new WarningMessage(
                     WarningMessage.POSSIBLE_ERRORS,
                     "Transform Class "
                         + entry.getKey()
                         + " is specified as a global transform in "
                         + entry.getValue().toExternalForm()
                         + " but it is not annotated by "
                         + GroovyASTTransformation.class.getName()
                         + " the global transform associated with it may fail and cause the compilation to fail.",
                     null,
                     null));
         continue;
       }
       if (ASTTransformation.class.isAssignableFrom(gTransClass)) {
         final ASTTransformation instance = (ASTTransformation) gTransClass.newInstance();
         if (instance instanceof CompilationUnitAware) {
           ((CompilationUnitAware) instance).setCompilationUnit(compilationUnit);
         }
         CompilationUnit.SourceUnitOperation suOp =
             new CompilationUnit.SourceUnitOperation() {
               public void call(SourceUnit source) throws CompilationFailedException {
                 instance.visit(new ASTNode[] {source.getAST()}, source);
               }
             };
         if (isFirstScan) {
           compilationUnit.addPhaseOperation(suOp, transformAnnotation.phase().getPhaseNumber());
         } else {
           compilationUnit.addNewPhaseOperation(
               suOp, transformAnnotation.phase().getPhaseNumber());
         }
       } else {
         compilationUnit
             .getErrorCollector()
             .addError(
                 new SimpleMessage(
                     "Transform Class "
                         + entry.getKey()
                         + " specified at "
                         + entry.getValue().toExternalForm()
                         + " is not an ASTTransformation.",
                     null));
       }
     } catch (Exception e) {
       compilationUnit
           .getErrorCollector()
           .addError(
               new SimpleMessage(
                   "Could not instantiate global transform class "
                       + entry.getKey()
                       + " specified at "
                       + entry.getValue().toExternalForm()
                       + "  because of exception "
                       + e.toString(),
                   null));
     }
   }
 }
  private static void doAddGlobalTransforms(
      ASTTransformationsContext context, boolean isFirstScan) {
    final CompilationUnit compilationUnit = context.getCompilationUnit();
    GroovyClassLoader transformLoader = compilationUnit.getTransformLoader();
    Map<String, URL> transformNames = new LinkedHashMap<String, URL>();
    try {
      Enumeration<URL> globalServices =
          transformLoader.getResources(
              "META-INF/services/org.codehaus.groovy.transform.ASTTransformation");
      while (globalServices.hasMoreElements()) {
        URL service = globalServices.nextElement();
        String className;
        BufferedReader svcIn = null;
        try {
          svcIn = new BufferedReader(new InputStreamReader(service.openStream()));
          try {
            className = svcIn.readLine();
          } catch (IOException ioe) {
            compilationUnit
                .getErrorCollector()
                .addError(
                    new SimpleMessage(
                        "IOException reading the service definition at "
                            + service.toExternalForm()
                            + " because of exception "
                            + ioe.toString(),
                        null));
            continue;
          }
          Set<String> disabledGlobalTransforms =
              compilationUnit.getConfiguration().getDisabledGlobalASTTransformations();
          if (disabledGlobalTransforms == null) disabledGlobalTransforms = Collections.emptySet();
          while (className != null) {
            if (!className.startsWith("#") && className.length() > 0) {
              if (!disabledGlobalTransforms.contains(className)) {
                if (transformNames.containsKey(className)) {
                  if (!service.equals(transformNames.get(className))) {
                    compilationUnit
                        .getErrorCollector()
                        .addWarning(
                            WarningMessage.POSSIBLE_ERRORS,
                            "The global transform for class "
                                + className
                                + " is defined in both "
                                + transformNames.get(className).toExternalForm()
                                + " and "
                                + service.toExternalForm()
                                + " - the former definition will be used and the latter ignored.",
                            null,
                            null);
                  }

                } else {
                  transformNames.put(className, service);
                }
              }
            }
            try {
              className = svcIn.readLine();
            } catch (IOException ioe) {
              compilationUnit
                  .getErrorCollector()
                  .addError(
                      new SimpleMessage(
                          "IOException reading the service definition at "
                              + service.toExternalForm()
                              + " because of exception "
                              + ioe.toString(),
                          null));
              //noinspection UnnecessaryContinue
              continue;
            }
          }
        } finally {
          if (svcIn != null) svcIn.close();
        }
      }
    } catch (IOException e) {
      // FIXME the warning message will NPE with what I have :(
      compilationUnit
          .getErrorCollector()
          .addError(
              new SimpleMessage(
                  "IO Exception attempting to load global transforms:" + e.getMessage(), null));
    }
    try {
      Class.forName("java.lang.annotation.Annotation"); // test for 1.5 JVM
    } catch (Exception e) {
      // we failed, notify the user
      StringBuffer sb = new StringBuffer();
      sb.append("Global ASTTransformations are not enabled in retro builds of groovy.\n");
      sb.append("The following transformations will be ignored:");
      for (Map.Entry<String, URL> entry : transformNames.entrySet()) {
        sb.append('\t');
        sb.append(entry.getKey());
        sb.append('\n');
      }
      compilationUnit
          .getErrorCollector()
          .addWarning(
              new WarningMessage(WarningMessage.POSSIBLE_ERRORS, sb.toString(), null, null));
      return;
    }

    // record the transforms found in the first scan, so that in the 2nd scan, phase operations
    // can be added for only for new transforms that have come in
    if (isFirstScan) {
      for (Map.Entry<String, URL> entry : transformNames.entrySet()) {
        context.getGlobalTransformNames().add(entry.getKey());
      }
      addPhaseOperationsForGlobalTransforms(
          context.getCompilationUnit(), transformNames, isFirstScan);
    } else {
      Iterator<Map.Entry<String, URL>> it = transformNames.entrySet().iterator();
      while (it.hasNext()) {
        Map.Entry<String, URL> entry = it.next();
        if (!context.getGlobalTransformNames().add(entry.getKey())) {
          // phase operations for this transform class have already been added before, so remove
          // from current scan cycle
          it.remove();
        }
      }
      addPhaseOperationsForGlobalTransforms(
          context.getCompilationUnit(), transformNames, isFirstScan);
    }
  }