/*******************************************************************************
 * Copyright (c) 2000, 2008 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jdt.core.tests.model;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.*;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.tests.util.Util;
import org.eclipse.jdt.internal.core.JavaElement;

import junit.framework.Test;

public class RootManipulationsTests extends ModifyingResourceTests {
public RootManipulationsTests(String name) {
	super(name);
}
protected void assertJavaProject(String expected, IJavaProject project) throws CoreException {
	StringBuffer buffer = new StringBuffer();
	populate(buffer, project, 0);

	String actual = buffer.toString();
	if (!expected.equals(actual)) {
	 	System.out.println(Util.displayString(actual, 3));
	}
	assertEquals(expected, actual);
}
protected void copy(IPackageFragmentRoot root, IPath destination) throws JavaModelException {
	copy(root, destination, null);
}
protected void copy(IPackageFragmentRoot root, IPath destination, IClasspathEntry sibling) throws JavaModelException {
	root.copy(
		destination,
		IResource.NONE,
		IPackageFragmentRoot.DESTINATION_PROJECT_CLASSPATH,
		sibling,
		null);
}
protected void delete(IPackageFragmentRoot root) throws JavaModelException {
	// ensure indexing is not interferring with the deletion
	waitUntilIndexesReady();

	root.delete(
		IResource.NONE,
		IPackageFragmentRoot.ORIGINATING_PROJECT_CLASSPATH
			| IPackageFragmentRoot.OTHER_REFERRING_PROJECTS_CLASSPATH,
		null);
}
protected void move(IPackageFragmentRoot root, IPath destination) throws JavaModelException {
	move(root, destination, null);
}
protected void move(IPackageFragmentRoot root, IPath destination, IClasspathEntry sibling) throws JavaModelException {
	// ensure indexing is not interferring with the move
	waitUntilIndexesReady();

	root.move(
		destination,
		IResource.NONE,
		IPackageFragmentRoot.DESTINATION_PROJECT_CLASSPATH
			| IPackageFragmentRoot.ORIGINATING_PROJECT_CLASSPATH
			| IPackageFragmentRoot.OTHER_REFERRING_PROJECTS_CLASSPATH,
		sibling,
		null);
}
protected void populate(StringBuffer buffer, IJavaElement element, int indent) throws CoreException {
	if (!(element instanceof IParent) || !(element instanceof IOpenable)) return;

	if (buffer.length() != 0) {
		buffer.append("\n");
	}
	for (int i = 0; i < indent; i++) buffer.append("\t");
	buffer.append(((JavaElement)element).toDebugString());

	IParent parent = (IParent)element;
	IJavaElement[] children = null;
	try {
		children = parent.getChildren();
	} catch (JavaModelException e) {
	}
	if (children != null) {
		for (int i = 0, length = children.length; i < length; i++) {
			populate(buffer, children[i], indent+1);
		}
	}

	Object[] nonJavaResources = null;
	try {
		if (element instanceof IJavaProject) {
			nonJavaResources = ((IJavaProject)element).getNonJavaResources();
		} else if (element instanceof IPackageFragmentRoot) {
			nonJavaResources = ((IPackageFragmentRoot)element).getNonJavaResources();
		} else if (element instanceof IPackageFragment) {
			nonJavaResources = ((IPackageFragment)element).getNonJavaResources();
		}
	} catch (JavaModelException e) {
	}
	if (nonJavaResources != null) {
		for (int i = 0, length = nonJavaResources.length; i < length; i++) {
			populate(buffer, nonJavaResources[i], indent+1);
		}
	}
}
protected void populate(StringBuffer buffer, Object nonJavaResource, int indent) {
	if (buffer.length() != 0) {
		buffer.append("\n");
	}
	for (int i = 0; i < indent; i++) buffer.append("\t");
	buffer.append(nonJavaResource);
	/*
	if (nonJavaResource instanceof IContainer) {
		IResource[] members = ((IContainer)nonJavaResource).members();
		for (int i = 0, length = members.length; i < length; i++) {
			populate(buffer, members[i], indent+1);
		}
	}*/
}
public static Test suite() {
	return buildModelTestSuite(RootManipulationsTests.class);
}
/*
 * Ensure that a simple copy of a source root to another project triggers the right delta
 * and that the model is up-to-date.
 */
public void testCopySourceFolder1() throws CoreException {
	try {
		this.createJavaProject("P1", new String[] {"src"}, "bin");
		this.createJavaProject("P2", new String[] {}, "bin");
		this.createFolder("/P1/src/p");
		this.createFile(
			"/P1/src/p/X.java",
			"package p;\n" +
			"public class X {\n" +
			"}"
		);
		IPackageFragmentRoot root = this.getPackageFragmentRoot("/P1/src");
		startDeltas();
		this.copy(root, new Path("/P2/src"));
		assertDeltas(
			"Unexpected delta",
			"P2[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	src[+]: {}\n" +
			"	ResourceDelta(/P2/.classpath)[*]"
		);
		ICompilationUnit cu = this.getCompilationUnit("/P2/src/p/X.java");
		assertTrue("Destination cu should exist", cu.exists());
	} finally {
		stopDeltas();
		this.deleteProject("P1");
		this.deleteProject("P2");
	}
}
/*
 * Ensure that coping and renaming a source root to another project triggers the right delta
 * and that the model is up-to-date.
 */
public void testCopySourceFolder2() throws CoreException {
	try {
		this.createJavaProject("P1", new String[] {"src"}, "bin");
		this.createJavaProject("P2", new String[] {}, "bin");
		this.createFolder("/P1/src/p");
		this.createFile(
			"/P1/src/p/X.java",
			"package p;\n" +
			"public class X {\n" +
			"}"
		);
		IPackageFragmentRoot root = this.getPackageFragmentRoot("/P1/src");
		startDeltas();
		this.copy(root, new Path("/P2/src2"));
		assertDeltas(
			"Unexpected delta",
			"P2[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	src2[+]: {}\n" +
			"	ResourceDelta(/P2/.classpath)[*]"
		);
		ICompilationUnit cu = this.getCompilationUnit("/P2/src2/p/X.java");
		assertTrue("Destination cu should exist", cu.exists());
	} finally {
		stopDeltas();
		this.deleteProject("P1");
		this.deleteProject("P2");
	}
}
/*
 * Ensure that coping a source root to another project triggers the right delta
 * and doesn't copy a nested source folder.
 */
public void testCopySourceFolder3() throws CoreException {
	try {
		IJavaProject p1 = this.createJavaProject("P1", new String[] {}, "bin");
		p1.setRawClasspath(createClasspath(new String[] {"/P1/src1", "src2/**", "/P1/src1/src2", ""}, false/*no inclusion*/, true/*exclusion*/), null);
		this.createJavaProject("P2", new String[] {}, "bin");
		this.createFolder("/P1/src1/p");
		this.createFile(
			"/P1/src1/p/X.java",
			"package p;\n" +
			"public class X {\n" +
			"}"
		);
		this.createFolder("/P1/src1/src2/q");
		this.createFile(
			"/P1/src1/src2/q/Y.java",
			"package q;\n" +
			"public class Y {\n" +
			"}"
		);
		IPackageFragmentRoot root = this.getPackageFragmentRoot("/P1/src1");
		startDeltas();
		this.copy(root, new Path("/P2/src1"));
		assertDeltas(
			"Unexpected delta",
			"P2[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	src1[+]: {}\n" +
			"	ResourceDelta(/P2/.classpath)[*]"
		);
		ICompilationUnit cu = this.getCompilationUnit("/P2/src1/p/X.java");
		assertTrue("Destination cu should exist", cu.exists());
		cu = this.getCompilationUnit("/P2/src1/src2/q/Y.java");
		assertTrue("Nested cu should not exist", !cu.exists());
	} finally {
		stopDeltas();
		this.deleteProject("P1");
		this.deleteProject("P2");
	}
}
/*
 * Ensure that copying a source root to another project using a sibling classpath entry triggers the right delta
 * and that the model is up-to-date.
 */
public void testCopySourceFolder4() throws CoreException {
	try {
		this.createJavaProject("P1", new String[] {"src"}, "bin");
		IJavaProject p2 = this.createJavaProject("P2", new String[] {"src1", "src2"}, "bin");
		IPackageFragmentRoot root = this.getPackageFragmentRoot("/P1/src");

		// insert first
		IClasspathEntry sibling = JavaCore.newSourceEntry(new Path("/P2/src1"));
		startDeltas();
		this.copy(root, new Path("/P2/src"), sibling);
		assertDeltas(
			"Unexpected delta (1)",
			"P2[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	src[+]: {}\n" +
			"	src1[*]: {REORDERED}\n" +
			"	src2[*]: {REORDERED}\n" +
			"	ResourceDelta(/P2/.classpath)[*]"
		);
		assertElementsEqual(
			"Unexpected roots of P2 after insertion first",
			"src [in P2]\n" +
			"src1 [in P2]\n" +
			"src2 [in P2]",
			p2.getPackageFragmentRoots());

		// insert in the middle
		sibling = JavaCore.newSourceEntry(new Path("/P2/src2"));
		startDeltas();
		this.copy(root, new Path("/P2/src3"), sibling);
		assertDeltas(
			"Unexpected delta (2)",
			"P2[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	src2[*]: {REORDERED}\n" +
			"	src3[+]: {}\n" +
			"	ResourceDelta(/P2/.classpath)[*]"
		);
		assertElementsEqual(
			"Unexpected roots of P2 after insertion in the middle",
			"src [in P2]\n" +
			"src1 [in P2]\n" +
			"src3 [in P2]\n" +
			"src2 [in P2]",
			p2.getPackageFragmentRoots());

		// insert last
		startDeltas();
		this.copy(root, new Path("/P2/src4"), null);
		assertDeltas(
			"Unexpected delta (3)",
			"P2[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	src4[+]: {}\n" +
			"	ResourceDelta(/P2/.classpath)[*]"
		);
		assertElementsEqual(
			"Unexpected roots of P2 after insertion last",
			"src [in P2]\n" +
			"src1 [in P2]\n" +
			"src3 [in P2]\n" +
			"src2 [in P2]\n" +
			"src4 [in P2]",
			p2.getPackageFragmentRoots());
	} finally {
		stopDeltas();
		this.deleteProject("P1");
		this.deleteProject("P2");
	}
}
/*
 * Ensure that coping a source root to another project with an existing source root in
 * REPLACE mode triggers the right delta and that the model is up-to-date.
 * (regression test bug 30511 IPackageFragmentRoot:move ignores FORCE flag)
 */
public void testCopySourceFolder5() throws CoreException {
	try {
		this.createJavaProject("P1", new String[] {"src"}, "bin");
		IJavaProject p2 = this.createJavaProject("P2", new String[] {"src"}, "bin");
		this.createFolder("/P1/src/p");
		this.createFile(
			"/P1/src/p/X.java",
			"package p;\n" +
			"public class X {\n" +
			"}"
		);

		IPackageFragmentRoot root = this.getPackageFragmentRoot("/P1/src");
		startDeltas();
		root.copy(new Path("/P2/src"), IResource.NONE, IPackageFragmentRoot.REPLACE, null, null);
		assertDeltas(
			"Unexpected delta",
			"P2[*]: {CHILDREN}\n" +
			"	src[*]: {CHILDREN}\n" +
			"		p[+]: {}"
		);
		assertJavaProject(
			"P2\n" +
			"	src\n" +
			"		<default>\n" +
			"		p\n" +
			"			X.java\n" +
			"	L/P2/.classpath\n" +
			"	L/P2/.project\n" +
			"	F/P2/.settings",
			p2);
	} finally {
		stopDeltas();
		this.deleteProject("P1");
		this.deleteProject("P2");
	}
}
/*
 * Ensure that coping and renaming a source root to same project in
 * REPLACE mode triggers the right delta and that the model is up-to-date.
 * (regression test bug 30857 IPackageFragmentRoot: copy removes source folders from classpath)
 */
public void testCopySourceFolder6() throws CoreException {
	try {
		IJavaProject project = this.createJavaProject("P", new String[] {"src"}, "bin");
		this.createFolder("/P/src/p");
		this.createFile(
			"/P/src/p/X.java",
			"package p;\n" +
			"public class X {\n" +
			"}"
		);

		IPackageFragmentRoot root = this.getPackageFragmentRoot("/P/src");
		startDeltas();
		root.copy(
			new Path("/P/src1"),
			IResource.KEEP_HISTORY,
			IPackageFragmentRoot.REPLACE | IPackageFragmentRoot.DESTINATION_PROJECT_CLASSPATH,
			null,
			null);
		assertDeltas(
			"Unexpected delta",
			"P[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	src1[+]: {}\n" +
			"	ResourceDelta(/P/.classpath)[*]"
		);
		assertJavaProject(
			"P\n" +
			"	src\n" +
			"		<default>\n" +
			"		p\n" +
			"			X.java\n" +
			"	src1\n" +
			"		<default>\n" +
			"		p\n" +
			"			X.java\n" +
			"	L/P/.classpath\n" +
			"	L/P/.project\n" +
			"	F/P/.settings",
			project);
	} finally {
		stopDeltas();
		this.deleteProject("P");
	}
}
/*
 * Ensure that coping a source root to another project with an existing source root in
 * non REPLACE mode throws the right JavaModelException.
 * (regression test bug 30511 IPackageFragmentRoot:move ignores FORCE flag)
 */
public void testFailCopySourceFolder1() throws CoreException {
	try {
		this.createJavaProject("P1", new String[] {"src"}, "bin");
		this.createJavaProject("P2", new String[] {"src"}, "bin");

		IPackageFragmentRoot root = this.getPackageFragmentRoot("/P1/src");
		try {
			root.copy(new Path("/P2/src"), IResource.NONE, IPackageFragmentRoot.DESTINATION_PROJECT_CLASSPATH, null, null);
		} catch (JavaModelException e) {
			assertEquals("/P2/src already exists in target", e.getMessage());
			return;
		}
		assertTrue("Should throw a JavaModelException", false);
	} finally {
		this.deleteProject("P1");
		this.deleteProject("P2");
	}
}
/*
 * Ensure that coping a source root to another project with an existing source root in
 * non REPLACE mode throws the right JavaModelException.
 * (regression test bug 30511 IPackageFragmentRoot:move ignores FORCE flag)
 */
public void testFailCopySourceFolder2() throws CoreException {
	try {
		this.createJavaProject("P1", new String[] {"src"}, "bin");
		this.createJavaProject("P2", new String[] {"src"}, "bin");
		this.deleteFolder("/P2/src");

		IPackageFragmentRoot root = this.getPackageFragmentRoot("/P1/src");
		try {
			root.copy(new Path("/P2/src"), IResource.NONE, IPackageFragmentRoot.DESTINATION_PROJECT_CLASSPATH, null, null);
		} catch (JavaModelException e) {
			assertEquals("/P2/src already exists in target", e.getMessage());
			return;
		}
		assertTrue("Should throw a JavaModelException", false);
	} finally {
		this.deleteProject("P1");
		this.deleteProject("P2");
	}
}
/*
 * Ensure that deleting a jar package fragment root triggers the right delta
 * and that the model is up-to-date.
 */
public void testDeleteJarFile1() throws CoreException {
	try {
		IJavaProject project = this.createJavaProject("P", new String[] {"src"}, new String[] {"/P/myLib.jar"}, "bin");
		this.createFile("/P/myLib.jar", "");

		IPackageFragmentRoot root = this.getPackageFragmentRoot("/P/myLib.jar");
		startDeltas();
		delete(root);
		assertDeltas(
			"Unexpected delta",
			"P[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	myLib.jar[-]: {}\n" +
			"	ResourceDelta(/P/.classpath)[*]"
		);
		assertJavaProject(
			"P\n" +
			"	src\n" +
			"		<default>\n" +
			"	L/P/.classpath\n" +
			"	L/P/.project\n" +
			"	F/P/.settings",
			project);
	} finally {
		stopDeltas();
		this.deleteProject("P");
	}
}
/*
 * Ensure that deleting am external jar package fragment root triggers the right delta
 * and that the model is up-to-date.
 * (regression test for bug 30506 IPackageFragmentRoot:delete does not handle external jars)
 */
public void testDeleteJarFile3() throws CoreException {
	try {
		IJavaProject project = this.createJavaProject("P", new String[] {"src"}, new String[] {getExternalJCLPathString()}, "bin");

		IPackageFragmentRoot root = project.getPackageFragmentRoot(getExternalJCLPathString());
		startDeltas();
		delete(root);
		assertDeltas(
			"Unexpected delta",
			"P[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	" + getExternalJCLPathString() + "[*]: {REMOVED FROM CLASSPATH}\n" +
			"	ResourceDelta(/P/.classpath)[*]"
		);
		assertJavaProject(
			"P\n" +
			"	src\n" +
			"		<default>\n" +
			"	L/P/.classpath\n" +
			"	L/P/.project\n" +
			"	F/P/.settings",
			project);
	} finally {
		stopDeltas();
		this.deleteProject("P");
	}
}
/*
 * Ensure that deleting a jar file that is referenced by 2 projects triggers the right delta
 * and that the model is up-to-date.
 */
public void testDeleteJarFile2() throws CoreException {
	try {
		IJavaProject p1 = this.createJavaProject("P1", new String[] {"src"}, new String[] {"/P1/myLib.jar"}, "bin");
		this.createFile("/P1/myLib.jar", "");
		IJavaProject p2 = this.createJavaProject("P2", new String[] {"src"}, new String[] {"/P1/myLib.jar"}, "bin");

		IPackageFragmentRoot root = this.getPackageFragmentRoot("/P1/myLib.jar");
		startDeltas();
		delete(root);
		assertDeltas(
			"Unexpected delta",
			"P1[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	myLib.jar[-]: {}\n" +
			"	ResourceDelta(/P1/.classpath)[*]\n" +
			"P2[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	/P1/myLib.jar[-]: {}\n" +
			"	ResourceDelta(/P2/.classpath)[*]"
		);
		assertJavaProject(
			"P1\n" +
			"	src\n" +
			"		<default>\n" +
			"	L/P1/.classpath\n" +
			"	L/P1/.project\n" +
			"	F/P1/.settings",
			p1);
		assertJavaProject(
			"P2\n" +
			"	src\n" +
			"		<default>\n" +
			"	L/P2/.classpath\n" +
			"	L/P2/.project\n" +
			"	F/P2/.settings",
			p2);
	} finally {
		stopDeltas();
		this.deleteProject("P1");
		this.deleteProject("P2");
	}
}
/*
 * Ensure that a simple delete of a source root triggers the right delta
 * and that the model is up-to-date.
 */
public void testDeleteSourceFolder1() throws CoreException {
	try {
		IJavaProject project = this.createJavaProject("P", new String[] {"src"}, "bin");
		this.createFolder("/P/src/p");
		this.createFile(
			"/P/src/p/X.java",
			"package p;\n" +
			"public class X {\n" +
			"}"
		);
		IPackageFragmentRoot root = this.getPackageFragmentRoot("/P/src");
		startDeltas();
		delete(root);
		assertDeltas(
			"Unexpected delta",
			"P[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	src[-]: {}\n" +
			"	ResourceDelta(/P/.classpath)[*]"
		);
		assertJavaProject(
			"P\n" +
			"	L/P/.classpath\n" +
			"	L/P/.project\n" +
			"	F/P/.settings",
			project);
	} finally {
		stopDeltas();
		this.deleteProject("P");
	}
}
/*
 * Ensure that deleting a source root triggers the right delta
 * and doesn't delete a nested source folder.
 */
public void testDeleteSourceFolder2() throws CoreException {
	try {
		IJavaProject project = this.createJavaProject("P", new String[] {}, "bin");
		project.setRawClasspath(createClasspath(new String[] {"/P/src1", "src2/**", "/P/src1/src2", ""}, false/*no inclusion*/, true/*exclusion*/), null);
		IFolder folder = this.createFolder("/P/src1/p");
		IFile file = this.createFile(
			"/P/src1/p/X.java",
			"package p;\n" +
			"public class X {\n" +
			"}"
		);
		this.createFolder("/P/src1/src2/q");
		this.createFile(
			"/P/src1/src2/q/Y.java",
			"package q;\n" +
			"public class Y {\n" +
			"}"
		);
		IPackageFragmentRoot root = this.getPackageFragmentRoot("/P/src1");
		startDeltas();
		delete(root);

		assertDeltas(
			"Unexpected delta",
			"P[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	src1[*]: {REMOVED FROM CLASSPATH}\n" +
			"	src1/src2[*]: {REORDERED}\n" +
			"	ResourceDelta(/P/.classpath)[*]\n" +
			"	ResourceDelta(/P/src1)[*]"
		);

		assertJavaProject(
			"P\n" +
			"	src1/src2\n" +
			"		<default>\n" +
			"		q\n" +
			"			Y.java\n" +
			"	L/P/.classpath\n" +
			"	L/P/.project\n" +
			"	F/P/.settings\n" +
			"	F/P/src1",
			project);

		assertTrue("Original package folder should not exist", !folder.exists());
		assertTrue("Original cu file should not exist", !file.exists());
	} finally {
		stopDeltas();
		this.deleteProject("P");
	}
}
/*
 * Ensure that a simple move of a source root to another project triggers the right delta
 * and that the model is up-to-date.
 */
public void testMoveSourceFolder1() throws CoreException {
	try {
		IJavaProject p1 = this.createJavaProject("P1", new String[] {"src"}, "bin");
		IJavaProject p2 = this.createJavaProject("P2", new String[] {}, "bin");
		this.createFolder("/P1/src/p");
		this.createFile(
			"/P1/src/p/X.java",
			"package p;\n" +
			"public class X {\n" +
			"}"
		);
		IPackageFragmentRoot root = this.getPackageFragmentRoot("/P1/src");
		startDeltas();
		this.move(root, new Path("/P2/src"));
		assertDeltas(
			"Unexpected delta",
			"P1[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	src[-]: {MOVED_TO(src [in P2])}\n" +
			"	ResourceDelta(/P1/.classpath)[*]\n" +
			"P2[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	src[+]: {MOVED_FROM(src [in P1])}\n" +
			"	ResourceDelta(/P2/.classpath)[*]"
		);
		assertJavaProject(
			"P1\n" +
			"	L/P1/.classpath\n" +
			"	L/P1/.project\n" +
			"	F/P1/.settings",
			p1);
		assertJavaProject(
			"P2\n" +
			"	src\n" +
			"		<default>\n" +
			"		p\n" +
			"			X.java\n" +
			"	L/P2/.classpath\n" +
			"	L/P2/.project\n" +
			"	F/P2/.settings",
			p2);
	} finally {
		stopDeltas();
		this.deleteProject("P1");
		this.deleteProject("P2");
	}
}
/*
 * Ensure that moving and renaming a source root to another project triggers the right delta
 * and that the model is up-to-date.
 */
public void testMoveSourceFolder2() throws CoreException {
	try {
		IJavaProject p1 = this.createJavaProject("P1", new String[] {"src"}, "bin");
		IJavaProject p2 = this.createJavaProject("P2", new String[] {}, "bin");
		this.createFolder("/P1/src/p");
		this.createFile(
			"/P1/src/p/X.java",
			"package p;\n" +
			"public class X {\n" +
			"}"
		);
		IPackageFragmentRoot root = this.getPackageFragmentRoot("/P1/src");
		startDeltas();
		this.move(root, new Path("/P2/src2"));

		assertDeltas(
			"Unexpected delta",
			"P1[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	src[-]: {MOVED_TO(src2 [in P2])}\n" +
			"	ResourceDelta(/P1/.classpath)[*]\n" +
			"P2[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	src2[+]: {MOVED_FROM(src [in P1])}\n" +
			"	ResourceDelta(/P2/.classpath)[*]"
		);

		assertJavaProject(
			"P1\n" +
			"	L/P1/.classpath\n" +
			"	L/P1/.project\n" +
			"	F/P1/.settings",
			p1);
		assertJavaProject(
			"P2\n" +
			"	src2\n" +
			"		<default>\n" +
			"		p\n" +
			"			X.java\n" +
			"	L/P2/.classpath\n" +
			"	L/P2/.project\n" +
			"	F/P2/.settings",
			p2);
	} finally {
		stopDeltas();
		this.deleteProject("P1");
		this.deleteProject("P2");
	}
}
/*
 * Ensure that moving a source root to another project triggers the right delta
 * and doesn't move a nested source folder.
 */
public void testMoveSourceFolder3() throws CoreException {
	try {
		IJavaProject p1 = this.createJavaProject("P1", new String[] {}, "bin");
		p1.setRawClasspath(createClasspath(new String[] {"/P1/src1", "src2/**", "/P1/src1/src2", ""}, false/*no inclusion*/, true/*exclusion*/), null);
		IJavaProject p2 = this.createJavaProject("P2", new String[] {}, "bin");
		this.createFolder("/P1/src1/p");
		this.createFile(
			"/P1/src1/p/X.java",
			"package p;\n" +
			"public class X {\n" +
			"}"
		);
		this.createFolder("/P1/src1/src2/q");
		this.createFile(
			"/P1/src1/src2/q/Y.java",
			"package q;\n" +
			"public class Y {\n" +
			"}"
		);
		IPackageFragmentRoot root = this.getPackageFragmentRoot("/P1/src1");
		startDeltas();
		this.move(root, new Path("/P2/src1"));

		assertDeltas(
			"Unexpected delta",
			"P1[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	src1[*]: {REMOVED FROM CLASSPATH}\n" +
			"	src1/src2[*]: {REORDERED}\n" +
			"	ResourceDelta(/P1/.classpath)[*]\n" +
			"	ResourceDelta(/P1/src1)[*]\n" +
			"P2[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	src1[+]: {}\n" +
			"	ResourceDelta(/P2/.classpath)[*]"
		);

		assertJavaProject(
			"P1\n" +
			"	src1/src2\n" +
			"		<default>\n" +
			"		q\n" +
			"			Y.java\n" +
			"	L/P1/.classpath\n" +
			"	L/P1/.project\n" +
			"	F/P1/.settings\n" +
			"	F/P1/src1",
			p1);
		assertJavaProject(
			"P2\n" +
			"	src1\n" +
			"		<default>\n" +
			"		p\n" +
			"			X.java\n" +
			"	L/P2/.classpath\n" +
			"	L/P2/.project\n" +
			"	F/P2/.settings",
			p2);
	} finally {
		stopDeltas();
		this.deleteProject("P1");
		this.deleteProject("P2");
	}
}
/*
 * Ensure that moving a source root to another project before the first root triggers the right delta
 * and that the model is up-to-date.
 */
public void testMoveSourceFolder4() throws CoreException {
	try {
		IJavaProject p1 = this.createJavaProject("P1", new String[] {"src"}, "bin");
		IJavaProject p2 = this.createJavaProject("P2", new String[] {"src1", "src2"}, "bin");
		IPackageFragmentRoot root = this.getPackageFragmentRoot("/P1/src");

		// insert first
		IClasspathEntry sibling = JavaCore.newSourceEntry(new Path("/P2/src1"));
		startDeltas();
		this.move(root, new Path("/P2/src"), sibling);
		assertDeltas(
			"Unexpected delta",
			"P1[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	src[-]: {MOVED_TO(src [in P2])}\n" +
			"	ResourceDelta(/P1/.classpath)[*]\n" +
			"P2[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	src[+]: {MOVED_FROM(src [in P1])}\n" +
			"	src1[*]: {REORDERED}\n" +
			"	src2[*]: {REORDERED}\n" +
			"	ResourceDelta(/P2/.classpath)[*]"
		);
		assertJavaProject(
			"P1\n" +
			"	L/P1/.classpath\n" +
			"	L/P1/.project\n" +
			"	F/P1/.settings",
			p1);
		assertJavaProject(
			"P2\n" +
			"	src\n" +
			"		<default>\n" +
			"	src1\n" +
			"		<default>\n" +
			"	src2\n" +
			"		<default>\n" +
			"	L/P2/.classpath\n" +
			"	L/P2/.project\n" +
			"	F/P2/.settings",
			p2);
	} finally {
		stopDeltas();
		this.deleteProject("P1");
		this.deleteProject("P2");
	}
}
/*
 * Ensure that moving a source root to another project in the middle of existing roots triggers the right delta
 * and that the model is up-to-date.
 */
public void testMoveSourceFolder5() throws CoreException {
	try {
		IJavaProject p1 = this.createJavaProject("P1", new String[] {"src"}, "bin");
		IJavaProject p2 = this.createJavaProject("P2", new String[] {"src1", "src2"}, "bin");
		IPackageFragmentRoot root = this.getPackageFragmentRoot("/P1/src");

		// insert in the middle
		IClasspathEntry sibling = JavaCore.newSourceEntry(new Path("/P2/src2"));
		startDeltas();
		this.move(root, new Path("/P2/src"), sibling);
		assertDeltas(
			"Unexpected delta",
			"P1[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	src[-]: {MOVED_TO(src [in P2])}\n" +
			"	ResourceDelta(/P1/.classpath)[*]\n" +
			"P2[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	src[+]: {MOVED_FROM(src [in P1])}\n" +
			"	src2[*]: {REORDERED}\n" +
			"	ResourceDelta(/P2/.classpath)[*]"
		);
		assertJavaProject(
			"P1\n" +
			"	L/P1/.classpath\n" +
			"	L/P1/.project\n" +
			"	F/P1/.settings",
			p1);
		assertJavaProject(
			"P2\n" +
			"	src1\n" +
			"		<default>\n" +
			"	src\n" +
			"		<default>\n" +
			"	src2\n" +
			"		<default>\n" +
			"	L/P2/.classpath\n" +
			"	L/P2/.project\n" +
			"	F/P2/.settings",
			p2);
	} finally {
		stopDeltas();
		this.deleteProject("P1");
		this.deleteProject("P2");
	}
}
/*
 * Ensure that moving a source root to another project at the end of the classpath triggers the right delta
 * and that the model is up-to-date.
 */
public void testMoveSourceFolder6() throws CoreException {
	try {
		IJavaProject p1 = this.createJavaProject("P1", new String[] {"src"}, "bin");
		IJavaProject p2 = this.createJavaProject("P2", new String[] {"src1", "src2"}, "bin");
		IPackageFragmentRoot root = this.getPackageFragmentRoot("/P1/src");

		// insert last
		startDeltas();
		this.move(root, new Path("/P2/src"), null);
		assertDeltas(
			"Unexpected delta",
			"P1[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	src[-]: {MOVED_TO(src [in P2])}\n" +
			"	ResourceDelta(/P1/.classpath)[*]\n" +
			"P2[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	src[+]: {MOVED_FROM(src [in P1])}\n" +
			"	ResourceDelta(/P2/.classpath)[*]"
		);
		assertJavaProject(
			"P1\n" +
			"	L/P1/.classpath\n" +
			"	L/P1/.project\n" +
			"	F/P1/.settings",
			p1);
		assertJavaProject(
			"P2\n" +
			"	src1\n" +
			"		<default>\n" +
			"	src2\n" +
			"		<default>\n" +
			"	src\n" +
			"		<default>\n" +
			"	L/P2/.classpath\n" +
			"	L/P2/.project\n" +
			"	F/P2/.settings",
			p2);
	} finally {
		stopDeltas();
		this.deleteProject("P1");
		this.deleteProject("P2");
	}
}
/*
 * Ensure that a simple rename of a source root triggers the right delta
 * and that the model is up-to-date.
 */
public void testRenameSourceFolder1() throws CoreException {
	try {
		IJavaProject project = this.createJavaProject("P", new String[] {"src1"}, "bin");
		this.createFolder("/P/src1/p");
		this.createFile(
			"/P/src1/p/X.java",
			"package p;\n" +
			"public class X {\n" +
			"}"
		);
		IPackageFragmentRoot root = this.getPackageFragmentRoot("/P/src1");
		startDeltas();
		this.move(root, new Path("/P/src2"));
		assertDeltas(
			"Unexpected delta",
			"P[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	src1[-]: {MOVED_TO(src2 [in P])}\n" +
			"	src2[+]: {MOVED_FROM(src1 [in P])}\n" +
			"	ResourceDelta(/P/.classpath)[*]"
		);
		assertJavaProject(
			"P\n" +
			"	src2\n" +
			"		<default>\n" +
			"		p\n" +
			"			X.java\n" +
			"	L/P/.classpath\n" +
			"	L/P/.project\n" +
			"	F/P/.settings",
			project);
	} finally {
		stopDeltas();
		this.deleteProject("P");
	}
}
/*
 * Ensure that renaming a nested source root doesn't throw a JavaModelException
 * (regression test for bug 129991 [refactoring] Rename sourcefolder fails with JME)
 */
public void testRenameSourceFolder3() throws CoreException {
	try {
		createJavaProject("P");
		editFile(
			"/P/.classpath",
			"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
			"<classpath>\n" +
			"	<classpathentry excluding=\"src1/\" kind=\"src\" path=\"\"/>\n" +
			"	<classpathentry kind=\"src\" path=\"src1\"/>\n" +
			"</classpath>"
		);
		createFolder("/P/src1");
		IPackageFragmentRoot root = getPackageFragmentRoot("/P/src1");
		startDeltas();
		move(root, new Path("/P/src2"));
		assertDeltas(
			"Unexpected delta",
			"P[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	<project root>[*]: {ADDED TO CLASSPATH | REMOVED FROM CLASSPATH}\n" +
			"	src1[-]: {MOVED_TO(src2 [in P])}\n" +
			"	src2[+]: {MOVED_FROM(src1 [in P])}\n" +
			"	ResourceDelta(/P/.classpath)[*]"
		);
	} finally {
		stopDeltas();
		deleteProject("P");
	}
}
/*
 * Ensure that renaming a nested source root doesn't throw a JavaModelException
 * (regression test for bug 129991 [refactoring] Rename sourcefolder fails with JME)
 */
public void testRenameSourceFolder4() throws CoreException {
	try {
		createJavaProject("P");
		editFile(
			"/P/.classpath",
			"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
			"<classpath>\n" +
			"	<classpathentry excluding=\"src1/**\" kind=\"src\" path=\"\"/>\n" +
			"	<classpathentry kind=\"src\" path=\"src1\"/>\n" +
			"</classpath>"
		);
		createFolder("/P/src1");
		IPackageFragmentRoot root = getPackageFragmentRoot("/P/src1");
		startDeltas();
		move(root, new Path("/P/src2"));
		assertDeltas(
			"Unexpected delta",
			"P[*]: {CHILDREN}\n" +
			"	<project root>[*]: {CHILDREN}\n" +
			"		src2[+]: {MOVED_FROM(<default> [in src1 [in P]])}\n" +
			"	src1[-]: {MOVED_TO(src2 [in <project root> [in P]])}"
		);
	} finally {
		stopDeltas();
		deleteProject("P");
	}
}
/*
 * Ensure that renaming a source root keeps the same roots order,
 * and that it triggers the right delta and that the model is up-to-date.
 */
public void testRenameSourceFolder2() throws CoreException {
	try {
		IJavaProject project = this.createJavaProject("P", new String[] {"src1", "src2", "src3"}, "bin");

		// rename src1
		IPackageFragmentRoot root = this.getPackageFragmentRoot("/P/src1");
		startDeltas();
		this.move(root, new Path("/P/src4"));
		assertDeltas(
			"Unexpected delta after renaming src1",
			"P[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	src1[-]: {MOVED_TO(src4 [in P])}\n" +
			"	src4[+]: {MOVED_FROM(src1 [in P])}\n" +
			"	ResourceDelta(/P/.classpath)[*]"
		);
		assertJavaProject(
			"P\n" +
			"	src4\n" +
			"		<default>\n" +
			"	src2\n" +
			"		<default>\n" +
			"	src3\n" +
			"		<default>\n" +
			"	L/P/.classpath\n" +
			"	L/P/.project\n" +
			"	F/P/.settings",
			project);

		// rename src2
		root = this.getPackageFragmentRoot("/P/src2");
		clearDeltas();
		this.move(root, new Path("/P/src5"));
		assertDeltas(
			"Unexpected delta after renaming src2",
			"P[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	src2[-]: {MOVED_TO(src5 [in P])}\n" +
			"	src5[+]: {MOVED_FROM(src2 [in P])}\n" +
			"	ResourceDelta(/P/.classpath)[*]"
		);
		assertJavaProject(
			"P\n" +
			"	src4\n" +
			"		<default>\n" +
			"	src5\n" +
			"		<default>\n" +
			"	src3\n" +
			"		<default>\n" +
			"	L/P/.classpath\n" +
			"	L/P/.project\n" +
			"	F/P/.settings",
			project);

		// rename src3
		root = this.getPackageFragmentRoot("/P/src3");
		clearDeltas();
		this.move(root, new Path("/P/src6"));
		assertDeltas(
			"Unexpected delta after renaming src3",
			"P[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	src3[-]: {MOVED_TO(src6 [in P])}\n" +
			"	src6[+]: {MOVED_FROM(src3 [in P])}\n" +
			"	ResourceDelta(/P/.classpath)[*]"
		);
		assertJavaProject(
			"P\n" +
			"	src4\n" +
			"		<default>\n" +
			"	src5\n" +
			"		<default>\n" +
			"	src6\n" +
			"		<default>\n" +
			"	L/P/.classpath\n" +
			"	L/P/.project\n" +
			"	F/P/.settings",
			project);
	} finally {
		stopDeltas();
		this.deleteProject("P");
	}
}
/*
 * Ensure that a simple rename of a jar file triggers the right delta
 * and that the model is up-to-date.
 */
public void testRenameJarFile1() throws CoreException {
	try {
		IJavaProject project = this.createJavaProject("P", new String[] {"src"}, new String[] {"/P/myLib.jar"}, "bin");
		this.createFile("/P/myLib.jar", "");

		IPackageFragmentRoot root = this.getPackageFragmentRoot("/P/myLib.jar");
		startDeltas();
		this.move(root, new Path("/P/myLib2.jar"));
		assertDeltas(
			"Unexpected delta",
			"P[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	myLib.jar[-]: {MOVED_TO(myLib2.jar [in P])}\n" +
			"	myLib2.jar[+]: {MOVED_FROM(myLib.jar [in P])}\n" +
			"	ResourceDelta(/P/.classpath)[*]"
		);
		assertJavaProject(
			"P\n" +
			"	src\n" +
			"		<default>\n" +
			"	myLib2.jar\n" +
			"	L/P/.classpath\n" +
			"	L/P/.project\n" +
			"	F/P/.settings",
			project);
	} finally {
		stopDeltas();
		this.deleteProject("P");
	}
}
/*
 * Ensure that renaming of a jar file that is referenced by 2 projects triggers the right delta
 * and that the model is up-to-date.
 */
public void testRenameJarFile2() throws CoreException {
	try {
		IJavaProject p1 = this.createJavaProject("P1", new String[] {"src"}, new String[] {"/P1/myLib.jar"}, "bin");
		this.createFile("/P1/myLib.jar", "");
		IJavaProject p2 = this.createJavaProject("P2", new String[] {"src"}, new String[] {"/P1/myLib.jar"}, "bin");

		IPackageFragmentRoot root = this.getPackageFragmentRoot("/P1/myLib.jar");
		startDeltas();
		this.move(root, new Path("/P1/myLib2.jar"));
		assertDeltas(
			"Unexpected delta",
			"P1[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	myLib.jar[-]: {MOVED_TO(myLib2.jar [in P1])}\n" +
			"	myLib2.jar[+]: {MOVED_FROM(myLib.jar [in P1])}\n" +
			"	ResourceDelta(/P1/.classpath)[*]\n" +
			"P2[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	/P1/myLib.jar[-]: {MOVED_TO(myLib2.jar [in P1])}\n" +
			"	/P1/myLib2.jar[+]: {MOVED_FROM(myLib.jar [in P1])}\n" +
			"	ResourceDelta(/P2/.classpath)[*]"
		);
		assertJavaProject(
			"P1\n" +
			"	src\n" +
			"		<default>\n" +
			"	myLib2.jar\n" +
			"	L/P1/.classpath\n" +
			"	L/P1/.project\n" +
			"	F/P1/.settings",
			p1);
		assertJavaProject(
			"P2\n" +
			"	src\n" +
			"		<default>\n" +
			"	/P1/myLib2.jar\n" +
			"	L/P2/.classpath\n" +
			"	L/P2/.project\n" +
			"	F/P2/.settings",
			p2);
	} finally {
		stopDeltas();
		this.deleteProject("P1");
		this.deleteProject("P2");
	}
}
/*
 * Ensure that renaming of a jar file to an existing file in REPLACE mode
 * triggers the right delta and that the model is up-to-date.
 */
public void testRenameJarFile3() throws CoreException {
	try {
		IJavaProject project = this.createJavaProject("P", new String[] {"src"}, new String[] {"/P/myLib1.jar", "/P/myLib2.jar"}, "bin");
		this.createFile("/P/myLib1.jar", "");
		this.createFile("/P/myLib2.jar", "");

		IPackageFragmentRoot root = this.getPackageFragmentRoot("/P/myLib1.jar");
		startDeltas();
		root.move(
			new Path("/P/myLib2.jar"),
			IResource.NONE,
			IPackageFragmentRoot.ORIGINATING_PROJECT_CLASSPATH
				| IPackageFragmentRoot.DESTINATION_PROJECT_CLASSPATH
				| IPackageFragmentRoot.REPLACE,
			null,
			null);
		assertDeltas(
			"Unexpected delta",
			"P[*]: {CHILDREN | CONTENT | RAW CLASSPATH CHANGED | RESOLVED CLASSPATH CHANGED}\n" +
			"	myLib1.jar[-]: {MOVED_TO(myLib2.jar [in P])}\n" +
			"	myLib2.jar[*]: {CONTENT | REORDERED | ARCHIVE CONTENT CHANGED}\n" +
			"	ResourceDelta(/P/.classpath)[*]"
		);
		assertJavaProject(
			"P\n" +
			"	src\n" +
			"		<default>\n" +
			"	myLib2.jar\n" +
			"	L/P/.classpath\n" +
			"	L/P/.project\n" +
			"	F/P/.settings",
			project);
	} finally {
		stopDeltas();
		this.deleteProject("P");
	}
}
}
