/******************************************************************************* * * Copyright (c) 2004-2011 Oracle Corporation. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * * Kohsuke Kawaguchi, Nikita Levyankov * * *******************************************************************************/ package hudson.scm; import com.google.common.collect.Iterators; import hudson.MarkupText; import hudson.Util; import hudson.model.AbstractBuild; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import org.apache.commons.collections.CollectionUtils; import org.kohsuke.stapler.export.Exported; import org.kohsuke.stapler.export.ExportedBean; /** * Represents SCM change list. * <p/> * < * p/> * Use the "index" view of this object to render the changeset detail page, and * use the "digest" view of this object to render the summary page. For the * change list at project level, see {@link SCM}. * <p/> * < * p/> * {@link Iterator} is expected to return recent changes first. * * @author Kohsuke Kawaguchi * @author Nikia Levyankov */ @ExportedBean(defaultVisibility = 999) public abstract class ChangeLogSet<T extends ChangeLogSet.Entry> implements Iterable<T> { /** * {@link AbstractBuild} whose change log this object represents. */ //TODO: review and check whether we can do it private public final AbstractBuild<?, ?> build; protected ChangeLogSet(AbstractBuild<?, ?> build) { this.build = build; } public AbstractBuild<?, ?> getBuild() { return build; } /** * Returns true if there's no change. * * @return if {@link #getLogs()} returns empty or null list */ public boolean isEmptySet() { return CollectionUtils.isEmpty(getLogs()); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public Iterator<T> iterator() { return isEmptySet() ? Iterators.<T>emptyIterator() : getLogs().iterator(); } /** * All changes in this change set. */ // method for the remote API. @Exported public final Object[] getItems() { List<T> r = new ArrayList<T>(); for (T t : this) { r.add(t); } return r.toArray(); } /** * Optional identification of the kind of SCM being used. * * @return a short token, such as the SCM's main CLI executable name * @since 1.284 */ @Exported public String getKind() { return null; } /** * Returns change log entries. * * @return logs. */ public abstract Collection<T> getLogs(); /** * Constant instance that represents no changes. */ public static ChangeLogSet<? extends ChangeLogSet.Entry> createEmpty(AbstractBuild build) { return new EmptyChangeLogSet(build); } @ExportedBean(defaultVisibility = 999) public static abstract class Entry implements ChangeLogEntry { private ChangeLogSet parent; public ChangeLogSet getParent() { return parent; } /** * Should be invoked before a {@link ChangeLogSet} is exposed to public. */ protected void setParent(ChangeLogSet parent) { this.parent = parent; } /** * Returns a set of paths in the workspace that was affected by this * change. <p> Noted: since this is a new interface, some of the SCMs * may not have implemented this interface. The default implementation * for this interface is throw UnsupportedOperationException <p> It * doesn't throw NoSuchMethodException because I rather to throw a * runtime exception * * @return AffectedFile never null. * @since 1.309 */ public Collection<? extends AffectedFile> getAffectedFiles() { String scm = getScmKind(); throw new UnsupportedOperationException("getAffectedFiles() is not implemented by " + scm); } /** * Gets the text fully marked up by {@link ChangeLogAnnotator}. */ public String getMsgAnnotated() { MarkupText markup = new MarkupText(getMsg()); for (ChangeLogAnnotator a : ChangeLogAnnotator.all()) { a.annotate(parent.build, this, markup); } return markup.toString(false); } /** * Message escaped for HTML */ public String getMsgEscaped() { return Util.escape(getMsg()); } /** * Return a form of commit identification either an ID, name or identity. * * @return String A commit ID. */ @Exported public String getCommitId() { return null; } /** * {@inheritDoc} */ public String getCurrentRevision() { String scm = getScmKind(); throw new UnsupportedOperationException("getCurrentRevision() is not implemented by " + scm); } /** * Returns scm name. Help method used for throwing exception while * executing unimplemented method. * * @return name. */ private String getScmKind() { String scm = "this SCM"; ChangeLogSet parent = getParent(); if (null != parent) { String kind = parent.getKind(); if (null != kind && kind.trim().length() > 0) { scm = kind; } } return scm; } } /** * Represents a file change. Contains filename, edit type, etc. * * I checked the API names against some some major SCMs and most SCMs can * adapt to this interface with very little changes * * @see ChangeLogSet.Entry#getAffectedFiles() */ public interface AffectedFile { /** * The path in the workspace that was affected <p> Contains string like * 'foo/bar/zot'. No leading/trailing '/', and separator must be * normalized to '/'. * * @return never null. */ String getPath(); /** * Return whether the file is new/modified/deleted */ EditType getEditType(); } }