/*
 * Copyright 2012-2014 the original author or authors.
 *
 * Licensed 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 org.springframework.boot.dependency.tools;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.springframework.boot.dependency.tools.Dependency.Exclusion;

/**
 * Abstract base implementation for {@link Dependencies}.
 *
 * @author Phillip Webb
 * @author Andy Wilkinson
 * @since 1.1.0
 */
abstract class AbstractDependencies implements Dependencies {

	private final Map<ArtifactAndGroupId, Dependency> byArtifactAndGroupId;

	private final Map<String, Dependency> byArtifactId;

	public AbstractDependencies() {
		this.byArtifactAndGroupId = new LinkedHashMap<ArtifactAndGroupId, Dependency>();
		this.byArtifactId = new LinkedHashMap<String, Dependency>();
	}

	@Override
	public Dependency find(String groupId, String artifactId) {
		return this.byArtifactAndGroupId.get(new ArtifactAndGroupId(groupId, artifactId));
	}

	@Override
	public Dependency find(String artifactId) {
		return this.byArtifactId.get(artifactId);
	}

	@Override
	public Iterator<Dependency> iterator() {
		return this.byArtifactAndGroupId.values().iterator();
	}

	protected void add(ArtifactAndGroupId artifactAndGroupId, Dependency dependency) {
		Dependency existing = this.byArtifactAndGroupId.get(artifactAndGroupId);
		if (existing != null) {
			dependency = mergeDependencies(existing, dependency);
		}
		this.byArtifactAndGroupId.put(artifactAndGroupId, dependency);
		this.byArtifactId.put(dependency.getArtifactId(), dependency);
	}

	private Dependency mergeDependencies(Dependency existingDependency,
			Dependency newDependency) {
		List<Exclusion> combinedExclusions = new ArrayList<Exclusion>();
		combinedExclusions.addAll(existingDependency.getExclusions());
		combinedExclusions.addAll(newDependency.getExclusions());

		return new Dependency(newDependency.getGroupId(), newDependency.getArtifactId(),
				newDependency.getVersion(), combinedExclusions);
	}

	/**
	 * Simple holder for an artifact+group ID.
	 */
	protected static class ArtifactAndGroupId {

		private final String groupId;

		private final String artifactId;

		public ArtifactAndGroupId(Dependency dependency) {
			this(dependency.getGroupId(), dependency.getArtifactId());
		}

		public ArtifactAndGroupId(String groupId, String artifactId) {
			Assert.notNull(groupId, "GroupId must not be null");
			Assert.notNull(artifactId, "ArtifactId must not be null");
			this.groupId = groupId;
			this.artifactId = artifactId;
		}

		public Dependency newDependency(String version) {
			return new Dependency(this.groupId, this.artifactId, version);
		}

		@Override
		public int hashCode() {
			return this.groupId.hashCode() * 31 + this.artifactId.hashCode();
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj) {
				return true;
			}
			if (obj == null) {
				return false;
			}
			if (getClass() == obj.getClass()) {
				ArtifactAndGroupId other = (ArtifactAndGroupId) obj;
				boolean result = true;
				result &= this.groupId.equals(other.groupId);
				result &= this.artifactId.equals(other.artifactId);
				return result;
			}
			return false;
		}

	}

}

@@ -773,17 +773,26 @@
 .util.Li
-s
+nkedHashSe
 t;%0Aimpor
@@ -807,16 +807,38 @@
 til.Map;
+%0Aimport java.util.Set;
 %0A%0Aimport
@@ -963,24 +963,25 @@
 dencies%7D.%0A *
+ 
 %0A * @author 
@@ -2273,19 +2273,18 @@
 cy) %7B%0A%09%09
-Lis
+Se
 t%3CExclus
@@ -2313,24 +2313,28 @@
 s = new 
-ArrayLis
+LinkedHashSe
 t%3CExclus
@@ -2466,17 +2466,16 @@
 ons());%0A
-%0A
 %09%09return
@@ -2581,16 +2581,41 @@
 sion(), 
+new ArrayList%3CExclusion%3E(
 combined
@@ -2625,16 +2625,17 @@
 lusions)
+)
 ;%0A%09%7D%0A%0A%09/

Switch mergeDependencies to use a set.