/*******************************************************************************
 * Copyright (c) 2008, 2018 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.equinox.common.tests;

import static org.junit.Assert.assertThrows;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;

import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.URIUtil;
import org.eclipse.core.tests.harness.CoreTest;
import org.osgi.framework.FrameworkUtil;

/**
 * Tests for the {@link URIUtil} class.
 */
public class URIUtilTest extends CoreTest {
	/** Constant value indicating if the current platform is Windows */
	private static final boolean WINDOWS = java.io.File.separatorChar == '\\';

	private static final String[] testPaths = new String[] {"abc", "with spaces", "with%percent"};

	// re-enable once bug 331314 is fixed
	public void testBug331314() {
		doTestBug331314("File with spaces");
		doTestBug331314("FileWithBrackets[]");
		doTestBug331314("normal");
	}

	private void doTestBug331314(String name) {
		File f = new File(new File(System.getProperty("java.io.tmpdir")), name);
		URI original = f.toURI();
		URI base = f.getParentFile().toURI();
		URI relative = URIUtil.makeRelative(original, base);
		assertFalse(name, relative.isAbsolute());
		assertEquals("Wrong ssp", name, relative.getSchemeSpecificPart());
	}

	/**
	 * Tests for {@link URIUtil#toJarURI(URI, IPath)}.
	 */
	public void testToJARURI() {
		URL locationURL = FileLocator.find(FrameworkUtil.getBundle(getClass()),
				IPath.fromOSString("Plugin_Testing/uriutil/test.jar"), null);
		try {
			locationURL = FileLocator.resolve(locationURL);
			URI location = URIUtil.toURI(locationURL);
			final String suffix = "test/1029/test.txt";
			URI jar = URIUtil.toJarURI(location, IPath.fromOSString(suffix));
			InputStream is = jar.toURL().openStream();
			is.close();

			//null entry path
			URI jar2 = URIUtil.toJarURI(location, null);
			assertEquals("2.0", jar.toString(), jar2.toString() + suffix);

		} catch (MalformedURLException e) {
			fail("1.0", e);
		} catch (IOException e) {
			fail("1.1", e);
		} catch (URISyntaxException e) {
			fail("1.2", e);
		}
	}

	/**
	 * Tests for {@link URIUtil#toFile(URI)}.
	 * @throws URISyntaxException
	 */
	public void testToFile() throws URISyntaxException {
		File base = new File(System.getProperty("java.io.tmpdir"));
		for (int i = 0; i < testPaths.length; i++) {
			File original = new File(base, testPaths[i]);
			URI uri = original.toURI();
			File result = URIUtil.toFile(uri);
			assertEquals("1." + i, original, result);
		}
	}

	/**
	 * Tests for {@link URIUtil#toFile(URI)} involving UNC paths.
	 * @throws URISyntaxException
	 */
	public void testToFileUNC() throws URISyntaxException {
		if (!WINDOWS) {
			return;
		}
		//UNC paths
		URI path = new URI("file://HOST/some/path");
		File result = URIUtil.toFile(path);
		if (File.pathSeparatorChar == '/') {
			assertTrue("2.0", result.getAbsolutePath().startsWith("//"));
		} else {
			assertTrue("2.1", result.getAbsolutePath().startsWith("\\\\"));
		}
		assertTrue("2.2", IPath.fromOSString(result.toString()).isUNC());
	}

	/**
	 * Tests for {@link URIUtil#toUnencodedString(URI)}.
	 */
	public void testToUnencodedString() throws URISyntaxException {
		assertEquals("1.0", "http://foo.bar", URIUtil.toUnencodedString(new URI("http://foo.bar")));
		assertEquals("1.1", "http://foo.bar#fragment", URIUtil.toUnencodedString(new URI("http://foo.bar#fragment")));
		assertEquals("1.2", "foo.bar#fragment", URIUtil.toUnencodedString(new URI("foo.bar#fragment")));
		assertEquals("1.3", "#fragment", URIUtil.toUnencodedString(new URI("#fragment")));

		//spaces
		assertEquals("2.1", "http://foo.bar/a b", URIUtil.toUnencodedString(new URI("http://foo.bar/a%20b")));
		assertEquals("2.2", "http://foo.bar/a#b c", URIUtil.toUnencodedString(new URI("http://foo.bar/a#b%20c")));
		assertEquals("2.3", "foo.bar/a b", URIUtil.toUnencodedString(new URI("foo.bar/a%20b")));
		assertEquals("2.4", "#a b", URIUtil.toUnencodedString(new URI("#a%20b")));
	}

	/**
	 * Tests for {@link URIUtil#fromString(String)}.
	 */
	public void testFromString() throws URISyntaxException {
		//spaces
		assertEquals("1.1", new URI("http://foo.bar/a%20b"), URIUtil.fromString("http://foo.bar/a b"));
		assertEquals("1.2", new URI("http://foo.bar/a#b%20c"), URIUtil.fromString("http://foo.bar/a#b c"));
		assertEquals("1.3", new URI("foo.bar/a%20b"), URIUtil.fromString("foo.bar/a b"));
		assertEquals("1.4", new URI("#a%20b"), URIUtil.fromString("#a b"));
		assertEquals("1.5", new URI("file:/C:/foo.bar/a%20b"), URIUtil.fromString("file:/C:/foo.bar/a b"));

		//percent character
		assertEquals("2.1", new URI("http://foo.bar/a%2520b"), URIUtil.fromString("http://foo.bar/a%20b"));
		assertEquals("2.2", new URI("http://foo.bar/a#b%2520c"), URIUtil.fromString("http://foo.bar/a#b%20c"));
		assertEquals("2.3", new URI("foo.bar/a%2520b"), URIUtil.fromString("foo.bar/a%20b"));
		assertEquals("2.4", new URI("#a%2520b"), URIUtil.fromString("#a%20b"));
		assertEquals("2.5", new URI("file:/C:/foo.bar/a%2520b"), URIUtil.fromString("file:/C:/foo.bar/a%20b"));

		//relative URI
		assertEquals("3.1", new URI("a/b"), URIUtil.fromString("file:a/b"));
		assertEquals("3.2", new URI("a/b"), URIUtil.fromString("a/b"));
		if (WINDOWS) {
			assertEquals("3.3", new URI("file:/c:/a/b"), URIUtil.fromString("file:c:/a/b"));
			assertEquals("3.4", new URI("file:/c:/a/b"), URIUtil.fromString("file:c:\\a\\b"));
			assertEquals("3.5", new URI("file:/c:/a/b"), URIUtil.fromString("file:/c:\\a\\b"));
			assertEquals("3.6", new URI("file:/a/b/c"), URIUtil.fromString("file:/a/b/c"));//bug 264101
			//backslash
			assertEquals("3.7", new URI("file:/a/b/c"), URIUtil.fromString("file:\\a\\b\\c"));//bug 264101
		}

		//encoded legal character
		assertEquals("4.1", new URI("http://foo.bar/a%2Cb").getSchemeSpecificPart(), URIUtil.fromString("http://foo.bar/a,b").getSchemeSpecificPart());
		assertEquals("4.2", new URI("file:/foo.bar/a%2Cb").getSchemeSpecificPart(), URIUtil.fromString("file:/foo.bar/a,b").getSchemeSpecificPart());

		//backslash
		URI uri = URIUtil.fromString("a\\b");
		System.out.println(uri);
	}

	/**
	 * Tests for {@link URIUtil#toURI(java.net.URL)}.
	 */
	public void testURLtoURI() throws MalformedURLException, URISyntaxException {
		//spaces
		assertEquals("1.1", new URI("http://foo.bar/a%20b"), URIUtil.toURI(new URL("http://foo.bar/a b")));
		assertEquals("1.2", new URI("http://foo.bar/a#b%20c"), URIUtil.toURI(new URL("http://foo.bar/a#b c")));

		//% characters
		assertEquals("2.1", new URI("http://foo.bar/a%25b"), URIUtil.toURI(new URL("http://foo.bar/a%b")));

		//UNC paths
		assertEquals("3.1", new URI("file:////SERVER/some/path"), URIUtil.toURI(new URL("file://SERVER/some/path")));
		assertEquals("3.2", new URI("file:////SERVER/some/path"), URIUtil.toURI(new URL("file:////SERVER/some/path")));
	}

	/**
	 * Tests for {@link URIUtil#toURL(java.net.URI)}.
	 */
	public void testURItoURL() throws MalformedURLException, URISyntaxException {
		//spaces
		assertEquals("1.1", new URL("http://foo.bar/a%20b"), URIUtil.toURL(new URI("http://foo.bar/a%20b")));
		assertEquals("1.2", new URL("http://foo.bar/a#b%20c"), URIUtil.toURL(new URI("http://foo.bar/a#b%20c")));

		//% characters
		assertEquals("2.1", new URL("http://foo.bar/a%25b"), URIUtil.toURL(new URI("http://foo.bar/a%25b")));

		//UNC paths
		assertEquals("3.1", new URL("file:////SERVER/some/path"), URIUtil.toURL(new URI("file:////SERVER/some/path")));
		assertEquals("3.2", new URL("file://SERVER/some/path"), URIUtil.toURL(new URI("file://SERVER/some/path")));
	}

	/**
	 * Tests handling of Absolute file system paths on Windows incorrectly encoded as
	 * relative URIs (file:c:/tmp).
	 */
	public void testWindowsPathsFromURI() throws MalformedURLException, URISyntaxException {
		if (!WINDOWS) {
			return;
		}
		assertEquals("1.1", new URI("file:/c:/foo/bar.txt"), URIUtil.toURI(new URL("file:c:/foo/bar.txt")));
		assertEquals("1.2", new URI("file:/c:/foo/bar.txt"), URIUtil.toURI(new URL("file:/c:/foo/bar.txt")));
	}

	/**
	 * Tests handling of Absolute file system paths on Windows incorrectly encoded as
	 * relative URIs (file:c:/tmp).
	 */
	public void testWindowsPathsFromString() throws URISyntaxException {
		if (!WINDOWS) {
			return;
		}
		assertEquals("1.1", new URI("file:/c:/foo/bar.txt"), URIUtil.fromString("file:c:/foo/bar.txt"));
		assertEquals("1.2", new URI("file:/c:/foo/bar.txt"), URIUtil.fromString("file:/c:/foo/bar.txt"));
	}

	/**
	 * Tests handling of conversion from a File with spaces to URL and File to URI and equivalence of the resulting URI
	 */
	public void testFileWithSpaces() throws MalformedURLException, URISyntaxException {
		File fileWithSpaces = new File("/c:/with spaces/goo");
		URI correctURI = fileWithSpaces.toURI();
		URL fileURL = fileWithSpaces.toURL();
		URI fileURI = null;
		try {
			fileURI = fileURL.toURI();
			fail();
		} catch (URISyntaxException e) {
			fileURI = URIUtil.toURI(fileURL);
		}
		assertEquals("1.1", correctURI, fileURI);

		try {
			fileURI = new URI(fileURL.toString());
			fail();
		} catch (URISyntaxException e) {
			fileURI = URIUtil.fromString(fileURL.toString());
		}
		assertEquals("1.2", correctURI, fileURI);
	}

	/**
	 * Tests handling of conversion from a File with spaces to URL and File to URI and equivalence of the resulting URI
	 */
	public void testFileWithBrackets() throws MalformedURLException, URISyntaxException {
		File fileWithSpaces = new File("/c:/with[brackets]/goo");
		URI correctURI = fileWithSpaces.toURI();
		URL fileURL = fileWithSpaces.toURL();
		URI fileURI = null;
		try {
			fileURI = fileURL.toURI();
			fail();
		} catch (URISyntaxException e) {
			fileURI = URIUtil.toURI(fileURL);
		}
		assertEquals("1.1", correctURI, fileURI);

		try {
			fileURI = new URI(fileURL.toString());
			fail();
		} catch (URISyntaxException e) {
			fileURI = URIUtil.fromString(fileURL.toString());
		}
		assertEquals("1.2", correctURI, fileURI);
	}

	/**
	 * Tests for {@link URIUtil#append(URI, String)}.
	 * @throws URISyntaxException
	 */
	public void testAppend() throws URISyntaxException {
		URI base = new URI("http://a.b.c/a%20b/");
		URI result = URIUtil.append(base, "file.txt");
		assertEquals("1.0", "http://a.b.c/a%20b/file.txt", result.toString());
		assertEquals("1.1", "//a.b.c/a b/file.txt", result.getSchemeSpecificPart());

		base = new URI("http://a.b.c/a%20b/");
		result = URIUtil.append(base, "a b.txt");
		assertEquals("2.0", "http://a.b.c/a%20b/a%20b.txt", result.toString());
		assertEquals("2.1", "//a.b.c/a b/a b.txt", result.getSchemeSpecificPart());

	}

	/**
	 * Tests for {@link URIUtil#append(URI, String)} when dealing with UNC paths.
	 */
	public void testAppendUNC() throws URISyntaxException {
		//UNC paths
		URI base = new URI("file:////SERVER/some/path/");
		URI relative = new URI("plugins/javax.servlet_2.4.0.v200806031604.jar");
		URI expectedResolved = new URI("file:////SERVER/some/path/plugins/javax.servlet_2.4.0.v200806031604.jar");
		URI resolved = URIUtil.append(base, relative.toString());
		assertEquals("1.0", expectedResolved, resolved);
	}

	/**
	 * Tests for {@link URIUtil#append(URI, String)} when dealing with paths containing brackets.
	 * @throws URISyntaxException
	 */
	public void testAppendWithBrackets() throws URISyntaxException {
		//append a simple string
		URI base = new URI("http://example.com/base/");
		URI result = URIUtil.append(base, "file[with brackets].txt");
		assertEquals("1.0", "http://example.com/base/file%5Bwith%20brackets%5D.txt", result.toString());
		assertEquals("1.1", "/base/file[with brackets].txt", result.getPath());

		//append a relative path
		result = URIUtil.append(base, "some/path/file[with brackets].txt");
		assertEquals("2.0", "http://example.com/base/some/path/file%5Bwith%20brackets%5D.txt", result.toString());
		assertEquals("2.1", "/base/some/path/file[with brackets].txt", result.getPath());

		//simple string where base has no trailing separator
		base = new URI("http://example.com/base");
		result = URIUtil.append(base, "file[with brackets].txt");
		assertEquals("3.0", "http://example.com/base/file%5Bwith%20brackets%5D.txt", result.toString());
		assertEquals("3.1", "/base/file[with brackets].txt", result.getPath());

		//append a path where base has no trailing separator
		result = URIUtil.append(base, "some/path/file[with brackets].txt");
		assertEquals("4.0", "http://example.com/base/some/path/file%5Bwith%20brackets%5D.txt", result.toString());
		assertEquals("4.1", "/base/some/path/file[with brackets].txt", result.getPath());

		//TODO opaque URI
		//		URI opaque = new URI("opaque:something/opaque/");
		//		result = URIUtil.append(opaque, "some/path/file[with brackets].txt");
		//		assertEquals("5.0", "opaque:something/opaque/some/path/file%5Bwith%20brackets%5D.txt", result.toString());
		//		assertEquals("5.1", null, result.getPath());
	}

	public void testBug286339() throws URISyntaxException {

		//single letter server path
		URI base = new URI("file:////S/some/path/");
		URI relative = new URI("plugins/javax.servlet_2.4.0.v200806031604.jar");
		URI expectedResolved = new URI("file:////S/some/path/plugins/javax.servlet_2.4.0.v200806031604.jar");
		URI resolved = URIUtil.append(base, relative.toString());
		assertEquals("1.1", expectedResolved, resolved);

	}

	public void testAppendWindows() throws URISyntaxException {
		if (!WINDOWS) {
			return;
		}
		URI base = new URI("file:/C:/a%20b");
		URI result = URIUtil.append(base, "file.txt");
		assertEquals("1.0", "file:/C:/a%20b/file.txt", result.toString());
		assertEquals("1.1", "/C:/a b/file.txt", result.getSchemeSpecificPart());

		base = new URI("file:/C:/Documents%20and%20Settings/aniefer/junit-workspace/pde.build/265726/buildRepo/");
		result = URIUtil.append(base, "content.jar");
		assertEquals("2.0", "file:/C:/Documents%20and%20Settings/aniefer/junit-workspace/pde.build/265726/buildRepo/content.jar", result.toString());
		assertEquals("2.1", "/C:/Documents and Settings/aniefer/junit-workspace/pde.build/265726/buildRepo/content.jar", result.getSchemeSpecificPart());
	}

	/**
	 * Tests handling of conversion from a File with %20 to URL and File to URI and equivalence of the resulting URI
	 */
	public void testFileWithPercent20() throws MalformedURLException, URISyntaxException {
		File fileWithPercent20 = new File("/c:/with%20spaces/goo");
		URI correctURI = fileWithPercent20.toURI();

		URL fileURL = fileWithPercent20.toURL();
		assertNotSame("1.1", correctURI, fileURL.toURI());
		assertEquals("1.2", correctURI, URIUtil.toURI(fileURL));
		assertNotSame("1.3", correctURI, new URI(fileURL.toString()));
		// we expect these to not be the same because fromString assumes a decoded URL String
		assertNotSame("1.4", correctURI, URIUtil.fromString(fileURL.toString()));
	}

	public void testRemoveExtension() {
		try {
			URI uri1 = new URI("file:/foo/bar/zoo.txt");
			assertEquals(new URI("file:/foo/bar/zoo"), URIUtil.removeFileExtension(uri1));

			URI uri2 = new URI("file:/foo/bar.zoo/foo.txt");
			assertEquals(new URI("file:/foo/bar.zoo/foo"), URIUtil.removeFileExtension(uri2));

			URI uri3 = new URI("file:/foo/bar.zoo/foo");
			assertEquals(new URI("file:/foo/bar.zoo/foo"), URIUtil.removeFileExtension(uri3));

			URI uri4 = new URI("file:/C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/testRepo/plugins/org.junit_3.8.2.v200706111738.jar");
			assertEquals(new URI("file:/C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/testRepo/plugins/org.junit_3.8.2.v200706111738"), URIUtil.removeFileExtension(uri4));
		} catch (URISyntaxException e) {
			fail("URI syntax exception", e);
		}
	}

	public void testRemoveFileExtensionFromFile() {
		String fileName = "/c:/some.dir/afile";
		File testFileWithExtension = new File(fileName + ".extension");
		File testFileWithOutExtension = new File(fileName);
		URI correctURI = testFileWithOutExtension.toURI();

		assertEquals(correctURI, URIUtil.removeFileExtension(testFileWithExtension.toURI()));
		assertEquals(correctURI, URIUtil.removeFileExtension(testFileWithOutExtension.toURI()));
	}

	public void testSameURI() throws URISyntaxException {
		assertFalse("1.0", URIUtil.sameURI(new File("a").toURI(), URIUtil.fromString("file:a")));
		assertFalse("1.1", URIUtil.sameURI(new URI("file:/a"), URIUtil.fromString("file:a")));

		//encoded characters
		assertTrue("2.0", URIUtil.sameURI(new URI("foo:/a%2Cb"), new URI("foo:/a,b")));
		assertTrue("2.1", URIUtil.sameURI(new URI("file:/a%2Cb"), new URI("file:/a,b")));
	}

	public void testSameURIWindows() throws URISyntaxException {
		if (!WINDOWS) {
			return;
		}
		//device and case variants
		assertTrue("1.0", URIUtil.sameURI(new URI("file:C:/a"), new URI("file:c:/a")));
		assertTrue("1.1", URIUtil.sameURI(new URI("file:/C:/a"), new URI("file:/c:/a")));
		assertTrue("1.2", URIUtil.sameURI(new URI("file:/A"), new URI("file:/a")));
		assertTrue("1.3", URIUtil.sameURI(new URI("file:A"), new URI("file:a")));
		assertTrue("1.4", URIUtil.sameURI(new URI("file:/A/"), new URI("file:/a/")));

		//negative cases
		assertFalse("2.0", URIUtil.sameURI(new URI("file:/a/b"), new URI("file:/c:/a/b")));
	}

	public void testMakeAbsolute() throws URISyntaxException {
		URI[][] data = new URI[][] {
				// simple path
				new URI[] {new URI("b"), new URI("file:/a/"), new URI("file:/a/b")}, //
				new URI[] {new URI("b"), new URI("file:/a"), new URI("file:/a/b")},
				// common root
				new URI[] {new URI("plugins/foo.jar"), new URI("file:/eclipse/"), new URI("file:/eclipse/plugins/foo.jar")},
				// non-local
				new URI[] {new URI("http:/foo.com/a/b"), new URI("file:/a/x"), new URI("http:/foo.com/a/b")}, //
				new URI[] {new URI("file:/a/b"), new URI("http:/foo.com/a/x"), new URI("file:/a/b")}, //
				//
				new URI[] {new URI("../plugins/foo.jar"), new URI("file:/eclipse/configuration"), new URI("file:/eclipse/plugins/foo.jar")}, //
				//cases that can't be made absolute
				//different scheme
				new URI[] {new URI("file:../plugins/foo.jar"), new URI("http:/eclipse/configuration"), new URI("file:../plugins/foo.jar")}, //
				//already absolute
				new URI[] {new URI("file:../plugins/foo.jar"), new URI("file:/eclipse/configuration"), new URI("file:../plugins/foo.jar")}, //
				new URI[] {new URI("file:/foo.jar"), new URI("file:/eclipse/configuration"), new URI("file:/foo.jar")}, //
				//encoded characters
				new URI[] {new URI("plugins%5Cfoo.jar"), new URI("file:/eclipse/"), new URI("file:/eclipse/plugins%5Cfoo.jar")},//
				new URI[] {new URI("a%20b"), new URI("file:/eclipse/"), new URI("file:/eclipse/a%20b")},//
		};

		for (int i = 0; i < data.length; i++) {
			URI location = data[i][0];
			URI root = data[i][1];
			URI expected = data[i][2];
			URI actual = URIUtil.makeAbsolute(location, root);
			assertEquals("1." + Integer.toString(i), expected, actual);
		}

		// run some Windows-specific tests with drive letters
		if (!WINDOWS) {
			return;
		}
		data = new URI[][] {
				// simple path
				new URI[] {new URI("b"), new URI("file:/c:/a/"), new URI("file:/c:/a/b")}, //
				new URI[] {new URI("b"), new URI("file:/c:/a"), new URI("file:/c:/a/b")},
				// common root
				new URI[] {new URI("plugins/foo.jar"), new URI("file:/c:/eclipse/"), new URI("file:/c:/eclipse/plugins/foo.jar")},
				// different drives
				new URI[] {new URI("file:/c:/a/b"), new URI("file:/d:/a/x"), new URI("file:/c:/a/b")}, //
				new URI[] {new URI("file:/c:/eclipse/plugins/foo.jar"), new URI("file:/d:/eclipse/"), new URI("file:/c:/eclipse/plugins/foo.jar")},
				// non-local
				new URI[] {new URI("http:/c:/a/b"), new URI("file:/c:/a/x"), new URI("http:/c:/a/b")}, //
				new URI[] {new URI("file:/c:/a/b"), new URI("http:/c:/a/x"), new URI("file:/c:/a/b")}, //
				//
				new URI[] {new URI("b"), new URI("file:/C:/a/"), new URI("file:/C:/a/b")}, //
				new URI[] {new URI("b"), new URI("file:/C:/a"), new URI("file:/C:/a/b")}, //
				new URI[] {new URI("file:/c:/"), new URI("file:/d:/"), new URI("file:/c:/")}, //
				new URI[] {new URI("/c:/a/b/c"), new URI("file:/d:/a/b/"), new URI("file:/c:/a/b/c")}, //
				new URI[] {new URI(""), new URI("file:/c:/"), new URI("file:/c:/")}, //
				//
				new URI[] {new URI("../plugins/foo.jar"), new URI("file:/c:/eclipse/configuration"), new URI("file:/c:/eclipse/plugins/foo.jar")}, //
				//already absolute
				new URI[] {new URI("file:../plugins/foo.jar"), new URI("file:/c:/eclipse/configuration"), new URI("file:../plugins/foo.jar")}, //
		};
		for (int i = 0; i < data.length; i++) {
			URI location = data[i][0];
			URI root = data[i][1];
			URI expected = data[i][2];
			URI actual = URIUtil.makeAbsolute(location, root);
			assertEquals("2." + Integer.toString(i), expected, actual);
		}
	}

	/**
	 * Tests for {@link URIUtil#makeAbsolute(URI, URI)} involving UNC paths.
	 */
	public void testMakeAbsoluteUNC() throws URISyntaxException {
		URI base = new URI("file:////SERVER/some/path/");
		URI relative = new URI("plugins/javax.servlet_2.4.0.v200806031604.jar");
		URI result = URIUtil.makeAbsolute(relative, base);
		assertEquals("1.0", new URI("file:////SERVER/some/path/plugins/javax.servlet_2.4.0.v200806031604.jar"), result);

		//an absolute URI should not be resolved
		URI absolute = new URI("file:////ANOTHERSERVER/another/path");
		URI resolved = URIUtil.makeAbsolute(absolute, base);
		assertEquals("1.1", absolute, resolved);

	}

	public void testMakeRelative() throws URISyntaxException {
		URI[][] data = new URI[][] {
				// simple path
				new URI[] {new URI("file:/a/b"), new URI("file:/a/x"), new URI("../b")},
				// common root
				new URI[] {new URI("file:/eclipse/plugins/foo.jar"), new URI("file:/eclipse/"), new URI("plugins/foo.jar")},
				// non-local
				new URI[] {new URI("http:/foo.com/a/b"), new URI("file:/a/x"), new URI("http:/foo.com/a/b")}, //
				new URI[] {new URI("file:/a/b"), new URI("http:/foo.com/a/x"), new URI("file:/a/b")}, //
				//
				new URI[] {new URI("file:/"), new URI("file:/"), new URI("")}, //
		};

		for (int i = 0; i < data.length; i++) {
			URI location = data[i][0];
			URI root = data[i][1];
			URI expected = data[i][2];
			URI actual = URIUtil.makeRelative(location, root);
			assertEquals("1." + Integer.toString(i), expected, actual);
		}

		// test some Windows-specific paths with drive letters
		if (!WINDOWS) {
			return;
		}
		data = new URI[][] {
				// simple path
				new URI[] {new URI("file:/c:/a/b"), new URI("file:/c:/a/x"), new URI("../b")},
				// common root
				new URI[] {new URI("file:/c:/eclipse/plugins/foo.jar"), new URI("file:/c:/eclipse/"), new URI("plugins/foo.jar")},
				// different drives
				new URI[] {new URI("file:/c:/a/b"), new URI("file:/d:/a/x"), new URI("file:/c:/a/b")}, //
				new URI[] {new URI("file:/c:/eclipse/plugins/foo.jar"), new URI("file:/d:/eclipse/"), new URI("file:/c:/eclipse/plugins/foo.jar")},
				// non-local
				new URI[] {new URI("http:/c:/a/b"), new URI("file:/c:/a/x"), new URI("http:/c:/a/b")}, //
				new URI[] {new URI("file:/c:/a/b"), new URI("http:/c:/a/x"), new URI("file:/c:/a/b")}, //
				//
				new URI[] {new URI("file:/c:/a/b"), new URI("file:/C:/a/x"), new URI("../b")}, //
				new URI[] {new URI("file:/c:/"), new URI("file:/d:/"), new URI("file:/c:/")}, //
				new URI[] {new URI("file:/c:/"), new URI("file:/c:/"), new URI("")}, //
		};
		for (int i = 0; i < data.length; i++) {
			URI location = data[i][0];
			URI root = data[i][1];
			URI expected = data[i][2];
			URI actual = URIUtil.makeRelative(location, root);
			assertEquals("2." + Integer.toString(i), expected, actual);
		}
	}

	/**
	 * Test UNC-Paths containing a $.
	 */
	public void testDollar() throws URISyntaxException {
		final var relative = "SomePath";
		final String[] uris = {
				// there must be four slashes because otherwise "WSL$" would be the host name
				// and it is not allows to use $ in the host name
				// @formatter:off
				"file:////WSL$/Ubuntu",
				"file:////WSL$/Ubuntu/",
				"file://////WSL$/Ubuntu",
				"file://////WSL$/Ubuntu/"
				// @formatter:on
		};


		// Test that appending a segment without trailing slash works as expected
		final URI expectedResolved1 = new URI("file:////WSL$/Ubuntu/SomePath");
		for (String uri : uris) {
			final URI base = new URI(uri);
			final URI resolved = URIUtil.append(base, relative);
			assertEquals("Appending " + relative + " to URI of " + uri + " faild", expectedResolved1, resolved);
		}

		// Test that appending a segment with trailing slash works as expected
		final var relative2 = "SomePath/";
		final URI expectedResolved2 = new URI("file:////WSL$/Ubuntu/SomePath/");

		for (String uri : uris) {
			final URI base = new URI(uri);
			final URI resolved = URIUtil.append(base, relative2);
			assertEquals("Appending " + relative2 + " to URI of " + uri + "failed", expectedResolved2, resolved);
		}

		// Ensure that creating an URI from string works as expected, too
		for (String uri : uris) {
			new URI(uri.toString());
			final URI resolved = URIUtil.fromString(uri);
			assertEquals("Creating URI using fromString " + uri + " failed", new URI(uri), resolved);
		}

		final String[] expected3 = {
				// @formatter:off
				"file:////WSL$/Ubuntu",
				"file:////WSL$/Ubuntu/",
				"file:////WSL$/Ubuntu",
				"file:////WSL$/Ubuntu/"
				// @formatter:on
		};

		// The following two ctors actually work as expected
		for (String uri : uris) {
			final URI test = new URI(uri);
			new URI(test.getScheme(), test.getSchemeSpecificPart(), test.getFragment());
			assertEquals(
					"Crating an URI using URIUtil from URI using Scheme, SchemeSpecificPart and Fragment failed for "
							+ uri,
					test,
					URIUtil.toURI(test.getScheme(), test.getSchemeSpecificPart(), test.getFragment()));

		}

		for (int i = 0; i < uris.length; i++) {
			URI test = new URI(uris[i]);
			URI ref = new URI(expected3[i]);

			new URI(test.getScheme(), test.getAuthority(), test.getPath(), test.getQuery(), test.getFragment());
			assertEquals(
					"Crating an URI using URIUtil from URI using Scheme, Authority, Path, Query and Fragment failed for "
							+ uris[i],
					ref, URIUtil.toURI(test.getScheme(), test.getAuthority(), test.getPath(), test.getQuery(),
							test.getFragment()));

		}

		// The other two ctors fail, if the URL contains a $
		for (int i = 0; i < uris.length; i++) {
			final URI test = new URI(uris[i]);
			final URI ref = new URI(expected3[i]);
			if (i < 2) {
			assertThrows(
					"Crating an URI from URI using Scheme, UserInfo, Host, Port, Path, Query and Fragment should fail for "
							+ uris[i],
					URISyntaxException.class, () -> {
				new URI(test.getScheme(), test.getUserInfo(), test.getHost(), test.getPort(), test.getPath(),
								test.getQuery(), test.getFragment());
						});
			}
			assertEquals("Crating an URI using URIUtil from URI using Scheme, UserInfo, Host, Port, Path, Query and Fragment failed for "
					+ uris[i], ref,
					URIUtil.toURI(test.getScheme(), test.getUserInfo(), test.getHost(), test.getPort(), test.getPath(),
							test.getQuery(), test.getFragment()));
		}

		for (int i = 0; i < uris.length; i++) {
			final URI test = new URI(uris[i]);
			final URI ref = new URI(expected3[i]);
			if (i < 2) {
			assertThrows("Crating an URI from URI using Scheme, Host, Path, and Fragment should fail for " + uris[i],
					URISyntaxException.class, () -> {
						new URI(test.getScheme(), test.getHost(), test.getPath(), test.getFragment());
					});
			}
			assertEquals(
					"Crating an URI using URIUtil from URI using Scheme, Host, Path, and Fragment failed for "
							+ uris[i],
					ref,
					URIUtil.toURI(test.getScheme(), test.getHost(), test.getPath(), test.getFragment()));
		}



	}
}
