Allow multiline strings to have comments inside

Change-Id: Ibde7c5e7209e2b33b295dbf28356701beb2b2e5d
Signed-off-by: Harry Terkelsen <het@google.com>
diff --git a/com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/model/util/Imports_uriAsEnteredByUser_Test.java b/com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/model/util/Imports_getPath_Test.java
similarity index 81%
rename from com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/model/util/Imports_uriAsEnteredByUser_Test.java
rename to com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/model/util/Imports_getPath_Test.java
index 660ec8e..0391a7e 100644
--- a/com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/model/util/Imports_uriAsEnteredByUser_Test.java
+++ b/com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/model/util/Imports_getPath_Test.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012 Google Inc.
+ * Copyright (c) 2014 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
@@ -8,29 +8,28 @@
  */
 package com.google.eclipse.protobuf.model.util;
 
+import static com.google.eclipse.protobuf.junit.core.IntegrationTestModule.integrationTestModule;
+import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
 import static org.eclipse.xtext.EcoreUtil2.getAllContentsOfType;
 import static org.hamcrest.core.IsEqual.equalTo;
 import static org.junit.Assert.assertThat;
 
-import static com.google.eclipse.protobuf.junit.core.IntegrationTestModule.integrationTestModule;
-import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
-
-import java.util.List;
-
-import org.junit.Rule;
-import org.junit.Test;
-
 import com.google.eclipse.protobuf.junit.core.XtextRule;
 import com.google.eclipse.protobuf.protobuf.Import;
 import com.google.eclipse.protobuf.protobuf.Protobuf;
 import com.google.inject.Inject;
 
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.List;
+
 /**
- * Tests for <code>{@link Imports#uriAsEnteredByUser(Import)}</code>
+ * Tests for <code>{@link Imports#getPath(Import)}</code>
  *
  * @author alruiz@google.com (Alex Ruiz)
  */
-public class Imports_uriAsEnteredByUser_Test {
+public class Imports_getPath_Test {
   @Rule public XtextRule xtext = overrideRuntimeModuleWith(integrationTestModule());
 
   @Inject private Imports imports;
@@ -40,8 +39,7 @@
   @Test public void should_return_import_URI_as_entered_by_user() {
     Protobuf root = xtext.root();
     Import anImport = firstImportOf(root);
-    anImport.setImportURI("file:/test-protos/types.proto"); // simulate the URI is resolved
-    assertThat(imports.uriAsEnteredByUser(anImport), equalTo("types.proto"));
+    assertThat(imports.getPath(anImport), equalTo("types.proto"));
   }
 
   private Import firstImportOf(Protobuf root) {
diff --git a/com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/validation/ImportValidator_checkUnknownSyntaxImports_withNonProto2Imports_Tests.java b/com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/validation/ImportValidator_checkUnknownSyntaxImports_withNonProto2Imports_Tests.java
index 67e844f..c8b91bc 100644
--- a/com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/validation/ImportValidator_checkUnknownSyntaxImports_withNonProto2Imports_Tests.java
+++ b/com.google.eclipse.protobuf.integration.test/src/com/google/eclipse/protobuf/validation/ImportValidator_checkUnknownSyntaxImports_withNonProto2Imports_Tests.java
@@ -8,16 +8,22 @@
  */
 package com.google.eclipse.protobuf.validation;
 
+import static com.google.eclipse.protobuf.junit.core.IntegrationTestModule.integrationTestModule;
+import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.IMPORT__PATH;
+import static com.google.eclipse.protobuf.validation.Messages.importingUnsupportedSyntax;
 import static org.eclipse.xtext.validation.ValidationMessageAcceptor.INSIGNIFICANT_INDEX;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
-import static com.google.eclipse.protobuf.junit.core.IntegrationTestModule.integrationTestModule;
-import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
-import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.IMPORT__IMPORT_URI;
-import static com.google.eclipse.protobuf.validation.Messages.importingUnsupportedSyntax;
-
-import java.util.List;
+import com.google.eclipse.protobuf.junit.core.AbstractTestModule;
+import com.google.eclipse.protobuf.junit.core.XtextRule;
+import com.google.eclipse.protobuf.model.util.Imports;
+import com.google.eclipse.protobuf.model.util.Protobufs;
+import com.google.eclipse.protobuf.protobuf.Import;
+import com.google.eclipse.protobuf.protobuf.Protobuf;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
 
 import org.eclipse.emf.common.util.URI;
 import org.eclipse.xtext.validation.ValidationMessageAcceptor;
@@ -25,13 +31,7 @@
 import org.junit.Rule;
 import org.junit.Test;
 
-import com.google.eclipse.protobuf.junit.core.AbstractTestModule;
-import com.google.eclipse.protobuf.junit.core.XtextRule;
-import com.google.eclipse.protobuf.model.util.Protobufs;
-import com.google.eclipse.protobuf.protobuf.Import;
-import com.google.eclipse.protobuf.protobuf.Protobuf;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
+import java.util.List;
 
 /**
  * Tests for <code>{@link ImportValidator#checkUnknownSyntaxImports(Protobuf)}</code>
@@ -43,6 +43,7 @@
 
   @Inject private ProtobufsStub protobufs;
   @Inject private ImportValidator validator;
+  @Inject private Imports imports;
 
   private ValidationMessageAcceptor messageAcceptor;
 
@@ -91,9 +92,9 @@
   }
 
   private Import findImportReferreringToFile(String fileName) {
-    List<Import> imports = protobufs.importsIn(xtext.root());
-    for (Import anImport : imports) {
-      if (anImport.getImportURI().endsWith(fileName)) {
+    List<Import> importsInProto = protobufs.importsIn(xtext.root());
+    for (Import anImport : importsInProto) {
+      if (imports.getPath(anImport).endsWith(fileName)) {
         return anImport;
       }
     }
@@ -101,7 +102,11 @@
   }
 
   private void verifyThatImportingUnknownSyntaxFileCreatedWarning(Import anImport) {
-    verify(messageAcceptor).acceptWarning(importingUnsupportedSyntax, anImport, IMPORT__IMPORT_URI, INSIGNIFICANT_INDEX, null,
+    verify(messageAcceptor).acceptWarning(importingUnsupportedSyntax,
+        anImport,
+        IMPORT__PATH,
+        INSIGNIFICANT_INDEX,
+        null,
         new String[0]);
   }
 
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/MultilineStringsSyntaxTest.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/MultilineStringsSyntaxTest.java
new file mode 100644
index 0000000..74dd5d9
--- /dev/null
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/bugs/MultilineStringsSyntaxTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2014 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.bugs;
+
+import static com.google.eclipse.protobuf.junit.core.UnitTestModule.unitTestModule;
+import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.google.eclipse.protobuf.junit.core.XtextRule;
+import com.google.eclipse.protobuf.model.util.Syntaxes;
+import com.google.eclipse.protobuf.protobuf.Syntax;
+import com.google.eclipse.protobuf.validation.ProtobufJavaValidator;
+import com.google.inject.Inject;
+
+import org.eclipse.emf.common.util.BasicDiagnostic;
+import org.junit.Rule;
+import org.junit.Test;
+
+/**
+ * Test that the parser correctly handles multiline strings.
+ */
+public class MultilineStringsSyntaxTest {
+  @Rule public XtextRule xtext = overrideRuntimeModuleWith(unitTestModule());
+
+  @Inject private ProtobufJavaValidator validator;
+  @Inject private Syntaxes syntaxes;
+
+  // syntax = "p" // comment inside string
+  //          "roto" /* another comment */
+  //          "2";
+  @Test public void shouldAllowCommentsInsideStrings() {
+    boolean isValid = validator.validate(xtext.root(), new BasicDiagnostic(), null);
+    assertTrue(isValid);
+  }
+
+  // syntax = "p" // comment inside string
+  //          "roto" /* another comment */
+  //          "2";
+  @Test public void shouldUnderstandMultilineStrings() {
+    Syntax syntax = xtext.root().getSyntax();
+    assertEquals("proto2", syntaxes.getName(syntax));
+  }
+}
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/conversion/STRINGValueConverter_toValue_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/conversion/STRINGValueConverter_toValue_Test.java
deleted file mode 100644
index d43cb92..0000000
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/conversion/STRINGValueConverter_toValue_Test.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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.conversion;
-
-import static java.util.Arrays.asList;
-
-import static org.hamcrest.core.IsEqual.equalTo;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.mock;
-
-import static com.google.eclipse.protobuf.junit.core.UnitTestModule.unitTestModule;
-import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
-
-import java.util.Collection;
-
-import org.eclipse.xtext.nodemodel.INode;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-import com.google.eclipse.protobuf.junit.core.XtextRule;
-
-/**
- * Tests for <code>{@link STRINGValueConverter#toValue(String, INode)}</code>.
- *
- * @author alruiz@google.com (Alex Ruiz)
- */
-@RunWith(Parameterized.class)
-public class STRINGValueConverter_toValue_Test {
-  @Rule public XtextRule xtext = overrideRuntimeModuleWith(unitTestModule());
-
-  private final String input;
-  private final String expected;
-
-  @Parameters
-  public static Collection<Object[]> parameters() {
-    return asList(new Object[][] {
-      { null, null },
-      { "\"Hello World!\"", "Hello World!" },
-      { "\"Hello\"\n\" World!\"", "Hello World!" },
-      { "\"Hello\"\r\" World!\"", "Hello World!" },
-      { "\"Hello\"\n\n\" World!\"", "Hello World!" },
-      { "\"Hello\"\n\r\" World!\"", "Hello World!" }
-    });
-  }
-
-  public STRINGValueConverter_toValue_Test(String input, String expected) {
-    this.input = input;
-    this.expected = expected;
-  }
-
-  private STRINGValueConverter converter;
-  private INode node;
-
-  @Before public void setUp() {
-    node = mock(INode.class);
-    converter = xtext.injector().getInstance(STRINGValueConverter.class);
-  }
-
-  @Test public void should_parse_multi_line_string() {
-    String value = converter.toValue(input, node);
-    assertThat(value, equalTo(expected));
-  }
-}
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/formatting/ProtobufFormatter_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/formatting/ProtobufFormatter_Test.java
index 75e9a4b..f154b68 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/formatting/ProtobufFormatter_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/formatting/ProtobufFormatter_Test.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012 Google Inc.
+ * Copyright (c) 2014 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
@@ -8,10 +8,12 @@
  */
 package com.google.eclipse.protobuf.formatting;
 
-import static org.junit.Assert.assertEquals;
-
 import static com.google.eclipse.protobuf.formatting.CommentReaderRule.overrideRuntimeModuleWith;
 import static com.google.eclipse.protobuf.junit.core.UnitTestModule.unitTestModule;
+import static org.junit.Assert.assertEquals;
+
+import com.google.eclipse.protobuf.junit.core.AbstractTestModule;
+import com.google.inject.Inject;
 
 import org.eclipse.xtext.formatting.IIndentationInformation;
 import org.eclipse.xtext.formatting.INodeModelFormatter;
@@ -21,9 +23,6 @@
 import org.junit.Rule;
 import org.junit.Test;
 
-import com.google.eclipse.protobuf.junit.core.AbstractTestModule;
-import com.google.inject.Inject;
-
 /**
  * @Tests for <code>{@link ProtobufFormatter}</code>.
  *
@@ -156,6 +155,16 @@
     assertThatFormattingWorksCorrectly();
   }
 
+  // syntax = "proto" // version on next line
+  //          '3';
+
+  // syntax = "proto" // version on next line
+  //   '3';
+  @Test public void should_format_multiline_strings() {
+    // TODO(het): Switch to clang-format so this works correctly
+    assertThatFormattingWorksCorrectly();
+  }
+
   // syntax = 'proto2';package com.google.protobuf.test;import 'google/protobuf/descriptor.proto';import
   // public 'address.proto';import weak 'salary.proto';option java_package = "com.foo.bar";option
   // optimize_for = CODE_SIZE;
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/grammar/Syntaxes_proto2_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/grammar/Syntaxes_proto2_Test.java
index 284c20f..4547665 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/grammar/Syntaxes_proto2_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/grammar/Syntaxes_proto2_Test.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Google Inc.
+ * Copyright (c) 2014 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
@@ -11,15 +11,17 @@
 import static org.hamcrest.core.IsEqual.equalTo;
 import static org.junit.Assert.assertThat;
 
+import com.google.eclipse.protobuf.model.util.Syntaxes;
+
 import org.junit.Test;
 
 /**
- * Tests for <code>{@link Syntaxes#proto2()}</code>.
+ * Tests for <code>{@link Syntaxes#PROTO2}</code>.
  *
  * @author alruiz@google.com (Alex Ruiz)
  */
 public class Syntaxes_proto2_Test {
   @Test public void should_return_proto2() {
-    assertThat(Syntaxes.proto2(), equalTo("proto2"));
+    assertThat(Syntaxes.PROTO2, equalTo("proto2"));
   }
 }
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/grammar/Syntaxes_proto3_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/grammar/Syntaxes_proto3_Test.java
index 66c9a06..1b7fdfb 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/grammar/Syntaxes_proto3_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/grammar/Syntaxes_proto3_Test.java
@@ -11,13 +11,15 @@
 import static org.hamcrest.core.IsEqual.equalTo;
 import static org.junit.Assert.assertThat;
 
+import com.google.eclipse.protobuf.model.util.Syntaxes;
+
 import org.junit.Test;
 
 /**
- * Tests for <code>{@link Syntaxes#proto3()}</code>.
+ * Tests for <code>{@link Syntaxes#PROTO3}</code>.
  */
 public class Syntaxes_proto3_Test {
   @Test public void should_return_proto3() {
-    assertThat(Syntaxes.proto3(), equalTo("proto3"));
+    assertThat(Syntaxes.PROTO3, equalTo("proto3"));
   }
 }
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/IntegrationTestModule.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/IntegrationTestModule.java
index 695fdad..f66915f 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/IntegrationTestModule.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/junit/core/IntegrationTestModule.java
@@ -1,5 +1,5 @@
 /*
-* Copyright (c) 2011 Google Inc.
+* Copyright (c) 2014 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
@@ -8,17 +8,18 @@
  */
 package com.google.eclipse.protobuf.junit.core;
 
+import static com.google.eclipse.protobuf.junit.core.GeneratedProtoFiles.protoFile;
 import static org.eclipse.xtext.util.Strings.isEmpty;
 
-import static com.google.eclipse.protobuf.junit.core.GeneratedProtoFiles.protoFile;
-
-import java.io.File;
+import com.google.eclipse.protobuf.model.util.Imports;
+import com.google.eclipse.protobuf.protobuf.Import;
+import com.google.eclipse.protobuf.scoping.IImportResolver;
+import com.google.inject.Inject;
 
 import org.eclipse.emf.common.util.URI;
 import org.eclipse.emf.ecore.EReference;
 
-import com.google.eclipse.protobuf.protobuf.Import;
-import com.google.eclipse.protobuf.scoping.IImportResolver;
+import java.io.File;
 
 /**
  * Guice module for unit testing.
@@ -38,19 +39,24 @@
   }
 
   private static class ImportResolver implements IImportResolver {
-    @Override public void resolveAndUpdateUri(Import anImport) {
-      String importUri = anImport.getImportURI();
+    @Inject
+    private Imports imports;
+
+    @Override
+    public String resolve(Import anImport) {
+      String importUri = imports.getPath(anImport);
       URI uri = URI.createURI(importUri);
-      if (!isEmpty(uri.scheme()))
-      {
-        return; // already resolved.
+      if (!isEmpty(uri.scheme())) {
+        return importUri; // already resolved.
       }
       File file = protoFile(importUri);
       if (!file.exists()) {
-        return; // file does not exist.
+        return null; // file does not exist.
       }
-      String resolvedUri = file.toURI().toString();
-      anImport.setImportURI(resolvedUri);
+      return file.toURI().toString();
     }
+
+    @Override
+    public void invalidateCacheFor(Import anImport) {}
   }
 }
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/model/util/Imports_resolvedUriOf_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/model/util/Imports_resolvedUriOf_Test.java
index 235946b..677f6f0 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/model/util/Imports_resolvedUriOf_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/model/util/Imports_resolvedUriOf_Test.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012 Google Inc.
+ * Copyright (c) 2014 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
@@ -8,26 +8,25 @@
  */
 package com.google.eclipse.protobuf.model.util;
 
+import static com.google.eclipse.protobuf.junit.core.UnitTestModule.unitTestModule;
+import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
 import static org.hamcrest.core.IsEqual.equalTo;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
-import static com.google.eclipse.protobuf.junit.core.UnitTestModule.unitTestModule;
-import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
-
-import org.eclipse.emf.common.util.URI;
-import org.eclipse.xtext.scoping.impl.ImportUriResolver;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
 import com.google.eclipse.protobuf.junit.core.AbstractTestModule;
 import com.google.eclipse.protobuf.junit.core.XtextRule;
 import com.google.eclipse.protobuf.protobuf.Import;
+import com.google.eclipse.protobuf.scoping.IImportResolver;
 import com.google.inject.Inject;
 
+import org.eclipse.emf.common.util.URI;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
 /**
  * Tests for <code>{@link Imports#resolvedUriOf(Import)}</code>
  *
@@ -36,7 +35,7 @@
 public class Imports_resolvedUriOf_Test {
   @Rule public XtextRule xtext = overrideRuntimeModuleWith(unitTestModule(), new TestModule());
 
-  @Inject private ImportUriResolver uriResolver;
+  @Inject private IImportResolver importResolver;
   @Inject private Imports imports;
 
   private Import anImport;
@@ -46,20 +45,20 @@
   }
 
   @Test public void should_return_resolved_URI() {
-    when(uriResolver.apply(anImport)).thenReturn("file:/protos/test.proto");
+    when(importResolver.resolve(anImport)).thenReturn("file:/protos/test.proto");
     URI uri = imports.resolvedUriOf(anImport);
     assertThat(uri.path(), equalTo("/protos/test.proto"));
   }
 
   @Test public void should_return_null_if_URI_cannot_be_resolved() {
-    when(uriResolver.apply(anImport)).thenReturn(null);
+    when(importResolver.resolve(anImport)).thenReturn(null);
     URI uri = imports.resolvedUriOf(anImport);
     assertNull(uri);
   }
 
   private static class TestModule extends AbstractTestModule {
     @Override protected void configure() {
-      mockAndBind(ImportUriResolver.class);
+      mockAndBind(IImportResolver.class);
     }
   }
 }
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/model/util/Protobufs_importsIn_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/model/util/Protobufs_importsIn_Test.java
index 926a801..75397b6 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/model/util/Protobufs_importsIn_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/model/util/Protobufs_importsIn_Test.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Google Inc.
+ * Copyright (c) 2014 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
@@ -8,22 +8,21 @@
  */
 package com.google.eclipse.protobuf.model.util;
 
-import static org.hamcrest.core.IsEqual.equalTo;
-import static org.junit.Assert.assertThat;
-
 import static com.google.eclipse.protobuf.junit.core.UnitTestModule.unitTestModule;
 import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
-
-import java.util.List;
-
-import org.junit.Rule;
-import org.junit.Test;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.assertThat;
 
 import com.google.eclipse.protobuf.junit.core.XtextRule;
 import com.google.eclipse.protobuf.protobuf.Import;
 import com.google.eclipse.protobuf.protobuf.Protobuf;
 import com.google.inject.Inject;
 
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.List;
+
 /**
  * Tests for <code>{@link Protobufs#importsIn(Protobuf)}</code>.
  *
@@ -33,6 +32,7 @@
   @Rule public XtextRule xtext = overrideRuntimeModuleWith(unitTestModule());
 
   @Inject private Protobufs protobufs;
+  @Inject private Imports imports;
 
   // syntax = "proto2";
   //
@@ -41,8 +41,8 @@
   @Test public void should_return_all_imports() {
     List<Import> allImports = protobufs.importsIn(xtext.root());
     assertThat(allImports.size(), equalTo(2));
-    assertThat(allImports.get(0).getImportURI(), equalTo("luke.proto"));
-    assertThat(allImports.get(1).getImportURI(), equalTo("leia.proto"));
+    assertThat(imports.getPath(allImports.get(0)), equalTo("luke.proto"));
+    assertThat(imports.getPath(allImports.get(1)), equalTo("leia.proto"));
   }
 
   // syntax = "proto2";
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/validation/ProtobufJavaValidator_checkSyntaxIsKnown_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/validation/ProtobufJavaValidator_checkSyntaxIsKnown_Test.java
index 4ec6ede..595e086 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/validation/ProtobufJavaValidator_checkSyntaxIsKnown_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/validation/ProtobufJavaValidator_checkSyntaxIsKnown_Test.java
@@ -8,6 +8,8 @@
  */
 package com.google.eclipse.protobuf.validation;
 
+import static com.google.eclipse.protobuf.junit.core.UnitTestModule.unitTestModule;
+import static com.google.eclipse.protobuf.junit.core.XtextRule.overrideRuntimeModuleWith;
 import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.SYNTAX__NAME;
 import static com.google.eclipse.protobuf.validation.ProtobufJavaValidator.SYNTAX_IS_NOT_KNOWN_ERROR;
 import static org.eclipse.xtext.validation.ValidationMessageAcceptor.INSIGNIFICANT_INDEX;
@@ -16,10 +18,15 @@
 import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
+import com.google.eclipse.protobuf.junit.core.XtextRule;
+import com.google.eclipse.protobuf.protobuf.ProtobufFactory;
+import com.google.eclipse.protobuf.protobuf.StringLiteral;
 import com.google.eclipse.protobuf.protobuf.Syntax;
+import com.google.inject.Inject;
 
 import org.eclipse.xtext.validation.ValidationMessageAcceptor;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 
 /**
@@ -28,19 +35,20 @@
  * @author alruiz@google.com (Alex Ruiz)
  */
 public class ProtobufJavaValidator_checkSyntaxIsKnown_Test {
+  @Rule public XtextRule xtext = overrideRuntimeModuleWith(unitTestModule());
+
   private Syntax syntax;
   private ValidationMessageAcceptor messageAcceptor;
-  private ProtobufJavaValidator validator;
+  @Inject private ProtobufJavaValidator validator;
 
   @Before public void setUp() {
     syntax = mock(Syntax.class);
     messageAcceptor = mock(ValidationMessageAcceptor.class);
-    validator = new ProtobufJavaValidator();
     validator.setMessageAcceptor(messageAcceptor);
   }
 
   @Test public void should_create_error_if_syntax_is_not_proto2_or_proto3() {
-    when(syntax.getName()).thenReturn("proto5");
+    when(syntax.getName()).thenReturn(createLiteralOf("proto5"));
     validator.checkSyntaxIsKnown(syntax);
     String message = "Unrecognized syntax identifier \"proto5\".  "
         + "This parser only recognizes \"proto2\" and \"proto3\".";
@@ -49,14 +57,20 @@
   }
 
   @Test public void should_not_create_error_if_syntax_is_proto2() {
-    when(syntax.getName()).thenReturn("proto2");
+    when(syntax.getName()).thenReturn(createLiteralOf("proto2"));
     validator.checkSyntaxIsKnown(syntax);
     verifyZeroInteractions(messageAcceptor);
   }
 
   @Test public void should_not_create_error_if_syntax_is_proto3() {
-    when(syntax.getName()).thenReturn("proto3");
+    when(syntax.getName()).thenReturn(createLiteralOf("proto3"));
     validator.checkSyntaxIsKnown(syntax);
     verifyZeroInteractions(messageAcceptor);
   }
+
+  private static StringLiteral createLiteralOf(String s) {
+    StringLiteral literalImpl = ProtobufFactory.eINSTANCE.createStringLiteral();
+    literalImpl.getChunks().add(s);
+    return literalImpl;
+  }
 }
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 018ca3b..0b90171 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
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Google Inc.
+ * Copyright (c) 2014 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
@@ -8,7 +8,6 @@
  */
 package com.google.eclipse.protobuf.ui;
 
-import static com.google.eclipse.protobuf.ui.util.Workbenches.activeWorkbenchWindow;
 import static com.google.inject.name.Names.named;
 import static org.eclipse.ui.PlatformUI.isWorkbenchRunning;
 
@@ -16,15 +15,15 @@
 import com.google.eclipse.protobuf.resource.IResourceVerifier;
 import com.google.eclipse.protobuf.scoping.IImportResolver;
 import com.google.eclipse.protobuf.scoping.IUriResolver;
-import com.google.eclipse.protobuf.ui.builder.nature.AutoAddNatureEditorCallback;
+import com.google.eclipse.protobuf.ui.builder.nature.ProtobufEditorCallback;
 import com.google.eclipse.protobuf.ui.documentation.ProtobufDocumentationProvider;
 import com.google.eclipse.protobuf.ui.editor.FileOutsideWorkspaceIconUpdater;
 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.editor.syntaxcoloring.HighlightingConfiguration;
+import com.google.eclipse.protobuf.ui.editor.syntaxcoloring.ProtobufAntlrTokenToAttributeIdMapper;
 import com.google.eclipse.protobuf.ui.editor.syntaxcoloring.ProtobufSemanticHighlightingCalculator;
-import com.google.eclipse.protobuf.ui.internal.ProtobufActivator;
 import com.google.eclipse.protobuf.ui.outline.LinkWithEditor;
 import com.google.eclipse.protobuf.ui.outline.ProtobufOutlinePage;
 import com.google.eclipse.protobuf.ui.parser.PreferenceDrivenProtobufParser;
@@ -38,11 +37,9 @@
 import com.google.eclipse.protobuf.ui.resource.ResourceVerifier;
 import com.google.eclipse.protobuf.ui.scoping.ImportResolver;
 import com.google.eclipse.protobuf.ui.scoping.UriResolver;
-import com.google.eclipse.protobuf.ui.validation.ValidateFileOnActivation;
 import com.google.inject.Binder;
 
 import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
-import org.eclipse.ui.IWorkbenchWindow;
 import org.eclipse.ui.plugin.AbstractUIPlugin;
 import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
 import org.eclipse.xtext.documentation.IEObjectDocumentationProvider;
@@ -54,6 +51,7 @@
 import org.eclipse.xtext.ui.editor.model.XtextDocumentProvider;
 import org.eclipse.xtext.ui.editor.outline.actions.IOutlineContribution;
 import org.eclipse.xtext.ui.editor.preferences.IPreferenceStoreInitializer;
+import org.eclipse.xtext.ui.editor.syntaxcoloring.AbstractAntlrTokenToAttributeIdMapper;
 import org.eclipse.xtext.ui.editor.syntaxcoloring.IHighlightingConfiguration;
 import org.eclipse.xtext.ui.editor.syntaxcoloring.ISemanticHighlightingCalculator;
 import org.eclipse.xtext.ui.resource.IResourceSetProvider;
@@ -67,7 +65,6 @@
 public class ProtobufUiModule extends AbstractProtobufUiModule {
   public ProtobufUiModule(AbstractUIPlugin plugin) {
     super(plugin);
-    setValidationTrigger(activeWorkbenchWindow(), plugin);
   }
 
   public Class<? extends IImportResolver> bindImportResolver() {
@@ -115,7 +112,7 @@
   }
 
   @Override public Class<? extends IXtextEditorCallback> bindIXtextEditorCallback() {
-    return AutoAddNatureEditorCallback.class;
+    return ProtobufEditorCallback.class;
   }
 
   public Class<? extends XtextDocumentProvider> bindXtextDocumentProvider() {
@@ -158,10 +155,7 @@
           .to(LinkWithEditor.class);
   }
 
-  private void setValidationTrigger(IWorkbenchWindow w, AbstractUIPlugin plugin) {
-    if (w == null || !(plugin instanceof ProtobufActivator)) {
-      return;
-    }
-    w.getPartService().addPartListener(new ValidateFileOnActivation());
+  public Class<? extends AbstractAntlrTokenToAttributeIdMapper> bindAbstractAntlrTokenToAttributeIdMapper() {
+    return ProtobufAntlrTokenToAttributeIdMapper.class;
   }
 }
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/builder/nature/AutoAddNatureEditorCallback.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/builder/nature/AutoAddNatureEditorCallback.java
deleted file mode 100644
index dcd8da2..0000000
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/builder/nature/AutoAddNatureEditorCallback.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.builder.nature;
-
-import com.google.inject.Inject;
-
-import org.eclipse.core.resources.IProject;
-import org.eclipse.core.resources.IResource;
-import org.eclipse.xtext.builder.nature.ToggleXtextNatureAction;
-import org.eclipse.xtext.builder.nature.XtextNature;
-import org.eclipse.xtext.ui.editor.IXtextEditorCallback;
-import org.eclipse.xtext.ui.editor.XtextEditor;
-
-
-/**
- * Automatically adds {@link XtextNature} to a project if needed (e.g. when opening a 'Protocol
- * Buffer' editor for the first time.)
- *
- * @author alruiz@google.com (Alex Ruiz)
- */
-@SuppressWarnings("restriction")
-public class AutoAddNatureEditorCallback extends IXtextEditorCallback.NullImpl {
-  @Inject private ToggleXtextNatureAction xtext;
-
-  @Override public void afterCreatePartControl(XtextEditor editor) {
-    IResource resource = editor.getResource();
-    if (resource == null) {
-      return;
-    }
-    IProject project = resource.getProject();
-    if (!project.isAccessible() || project.isHidden()) {
-      return;
-    }
-    if (!xtext.hasNature(project)) {
-      xtext.toggleNature(project);
-    }
-  }
-}
\ No newline at end of file
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/builder/nature/ProtobufEditorCallback.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/builder/nature/ProtobufEditorCallback.java
new file mode 100644
index 0000000..0871c71
--- /dev/null
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/builder/nature/ProtobufEditorCallback.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2014 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.builder.nature;
+
+import com.google.eclipse.protobuf.preferences.general.GeneralPreferences;
+import com.google.eclipse.protobuf.ui.validation.ProtobufValidation;
+import com.google.inject.Inject;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.xtext.builder.nature.ToggleXtextNatureAction;
+import org.eclipse.xtext.builder.nature.XtextNature;
+import org.eclipse.xtext.ui.editor.IXtextEditorCallback;
+import org.eclipse.xtext.ui.editor.XtextEditor;
+import org.eclipse.xtext.ui.editor.preferences.IPreferenceStoreAccess;
+
+
+/**
+ * Automatically adds {@link XtextNature} to a project if needed (e.g. when opening a 'Protocol
+ * Buffer' editor for the first time) and performs validation on a protobuf file when it is opened.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+@SuppressWarnings("restriction")
+public class ProtobufEditorCallback extends IXtextEditorCallback.NullImpl {
+  @Inject private ToggleXtextNatureAction xtext;
+  @Inject private IPreferenceStoreAccess storeAccess;
+  @Inject private ProtobufValidation validator;
+
+  @Override public void afterCreatePartControl(XtextEditor editor) {
+    IResource resource = editor.getResource();
+    if (resource == null) {
+      return;
+    }
+    IProject project = resource.getProject();
+    addXtextNatureToProject(project);
+    validateEditorIfEnabled(editor, project);
+  }
+
+  private void addXtextNatureToProject(IProject project) {
+    if (!project.isAccessible() || project.isHidden()) {
+      return;
+    }
+    if (!xtext.hasNature(project)) {
+      xtext.toggleNature(project);
+    }
+  }
+
+  private void validateEditorIfEnabled(XtextEditor editor, IProject project) {
+    GeneralPreferences preferences = new GeneralPreferences(storeAccess, project);
+    boolean shouldValidate = preferences.shouldValidateFilesOnActivation();
+    if (shouldValidate) {
+      validator.validate(editor);
+    }
+  }
+}
\ No newline at end of file
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/contentassist/ProtobufProposalProvider.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/contentassist/ProtobufProposalProvider.java
index 3a2fbfa..b575932 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/contentassist/ProtobufProposalProvider.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/contentassist/ProtobufProposalProvider.java
@@ -120,7 +120,7 @@
       ICompletionProposalAcceptor acceptor) {
     String proposal = SYNTAX + space() + EQUAL_PROTO2_IN_QUOTES;
     proposeAndAccept(proposal, imageHelper.getImage(images.imageFor(SYNTAX)), context, acceptor);
-    
+
     proposal = SYNTAX + space() + EQUAL_PROTO3_IN_QUOTES;
     proposeAndAccept(proposal, imageHelper.getImage(images.imageFor(SYNTAX)), context, acceptor);
   }
@@ -203,7 +203,7 @@
   @Override public void complete_ID(EObject model, RuleCall ruleCall, ContentAssistContext context,
       ICompletionProposalAcceptor acceptor) {}
 
-  @Override public void complete_STRING(EObject model, RuleCall ruleCall, ContentAssistContext context,
+  @Override public void complete_CHUNK(EObject model, RuleCall ruleCall, ContentAssistContext context,
       ICompletionProposalAcceptor acceptor) {}
 
   @Override public void completeKeyword(Keyword keyword, ContentAssistContext context,
@@ -272,7 +272,7 @@
   private void proposeEqualProto2(ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
     proposeAndAccept(EQUAL_PROTO2_IN_QUOTES, context, acceptor);
   }
-  
+
   private void proposeEqualProto3(ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
     proposeAndAccept(EQUAL_PROTO3_IN_QUOTES, context, acceptor);
   }
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 4e04505..ccda505 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
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Google Inc.
+ * Copyright (c) 2014 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
@@ -8,7 +8,8 @@
  */
 package com.google.eclipse.protobuf.ui.editor.hyperlinking;
 
-import static org.eclipse.emf.common.util.URI.createURI;
+import com.google.eclipse.protobuf.ui.editor.FileOpener;
+import com.google.inject.Inject;
 
 import org.apache.log4j.Logger;
 import org.eclipse.emf.common.util.URI;
@@ -18,10 +19,6 @@
 import org.eclipse.ui.PartInitException;
 import org.eclipse.xtext.nodemodel.INode;
 
-import com.google.eclipse.protobuf.protobuf.Import;
-import com.google.eclipse.protobuf.ui.editor.FileOpener;
-import com.google.inject.Inject;
-
 /**
  * A hyperlink for imported .proto files.
  *
@@ -35,8 +32,8 @@
   private URI importUri;
   private IRegion region;
 
-  void update(Import anImport, INode importUriNode) {
-    importUri = createURI(anImport.getImportURI());
+  void update(URI uri, INode importUriNode) {
+    importUri = uri;
     region = new Region(importUriNode.getOffset(), importUriNode.getLength());
   }
 
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/hyperlinking/ProtobufHyperlinkDetector.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/hyperlinking/ProtobufHyperlinkDetector.java
index 18c6540..c2482ee 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/hyperlinking/ProtobufHyperlinkDetector.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/hyperlinking/ProtobufHyperlinkDetector.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Google Inc.
+ * Copyright (c) 2014 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
@@ -8,7 +8,13 @@
  */
 package com.google.eclipse.protobuf.ui.editor.hyperlinking;
 
-import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.IMPORT__IMPORT_URI;
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.IMPORT__PATH;
+
+import com.google.eclipse.protobuf.model.util.INodes;
+import com.google.eclipse.protobuf.model.util.Imports;
+import com.google.eclipse.protobuf.protobuf.Import;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
 
 import org.eclipse.emf.ecore.EObject;
 import org.eclipse.jface.text.IRegion;
@@ -23,12 +29,6 @@
 import org.eclipse.xtext.ui.editor.model.IXtextDocument;
 import org.eclipse.xtext.util.concurrent.IUnitOfWork;
 
-import com.google.eclipse.protobuf.model.util.INodes;
-import com.google.eclipse.protobuf.model.util.Imports;
-import com.google.eclipse.protobuf.protobuf.Import;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
 /**
  * Represents an implementation of interface <code>{@link IHyperlinkDetector}</code> to find and convert
  * {@link CrossReference elements}, at a given location, to {@code IHyperlink}.
@@ -61,6 +61,9 @@
     return document.readOnly(new IUnitOfWork<IHyperlink[], XtextResource>() {
       @Override public IHyperlink[] exec(XtextResource resource) {
         EObject resolved = eObjectAtOffsetHelper.resolveElementAt(resource, region.getOffset());
+        if (resolved.eContainer() instanceof Import) {
+          resolved = resolved.eContainer();
+        }
         if (!(resolved instanceof Import)) {
           return NO_HYPERLINKS;
         }
@@ -68,12 +71,12 @@
         if (!imports.isResolved(anImport)) {
           return NO_HYPERLINKS;
         }
-        INode importUriNode = nodes.firstNodeForFeature(anImport, IMPORT__IMPORT_URI);
+        INode importUriNode = nodes.firstNodeForFeature(anImport, IMPORT__PATH);
         if (importUriNode.getLength() == 0) {
           return NO_HYPERLINKS;
         }
         ImportHyperlink hyperlink = importHyperlinkProvider.get();
-        hyperlink.update(anImport, importUriNode);
+        hyperlink.update(imports.resolvedUriOf(anImport), importUriNode);
         return new IHyperlink[] { hyperlink };
       }
     });
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/syntaxcoloring/ProtobufAntlrTokenToAttributeIdMapper.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/syntaxcoloring/ProtobufAntlrTokenToAttributeIdMapper.java
new file mode 100644
index 0000000..191dd6b
--- /dev/null
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/syntaxcoloring/ProtobufAntlrTokenToAttributeIdMapper.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2014 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.syntaxcoloring;
+
+import com.google.inject.Singleton;
+
+import org.eclipse.xtext.ui.editor.syntaxcoloring.DefaultAntlrTokenToAttributeIdMapper;
+import org.eclipse.xtext.ui.editor.syntaxcoloring.DefaultHighlightingConfiguration;
+
+/**
+ * Adds syntax highlighting information to tokens.
+ */
+@Singleton
+public class ProtobufAntlrTokenToAttributeIdMapper extends DefaultAntlrTokenToAttributeIdMapper {
+  @Override
+  protected String calculateId(String tokenName, int tokenType) {
+    if ("RULE_CHUNK".equals(tokenName)) {
+      return DefaultHighlightingConfiguration.STRING_ID;
+    }
+    return super.calculateId(tokenName, tokenType);
+  }
+}
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/grammar/CompoundElement.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/grammar/CompoundElement.java
index dfa1a0c..fe9117d 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/grammar/CompoundElement.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/grammar/CompoundElement.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Google Inc.
+ * Copyright (c) 2014 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
@@ -13,10 +13,10 @@
 import static com.google.eclipse.protobuf.grammar.CommonKeyword.EQUAL;
 import static com.google.eclipse.protobuf.grammar.CommonKeyword.OPENING_BRACKET;
 import static com.google.eclipse.protobuf.grammar.CommonKeyword.SEMICOLON;
-import static com.google.eclipse.protobuf.grammar.Syntaxes.proto2;
 import static com.google.eclipse.protobuf.util.CommonWords.space;
 
 import com.google.eclipse.protobuf.grammar.CommonKeyword;
+import com.google.eclipse.protobuf.model.util.Syntaxes;
 
 /**
  * Element composed of one or more keywords.
@@ -30,9 +30,9 @@
   EMPTY_STRING(join(QUOTE, QUOTE)),
   DEFAULT_EQUAL_STRING(joinWithWhitespace(DEFAULT_EQUAL, EMPTY_STRING)),
   DEFAULT_EQUAL_STRING_IN_BRACKETS(inBrackets(DEFAULT_EQUAL_STRING)),
-  PROTO2_IN_QUOTES(join(QUOTE, proto2(), QUOTE, SEMICOLON)),
+  PROTO2_IN_QUOTES(join(QUOTE, Syntaxes.PROTO2, QUOTE, SEMICOLON)),
   EQUAL_PROTO2_IN_QUOTES(joinWithWhitespace(EQUAL, PROTO2_IN_QUOTES)),
-  PROTO3_IN_QUOTES(join(QUOTE, proto2(), QUOTE, SEMICOLON)),
+  PROTO3_IN_QUOTES(join(QUOTE, Syntaxes.PROTO3, QUOTE, SEMICOLON)),
   EQUAL_PROTO3_IN_QUOTES(joinWithWhitespace(EQUAL, PROTO2_IN_QUOTES));
 
   private final String value;
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/labeling/Labels.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/labeling/Labels.java
index d86399c..69ca6ad 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/labeling/Labels.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/labeling/Labels.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Google Inc.
+ * Copyright (c) 2014 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
@@ -9,13 +9,8 @@
  */
 package com.google.eclipse.protobuf.ui.labeling;
 
-import static org.eclipse.jface.viewers.StyledString.DECORATIONS_STYLER;
-
 import static com.google.eclipse.protobuf.ui.labeling.Messages.unresolved;
-
-import org.eclipse.emf.common.util.EList;
-import org.eclipse.emf.ecore.EObject;
-import org.eclipse.jface.viewers.StyledString;
+import static org.eclipse.jface.viewers.StyledString.DECORATIONS_STYLER;
 
 import com.google.eclipse.protobuf.model.util.Imports;
 import com.google.eclipse.protobuf.model.util.MessageFields;
@@ -37,6 +32,10 @@
 import com.google.eclipse.protobuf.protobuf.TypeExtension;
 import com.google.inject.Inject;
 
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.jface.viewers.StyledString;
+
 /**
  * Registry of commonly used text in the 'Protocol Buffer' editor.
  *
@@ -128,7 +127,7 @@
   }
 
   private Object labelFor(Import anImport) {
-    return imports.uriAsEnteredByUser(anImport);
+    return imports.getPath(anImport);
   }
 
   private Object labelFor(Literal literal) {
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/quickfix/ProtobufQuickfixProvider.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/quickfix/ProtobufQuickfixProvider.java
index aa8f353..5e789b9 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/quickfix/ProtobufQuickfixProvider.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/quickfix/ProtobufQuickfixProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Google Inc.
+ * Copyright (c) 2014 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
@@ -8,8 +8,6 @@
  */
 package com.google.eclipse.protobuf.ui.quickfix;
 
-import static com.google.eclipse.protobuf.grammar.Syntaxes.proto2;
-import static com.google.eclipse.protobuf.grammar.Syntaxes.proto3;
 import static com.google.eclipse.protobuf.protobuf.BOOL.FALSE;
 import static com.google.eclipse.protobuf.protobuf.BOOL.TRUE;
 import static com.google.eclipse.protobuf.ui.quickfix.Messages.changeValueDescription;
@@ -31,6 +29,7 @@
 import com.google.eclipse.protobuf.grammar.CommonKeyword;
 import com.google.eclipse.protobuf.model.util.INodes;
 import com.google.eclipse.protobuf.model.util.IndexedElements;
+import com.google.eclipse.protobuf.model.util.Syntaxes;
 import com.google.eclipse.protobuf.naming.NameResolver;
 import com.google.eclipse.protobuf.protobuf.BOOL;
 import com.google.eclipse.protobuf.protobuf.BooleanLink;
@@ -41,9 +40,9 @@
 import com.google.eclipse.protobuf.protobuf.Package;
 import com.google.eclipse.protobuf.protobuf.ProtobufFactory;
 import com.google.eclipse.protobuf.protobuf.StringLink;
+import com.google.eclipse.protobuf.protobuf.StringLiteral;
 import com.google.eclipse.protobuf.protobuf.Syntax;
 import com.google.eclipse.protobuf.protobuf.Value;
-import com.google.eclipse.protobuf.validation.ProtobufJavaValidator;
 import com.google.inject.Inject;
 
 import org.eclipse.emf.ecore.EObject;
@@ -67,17 +66,18 @@
   @Inject private IndexedElements indexedElements;
   @Inject private NameResolver nameResolver;
   @Inject private INodes nodes;
+  @Inject private Syntaxes syntaxes;
 
   @Fix(SYNTAX_IS_NOT_KNOWN_ERROR)
   public void changeSyntaxToProto2(Issue issue, IssueResolutionAcceptor acceptor) {
     ISemanticModification modification = new ISemanticModification() {
       @Override public void apply(EObject element, IModificationContext context) throws Exception {
         Syntax syntax = (Syntax) element;
-        syntax.setName(proto2());
+        syntaxes.setName(syntax, Syntaxes.PROTO2);
       }
     };
-    String description = String.format(changeValueDescription, "syntax", quote(proto2()));
-    String label = String.format(changeValueLabel, proto2());
+    String description = String.format(changeValueDescription, "syntax", quote(Syntaxes.PROTO2));
+    String label = String.format(changeValueLabel, Syntaxes.PROTO2);
     acceptor.accept(issue, label, description, ICON_FOR_CHANGE, modification);
   }
 
@@ -86,11 +86,11 @@
     ISemanticModification modification = new ISemanticModification() {
       @Override public void apply(EObject element, IModificationContext context) throws Exception {
         Syntax syntax = (Syntax) element;
-        syntax.setName(proto3());
+        syntaxes.setName(syntax, Syntaxes.PROTO3);
       }
     };
-    String description = String.format(changeValueDescription, "syntax", quote(proto3()));
-    String label = String.format(changeValueLabel, proto3());
+    String description = String.format(changeValueDescription, "syntax", quote(Syntaxes.PROTO3));
+    String label = String.format(changeValueLabel, Syntaxes.PROTO3);
     acceptor.accept(issue, label, description, ICON_FOR_CHANGE, modification);
   }
 
@@ -217,7 +217,9 @@
 
   private StringLink linkTo(String value) {
     StringLink link = ProtobufFactory.eINSTANCE.createStringLink();
-    link.setTarget(value);
+    StringLiteral literal = ProtobufFactory.eINSTANCE.createStringLiteral();
+    literal.getChunks().add(value);
+    link.setTarget(literal);
     return link;
   }
 
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/scoping/ImportResolver.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/scoping/ImportResolver.java
index c00c3b1..f9d2a74 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/scoping/ImportResolver.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/scoping/ImportResolver.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Google Inc.
+ * Copyright (c) 2014 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
@@ -8,6 +8,9 @@
  */
 package com.google.eclipse.protobuf.ui.scoping;
 
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
 import com.google.eclipse.protobuf.model.util.Imports;
 import com.google.eclipse.protobuf.protobuf.Import;
 import com.google.eclipse.protobuf.scoping.IImportResolver;
@@ -21,6 +24,8 @@
 import org.eclipse.emf.common.util.URI;
 import org.eclipse.emf.ecore.resource.Resource;
 
+import java.util.concurrent.ExecutionException;
+
 /**
  * Resolves "import" URIs.
  *
@@ -32,6 +37,18 @@
   @Inject private IUriResolver resolver;
   @Inject private Uris uris;
 
+  private LoadingCache<Import, String> cache =
+      CacheBuilder.newBuilder().build(new CacheLoader<Import, String>() {
+        @Override
+        public String load(Import key) throws Exception {
+          String result = internalResolveUri(key);
+          if (result == null) {
+            throw new Exception("Unable to resolve import: " + imports.getPath(key));
+          }
+          return result;
+        }
+      });
+
   /*
    * The import URI is relative to the file where the import is. Protoc works fine, but the editor doesn't.
    * In order for the editor to see the import, we need to add to the import URI "platform:resource" and the parent
@@ -47,14 +64,16 @@
    * If we import "folder/proto2.proto" into proto1.proto, proto1.proto will compile fine, but the editor will complain.
    * We need to have the import URI as "platform:/resource/protobuf-test/folder/proto2.proto" for the editor to see it.
    */
-  @Override public void resolveAndUpdateUri(Import anImport) {
-    if (imports.isResolved(anImport)) {
-      return;
+  @Override public String resolve(Import anImport) {
+    try {
+      return cache.get(anImport);
+    } catch (ExecutionException e) {
+      return null;
     }
-    String resolved = resolveUri(anImport.getImportURI(), anImport.eResource());
-    if (resolved != null) {
-      anImport.setImportURI(resolved);
-    }
+  }
+
+  private String internalResolveUri(Import anImport) {
+    return resolveUri(imports.getPath(anImport), anImport.eResource());
   }
 
   private String resolveUri(String importUri, Resource resource) {
@@ -68,4 +87,9 @@
     IProject project = uris.projectOfReferredFile(resourceUri);
     return resolver.resolveUri(importUri, resourceUri, project);
   }
+
+  @Override
+  public void invalidateCacheFor(Import anImport) {
+    cache.invalidate(anImport);
+  }
 }
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/ProtobufValidation.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/ProtobufValidation.java
index 340e12c..060597c 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/ProtobufValidation.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/ProtobufValidation.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Google Inc.
+ * Copyright (c) 2014 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
@@ -8,13 +8,15 @@
  */
 package com.google.eclipse.protobuf.ui.validation;
 
-import static org.eclipse.xtext.EcoreUtil2.getAllContentsOfType;
-
-import java.util.List;
+import com.google.eclipse.protobuf.protobuf.Import;
+import com.google.eclipse.protobuf.scoping.IImportResolver;
+import com.google.eclipse.protobuf.ui.plugin.ProtobufEditorPlugIn;
+import com.google.inject.Inject;
 
 import org.eclipse.emf.ecore.EObject;
 import org.eclipse.ui.IEditorPart;
 import org.eclipse.ui.part.FileEditorInput;
+import org.eclipse.xtext.EcoreUtil2;
 import org.eclipse.xtext.parser.IParseResult;
 import org.eclipse.xtext.resource.XtextResource;
 import org.eclipse.xtext.resource.impl.ListBasedDiagnosticConsumer;
@@ -23,15 +25,15 @@
 import org.eclipse.xtext.ui.editor.model.XtextDocument;
 import org.eclipse.xtext.util.concurrent.IUnitOfWork;
 
-import com.google.eclipse.protobuf.model.util.Imports;
-import com.google.eclipse.protobuf.protobuf.Import;
-import com.google.eclipse.protobuf.ui.plugin.ProtobufEditorPlugIn;
+import java.util.List;
 
 /**
  * @author alruiz@google.com (Alex Ruiz)
  */
-final class ProtobufValidation {
-  static void validate(IEditorPart editor) {
+public final class ProtobufValidation {
+  @Inject private IImportResolver resolver;
+
+  public void validate(IEditorPart editor) {
     if (!(editor instanceof XtextEditor) || !(editor.getEditorInput() instanceof FileEditorInput)) {
       return;
     }
@@ -42,7 +44,7 @@
     validate(xtextEditor);
   }
 
-  private static void validate(XtextEditor editor) {
+  private void validate(XtextEditor editor) {
     final IXtextDocument document = editor.getDocument();
     if (!(document instanceof XtextDocument)) {
       return;
@@ -53,36 +55,25 @@
         if (root == null) {
           return;
         }
-        resetUriInImports(root);
+        invalidateCacheForImports(root);
         resource.getLinker().linkModel(root, new ListBasedDiagnosticConsumer());
         ((XtextDocument) document).checkAndUpdateAnnotations();
       }
     });
   }
 
-  private static EObject rootOf(XtextResource resource) {
+  private void invalidateCacheForImports(EObject root) {
+    List<Import> imports = EcoreUtil2.getAllContentsOfType(root, Import.class);
+    for (Import anImport : imports) {
+      resolver.invalidateCacheFor(anImport);
+    }
+  }
+
+  private EObject rootOf(XtextResource resource) {
     if (resource == null) {
       return null;
     }
     IParseResult parseResult = resource.getParseResult();
     return parseResult == null ? null : parseResult.getRootASTElement();
   }
-
-  private static void resetUriInImports(EObject root) {
-    List<Import> imports = getAllContentsOfType(root, Import.class);
-    for (Import anImport : imports) {
-      resetUri(anImport);
-    }
-  }
-
-  private static void resetUri(Import anImport) {
-    Imports imports = ProtobufEditorPlugIn.getInstanceOf(Imports.class);
-    String uri = imports.uriAsEnteredByUser(anImport);
-    if (uri == null) {
-      return;
-    }
-    anImport.setImportURI(uri);
-  }
-
-  private ProtobufValidation() {}
 }
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/ValidateFileOnActivation.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/ValidateFileOnActivation.java
deleted file mode 100644
index 55f2941..0000000
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/ValidateFileOnActivation.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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.validation;
-
-import static com.google.eclipse.protobuf.ui.validation.ProtobufValidation.validate;
-
-import com.google.eclipse.protobuf.preferences.general.GeneralPreferences;
-import com.google.eclipse.protobuf.ui.plugin.ProtobufEditorPlugIn;
-import com.google.eclipse.protobuf.ui.util.Editors;
-
-import org.eclipse.core.resources.IProject;
-import org.eclipse.ui.IEditorPart;
-import org.eclipse.ui.IWorkbenchPage;
-import org.eclipse.ui.IWorkbenchPartReference;
-import org.eclipse.xtext.ui.editor.preferences.IPreferenceStoreAccess;
-
-/**
- * Validates a .proto file when it is opened or activated.
- *
- * @author alruiz@google.com (Alex Ruiz)
- */
-public class ValidateFileOnActivation extends AbstractPartListener {
-  /**
-   * Validates the active active editor in the given part that contains a .proto file in the Workspace.
-   * @param ref the part that was activated.
-   */
-  @Override public void partActivated(IWorkbenchPartReference ref) {
-    IEditorPart activeEditor = activeEditor(ref);
-    IProject project = projectFrom(activeEditor);
-    if (project == null || !shouldValidateEditor(project)) {
-      return;
-    }
-    validate(activeEditor);
-  }
-
-  private IEditorPart activeEditor(IWorkbenchPartReference ref) {
-    IWorkbenchPage page = ref.getPage();
-    return (page == null) ? null : page.getActiveEditor();
-  }
-
-  private IProject projectFrom(IEditorPart editor) {
-    Editors editors = ProtobufEditorPlugIn.getInstanceOf(Editors.class);
-    return editors.projectOwningFileDisplayedIn(editor);
-  }
-
-  private boolean shouldValidateEditor(IProject project) {
-    IPreferenceStoreAccess storeAccess = ProtobufEditorPlugIn.getInstanceOf(IPreferenceStoreAccess.class);
-    GeneralPreferences preferences = new GeneralPreferences(storeAccess, project);
-    return preferences.shouldValidateFilesOnActivation();
-  }
-}
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/ValidationTrigger.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/ValidationTrigger.java
index 46a278b..468dda2 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/ValidationTrigger.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/validation/ValidationTrigger.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Google Inc.
+ * Copyright (c) 2014 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
@@ -9,9 +9,10 @@
 package com.google.eclipse.protobuf.ui.validation;
 
 import static com.google.common.base.Objects.equal;
-import static com.google.eclipse.protobuf.ui.validation.ProtobufValidation.validate;
 
-import java.net.URI;
+import com.google.eclipse.protobuf.ui.util.Editors;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
 
 import org.eclipse.core.resources.IProject;
 import org.eclipse.ui.IEditorPart;
@@ -20,9 +21,7 @@
 import org.eclipse.ui.IWorkbenchWindow;
 import org.eclipse.ui.PlatformUI;
 
-import com.google.eclipse.protobuf.ui.util.Editors;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
+import java.net.URI;
 
 /**
  * Triggers validation of .proto files.
@@ -33,6 +32,7 @@
   private final String PROTO_EDITOR_ID = "com.google.eclipse.protobuf.Protobuf";
 
   @Inject private Editors editors;
+  @Inject private ProtobufValidation validator;
 
   /**
    * Triggers validation of all open .proto files belonging to the given project.
@@ -57,7 +57,7 @@
     if (fileProject == null || !haveEqualUris(project, fileProject)) {
       return;
     }
-    validate(editor);
+    validator.validate(editor);
   }
 
   private boolean haveEqualUris(IProject p1, IProject p2) {
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/Protobuf.xtext b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/Protobuf.xtext
index 6c60868..2088607 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/Protobuf.xtext
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/Protobuf.xtext
@@ -18,7 +18,7 @@
   (elements+=ProtobufElement)*;
 
 Syntax:
-  'syntax' '=' name=STRING (';')+;
+  'syntax' '=' name=StringLiteral (';')+;
 
 ProtobufElement:
   Package | Import | Option | ComplexType | TypeExtension | Service;
@@ -33,13 +33,13 @@
   NormalImport | PublicImport | WeakImport;
 
 NormalImport:
-  'import' importURI=STRING (';')+;
+  'import' path=StringLiteral (';')+;
 
 PublicImport:
-  'import' 'public' importURI=STRING (';')+;
+  'import' 'public' path=StringLiteral (';')+;
 
 WeakImport:
-  'import' 'weak' importURI=STRING (';')+;
+  'import' 'weak' path=StringLiteral (';')+;
 
 ComplexType:
   Enum | ExtensibleType;
@@ -289,14 +289,14 @@
   '0'..'9';
 
 StringLink:
-  target=STRING;
+  target=StringLiteral;
 
-terminal STRING:
-  SL_STRING (SL_STRING)*;
+StringLiteral:
+  chunks+=CHUNK+;
 
-terminal SL_STRING:
-  '"' ('\\' ('"' | "'" | '\\' | !('\\' | '"')) | !('\\' | '"'))* '"' (WS)* |
-  "'" ('\\' ('"' | "'" | '\\' | !('\\' | "'")) | !('\\' | "'"))* "'" (WS)*;
+terminal CHUNK:
+  '"' ('\\' ('"' | "'" | '\\' | !('\\' | '"')) | !('\\' | '"'))* '"' |
+  "'" ('\\' ('"' | "'" | '\\' | !('\\' | "'")) | !('\\' | "'"))* "'";
 
 terminal ID: '^'?('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*;
 terminal ML_COMMENT: '/*' -> '*/';
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/ProtobufRuntimeModule.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/ProtobufRuntimeModule.java
index bae304a..541b1a1 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/ProtobufRuntimeModule.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/ProtobufRuntimeModule.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Google Inc.
+ * Copyright (c) 2014 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
@@ -15,7 +15,7 @@
 import com.google.eclipse.protobuf.resource.FastXtextResourceSet;
 import com.google.eclipse.protobuf.resource.GlobalResourceServiceProvider;
 import com.google.eclipse.protobuf.scoping.ExtensionRegistryProvider;
-import com.google.eclipse.protobuf.scoping.ProtobufImportUriResolver;
+import com.google.eclipse.protobuf.scoping.ProtobufGlobalScopeProvider;
 import com.google.eclipse.protobuf.validation.ProtobufResourceValidator;
 import com.google.eclipse.protobuf.validation.ProtobufSyntaxErrorMessageProvider;
 import com.google.inject.Binder;
@@ -28,7 +28,7 @@
 import org.eclipse.xtext.resource.IGlobalServiceProvider;
 import org.eclipse.xtext.resource.XtextResource;
 import org.eclipse.xtext.resource.XtextResourceSet;
-import org.eclipse.xtext.scoping.impl.ImportUriResolver;
+import org.eclipse.xtext.scoping.IGlobalScopeProvider;
 import org.eclipse.xtext.validation.IResourceValidator;
 
 /**
@@ -39,14 +39,10 @@
     return GlobalResourceServiceProvider.class;
   }
 
-  public Class<? extends ImportUriResolver> bindImportUriResolver() {
-    return ProtobufImportUriResolver.class;
-  }
-
   public Class<? extends IQualifiedNameConverter> bindIQualifiedNameConverter() {
     return ProtobufQualifiedNameConverter.class;
   }
-  
+
   @Override public Class<? extends IQualifiedNameProvider> bindIQualifiedNameProvider() {
     return ProtobufQualifiedNameProvider.class;
   }
@@ -71,6 +67,10 @@
     return FastXtextResourceSet.class;
   }
 
+  @Override public Class<? extends IGlobalScopeProvider> bindIGlobalScopeProvider() {
+    return ProtobufGlobalScopeProvider.class;
+  }
+
   public void configureExtensionRegistry(Binder binder) {
     binder.bind(IExtensionRegistry.class).toProvider(ExtensionRegistryProvider.class);
   }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/conversion/ProtobufTerminalConverters.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/conversion/ProtobufTerminalConverters.java
index fa83fc7..8a57a6a 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/conversion/ProtobufTerminalConverters.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/conversion/ProtobufTerminalConverters.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Google Inc.
+ * Copyright (c) 2014 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
@@ -8,12 +8,12 @@
  */
 package com.google.eclipse.protobuf.conversion;
 
+import com.google.inject.Inject;
+
 import org.eclipse.xtext.common.services.DefaultTerminalConverters;
 import org.eclipse.xtext.conversion.IValueConverter;
 import org.eclipse.xtext.conversion.ValueConverter;
 
-import com.google.inject.Inject;
-
 /**
  * @author alruiz@google.com (Alex Ruiz)
  */
@@ -21,7 +21,6 @@
   @Inject private DOUBLEValueConverter doubleValueConverter;
   @Inject private HEXValueConverter hexValueConverter;
   @Inject private LONGValueConverter longValueConverter;
-  @Inject private STRINGValueConverter stringValueConverter;
 
   @ValueConverter(rule = "DOUBLE")
   public IValueConverter<Double> DOUBLE() {
@@ -37,9 +36,4 @@
   public IValueConverter<Long> LONG() {
     return longValueConverter;
   }
-
-  @ValueConverter(rule = "STRING")
-  @Override public IValueConverter<String> STRING() {
-    return stringValueConverter;
-  }
 }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/conversion/STRINGValueConverter.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/conversion/STRINGValueConverter.java
deleted file mode 100644
index 9d5a13e..0000000
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/conversion/STRINGValueConverter.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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.conversion;
-
-import static org.eclipse.xtext.util.Strings.convertToJavaString;
-
-import static com.google.eclipse.protobuf.util.Strings.removeLineBreaks;
-import static com.google.eclipse.protobuf.util.Strings.unquote;
-
-import org.eclipse.xtext.conversion.ValueConverterException;
-import org.eclipse.xtext.conversion.impl.AbstractLexerBasedConverter;
-import org.eclipse.xtext.nodemodel.INode;
-
-import com.google.common.base.Function;
-
-/**
- * Converts multi-line strings to {@code String}s.
- *
- * @author alruiz@google.com (Alex Ruiz)
- */
-public class STRINGValueConverter extends AbstractLexerBasedConverter<String> {
-  @Override protected String toEscapedString(String value) {
-    if (value == null) {
-      return null;
-    }
-    // TODO check if we really need to quote
-    return '"' + toValue(value) + '"';
-  }
-
-  /**
-   * Creates a {@code String} from the given input, if the given input represents a multiple-line string.
-   * @param string the given input.
-   * @param node the parsed node including hidden parts.
-   * @return the new {@code String}.
-   * @throws ValueConverterException if the given input has illegal characters.
-   */
-  @Override public String toValue(String string, INode node) throws ValueConverterException {
-    if (string == null) {
-      return null;
-    }
-    try {
-      return toValue(string);
-    } catch (RuntimeException e) {
-      throw parsingError(string, node, e);
-    }
-  }
-
-  private String toValue(String string) {
-    return removeLineBreaks(string, LineTransformation.INSTANCE);
-  }
-
-  private ValueConverterException parsingError(String string, INode node, Exception cause) {
-    return new ValueConverterException("Couldn't convert '" + string + "' to String.", node, cause);
-  }
-
-  private static class LineTransformation implements Function<String, String> {
-    private static final LineTransformation INSTANCE = new LineTransformation();
-
-    @Override public String apply(String input) {
-      return convertToJavaString(unquote(input), true);
-    }
-  }
-}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/formatting/ProtobufFormatter.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/formatting/ProtobufFormatter.java
index 10ec51e..e1115a6 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/formatting/ProtobufFormatter.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/formatting/ProtobufFormatter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Google Inc.
+ * Copyright (c) 2014 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
@@ -16,12 +16,12 @@
 import static com.google.eclipse.protobuf.grammar.CommonKeyword.SEMICOLON;
 import static com.google.eclipse.protobuf.util.CommonWords.space;
 
+import com.google.eclipse.protobuf.services.ProtobufGrammarAccess;
+
 import org.eclipse.xtext.Keyword;
 import org.eclipse.xtext.formatting.impl.AbstractDeclarativeFormatter;
 import org.eclipse.xtext.formatting.impl.FormattingConfig;
 
-import com.google.eclipse.protobuf.services.ProtobufGrammarAccess;
-
 /**
  * Provides custom formatting.
  *
@@ -49,6 +49,12 @@
     c.setLinewrap(1).after(g.getRpcRule());
     c.setLinewrap(2).after(g.getServiceRule());
     c.setLinewrap(1).after(g.getStreamRule());
+
+    c.setLinewrap(1).between(g.getCHUNKRule(), g.getCHUNKRule());
+    c.setLinewrap(1).between(g.getML_COMMENTRule(), g.getCHUNKRule());
+    c.setIndentationIncrement().before(g.getStringLiteralRule());
+    c.setIndentationDecrement().after(g.getStringLiteralRule());
+
     for (Keyword k : g.findKeywords(EQUAL.toString())) {
       c.setSpace(space()).around(k);
     }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/grammar/Syntaxes.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/grammar/Syntaxes.java
deleted file mode 100644
index f27176a..0000000
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/grammar/Syntaxes.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.grammar;
-
-/**
- * @author alruiz@google.com (Alex Ruiz)
- */
-public final class Syntaxes {
-  /**
-   * Returns the value to use to in the "syntax" element if this is a proto2 file.
-   * @return the {@code String} "proto2".
-   */
-  public static String proto2() {
-    return "proto2";
-  }
-
-  /**
-   * Returns the value to use to in the "syntax" element if this is a proto3 file.
-   * @return the {@code String} "proto3".
-   */
-  public static String proto3() {
-    return "proto3";
-  }
-
-  /**
-   * Indicates whether the given {@code String} is equal to <code>{@link #proto2()}</code>.
-   * @param s the {@code String} to check.
-   * @return {@code true} if the given {@code String} is equal to "proto2," {@code false} otherwise.
-   */
-  public static boolean isSpecifyingProto2Syntax(String s) {
-    return proto2().equals(s);
-  }
-
-  /**
-   * Indicates whether the given {@code String} is equal to <code>{@link #proto3()}</code>.
-   * @param s the {@code String} to check.
-   * @return {@code true} if the given {@code String} is equal to "proto3," {@code false} otherwise.
-   */
-  public static boolean isSpecifyingProto3Syntax(String s) {
-    return proto3().equals(s);
-  }
-
-  private Syntaxes() {}
-}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/INodes.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/INodes.java
index 00d8bdf..7242757 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/INodes.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/INodes.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Google Inc.
+ * Copyright (c) 2014 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
@@ -10,7 +10,7 @@
 
 import static org.eclipse.xtext.nodemodel.util.NodeModelUtils.findNodesForFeature;
 
-import java.util.List;
+import com.google.inject.Singleton;
 
 import org.eclipse.emf.ecore.EObject;
 import org.eclipse.emf.ecore.EStructuralFeature;
@@ -20,7 +20,7 @@
 import org.eclipse.xtext.nodemodel.ILeafNode;
 import org.eclipse.xtext.nodemodel.INode;
 
-import com.google.inject.Singleton;
+import java.util.List;
 
 /**
  * Utility methods related to <code>{@link INode}</code>s.
@@ -107,7 +107,8 @@
       return false;
     }
     TerminalRule terminalRule = (TerminalRule) rule;
-    return "STRING".equals(terminalRule.getName());
+    return "StringLiteral".equals(terminalRule.getName())
+        || "CHUNK".equals(terminalRule.getName());
   }
 
   /**
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/Imports.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/Imports.java
index 9c0fa64..8fa6a93 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/Imports.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/Imports.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Google Inc.
+ * Copyright (c) 2014 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
@@ -8,22 +8,20 @@
  */
 package com.google.eclipse.protobuf.model.util;
 
+import static com.google.common.base.Strings.isNullOrEmpty;
 import static org.eclipse.xtext.util.Strings.isEmpty;
-import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.IMPORT__IMPORT_URI;
+
+import com.google.eclipse.protobuf.protobuf.Import;
+import com.google.eclipse.protobuf.resource.ResourceSets;
+import com.google.eclipse.protobuf.scoping.IImportResolver;
+import com.google.eclipse.protobuf.scoping.ProtoDescriptorProvider;
+import com.google.eclipse.protobuf.util.EResources;
+import com.google.inject.Inject;
 
 import org.eclipse.core.resources.IProject;
 import org.eclipse.emf.common.util.URI;
 import org.eclipse.emf.ecore.resource.Resource;
 import org.eclipse.emf.ecore.resource.ResourceSet;
-import org.eclipse.xtext.nodemodel.INode;
-import org.eclipse.xtext.scoping.impl.ImportUriResolver;
-
-import com.google.eclipse.protobuf.conversion.STRINGValueConverter;
-import com.google.eclipse.protobuf.protobuf.Import;
-import com.google.eclipse.protobuf.resource.ResourceSets;
-import com.google.eclipse.protobuf.scoping.ProtoDescriptorProvider;
-import com.google.eclipse.protobuf.util.EResources;
-import com.google.inject.Inject;
 
 /**
  * Utility methods related to imports.
@@ -32,10 +30,9 @@
  */
 public class Imports {
   @Inject private ProtoDescriptorProvider descriptorProvider;
-  @Inject private INodes nodes;
   @Inject private ResourceSets resourceSets;
-  @Inject private STRINGValueConverter converter;
-  @Inject private ImportUriResolver uriResolver;
+  @Inject private StringLiterals stringLiterals;
+  @Inject private IImportResolver importResolver;
 
   /**
    * Indicates whether the URI of the given {@code Import} is equal to the path of the file "descriptor.proto."
@@ -50,7 +47,7 @@
     }
     IProject project = EResources.getProjectOf(anImport.eResource());
     URI descriptorLocation =
-        descriptorProvider.descriptorLocation(project, anImport.getImportURI());
+        descriptorProvider.descriptorLocation(project, getPath(anImport));
     return descriptorLocation != null;
   }
 
@@ -66,7 +63,7 @@
     if (anImport == null) {
       return false;
     }
-    String importUri = anImport.getImportURI();
+    String importUri = getPath(anImport);
     IProject project = EResources.getProjectOf(anImport.eResource());
     for (URI locationUri : descriptorProvider.allDescriptorLocations(project)) {
       String location = locationUri.toString();
@@ -78,31 +75,12 @@
   }
 
   /**
-   * Returns the URI of the given {@code Import} as it looks in the editor (i.e. before it is resolved.)
+   * Indicates whether the URI of the given {@code Import} can be resolved.
    * @param anImport the given {@code Import}.
-   * @return the URI of the given {@code Import} as it looks in the editor.
-   */
-  public String uriAsEnteredByUser(Import anImport) {
-    INode node = nodes.firstNodeForFeature(anImport, IMPORT__IMPORT_URI);
-    String text = (node == null) ? null : node.getText();
-    if (text == null) {
-      return null;
-    }
-    return converter.toValue(text, node);
-  }
-
-  /**
-   * Indicates whether the URI of the given {@code Import} has been resolved.
-   * @param anImport the given {@code Import}.
-   * @return {@code true} if the URI of the given {@code Import} has been resolved, {@code false} otherwise.
+   * @return {@code true} if the URI of the given {@code Import} can be resolved, {@code false} otherwise.
    */
   public boolean isResolved(Import anImport) {
-    String uriAsText = anImport.getImportURI();
-    if (!isEmpty(uriAsText)) {
-      URI uri = URI.createURI(uriAsText);
-      return isResolved(uri);
-    }
-    return false;
+    return resolvedUriOf(anImport) != null;
   }
 
   /**
@@ -126,8 +104,8 @@
    * @return the resolved URI of the given {@code Import}, or {@code null} if the URI was not successfully resolved.
    */
   public URI resolvedUriOf(Import anImport) {
-    String resolvedUri = uriResolver.apply(anImport);
-    if (isEmpty(resolvedUri)) {
+    String resolvedUri = importResolver.resolve(anImport);
+    if (isNullOrEmpty(resolvedUri)) {
       return null;
     }
     URI uri = URI.createURI(resolvedUri);
@@ -137,4 +115,11 @@
   private boolean isResolved(URI uri) {
     return !isEmpty(uri.scheme());
   }
+
+  /**
+   * Returns the path that is being imported by the given {@link Import}.
+   */
+  public String getPath(Import anImport) {
+    return stringLiterals.getCombinedString(anImport.getPath());
+  }
 }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/Messages.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/Messages.java
index 82509a0..0958e94 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/Messages.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/Messages.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Google Inc.
+ * Copyright (c) 2014 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
@@ -8,29 +8,17 @@
  */
 package com.google.eclipse.protobuf.model.util;
 
-import static java.util.Collections.unmodifiableList;
-
+import static com.google.common.collect.Sets.newHashSet;
 import static org.eclipse.xtext.EcoreUtil2.getAllContentsOfType;
 
-import static com.google.common.collect.Lists.newArrayList;
-import static com.google.common.collect.Sets.newHashSet;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Set;
-
-import com.google.common.base.Strings;
-import com.google.eclipse.protobuf.protobuf.Group;
 import com.google.eclipse.protobuf.protobuf.Message;
-import com.google.eclipse.protobuf.protobuf.MessageElement;
-import com.google.eclipse.protobuf.protobuf.MessageField;
-import com.google.eclipse.protobuf.protobuf.OneOf;
 import com.google.eclipse.protobuf.protobuf.Protobuf;
 import com.google.eclipse.protobuf.protobuf.TypeExtension;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
-import org.eclipse.emf.ecore.EObject;
+import java.util.Collection;
+import java.util.Set;
 
 /**
  * Utility methods related to <code>{@link Message}</code>s.
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/StringLiterals.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/StringLiterals.java
new file mode 100644
index 0000000..dd9fbb5
--- /dev/null
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/StringLiterals.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014 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.model.util;
+
+import static com.google.eclipse.protobuf.util.Strings.unquote;
+import static org.eclipse.xtext.util.Strings.convertToJavaString;
+
+import com.google.eclipse.protobuf.protobuf.StringLiteral;
+import com.google.inject.Singleton;
+
+/**
+ * Helper methods for working with {@link StringLiteral}s.
+ */
+@Singleton public class StringLiterals {
+  /**
+   * Returns the result of combining the chunks of a string literal.
+   */
+  public String getCombinedString(StringLiteral stringLiteral) {
+    StringBuilder sb = new StringBuilder();
+    for (String chunk : stringLiteral.getChunks()) {
+      sb.append(convertToJavaString(unquote(chunk)));
+    }
+    return sb.toString();
+  }
+}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/Syntaxes.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/Syntaxes.java
new file mode 100644
index 0000000..8fe751c
--- /dev/null
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/Syntaxes.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2014 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.model.util;
+
+import com.google.eclipse.protobuf.protobuf.ProtobufFactory;
+import com.google.eclipse.protobuf.protobuf.StringLiteral;
+import com.google.eclipse.protobuf.protobuf.Syntax;
+import com.google.eclipse.protobuf.util.Strings;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+/**
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+@Singleton public final class Syntaxes {
+  public static final String PROTO2 = "proto2";
+  public static final String PROTO3 = "proto3";
+
+  @Inject
+  private StringLiterals stringLiterals;
+
+  /**
+   * Indicates whether the given {@code String} is equal to <code>{@link #PROTO2}</code>.
+   * @param s the {@code String} to check.
+   * @return {@code true} if the given {@code String} is equal to "proto2," {@code false} otherwise.
+   */
+  public boolean isSpecifyingProto2Syntax(String s) {
+    return PROTO2.equals(s);
+  }
+
+  /**
+   * Indicates whether the given {@code Syntax} specifies proto2 syntax.
+   * @param s the {@code Syntax} to check.
+   * @return {@code true} if the given {@code Syntax} specifies "proto2," {@code false} otherwise.
+   */
+  public boolean isSpecifyingProto2Syntax(Syntax s) {
+    return PROTO2.equals(getName(s));
+  }
+
+  /**
+   * Indicates whether the given {@code String} is equal to <code>{@link #PROTO3}</code>.
+   * @param s the {@code String} to check.
+   * @return {@code true} if the given {@code String} is equal to "proto3," {@code false} otherwise.
+   */
+  public boolean isSpecifyingProto3Syntax(String s) {
+    return PROTO3.equals(s);
+  }
+
+  /**
+   * Indicates whether the given {@code Syntax} specifies proto3 syntax.
+   * @param s the {@code Syntax} to check.
+   * @return {@code true} if the given {@code Syntax} specifies "proto3," {@code false} otherwise.
+   */
+  public boolean isSpecifyingProto3Syntax(Syntax s) {
+    return PROTO3.equals(getName(s));
+  }
+
+  /**
+   * Returns the name of the syntax specified by this syntax element.
+   */
+  public String getName(Syntax s) {
+    return stringLiterals.getCombinedString(s.getName());
+  }
+
+  /**
+   * Sets the name of the syntax specified by this syntax element.
+   */
+  public void setName(Syntax syntax, String name) {
+    StringLiteral literal = ProtobufFactory.eINSTANCE.createStringLiteral();
+    literal.getChunks().add(Strings.quote(name));
+    syntax.setName(literal);
+  }
+}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ComplexTypeFinderStrategy.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ComplexTypeFinderStrategy.java
index 28a31a6..6b8b932 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ComplexTypeFinderStrategy.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ComplexTypeFinderStrategy.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Google Inc.
+ * Copyright (c) 2014 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
@@ -8,19 +8,11 @@
  */
 package com.google.eclipse.protobuf.scoping;
 
+import static com.google.common.collect.Sets.newHashSet;
 import static java.util.Collections.emptySet;
 import static org.eclipse.xtext.resource.EObjectDescription.create;
-import static com.google.common.collect.Sets.newHashSet;
 
-import java.util.Collection;
-import java.util.List;
-import java.util.Set;
-
-import org.eclipse.core.resources.IProject;
-import org.eclipse.emf.ecore.EObject;
-import org.eclipse.xtext.naming.QualifiedName;
-import org.eclipse.xtext.resource.IEObjectDescription;
-
+import com.google.eclipse.protobuf.model.util.Imports;
 import com.google.eclipse.protobuf.naming.LocalNamesProvider;
 import com.google.eclipse.protobuf.naming.NormalNamingStrategy;
 import com.google.eclipse.protobuf.protobuf.ComplexType;
@@ -29,6 +21,15 @@
 import com.google.eclipse.protobuf.util.EResources;
 import com.google.inject.Inject;
 
+import org.eclipse.core.resources.IProject;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.xtext.naming.QualifiedName;
+import org.eclipse.xtext.resource.IEObjectDescription;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
 /**
  * @author alruiz@google.com (Alex Ruiz)
  */
@@ -38,6 +39,7 @@
   @Inject private LocalNamesProvider localNamesProvider;
   @Inject private NormalNamingStrategy namingStrategy;
   @Inject private QualifiedNameDescriptions qualifiedNamesDescriptions;
+  @Inject private Imports imports;
 
   @Override public Collection<IEObjectDescription> imported(Package fromImporter, Package fromImported, Object target,
       Class<? extends ComplexType> typeOfComplexType) {
@@ -55,7 +57,7 @@
       Class<? extends ComplexType> typeOfComplexType) {
     IProject project = EResources.getProjectOf(anImport.eResource());
     Set<IEObjectDescription> descriptions = newHashSet();
-    ProtoDescriptor descriptor = descriptorProvider.descriptor(project, anImport.getImportURI());
+    ProtoDescriptor descriptor = descriptorProvider.descriptor(project, imports.getPath(anImport));
     for (ComplexType complexType : descriptor.allTypes()) {
       if (!typeOfComplexType.isInstance(complexType)) {
         continue;
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/IImportResolver.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/IImportResolver.java
index 6a73dc3..5e1e4b5 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/IImportResolver.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/IImportResolver.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Google Inc.
+ * Copyright (c) 2014 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
@@ -20,14 +20,25 @@
 @ImplementedBy(NullImportResolver.class)
 public interface IImportResolver {
   /**
-   * Resolves the URI of the given {@code Import}. This method will update the URI of the given {@code Import} if it was
-   * successfully resolved.
+   * Returns the resolved path of the given {@code Import} or {@code null} if the path cannot be
+   * resolved.
+   *
    * @param anImport the given {@code Import}.
    */
-  void resolveAndUpdateUri(Import anImport);
+  String resolve(Import anImport);
+
+  /**
+   * Invalidates any cached results for the resolution of the given import.
+   */
+  void invalidateCacheFor(Import anImport);
 
   class NullImportResolver implements IImportResolver {
-    @Override public void resolveAndUpdateUri(Import anImport) {
+    @Override public String resolve(Import anImport) {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void invalidateCacheFor(Import anImport) {
       throw new UnsupportedOperationException();
     }
   }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufGlobalScopeProvider.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufGlobalScopeProvider.java
new file mode 100644
index 0000000..9f10eb0
--- /dev/null
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufGlobalScopeProvider.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2014 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.scoping;
+
+import com.google.eclipse.protobuf.model.util.Protobufs;
+import com.google.eclipse.protobuf.model.util.Resources;
+import com.google.eclipse.protobuf.protobuf.Import;
+import com.google.eclipse.protobuf.protobuf.Protobuf;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.xtext.EcoreUtil2;
+import org.eclipse.xtext.scoping.impl.ImportUriGlobalScopeProvider;
+import org.eclipse.xtext.util.IAcceptor;
+import org.eclipse.xtext.util.IResourceScopeCache;
+
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+
+/**
+ * A global scope provider that reads each {@link Import} in the protobuf file, resolves it, and
+ * caches the result.
+ */
+public class ProtobufGlobalScopeProvider extends ImportUriGlobalScopeProvider {
+  @Inject
+  private IResourceScopeCache cache;
+
+  @Inject
+  private Resources resources;
+
+  @Inject
+  private Protobufs protobufs;
+
+  @Inject
+  private IImportResolver importResolver;
+
+  @Override
+  protected LinkedHashSet<URI> getImportedUris(final Resource resource) {
+    return cache.get(ProtobufGlobalScopeProvider.class.getName(), resource,
+        new Provider<LinkedHashSet<URI>>() {
+          @Override
+          public LinkedHashSet<URI> get() {
+            final LinkedHashSet<URI> uniqueImportURIs = new LinkedHashSet<>(5);
+            IAcceptor<String> collector = createURICollector(resource, uniqueImportURIs);
+            Protobuf protobuf = resources.rootOf(resource);
+            if (protobuf == null) {
+              return uniqueImportURIs;
+            }
+            for (Import anImport : protobufs.importsIn(protobuf)) {
+              collector.accept(importResolver.resolve(anImport));
+            }
+            Iterator<URI> uriIter = uniqueImportURIs.iterator();
+            while (uriIter.hasNext()) {
+              if (!EcoreUtil2.isValidUri(resource, uriIter.next())) {
+                uriIter.remove();
+              }
+            }
+            return uniqueImportURIs;
+          }
+        });
+  }
+}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufImportUriResolver.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufImportUriResolver.java
deleted file mode 100644
index b3dfcec..0000000
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufImportUriResolver.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.scoping;
-
-import org.eclipse.emf.ecore.EObject;
-import org.eclipse.xtext.scoping.impl.ImportUriResolver;
-
-import com.google.eclipse.protobuf.protobuf.Import;
-import com.google.inject.Inject;
-
-/**
- * This implementation mimics how protoc understands URIs of imported files. For example, the URI
- * "platform:/resource/proto1.proto" is understood by EMF but not by protoc. The URI in the proto file needs to be
- * simply "proto1.proto" for protoc to understand it.
- * <p>
- * This {@link ImportUriResolver} adds "platform:/resource" to any URI if is not specified, so EMF can find the imported
- * resource.
- * </p>
- *
- * @author alruiz@google.com (Alex Ruiz)
- */
-public class ProtobufImportUriResolver extends ImportUriResolver {
-  @Inject private IImportResolver delegate;
-
-  /**
-   * If the given {@code EObject} is a <code>{@link Import}</code>, this method will add "platform:/resource" to the URI
-   * of such import if not specified already.
-   * @param from the given element to resolve.
-   * @return the {@code String} representation of the given object's {@code URI}.
-   */
-  @Override public String apply(EObject from) {
-    if (from instanceof Import) {
-      Import anImport = (Import) from;
-      delegate.resolveAndUpdateUri(anImport);
-    }
-    return super.apply(from);
-  }
-}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/ImportValidator.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/ImportValidator.java
index 7ae7e45..f03df7f 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/ImportValidator.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/ImportValidator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Google Inc.
+ * Copyright (c) 2014 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
@@ -8,26 +8,13 @@
  */
 package com.google.eclipse.protobuf.validation;
 
-import static java.lang.String.format;
-
-import static org.eclipse.xtext.util.Tuples.pair;
-
 import static com.google.common.collect.Maps.newHashMap;
 import static com.google.common.collect.Sets.newHashSet;
-import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.IMPORT__IMPORT_URI;
+import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.IMPORT__PATH;
 import static com.google.eclipse.protobuf.validation.Messages.importNotFound;
 import static com.google.eclipse.protobuf.validation.Messages.importingUnsupportedSyntax;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
-import org.eclipse.emf.ecore.resource.Resource;
-import org.eclipse.xtext.scoping.impl.ImportUriResolver;
-import org.eclipse.xtext.util.Pair;
-import org.eclipse.xtext.validation.AbstractDeclarativeValidator;
-import org.eclipse.xtext.validation.Check;
-import org.eclipse.xtext.validation.EValidatorRegistrar;
+import static java.lang.String.format;
+import static org.eclipse.xtext.util.Tuples.pair;
 
 import com.google.eclipse.protobuf.model.util.Imports;
 import com.google.eclipse.protobuf.model.util.Protobufs;
@@ -36,6 +23,16 @@
 import com.google.eclipse.protobuf.protobuf.Protobuf;
 import com.google.inject.Inject;
 
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.xtext.util.Pair;
+import org.eclipse.xtext.validation.AbstractDeclarativeValidator;
+import org.eclipse.xtext.validation.Check;
+import org.eclipse.xtext.validation.EValidatorRegistrar;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
 /**
  * Verifies that "imports" contain correct values.
  *
@@ -45,7 +42,6 @@
   @Inject private Imports imports;
   @Inject private Protobufs protobufs;
   @Inject private Resources resources;
-  @Inject private ImportUriResolver uriResolver;
 
   @Override public void register(EValidatorRegistrar registrar) {}
 
@@ -115,7 +111,7 @@
   }
 
   private void warnUnsupportedImportFoundIn(Import anImport) {
-    warning(importingUnsupportedSyntax, anImport, IMPORT__IMPORT_URI, INSIGNIFICANT_INDEX);
+    warning(importingUnsupportedSyntax, anImport, IMPORT__PATH, INSIGNIFICANT_INDEX);
   }
 
   /**
@@ -124,14 +120,12 @@
    *
    * @param anImport the given {@code Import}.
    */
-  @Check public void checkUriIsResolved(Import anImport) {
+  @Check
+  public void checkUriIsResolved(Import anImport) {
     if (imports.isResolved(anImport)) {
       return;
     }
-    uriResolver.apply(anImport);
-    if (!imports.isResolved(anImport)) {
-      error(format(importNotFound, anImport.getImportURI()), IMPORT__IMPORT_URI);
-    }
+    error(format(importNotFound, imports.getPath(anImport)), IMPORT__PATH);
   }
 
   private static enum HasKnownSyntax {
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/ProtobufJavaValidator.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/ProtobufJavaValidator.java
index 3ec975b..286c736 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/ProtobufJavaValidator.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/ProtobufJavaValidator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011 Google Inc.
+ * Copyright (c) 2014 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
@@ -28,9 +28,9 @@
 import static com.google.eclipse.protobuf.validation.Messages.unrecognizedSyntaxIdentifier;
 import static java.lang.String.format;
 
-import com.google.eclipse.protobuf.grammar.Syntaxes;
 import com.google.eclipse.protobuf.model.util.IndexedElements;
 import com.google.eclipse.protobuf.model.util.Protobufs;
+import com.google.eclipse.protobuf.model.util.Syntaxes;
 import com.google.eclipse.protobuf.naming.NameResolver;
 import com.google.eclipse.protobuf.protobuf.IndexedElement;
 import com.google.eclipse.protobuf.protobuf.MapType;
@@ -74,6 +74,7 @@
   @Inject private NameResolver nameResolver;
   @Inject private Protobufs protobufs;
   @Inject private IQualifiedNameProvider qualifiedNameProvider;
+  @Inject private Syntaxes syntaxes;
 
   @Check public void checkIsKnownSyntax(Protobuf protobuf) {
     if (!protobufs.hasKnownSyntax(protobuf)) {
@@ -82,10 +83,10 @@
   }
 
   @Check public void checkSyntaxIsKnown(Syntax syntax) {
-    String name = syntax.getName();
-    if (Syntaxes.proto2().equals(name) || Syntaxes.proto3().equals(name)) {
+    if (syntaxes.isSpecifyingProto2Syntax(syntax) || syntaxes.isSpecifyingProto3Syntax(syntax)) {
       return;
     }
+    String name = syntaxes.getName(syntax);
     String msg = (name == null) ? expectedSyntaxIdentifier : format(unrecognizedSyntaxIdentifier, name);
     error(msg, syntax, SYNTAX__NAME, SYNTAX_IS_NOT_KNOWN_ERROR);
   }
@@ -94,10 +95,10 @@
     if (isNameNull(e)) {
       return; // we already show an error if name is null, no need to go further.
     }
-    
+
     EObject container = e.eContainer();
     if (container instanceof OneOf) {
-      container = container.eContainer();  
+      container = container.eContainer();
     }
     if (container instanceof Message) {
       Message message = (Message) container;
@@ -142,7 +143,7 @@
       container = container.eContainer();
     }
     if (container instanceof Protobuf) {
-      return Syntaxes.isSpecifyingProto2Syntax(((Protobuf) container).getSyntax().getName());
+      return syntaxes.isSpecifyingProto2Syntax(((Protobuf) container).getSyntax());
     }
     return false;
   }
@@ -153,12 +154,12 @@
       container = container.eContainer();
     }
     if (container instanceof Protobuf) {
-      return Syntaxes.isSpecifyingProto3Syntax(((Protobuf) container).getSyntax().getName());
+      return syntaxes.isSpecifyingProto3Syntax(((Protobuf) container).getSyntax());
     }
     return false;
   }
 
-  private boolean checkTagNumberIsUnique(IndexedElement e, EObject message, 
+  private boolean checkTagNumberIsUnique(IndexedElement e, EObject message,
       Iterable<MessageElement> elements) {
     long index = indexedElements.indexOf(e);
 
@@ -174,13 +175,14 @@
         }
         if (indexedElements.indexOf(other) == index) {
           QualifiedName messageName = qualifiedNameProvider.getFullyQualifiedName(message);
-          String msg = format(fieldNumberAlreadyUsed, index, messageName.toString(), nameResolver.nameOf(other));
+          String msg = format(fieldNumberAlreadyUsed, index, messageName.toString(),
+              nameResolver.nameOf(other));
           invalidTagNumberError(msg, e);
           return false;
         }
       }
     }
-    
+
     return true;
   }
 
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/ProtobufSyntaxErrorMessageProvider.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/ProtobufSyntaxErrorMessageProvider.java
index 4bdfebe..16b68bf 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/ProtobufSyntaxErrorMessageProvider.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/validation/ProtobufSyntaxErrorMessageProvider.java
@@ -31,9 +31,6 @@
     if (currentContext instanceof MessageField) {
       message = mapToProtocMessage(message, (MessageField) currentContext);
     }
-    if (currentContext == null && message.contains("RULE_STRING")) {
-      return null;
-    }
     return new SyntaxErrorMessage(message, SYNTAX_DIAGNOSTIC);
   }