| /* |
| * 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 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 org.eclipse.emf.common.util.*; |
| import org.eclipse.emf.ecore.*; |
| import org.eclipse.emf.ecore.resource.ResourceSet; |
| import org.eclipse.xtext.naming.*; |
| import org.eclipse.xtext.resource.*; |
| import org.eclipse.xtext.scoping.IScope; |
| import org.eclipse.xtext.scoping.impl.*; |
| |
| 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.ProtobufElementFinder; |
| import com.google.inject.Inject; |
| |
| /** |
| * Custom scoping description. |
| * |
| * @author alruiz@google.com (Alex Ruiz) |
| * |
| * @see <a href="http://www.eclipse.org/Xtext/documentation/latest/xtext.html#scoping">Xtext Scoping</a> |
| */ |
| public class ProtobufScopeProvider extends AbstractDeclarativeScopeProvider { |
| |
| private static final boolean DO_NOT_IGNORE_CASE = false; |
| |
| @Inject private ProtobufElementFinder finder; |
| @Inject private DescriptorProvider descriptorProvider; |
| @Inject private IQualifiedNameProvider nameProvider; |
| @Inject private ImportUriResolver uriResolver; |
| @Inject private LocalNamesProvider localNamesProvider; |
| @Inject private PackageResolver packageResolver; |
| |
| @SuppressWarnings("unused") |
| IScope scope_TypeReference_type(TypeReference typeRef, EReference reference) { |
| Protobuf root = finder.rootOf(typeRef); |
| Set<IEObjectDescription> descriptions = new HashSet<IEObjectDescription>(); |
| EObject current = typeRef.eContainer().eContainer(); // get message of the property containing the TypeReference |
| while (current != null) { |
| descriptions.addAll(typesIn(current)); |
| current = current.eContainer(); |
| } |
| descriptions.addAll(importedTypes(root, Type.class)); |
| return createScope(descriptions); |
| } |
| |
| private Collection<IEObjectDescription> typesIn(EObject root) { |
| return children(root, Type.class); |
| } |
| |
| @SuppressWarnings("unused") |
| IScope scope_MessageReference_type(MessageReference msgRef, EReference reference) { |
| Protobuf root = finder.rootOf(msgRef); |
| Set<IEObjectDescription> descriptions = new HashSet<IEObjectDescription>(); |
| descriptions.addAll(messagesIn(root)); |
| descriptions.addAll(importedTypes(root, Message.class)); |
| return createScope(descriptions); |
| } |
| |
| private Collection<IEObjectDescription> messagesIn(Protobuf root) { |
| return children(root, Message.class); |
| } |
| |
| private <T extends Type> Collection<IEObjectDescription> children(EObject root, Class<T> targetType) { |
| return children(root, targetType, 0); |
| } |
| |
| private <T extends Type> Collection<IEObjectDescription> children(EObject root, Class<T> targetType, int level) { |
| List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>(); |
| for (EObject element : root.eContents()) { |
| if (!targetType.isInstance(element)) continue; |
| List<QualifiedName> names = localNamesProvider.namesOf(element); |
| int nameCount = names.size(); |
| for (int i = level; i < nameCount; i++) { |
| descriptions.add(create(names.get(i), element)); |
| } |
| descriptions.add(create(nameProvider.getFullyQualifiedName(element), element)); |
| // TODO investigate if groups can have messages, and if so, add those messages to the scope. |
| if (element instanceof Message) { |
| descriptions.addAll(children(element, targetType, level + 1)); |
| } |
| } |
| return descriptions; |
| } |
| |
| private <T extends Type> Collection<IEObjectDescription> importedTypes(Protobuf root, Class<T> targetType) { |
| List<Import> imports = finder.importsIn(root); |
| if (imports.isEmpty()) return emptyList(); |
| Package importRootPackage = finder.packageOf(root); |
| List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>(); |
| for (Import anImport : imports) { |
| XtextResource imported = importedResource(anImport); |
| EObject importedRoot = imported.getParseResult().getRootASTElement(); |
| if (arePackagesRelated(importRootPackage, importedRoot)) { |
| descriptions.addAll(typesIn(importedRoot)); |
| continue; |
| } |
| descriptions.addAll(children(imported, targetType)); |
| } |
| return descriptions; |
| } |
| |
| private XtextResource importedResource(Import anImport) { |
| ResourceSet resourceSet = finder.rootOf(anImport).eResource().getResourceSet(); |
| URI importUri = createURI(uriResolver.apply(anImport)); |
| return (XtextResource) resourceSet.getResource(importUri, true); |
| } |
| |
| private boolean arePackagesRelated(Package aPackage, EObject root) { |
| Package p = finder.packageOf(root); |
| return packageResolver.areRelated(aPackage, p); |
| } |
| |
| private <T extends Type> Collection<IEObjectDescription> children(XtextResource resource, Class<T> targetType) { |
| List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>(); |
| TreeIterator<Object> contents = getAllContents(resource, true); |
| while (contents.hasNext()) { |
| Object next = contents.next(); |
| if (!targetType.isInstance(next)) continue; |
| T type = targetType.cast(next); |
| descriptions.add(create(nameProvider.getFullyQualifiedName(type), type)); |
| } |
| return descriptions; |
| } |
| |
| @SuppressWarnings("unused") |
| IScope scope_LiteralRef_literal(LiteralRef literalRef, EReference reference) { |
| EObject container = literalRef.eContainer(); |
| if (container instanceof Property) { |
| Enum enumType = finder.enumTypeOf((Property) container); |
| if (enumType != null) return scopeForLiterals(enumType); |
| } |
| Descriptor descriptor = descriptorProvider.get(); |
| if (container instanceof Option && descriptor.isOptimizeForOption((Option) container)) { |
| Enum optimizedMode = descriptor.optimizedMode(); |
| return scopeForLiterals(optimizedMode); |
| } |
| if (container instanceof FieldOption && descriptor.isCTypeOption((FieldOption) container)) { |
| Enum cType = descriptor.cType(); |
| return scopeForLiterals(cType); |
| } |
| return null; |
| } |
| |
| private static IScope scopeForLiterals(Enum enumType) { |
| Collection<IEObjectDescription> descriptions = describeLiterals(enumType); |
| return createScope(descriptions); |
| } |
| |
| private static Collection<IEObjectDescription> describeLiterals(Enum enumType) { |
| List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>(); |
| for (Literal literal : enumType.getLiterals()) |
| descriptions.add(create(literal.getName(), literal)); |
| return descriptions; |
| } |
| |
| private static IScope createScope(Iterable<IEObjectDescription> descriptions) { |
| return new SimpleScope(descriptions, DO_NOT_IGNORE_CASE); |
| } |
| } |