@Override
  public void run() throws Exception {
    Set<String> artifacts = new LinkedHashSet<String>();
    boolean defaults =
        js == null && jvm == null && src == null && scripts == null && docs == null && all == null;
    if (BooleanUtil.isTrue(all)) {
      artifacts.addAll(Arrays.asList(ArtifactContext.allSuffixes()));
    }
    if (BooleanUtil.isTrue(js) || defaults) {
      artifacts.add(ArtifactContext.JS);
      artifacts.add(ArtifactContext.JS_MODEL);
      artifacts.add(ArtifactContext.RESOURCES);
    } else if (BooleanUtil.isFalse(js)) {
      artifacts.remove(ArtifactContext.JS);
      artifacts.remove(ArtifactContext.JS_MODEL);
      artifacts.remove(ArtifactContext.RESOURCES);
    }
    if (BooleanUtil.isTrue(jvm) || defaults) {
      // put the CAR first since its presence will shortcut the other three
      artifacts.add(ArtifactContext.CAR);
      artifacts.add(ArtifactContext.JAR);
      artifacts.add(ArtifactContext.MODULE_PROPERTIES);
      artifacts.add(ArtifactContext.MODULE_XML);
    } else if (BooleanUtil.isFalse(jvm)) {
      artifacts.remove(ArtifactContext.CAR);
      artifacts.remove(ArtifactContext.JAR);
      artifacts.remove(ArtifactContext.MODULE_PROPERTIES);
      artifacts.remove(ArtifactContext.MODULE_XML);
    }
    if (BooleanUtil.isTrue(src)) {
      artifacts.add(ArtifactContext.SRC);
    } else if (BooleanUtil.isFalse(src)) {
      artifacts.remove(ArtifactContext.SRC);
    }
    if (BooleanUtil.isTrue(scripts)) {
      artifacts.add(ArtifactContext.SCRIPTS_ZIPPED);
    } else if (BooleanUtil.isFalse(scripts)) {
      artifacts.remove(ArtifactContext.SCRIPTS_ZIPPED);
    }
    if (BooleanUtil.isTrue(docs)) {
      artifacts.add(ArtifactContext.DOCS);
    } else if (BooleanUtil.isFalse(docs)) {
      artifacts.remove(ArtifactContext.DOCS);
    }

    // Create the list of ArtifactContexts to copy
    List<ArtifactContext> acs = new ArrayList<ArtifactContext>();
    String[] artifactsArray = new String[artifacts.size()];
    artifacts.toArray(artifactsArray);
    for (ModuleSpec module : modules) {
      if (module != ModuleSpec.DEFAULT_MODULE && !module.isVersioned()) {
        String version =
            checkModuleVersionsOrShowSuggestions(
                getRepositoryManager(), module.getName(), null, ModuleQuery.Type.ALL, null, null);
        module = new ModuleSpec(module.getName(), version);
      }
      ArtifactContext ac =
          new ArtifactContext(module.getName(), module.getVersion(), artifactsArray);
      ac.setIgnoreDependencies(!withDependencies);
      ac.setForceOperation(true);
      acs.add(ac);
    }

    // Now do the actual copying
    final boolean logArtifacts =
        verbose != null && (verbose.contains("all") || verbose.contains("files"));
    ModuleCopycat copier =
        new ModuleCopycat(
            getRepositoryManager(),
            getOutputRepositoryManager(),
            log,
            new ModuleCopycat.CopycatFeedback() {
              @Override
              public boolean beforeCopyModule(ArtifactContext ac, int count, int max)
                  throws IOException {
                String module = ModuleUtil.makeModuleName(ac.getName(), ac.getVersion());
                msg("copying.module", module, count + 1, max).flush();
                return true;
              }

              @Override
              public void afterCopyModule(ArtifactContext ac, int count, int max, boolean copied)
                  throws IOException {
                if (!logArtifacts) {
                  append(") ").msg((copied) ? "copying.ok" : "copying.skipped").newline().flush();
                }
              }

              @Override
              public boolean beforeCopyArtifact(
                  ArtifactContext ac, ArtifactResult ar, int count, int max) throws IOException {
                if (logArtifacts) {
                  if (count == 0) {
                    append(" -- ");
                    append(ar.repositoryDisplayString());
                    newline().flush();
                  }
                  append("    ")
                      .msg("copying.artifact", ar.artifact().getName(), count + 1, max)
                      .flush();
                } else {
                  if (count > 0) {
                    append(", ");
                  } else {
                    append(" (");
                  }
                  String name = ArtifactContext.getSuffixFromFilename(ar.artifact().getName());
                  if (name.startsWith(".") || name.startsWith("-")) {
                    name = name.substring(1);
                  } else if ("module-doc".equals(name)) {
                    name = "doc";
                  }
                  append(name);
                }
                return true;
              }

              @Override
              public void afterCopyArtifact(
                  ArtifactContext ac, ArtifactResult ar, int count, int max, boolean copied)
                  throws IOException {
                if (logArtifacts) {
                  append(" ").msg((copied) ? "copying.ok" : "copying.skipped").newline().flush();
                }
              }

              @Override
              public void notFound(ArtifactContext ac) throws IOException {
                String err =
                    getModuleNotFoundErrorMessage(
                        getRepositoryManager(), ac.getName(), ac.getVersion());
                errorAppend(err);
                errorNewline();
              }
            });
    copier.copyModules(acs);
  }