blob: 90c1737cfa900453f27b20daf1f47f7ea927aa75 [file] [log] [blame]
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
package org.eclipse.compare.internal.patch;
import java.io.*;
import java.util.*;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.compare.*;
import org.eclipse.compare.internal.*;
import org.eclipse.compare.structuremergeviewer.*;
/**
* Shows the parsed patch file and any mismatches
* between files, hunks and the currently selected
* resources.
*/
/* package */ class PreviewPatchPage extends WizardPage {
/**
* Used with CompareInput
*/
static class HunkInput implements ITypedElement, IStreamContentAccessor {
String fContent;
String fType;
HunkInput(String type, String s) {
fType= type;
fContent= s;
}
public Image getImage() {
return null;
}
public String getName() {
return PatchMessages.getString("PreviewPatchPage.NoName.text"); //$NON-NLS-1$
}
public String getType() {
return fType;
}
public InputStream getContents() {
return new ByteArrayInputStream(fContent.getBytes());
}
};
private PatchWizard fPatchWizard;
private Tree fTree;
private Combo fStripPrefixSegments;
private CompareViewerSwitchingPane fHunkViewer;
private Button fIgnoreWhitespaceButton;
private Button fReversePatchButton;
private Text fFuzzField;
private Image fNullImage;
private Image fAddImage;
private Image fDelImage;
private Image fErrorImage;
private Image fErrorAddImage;
private Image fErrorDelImage;
private CompareConfiguration fCompareConfiguration;
/* package */ PreviewPatchPage(PatchWizard pw) {
super("PreviewPatchPage", //$NON-NLS-1$
PatchMessages.getString("PreviewPatchPage.title"), null); //$NON-NLS-1$
setMessage(PatchMessages.getString("PreviewPatchPage.message")); //$NON-NLS-1$
fPatchWizard= pw;
//setPageComplete(false);
int w= 16;
ImageDescriptor addId= CompareUIPlugin.getImageDescriptor("ovr16/add_ov.gif"); //$NON-NLS-1$
ImageDescriptor delId= CompareUIPlugin.getImageDescriptor("ovr16/del_ov.gif"); //$NON-NLS-1$
ImageDescriptor errId= CompareUIPlugin.getImageDescriptor("ovr16/error_ov.gif"); //$NON-NLS-1$
Image errIm= errId.createImage();
fNullImage= new DiffImage(null, null, w).createImage();
fAddImage= new DiffImage(null, addId, w).createImage();
fDelImage= new DiffImage(null, delId, w).createImage();
fErrorImage= new DiffImage(errIm, null, w).createImage();
fErrorAddImage= new DiffImage(errIm, addId, w).createImage();
fErrorDelImage= new DiffImage(errIm, delId, w).createImage();
fCompareConfiguration= new CompareConfiguration();
fCompareConfiguration.setLeftEditable(false);
fCompareConfiguration.setLeftLabel(PatchMessages.getString("PreviewPatchPage.Left.title")); //$NON-NLS-1$
fCompareConfiguration.setRightEditable(false);
fCompareConfiguration.setRightLabel(PatchMessages.getString("PreviewPatchPage.Right.title")); //$NON-NLS-1$
}
/* (non-Javadoc)
* Method declared in WizardPage
*/
public void setVisible(boolean visible) {
if (visible)
buildTree();
super.setVisible(visible);
}
Image getImage(Diff diff) {
if (diff.fMatches) {
switch (diff.getType()) {
case Differencer.ADDITION:
return fAddImage;
case Differencer.DELETION:
return fDelImage;
}
return fNullImage;
}
switch (diff.getType()) {
case Differencer.ADDITION:
return fErrorAddImage;
case Differencer.DELETION:
return fErrorDelImage;
}
return fErrorImage;
}
Image getImage(Hunk hunk) {
if (hunk.fMatches)
return fNullImage;
return fErrorImage;
}
public void createControl(Composite parent) {
Composite composite= new Composite(parent, SWT.NULL);
composite.setLayout(new GridLayout());
composite.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL | GridData.HORIZONTAL_ALIGN_FILL));
setControl(composite);
buildPatchOptionsGroup(composite);
Splitter splitter= new Splitter(composite, SWT.VERTICAL);
splitter.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL
| GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_VERTICAL));
// top pane showing diffs and hunks in a check box tree
fTree= new Tree(splitter, SWT.CHECK | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
GridData gd= new GridData();
gd.verticalAlignment= GridData.FILL;
gd.horizontalAlignment= GridData.FILL;
gd.grabExcessHorizontalSpace= true;
gd.grabExcessVerticalSpace= true;
fTree.setLayoutData(gd);
// bottom pane showing hunks in compare viewer
fHunkViewer= new CompareViewerSwitchingPane(splitter, SWT.BORDER | SWT.FLAT) {
protected Viewer getViewer(Viewer oldViewer, Object input) {
return CompareUI.findContentViewer(oldViewer, (ICompareInput)input, this, fCompareConfiguration);
}
};
gd= new GridData();
gd.verticalAlignment= GridData.FILL;
gd.horizontalAlignment= GridData.FILL;
gd.grabExcessHorizontalSpace= true;
gd.grabExcessVerticalSpace= true;
fHunkViewer.setLayoutData(gd);
// register listeners
fTree.addSelectionListener(
new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
TreeItem ti= (TreeItem) e.item;
Object data= e.item.getData();
if (e.detail == SWT.CHECK) {
boolean checked= ti.getChecked();
if (data instanceof Hunk) {
Hunk hunk= (Hunk) data;
checked= checked && hunk.fMatches;
//hunk.setEnabled(checked);
ti.setChecked(checked);
updateGrayedState(ti);
} else if (data instanceof Diff) {
updateCheckedState(ti);
}
} else {
if (data instanceof Hunk)
PreviewPatchPage.this.fHunkViewer.setInput(createInput((Hunk)data));
else
PreviewPatchPage.this.fHunkViewer.setInput(null);
}
}
}
);
// creating tree's content
buildTree();
// WorkbenchHelp.setHelp(composite, new DialogPageContextComputer(this, PATCH_HELP_CONTEXT_ID));
}
/**
* Create the group for setting various patch options
*/
private void buildPatchOptionsGroup(Composite parent) {
final Patcher patcher= fPatchWizard.getPatcher();
Group group= new Group(parent, SWT.NONE);
group.setText(PatchMessages.getString("PreviewPatchPage.PatchOptions.title")); //$NON-NLS-1$
GridLayout layout= new GridLayout();
layout.numColumns= 5;
group.setLayout(layout);
group.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
//fPatchFileGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
// 1st row
new Label(group, SWT.NONE).setText(PatchMessages.getString("PreviewPatchPage.IgnoreSegments.text")); //$NON-NLS-1$
fStripPrefixSegments= new Combo(group, SWT.DROP_DOWN | SWT.READ_ONLY | SWT.SIMPLE);
int prefixCnt= patcher.getStripPrefixSegments();
String prefix= Integer.toString(prefixCnt);
fStripPrefixSegments.add(prefix);
fStripPrefixSegments.setText(prefix);
addSpacer(group);
fReversePatchButton= new Button(group, SWT.CHECK);
fReversePatchButton.setText(PatchMessages.getString("PreviewPatchPage.ReversePatch.text")); //$NON-NLS-1$
addSpacer(group);
// 2nd row
Label l= new Label(group, SWT.NONE);
l.setText(PatchMessages.getString("PreviewPatchPage.FuzzFactor.text")); //$NON-NLS-1$
l.setToolTipText(PatchMessages.getString("PreviewPatchPage.FuzzFactor.tooltip")); //$NON-NLS-1$
fFuzzField= new Text(group, SWT.BORDER);
fFuzzField.setText("2"); //$NON-NLS-1$
GridData gd2= new GridData(GridData.HORIZONTAL_ALIGN_CENTER);
gd2.widthHint= 30;
fFuzzField.setLayoutData(gd2);
addSpacer(group);
fIgnoreWhitespaceButton= new Button(group, SWT.CHECK);
fIgnoreWhitespaceButton.setText(PatchMessages.getString("PreviewPatchPage.IgnoreWhitespace.text")); //$NON-NLS-1$
addSpacer(group);
// register listeners
if (fStripPrefixSegments != null)
fStripPrefixSegments.addSelectionListener(
new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
if (patcher.setStripPrefixSegments(getStripPrefixSegments()))
updateTree();
}
}
);
fReversePatchButton.addSelectionListener(
new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
if (patcher.setReversed(fReversePatchButton.getSelection()))
updateTree();
}
}
);
fIgnoreWhitespaceButton.addSelectionListener(
new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
if (patcher.setIgnoreWhitespace(fIgnoreWhitespaceButton.getSelection()))
updateTree();
}
}
);
fFuzzField.addModifyListener(
new ModifyListener() {
public void modifyText(ModifyEvent e) {
if (patcher.setFuzz(getFuzzFactor()))
updateTree();
}
}
);
}
ICompareInput createInput(Hunk hunk) {
String[] lines= hunk.fLines;
StringBuffer left= new StringBuffer();
StringBuffer right= new StringBuffer();
for (int i= 0; i < lines.length; i++) {
String line= lines[i];
String rest= line.substring(1);
switch (line.charAt(0)) {
case ' ':
left.append(rest);
right.append(rest);
break;
case '-':
left.append(rest);
break;
case '+':
right.append(rest);
break;
}
}
Diff diff= hunk.fParent;
IPath path= diff.getPath();
String type= path.getFileExtension();
return new DiffNode(new HunkInput(type, left.toString()), new HunkInput(type, right.toString()));
}
/**
* Builds a tree from list of Diffs.
* As a side effect it calculates the maximum number of segments
* in all paths.
*/
private void buildTree() {
setPageComplete(true);
if (fTree != null && !fTree.isDisposed()) {
fTree.removeAll();
fHunkViewer.setInput(null);
int length= 99;
Diff[] diffs= fPatchWizard.getPatcher().getDiffs();
if (diffs != null) {
for (int i= 0; i < diffs.length; i++) {
Diff diff= diffs[i];
TreeItem d= new TreeItem(fTree, SWT.NULL);
d.setData(diff);
d.setImage(getImage(diff));
if (diff.fOldPath != null)
length= Math.min(length, diff.fOldPath.segmentCount());
if (diff.fNewPath != null)
length= Math.min(length, diff.fNewPath.segmentCount());
java.util.List hunks= diff.fHunks;
java.util.Iterator iter= hunks.iterator();
while (iter.hasNext()) {
Hunk hunk= (Hunk) iter.next();
TreeItem h= new TreeItem(d, SWT.NULL);
h.setData(hunk);
h.setText(hunk.getDescription());
}
}
}
if (fStripPrefixSegments != null && length != 99)
for (int i= 1; i < length; i++)
fStripPrefixSegments.add(Integer.toString(i));
}
updateTree();
}
private IFile existsInSelection(IPath path) {
IResource target= fPatchWizard.getTarget();
if (target instanceof IFile) {
IFile file= (IFile) target;
if (matches(file.getFullPath(), path))
return file;
} else if (target instanceof IContainer) {
IContainer c= (IContainer) target;
if (c.exists(path))
return c.getFile(path);
}
return null;
}
/**
* Returns true if path completely matches the end of fullpath
*/
private boolean matches(IPath fullpath, IPath path) {
for (IPath p= fullpath; path.segmentCount() <= p.segmentCount();
p= p.removeFirstSegments(1)) {
if (p.equals(path))
return true;
}
return false;
}
/**
* Updates label and checked state of tree items.
*/
private void updateTree() {
if (fTree == null || fTree.isDisposed())
return;
int strip= getStripPrefixSegments();
TreeItem[] children= fTree.getItems();
for (int i= 0; i < children.length; i++) {
TreeItem item= children[i];
Diff diff= (Diff) item.getData();
diff.fMatches= false;
String error= null;
boolean create= false;
IFile file= null;
if (diff.getType() == Differencer.ADDITION) {
IPath p= diff.fNewPath;
if (strip > 0 && strip < p.segmentCount())
p= p.removeFirstSegments(strip);
file= existsInSelection(p);
if (file == null) {
diff.fMatches= true;
} else {
// file already exists
error= PatchMessages.getString("PreviewPatchPage.FileExists.error"); //$NON-NLS-1$
}
create= true;
} else {
IPath p= diff.fOldPath;
if (strip > 0 && strip < p.segmentCount())
p= p.removeFirstSegments(strip);
file= existsInSelection(p);
diff.fMatches= false;
if (file != null) {
if (file.isReadOnly()) {
// file is readonly
error= PatchMessages.getString("PreviewPatchPage.FileIsReadOnly.error"); //$NON-NLS-1$
file= null;
} else {
diff.fMatches= true;
}
} else {
// file doesn't exist
error= PatchMessages.getString("PreviewPatchPage.FileDoesNotExist.error"); //$NON-NLS-1$
}
}
ArrayList failedHunks= new ArrayList();
fPatchWizard.getPatcher().apply(diff, file, create, failedHunks);
if (failedHunks.size() > 0)
diff.fRejected= fPatchWizard.getPatcher().getRejected(failedHunks);
int checkedSubs= 0; // counts checked hunk items
TreeItem[] hunkItems= item.getItems();
for (int h= 0; h < hunkItems.length; h++) {
Hunk hunk= (Hunk) hunkItems[h].getData();
boolean failed= failedHunks.contains(hunk);
String hunkError= null;
if (failed)
hunkError= PatchMessages.getString("PreviewPatchPage.NoMatch.error"); //$NON-NLS-1$
boolean check= !failed;
hunkItems[h].setChecked(check);
if (check)
checkedSubs++;
String hunkLabel= hunk.getDescription();
if (hunkError != null)
hunkLabel+= " " + hunkError; //$NON-NLS-1$
hunkItems[h].setText(hunkLabel);
hunkItems[h].setImage(getImage(hunk));
}
String label= diff.getDescription(strip);
if (error != null)
label+= " " + error; //$NON-NLS-1$
item.setText(label);
item.setImage(getImage(diff));
item.setChecked(checkedSubs > 0);
boolean gray= (checkedSubs > 0 && checkedSubs < hunkItems.length);
item.setGrayed(gray);
item.setExpanded(gray);
}
setPageComplete(updateModel());
}
/**
* Updates the gray state of the given diff and the checked state of its children.
*/
private void updateCheckedState(TreeItem diffItem) {
boolean checked= diffItem.getChecked();
// check whether we can enable all hunks
TreeItem[] hunks= diffItem.getItems();
int checkedCount= 0;
for (int i= 0; i < hunks.length; i++) {
Hunk hunk= (Hunk) hunks[i].getData();
if (checked) {
if (hunk.fMatches) {
hunks[i].setChecked(true);
checkedCount++;
}
} else {
hunks[i].setChecked(false);
}
}
diffItem.setGrayed(checkedCount > 0 && checkedCount < hunks.length);
diffItem.setChecked(checkedCount > 0);
setPageComplete(updateModel());
}
/**
* Updates the gray state of the given items parent.
*/
private void updateGrayedState(TreeItem hunk) {
TreeItem diff= hunk.getParentItem();
TreeItem[] hunks= diff.getItems();
int checked= 0;
for (int i= 0; i < hunks.length; i++)
if (hunks[i].getChecked())
checked++;
diff.setChecked(checked > 0);
diff.setGrayed(checked > 0 && checked < hunks.length);
setPageComplete(updateModel());
}
private void addSpacer(Composite parent) {
Label label= new Label(parent, SWT.NONE);
GridData gd= new GridData(GridData.FILL_HORIZONTAL);
gd.widthHint= 20;
label.setLayoutData(gd);
}
private int getStripPrefixSegments() {
int stripPrefixSegments= 0;
if (fStripPrefixSegments != null) {
String s= fStripPrefixSegments.getText();
try {
stripPrefixSegments= Integer.parseInt(s);
} catch(NumberFormatException ex) {
}
}
return stripPrefixSegments;
}
private int getFuzzFactor() {
int fuzzFactor= 0;
if (fFuzzField != null) {
String s= fFuzzField.getText();
try {
fuzzFactor= Integer.parseInt(s);
} catch(NumberFormatException ex) {
}
}
return fuzzFactor;
}
public boolean updateModel() {
boolean atLeastOneIsEnabled= false;
if (fTree != null && !fTree.isDisposed()) {
TreeItem [] diffItems= fTree.getItems();
for (int i= 0; i < diffItems.length; i++) {
TreeItem diffItem= diffItems[i];
Object data= diffItem.getData();
if (data instanceof Diff) {
Diff diff= (Diff) data;
boolean b= diffItem.getChecked();
diff.setEnabled(b);
if (b) {
TreeItem [] hunkItems= diffItem.getItems();
for (int j= 0; j < hunkItems.length; j++) {
TreeItem hunkItem= hunkItems[j];
data= hunkItem.getData();
if (data instanceof Hunk) {
Hunk hunk= (Hunk) data;
b= hunkItem.getChecked();
hunk.setEnabled(b);
if (b) {
atLeastOneIsEnabled= true;
}
}
}
}
}
}
}
return atLeastOneIsEnabled;
}
}