Merge branch 'master' of https://code.google.com/p/protobuf-dt/ into
option-ref

Conflicts:
	com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/ProtoDescriptor.java
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 853b1c0..3e88b60 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
@@ -261,11 +261,6 @@
     proposeAndAccept(name, context, acceptor);
   }
 
-  private ICompletionProposal createCompletionProposal(String proposal, String displayString,
-      ContentAssistContext context) {
-    return createCompletionProposal(proposal, displayString, defaultImage(), context);
-  }
-
   private void proposeAndAccept(String proposalText, ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
     acceptor.accept(createCompletionProposal(proposalText, context));
   }
@@ -337,7 +332,8 @@
     } else if (properties.isBool(option)) {
       proposalText = proposalText + TRUE;
     }
-    ICompletionProposal proposal = createCompletionProposal(proposalText, displayString, context);
+    Image image = imageHelper.getImage(images.imageFor(Option.class));
+    ICompletionProposal proposal = createCompletionProposal(proposalText, displayString, image, context);
     if (isStringOption && proposal instanceof ConfigurableCompletionProposal) {
       // set cursor between the proposal's quotes
       ConfigurableCompletionProposal configurable = (ConfigurableCompletionProposal) proposal;
@@ -417,4 +413,9 @@
     String previousWord = styledText.getTextRange(start, valueLength);
     return word.equals(previousWord);
   }
+
+  /** {@inheritDoc} */
+  @Override public void completePropertyRef_Property(EObject model, Assignment assignment,
+      ContentAssistContext context, ICompletionProposalAcceptor acceptor) {
+  }
 }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/OptionDescriptions.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/OptionDescriptions.java
index 716232e..d92fbfb 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/OptionDescriptions.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/OptionDescriptions.java
@@ -8,19 +8,23 @@
  */
 package com.google.eclipse.protobuf.scoping;
 
-import static com.google.eclipse.protobuf.scoping.OptionType.*;
 import static java.util.Collections.emptyList;
+import static org.eclipse.emf.common.util.URI.createURI;
+import static org.eclipse.emf.ecore.util.EcoreUtil.getAllContents;
 import static org.eclipse.xtext.resource.EObjectDescription.create;
 
 import java.util.*;
-import java.util.Map.Entry;
 
+import org.eclipse.emf.common.util.*;
 import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.resource.*;
 import org.eclipse.xtext.naming.QualifiedName;
 import org.eclipse.xtext.resource.IEObjectDescription;
+import org.eclipse.xtext.scoping.impl.ImportUriResolver;
 
 import com.google.eclipse.protobuf.protobuf.*;
-import com.google.eclipse.protobuf.protobuf.Enum;
+import com.google.eclipse.protobuf.protobuf.Package;
+import com.google.eclipse.protobuf.util.*;
 import com.google.inject.Inject;
 
 /**
@@ -28,19 +32,14 @@
  */
 class OptionDescriptions {
 
-  private static final Map<Class<?>, OptionType> OPTION_TYPES_BY_CONTAINER = new HashMap<Class<?>, OptionType>();
-
-  static {
-    OPTION_TYPES_BY_CONTAINER.put(Protobuf.class, FILE);
-    OPTION_TYPES_BY_CONTAINER.put(Enum.class, ENUM);
-    OPTION_TYPES_BY_CONTAINER.put(Message.class, MESSAGE);
-    OPTION_TYPES_BY_CONTAINER.put(Service.class, SERVICE);
-    OPTION_TYPES_BY_CONTAINER.put(Rpc.class, RPC);
-  }
-
   @Inject private ProtoDescriptorProvider descriptorProvider;
+  @Inject private ProtobufElementFinder finder;
+  @Inject private ImportedNamesProvider importedNamesProvider;
+  @Inject private Imports imports;
   @Inject private LocalNamesProvider localNamesProvider;
+  @Inject private PackageResolver packageResolver;
   @Inject private QualifiedNameDescriptions qualifiedNamesDescriptions;
+  @Inject private ImportUriResolver uriResolver;
 
   Collection <IEObjectDescription> builtInOptionProperties(BuiltInOption option) {
     ProtoDescriptor descriptor = descriptorProvider.get();
@@ -49,12 +48,11 @@
     return emptyList();
   }
 
-  Collection <IEObjectDescription> localCustomOptionProperties(EObject root, CustomOption option) {
-    return localCustomOptionProperties(root, option, 0);
+  Collection <IEObjectDescription> localCustomOptionProperties(EObject root, OptionType optionType) {
+    return localCustomOptionProperties(root, optionType, 0);
   }
 
-  private  Collection <IEObjectDescription> localCustomOptionProperties(EObject root, CustomOption option, int level) {
-    OptionType optionType = optionType(option);
+  private Collection <IEObjectDescription> localCustomOptionProperties(EObject root, OptionType optionType, int level) {
     if (optionType == null) return emptyList();
     List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>();
     for (EObject element : root.eContents()) {
@@ -72,23 +70,87 @@
         continue;
       }
       if (element instanceof Message) {
-        descriptions.addAll(localCustomOptionProperties(element, option, level + 1));
+        descriptions.addAll(localCustomOptionProperties(element, optionType, level + 1));
       }
     }
     return descriptions;
   }
 
-  private OptionType optionType(CustomOption option) {
-    EObject container = option.eContainer();
-    for (Entry<Class<?>, OptionType> optionTypeByContainer : OPTION_TYPES_BY_CONTAINER.entrySet()) {
-      if (optionTypeByContainer.getKey().isInstance(container)) {
-        return optionTypeByContainer.getValue();
-      }
-    }
-    return null;
+  Collection<IEObjectDescription> importedCustomOptionProperties(Protobuf root, OptionType optionType) {
+    List<Import> allImports = finder.importsIn(root);
+    if (allImports.isEmpty()) return emptyList();
+    ResourceSet resourceSet = root.eResource().getResourceSet();
+    return importedCustomOptionProperties(allImports, finder.packageOf(root), resourceSet, optionType);
   }
 
-  private boolean isExtendingOptionMessage(EObject o, OptionType optionType) {
+  private Collection<IEObjectDescription> importedCustomOptionProperties(List<Import> allImports, Package aPackage,
+      ResourceSet resourceSet, OptionType optionType) {
+    List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>();
+    for (Import anImport : allImports) {
+      if (imports.isImportingDescriptor(anImport)) continue;
+      Resource importedResource = importedResource(anImport, resourceSet);
+      Protobuf importedRoot = finder.rootOf(importedResource);
+      if (importedRoot != null) {
+        descriptions.addAll(publicImportedCustomOptionProperties(importedRoot, optionType));
+        if (arePackagesRelated(aPackage, importedRoot)) {
+          descriptions.addAll(localCustomOptionProperties(importedRoot, optionType));
+          continue;
+        }
+      }
+      descriptions.addAll(localCustomOptionProperties(importedResource, optionType));
+    }
+    return descriptions;
+  }
+
+  private Resource importedResource(Import anImport, ResourceSet resourceSet) {
+    URI importUri = createURI(uriResolver.apply(anImport));
+    try {
+      return resourceSet.getResource(importUri, true);
+    } catch (Throwable t) {
+      return null;
+    }
+  }
+
+  private <T extends Type> Collection<IEObjectDescription> publicImportedCustomOptionProperties(Protobuf root,
+      OptionType optionType) {
+    List<Import> allImports = finder.publicImportsIn(root);
+    if (allImports.isEmpty()) return emptyList();
+    ResourceSet resourceSet = root.eResource().getResourceSet();
+    return importedCustomOptionProperties(allImports, finder.packageOf(root), resourceSet, optionType);
+  }
+
+  private boolean arePackagesRelated(Package aPackage, EObject root) {
+    Package p = finder.packageOf(root);
+    return packageResolver.areRelated(aPackage, p);
+  }
+
+  private Collection<IEObjectDescription> describe(Collection<Property> properties) {
+    List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>();
+    for (Property p : properties) {
+      descriptions.add(create(p.getName(), p));
+    }
+    return descriptions;
+  }
+
+  private Collection<IEObjectDescription> localCustomOptionProperties(Resource resource, OptionType optionType) {
+    List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>();
+    TreeIterator<Object> contents = getAllContents(resource, true);
+    while (contents.hasNext()) {
+      Object next = contents.next();
+      if (!isExtendingOptionMessage(next, optionType)) continue;
+      ExtendMessage extend = (ExtendMessage) next;
+      for (MessageElement e : extend.getElements()) {
+        if (!(e instanceof Property)) continue;
+        descriptions.addAll(qualifiedNamesDescriptions.qualifiedNames(e));
+        for (QualifiedName name : importedNamesProvider.namesOf(e)) {
+          descriptions.add(create(name, e));
+        }
+      }
+    }
+    return descriptions;
+  }
+
+  private boolean isExtendingOptionMessage(Object o, OptionType optionType) {
     if (!(o instanceof ExtendMessage)) return false;
     Message message = messageFrom((ExtendMessage) o);
     if (message == null) return false;
@@ -99,12 +161,4 @@
     MessageRef ref = extend.getMessage();
     return ref == null ? null : ref.getType();
   }
-
-  private Collection<IEObjectDescription> describe(Collection<Property> properties) {
-    List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>();
-    for (Property p : properties) {
-      descriptions.add(create(p.getName(), p));
-    }
-    return descriptions;
-  }
 }
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 f5893a3..2c75f54 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
@@ -8,6 +8,14 @@
  */
 package com.google.eclipse.protobuf.scoping;
 
+import java.util.*;
+import java.util.Map.Entry;
+
+import org.eclipse.emf.ecore.EObject;
+
+import com.google.eclipse.protobuf.protobuf.*;
+import com.google.eclipse.protobuf.protobuf.Enum;
+
 /**
  * @author alruiz@google.com (Alex Ruiz)
  */
@@ -15,9 +23,29 @@
   FILE("FileOptions"), MESSAGE("MessageOptions"), FIELD("FieldOptions"), ENUM("EnumOptions"),
       LITERAL("EnumValueOptions"), SERVICE("ServiceOptions"), RPC("MethodOptions");
 
+  private static final Map<Class<?>, OptionType> OPTION_TYPES_BY_CONTAINER = new HashMap<Class<?>, OptionType>();
+
+  static {
+    OPTION_TYPES_BY_CONTAINER.put(Protobuf.class, FILE);
+    OPTION_TYPES_BY_CONTAINER.put(Enum.class, ENUM);
+    OPTION_TYPES_BY_CONTAINER.put(Message.class, MESSAGE);
+    OPTION_TYPES_BY_CONTAINER.put(Service.class, SERVICE);
+    OPTION_TYPES_BY_CONTAINER.put(Rpc.class, RPC);
+  }
+
   final String messageName;
 
   private OptionType(String messageName) {
     this.messageName = messageName;
   }
-}
\ No newline at end of file
+
+  static OptionType optionType(CustomOption option) {
+    EObject container = option.eContainer();
+    for (Entry<Class<?>, OptionType> optionTypeByContainer : OPTION_TYPES_BY_CONTAINER.entrySet()) {
+      if (optionTypeByContainer.getKey().isInstance(container)) {
+        return optionTypeByContainer.getValue();
+      }
+    }
+    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
index 70bdd4c..af6225f 100644
--- 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
@@ -46,11 +46,11 @@
   private static final Map<String, OptionType> OPTION_DEFINITION_BY_NAME = new HashMap<String, OptionType>();
 
   static {
-    addOptionTypeMappings();
+    addOptionTypes(FILE, MESSAGE, FIELD, ENUM);
   }
 
-  private static void addOptionTypeMappings() {
-    for (OptionType type : OptionType.values()) {
+  private static void addOptionTypes(OptionType...types) {
+    for (OptionType type : types) {
       OPTION_DEFINITION_BY_NAME.put(type.messageName, type);
     }
   }
@@ -133,23 +133,33 @@
   }
 
   /**
-   * Returns the options available for the given option container. The returned options are defined in
+   * 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 optionContainer the given container of an option.
-   * @return the options available for the given option container, or an empty collection if the are not any
-   * options available for the given option container.
+   * @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<Property> availableOptionPropertiesFor(EObject optionContainer) {
-    if (optionContainer instanceof Protobuf) return optionsOfType(FILE);
-    if (optionContainer instanceof Enum) return optionsOfType(ENUM);
-    if (optionContainer instanceof Message) return optionsOfType(MESSAGE);
-    if (optionContainer instanceof Service) return optionsOfType(SERVICE);
-    if (optionContainer instanceof Rpc) return optionsOfType(RPC);
+  public Collection<Property> availableOptionPropertiesFor(EObject o) {
+    EObject target = o;
+    if (target instanceof BuiltInOption) target = target.eContainer();
+    if (target instanceof Protobuf) return fileOptions();
+    if (target instanceof Enum) return enumOptions();
+    if (target instanceof Message) return messageOptions();
     return emptyList();
   }
 
   /**
+   * Returns all the file-level options available. These are the options 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>.)
+   * @return all the file-level options available.
+   */
+  public Collection<Property> fileOptions() {
+    return optionsOfType(FILE);
+  }
+
+  /**
    * Looks up an option per name, as 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>.)
@@ -184,6 +194,16 @@
   }
 
   /**
+   * Returns all the message-level options available. These are the options 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>.)
+   * @return all the message-level options available.
+   */
+  public Collection<Property> messageOptions() {
+    return optionsOfType(MESSAGE);
+  }
+
+  /**
    * Returns all the field-level options available. These are the options 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>.)
@@ -192,7 +212,17 @@
   public Collection<Property> fieldOptions() {
     return optionsOfType(FIELD);
   }
-  
+
+  /**
+   * Returns all the enum-level options available. These are the options 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>.)
+   * @return all the enum-level options available.
+   */
+  public Collection<Property> enumOptions() {
+    return optionsOfType(ENUM);
+  }
+
   private Collection<Property> optionsOfType(OptionType type) {
     return unmodifiableCollection(optionsByType.get(type).values());
   }
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 be4bcc9..041fcc1 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,6 +8,8 @@
  */
 package com.google.eclipse.protobuf.scoping;
 
+import static com.google.eclipse.protobuf.scoping.OptionType.optionType;
+
 import java.util.*;
 
 import org.eclipse.emf.ecore.*;
@@ -97,7 +99,13 @@
     }
     if (mayBeOption instanceof CustomOption) {
       Protobuf root = finder.rootOf(propertyRef);
-      descriptions.addAll(optionDescriptions.localCustomOptionProperties(root, (CustomOption) mayBeOption));
+      OptionType optionType = optionType((CustomOption) mayBeOption);
+      EObject current = mayBeOption.eContainer();
+      while (current != null) {
+        descriptions.addAll(optionDescriptions.localCustomOptionProperties(current, optionType));
+        current = current.eContainer();
+      }
+      descriptions.addAll(optionDescriptions.importedCustomOptionProperties(root, optionType));
     }
     return createScope(descriptions);
   }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/QualifiedNames.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/QualifiedNames.java
index eb5bdc0..a073fe8 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/QualifiedNames.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/QualifiedNames.java
@@ -33,7 +33,7 @@
     return QualifiedName.create(segments.toArray(new String[segments.size()]));
   }
 
-  List<QualifiedName> addPackageNameSegments(QualifiedName name, Package p) {
+  Collection<QualifiedName> addPackageNameSegments(QualifiedName name, Package p) {
     QualifiedName current = name;
     List<String> segments = fqnSegments(p);
     int segmentCount = segments.size();
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/TypeDescriptions.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/TypeDescriptions.java
index 5082138..8892b06 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/TypeDescriptions.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/scoping/TypeDescriptions.java
@@ -19,12 +19,12 @@
 import org.eclipse.emf.ecore.EObject;
 import org.eclipse.emf.ecore.resource.*;
 import org.eclipse.xtext.naming.QualifiedName;
-import org.eclipse.xtext.resource.*;
+import org.eclipse.xtext.resource.IEObjectDescription;
 import org.eclipse.xtext.scoping.impl.ImportUriResolver;
 
 import com.google.eclipse.protobuf.protobuf.*;
 import com.google.eclipse.protobuf.protobuf.Package;
-import com.google.eclipse.protobuf.util.ProtobufElementFinder;
+import com.google.eclipse.protobuf.util.*;
 import com.google.inject.Inject;
 
 /**
@@ -35,6 +35,7 @@
   @Inject private ProtoDescriptorProvider descriptorProvider;
   @Inject private ProtobufElementFinder finder;
   @Inject private ImportedNamesProvider importedNamesProvider;
+  @Inject private Imports imports;
   @Inject private LocalNamesProvider localNamesProvider;
   @Inject private PackageResolver packageResolver;
   @Inject private QualifiedNameDescriptions qualifiedNamesDescriptions;
@@ -65,19 +66,20 @@
   <T extends Type> Collection<IEObjectDescription> importedTypes(Protobuf root, Class<T> targetType) {
     List<Import> allImports = finder.importsIn(root);
     if (allImports.isEmpty()) return emptyList();
-    return importedTypes(allImports, finder.packageOf(root), targetType);
+    ResourceSet resourceSet = root.eResource().getResourceSet();
+    return importedTypes(allImports, finder.packageOf(root), resourceSet, targetType);
   }
 
   private <T extends Type> Collection<IEObjectDescription> importedTypes(List<Import> allImports, Package aPackage,
-      Class<T> targetType) {
+      ResourceSet resourceSet, Class<T> targetType) {
     List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>();
     for (Import anImport : allImports) {
-      if (isImportingDescriptor(anImport)) {
+      if (imports.isImportingDescriptor(anImport)) {
         descriptions.addAll(allBuiltInTypes(targetType));
         continue;
       }
-      Resource importedResource = importedResourceFrom(anImport);
-      Protobuf importedRoot = rootElementOf(importedResource);
+      Resource importedResource = importedResource(anImport, resourceSet);
+      Protobuf importedRoot = finder.rootOf(importedResource);
       if (importedRoot != null) {
         descriptions.addAll(publicImportedTypes(importedRoot, targetType));
         if (arePackagesRelated(aPackage, importedRoot)) {
@@ -85,16 +87,11 @@
           continue;
         }
       }
-      descriptions.addAll(children(importedResource, targetType));
+      descriptions.addAll(localTypes(importedResource, targetType));
     }
     return descriptions;
   }
 
-  private boolean isImportingDescriptor(Import anImport) {
-    String descriptorLocation = descriptorProvider.descriptorLocation().toString();
-    return descriptorLocation.equals(anImport.getImportURI());
-  }
-
   private <T extends Type> Collection<IEObjectDescription> allBuiltInTypes(Class<T> targetType) {
     List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>();
     ProtoDescriptor descriptor = descriptorProvider.get();
@@ -106,14 +103,7 @@
     return descriptions;
   }
 
-  private <T extends Type> Collection<IEObjectDescription> publicImportedTypes(Protobuf root, Class<T> targetType) {
-    List<Import> allImports = finder.publicImportsIn(root);
-    if (allImports.isEmpty()) return emptyList();
-    return importedTypes(allImports, finder.packageOf(root), targetType);
-  }
-
-  private Resource importedResourceFrom(Import anImport) {
-    ResourceSet resourceSet = finder.rootOf(anImport).eResource().getResourceSet();
+  private Resource importedResource(Import anImport, ResourceSet resourceSet) {
     URI importUri = createURI(uriResolver.apply(anImport));
     try {
       return resourceSet.getResource(importUri, true);
@@ -122,17 +112,11 @@
     }
   }
 
-  private Protobuf rootElementOf(Resource resource) {
-    if (resource instanceof XtextResource) {
-      EObject root = ((XtextResource) resource).getParseResult().getRootASTElement();
-      return (Protobuf) root;
-    }
-    TreeIterator<Object> contents = getAllContents(resource, true);
-    if (contents.hasNext()) {
-      Object next = contents.next();
-      if (next instanceof Protobuf) return (Protobuf) next;
-    }
-    return null;
+  private <T extends Type> Collection<IEObjectDescription> publicImportedTypes(Protobuf root, Class<T> targetType) {
+    List<Import> allImports = finder.publicImportsIn(root);
+    if (allImports.isEmpty()) return emptyList();
+    ResourceSet resourceSet = root.eResource().getResourceSet();
+    return importedTypes(allImports, finder.packageOf(root), resourceSet, targetType);
   }
 
   private boolean arePackagesRelated(Package aPackage, EObject root) {
@@ -140,7 +124,7 @@
     return packageResolver.areRelated(aPackage, p);
   }
 
-  private <T extends Type> Collection<IEObjectDescription> children(Resource resource, Class<T> targetType) {
+  private <T extends Type> Collection<IEObjectDescription> localTypes(Resource resource, Class<T> targetType) {
     List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>();
     TreeIterator<Object> contents = getAllContents(resource, true);
     while (contents.hasNext()) {
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/Imports.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/Imports.java
index ea34fd0..d2ed78e 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/Imports.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/Imports.java
@@ -11,7 +11,8 @@
 import static com.google.eclipse.protobuf.scoping.ProtoDescriptor.DESCRIPTOR_IMPORT_URI;
 
 import com.google.eclipse.protobuf.protobuf.Import;
-import com.google.inject.Singleton;
+import com.google.eclipse.protobuf.scoping.ProtoDescriptorProvider;
+import com.google.inject.*;
 
 /**
  * Utility methods related to imports.
@@ -21,6 +22,8 @@
 @Singleton
 public class Imports {
 
+  @Inject private ProtoDescriptorProvider descriptorProvider;
+
   /**
    * Indicates whether the URI of the given import is equal to the path of descriptor.proto
    * ("google/protobuf/descriptor.proto").
@@ -42,4 +45,14 @@
   public boolean isUnresolvedDescriptorUri(String uri) {
     return DESCRIPTOR_IMPORT_URI.equals(uri);
   }
+
+  /**
+   * Indicates whether the given <code>{@link Import}</code> is pointing to descriptor.proto.
+   * @param anImport the given import to check.
+   * @return {@code true} if the given import is pointing to descriptor.proto, {@code false} otherwise.
+   */
+  public boolean isImportingDescriptor(Import anImport) {
+    String descriptorLocation = descriptorProvider.descriptorLocation().toString();
+    return descriptorLocation.equals(anImport.getImportURI());
+  }
 }
diff --git a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/ProtobufElementFinder.java b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/ProtobufElementFinder.java
index 64ba56d..e3a255c 100644
--- a/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/ProtobufElementFinder.java
+++ b/com.google.eclipse.protobuf/src/com/google/eclipse/protobuf/util/ProtobufElementFinder.java
@@ -9,10 +9,14 @@
 package com.google.eclipse.protobuf.util;
 
 import static java.util.Collections.unmodifiableList;
+import static org.eclipse.emf.ecore.util.EcoreUtil.getAllContents;
 
 import java.util.*;
 
+import org.eclipse.emf.common.util.TreeIterator;
 import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.xtext.resource.XtextResource;
 
 import com.google.eclipse.protobuf.protobuf.*;
 import com.google.eclipse.protobuf.protobuf.Enum;
@@ -103,4 +107,21 @@
     }
     return unmodifiableList(imports);
   }
-}
+
+  /**
+   * Returns the root element of the given resource.
+   * @param resource the given resource.
+   * @return the root element of the given resource, or {@code null} if the given resource does not have a root element.
+   */
+  public Protobuf rootOf(Resource resource) {
+    if (resource instanceof XtextResource) {
+      EObject root = ((XtextResource) resource).getParseResult().getRootASTElement();
+      return (Protobuf) root;
+    }
+    TreeIterator<Object> contents = getAllContents(resource, true);
+    if (contents.hasNext()) {
+      Object next = contents.next();
+      if (next instanceof Protobuf) return (Protobuf) next;
+    }
+    return null;
+  }}