Fix broken scoping of provided descriptor.proto

* Fixes bug https://github.com/google/protobuf-dt/issues/4
* Removes changes from https://github.com/google/protobuf-dt/commit/d380a66e5b08981955090ec84d79cc3feb00a21d

Change-Id: I990c17d69686c7e90160cddb741ea9e114da2dd3
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 4668d91..990696b 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
@@ -10,21 +10,14 @@
 
 import static com.google.eclipse.protobuf.junit.core.GeneratedProtoFiles.protoFile;
 import static org.eclipse.xtext.util.Strings.isEmpty;
-
-import com.google.eclipse.protobuf.model.util.Imports;
-import com.google.eclipse.protobuf.preferences.general.PreferenceNames;
-import com.google.eclipse.protobuf.protobuf.Import;
-import com.google.eclipse.protobuf.scoping.IImportResolver;
-import com.google.eclipse.protobuf.scoping.IUriResolver;
-import com.google.inject.Inject;
+import java.io.File;
 
 import org.eclipse.emf.common.util.URI;
 import org.eclipse.emf.ecore.EReference;
-import org.eclipse.jface.preference.IPreferenceStore;
-import org.eclipse.xtext.ui.editor.preferences.IPreferenceStoreAccess;
-import org.mockito.Mockito;
-
-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;
 
 /**
  * Guice module for unit testing.
@@ -42,22 +35,6 @@
   protected void configure() {
     binder().bind(IImportResolver.class).to(ImportResolver.class);
     mockAndBind(EReference.class);
-    IPreferenceStoreAccess mockStoreAccess = Mockito.mock(IPreferenceStoreAccess.class);
-    IUriResolver mockUriResolver = Mockito.mock(IUriResolver.class);
-    binder().bind(IPreferenceStoreAccess.class).toInstance(mockStoreAccess);
-    // TODO (atrookey) Get rid of the excessive mocking.
-    binder().bind(IUriResolver.class).toInstance(mockUriResolver);
-    IPreferenceStore mockPreferenceStore = Mockito.mock(IPreferenceStore.class);
-    Mockito.when(mockStoreAccess.getWritablePreferenceStore(Mockito.anyObject()))
-        .thenReturn(mockPreferenceStore);
-    Mockito.when(mockPreferenceStore.getString(PreferenceNames.DESCRIPTOR_PROTO_PATH))
-        .thenReturn(PreferenceNames.DEFAULT_DESCRIPTOR_PATH);
-    Mockito.when(
-            mockUriResolver.resolveUri(
-                Mockito.eq(PreferenceNames.DEFAULT_DESCRIPTOR_PATH),
-                Mockito.anyObject(),
-                Mockito.anyObject()))
-        .thenReturn("platform:/plugin/com.google.eclipse.protobuf/descriptor.proto");
   }
 
   private static class ImportResolver implements IImportResolver {
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/model/util/MessageFields_mapEntryTypeOf_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/model/util/MessageFields_mapEntryTypeOf_Test.java
deleted file mode 100644
index 0bdb09f..0000000
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/model/util/MessageFields_mapEntryTypeOf_Test.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (c) 2015 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 org.hamcrest.core.IsEqual.equalTo;
-import static org.hamcrest.core.IsNull.nullValue;
-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 org.junit.Rule;
-import org.junit.Test;
-
-import com.google.eclipse.protobuf.junit.core.XtextRule;
-import com.google.eclipse.protobuf.protobuf.MessageField;
-import com.google.inject.Inject;
-
-/**
- * Tests for <code>{@link MessageFields#mapEntryTypeOf(MessageField)}</code>.
- *
- * @author jogl@google.com (John Glassmyer)
- */
-public class MessageFields_mapEntryTypeOf_Test {
-  @Rule public XtextRule xtext = overrideRuntimeModuleWith(unitTestModule());
-
-  @Inject private MessageFields fields;
-
-  // syntax = "proto2";
-  //
-  // message Person {
-  //   optional string name = 1;
-  // }
-  @Test public void should_return_null_for_scalar() {
-    MessageField field = xtext.find("name", MessageField.class);
-    assertThat(fields.mapEntryTypeOf(field), nullValue());
-  }
-
-  // syntax = "proto2";
-  //
-  // message Person {
-  //   optional string name = 1;
-  //   optional PhoneNumber number = 2;
-  //
-  //   message PhoneNumber {
-  //     optional string value = 1;
-  //   }
-  // }
-  @Test public void should_return_null_for_message() {
-    MessageField field = xtext.find("number", MessageField.class);
-    assertThat(fields.mapEntryTypeOf(field), nullValue());
-  }
-
-  // syntax = "proto2";
-  //
-  // message Person {
-  //   optional map<string, PhoneNumber> phone_book = 1;
-  //
-  //   message PhoneNumber {
-  //     optional string value = 1;
-  //   }
-  // }
-  @Test public void should_return_entry_type_for_map() {
-    MessageField field = xtext.find("phone_book", MessageField.class);
-    assertThat(fields.mapEntryTypeOf(field).getName(), equalTo("MapEntry"));
-  }
-}
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtoDescriptor_availableOptionsFor_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtoDescriptor_availableOptionsFor_Test.java
deleted file mode 100644
index 4d29754..0000000
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtoDescriptor_availableOptionsFor_Test.java
+++ /dev/null
@@ -1,103 +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 static com.google.common.collect.Maps.newHashMap;
-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.junit.matchers.FieldHasType.isBool;
-import static com.google.eclipse.protobuf.junit.matchers.FieldHasType.isString;
-import static org.hamcrest.core.IsEqual.equalTo;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.mock;
-
-import com.google.eclipse.protobuf.junit.core.XtextRule;
-import com.google.eclipse.protobuf.protobuf.Import;
-import com.google.eclipse.protobuf.protobuf.Message;
-import com.google.eclipse.protobuf.protobuf.MessageField;
-import com.google.eclipse.protobuf.protobuf.Protobuf;
-import com.google.inject.Inject;
-
-import org.eclipse.emf.ecore.EObject;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-import java.util.Collection;
-import java.util.Map;
-
-/**
- * Tests for <code>{@link ProtoDescriptor#availableOptionsFor(EObject)}</code>.
- *
- * @author alruiz@google.com (Alex Ruiz)
- */
-public class ProtoDescriptor_availableOptionsFor_Test {
-  @Rule public XtextRule xtext = overrideRuntimeModuleWith(integrationTestModule());
-
-  @Inject private ProtoDescriptorProvider descriptorProvider;
-  private Options options;
-  private ProtoDescriptor descriptor;
-
-  @Before public void setUp() {
-    options = new Options();
-    descriptor = descriptorProvider.primaryDescriptor(null);
-  }
-
-  @Test public void should_return_all_file_options() {
-    Protobuf optionContainer = mock(Protobuf.class);
-    options.mapByName(descriptor.availableOptionsFor(optionContainer));
-    assertThat(options.option("java_package"), isString());
-    assertThat(options.option("java_outer_classname"), isString());
-    assertThat(options.option("java_multiple_files"), isBool());
-    assertThat(options.option("java_generate_equals_and_hash"), isBool());
-    assertNotNull(options.option("optimize_for"));
-    assertThat(options.option("cc_generic_services"), isBool());
-    assertThat(options.option("java_generic_services"), isBool());
-    assertThat(options.option("py_generic_services"), isBool());
-  }
-
-  @Test public void should_return_all_message_options() {
-    Message optionContainer = mock(Message.class);
-    options.mapByName(descriptor.availableOptionsFor(optionContainer));
-    assertThat(options.option("message_set_wire_format"), isBool());
-    assertThat(options.option("no_standard_descriptor_accessor"), isBool());
-  }
-
-  @Test public void should_return_all_field_options() {
-    MessageField optionContainer = mock(MessageField.class);
-    options.mapByName(descriptor.availableOptionsFor(optionContainer));
-    assertNotNull(options.option("ctype"));
-    assertThat(options.option("packed"), isBool());
-    assertThat(options.option("deprecated"), isBool());
-    assertThat(options.option("experimental_map_key"), isString());
-  }
-
-  @Test public void should_return_empty_List_if_given_model_does_not_have_options() {
-    Import optionContainer = mock(Import.class);
-    Collection<MessageField> foundOptions = descriptor.availableOptionsFor(optionContainer);
-    assertThat(foundOptions.size(), equalTo(0));
-  }
-
-  private static class Options {
-    private final Map<String, MessageField> optionsByName = newHashMap();
-
-    void mapByName(Collection<MessageField> options) {
-      optionsByName.clear();
-      for (MessageField option : options) {
-        String name = option.getName();
-        optionsByName.put(name, option);
-      }
-    }
-
-    MessageField option(String name) {
-      return optionsByName.get(name);
-    }
-  }
-}
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtoDescriptor_enumTypeOf_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtoDescriptor_enumTypeOf_Test.java
deleted file mode 100644
index 137f118..0000000
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtoDescriptor_enumTypeOf_Test.java
+++ /dev/null
@@ -1,51 +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 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.scoping.OptionType.FILE;
-import static org.hamcrest.core.IsEqual.equalTo;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertThat;
-
-import com.google.eclipse.protobuf.junit.core.XtextRule;
-import com.google.eclipse.protobuf.protobuf.Enum;
-import com.google.eclipse.protobuf.protobuf.MessageField;
-import com.google.inject.Inject;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-/**
- * Tests for <code>{@link ProtoDescriptor#enumTypeOf(MessageField)}</code>.
- *
- * @author alruiz@google.com (Alex Ruiz)
- */
-public class ProtoDescriptor_enumTypeOf_Test {
-  @Rule public XtextRule xtext = overrideRuntimeModuleWith(integrationTestModule());
-
-  @Inject private ProtoDescriptorProvider descriptorProvider;
-  private ProtoDescriptor descriptor;
-
-  @Before public void setUp() {
-    descriptor = descriptorProvider.primaryDescriptor(null);
-  }
-
-  @Test public void should_return_Enum_if_field_type_is_enum() {
-    MessageField option = descriptor.option("optimize_for", FILE);
-    Enum anEnum = descriptor.enumTypeOf(option);
-    assertThat(anEnum.getName(), equalTo("OptimizeMode"));
-  }
-
-  @Test public void should_return_null_if_field_is_null() {
-    assertNull(descriptor.enumTypeOf(null));
-  }
-}
diff --git a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_ComplexTypeLink_target_Test.java b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_ComplexTypeLink_target_Test.java
index 6b0b1f5..8062b5d 100644
--- a/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_ComplexTypeLink_target_Test.java
+++ b/com.google.eclipse.protobuf.test/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider_scope_ComplexTypeLink_target_Test.java
@@ -10,27 +10,25 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.when;
-
 import static com.google.eclipse.protobuf.junit.IEObjectDescriptions.descriptionsIn;
 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.junit.matchers.ContainAllNames.containAll;
-import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.COMPLEX_TYPE;
-import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.COMPLEX_TYPE_LINK;
 import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.COMPLEX_TYPE_LINK__TARGET;
 
 import org.eclipse.emf.ecore.EReference;
 import org.eclipse.xtext.naming.QualifiedName;
 import org.eclipse.xtext.scoping.IScope;
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 
 import com.google.eclipse.protobuf.junit.core.XtextRule;
 import com.google.eclipse.protobuf.protobuf.Enum;
+import com.google.eclipse.protobuf.protobuf.MapType;
+import com.google.eclipse.protobuf.protobuf.MapTypeLink;
 import com.google.eclipse.protobuf.protobuf.ComplexTypeLink;
 import com.google.eclipse.protobuf.protobuf.MessageField;
+import com.google.eclipse.protobuf.protobuf.TypeLink;
 import com.google.inject.Inject;
 
 /**
@@ -41,15 +39,8 @@
 public class ProtobufScopeProvider_scope_ComplexTypeLink_target_Test {
   @Rule public XtextRule xtext = overrideRuntimeModuleWith(integrationTestModule());
 
-  @Inject private EReference reference;
   @Inject private ProtobufScopeProvider scopeProvider;
 
-  @Before
-  public void setup() {
-    when(reference.getEReferenceType()).thenReturn(COMPLEX_TYPE);
-    when(reference.getEContainingClass()).thenReturn(COMPLEX_TYPE_LINK);
-  }
-
   // syntax = "proto2";
   // package com.google.proto;
   //
@@ -70,7 +61,7 @@
   // }
   @Test public void should_provide_Types() {
     MessageField field = xtext.find("type", MessageField.class);
-    IScope scope = scopeProvider.getScope(typeOf(field), reference);
+    IScope scope = scopeProvider.getScope(typeOf(field), COMPLEX_TYPE_LINK__TARGET);
     assertThat(descriptionsIn(scope), containAll("Type", "proto.Type", "google.proto.Type",
         "com.google.proto.Type", "Address", "proto.Address", "google.proto.Address",
         "com.google.proto.Address", "Contact", "proto.Contact", "google.proto.Contact",
@@ -104,7 +95,7 @@
   // }
   @Test public void should_provide_imported_Types() {
     MessageField field = xtext.find("type", " =", MessageField.class);
-    IScope scope = scopeProvider.getScope(typeOf(field), reference);
+    IScope scope = scopeProvider.getScope(typeOf(field), COMPLEX_TYPE_LINK__TARGET);
     assertThat(descriptionsIn(scope), containAll("test.proto.Type", "test.proto.Address",
         "Contact", "proto.Contact", "google.proto.Contact", "com.google.proto.Contact"));
   }
@@ -136,7 +127,7 @@
   // }
   @Test public void should_provide_imported_Types_with_equal_package() {
     MessageField field = xtext.find("type", " =", MessageField.class);
-    IScope scope = scopeProvider.getScope(typeOf(field), reference);
+    IScope scope = scopeProvider.getScope(typeOf(field), COMPLEX_TYPE_LINK__TARGET);
     assertThat(descriptionsIn(scope), containAll("Type", "proto.Type", "google.proto.Type",
         "com.google.proto.Type", "Address", "proto.Address", "google.proto.Address",
         "com.google.proto.Address", "Contact", "proto.Contact", "google.proto.Contact",
@@ -170,7 +161,7 @@
   // }
   @Test public void should_provide_public_imported_Types() {
     MessageField field = xtext.find("type", " =", MessageField.class);
-    IScope scope = scopeProvider.getScope(typeOf(field), reference);
+    IScope scope = scopeProvider.getScope(typeOf(field), COMPLEX_TYPE_LINK__TARGET);
     assertThat(descriptionsIn(scope), containAll("test.proto.Type", "test.proto.Address",
         "Contact", "proto.Contact", "google.proto.Contact", "com.google.proto.Contact"));
   }
@@ -209,7 +200,7 @@
   // }
   @Test public void should_provide_public_imported_Types_with_more_than_one_level() {
     MessageField field = xtext.find("type", " =", MessageField.class);
-    IScope scope = scopeProvider.getScope(typeOf(field), reference);
+    IScope scope = scopeProvider.getScope(typeOf(field), COMPLEX_TYPE_LINK__TARGET);
     assertThat(descriptionsIn(scope), containAll("test.proto.Type", "test.proto.Address",
         "Contact", "proto.Contact", "google.proto.Contact", "com.google.proto.Contact"));
   }
@@ -239,11 +230,29 @@
     IScope scope = scopeProvider.getScope(field, COMPLEX_TYPE_LINK__TARGET);
     Object scopedEnum =
         scope
-            .getSingleElement(QualifiedName.create("sample", "proto", "foo", "Fruit"))
-            .getEObjectOrProxy();
+        .getSingleElement(QualifiedName.create("sample", "proto", "foo", "Fruit"))
+        .getEObjectOrProxy();
     assertEquals(expectedEnum, scopedEnum);
   }
 
+  //  syntax = "proto2";
+  //  package sample.proto.foo;
+  //
+  //  message Foo {
+  //    map<string, Bar> mapField = 1;
+  //  }
+  //
+  //  message Bar {}
+  @Test public void should_provide_ComplexType_for_map_value() {
+    MessageField mapField = xtext.find("mapField", " =", MessageField.class);
+    MapType mapType = ((MapTypeLink) mapField.getType()).getTarget();
+    TypeLink valueType = mapType.getValueType();
+    IScope scope = scopeProvider.getScope(valueType, COMPLEX_TYPE_LINK__TARGET);
+    assertThat(descriptionsIn(scope), containAll("Foo", "proto.foo.Foo", "foo.Foo",
+        "Bar", "proto.foo.Bar", "foo.Bar", "sample.proto.foo.Foo",
+        "sample.proto.foo.Bar"));
+  }
+
   private static ComplexTypeLink typeOf(MessageField field) {
     return (ComplexTypeLink) field.getType();
   }
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 9f87e74..0deeb76 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
@@ -8,9 +8,7 @@
  */
 package com.google.eclipse.protobuf.ui.contentassist;
 
-import static com.google.common.collect.Lists.newArrayList;
 import static com.google.eclipse.protobuf.grammar.CommonKeyword.CLOSING_BRACKET;
-import static com.google.eclipse.protobuf.grammar.CommonKeyword.DEFAULT;
 import static com.google.eclipse.protobuf.grammar.CommonKeyword.EQUAL;
 import static com.google.eclipse.protobuf.grammar.CommonKeyword.FALSE;
 import static com.google.eclipse.protobuf.grammar.CommonKeyword.NAN;
@@ -19,12 +17,8 @@
 import static com.google.eclipse.protobuf.grammar.CommonKeyword.SYNTAX;
 import static com.google.eclipse.protobuf.grammar.CommonKeyword.TRUE;
 import static com.google.eclipse.protobuf.protobuf.ModifierEnum.OPTIONAL;
-import static com.google.eclipse.protobuf.protobuf.ModifierEnum.REPEATED;
 import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.LITERAL;
-import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.OPTION;
-import static com.google.eclipse.protobuf.ui.grammar.CompoundElement.DEFAULT_EQUAL;
 import static com.google.eclipse.protobuf.ui.grammar.CompoundElement.DEFAULT_EQUAL_IN_BRACKETS;
-import static com.google.eclipse.protobuf.ui.grammar.CompoundElement.DEFAULT_EQUAL_STRING;
 import static com.google.eclipse.protobuf.ui.grammar.CompoundElement.DEFAULT_EQUAL_STRING_IN_BRACKETS;
 import static com.google.eclipse.protobuf.ui.grammar.CompoundElement.EMPTY_STRING;
 import static com.google.eclipse.protobuf.ui.grammar.CompoundElement.EQUAL_PROTO2_IN_QUOTES;
@@ -33,7 +27,6 @@
 import static com.google.eclipse.protobuf.ui.grammar.CompoundElement.PROTO3_IN_QUOTES;
 import static com.google.eclipse.protobuf.util.CommonWords.space;
 import static java.lang.String.valueOf;
-import static java.util.Collections.emptyList;
 import static org.eclipse.xtext.EcoreUtil2.getAllContentsOfType;
 import static org.eclipse.xtext.util.Strings.toFirstLower;
 
@@ -49,20 +42,14 @@
 import com.google.eclipse.protobuf.protobuf.DefaultValueFieldOption;
 import com.google.eclipse.protobuf.protobuf.Enum;
 import com.google.eclipse.protobuf.protobuf.FieldName;
-import com.google.eclipse.protobuf.protobuf.FieldOption;
 import com.google.eclipse.protobuf.protobuf.IndexedElement;
 import com.google.eclipse.protobuf.protobuf.Literal;
 import com.google.eclipse.protobuf.protobuf.MessageField;
 import com.google.eclipse.protobuf.protobuf.ModifierEnum;
-import com.google.eclipse.protobuf.protobuf.NativeFieldOption;
-import com.google.eclipse.protobuf.protobuf.NativeOption;
 import com.google.eclipse.protobuf.protobuf.Option;
 import com.google.eclipse.protobuf.protobuf.SimpleValueField;
-import com.google.eclipse.protobuf.scoping.ProtoDescriptor;
-import com.google.eclipse.protobuf.scoping.ProtoDescriptorProvider;
 import com.google.eclipse.protobuf.ui.grammar.CompoundElement;
 import com.google.eclipse.protobuf.ui.labeling.Images;
-import com.google.eclipse.protobuf.util.EResources;
 import com.google.inject.Inject;
 
 import org.eclipse.emf.ecore.EObject;
@@ -78,9 +65,6 @@
 import org.eclipse.xtext.ui.editor.contentassist.ContentAssistContext;
 import org.eclipse.xtext.ui.editor.contentassist.ICompletionProposalAcceptor;
 
-import java.util.Collection;
-import java.util.List;
-
 
 /**
  * @author alruiz@google.com (Alex Ruiz)
@@ -88,7 +72,6 @@
  * @see <a href="http://www.eclipse.org/Xtext/documentation/310_eclipse_support.html#content-assist">Xtext Content Assist</a>
  */
 public class ProtobufProposalProvider extends AbstractProtobufProposalProvider {
-  @Inject private ProtoDescriptorProvider descriptorProvider;
   @Inject private Images images;
   @Inject private IndexedElements indexedElements;
   @Inject private PluginImageHelper imageHelper;
@@ -114,40 +97,6 @@
     proposeAndAccept(proposal, imageHelper.getImage(images.imageFor(SYNTAX)), context, acceptor);
   }
 
-  @Override public void completeNativeOption_Source(EObject model, Assignment assignment,
-      ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
-    ProtoDescriptor descriptor =
-        descriptorProvider.primaryDescriptor(EResources.getProjectOf(model.eResource()));
-    Collection<MessageField> optionProperties = descriptor.availableOptionsFor(model);
-    if (!optionProperties.isEmpty()) {
-      proposeOptions(optionProperties, context, acceptor);
-    }
-  }
-
-  private void proposeOptions(Collection<MessageField> optionSources, ContentAssistContext context,
-      ICompletionProposalAcceptor acceptor) {
-    for (MessageField source : optionSources) {
-      proposeOption(source, context, acceptor);
-    }
-  }
-
-  @Override public void completeNativeOption_Value(EObject model, Assignment assignment,
-      ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
-    NativeOption option = (NativeOption) model;
-    MessageField optionSource = (MessageField) options.rootSourceOf(option);
-    if (optionSource == null) {
-      return;
-    }
-    ProtoDescriptor descriptor =
-        descriptorProvider.primaryDescriptor(EResources.getProjectOf(model.eResource()));
-    Enum enumType = descriptor.enumTypeOf(optionSource);
-    if (enumType != null) {
-      proposeAndAccept(enumType, context, acceptor);
-      return;
-    }
-    proposePrimitiveValues(optionSource, context, acceptor);
-  }
-
   @Override public void complete_ID(EObject model, RuleCall ruleCall, ContentAssistContext context,
       ICompletionProposalAcceptor acceptor) {}
 
@@ -356,93 +305,6 @@
     return imageHelper.getImage(images.defaultImage());
   }
 
-  @Override public void completeNativeFieldOption_Source(EObject model, Assignment assignment,
-      ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
-    MessageField field = extractElementFromContext(context, MessageField.class);
-    if (field != null) {
-      proposeNativeOptions(field, context, acceptor);
-    }
-  }
-
-  private void proposeNativeOptions(MessageField field, ContentAssistContext context,
-      ICompletionProposalAcceptor acceptor) {
-    List<String> optionNames = existingFieldOptionNames(field);
-    proposeDefaultKeyword(field, optionNames, context, acceptor);
-    ProtoDescriptor descriptor =
-        descriptorProvider.primaryDescriptor(EResources.getProjectOf(context.getResource()));
-    for (MessageField optionSource : descriptor.availableOptionsFor(field)) {
-      String optionName = optionSource.getName();
-      if (optionNames.contains(optionName) || ("packed".equals(optionName) && !canBePacked(field))) {
-        continue;
-      }
-      proposeOption(optionSource, context, acceptor);
-    }
-  }
-
-  private List<String> existingFieldOptionNames(IndexedElement e) {
-    List<FieldOption> allFieldOptions = indexedElements.fieldOptionsOf(e);
-    if (allFieldOptions.isEmpty()) {
-      return emptyList();
-    }
-    List<String> optionNames = newArrayList();
-    for (FieldOption option : allFieldOptions) {
-      optionNames.add(options.nameOf(option));
-    }
-    return optionNames;
-  }
-
-  private void proposeDefaultKeyword(IndexedElement e, List<String> existingOptionNames, ContentAssistContext context,
-      ICompletionProposalAcceptor acceptor) {
-    if (e instanceof MessageField) {
-      MessageField field = (MessageField) e;
-      if (!messageFields.isOptional(field) || existingOptionNames.contains(DEFAULT.toString())) {
-        return;
-      }
-      CompoundElement display = DEFAULT_EQUAL;
-      int cursorPosition = display.charCount();
-      if (messageFields.isString(field)) {
-        display = DEFAULT_EQUAL_STRING;
-        cursorPosition++;
-      }
-      createAndAccept(display, cursorPosition, context, acceptor);
-    }
-  }
-
-  private boolean canBePacked(IndexedElement e) {
-    if (e instanceof MessageField) {
-      MessageField field = (MessageField) e;
-      return messageFields.isPrimitive(field) && REPEATED.equals(field.getModifier());
-    }
-    return false;
-  }
-
-  private void proposeOption(MessageField optionSource, ContentAssistContext context,
-      ICompletionProposalAcceptor acceptor) {
-    String displayString = optionSource.getName();
-    String proposalText = displayString + space() + EQUAL + space();
-    Object value = defaultValueOf(optionSource);
-    if (value != null) {
-      proposalText = proposalText + value;
-    }
-    ICompletionProposal proposal = createCompletionProposal(proposalText, displayString, imageForOption(), context);
-    if (value == EMPTY_STRING && proposal instanceof ConfigurableCompletionProposal) {
-      // set cursor between the proposal's quotes
-      ConfigurableCompletionProposal configurable = (ConfigurableCompletionProposal) proposal;
-      configurable.setCursorPosition(proposalText.length() - 1);
-    }
-    acceptor.accept(proposal);
-  }
-
-  private Object defaultValueOf(MessageField field) {
-    if (messageFields.isBool(field)) {
-      return TRUE;
-    }
-    if (messageFields.isString(field)) {
-      return EMPTY_STRING;
-    }
-    return null;
-  }
-
   @Override public void completeDefaultValueFieldOption_Value(EObject model, Assignment assignment,
       ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
     MessageField field = null;
@@ -458,22 +320,6 @@
     proposeFieldValue(field, context, acceptor);
   }
 
-  @Override public void completeNativeFieldOption_Value(EObject model, Assignment assignment,
-      ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
-    if (model instanceof NativeFieldOption) {
-      NativeFieldOption option = (NativeFieldOption) model;
-      ProtoDescriptor descriptor =
-          descriptorProvider.primaryDescriptor(EResources.getProjectOf(context.getResource()));
-      MessageField field = (MessageField) options.rootSourceOf(option);
-      Enum enumType = descriptor.enumTypeOf(field);
-      if (enumType != null) {
-        proposeAndAccept(enumType, context, acceptor);
-        return;
-      }
-      proposePrimitiveValues(field, context, acceptor);
-    }
-  }
-
   private boolean proposePrimitiveValues(MessageField field, ContentAssistContext context,
       ICompletionProposalAcceptor acceptor) {
     if (messageFields.isBool(field)) {
@@ -513,10 +359,6 @@
     acceptor.accept(proposal);
   }
 
-  private Image imageForOption() {
-    return imageHelper.getImage(images.imageFor(OPTION));
-  }
-
   @Override public void completeOptionSource_Target(EObject model, Assignment assignment,
       ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
   }
diff --git a/com.google.eclipse.protobuf/build.properties b/com.google.eclipse.protobuf/build.properties
index a06b6f0..df8424a 100644
--- a/com.google.eclipse.protobuf/build.properties
+++ b/com.google.eclipse.protobuf/build.properties
@@ -5,6 +5,5 @@
                plugin.xml,\
                OSGI-INF/,\
                descriptor.proto,\
-               map_entry.proto,\
                schema/descriptor-source.exsd,\
                model/
diff --git a/com.google.eclipse.protobuf/map_entry.proto b/com.google.eclipse.protobuf/map_entry.proto
deleted file mode 100644
index 64ef6a5..0000000
--- a/com.google.eclipse.protobuf/map_entry.proto
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2015 Google Inc.  All rights reserved.
-syntax = "proto2";
-
-package google.protobuf;
-
-// An entry in a map field.
-//
-// Specified here as if the defining map field were "map<string, string>" as a temporary hack
-// until protobuf-dt is able to synthesize entry types for map fields with arbitary value types.
-// This avoids "Couldn't resolve" errors on "key" and "value" fields and enables completion of them.
-message MapEntry {
-  // The key of an entry in a map field.
-  //
-  // Specified here as "string" due to technical limitations in protobuf-dt. The actual type of
-  // this key field is specified by the defining map field.
-  optional string key = 1;
-
-  // The value of an entry in a map field.
-  //
-  // Specified here as "string" due to technical limitations in protobuf-dt. The actual type of
-  // this value field is specified by the defining map field.
-  optional string value = 2;
-}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/MessageFields.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/MessageFields.java
index 1107a3d..e882b82 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/MessageFields.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/model/util/MessageFields.java
@@ -37,8 +37,6 @@
 import com.google.eclipse.protobuf.protobuf.ScalarType;
 import com.google.eclipse.protobuf.protobuf.ScalarTypeLink;
 import com.google.eclipse.protobuf.protobuf.TypeLink;
-import com.google.eclipse.protobuf.scoping.ProtoDescriptorProvider;
-import com.google.inject.Inject;
 import com.google.inject.Singleton;
 
 /**
@@ -46,11 +44,12 @@
  *
  * @author alruiz@google.com (Alex Ruiz)
  */
-@Singleton public class MessageFields {
-  @Inject private ProtoDescriptorProvider descriptorProvider;
-
+@Singleton
+public class MessageFields {
   /**
-   * Indicates whether the modifier of the given field is <code>{@link ModifierEnum#OPTIONAL}</code>.
+   * Indicates whether the modifier of the given field is <code>{@link
+   * ModifierEnum#OPTIONAL}</code>.
+   *
    * @param field the given field.
    * @return {@code true} if the modifier of the given field is "optional," {@code false} otherwise.
    */
@@ -59,9 +58,11 @@
   }
 
   /**
-   * Indicates whether the type of the given field is primitive. Primitive types include: {@code double}, {@code float},
-   * {@code int32}, {@code int64}, {@code uint32}, {@code uint64}, {@code sint32}, {@code sint64}, {@code fixed32},
-   * {@code fixed64}, {@code sfixed32}, {@code sfixed64} and {@code bool}.
+   * Indicates whether the type of the given field is primitive. Primitive types include: {@code
+   * double}, {@code float}, {@code int32}, {@code int64}, {@code uint32}, {@code uint64}, {@code
+   * sint32}, {@code sint64}, {@code fixed32}, {@code fixed64}, {@code sfixed32}, {@code sfixed64}
+   * and {@code bool}.
+   *
    * @param field the given field.
    * @return {@code true} if the type of the given field is primitive, {@code false} otherwise.
    */
@@ -76,6 +77,7 @@
 
   /**
    * Indicates whether the given field is of type {@code bool}.
+   *
    * @param field the given field.
    * @return {@code true} if the given field is of type {@code bool}, {@code false} otherwise.
    */
@@ -85,6 +87,7 @@
 
   /**
    * Indicates whether the given field is of type {@code bytes}.
+   *
    * @param field the given field.
    * @return {@code true} if the given field is of type {@code bytes}, {@code false} otherwise.
    */
@@ -94,6 +97,7 @@
 
   /**
    * Indicates whether the given field is of type {@code float} or {@code double}.
+   *
    * @param field the given field.
    * @return {@code true} if the given field is a floating point number, {@code false} otherwise.
    */
@@ -102,17 +106,21 @@
   }
 
   /**
-   * Indicates whether the given field is of type {@code fixed32}, {@code fixed64}, {@code int32}, {@code int64},
-   * {@code sfixed32}, {@code sfixed64}, {@code sint32}, {@code sint64}, {@code uint32} or {@code uint64}.
+   * Indicates whether the given field is of type {@code fixed32}, {@code fixed64}, {@code int32},
+   * {@code int64}, {@code sfixed32}, {@code sfixed64}, {@code sint32}, {@code sint64}, {@code
+   * uint32} or {@code uint64}.
+   *
    * @param field the given field.
    * @return {@code true} if the given field is an integer, {@code false} otherwise.
    */
   public boolean isInteger(MessageField field) {
-    return isScalarType(field, FIXED32, FIXED64, INT32, INT64, SFIXED32, SFIXED64, SINT32, SINT64, UINT32, UINT64);
+    return isScalarType(
+        field, FIXED32, FIXED64, INT32, INT64, SFIXED32, SFIXED64, SINT32, SINT64, UINT32, UINT64);
   }
 
   /**
    * Indicates whether the given field is of type {@code string}.
+   *
    * @param field the given field.
    * @return {@code true} if the given field is of type {@code string}, {@code false} otherwise.
    */
@@ -121,7 +129,9 @@
   }
 
   /**
-   * Indicates whether the given field is of type {@code fixed32}, {@code fixed64}, {@code uint32} or {@code uint64}.
+   * Indicates whether the given field is of type {@code fixed32}, {@code fixed64}, {@code uint32}
+   * or {@code uint64}.
+   *
    * @param field the given field.
    * @return {@code true} if the given field is an unsigned integer, {@code false} otherwise.
    */
@@ -144,6 +154,7 @@
 
   /**
    * Returns the name of the type of the given field.
+   *
    * @param field the given field.
    * @return the name of the type of the given field.
    */
@@ -173,8 +184,10 @@
 
   /**
    * Returns the message type of the given field, only if the type of the given field is a message.
+   *
    * @param field the given field.
-   * @return the message type of the given field or {@code null} if the type of the given field is not message.
+   * @return the message type of the given field or {@code null} if the type of the given field is
+   *     not message.
    */
   public Message messageTypeOf(MessageField field) {
     ComplexType fieldType = complexTypeOf(field.getType());
@@ -183,8 +196,10 @@
 
   /**
    * Returns the enum type of the given field, only if the type of the given field is an enum.
+   *
    * @param field the given field.
-   * @return the enum type of the given field or {@code null} if the type of the given field is not enum.
+   * @return the enum type of the given field or {@code null} if the type of the given field is not
+   *     enum.
    */
   public Enum enumTypeOf(MessageField field) {
     ComplexType fieldType = complexTypeOf(field.getType());
@@ -197,8 +212,10 @@
 
   /**
    * Returns the scalar type of the given field, only if the type of the given field is a scalar.
+   *
    * @param field the given field.
-   * @return the scalar type of the given field or {@code null} if the type of the given field is not a scalar.
+   * @return the scalar type of the given field or {@code null} if the type of the given field is
+   *     not a scalar.
    */
   public ScalarType scalarTypeOf(MessageField field) {
     return scalarTypeOf(field.getType());
@@ -211,15 +228,4 @@
   private MapType mapTypeOf(TypeLink typeLink) {
     return typeLink instanceof MapTypeLink ? ((MapTypeLink) typeLink).getTarget() : null;
   }
-
-  /**
-   * Returns a Message representing an entry in a map of the given field's type, or null if the
-   * given field is not of map type.
-   */
-  public Message mapEntryTypeOf(MessageField field) {
-    // TODO(jogl): Dynamically create and return a message type with key and value fields matching
-    // the key and value types of the target MapType.
-    return field.getType() instanceof MapTypeLink
-        ? (Message) descriptorProvider.mapEntryDescriptor().allTypes().get(0) : null;
-  }
 }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/preferences/general/GeneralPreferences.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/preferences/general/GeneralPreferences.java
index bfabcd4..d7f7f20 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/preferences/general/GeneralPreferences.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/preferences/general/GeneralPreferences.java
@@ -15,19 +15,16 @@
 
 import org.eclipse.core.resources.IProject;
 import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.util.IPropertyChangeListener;
 import org.eclipse.xtext.ui.editor.preferences.IPreferenceStoreAccess;
 
-/**
- * @author alruiz@google.com (Alex Ruiz)
- */
+/** @author alruiz@google.com (Alex Ruiz) */
 public class GeneralPreferences {
   private final IPreferenceStore store;
 
   public GeneralPreferences(IPreferenceStoreAccess storeAccess, IProject project) {
     IPreferenceStore preferenceStore = storeAccess.getWritablePreferenceStore(project);
-    boolean enableProjectSettings =
-        preferenceStore.getBoolean(ENABLE_PROJECT_SETTINGS_PREFERENCE_NAME);
-    if (!enableProjectSettings) {
+    if (!preferenceStore.getBoolean(ENABLE_PROJECT_SETTINGS_PREFERENCE_NAME)) {
       preferenceStore = storeAccess.getWritablePreferenceStore();
     }
     this.store = preferenceStore;
@@ -41,6 +38,10 @@
     return store.getString(PreferenceNames.DESCRIPTOR_PROTO_PATH);
   }
 
+  public void addPropertyChangeListener(IPropertyChangeListener listener) {
+    store.addPropertyChangeListener(listener);
+  }
+
   public static class Initializer extends DefaultPreservingInitializer {
     @Override
     public void setDefaults() {
@@ -49,4 +50,4 @@
       setDefault(PreferenceNames.DESCRIPTOR_PROTO_PATH, PreferenceNames.DEFAULT_DESCRIPTOR_PATH);
     }
   }
-}
\ No newline at end of file
+}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/preferences/general/PreferenceNames.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/preferences/general/PreferenceNames.java
index 0d75034..7598f6e 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/preferences/general/PreferenceNames.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/preferences/general/PreferenceNames.java
@@ -8,14 +8,27 @@
  */
 package com.google.eclipse.protobuf.preferences.general;
 
-/**
- * Preference names for descriptor proto preferences.
- */
+/** Preference names for descriptor proto preferences. */
 public final class PreferenceNames {
+  /**
+   * Default value for the preference {@literal general.descriptorPath}.
+   */
   public static final String DEFAULT_DESCRIPTOR_PATH = "google/protobuf/descriptor.proto";
+
+  /**
+   * Preference storing the import path of the options definition file.
+   */
   public static final String DESCRIPTOR_PROTO_PATH = "general.descriptorPath";
+
+  /**
+   * Preference determining whether the project specific settings for the are enabled.
+   */
   public static final String ENABLE_PROJECT_SETTINGS_PREFERENCE_NAME =
       "general.enableProjectSettings";
+
+  /**
+   * Preference determining whether protocol buffer files are validated on activation.
+   */
   public static final String VALIDATE_FILES_ON_ACTIVATION = "general.validateFilesOnActivation";
 
   private PreferenceNames() {}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/OptionType.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/OptionType.java
index 2d773c9..140c530 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/OptionType.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/OptionType.java
@@ -10,7 +10,6 @@
 
 import static com.google.common.collect.Maps.newHashMap;
 
-import com.google.eclipse.protobuf.protobuf.AbstractOption;
 import com.google.eclipse.protobuf.protobuf.Enum;
 import com.google.eclipse.protobuf.protobuf.IndexedElement;
 import com.google.eclipse.protobuf.protobuf.Literal;
@@ -23,14 +22,22 @@
 import java.util.Map;
 import java.util.Map.Entry;
 
+import org.eclipse.emf.ecore.EObject;
+
 /**
  * Types of options (by location.)
  *
  * @author alruiz@google.com (Alex Ruiz)
  */
 enum OptionType {
-  FILE("FileOptions"), MESSAGE("MessageOptions"), FIELD("FieldOptions"), ENUM("EnumOptions"),
-  LITERAL("EnumValueOptions"), SERVICE("ServiceOptions"), RPC("MethodOptions"), STREAM("StreamOptions");
+  FILE("FileOptions"),
+  MESSAGE("MessageOptions"),
+  FIELD("FieldOptions"),
+  ENUM("EnumOptions"),
+  LITERAL("EnumValueOptions"),
+  SERVICE("ServiceOptions"),
+  RPC("MethodOptions"),
+  STREAM("StreamOptions");
 
   private static final Map<Class<?>, OptionType> OPTION_TYPES_BY_CONTAINER = newHashMap();
 
@@ -54,6 +61,7 @@
 
   /**
    * Returns the name of the message in descriptor.proto that specifies the type of an option.
+   *
    * @return the name of the message in descriptor.proto that specifies the type of an option.
    */
   String messageName() {
@@ -62,20 +70,20 @@
 
   /**
    * Returns the type of the given option.
-   * @param option the given option.
+   *
+   * @param container the {@code OptionSource}, {@code Option}, or container of an {@code Option}.
    * @return the type of the given option or {@code null} if a type cannot be found.
    */
-  static OptionType typeOf(AbstractOption option) {
-    return findOptionTypeForLevelOf(option.eContainer());
-  }
-
-  static OptionType findOptionTypeForLevelOf(Object container) {
-    for (Entry<Class<?>, OptionType> optionTypeByContainer : OPTION_TYPES_BY_CONTAINER.entrySet()) {
-      if (optionTypeByContainer.getKey().isInstance(container)) {
-        return optionTypeByContainer.getValue();
+  static OptionType typeOf(EObject container) {
+    if (container != null) {
+      for (Entry<Class<?>, OptionType> optionTypeByContainer :
+          OPTION_TYPES_BY_CONTAINER.entrySet()) {
+        if (optionTypeByContainer.getKey().isInstance(container)) {
+          return optionTypeByContainer.getValue();
+        }
       }
+      return typeOf(container.eContainer());
     }
-
     return null;
   }
 }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtoDescriptor.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtoDescriptor.java
deleted file mode 100644
index e1906e7..0000000
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtoDescriptor.java
+++ /dev/null
@@ -1,227 +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 static com.google.common.base.Strings.isNullOrEmpty;
-import static com.google.eclipse.protobuf.protobuf.ProtobufPackage.Literals.MESSAGE_FIELD__TYPE;
-import static com.google.eclipse.protobuf.scoping.OptionType.findOptionTypeForLevelOf;
-import static com.google.eclipse.protobuf.util.Encodings.UTF_8;
-import static java.util.Collections.emptyList;
-import static java.util.Collections.unmodifiableCollection;
-import static java.util.Collections.unmodifiableList;
-import static org.eclipse.xtext.EcoreUtil2.getAllContentsOfType;
-import static org.eclipse.xtext.EcoreUtil2.resolveLazyCrossReferences;
-import static org.eclipse.xtext.util.CancelIndicator.NullImpl;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.eclipse.protobuf.model.util.INodes;
-import com.google.eclipse.protobuf.protobuf.ComplexType;
-import com.google.eclipse.protobuf.protobuf.Enum;
-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.NativeOption;
-import com.google.eclipse.protobuf.protobuf.Protobuf;
-
-import org.eclipse.emf.common.util.URI;
-import org.eclipse.emf.ecore.EObject;
-import org.eclipse.xtext.nodemodel.INode;
-import org.eclipse.xtext.parser.IParseResult;
-import org.eclipse.xtext.parser.IParser;
-import org.eclipse.xtext.resource.XtextResource;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Contains the elements from descriptor.proto (provided with protobuf's library.)
- *
- * @author alruiz@google.com (Alex Ruiz)
- */
-public class ProtoDescriptor {
-  private static final Map<String, OptionType> OPTION_DEFINITION_BY_NAME = new HashMap<>();
-
-  static {
-    populateMap();
-  }
-
-  private static void populateMap() {
-    for (OptionType type : OptionType.values()) {
-      OPTION_DEFINITION_BY_NAME.put(type.messageName(), type);
-    }
-  }
-
-  private final List<ComplexType> allTypes = new ArrayList<>();
-  private final Map<OptionType, Map<String, MessageField>> optionsByType = new HashMap<>();
-  private final Map<String, Enum> enumsByName = new HashMap<>();
-
-  private Protobuf root;
-
-  private final String importUri;
-  private final INodes nodes;
-  private final XtextResource resource;
-
-  ProtoDescriptor(String importUri, URI location, IParser parser, INodes nodes) {
-    this.importUri = importUri;
-    this.nodes = nodes;
-    addOptionTypes();
-    resource = new XtextResource(location);
-    try (InputStreamReader reader = new InputStreamReader(contents(location), UTF_8)) {
-      IParseResult result = parser.parse(reader);
-      root = (Protobuf) result.getRootASTElement();
-      resource.getContents().add(root);
-      resolveLazyCrossReferences(resource, NullImpl);
-      initContents();
-    } catch (Throwable t) {
-      throw new IllegalStateException("Unable to parse descriptor.proto", t);
-    }
-  }
-
-  /**
-   * Returns the contents of the descriptor file at the given location.
-   * @param descriptorLocation the location of the descriptor file.
-   * @return the contents of the descriptor file.
-   * @throws IOException if something goes wrong.
-   */
-  protected InputStream contents(URI descriptorLocation) throws IOException {
-    URL url = new URL(descriptorLocation.toString());
-    return url.openConnection().getInputStream();
-  }
-
-  private void addOptionTypes() {
-    for (OptionType type : OptionType.values()) {
-      optionsByType.put(type, new LinkedHashMap<String, MessageField>());
-    }
-  }
-
-  private void initContents() {
-    allTypes.addAll(getAllContentsOfType(root, ComplexType.class));
-    for (ComplexType t : allTypes) {
-      if (!(t instanceof Message)) {
-        continue;
-      }
-      Message m = (Message) t;
-      OptionType type = OPTION_DEFINITION_BY_NAME.get(m.getName());
-      if (type == null) {
-        continue;
-      }
-      initOptions(m, type);
-    }
-  }
-
-  private void initOptions(Message optionGroup, OptionType type) {
-    for (MessageElement e : optionGroup.getElements()) {
-      if (e instanceof MessageField) {
-        addOption((MessageField) e, type);
-        continue;
-      }
-      if (e instanceof Enum) {
-        Enum anEnum = (Enum) e;
-        String name = anEnum.getName();
-        enumsByName.put(name, anEnum);
-      }
-    }
-  }
-
-  private void addOption(MessageField optionSource, OptionType type) {
-    if (shouldIgnore(optionSource)) {
-      return;
-    }
-    String name = optionSource.getName();
-    optionsByType.get(type).put(name, optionSource);
-  }
-
-  private boolean shouldIgnore(MessageField field) {
-    return "uninterpreted_option".equals(field.getName());
-  }
-
-  /**
-   * Returns the options available for the given option or option container. The returned options are defined in
-   * {@code google/protobuf/descriptor.proto} (more details can be found <a
-   * href=http://code.google.com/apis/protocolbuffers/docs/proto.html#options" target="_blank">here</a>.)
-   * @param o the given option or option container.
-   * @return the options available for the given option or option container, or an empty collection if the are not any
-   *         options available.
-   */
-  public Collection<MessageField> availableOptionsFor(EObject o) {
-    EObject target = o;
-    if (target instanceof NativeOption) {
-      target = target.eContainer();
-    }
-    OptionType type = findOptionTypeForLevelOf(target);
-    if (type == null) {
-      return emptyList();
-    }
-    return optionsOfType(type);
-  }
-
-  @VisibleForTesting Collection<MessageField> optionsOfType(OptionType type) {
-    return unmodifiableCollection(optionsByType.get(type).values());
-  }
-
-  /**
-   * Returns the enum type of the given field, only if the given field is defined in
-   * {@code google/protobuf/descriptor.proto} and its type is enum (more details can be found <a
-   * href=http://code.google.com/apis/protocolbuffers/docs/proto.html#options" target="_blank">here</a>.)
-   * @param field the given field.
-   * @return the enum type of the given field or {@code null} if the type of the given field is not enum.
-   */
-  public Enum enumTypeOf(MessageField field) {
-    if (field == null) {
-      return null;
-    }
-    INode node = nodes.firstNodeForFeature(field, MESSAGE_FIELD__TYPE);
-    if (node == null) {
-      return null;
-    }
-    String typeName = node.getText();
-    return (isNullOrEmpty(typeName)) ? null : enumByName(typeName.trim());
-  }
-
-  @VisibleForTesting Enum enumByName(String qualifiedName) {
-    String[] segments = qualifiedName.split("\\.");
-    if (segments == null || segments.length == 0) {
-      return null;
-    }
-    return enumsByName.get(segments[segments.length - 1]);
-  }
-
-  /**
-   * Returns all types in descriptor.proto.
-   * @return all types in descriptor.proto.
-   */
-  public List<ComplexType> allTypes() {
-    return unmodifiableList(allTypes);
-  }
-
-  public XtextResource resource() {
-    return resource;
-  }
-
-  /**
-   * Returns the URI to use when importing descriptor.proto.
-   * @return the URI to use when importing descriptor.proto.
-   */
-  public String importUri() {
-    return importUri;
-  }
-
-  @VisibleForTesting MessageField option(String name, OptionType type) {
-    Map<String, MessageField> optionByName = optionsByType.get(type);
-    return (optionByName != null) ? optionByName.get(name) : null;
-  }
-}
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtoDescriptorProvider.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtoDescriptorProvider.java
index 68b8989..66d8981 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtoDescriptorProvider.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtoDescriptorProvider.java
@@ -9,25 +9,7 @@
 package com.google.eclipse.protobuf.scoping;
 
 import static com.google.eclipse.protobuf.preferences.general.PreferenceNames.DESCRIPTOR_PROTO_PATH;
-
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
-import com.google.common.collect.ImmutableList;
-import com.google.eclipse.protobuf.model.util.INodes;
-import com.google.eclipse.protobuf.preferences.general.GeneralPreferences;
-import com.google.eclipse.protobuf.preferences.general.PreferenceNames;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-
-import org.eclipse.core.resources.IProject;
-import org.eclipse.core.runtime.IConfigurationElement;
-import org.eclipse.core.runtime.IExtensionRegistry;
-import org.eclipse.emf.common.util.URI;
-import org.eclipse.jface.util.IPropertyChangeListener;
-import org.eclipse.jface.util.PropertyChangeEvent;
-import org.eclipse.xtext.parser.IParser;
-import org.eclipse.xtext.ui.editor.preferences.IPreferenceStoreAccess;
+import static com.google.eclipse.protobuf.preferences.general.PreferenceNames.ENABLE_PROJECT_SETTINGS_PREFERENCE_NAME;
 
 import java.util.LinkedHashMap;
 import java.util.Map;
@@ -36,68 +18,98 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.xtext.ui.editor.preferences.IPreferenceStoreAccess;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableList;
+import com.google.eclipse.protobuf.preferences.general.GeneralPreferences;
+import com.google.eclipse.protobuf.preferences.general.PreferenceNames;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
 /**
  * Provider of <code>{@link ProtoDescriptor}</code>s.
  *
  * @author Alex Ruiz
  */
-@Singleton public class ProtoDescriptorProvider {
-  private static final String EXTENSION_ID = "com.google.eclipse.protobuf.descriptorSource";
+@Singleton
+public class ProtoDescriptorProvider {
+  public class ProtoDescriptorInfo {
+    final String importUri;
+    final URI location;
 
-  private final IPreferenceStoreAccess storeAccess;
-  private final IExtensionRegistry registry;
-  private final IParser parser;
-  private final INodes nodes;
-  private final IUriResolver resolver;
+    ProtoDescriptorInfo(String importUri, URI location) {
+      this.importUri = importUri;
+      this.location = location;
+    }
+  }
 
   private static final URI DEFAULT_DESCRIPTOR_LOCATION =
       URI.createURI("platform:/plugin/com.google.eclipse.protobuf/descriptor.proto");
-  private final ProtoDescriptorInfo openSourceProtoDescriptorInfo;
-  private final ProtoDescriptorInfo extensionPointDescriptorInfo;
-
-  private static final String MAP_ENTRY_DESCRIPTOR_PATH = "google/protobuf/map_entry.proto";
-  private static final URI MAP_ENTRY_DESCRIPTOR_LOCATION =
-      URI.createURI("platform:/plugin/com.google.eclipse.protobuf/map_entry.proto");;
-  private final ProtoDescriptorInfo mapEntryDescriptorInfo;
-
+  private static final String EXTENSION_ID = "com.google.eclipse.protobuf.descriptorSource";
   private static final Logger LOG =
       Logger.getLogger(ProtoDescriptorProvider.class.getCanonicalName());
 
-  private final
-      LoadingCache<IProject, Map<String, ProtoDescriptorInfo>> descriptorCache = CacheBuilder
-          .newBuilder().build(new CacheLoader<IProject, Map<String, ProtoDescriptorInfo>>() {
-        @Override
-        public Map<String, ProtoDescriptorInfo> load(final IProject project) {
-          return loadDescriptorInfos(project);
-        }
-      });
+  private static URI descriptorLocation(IConfigurationElement e) {
+    String path = e.getAttribute("path");
+    if (path.isEmpty()) {
+      return null;
+    }
+    StringBuilder uri = new StringBuilder();
+    uri.append("platform:/plugin/").append(e.getContributor().getName()).append("/").append(path);
+    return URI.createURI(uri.toString());
+  }
+
+  private final LoadingCache<IProject, Map<String, ProtoDescriptorInfo>> descriptorCache =
+      CacheBuilder.newBuilder()
+          .build(
+              new CacheLoader<IProject, Map<String, ProtoDescriptorInfo>>() {
+                @Override
+                public Map<String, ProtoDescriptorInfo> load(final IProject project) {
+                  return loadDescriptorInfos(project);
+                }
+              });
+
+  private final ProtoDescriptorInfo extensionPointDescriptorInfo;
+  private final ProtoDescriptorInfo openSourceProtoDescriptorInfo;
+  private final IExtensionRegistry registry;
+
+  private final IUriResolver resolver;
+
+  private final IPreferenceStoreAccess storeAccess;
 
   @Inject
-  ProtoDescriptorProvider(IPreferenceStoreAccess storeAccess, IExtensionRegistry registry,
-      IParser parser, INodes nodes, IUriResolver resolver) {
+  ProtoDescriptorProvider(
+      IPreferenceStoreAccess storeAccess, IExtensionRegistry registry, IUriResolver resolver) {
     this.storeAccess = storeAccess;
     this.registry = registry;
-    this.parser = parser;
-    this.nodes = nodes;
     this.resolver = resolver;
     this.openSourceProtoDescriptorInfo = getOpenSourceProtoDescriptorInfo();
-    this.mapEntryDescriptorInfo = getMapEntryDescriptorInfo();
     this.extensionPointDescriptorInfo = getExtensionPointDescriptorInfo();
   }
 
-  public ProtoDescriptor primaryDescriptor(IProject project) {
+  public ImmutableList<URI> allDescriptorLocations(IProject project) {
     Map<String, ProtoDescriptorInfo> descriptorInfos = getDescriptorInfosFor(project);
+    ImmutableList.Builder<URI> descriptorLocations = ImmutableList.builder();
     for (ProtoDescriptorInfo descriptorInfo : descriptorInfos.values()) {
-      return descriptorInfo.protoDescriptor;
+      descriptorLocations.add(descriptorInfo.location);
     }
-    return openSourceProtoDescriptorInfo.protoDescriptor;
+    return descriptorLocations.build();
   }
 
-  public ProtoDescriptor descriptor(IProject project, String importUri) {
+  public ProtoDescriptorInfo descriptor(IProject project, String importUri) {
     Map<String, ProtoDescriptorInfo> descriptorInfos = getDescriptorInfosFor(project);
     ProtoDescriptorInfo descriptorInfo = descriptorInfos.get(importUri);
     if (descriptorInfo != null) {
-      return descriptorInfo.protoDescriptor;
+      return descriptorInfo;
     }
     URI uri = URI.createURI(importUri);
     for (Entry<String, ProtoDescriptorInfo> info : descriptorInfos.entrySet()) {
@@ -108,13 +120,24 @@
     return null;
   }
 
-  public ImmutableList<URI> allDescriptorLocations(IProject project) {
-    Map<String, ProtoDescriptorInfo> descriptorInfos = getDescriptorInfosFor(project);
-    ImmutableList.Builder<URI> descriptorLocations = ImmutableList.builder();
-    for (ProtoDescriptorInfo descriptorInfo : descriptorInfos.values()) {
-      descriptorLocations.add(descriptorInfo.location);
+  private ProtoDescriptorInfo descriptorInfo(IConfigurationElement e) {
+    String importUri = e.getAttribute("importUri");
+    if (importUri.isEmpty()) {
+      return null;
     }
-    return descriptorLocations.build();
+    URI location = descriptorLocation(e);
+    if (location == null) {
+      return null;
+    }
+    try {
+      return new ProtoDescriptorInfo(importUri, location);
+    } catch (IllegalStateException exception) {
+      LOG.log(
+          Level.WARNING,
+          "Error when initializing descriptor proto from extension point",
+          exception);
+      return null;
+    }
   }
 
   public URI descriptorLocation(IProject project, String importUri) {
@@ -127,63 +150,9 @@
     return null;
   }
 
-  public ProtoDescriptor mapEntryDescriptor() {
-    return mapEntryDescriptorInfo.protoDescriptor;
-  }
-
-  private Map<String, ProtoDescriptorInfo> loadDescriptorInfos(final IProject project) {
-    Map<String, ProtoDescriptorInfo> descriptorInfos = new LinkedHashMap<>();
-
-    // Add descriptor.proto from preferences
-    GeneralPreferences preferences = new GeneralPreferences(storeAccess, project);
-    String descriptorProtoUri = preferences.getDescriptorProtoPath();
-    if (!PreferenceNames.DEFAULT_DESCRIPTOR_PATH.equals(descriptorProtoUri)) {
-      String resolvedUri = resolver.resolveUri(descriptorProtoUri, null, project);
-      if (resolvedUri != null) {
-        URI descriptorProtoLocation = URI.createURI(resolvedUri);
-        if (descriptorProtoLocation != null) {
-          ProtoDescriptor protoDescriptor =
-              new ProtoDescriptor(descriptorProtoUri, descriptorProtoLocation, parser, nodes);
-          ProtoDescriptorInfo descriptorInfo =
-              new ProtoDescriptorInfo(descriptorProtoUri, descriptorProtoLocation, protoDescriptor);
-          descriptorInfos.put(descriptorProtoUri, descriptorInfo);
-        }
-      } else {
-        LOG.log(Level.WARNING,
-            "Unable to resolve URI for descriptor proto location: " + descriptorProtoUri);
-      }
-    }
-
-    // Add the extension point descriptor proto
-    if (extensionPointDescriptorInfo != null) {
-      if (!descriptorInfos.containsKey(extensionPointDescriptorInfo.importUri)) {
-        descriptorInfos.put(extensionPointDescriptorInfo.importUri, extensionPointDescriptorInfo);
-      }
-    }
-
-    // Add the open source descriptor proto
-    if (!descriptorInfos.containsKey(PreferenceNames.DEFAULT_DESCRIPTOR_PATH)) {
-      descriptorInfos.put(PreferenceNames.DEFAULT_DESCRIPTOR_PATH,
-          openSourceProtoDescriptorInfo);
-    }
-
-    // Set property change listener for this project
-    storeAccess.getContextPreferenceStore(project).addPropertyChangeListener(
-        new IPropertyChangeListener() {
-          @Override
-          public void propertyChange(PropertyChangeEvent event) {
-            if (event.getProperty().contains(DESCRIPTOR_PROTO_PATH)) {
-              descriptorCache.invalidate(project);
-            }
-          }
-        });
-    return descriptorInfos;
-  }
-
   private Map<String, ProtoDescriptorInfo> getDescriptorInfosFor(IProject project) {
     if (project == null) {
-      Map<String, ProtoDescriptorInfo> descriptorInfos =
-          new LinkedHashMap<>();
+      Map<String, ProtoDescriptorInfo> descriptorInfos = new LinkedHashMap<>();
       descriptorInfos.put(PreferenceNames.DEFAULT_DESCRIPTOR_PATH, openSourceProtoDescriptorInfo);
       return descriptorInfos;
     }
@@ -195,20 +164,6 @@
     }
   }
 
-  private ProtoDescriptorInfo getOpenSourceProtoDescriptorInfo() {
-    ProtoDescriptor descriptor = new ProtoDescriptor(PreferenceNames.DEFAULT_DESCRIPTOR_PATH,
-        DEFAULT_DESCRIPTOR_LOCATION, parser, nodes);
-    return new ProtoDescriptorInfo(PreferenceNames.DEFAULT_DESCRIPTOR_PATH,
-        DEFAULT_DESCRIPTOR_LOCATION, descriptor);
-  }
-
-  private ProtoDescriptorInfo getMapEntryDescriptorInfo() {
-    ProtoDescriptor descriptor = new ProtoDescriptor(
-        MAP_ENTRY_DESCRIPTOR_PATH, MAP_ENTRY_DESCRIPTOR_LOCATION, parser, nodes);
-    return new ProtoDescriptorInfo(
-        MAP_ENTRY_DESCRIPTOR_PATH, MAP_ENTRY_DESCRIPTOR_LOCATION, descriptor);
-  }
-
   private ProtoDescriptorInfo getExtensionPointDescriptorInfo() {
     IConfigurationElement[] config = registry.getConfigurationElementsFor(EXTENSION_ID);
     if (config == null) {
@@ -223,44 +178,67 @@
     return null;
   }
 
-  private ProtoDescriptorInfo descriptorInfo(IConfigurationElement e) {
-    String importUri = e.getAttribute("importUri");
-    if (importUri.isEmpty()) {
-      return null;
-    }
-    URI location = descriptorLocation(e);
-    if (location == null) {
-      return null;
-    }
-    try {
-      return new ProtoDescriptorInfo(importUri, location,
-          new ProtoDescriptor(importUri, location, parser, nodes));
-    } catch (IllegalStateException exception) {
-      LOG.log(Level.WARNING, "Error when initializing descriptor proto from extension point",
-          exception);
-      return null;
-    }
+  private ProtoDescriptorInfo getOpenSourceProtoDescriptorInfo() {
+    return new ProtoDescriptorInfo(
+        PreferenceNames.DEFAULT_DESCRIPTOR_PATH, DEFAULT_DESCRIPTOR_LOCATION);
   }
 
-  private static URI descriptorLocation(IConfigurationElement e) {
-    String path = e.getAttribute("path");
-    if (path.isEmpty()) {
-      return null;
+  private Map<String, ProtoDescriptorInfo> loadDescriptorInfos(final IProject project) {
+    Map<String, ProtoDescriptorInfo> descriptorInfos = new LinkedHashMap<>();
+
+    // Add descriptor.proto from preferences
+    GeneralPreferences preferences = new GeneralPreferences(storeAccess, project);
+    String descriptorProtoUri = preferences.getDescriptorProtoPath();
+    if (!PreferenceNames.DEFAULT_DESCRIPTOR_PATH.equals(descriptorProtoUri)) {
+      String resolvedUri = resolver.resolveUri(descriptorProtoUri, null, project);
+      if (resolvedUri != null) {
+        URI descriptorProtoLocation = URI.createURI(resolvedUri);
+        if (descriptorProtoLocation != null) {
+          ProtoDescriptorInfo descriptorInfo =
+              new ProtoDescriptorInfo(descriptorProtoUri, descriptorProtoLocation);
+          descriptorInfos.put(descriptorProtoUri, descriptorInfo);
+        }
+      } else {
+        LOG.log(
+            Level.WARNING,
+            "Unable to resolve URI for descriptor proto location: " + descriptorProtoUri);
+      }
     }
-    StringBuilder uri = new StringBuilder();
-    uri.append("platform:/plugin/").append(e.getContributor().getName()).append("/").append(path);
-    return URI.createURI(uri.toString());
+
+    // Add the extension point descriptor proto
+    if (extensionPointDescriptorInfo != null) {
+      if (!descriptorInfos.containsKey(extensionPointDescriptorInfo.importUri)) {
+        descriptorInfos.put(extensionPointDescriptorInfo.importUri, extensionPointDescriptorInfo);
+      }
+    }
+
+    // Add the open source descriptor proto
+    if (!descriptorInfos.containsKey(PreferenceNames.DEFAULT_DESCRIPTOR_PATH)) {
+      descriptorInfos.put(PreferenceNames.DEFAULT_DESCRIPTOR_PATH, openSourceProtoDescriptorInfo);
+    }
+
+    IPropertyChangeListener changeListener =
+        new IPropertyChangeListener() {
+          @Override
+          public void propertyChange(PropertyChangeEvent event) {
+            String property = event.getProperty();
+            if (property.contains(DESCRIPTOR_PROTO_PATH)
+                || property.contains(ENABLE_PROJECT_SETTINGS_PREFERENCE_NAME)) {
+              descriptorCache.invalidate(project);
+            }
+          }
+        };
+
+    // Set property change listener for this project
+    preferences.addPropertyChangeListener(changeListener);
+    return descriptorInfos;
   }
 
-  private static class ProtoDescriptorInfo {
-    final String importUri;
-    final URI location;
-    final ProtoDescriptor protoDescriptor;
-
-    ProtoDescriptorInfo(String importUri, URI location, ProtoDescriptor protoDescriptor) {
-      this.importUri = importUri;
-      this.location = location;
-      this.protoDescriptor = protoDescriptor;
+  public ProtoDescriptorInfo primaryDescriptor(IProject project) {
+    Map<String, ProtoDescriptorInfo> descriptorInfos = getDescriptorInfosFor(project);
+    for (ProtoDescriptorInfo descriptorInfo : descriptorInfos.values()) {
+      return descriptorInfo;
     }
+    return openSourceProtoDescriptorInfo;
   }
 }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider.java
index 0efad69..5baf2c7 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtobufScopeProvider.java
@@ -8,18 +8,10 @@
  */
 package com.google.eclipse.protobuf.scoping;
 
-import static com.google.eclipse.protobuf.util.Encodings.UTF_8;
 import static com.google.eclipse.protobuf.util.Tracer.DEBUG_SCOPING;
 import static com.google.eclipse.protobuf.validation.ProtobufResourceValidator.getScopeProviderTimingCollector;
-import static java.util.Collections.singletonMap;
-import static org.eclipse.emf.ecore.resource.ContentHandler.UNSPECIFIED_CONTENT_TYPE;
-import static org.eclipse.xtext.EcoreUtil2.resolveLazyCrossReferences;
-import static org.eclipse.xtext.resource.XtextResource.OPTION_ENCODING;
-import static org.eclipse.xtext.util.CancelIndicator.NullImpl;
-
 import com.google.eclipse.protobuf.naming.ProtobufQualifiedNameConverter;
 import com.google.eclipse.protobuf.naming.ProtobufQualifiedNameProvider;
-import com.google.eclipse.protobuf.preferences.general.PreferenceNames;
 import com.google.eclipse.protobuf.protobuf.ComplexType;
 import com.google.eclipse.protobuf.protobuf.ComplexTypeLink;
 import com.google.eclipse.protobuf.protobuf.ComplexValue;
@@ -27,15 +19,11 @@
 import com.google.eclipse.protobuf.protobuf.CustomFieldOption;
 import com.google.eclipse.protobuf.protobuf.CustomOption;
 import com.google.eclipse.protobuf.protobuf.DefaultValueFieldOption;
-import com.google.eclipse.protobuf.protobuf.Enum;
 import com.google.eclipse.protobuf.protobuf.ExtensionFieldName;
 import com.google.eclipse.protobuf.protobuf.FieldName;
-import com.google.eclipse.protobuf.protobuf.FieldOption;
 import com.google.eclipse.protobuf.protobuf.Group;
 import com.google.eclipse.protobuf.protobuf.IndexedElement;
-import com.google.eclipse.protobuf.protobuf.Literal;
 import com.google.eclipse.protobuf.protobuf.LiteralLink;
-import com.google.eclipse.protobuf.protobuf.Message;
 import com.google.eclipse.protobuf.protobuf.MessageField;
 import com.google.eclipse.protobuf.protobuf.NativeFieldOption;
 import com.google.eclipse.protobuf.protobuf.NativeOption;
@@ -44,33 +32,24 @@
 import com.google.eclipse.protobuf.protobuf.OptionSource;
 import com.google.eclipse.protobuf.protobuf.Package;
 import com.google.eclipse.protobuf.protobuf.Protobuf;
-import com.google.eclipse.protobuf.protobuf.Rpc;
-import com.google.eclipse.protobuf.protobuf.Stream;
 import com.google.eclipse.protobuf.protobuf.TypeLink;
 import com.google.eclipse.protobuf.protobuf.ValueField;
 import com.google.eclipse.protobuf.util.EResources;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
-import org.apache.log4j.Level;
 import org.eclipse.core.resources.IProject;
 import org.eclipse.emf.common.util.EList;
-import org.eclipse.emf.common.util.URI;
 import org.eclipse.emf.ecore.EObject;
 import org.eclipse.emf.ecore.EReference;
 import org.eclipse.emf.ecore.resource.Resource;
 import org.eclipse.emf.ecore.resource.ResourceSet;
 import org.eclipse.jdt.annotation.Nullable;
-import org.eclipse.jface.preference.IPreferenceStore;
 import org.eclipse.xtext.naming.QualifiedName;
 import org.eclipse.xtext.scoping.IScope;
 import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider;
 import org.eclipse.xtext.scoping.impl.ImportNormalizer;
-import org.eclipse.xtext.ui.editor.preferences.IPreferenceStoreAccess;
 import org.eclipse.xtext.util.IResourceScopeCache;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -81,40 +60,8 @@
  * @author atrookey@google.com (Alexander Rookey)
  */
 public class ProtobufScopeProvider extends AbstractDeclarativeScopeProvider {
-  /**
-   * Returns the name of the descriptor.proto message declaring default options. The options must
-   * match the option type of {@code context}. For example, if {@code context} is a file option,
-   * {@code FileOptions} should be returned because it contains the declarations of the default file
-   * options.
-   */
-  // TODO (atrookey) Create utility for getting the type of an Option.
-  private static String getOptionType(EObject context) {
-    if (context == null) {
-      return "FileOptions";
-    }
-    if (context instanceof Message) {
-      return "MessageOptions";
-    }
-    if (context instanceof Enum) {
-      return "EnumOptions";
-    }
-    if (context instanceof FieldOption) {
-      return "FieldOptions";
-    }
-    if (context instanceof Rpc) {
-      return "MethodOptions";
-    }
-    if (context instanceof Stream) {
-      return "StreamOptions";
-    }
-    if (context instanceof Literal) {
-      return "EnumValueOptions";
-    }
-    return getOptionType(context.eContainer());
-  }
 
-  @Inject private IPreferenceStoreAccess storeAccess;
-  @Inject private IUriResolver uriResolver;
+  @Inject private ProtoDescriptorProvider descriptorProvider;
   @Inject private IResourceScopeCache cache;
   @Inject private ProtobufQualifiedNameConverter nameConverter;
   @Inject private ProtobufQualifiedNameProvider nameProvider;
@@ -217,27 +164,10 @@
 
   /** Returns descriptor associated with the current project. */
   private @Nullable Resource getDescriptorResource(EObject context) {
-    URI descriptorLocation;
     IProject project = EResources.getProjectOf(context.eResource());
-    IPreferenceStore store = storeAccess.getWritablePreferenceStore(project);
-    String rawDescriptorLocation = store.getString(PreferenceNames.DESCRIPTOR_PROTO_PATH);
-    descriptorLocation =
-        URI.createURI(uriResolver.resolveUri(rawDescriptorLocation, null, project));
     ResourceSet resourceSet = context.eResource().getResourceSet();
-    Resource resource = resourceSet.getResource(descriptorLocation, true);
-    if (resource != null) {
-      return resource;
-    }
-    try {
-      InputStream contents = openFile(descriptorLocation);
-      resource = resourceSet.createResource(descriptorLocation, UNSPECIFIED_CONTENT_TYPE);
-      resource.load(contents, singletonMap(OPTION_ENCODING, UTF_8));
-      resolveLazyCrossReferences(resource, NullImpl);
-      return resource;
-    } catch (IOException e) {
-      logger.log(Level.ERROR, e);
-    }
-    return null;
+    ProtoDescriptorProvider.ProtoDescriptorInfo descriptorInfo = descriptorProvider.primaryDescriptor(project);
+    return resourceSet.getResource(descriptorInfo.location, true);
   }
 
   /** Returns the global scope provider. */
@@ -284,14 +214,6 @@
   }
 
   /**
-   * Returns the InputStream associated with the resource at location {@code descriptorLocation}.
-   */
-  private InputStream openFile(URI fileLocation) throws IOException {
-    URL url = new URL(fileLocation.toString());
-    return url.openConnection().getInputStream();
-  }
-
-  /**
    * Scopes the {@code FieldName}.
    *
    * <p>For example: <pre>
@@ -464,7 +386,7 @@
    * google.protobuf.FileOptions.java_package} defined in descriptor.proto.
    */
   public IScope scope_OptionSource_target(OptionSource optionSource, EReference reference) {
-    String optionType = getOptionType(optionSource);
+    String optionType = OptionType.typeOf(optionSource).messageName();
     Resource resource = optionSource.eResource();
     IScope descriptorScope =
         cache.get(