View | Details | Raw Unified | Return to bug 199846 | Differences between
and this patch

Collapse All | Expand All

(-)compare/org/eclipse/compare/internal/patch/FileDiffResult.java (-13 / +17 lines)
Lines 31-37 Link Here
31
	private List fBeforeLines, fAfterLines;
31
	private List fBeforeLines, fAfterLines;
32
	private final PatchConfiguration configuration;
32
	private final PatchConfiguration configuration;
33
	private String charset;
33
	private String charset;
34
	private int fuzz;
34
	private int fFuzz;
35
	
35
	
36
	public FileDiffResult(FileDiff diff, PatchConfiguration configuration) {
36
	public FileDiffResult(FileDiff diff, PatchConfiguration configuration) {
37
		super();
37
		super();
Lines 146-152 Link Here
146
		fBeforeLines = new ArrayList();
146
		fBeforeLines = new ArrayList();
147
		fBeforeLines.addAll(lines);
147
		fBeforeLines.addAll(lines);
148
		if (getConfiguration().getFuzz() == -1) {
148
		if (getConfiguration().getFuzz() == -1) {
149
			fuzz = calculateFuzz(fBeforeLines, monitor);
149
			fFuzz = calculateFuzz(fBeforeLines, monitor);
150
		}
150
		}
151
		int shift= 0;
151
		int shift= 0;
152
		Hunk[] hunks = fDiff.getHunks();
152
		Hunk[] hunks = fDiff.getHunks();
Lines 208-215 Link Here
208
	public int calculateFuzz(List lines, IProgressMonitor monitor) {
208
	public int calculateFuzz(List lines, IProgressMonitor monitor) {
209
		if (monitor == null)
209
		if (monitor == null)
210
			monitor = new NullProgressMonitor();
210
			monitor = new NullProgressMonitor();
211
		fBeforeLines = new ArrayList();
211
		fBeforeLines = new ArrayList(lines);
212
		fBeforeLines.addAll(lines);
213
		// TODO: What about deletions?
212
		// TODO: What about deletions?
214
		if (fDiff.getDiffType(getConfiguration().isReversed()) == Differencer.ADDITION) {
213
		if (fDiff.getDiffType(getConfiguration().isReversed()) == Differencer.ADDITION) {
215
			// Additions don't need to adjust the fuzz factor
214
			// Additions don't need to adjust the fuzz factor
Lines 217-223 Link Here
217
			return -1;
216
			return -1;
218
		}
217
		}
219
		int shift= 0;
218
		int shift= 0;
220
		int fuzz1 = 0;
219
		int highestFuzz = 0; // the maximum fuzz factor for all hunks
221
		String name = getTargetPath() != null ? getTargetPath().lastSegment() : ""; //$NON-NLS-1$
220
		String name = getTargetPath() != null ? getTargetPath().lastSegment() : ""; //$NON-NLS-1$
222
		Hunk[] hunks = fDiff.getHunks();
221
		Hunk[] hunks = fDiff.getHunks();
223
		for (int j = 0; j < hunks.length; j++) {
222
		for (int j = 0; j < hunks.length; j++) {
Lines 225-240 Link Here
225
			monitor.subTask(NLS.bind(PatchMessages.PreviewPatchPage_GuessFuzzProgress_format, new String[] {name, Integer.toString(j + 1)}));
224
			monitor.subTask(NLS.bind(PatchMessages.PreviewPatchPage_GuessFuzzProgress_format, new String[] {name, Integer.toString(j + 1)}));
226
			HunkResult result = getHunkResult(h);
225
			HunkResult result = getHunkResult(h);
227
			result.setShift(shift);
226
			result.setShift(shift);
228
			int f= result.calculateFuzz(lines, monitor);
227
			int fuzz = result.calculateFuzz(lines, monitor);
229
			if (f >= 0) {
228
			/*
230
				shift = result.getShift();
229
			 * TODO (tzarna): I believe this is not needed anymore as the
231
			}
230
			 * calculateFuzz does what it's supposed to, instead of calculate
232
			if (f>fuzz1)
231
			 * shifting
233
				fuzz1= f;
232
			 */
233
			// if (fuzz >= 0) {
234
			// shift = result.getShift();
235
			// }
236
			if (fuzz > highestFuzz)
237
				highestFuzz = fuzz;
234
			monitor.worked(1);
238
			monitor.worked(1);
235
		}
239
		}
236
		fAfterLines = lines;
240
		fAfterLines = lines;
237
		return fuzz1;
241
		return highestFuzz;
238
	}
242
	}
239
	
243
	
240
	public IPath getTargetPath() {
244
	public IPath getTargetPath() {
Lines 330-336 Link Here
330
	public int getFuzz() {
334
	public int getFuzz() {
331
		int cf = configuration.getFuzz();
335
		int cf = configuration.getFuzz();
332
		if (cf == -1) {
336
		if (cf == -1) {
333
			return fuzz;
337
			return fFuzz;
334
		}
338
		}
335
		return cf;
339
		return cf;
336
	}
340
	}
(-)compare/org/eclipse/compare/internal/patch/HunkResult.java (-39 / +59 lines)
Lines 41-76 Link Here
41
	/**
41
	/**
42
	 * Try to apply the specified hunk to the given lines.
42
	 * Try to apply the specified hunk to the given lines.
43
	 * If the hunk cannot be applied at the original position
43
	 * If the hunk cannot be applied at the original position
44
	 * the methods tries fuzz lines before and after.
44
	 * the method tries shift lines up and down.
45
	 * @param lines the lines to be patched
45
	 * @param lines the lines to be patched
46
	 * @return whether the hunk could be applied
46
	 * @return whether the hunk could be applied
47
	 */
47
	 */
48
	public boolean patch(List lines) {
48
	public boolean patch(List lines) {
49
		fMatches = false;
49
		fMatches = false;
50
		PatchConfiguration configuration = getConfiguration();
50
		PatchConfiguration configuration = getConfiguration();
51
		int fuzz = fDiffResult.getFuzz();// configuration.getFuzz();
51
		if (isEnabled(configuration)) {
52
		if (isEnabled(configuration)) {
52
			if (fHunk.tryPatch(configuration, lines, fShift)) {
53
			if (fHunk.tryPatch(configuration, lines, fShift, fuzz)) {
53
				fShift+= fHunk.doPatch(configuration, lines, fShift);
54
				// it's a perfect match, no adjustment is needed
55
				fShift += fHunk.doPatch(configuration, lines, fShift);
54
				fMatches = true;
56
				fMatches = true;
55
			} else {
57
			} else {
56
				boolean found= false;
58
				boolean found= false;
57
				int oldShift= fShift;
59
				int oldShift= fShift;
58
				
60
				
59
				for (int i= 1; i <= fDiffResult.getFuzz(); i++) {
61
				int start = fHunk.getStart(configuration.isReversed());
60
					if (fHunk.tryPatch(configuration, lines, fShift-i)) {
62
				for (int i = 1; i <= start; i++) {
63
					if (fHunk.tryPatch(configuration, lines, fShift - i, fuzz)) {
61
						if (isAdjustShift())
64
						if (isAdjustShift())
62
							fShift-= i;
65
							fShift -= i;
63
						found= true;
66
						found = true;
64
						break;
67
						break;
65
					}
68
					}
66
				}
69
				}
67
				
70
				
68
				if (! found) {
71
				if (!found) {
69
					for (int i= 1; i <= fDiffResult.getFuzz(); i++) {
72
					for (int i = 1; i <= lines.size()-start/* fDiffResult.getFuzz()*/; i++) {
70
						if (fHunk.tryPatch(configuration, lines, fShift+i)) {
73
						if (fHunk.tryPatch(configuration, lines, fShift + i, fuzz)) {
71
							if (isAdjustShift())
74
							if (isAdjustShift())
72
								fShift+= i;
75
								fShift += i;
73
							found= true;
76
							found = true;
74
							break;
77
							break;
75
						}
78
						}
76
					}
79
					}
Lines 95-148 Link Here
95
	}
98
	}
96
99
97
	/**
100
	/**
98
	 * Calculate the fuzz factor that will allow the most hunks to be matched.
101
	 * Calculate the fuzz that will allow the most hunks to be matched. Even
99
	 * @param lines the lines of the target file
102
	 * though we're interested only in the value of the fuzz, the shifting is
100
	 * @param monitor a progress monitor
103
	 * done anyway.
104
	 * 
105
	 * @param lines
106
	 *            the lines of the target file
107
	 * @param monitor
108
	 *            a progress monitor
101
	 * @return the fuzz factor or -1 if the hunk could not be matched
109
	 * @return the fuzz factor or -1 if the hunk could not be matched
102
	 */
110
	 */
103
	public int calculateFuzz(List lines, IProgressMonitor monitor) {
111
	public int calculateFuzz(List lines, IProgressMonitor monitor) {
104
		
112
		fMatches = false;
105
		fMatches= false;
113
		// int shift = 0; // we're not interested in the shift value right now
106
		int fuzz = 0;
107
		PatchConfiguration configuration = getConfiguration();
114
		PatchConfiguration configuration = getConfiguration();
108
		if (fHunk.tryPatch(configuration, lines, fShift)) {
115
		int fuzz = 0;
109
			fShift+= fHunk.doPatch(configuration, lines, fShift);
116
		// TODO (tzarna): the default maximum fuzz factor is 2
110
			fMatches = true;
117
		for (; fuzz <= 2; fuzz++) {
111
		} else {
118
			// try to apply using lines coordinates from the patch
112
			int hugeFuzz= lines.size();	// the maximum we need for this file
119
			if (fHunk.tryPatch(configuration, lines, fShift, fuzz)) {
113
			fuzz= -1;	// not found
120
				// it's a perfect match, no adjustment is needed
121
				fShift += fHunk.doPatch(configuration, lines, fShift);
122
				fMatches = true;
123
				break;
124
			}
125
			//TODO (tzarna): hugeShift it to much, lines to the beg/end of a 
126
			// file would be enough the maximum shift we can use for this file
127
			int hugeShift = lines.size(); 
128
			// shift = -1; // not found
114
			
129
			
115
			for (int i= 1; i <= hugeFuzz; i++) {
130
			// shift up 
131
			for (int i = 1; i <= hugeShift; i++) {
116
				if (monitor.isCanceled()) {
132
				if (monitor.isCanceled()) {
117
					throw new OperationCanceledException();
133
					throw new OperationCanceledException();
118
				}
134
				}
119
				if (fHunk.tryPatch(configuration, lines, fShift-i)) {
135
				if (fHunk.tryPatch(configuration, lines, fShift - i, fuzz)) {
120
					fuzz= i;
136
					// shift = i;
121
					if (isAdjustShift())
137
					if (isAdjustShift())
122
						fShift-= i;
138
						fShift -= i;
123
					fMatches= true;
139
					fMatches = true;
124
					break;
140
					break;
125
				}
141
				}
126
			}
142
			}
127
			
143
128
			if (! fMatches) {
144
			// shift down
129
				for (int i= 1; i <= hugeFuzz; i++) {
145
			if (!fMatches) {
146
				for (int i = 1; i <= hugeShift; i++) {
130
					if (monitor.isCanceled()) {
147
					if (monitor.isCanceled()) {
131
						throw new OperationCanceledException();
148
						throw new OperationCanceledException();
132
					}
149
					}
133
					if (fHunk.tryPatch(configuration, lines, fShift+i)) {
150
					if (fHunk.tryPatch(configuration, lines, fShift + i, fuzz)) {
134
						fuzz= i;
151
						// shift = i;
135
						if (isAdjustShift())
152
						if (isAdjustShift())
136
							fShift+= i;
153
							fShift += i;
137
						fMatches= true;
154
						fMatches = true;
138
						break;
155
						break;
139
					}
156
					}
140
				}
157
				}
141
			}
158
			}
142
			
159
143
			if (fMatches)
160
			if (fMatches) {
144
				fShift+= fHunk.doPatch(configuration, lines, fShift);
161
				fShift += fHunk.doPatch(configuration, lines, fShift);
162
				break;
163
			}
145
		}
164
		}
165
		// TODO (tzarna): return -1 if the hunk could not be matched?
146
		return fuzz;
166
		return fuzz;
147
	}
167
	}
148
168
(-)compare/org/eclipse/compare/internal/patch/Hunk.java (-35 / +129 lines)
Lines 10-15 Link Here
10
 *******************************************************************************/
10
 *******************************************************************************/
11
package org.eclipse.compare.internal.patch;
11
package org.eclipse.compare.internal.patch;
12
12
13
import java.util.ArrayList;
13
import java.util.List;
14
import java.util.List;
14
15
15
import org.eclipse.compare.patch.PatchConfiguration;
16
import org.eclipse.compare.patch.PatchConfiguration;
Lines 167-216 Link Here
167
	 * The parameter shift is added to the line numbers given
168
	 * The parameter shift is added to the line numbers given
168
	 * in the hunk.
169
	 * in the hunk.
169
	 */
170
	 */
170
	public boolean tryPatch(PatchConfiguration configuration, List lines, int shift) {
171
	public boolean tryPatch(PatchConfiguration configuration, List lines, int shift, int fuzz) {
171
		boolean reverse = configuration.isReversed();
172
		boolean reverse = configuration.isReversed();
172
		int pos= getStart(reverse) + shift;
173
		int pos = getStart(reverse) + shift;
173
		int deleteMatches= 0;
174
		int deleteMatches = 0;
175
		List contextLines = new ArrayList();
176
		boolean contextLinesMatched = true;
174
		for (int i= 0; i < fLines.length; i++) {
177
		for (int i= 0; i < fLines.length; i++) {
175
			String s= fLines[i];
178
			String s = fLines[i];
176
			Assert.isTrue(s.length() > 0);
179
			Assert.isTrue(s.length() > 0);
177
			String line= s.substring(1);
180
			String line = s.substring(1);
178
			char controlChar= s.charAt(0);
181
			char controlChar = s.charAt(0);
182
			
179
			if (controlChar == ' ') {	// context lines
183
			if (controlChar == ' ') {	// context lines
180
				while (true) {
184
				
181
					if (pos < 0 || pos >= lines.size())
185
				if (pos < 0 || pos >= lines.size()) // out of the file
182
						return false;
183
					if (linesMatch(configuration, line, (String) lines.get(pos))) {
184
						pos++;
185
						break;
186
					}
187
					return false;
186
					return false;
188
				}
187
				contextLines.add(line);
188
				if (linesMatch(configuration, line, (String) lines.get(pos))) {
189
					pos++;
190
					continue;
191
				} else if (fuzz > 0) {
192
					// doesn't match, use the fuzz factor
193
					contextLinesMatched = false;
194
					pos++;
195
					continue;
196
				} 
197
				return false;
189
			} else if (isDeletedDelimeter(controlChar, reverse)) {
198
			} else if (isDeletedDelimeter(controlChar, reverse)) {
190
				// deleted lines
199
				// deleted lines
191
				while (true) {
200
				
192
					if (pos < 0 || pos >= lines.size())
201
				// use the fuzz factor if needed
193
						return false;
202
				// TODO (tzarna): extract method
194
					if (linesMatch(configuration, line, (String) lines.get(pos))) {
203
				if (!contextLinesMatched && fuzz > 0 && contextLines.size() > fuzz) {
195
						deleteMatches++;
204
					// three lines of context is typically the default
196
						pos++;
205
					if (contextLines.size() > 3) {
197
						break;
206
						// two "groups" of context lines -- both preceding and following
207
						if (contextLines.size() % 2 == 0) {
208
							// if the number of context lines is even treat the
209
							// first half part as following and the second one
210
							// as preceding...
211
							List followingContextLines = new ArrayList(contextLines.subList(0, contextLines.size() / 2 - 1));
212
							if (!checkFollowingContextLines(configuration, lines, fuzz, pos, followingContextLines))
213
								return false;
214
							List precedingContextLines = new ArrayList(contextLines.subList(contextLines.size() / 2 - 1, contextLines.size()));
215
							if (!checkPrecedingContextLines(configuration, lines, fuzz, pos, precedingContextLines))
216
								return false;
217
						} else {
218
							// ... otherwise, if the number is odd give them two
219
							// tries (e.g. for 5 context lines try both 2+3 and
220
							// 3+2)
221
							List followingContextLines = new ArrayList(contextLines.subList(0, contextLines.size() / 2));
222
							List precedingContextLines = new ArrayList(contextLines.subList(contextLines.size() / 2, contextLines.size()));
223
							if (checkPrecedingContextLines(configuration, lines, fuzz, pos, precedingContextLines) 
224
									&& checkFollowingContextLines(configuration, lines, fuzz, pos - contextLines.size() / 2 - 1, followingContextLines)) {
225
								// to omit the check of following of the context lines at the end of this method
226
								contextLinesMatched = true; 
227
								break;
228
							}
229
							// else give it another try
230
							precedingContextLines = new ArrayList(contextLines.subList(0, contextLines.size() / 2 + 1));
231
							if (!checkPrecedingContextLines(configuration, lines, fuzz, pos, precedingContextLines))
232
								return false;
233
							followingContextLines = new ArrayList(contextLines.subList(contextLines.size() / 2 + 1, contextLines.size()));
234
							if (!checkFollowingContextLines(configuration, lines, fuzz, pos, followingContextLines))
235
								return false;
236
						}
237
					} else {
238
							//ignore from the beginning
239
							if (!checkPrecedingContextLines(configuration, lines,
240
									fuzz, pos, contextLines)) return false;
198
					}
241
					}
199
					
242
				} 
200
					// We must remove all lines at once, return false if this
243
				// else if there is less or equal context line to the fuzz
201
					// fails. In other words, all lines considered for deletion
244
				// factor we ignore them all and treat as matching
202
					// must be found one by one.
245
				
203
246
				contextLines.clear();
204
					// if (deleteMatches <= 0)
247
				
205
						return false;
248
				if (pos < 0 || pos >= lines.size()) // out of the file
206
					// pos++;
249
					return false;
250
				if (linesMatch(configuration, line, (String) lines.get(pos))) {
251
					deleteMatches++;
252
					pos++;
253
					continue; // line matched, continue with the next one
207
				}
254
				}
255
256
				// We must remove all lines at once, return false if this
257
				// fails. In other words, all lines considered for deletion
258
				// must be found one by one.
259
260
				// if (deleteMatches <= 0)
261
				return false;
262
				// pos++;
208
			} else if (isAddedDelimeter(controlChar, reverse)) {
263
			} else if (isAddedDelimeter(controlChar, reverse)) {
209
				// added lines
264
				// added lines
210
				// we don't have to do anything for a 'try'
265
				// we don't have to do anything for a 'try'
266
				contextLines.clear();
211
			} else
267
			} else
212
				Assert.isTrue(false, "tryPatch: unknown control character: " + controlChar); //$NON-NLS-1$
268
				Assert.isTrue(false, "tryPatch: unknown control character: " + controlChar); //$NON-NLS-1$
213
		}
269
		}
270
		if (!contextLinesMatched && fuzz > 0 && contextLines.size() > fuzz)
271
			if (!checkFollowingContextLines(configuration, lines, fuzz, pos,
272
					contextLines))
273
				return false;
274
		return true;
275
	}
276
277
	private boolean checkPrecedingContextLines(
278
			PatchConfiguration configuration, List lines, int fuzz, int pos,
279
			List contextLines) {
280
		// ignore from the beginning
281
		for (int j = fuzz; j < contextLines.size(); j++) {
282
			if (!linesMatch(configuration, (String) contextLines.get(j),
283
					(String) lines.get(pos - contextLines.size() + j)))
284
				return false;
285
		}
286
		return true;
287
	}
288
	
289
	private boolean checkFollowingContextLines(
290
			PatchConfiguration configuration, List lines, int fuzz, int pos,
291
			List contextLines) {
292
		if (!contextLines.isEmpty()) {
293
			// ignore from the end
294
			for (int j = 0; j < contextLines.size() - fuzz; j++) {
295
				if (!linesMatch(configuration, (String) contextLines.get(j),
296
						(String) lines.get(pos - contextLines.size() + j)))
297
					return false;
298
			}
299
		}
214
		return true;
300
		return true;
215
	}
301
	}
216
	
302
	
Lines 243-257 Link Here
243
			Assert.isTrue(s.length() > 0);
329
			Assert.isTrue(s.length() > 0);
244
			String line= s.substring(1);
330
			String line= s.substring(1);
245
			char controlChar= s.charAt(0);
331
			char controlChar= s.charAt(0);
332
			// TODO (tzarna): don't process context lines as they are already
333
			// checked by the tryPatch method (with fuzz factor) or add the fuzz
334
			// factor processing here too
246
			if (controlChar == ' ') {	// context lines
335
			if (controlChar == ' ') {	// context lines
247
				while (true) {
248
					Assert.isTrue(pos < lines.size(), "doPatch: inconsistency in context"); //$NON-NLS-1$
336
					Assert.isTrue(pos < lines.size(), "doPatch: inconsistency in context"); //$NON-NLS-1$
249
					if (linesMatch(configuration, line, (String) lines.get(pos))) {
337
//					if (linesMatch(configuration, line, (String) lines.get(pos))) {
250
						pos++;
338
//						pos++;
251
						break;
339
//						break;
252
					}
340
//					}else if (fuzz > 0) {
341
//						// doesn't match, use the fuzz factor
342
//						fuzz--;
343
//						pos++;
344
//						break;
345
//					}
253
					pos++;
346
					pos++;
254
				}
347
//				}
255
			} else if (isDeletedDelimeter(controlChar, reverse)) {
348
			} else if (isDeletedDelimeter(controlChar, reverse)) {
256
				// deleted lines				
349
				// deleted lines				
257
				while (true) {
350
				while (true) {
Lines 259-264 Link Here
259
					if (linesMatch(configuration, line, (String) lines.get(pos))) {
352
					if (linesMatch(configuration, line, (String) lines.get(pos))) {
260
						break;
353
						break;
261
					}
354
					}
355
					// TODO (tzarna): does it ever happen?
262
					pos++;
356
					pos++;
263
				}
357
				}
264
				lines.remove(pos);
358
				lines.remove(pos);

Return to bug 199846