blob: 701f5b14a434255ee3e8b262272a8c85f7b2dc33 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2008 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.compare.internal.patch;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.compare.patch.IFilePatch;
import org.eclipse.compare.patch.IFilePatchResult;
import org.eclipse.compare.patch.PatchConfiguration;
import org.eclipse.compare.structuremergeviewer.Differencer;
import org.eclipse.core.resources.IStorage;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
/**
* A file diff represents a set of hunks that were associated with the
* same path in a patch file.
*/
public class FileDiff implements IFilePatch {
private IPath fOldPath, fNewPath;
private long oldDate, newDate;
private List fHunks= new ArrayList();
private DiffProject fProject; //the project that contains this diff
private String header;
private int addedLines, removedLines;
/**
* Create a file diff for the given path and date information.
* @param oldPath the path of the before state of the file
* @param oldDate the timestamp of the before state
* @param newPath the path of the after state
* @param newDate the timestamp of the after state
*/
protected FileDiff(IPath oldPath, long oldDate, IPath newPath, long newDate) {
fOldPath= oldPath;
this.oldDate = oldDate;
fNewPath= newPath;
this.newDate = newDate;
}
/**
* Return the parent project or <code>null</code> if there isn't one.
* @return the parent project or <code>null</code>
*/
public DiffProject getProject() {
return fProject;
}
/**
* Set the project of this diff to the given project.
* This method should only be called from
* {@link DiffProject#add(FileDiff)}
* @param diffProject the parent project
*/
void setProject(DiffProject diffProject) {
if (fProject == diffProject)
return;
if (fProject != null)
fProject.remove(this);
this.fProject= diffProject;
}
/**
* Get the path of the file diff.
* @param reverse whether the path of the before state or after state
* should be used
* @return the path of the file diff
*/
protected IPath getPath(boolean reverse) {
if (getDiffType(reverse) == Differencer.ADDITION) {
if (reverse)
return fOldPath;
return fNewPath;
}
if (reverse && fNewPath != null)
return fNewPath;
if (fOldPath != null)
return fOldPath;
return fNewPath;
}
/**
* Add the hunk to this file diff.
* @param hunk the hunk
*/
protected void add(Hunk hunk) {
fHunks.add(hunk);
hunk.setParent(this);
}
/**
* Remove the hunk from this file diff
* @param hunk the hunk
*/
protected void remove(Hunk hunk) {
fHunks.remove(hunk);
}
/**
* Return the hunks associated with this file diff.
* @return the hunks associated with this file diff
*/
public Hunk[] getHunks() {
return (Hunk[]) fHunks.toArray(new Hunk[fHunks.size()]);
}
/**
* Return the number of hunks associated with this file diff.
* @return the number of hunks associated with this file diff
*/
public int getHunkCount() {
return fHunks.size();
}
/**
* Return the difference type of this file diff.
* @param reverse whether the patch is being reversed
* @return the type of this file diff
*/
public int getDiffType(boolean reverse) {
if (fHunks.size() == 1) {
boolean add = false;
boolean delete = false;
Iterator iter = fHunks.iterator();
while (iter.hasNext()){
Hunk hunk = (Hunk) iter.next();
int type =hunk.getHunkType(reverse);
if (type == Hunk.ADDED){
add = true;
} else if (type == Hunk.DELETED ){
delete = true;
}
}
if (add && !delete){
return Differencer.ADDITION;
} else if (!add && delete){
return Differencer.DELETION;
}
}
return Differencer.CHANGE;
}
/**
* Return the path of this file diff with the specified number
* of leading segments striped.
* @param strip the number of leading segments to strip from the path
* @param reverse whether the patch is being reversed
* @return the path of this file diff with the specified number
* of leading segments striped
*/
protected IPath getStrippedPath(int strip, boolean reverse) {
IPath path= getPath(reverse);
if (strip > 0 && strip < path.segmentCount())
path= path.removeFirstSegments(strip);
return path;
}
/**
* Return the segment count of the path of this file diff.
* @return the segment count of the path of this file diff
*/
public int segmentCount() {
//Update prefix count - go through all of the diffs and find the smallest
//path segment contained in all diffs.
int length= 99;
if (fOldPath != null)
length= Math.min(length, fOldPath.segmentCount());
if (fNewPath != null)
length= Math.min(length, fNewPath.segmentCount());
return length;
}
public IFilePatchResult apply(IStorage contents,
PatchConfiguration configuration, IProgressMonitor monitor) {
FileDiffResult result = new FileDiffResult(this, configuration);
result.refresh(contents, monitor);
return result;
}
public IPath getTargetPath(PatchConfiguration configuration) {
return getStrippedPath(configuration.getPrefixSegmentStripCount(), configuration.isReversed());
}
public FileDiff asRelativeDiff() {
if (fProject == null)
return this;
IPath adjustedOldPath = null;
if (fOldPath != null) {
adjustedOldPath = new Path(null, fProject.getName()).append(fOldPath);
}
IPath adjustedNewPath = null;
if (fNewPath != null) {
adjustedNewPath = new Path(null, fProject.getName()).append(fNewPath);
}
FileDiff diff = new FileDiff(adjustedOldPath, 0, adjustedNewPath, 0);
for (Iterator iterator = fHunks.iterator(); iterator.hasNext();) {
Hunk hunk = (Hunk) iterator.next();
// Creating the hunk adds it to the parent diff
new Hunk(diff, hunk);
}
return diff;
}
public void setHeader(String header) {
this.header = header;
}
public String getHeader() {
return header;
}
public long getBeforeDate() {
return oldDate;
}
public long getAfterDate() {
return newDate;
}
public void setAddedLines(int addedLines) {
this.addedLines = addedLines;
}
public void setRemovedLines(int removedLines) {
this.removedLines = removedLines;
}
public int getAddedLines() {
return addedLines;
}
public int getRemovedLines() {
return removedLines;
}
}