Bug 109481 - [find/replace] replace doesn't work when using a regex with
a lookahead or boundary matchers

Fixes the bug for the File-Search case (comment 6).

Change-Id: Ia9f22deae6922cf1e5d76a8ec74a280889934b8f
Signed-off-by: fingerl <imelflorianingerl@gmail.com>
diff --git a/org.eclipse.search.tests/META-INF/MANIFEST.MF b/org.eclipse.search.tests/META-INF/MANIFEST.MF
index cd25df3..0a2795f 100644
--- a/org.eclipse.search.tests/META-INF/MANIFEST.MF
+++ b/org.eclipse.search.tests/META-INF/MANIFEST.MF
@@ -18,7 +18,8 @@
  org.junit;bundle-version="4.12.0",
  org.eclipse.ui.workbench.texteditor;bundle-version="[3.5.0,4.0.0)",
  org.eclipse.jface.text;bundle-version="[3.5.0,4.0.0)",
- org.eclipse.ui.editors;bundle-version="[3.5.0,4.0.0)"
+ org.eclipse.ui.editors;bundle-version="[3.5.0,4.0.0)",
+ org.eclipse.ltk.core.refactoring;bundle-version="[3.5.0,4.0.0)"
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Eclipse-BundleShape: dir
diff --git a/org.eclipse.search.tests/src/org/eclipse/search/tests/filesearch/FileSearchTests.java b/org.eclipse.search.tests/src/org/eclipse/search/tests/filesearch/FileSearchTests.java
index 4496d61..675013f 100644
--- a/org.eclipse.search.tests/src/org/eclipse/search/tests/filesearch/FileSearchTests.java
+++ b/org.eclipse.search.tests/src/org/eclipse/search/tests/filesearch/FileSearchTests.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2016 IBM Corporation and others.
+ * Copyright (c) 2000, 2017 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
@@ -9,13 +9,16 @@
  *     IBM Corporation - initial API and implementation
  *     Christian Walther (Indel AG) - Bug 399094, 402009: Add whole word option to file search
  *     Terry Parker <tparker@google.com> (Google Inc.) - Bug 441016 - Speed up text search by parallelizing it using JobGroups
+ *     Florian Ingerl <imelflorianingerl@gmail.com> - Bug 109481 - [find/replace] replace doesn't work when using a regex with a lookahead or boundary matchers
  *******************************************************************************/
 package org.eclipse.search.tests.filesearch;
 
 import static org.junit.Assert.assertEquals;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Scanner;
 import java.util.regex.Pattern;
 
 import org.junit.After;
@@ -24,6 +27,8 @@
 import org.junit.Test;
 
 import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
 
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IFolder;
@@ -38,10 +43,15 @@
 import org.eclipse.search.core.text.TextSearchScope;
 import org.eclipse.search.internal.core.text.PatternConstructor;
 import org.eclipse.search.internal.ui.SearchPlugin;
+import org.eclipse.search.internal.ui.text.FileSearchQuery;
+import org.eclipse.search.internal.ui.text.FileSearchResult;
+import org.eclipse.search.internal.ui.text.ReplaceRefactoring;
 import org.eclipse.search.tests.ResourceHelper;
 import org.eclipse.search.tests.SearchTestPlugin;
 import org.eclipse.search.ui.text.FileTextSearchScope;
 
+import org.eclipse.ltk.core.refactoring.Change;
+
 public class FileSearchTests {
 
 	private static class TestResult {
@@ -523,5 +533,50 @@
 		assertEquals("Number of results in file", expectedCount, k);
 	}
 
+	@Test
+	public void testReplaceWithLookarounds() throws CoreException, IOException {
+		IFolder folder= ResourceHelper.createFolder(fProject.getFolder("folder1"));
+		IFile file1= ResourceHelper.createFile(folder, "file1.txt", "1<2<3<4");
+		IFile file2= ResourceHelper.createFile(folder, "file2.txt", "4<5<6<7");
 
+		FileSearchResult searchResult= performSearch(new String[] { "*.txt" }, "(?<=(\\d)\\<)\\d(?=\\<(\\d))");
+		performReplace(searchResult, "$0=($1+$2)/2");
+
+		assertFileContent(file1, "1<2=(1+3)/2<3=(2+4)/2<4");
+		assertFileContent(file2, "4<5=(4+6)/2<6=(5+7)/2<7");
+	}
+
+	@Test
+	public void testReplaceRetainCase() throws CoreException, IOException {
+		IFolder folder= ResourceHelper.createFolder(fProject.getFolder("folder1"));
+		IFile file1= ResourceHelper.createFile(folder, "file1.txt", "FOO");
+
+		FileSearchResult searchResult= performSearch(new String[] { "*.txt" }, "FOO");
+		performReplace(searchResult, "xyz\\Cbar\\Cfar");
+
+		assertFileContent(file1, "xyzBARFAR");
+	}
+
+	private FileSearchResult performSearch(String[] fileNamePatterns, String pattern) {
+		FileTextSearchScope scope= FileTextSearchScope.newSearchScope(new IResource[] { fProject }, fileNamePatterns, false);
+		FileSearchQuery query= new FileSearchQuery(pattern, true, true, scope);
+		query.run(null);
+		return (FileSearchResult) query.getSearchResult();
+	}
+
+	private void performReplace(FileSearchResult searchResult, String replacementText) throws OperationCanceledException, CoreException {
+		ReplaceRefactoring refactoring= new ReplaceRefactoring(searchResult, null);
+		refactoring.setReplaceString(replacementText);
+		refactoring.checkInitialConditions(null);
+		refactoring.checkFinalConditions(null);
+		Change change= refactoring.createChange(null);
+		change.perform(new NullProgressMonitor());
+	}
+
+	private void assertFileContent(IFile file, String expected) throws CoreException {
+		try (Scanner scanner= new Scanner(file.getContents())) {
+			scanner.useDelimiter("\\A");
+			assertEquals(expected, scanner.next());
+		}
+	}
 }
diff --git a/org.eclipse.search/search/org/eclipse/search/internal/ui/text/ReplaceRefactoring.java b/org.eclipse.search/search/org/eclipse/search/internal/ui/text/ReplaceRefactoring.java
index 6ae6d9d..496fb8a 100644
--- a/org.eclipse.search/search/org/eclipse/search/internal/ui/text/ReplaceRefactoring.java
+++ b/org.eclipse.search/search/org/eclipse/search/internal/ui/text/ReplaceRefactoring.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007, 2015 IBM Corporation and others.
+ * Copyright (c) 2007, 2017 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
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Florian Ingerl <imelflorianingerl@gmail.com> - Bug 109481 - [find/replace] replace doesn't work when using a regex with a lookahead or boundary matchers
  *******************************************************************************/
 package org.eclipse.search.internal.ui.text;
 
@@ -437,7 +438,9 @@
 					continue;
 				}
 
-				String replacementString= computeReplacementString(pattern, originalText, fReplaceString, lineDelimiter);
+				String replacementString= PatternConstructor.interpretReplaceEscapes(fReplaceString, originalText,
+						lineDelimiter);
+				replacementString= computeReplacementString(pattern, document, offset, replacementString);
 				if (replacementString == null) {
 					resultingStatus.addError(Messages.format(SearchMessages.ReplaceRefactoring_error_match_content_changed, file.getName()));
 					continue;
@@ -467,21 +470,18 @@
 		return PatternConstructor.createPattern(query.getSearchString(), true, true, query.isCaseSensitive(), false);
 	}
 
-	private String computeReplacementString(Pattern pattern, String originalText, String replacementText, String lineDelimiter) throws PatternSyntaxException {
+	private String computeReplacementString(Pattern pattern, IDocument document, int offset, String replacementText)
+			throws PatternSyntaxException {
 		if (pattern != null) {
 			try {
-				replacementText= PatternConstructor.interpretReplaceEscapes(replacementText, originalText, lineDelimiter);
-
-				Matcher matcher= pattern.matcher(originalText);
-		        StringBuffer sb = new StringBuffer();
-		        matcher.reset();
-		        if (matcher.find()) {
+				Matcher matcher= pattern.matcher(document.get());
+				if (matcher.find(offset)) {
+					StringBuffer sb= new StringBuffer();
 		        	matcher.appendReplacement(sb, replacementText);
+					return sb.substring(offset);
 		        } else {
 		        	return null;
 		        }
-		        matcher.appendTail(sb);
-		        return sb.toString();
 			} catch (IndexOutOfBoundsException ex) {
 				throw new PatternSyntaxException(ex.getLocalizedMessage(), replacementText, -1);
 			}