In progress: [Issue 204] Import paths ineffective in protobuf-dt 1.2, on
Windows

* Fixed bug in Windows. Need to test that import resolution works on Mac
and Linux.
diff --git a/com.google.eclipse.protobuf.ui.test/src/com/google/eclipse/protobuf/ui/preferences/paths/core/ProjectVariable_replaceProjectNameWithProjectVariable_Test.java b/com.google.eclipse.protobuf.ui.test/src/com/google/eclipse/protobuf/ui/preferences/paths/core/ProjectVariable_replaceProjectNameWithProjectVariable_Test.java
index c8252b4..5ca7a53 100644
--- a/com.google.eclipse.protobuf.ui.test/src/com/google/eclipse/protobuf/ui/preferences/paths/core/ProjectVariable_replaceProjectNameWithProjectVariable_Test.java
+++ b/com.google.eclipse.protobuf.ui.test/src/com/google/eclipse/protobuf/ui/preferences/paths/core/ProjectVariable_replaceProjectNameWithProjectVariable_Test.java
@@ -32,20 +32,20 @@
     IPath path = new Path("/test/src/test");
     when(project.getName()).thenReturn("test");
     IPath newPath = ProjectVariable.replaceProjectNameWithProjectVariable(path, project);
-    assertThat(newPath.toOSString(), equalTo("/${project}/src/test"));
+    assertThat(newPath.toPortableString(), equalTo("/${project}/src/test"));
   }
 
   @Test public void should_not_use_variable_if_path_does_not_contain_project_name() {
     IPath path = new Path("/main/src/test");
     when(project.getName()).thenReturn("test");
     IPath newPath = ProjectVariable.replaceProjectNameWithProjectVariable(path, project);
-    assertThat(newPath.toOSString(), equalTo("/main/src/test"));
+    assertThat(newPath.toPortableString(), equalTo("/main/src/test"));
   }
 
   @Test public void should_not_use_variable_if_path_already_contains_it() {
     IPath path = new Path("/${project}/src/test");
     when(project.getName()).thenReturn("test");
     IPath newPath = ProjectVariable.replaceProjectNameWithProjectVariable(path, project);
-    assertThat(newPath.toOSString(), equalTo("/${project}/src/test"));
+    assertThat(newPath.toPortableString(), equalTo("/${project}/src/test"));
   }
 }
diff --git a/com.google.eclipse.protobuf.ui.test/src/com/google/eclipse/protobuf/ui/preferences/paths/core/ProjectVariable_replaceProjectVariableWithProjectName_Test.java b/com.google.eclipse.protobuf.ui.test/src/com/google/eclipse/protobuf/ui/preferences/paths/core/ProjectVariable_replaceProjectVariableWithProjectName_Test.java
index 117fcfe..76cd633 100644
--- a/com.google.eclipse.protobuf.ui.test/src/com/google/eclipse/protobuf/ui/preferences/paths/core/ProjectVariable_replaceProjectVariableWithProjectName_Test.java
+++ b/com.google.eclipse.protobuf.ui.test/src/com/google/eclipse/protobuf/ui/preferences/paths/core/ProjectVariable_replaceProjectVariableWithProjectName_Test.java
@@ -32,20 +32,20 @@
     when(project.getName()).thenReturn("test");
     IPath path = new Path("/${project}/src/test");
     IPath newPath = ProjectVariable.replaceProjectVariableWithProjectName(path, project);
-    assertThat(newPath.toOSString(), equalTo("/test/src/test"));
+    assertThat(newPath.toPortableString(), equalTo("/test/src/test"));
   }
 
   @Test public void should_not_use_project_name_if_path_does_not_contain_variable() {
     when(project.getName()).thenReturn("test");
     IPath path = new Path("/main/src/test");
     IPath newPath = ProjectVariable.replaceProjectVariableWithProjectName(path, project);
-    assertThat(newPath.toOSString(), equalTo("/main/src/test"));
+    assertThat(newPath.toPortableString(), equalTo("/main/src/test"));
   }
 
   @Test public void should_not_use_project_name_if_path_already_contains_it() {
     when(project.getName()).thenReturn("test");
     IPath path = new Path("/test/src/test");
     IPath newPath = ProjectVariable.replaceProjectVariableWithProjectName(path, project);
-    assertThat(newPath.toOSString(), equalTo("/test/src/test"));
+    assertThat(newPath.toPortableString(), equalTo("/test/src/test"));
   }
 }
diff --git a/com.google.eclipse.protobuf.ui.test/src/com/google/eclipse/protobuf/ui/scoping/FileSystemPathResolver_resolvePath_Test.java b/com.google.eclipse.protobuf.ui.test/src/com/google/eclipse/protobuf/ui/scoping/FileSystemPathResolver_resolvePath_Test.java
new file mode 100644
index 0000000..974f4da
--- /dev/null
+++ b/com.google.eclipse.protobuf.ui.test/src/com/google/eclipse/protobuf/ui/scoping/FileSystemPathResolver_resolvePath_Test.java
@@ -0,0 +1,53 @@
+/*

+ * Copyright (c) 2012 Google Inc.

+ *

+ * 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

+ *

+ * http://www.eclipse.org/legal/epl-v10.html

+ */

+package com.google.eclipse.protobuf.ui.scoping;

+

+import static org.hamcrest.MatcherAssert.assertThat;

+import static org.junit.Assert.assertNull;

+

+import java.io.File;

+import java.util.UUID;

+

+import org.hamcrest.core.IsEqual;

+import org.junit.*;

+import org.junit.rules.TemporaryFolder;

+

+/**

+ * Tests for <code>{@link FileSystemPathResolver#resolvePath(String)}</code>.

+ *

+ * @author alruiz@google.com (Alex Ruiz)

+ */

+public class FileSystemPathResolver_resolvePath_Test {

+  @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder();

+  

+  private FileSystemPathResolver resolver;

+  

+  @Before public void setUp() {

+    resolver = new FileSystemPathResolver();

+  }

+  

+  @Test public void should_resolve_path_if_directory_exists() {

+    File root = temporaryFolder.getRoot();

+    String path = root.getAbsolutePath();

+    assertThat(resolver.resolvePath(path), IsEqual.equalTo(root.toURI().getPath()));

+  }

+  

+  @Test public void should_return_null_if_path_is_null() {

+    assertNull(resolver.resolvePath(null));

+  }

+

+  @Test public void should_return_null_if_path_is_empty() {

+    assertNull(resolver.resolvePath(""));

+  }

+

+  @Test public void should_return_null_if_path_does_not_exist() {

+    String path = UUID.randomUUID().toString();

+    assertNull(resolver.resolvePath(path));

+  }

+}

diff --git a/com.google.eclipse.protobuf.ui.test/src/com/google/eclipse/protobuf/ui/scoping/MultipleDirectoriesFileResolverStrategy_resolveUri_Test.java b/com.google.eclipse.protobuf.ui.test/src/com/google/eclipse/protobuf/ui/scoping/MultipleDirectoriesFileResolverStrategy_resolveUri_Test.java
index 7780e35..41c085a 100644
--- a/com.google.eclipse.protobuf.ui.test/src/com/google/eclipse/protobuf/ui/scoping/MultipleDirectoriesFileResolverStrategy_resolveUri_Test.java
+++ b/com.google.eclipse.protobuf.ui.test/src/com/google/eclipse/protobuf/ui/scoping/MultipleDirectoriesFileResolverStrategy_resolveUri_Test.java
@@ -16,8 +16,11 @@
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.*;
 
+import java.io.*;
+
 import org.eclipse.emf.common.util.URI;
 import org.junit.*;
+import org.junit.rules.TemporaryFolder;
 
 import com.google.eclipse.protobuf.junit.core.*;
 import com.google.eclipse.protobuf.ui.preferences.StringPreference;
@@ -38,6 +41,7 @@
   }
 
   @Rule public XtextRule xtext = overrideRuntimeModuleWith(unitTestModule(), new TestModule());
+  @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder();
 
   @Inject private ResourceLocations locations;
   @Inject private Uris uris;
@@ -62,40 +66,35 @@
     assertThat(resolved, equalTo(expected));
   }
 
-  @Test public void should_resolve_file_URI() {
-    String expected = "file:/usr/local/project/src/protos/imported.proto";
-    when(directoryPaths.getValue()).thenReturn("/usr/local/project/src/protos");
+  @Test public void should_resolve_file_URI() throws IOException {
+    File file = temporaryFolder.newFile("imported.proto");
+    String expected = file.toURI().toString();
+    when(directoryPaths.getValue()).thenReturn(temporaryFolder.getRoot().toString());
     when(uris.referredResourceExists(URI.createURI(expected))).thenReturn(true);
-    String resolved = resolver.resolveUri("imported.proto", declaringResourceUri, allPreferences);
+    String resolved = resolver.resolveUri(file.getName(), declaringResourceUri, allPreferences);
     assertThat(resolved, equalTo(expected));
   }
 
-  @Test public void should_fall_back_to_file_system_if_platform_resource_URI_cannot_be_resolved() {
-    String expected = "file:/usr/local/project/src/protos/imported.proto";
+  @Test public void should_fall_back_to_file_system_if_platform_resource_URI_cannot_be_resolved() throws IOException {
+    File file = temporaryFolder.newFile("imported.proto");
+    String expected = file.toURI().toString();
     when(directoryPaths.getValue()).thenReturn("${workspace_loc:/src/protos}");
     // try the first time as resource platform
     when(uris.referredResourceExists(URI.createURI("platform:/resource/src/protos/imported.proto"))).thenReturn(false);
     // try again, but in the file system this time
-    when(locations.directoryLocation("/src/protos")).thenReturn("/usr/local/project/src/protos");
+    when(locations.directoryLocation("/src/protos")).thenReturn(temporaryFolder.getRoot().toString());
     when(uris.referredResourceExists(URI.createURI(expected))).thenReturn(true);
-    String resolved = resolver.resolveUri("imported.proto", declaringResourceUri, allPreferences);
+    String resolved = resolver.resolveUri(file.getName(), declaringResourceUri, allPreferences);
     assertThat(resolved, equalTo(expected));
   }
 
-  @Test public void should_return_null_if_platform_resource_URI_cannot_be_resolved() {
+  @Test public void should_return_null_if_URI_cannot_be_resolved() {
     when(directoryPaths.getValue()).thenReturn("${workspace_loc:/src/protos}");
     when(uris.referredResourceExists(any(URI.class))).thenReturn(false);
     String resolved = resolver.resolveUri("imported.proto", declaringResourceUri, allPreferences);
     assertNull(resolved);
   }
 
-  @Test public void should_return_null_if_file_URI_cannot_be_resolved() {
-    when(directoryPaths.getValue()).thenReturn("/usr/local/project/src/protos");
-    when(uris.referredResourceExists(any(URI.class))).thenReturn(false);
-    String resolved = resolver.resolveUri("imported.proto", declaringResourceUri, allPreferences);
-    assertNull(resolved);
-  }
-
   private static class TestModule extends AbstractTestModule {
     @Override protected void configure() {
       mockAndBind(Uris.class);
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/paths/core/DirectoryPath.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/paths/core/DirectoryPath.java
index f0f9c4a..d66cdf5 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/paths/core/DirectoryPath.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/preferences/paths/core/DirectoryPath.java
@@ -44,7 +44,8 @@
       isWorkspacePath = true;
       if (project != null) {
         IPath newPath = replaceProjectVariableWithProjectName(new Path(actualPath), project);
-        actualPath = newPath.toOSString();
+        // Issue 204: we'll create an URI from this path. It must not have OS-specific path separators.
+        actualPath = newPath.toPortableString();
       }
     }
     return new DirectoryPath(actualPath, isWorkspacePath);
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/scoping/FileSystemPathResolver.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/scoping/FileSystemPathResolver.java
new file mode 100644
index 0000000..2483182
--- /dev/null
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/scoping/FileSystemPathResolver.java
@@ -0,0 +1,31 @@
+/*

+ * Copyright (c) 2012 Google Inc.

+ *

+ * 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

+ *

+ * http://www.eclipse.org/legal/epl-v10.html

+ */

+package com.google.eclipse.protobuf.ui.scoping;

+

+import static org.eclipse.xtext.util.Strings.isEmpty;

+

+import java.io.File;

+

+import com.google.inject.Singleton;

+

+/**

+ * @author alruiz@google.com (Alex Ruiz)

+ */

+@Singleton class FileSystemPathResolver {

+  String resolvePath(String path) {

+    if (isEmpty(path)) {

+      return null;

+    }

+    File directory = new File(path);

+    if (!directory.isDirectory()) {

+      return null;

+    }

+    return directory.toURI().getPath();

+  }

+}

diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/scoping/MultipleDirectoriesFileResolverStrategy.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/scoping/MultipleDirectoriesFileResolverStrategy.java
index 71d6c4c..acd5160 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/scoping/MultipleDirectoriesFileResolverStrategy.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/scoping/MultipleDirectoriesFileResolverStrategy.java
@@ -10,8 +10,8 @@
 
 import static com.google.common.collect.Lists.newArrayList;
 import static com.google.eclipse.protobuf.ui.util.CommaSeparatedValues.splitCsv;
-import static com.google.eclipse.protobuf.ui.util.Uris.*;
 import static org.eclipse.core.runtime.IPath.SEPARATOR;
+import static org.eclipse.xtext.util.Strings.isEmpty;
 
 import java.util.List;
 
@@ -25,6 +25,9 @@
  * @author alruiz@google.com (Alex Ruiz)
  */
 class MultipleDirectoriesFileResolverStrategy implements FileResolverStrategy {
+  private static final String PATH_SEPARATOR = new String(new char[] { SEPARATOR });
+  
+  @Inject private FileSystemPathResolver pathResolver;
   @Inject private ResourceLocations locations;
   @Inject private Uris uris;
 
@@ -68,18 +71,38 @@
   }
 
   private String resolveUriInFileSystem(String importUri, String importRootPath) {
-    URI uri = uri(importUri, importRootPath, false);
+    URI uri = fileUri(importUri, importRootPath);
     return resolveUri(uri);
   }
 
   private URI uri(String importUri, String importRootPath, boolean isWorkspacePath) {
-    String prefix = isWorkspacePath ? PLATFORM_RESOURCE_PREFIX : FILE_PREFIX;
-    StringBuilder pathBuilder = new StringBuilder().append(prefix)
-                                                   .append(importRootPath)
-                                                   .append(SEPARATOR)
-                                                   .append(importUri);
-    String path = pathBuilder.toString();
-    return URI.createURI(path);
+    if (isWorkspacePath) {
+      return platformResourceUri(importUri, importRootPath);
+    }
+    return fileUri(importUri, importRootPath);
+  }
+  
+  private URI platformResourceUri(String importUri, String importRootPath) {
+    String path = buildUriPath(importUri, importRootPath);
+    return URI.createPlatformResourceURI(path, true);
+  }
+  
+  private URI fileUri(String importUri, String importRootPath) {
+    String resolvedImportRootPath = pathResolver.resolvePath(importRootPath);
+    if (isEmpty(resolvedImportRootPath)) {
+      return null;
+    }
+    String path = buildUriPath(importUri, resolvedImportRootPath);
+    return URI.createFileURI(path);
+  }
+  
+  private String buildUriPath(String importUri, String importRootPath) {
+    StringBuilder pathBuilder = new StringBuilder().append(importRootPath);
+    if (!importRootPath.endsWith(PATH_SEPARATOR)) {
+      pathBuilder.append(PATH_SEPARATOR);
+    }
+    pathBuilder.append(importUri);
+    return pathBuilder.toString();
   }
 
   private String resolveUri(URI uri) {
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/Uris.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/Uris.java
index 743fbf6..df16451 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/Uris.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/Uris.java
@@ -27,15 +27,15 @@
  * @author alruiz@google.com (Alex Ruiz)
  */
 @Singleton public class Uris {
-  public static String PLATFORM_RESOURCE_PREFIX = "platform:/resource";
-  public static String FILE_PREFIX = "file:";
-
   /**
    * Indicates whether the resource or file referred by the given URI exists.
-   * @param uri the URI to check.
+   * @param uri the URI to check. It may be {@code null}.
    * @return {@code true} if the resource or file referred by the given URI exists, {@code false} otherwise.
    */
   public boolean referredResourceExists(URI uri) {
+    if (uri == null) {
+      return false;
+    }
     if (uri.isFile()) {
       File file = new File(uri.path());
       return file.exists();
@@ -67,19 +67,19 @@
   /**
    * Returns the "prefix" of the given URI as follows:
    * <ul>
-   * <li><code>{@link #PLATFORM_RESOURCE_PREFIX}</code>, if the URI refers to a platform resource</li>
-   * <li><code>{@link #FILE_PREFIX}</code>, if the URI refers to a file</li>
-   * <li>{@code null} otherwise</li>
+   * <li>"platform:/resource", if the URI refers to a platform resource</li>
+   * <li>"file:", if the URI refers to a file</li>
+   * <li>an empty {@code String} otherwise</li>
    * </ul>
    * @param uri the given URI.
    * @return the "prefix" of the given URI.
    */
   public String prefixOf(URI uri) {
     if (uri.isFile()) {
-      return FILE_PREFIX;
+      return "file:";
     }
     if (uri.isPlatformResource()) {
-      return PLATFORM_RESOURCE_PREFIX;
+      return "platform:/resource";
     }
     return "";
   }