In progress: [ Issue 45 ] Protobuf-dt should be able to open files outside workspace
https://code.google.com/p/protobuf-dt/issues/detail?id=45

Import resolution in external files works now. Hyperlinking of types is not.
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/model/ProtobufDocumentProvider.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/model/ProtobufDocumentProvider.java
index 5f69dbb..2094ffa 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/model/ProtobufDocumentProvider.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/model/ProtobufDocumentProvider.java
@@ -6,9 +6,12 @@
 package com.google.eclipse.protobuf.ui.editor.model;
 
 import static com.google.eclipse.protobuf.ui.ProtobufUiModule.PLUGIN_ID;
+import static java.util.Collections.singletonMap;
 import static org.eclipse.core.runtime.IStatus.ERROR;
 import static org.eclipse.emf.common.util.URI.createURI;
+import static org.eclipse.emf.ecore.resource.ContentHandler.UNSPECIFIED_CONTENT_TYPE;
 import static org.eclipse.emf.ecore.util.EcoreUtil.resolveAll;
+import static org.eclipse.xtext.resource.XtextResource.OPTION_ENCODING;
 import static org.eclipse.xtext.util.CancelIndicator.NullImpl;
 import static org.eclipse.xtext.validation.CheckMode.FAST_ONLY;
 
@@ -17,21 +20,24 @@
 
 import org.eclipse.core.filesystem.*;
 import org.eclipse.core.runtime.*;
+import org.eclipse.emf.ecore.resource.ResourceSet;
 import org.eclipse.jface.text.IDocument;
 import org.eclipse.jface.text.source.IAnnotationModel;
 import org.eclipse.ui.IURIEditorInput;
 import org.eclipse.ui.ide.FileStoreEditorInput;
 import org.eclipse.xtext.linking.lazy.LazyLinkingResource;
-import org.eclipse.xtext.resource.*;
+import org.eclipse.xtext.resource.XtextResource;
 import org.eclipse.xtext.ui.editor.model.XtextDocument;
 import org.eclipse.xtext.ui.editor.model.XtextDocumentProvider;
 import org.eclipse.xtext.ui.editor.quickfix.IssueResolutionProvider;
 import org.eclipse.xtext.ui.editor.validation.AnnotationIssueProcessor;
 import org.eclipse.xtext.ui.editor.validation.ValidationJob;
+import org.eclipse.xtext.ui.resource.IResourceSetProvider;
 import org.eclipse.xtext.util.StringInputStream;
 import org.eclipse.xtext.validation.IResourceValidator;
 
 import com.google.eclipse.protobuf.ui.util.Closeables;
+import com.google.eclipse.protobuf.ui.util.Resources;
 import com.google.inject.Inject;
 
 /**
@@ -41,13 +47,13 @@
 
   private static final String ENCODING = "UTF-8";
   private static final String FILE_SCHEME = "file";
-  
+
   @Inject private Closeables closeables;
   @Inject private IssueResolutionProvider issueResolutionProvider;
-  @Inject private IResourceFactory resourceFactory;
+  @Inject private IResourceSetProvider resourceSetProvider;
   @Inject private IResourceValidator resourceValidator;
-  @Inject private XtextResourceSet resourceSet;
-  
+  @Inject private Resources resources;
+
   @Override protected ElementInfo createElementInfo(Object element) throws CoreException {
     if (element instanceof FileStoreEditorInput) return createElementInfo((FileStoreEditorInput) element);
     return super.createElementInfo(element);
@@ -101,7 +107,7 @@
       throw new CoreException(new Status(ERROR, PLUGIN_ID, message, t));
     }
   }
-  
+
   private File fileFrom(IURIEditorInput input) {
     URI uri = input.getURI();
     String scheme = uri.getScheme();
@@ -130,16 +136,16 @@
       if (!closeables.close(reader)) closeables.close(inputStream);
     }
   }
-  
+
   private Reader readerFor(InputStream inputStream) throws IOException {
     return new InputStreamReader(inputStream, ENCODING);
   }
-  
+
   private XtextResource createResource(String uri, InputStream input) {
-    XtextResource resource = (XtextResource) resourceFactory.createResource(createURI(uri));
-    resourceSet.getResources().add(resource);
+    ResourceSet resourceSet = resourceSetProvider.get(resources.activeProject());
+    XtextResource resource = (XtextResource) resourceSet.createResource(createURI(uri), UNSPECIFIED_CONTENT_TYPE);
     try {
-      resource.load(input, null);
+      resource.load(input, singletonMap(OPTION_ENCODING, ENCODING));
     } catch (IOException e) {
       throw new RuntimeException(e);
     }
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/scoping/FileUriResolver.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/scoping/FileUriResolver.java
index 83ac24a..2e00a09 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/scoping/FileUriResolver.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/scoping/FileUriResolver.java
@@ -8,14 +8,15 @@
  */
 package com.google.eclipse.protobuf.ui.scoping;
 
-import static com.google.eclipse.protobuf.ui.scoping.FileResolverStrategy.PREFIX;
+import static org.eclipse.xtext.util.Strings.isEmpty;
 
 import org.eclipse.core.resources.IProject;
 import org.eclipse.emf.common.util.URI;
 import org.eclipse.emf.ecore.resource.Resource;
 
 import com.google.eclipse.protobuf.scoping.IFileUriResolver;
-import com.google.eclipse.protobuf.ui.preferences.paths.*;
+import com.google.eclipse.protobuf.ui.preferences.paths.PathsPreferences;
+import com.google.eclipse.protobuf.ui.preferences.paths.PathsPreferencesProvider;
 import com.google.eclipse.protobuf.ui.util.Resources;
 import com.google.inject.Inject;
 
@@ -46,15 +47,19 @@
    * We need to have the import URI as "platform:/resource/protobuf-test/folder/proto2.proto" for the editor to see it.
    */
   public String resolveUri(String importUri, Resource declaringResource) {
-    if (importUri.startsWith(PREFIX)) return importUri;
+    if (hasScheme(importUri)) return importUri;
     String resolved = resolveUri(importUri, declaringResource.getURI());
-//    System.out.println(declaringResource.getURI() + " : " + importUri + " : " + resolved);
-    if (resolved == null) return importUri;
-    return resolved;
+    return (resolved == null) ? importUri : resolved;
+  }
+
+  private boolean hasScheme(String importUri) {
+    String scheme = URI.createURI(importUri).scheme();
+    return !isEmpty(scheme);
   }
 
   private String resolveUri(String importUri, URI resourceUri) {
     IProject project = resources.project(resourceUri);
+    if (project == null) project = resources.activeProject();
     PathsPreferences preferences = preferenceReader.getPreferences(project);
     return resolver(preferences).resolveUri(importUri, resourceUri, preferences);
   }
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/Resources.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/Resources.java
index 62a381b..77ddd63 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/Resources.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/util/Resources.java
@@ -12,6 +12,9 @@
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.Path;
 import org.eclipse.emf.common.util.URI;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.ui.*;
+import org.eclipse.ui.views.navigator.ResourceNavigator;
 
 import com.google.inject.Singleton;
 
@@ -26,10 +29,25 @@
   /**
    * Returns the project that contains the resource at the given URI.
    * @param resourceUri the given URI.
-   * @return the project that contains the resource at the given URI.
+   * @return the project that contains the resource at the given URI, or {@code null} if the resource at the given URI
+   * is not in a workspace.
    */
   public IProject project(URI resourceUri) {
-    return file(resourceUri).getProject();
+    IFile file = file(resourceUri);
+    return (file != null) ? file.getProject() : null;
+  }
+
+  public IProject activeProject() {
+    IViewReference[] references = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getViewReferences();
+    for (IViewReference reference : references) {
+      IViewPart part = reference.getView(false);
+      if (!(part instanceof ResourceNavigator)) continue;
+      ResourceNavigator navigator = (ResourceNavigator) part;
+      StructuredSelection sel = (StructuredSelection) navigator.getTreeViewer().getSelection();
+      IResource resource = (IResource) sel.getFirstElement();
+      return resource.getProject();
+    }
+    return null;
   }
 
   /**
@@ -38,21 +56,24 @@
    * @return {@code true} if the given URI belongs to an existing file, {@code false} otherwise.
    */
   public boolean fileExists(URI fileUri) {
-    return file(fileUri).exists();
+    IFile file = file(fileUri);
+    return (file != null) ? file.exists() : false;
   }
 
   /**
-   * Returns a handle to file identified by the given URI.
+   * Returns a handle to a workspace file identified by the given URI.
    * @param uri the given URI.
-   * @return a handle to file identified by the given URI.
+   * @return a handle to a workspace file identified by the given URI or {@code null} if the URI does not belong to a 
+   * workspace file.
    */
   public IFile file(URI uri) {
     IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
-    return root.getFile(pathOf(uri));
+    IPath path = pathOf(uri);
+    return (path != null) ? root.getFile(path) : null;
   }
 
   private IPath pathOf(URI uri) {
-    String s = uri.toPlatformString(true);
-    return new Path(s);
+    String platformUri = uri.toPlatformString(true);
+    return (platformUri != null) ? new Path(platformUri) : null;
   }
 }