blob: 9758fe2f16ec412ec847e2ef8a266826d8877110 [file] [log] [blame]
/*
* 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);
}
}