/**
   * This method attempts to find a matching dependency quickly, by bypassing full resolution of the
   * classpath path group for a plugin. It assumes the following: 1. The dependendency is directly
   * defined by the plugin artifact (this should always be true when a plugin implements an abstract
   * method of another) 2. The plugin artifact defines no overrides that effect the dependency (this
   * should be true as the plugin should just define the version it wants directly). If the
   * performance of resolvePathGroup is improved this method could be replaced by the version below.
   */
  private RepoArtifactId fastFindMatchingDependency(Target target, RepoArtifactId id) {
    // Find the matching id that belongs to the classpath
    PathGroup pathGroup = target.getPathGroup("classpath");
    RepoArtifactId match = null;
    String matchingPath = null;
    for (Iterator i = target.getPlugin().getArtifact().getDependencies().iterator();
        i.hasNext(); ) {
      RepoDependency dependency = (RepoDependency) i.next();
      if (dependency.getId().matches(id)) {
        for (Iterator j = dependency.getPathSpecs().iterator(); j.hasNext(); ) {
          RepoPathSpec pathSpec = (RepoPathSpec) j.next();
          if (pathGroup.getPaths().contains("plugin." + pathSpec.getTo())) {
            match = dependency.getId();
            matchingPath = pathSpec.getTo();
            break;
          }
        }
      }
    }

    Assert.isTrue(
        match != null,
        target.getPlugin().getArtifact().getId().toShortString()
            + " does not declare a dependency that matches "
            + id);

    // Apply any project overrides
    for (Iterator i = overrides.iterator(); i.hasNext(); ) {
      ws.quokka.core.model.Override override = (ws.quokka.core.model.Override) i.next();
      if (override.getWithVersion() != null && override.matches(match)) {
        Set paths = override.matchingPluginPaths(target.getPlugin().getArtifact().getId());
        if (paths.contains(matchingPath) || ((paths.size() == 1) && paths.contains("*"))) {
          log.verbose("Overriding " + match.toShortString() + " to " + override.getWithVersion());
          if (log.isDebugEnabled()) {
            log.debug(
                "Applied "
                    + override
                    + (override.getLocator() == null ? "" : " from " + override.getLocator()));
          }
          return new RepoArtifactId(
              id.getGroup(), id.getName(), id.getType(), override.getWithVersion());
        }
      }
    }

    return match;

    // Slow code
    //        List artifacts = resolvePathGroup(target, "classpath");
    //        for (Iterator i = artifacts.iterator(); i.hasNext();) {
    //            RepoArtifact artifact = (RepoArtifact) i.next();
    //            if (artifact.getId().matches(id)) {
    //                return artifact.getId();
    //            }
    //        }
  }
  private void addPathSpecDefaults(DependencySet dependencySet) {
    for (Iterator i = dependencySet.getDependencies().iterator(); i.hasNext(); ) {
      RepoDependency dependency = (RepoDependency) i.next();

      for (Iterator j = dependency.getPathSpecs().iterator(); j.hasNext(); ) {
        RepoPathSpec pathSpec = (RepoPathSpec) j.next();

        if (dependency instanceof PluginDependency) {
          // TODO: use defaults for plugin dependencies (from path defined in the repository?)
        } else {
          RepoPath path = (RepoPath) resolvedPaths.get(pathSpec.getTo());
          Assert.isTrue(
              path != null,
              pathSpec.getLocator(),
              "The 'to' path '" + pathSpec.getTo() + "' is not defined in the project");
          pathSpec.mergeDefaults(path);
        }
      }
    }
  }
  public ResolvedPath _getResolvedPluginPath(
      Plugin plugin, String pathId, boolean mergeWithCore, boolean overrideCore, boolean flatten) {
    // Create a mock artifact for the resolver as a way to add user specified path specs and
    // overrides
    RepoArtifact artifact = new RepoArtifact();
    RepoDependency dependency = new RepoDependency();
    RepoArtifactId pluginId = plugin.getArtifact().getId();
    dependency.setId(pluginId);
    artifact.addDependency(dependency);

    String id = "plugin";
    artifact.addPath(new RepoPath(id, "Plugin path", true, true));

    // Add dependencies
    if (plugin.getDependency() != null) {
      for (Iterator j = plugin.getDependency().getPathSpecs().iterator(); j.hasNext(); ) {
        RepoPathSpec pluginPathSpec = (RepoPathSpec) j.next();

        if (pluginPathSpec.getFrom().equals(pathId)) {
          // Add user specifications. Ignore the mandatory flag and to paths as they
          // are not relevant. However, allow descend to be false in case the writer of the
          // plugin added bogus dependencies.
          dependency.addPathSpec(
              new RepoPathSpec(
                  pathId,
                  id,
                  pluginPathSpec.getOptions(),
                  (pluginPathSpec.isDescend() == null) ? Boolean.TRUE : pluginPathSpec.isDescend(),
                  Boolean.TRUE));
        }
      }
    }

    if (dependency.getPathSpecs().size() == 0) {
      // Add default ... user hasn't specified anything
      dependency.addPathSpec(new RepoPathSpec(pathId, id, null, Boolean.TRUE, Boolean.TRUE));
    }

    // Add core overrides if applicable
    if (overrideCore) {
      for (Iterator j = coreOverrides.iterator(); j.hasNext(); ) {
        RepoOverride override = (RepoOverride) j.next();
        artifact.addOverride(override);
      }
    }

    // Add overrides
    for (Iterator i = overrides.iterator(); i.hasNext(); ) {
      ws.quokka.core.model.Override override = (ws.quokka.core.model.Override) i.next();
      Set paths = override.matchingPluginPaths(plugin.getArtifact().getId());

      if (paths.contains(pathId) || ((paths.size() == 1) && paths.contains("*"))) {
        // Create a copy of the override, moving matching plugin paths to be standard paths
        RepoOverride copy =
            new RepoOverride(
                Collections.singleton("*"),
                override.getGroup(),
                override.getName(),
                override.getType(),
                override.getVersion(),
                override.getWithVersion(),
                override.getWithPathSpecs());
        artifact.addOverride(copy);
      }
    }

    // Remove the plugin itself from the path
    // TODO: Look into a better way of doing this, perhaps modifying Resolver so it has the option
    // of not adding the root in first place.
    ResolvedPath path = pathResolver.resolvePath(id, artifact);
    path.setId("Plugin path '" + pathId + "' from " + pluginId.toShortString());

    List artifacts = new ArrayList();

    for (Iterator i = path.getArtifacts().iterator(); i.hasNext(); ) {
      artifact = (RepoArtifact) i.next();

      if (!artifact.getId().equals(pluginId)) {
        artifacts.add(artifact);
      }

      if (pluginId.equals(artifact.getId().getAnnotations().get("declaredBy"))) {
        artifact.getId().getAnnotations().remove("declaredBy");
      }
    }

    path = new ResolvedPath(path.getId(), artifacts);
    path = handleMergeAndFlatten(mergeWithCore, flatten, path);

    return path;
  }