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

Hyperlinking among types in external files works!
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/ProtobufUiModule.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/ProtobufUiModule.java
index c7b82d9..9f6c7ab 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/ProtobufUiModule.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/ProtobufUiModule.java
@@ -9,10 +9,13 @@
 package com.google.eclipse.protobuf.ui;
 
 import static com.google.inject.name.Names.named;
+import static org.eclipse.ui.PlatformUI.isWorkbenchRunning;
 
 import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
 import org.eclipse.ui.plugin.AbstractUIPlugin;
 import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
+import org.eclipse.xtext.ui.LanguageSpecific;
+import org.eclipse.xtext.ui.editor.IURIEditorOpener;
 import org.eclipse.xtext.ui.editor.IXtextEditorCallback;
 import org.eclipse.xtext.ui.editor.model.XtextDocumentProvider;
 import org.eclipse.xtext.ui.editor.outline.actions.IOutlineContribution;
@@ -20,6 +23,7 @@
 
 import com.google.eclipse.protobuf.scoping.IFileUriResolver;
 import com.google.eclipse.protobuf.ui.builder.AutoAddNatureEditorCallback;
+import com.google.eclipse.protobuf.ui.editor.ProtobufUriEditorOpener;
 import com.google.eclipse.protobuf.ui.editor.hyperlinking.ProtobufHyperlinkDetector;
 import com.google.eclipse.protobuf.ui.editor.model.ProtobufDocumentProvider;
 import com.google.eclipse.protobuf.ui.outline.LinkWithEditor;
@@ -81,4 +85,11 @@
   public void configureDocumentProvider(Binder binder) {
     binder.bind(XtextDocumentProvider.class).to(ProtobufDocumentProvider.class);
   }
+
+  @Override public void configureLanguageSpecificURIEditorOpener(Binder binder) {
+    if (!isWorkbenchRunning())return;
+    binder.bind(IURIEditorOpener.class)
+          .annotatedWith(LanguageSpecific.class)
+          .to(ProtobufUriEditorOpener.class);
+  }
 }
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/ProtobufUriEditorOpener.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/ProtobufUriEditorOpener.java
new file mode 100644
index 0000000..892de15
--- /dev/null
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/ProtobufUriEditorOpener.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2011 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.editor;
+
+import static com.google.eclipse.protobuf.ui.util.Resources.URI_SCHEME_FOR_FILES;
+import static org.eclipse.xtext.ui.editor.utils.EditorUtils.getXtextEditor;
+
+import org.apache.log4j.Logger;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EReference;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.xtext.ui.editor.LanguageSpecificURIEditorOpener;
+
+import com.google.eclipse.protobuf.ui.util.Resources;
+import com.google.inject.Inject;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class ProtobufUriEditorOpener extends LanguageSpecificURIEditorOpener {
+
+  private static Logger logger = Logger.getLogger(ProtobufUriEditorOpener.class);
+
+  @Inject private Resources resources;
+  
+  /** {@inheritDoc} */
+  @Override public IEditorPart open(URI uri, EReference crossReference, int indexInList, boolean select) {
+    if (URI_SCHEME_FOR_FILES.equals(uri.scheme())) {
+      try {
+        IEditorPart editor = resources.openProtoFileInFileSystem(uri.trimFragment());
+        selectAndReveal(editor, uri, crossReference, indexInList, select);
+        return getXtextEditor(editor);
+      } catch (PartInitException e) {
+        logger.error("Unable to open " + uri.toString(), e);
+      }
+    }
+    return super.open(uri, crossReference, indexInList, select);
+  }
+
+}
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/hyperlinking/ImportHyperlink.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/hyperlinking/ImportHyperlink.java
index 93c741d..26c901a 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/hyperlinking/ImportHyperlink.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/hyperlinking/ImportHyperlink.java
@@ -8,16 +8,13 @@
  */
 package com.google.eclipse.protobuf.ui.editor.hyperlinking;
 
-import org.eclipse.core.filesystem.EFS;
-import org.eclipse.core.filesystem.IFileStore;
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.runtime.Path;
+import static com.google.eclipse.protobuf.ui.util.Resources.URI_SCHEME_FOR_FILES;
+
+import org.apache.log4j.Logger;
 import org.eclipse.emf.common.util.URI;
 import org.eclipse.jface.text.IRegion;
 import org.eclipse.jface.text.hyperlink.IHyperlink;
-import org.eclipse.ui.*;
-import org.eclipse.ui.ide.FileStoreEditorInput;
-import org.eclipse.ui.part.FileEditorInput;
+import org.eclipse.ui.PartInitException;
 
 import com.google.eclipse.protobuf.ui.util.Resources;
 
@@ -28,6 +25,8 @@
  */
 class ImportHyperlink implements IHyperlink {
 
+  private static Logger logger = Logger.getLogger(ImportHyperlink.class);
+  
   private final URI importUri;
   private final IRegion region;
   private final Resources resources;
@@ -40,28 +39,11 @@
 
   public void open() {
     String scheme = importUri.scheme();
-    if ("platform".equals(scheme)) openFromWorkspace();
-    if ("file".equals(scheme)) openFromFileSystem();
-  }
-
-  private void openFromWorkspace() {
-    IFile file = resources.file(importUri);
-    IEditorInput editorInput = new FileEditorInput(file);
-    openFile(editorInput, "com.google.eclipse.protobuf.Protobuf");
-  }
-
-  private void openFromFileSystem() {
-    IFileStore fileStore = EFS.getLocalFileSystem().getStore(new Path(importUri.toFileString()));
-    IEditorInput editorInput = new FileStoreEditorInput(fileStore);
-    openFile(editorInput, "com.google.eclipse.protobuf.Protobuf"/*"org.eclipse.ui.DefaultTextEditor"*/);
-  }
-
-  private void openFile(IEditorInput editorInput, String editorId) {
-    IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
     try {
-      page.openEditor(editorInput, editorId);
+      if ("platform".equals(scheme)) resources.openProtoFileInPlatform(importUri);
+      if (URI_SCHEME_FOR_FILES.equals(scheme)) resources.openProtoFileInFileSystem(importUri);
     } catch (PartInitException e) {
-      e.printStackTrace();
+      logger.error("Unable to open " + importUri.toString(), e);
     }
   }
 
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 2094ffa..ab59060 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,6 +6,7 @@
 package com.google.eclipse.protobuf.ui.editor.model;
 
 import static com.google.eclipse.protobuf.ui.ProtobufUiModule.PLUGIN_ID;
+import static com.google.eclipse.protobuf.ui.util.Resources.URI_SCHEME_FOR_FILES;
 import static java.util.Collections.singletonMap;
 import static org.eclipse.core.runtime.IStatus.ERROR;
 import static org.eclipse.emf.common.util.URI.createURI;
@@ -46,7 +47,6 @@
 public class ProtobufDocumentProvider extends XtextDocumentProvider {
 
   private static final String ENCODING = "UTF-8";
-  private static final String FILE_SCHEME = "file";
 
   @Inject private Closeables closeables;
   @Inject private IssueResolutionProvider issueResolutionProvider;
@@ -111,8 +111,8 @@
   private File fileFrom(IURIEditorInput input) {
     URI uri = input.getURI();
     String scheme = uri.getScheme();
-    if (scheme != FILE_SCHEME) {
-      String cleanUri = uri.toString().replaceFirst(scheme, FILE_SCHEME);
+    if (scheme != URI_SCHEME_FOR_FILES) {
+      String cleanUri = uri.toString().replaceFirst(scheme, URI_SCHEME_FOR_FILES);
       uri = URI.create(cleanUri);
     }
     return new File(uri);
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 77ddd63..777cbab 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
@@ -8,12 +8,16 @@
  */
 package com.google.eclipse.protobuf.ui.util;
 
+import org.eclipse.core.filesystem.EFS;
+import org.eclipse.core.filesystem.IFileStore;
 import org.eclipse.core.resources.*;
 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.ide.FileStoreEditorInput;
+import org.eclipse.ui.part.FileEditorInput;
 import org.eclipse.ui.views.navigator.ResourceNavigator;
 
 import com.google.inject.Singleton;
@@ -26,6 +30,8 @@
 @Singleton
 public class Resources {
 
+  public static final String URI_SCHEME_FOR_FILES = "file";
+  
   /**
    * Returns the project that contains the resource at the given URI.
    * @param resourceUri the given URI.
@@ -61,6 +67,36 @@
   }
 
   /**
+   * Opens the .proto file identified by the given URI. The .proto file exists in the workspace.
+   * @param uri the URI of the file to open.
+   * @return an open and active editor, or {@code null} if an external editor was opened.
+   * @throws PartInitException if the editor cannot be opened or initialized.
+   */
+  public IEditorPart openProtoFileInPlatform(URI uri) throws PartInitException {
+    IFile file = file(uri);
+    IEditorInput editorInput = new FileEditorInput(file);
+    return openFile(editorInput);
+  }
+
+  /**
+   * Opens the .proto file identified by the given URI. The .proto file does not exist in the workspace, therefore is
+   * opened from the file system.
+   * @param uri the URI of the file to open.
+   * @return an open and active editor, or {@code null} if an external editor was opened.
+   * @throws PartInitException if the editor cannot be created or initialized.
+   */
+  public IEditorPart openProtoFileInFileSystem(URI uri) throws PartInitException {
+    IFileStore fileStore = EFS.getLocalFileSystem().getStore(new Path(uri.toFileString()));
+    IEditorInput editorInput = new FileStoreEditorInput(fileStore);
+    return openFile(editorInput/*"org.eclipse.ui.DefaultTextEditor"*/);
+  }
+  
+  private IEditorPart openFile(IEditorInput editorInput) throws PartInitException {
+    IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+    return page.openEditor(editorInput, "com.google.eclipse.protobuf.Protobuf");
+  }
+
+  /**
    * Returns a handle to a workspace file identified by the given URI.
    * @param uri 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