/*
* Copyright (c) 2016 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.model.util.QualifiedNames.removeLeadingDot;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.resource.ISelectable;
import org.eclipse.xtext.resource.impl.AliasedEObjectDescription;
import org.eclipse.xtext.scoping.IScope;
import org.eclipse.xtext.scoping.impl.ImportNormalizer;
import org.eclipse.xtext.scoping.impl.ImportScope;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;
/**
* {@link ImportScope} that allows additional ImportNormalizers to be added.
*
* @author (atrookey@google.com) Alexander Rookey
*/
public class ProtobufImportScope extends ImportScope {
private final EClass type;
private List<ImportNormalizer> normalizers;
public ProtobufImportScope(
List<ImportNormalizer> namespaceResolvers,
IScope parent,
ISelectable importFrom,
EClass type,
boolean ignoreCase) {
super(namespaceResolvers, parent, importFrom, type, ignoreCase);
this.type = type;
this.normalizers = removeDuplicates(namespaceResolvers);
}
/*
* Override {@link ImportScope.getAliasedElements(Iterable<IEObjectDescription>)} to use local
* {@link ImportNormalizer} list.
*/
@Override
protected Iterable<IEObjectDescription> getAliasedElements(
Iterable<IEObjectDescription> candidates) {
Multimap<QualifiedName, IEObjectDescription> keyToDescription = LinkedHashMultimap.create();
Multimap<QualifiedName, ImportNormalizer> keyToNormalizer = HashMultimap.create();
for (IEObjectDescription imported : candidates) {
QualifiedName fullyQualifiedName = imported.getName();
for (ImportNormalizer normalizer : normalizers) {
QualifiedName alias = normalizer.deresolve(fullyQualifiedName);
if (alias != null) {
QualifiedName key = alias;
if (isIgnoreCase()) {
key = key.toLowerCase();
}
keyToDescription.put(key, new AliasedEObjectDescription(alias, imported));
keyToNormalizer.put(key, normalizer);
}
}
}
for (QualifiedName name : keyToNormalizer.keySet()) {
if (keyToNormalizer.get(name).size() > 1) keyToDescription.removeAll(name);
}
return keyToDescription.values();
}
/*
* Override {@link ImportScope.getLocalElementsByName(QualifiedName)} to use local
* {@link ImportNormalizer} list.
*/
@Override
protected Iterable<IEObjectDescription> getLocalElementsByName(QualifiedName name) {
List<IEObjectDescription> result = new ArrayList<>();
QualifiedName resolvedQualifiedName = null;
ISelectable importFrom = getImportFrom();
for (ImportNormalizer normalizer : normalizers) {
final QualifiedName resolvedName = normalizer.resolve(name);
if (resolvedName != null) {
Iterable<IEObjectDescription> resolvedElements =
importFrom.getExportedObjects(type, resolvedName, isIgnoreCase());
for (IEObjectDescription resolvedElement : resolvedElements) {
if (resolvedQualifiedName == null) resolvedQualifiedName = resolvedName;
else if (!resolvedQualifiedName.equals(resolvedName)) {
if (result.get(0).getEObjectOrProxy() != resolvedElement.getEObjectOrProxy()) {
return Collections.emptyList();
}
}
QualifiedName alias = normalizer.deresolve(resolvedElement.getName());
if (alias == null)
throw new IllegalStateException(
"Couldn't deresolve " + resolvedElement.getName() + " with import " + normalizer);
final AliasedEObjectDescription aliasedEObjectDescription =
new AliasedEObjectDescription(alias, resolvedElement);
result.add(aliasedEObjectDescription);
}
}
}
return result;
}
/** Before getting element, check for and remove leading dot. */
@Override
public IEObjectDescription getSingleElement(QualifiedName name) {
return super.getSingleElement(removeLeadingDot(name));
}
public void addNormalizer(ImportNormalizer normalizer) {
normalizers.add(normalizer);
}
public void addAllNormalizers(Collection<ImportNormalizer> normalizer) {
normalizers.addAll(normalizer);
}
}