/*
* Copyright 2017 ThoughtWorks, Inc.
*
* 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 com.thoughtworks.go.domain;
import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.thoughtworks.go.config.CaseInsensitiveString;
import com.thoughtworks.go.config.materials.Materials;
import com.thoughtworks.go.config.materials.ScmMaterial;
import com.thoughtworks.go.config.materials.dependency.DependencyMaterial;
import com.thoughtworks.go.config.materials.git.GitMaterial;
import com.thoughtworks.go.domain.materials.*;
import com.thoughtworks.go.domain.materials.dependency.DependencyMaterialRevision;
import com.thoughtworks.go.domain.materials.git.GitMaterialUpdater;
import com.thoughtworks.go.util.ObjectUtil;
import com.thoughtworks.go.util.command.EnvironmentVariableContext;
import org.apache.log4j.Logger;
import static com.thoughtworks.go.util.ExceptionUtils.bombIfNull;
// Understands multiple materials each with their own revision
public class MaterialRevisions implements Serializable, Iterable<MaterialRevision> {
private static final Logger LOGGER = Logger.getLogger(MaterialRevisions.class);
public static final MaterialRevisions EMPTY = new MaterialRevisions();
private List<MaterialRevision> revisions = new ArrayList<>();
public MaterialRevisions(MaterialRevision... revisions) {
this.revisions.addAll(Arrays.asList(revisions));
}
public MaterialRevisions(Collection<MaterialRevision> revisions) {
this(revisions.toArray(new MaterialRevision[]{}));
}
public void addAll(MaterialRevisions materialRevisions) {
this.revisions.addAll(materialRevisions.getRevisions());
}
public void addRevision(MaterialRevision materialRevision) {
revisions.add(materialRevision);
}
public void addRevision(Material material, List<Modification> modifications) {
revisions.add(new MaterialRevision(material, modifications));
}
public void addRevision(Material material, Modification... modifications) {
for (Modification modification : modifications) {
bombIfNull(modification, "Modification cannot be null.");
}
addRevision(material, Arrays.asList(modifications));
}
public int totalNumberOfModifications() {
int count = 0;
for (MaterialRevision revision : revisions) {
count += revision.numberOfModifications();
}
return count;
}
public int numberOfRevisions() {
return revisions.size();
}
public void accept(ModificationVisitor visitor) {
for (MaterialRevision revision : revisions) {
revision.accept(visitor);
}
}
public boolean isEmpty() {
return totalNumberOfModifications() == 0;
}
public Date getDateOfLatestModification() {
return firstModifiedMaterialRevision().getDateOfLatestModification();
}
public String buildCausedBy() {
return firstModifiedMaterialRevision().buildCausedBy();
}
public String buildCauseMessage() {
return firstModifiedMaterialRevision().buildCauseMessage();
}
public String latestRevision() {
return firstModifiedMaterialRevision().getRevision().getRevision();
}
public MaterialRevision firstModifiedMaterialRevision() {
for (MaterialRevision revision : revisions) {
if (revision.isChanged()) {
return revision;
}
}
return revisions.isEmpty() ? new NullMaterialRevision() : revisions.get(0);
}
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
MaterialRevisions revisions1 = (MaterialRevisions) o;
if (!revisions.equals(revisions1.revisions)) {
return false;
}
return true;
}
public int hashCode() {
return revisions.hashCode();
}
/**
* @deprecated Very very evil - TODO: get rid of this as part of #2055
*/
public Materials getMaterials() {
Materials materials = new Materials();
for (MaterialRevision revision : revisions) {
materials.add(revision.getMaterial());
}
return materials;
}
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("MaterialRevision[\n");
for (MaterialRevision revision : revisions) {
builder.append("\t" + revision);
builder.append("\n");
}
builder.append("]");
return builder.toString();
}
public MaterialRevision getMaterialRevision(int index) {
return revisions.get(index);
}
private boolean internalHasChangedSince(MaterialRevisions original) {
if (revisions.size() != original.revisions.size()) {
return true;
}
for (MaterialRevision revision : revisions) {
MaterialRevision originalRevision = original.findRevisionFor(revision.getMaterial());
if (originalRevision == null || revision.hasChangedSince(originalRevision)) {
return true;
}
}
return false;
}
public boolean hasChangedSince(MaterialRevisions original) {
return filter(original).internalHasChangedSince(original);
}
public MaterialRevision findRevisionFor(Material material) {
for (MaterialRevision revision : revisions) {
if (material.getPipelineUniqueFingerprint().equals(revision.getMaterial().getPipelineUniqueFingerprint())) {
return revision;
}
}
return null;
}
public MaterialRevision findRevisionFor(MaterialConfig material) {
for (MaterialRevision revision : revisions) {
if (material.getPipelineUniqueFingerprint().equals(revision.getMaterial().getPipelineUniqueFingerprint())) {
return revision;
}
}
return null;
}
public MaterialRevision findRevisionUsingMaterialFingerprintFor(Material material) {
for (MaterialRevision revision : revisions) {
if (material.getFingerprint().equals(revision.getMaterial().getFingerprint())) {
return revision;
}
}
return null;
}
public List<MaterialRevision> getRevisions() {
return revisions;
}
public Iterator<MaterialRevision> iterator() {
return revisions.iterator();
}
public boolean containsMyCheckin(Matcher matcher) {
for (MaterialRevision materialRevision : this) {
for (Modification modification : materialRevision.getModifications()) {
String fullComment = String.format("%s %s", modification.getUserName(), modification.getComment());
if (matcher.matches(fullComment)) {
return true;
}
}
}
return false;
}
public boolean isSameAs(MaterialRevisions other) {
if (!getMaterials().equals(other.getMaterials())) {
return false;
}
for (MaterialRevision materialRevision : revisions) {
MaterialRevision peer = other.findRevisionFor(materialRevision.getMaterial());
if (peer == null) {
continue;
}
if (!materialRevision.hasSameHeadAs(peer)) {
return false;
}
}
return true;
}
public MaterialRevision getMaterialRevision(String folder) {
for (MaterialRevision materialRevision : revisions) {
if (ObjectUtil.nullSafeEquals(folder, materialRevision.getMaterial().getFolder())) {
return materialRevision;
}
}
return null;
}
private MaterialRevisions filter(MaterialRevisions other) {
MaterialRevisions filtered = new MaterialRevisions();
for (MaterialRevision myRevision : revisions) {
MaterialRevision originalRevision = other.findRevisionFor(myRevision.getMaterial());
filtered.addRevision(myRevision.filter(originalRevision));
}
return filtered;
}
public Map<CaseInsensitiveString, String> getNamedRevisions() {
Map<CaseInsensitiveString, String> results = new HashMap<>();
for (MaterialRevision mr : revisions) {
CaseInsensitiveString materialName = mr.getMaterial().getName();
if (!CaseInsensitiveString.isBlank(materialName)) {
results.put(materialName, getRevisionValueOf(mr.getRevision()));
}
}
return results;
}
private String getRevisionValueOf(Revision revision) {
if (revision instanceof DependencyMaterialRevision) {
return ((DependencyMaterialRevision) revision).getPipelineLabel();
}
return revision.getRevision();
}
public DependencyMaterialRevision findDependencyMaterialRevision(String pipelineName) {
for (MaterialRevision materialRevision : this) {
Revision revision = materialRevision.getRevision();
if (revision instanceof DependencyMaterialRevision) {
DependencyMaterialRevision dependencyMaterialRevision = (DependencyMaterialRevision) revision;
if (dependencyMaterialRevision.getPipelineName().equalsIgnoreCase(pipelineName)) {
return dependencyMaterialRevision;
}
}
}
return null;
}
public boolean isMissingModifications() {
if (isEmpty()) {
return true;
}
for (MaterialRevision materialRevision : this) {
if (materialRevision.getModifications().isEmpty()) {
return true;
}
}
return false;
}
public void populateEnvironmentVariables(EnvironmentVariableContext context, File workingDir) {
for (MaterialRevision revision : this) {
revision.populateEnvironmentVariables(context, workingDir);
}
}
public Modifications getModifications(Material material) {
for (MaterialRevision revision : revisions) {
if (revision.getMaterial().equals(material)) {
return revision.getModifications();
}
}
return new Modifications();
}
public boolean containsModificationFor(Material material) {
for (MaterialRevision materialRevision : this) {
if (material.equals(materialRevision.getMaterial())) {
return true;
}
}
return false;
}
public boolean containsModificationForFingerprint(Material material) {
String pipelineUniqueFingerprint = material.getPipelineUniqueFingerprint();
for (MaterialRevision materialRevision : this) {
if (pipelineUniqueFingerprint.equals(materialRevision.getMaterial().getPipelineUniqueFingerprint())) {
return true;
}
}
return false;
}
public MaterialRevision findRevisionForFingerPrint(String fingerprint) {//TODO: pass material in, it has a method to equate fingerprints(this is bad encapsulation) -jj/shilpa
for (MaterialRevision revision : revisions) {
if (fingerprint.equals(revision.getMaterial().getFingerprint())) {
return revision;
}
}
return null;
}
public void updateRevisionChangedStatus(MaterialRevisions original) {
for (MaterialRevision materialRevision : this) {
materialRevision.updateRevisionChangedStatus(original.findRevisionFor(materialRevision.getMaterial()));
}
}
public boolean hasDependencyMaterials() {
for (MaterialRevision materialRevision : this) {
if (materialRevision.getMaterial() instanceof DependencyMaterial) {
return true;
}
}
return false;
}
public BuildCommand updateToCommand(String baseDir) {
List<BuildCommand> commands = new ArrayList<>();
for (MaterialRevision revision : revisions) {
Material material = revision.getMaterial();
if(material instanceof ScmMaterial) {
if (material instanceof GitMaterial) {
GitMaterialUpdater updater = new GitMaterialUpdater((GitMaterial) material);
commands.add(updater.updateTo(baseDir, revision.toRevisionContext()));
} else {
commands.add(BuildCommand.fail("%s Material is not supported for new build command agent", material.getTypeForDisplay()));
}
}
}
return BuildCommand.compose(commands);
}
}