/**
   * Initializes default class loader. It inherits from classloader, which was used to load this
   * task, therefore build-in classes and resources are loaded automatically.
   *
   * <p>Notice: task's classpath has priority over default classpath, therefore we are able to
   * easily override resources (templates)
   */
  protected void initClassloader() {
    // NOTICE: we should use the same classloader, which was used to load this task
    // otherwise we won't be able to cast instances
    // (that were loaded via different class loaders)

    // we are introducting workaround,
    // that tries to add classpath for current task to original class loader
    // and stores original classpath
    // after the task is executed, classpath is restored

    ClassLoader classLoader = getClass().getClassLoader();
    if (classLoader instanceof AntClassLoader) {
      // add defined classpath
      AntClassLoader antClassLoader = (AntClassLoader) classLoader;
      antClassLoader.setParentFirst(false);
      antClassLoader.setProject(getProject());
      savedClasspath = antClassLoader.getClasspath();

      Path cp = getClasspath();
      // add defined classpath to original path
      cp.add(new Path(getProject(), savedClasspath));
      antClassLoader.setClassPath(cp);
    } else {
      // create new class loader
      classLoader =
          new AntClassLoader(getClass().getClassLoader(), getProject(), getClasspath(), false);
    }

    // set this class loader as new class loader for whole thread
    // ContextClassLoader is used in StringTemplate's PathGroupLoader and other classes
    Thread.currentThread().setContextClassLoader(classLoader);
  }
  /**
   * Main entry point for this task. It creates transformation specified by classname attribute,
   * passes all parameters and executes it.
   *
   * @throws BuildException if an error occured
   */
  @Override
  public void execute() throws BuildException {
    if (getClassname() == null) {
      throw new BuildException("Classname of transformation is not set.");
    }

    if (getSrcFile() != null && getSrcDir() != null) {
      throw new BuildException("Both srcFile and srcDir were set.");
    }

    if (getDestFile() != null && getDestDir() != null) {
      throw new BuildException("Both destFile and destDir were set.");
    }

    if (!resources.isEmpty() && (getSrcFile() != null || getSrcDir() != null)) {
      throw new BuildException("Resources can't be combined with srcFile or srcDir.");
    }

    initClassloader();

    try {
      TransformationLoader loader =
          new TransformationLoader(Thread.currentThread().getContextClassLoader());
      Transformation transObj = null;
      try {
        transObj = loader.getInstance(getClassname());
      } catch (InstantiationException ex) {
        throw new BuildException("Transformation couldn't be instanciated", ex);
      } catch (IllegalAccessException ex) {
        throw new BuildException("Transformation couldn't be accessed", ex);
      }

      if (transObj == null) {
        throw new BuildException("Transformation " + getClassname() + " couldn't be loaded");
      }

      List<MappedFile> files = getMappedFiles();
      if (files.isEmpty()) {
        log(
            "No files specified, " + transObj.getClass().getSimpleName() + " skipped",
            Project.MSG_INFO);
        return;
      }
      for (MappedFile mf : files) {
        log("Mapped file " + mf.getFrom() + " to " + mf.getTo(), Project.MSG_VERBOSE);
      }

      transObj.setParam("baseDir", getProject().getBaseDir().getPath());
      transObj.setMappedFiles(files);
      transObj.setParams(parameters);

      for (String key : parameters.keySet())
        log(
            "Parsed parameter "
                + key
                + " = "
                + parameters.get(key)
                + " ("
                + parameters.get(key).getClass().getName()
                + ")",
            Project.MSG_VERBOSE);

      try {
        log(
            "Executing transformation "
                + transObj.getClass().getSimpleName()
                + " on "
                + mappedFiles.size()
                + " files");
        transObj.execute();
      } catch (TransformationException ex) {
        log("Transformation has failed", ex, Project.MSG_ERR);
        if (isFailonerror()) {
          throw new BuildException("Transformation has failed.", ex);
        }
      }
    } finally {
      restoreClassloader();
    }
  }