/*
* 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 java.util.Collections.emptySet;
import static java.util.Collections.unmodifiableSet;
import static org.eclipse.emf.ecore.util.EcoreUtil.getAllContents;
import static com.google.common.collect.Sets.newHashSet;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.xtext.resource.IEObjectDescription;
import com.google.eclipse.protobuf.model.util.Imports;
import com.google.eclipse.protobuf.model.util.ModelObjects;
import com.google.eclipse.protobuf.model.util.Packages;
import com.google.eclipse.protobuf.model.util.Protobufs;
import com.google.eclipse.protobuf.model.util.Resources;
import com.google.eclipse.protobuf.protobuf.Group;
import com.google.eclipse.protobuf.protobuf.Import;
import com.google.eclipse.protobuf.protobuf.Message;
import com.google.eclipse.protobuf.protobuf.Package;
import com.google.eclipse.protobuf.protobuf.Protobuf;
import com.google.eclipse.protobuf.resource.ResourceSets;
import com.google.inject.Inject;
/**
* @author alruiz@google.com (Alex Ruiz)
*/
class ModelElementFinder {
@Inject private Imports imports;
@Inject private ModelObjects modelObjects;
@Inject private Packages packages;
@Inject private Protobufs protobufs;
@Inject private Resources resources;
@Inject private ResourceSets resourceSets;
<T> Collection<IEObjectDescription> find(EObject start, FinderStrategy<T> strategy, T criteria) {
Set<IEObjectDescription> descriptions = newHashSet();
descriptions.addAll(local(start, strategy, criteria));
Protobuf root = modelObjects.rootOf(start);
descriptions.addAll(imported(root, strategy, criteria));
return unmodifiableSet(descriptions);
}
private <T> Collection<IEObjectDescription> local(EObject start, FinderStrategy<T> strategy, T criteria) {
UniqueDescriptions descriptions = new UniqueDescriptions();
EObject current = start.eContainer();
while (current != null) {
descriptions.addAll(local(current, strategy, criteria, 0));
current = current.eContainer();
}
return descriptions.values();
}
<T> Collection<IEObjectDescription> find(Protobuf start, FinderStrategy<T> strategy, T criteria) {
Set<IEObjectDescription> descriptions = newHashSet();
descriptions.addAll(local(start, strategy, criteria, 0));
descriptions.addAll(imported(start, strategy, criteria));
return unmodifiableSet(descriptions);
}
private <T> Collection<IEObjectDescription> local(EObject start, FinderStrategy<T> strategy, T criteria, int level) {
UniqueDescriptions descriptions = new UniqueDescriptions();
for (EObject element : start.eContents()) {
descriptions.addAll(strategy.local(element, criteria, level));
if (element instanceof Message || element instanceof Group) {
descriptions.addAll(local(element, strategy, criteria, level + 1));
}
}
return descriptions.values();
}
private <T> Collection<IEObjectDescription> imported(Protobuf start, FinderStrategy<T> strategy, T criteria) {
List<Import> allImports = protobufs.importsIn(start);
if (allImports.isEmpty()) {
return emptyList();
}
ResourceSet resourceSet = start.eResource().getResourceSet();
return imported(allImports, modelObjects.packageOf(start), resourceSet, strategy, criteria);
}
private <T> Collection<IEObjectDescription> imported(List<Import> allImports, Package fromImporter,
ResourceSet resourceSet, FinderStrategy<T> strategy, T criteria) {
Set<IEObjectDescription> descriptions = newHashSet();
for (Import anImport : allImports) {
if (imports.isImportingDescriptor(anImport)) {
descriptions.addAll(strategy.inDescriptor(anImport, criteria));
continue;
}
URI resolvedUri = imports.resolvedUriOf(anImport);
if (resolvedUri == null) {
continue;
}
Resource imported = resourceSets.findResource(resourceSet, resolvedUri);
if (imported == null) {
continue;
}
Protobuf rootOfImported = resources.rootOf(imported);
if (!protobufs.isProto2(rootOfImported)) {
continue;
}
if (rootOfImported != null) {
descriptions.addAll(publicImported(rootOfImported, strategy, criteria));
if (arePackagesRelated(fromImporter, rootOfImported)) {
descriptions.addAll(local(rootOfImported, strategy, criteria, 0));
continue;
}
Package packageOfImported = modelObjects.packageOf(rootOfImported);
descriptions.addAll(imported(fromImporter, packageOfImported, imported, strategy, criteria));
}
}
return descriptions;
}
private <T> Collection<IEObjectDescription> publicImported(Protobuf start, FinderStrategy<T> strategy, T criteria) {
if (!protobufs.isProto2(start)) {
return emptySet();
}
List<Import> allImports = protobufs.publicImportsIn(start);
if (allImports.isEmpty()) {
return emptyList();
}
ResourceSet resourceSet = start.eResource().getResourceSet();
return imported(allImports, modelObjects.packageOf(start), resourceSet, strategy, criteria);
}
private boolean arePackagesRelated(Package aPackage, EObject root) {
Package p = modelObjects.packageOf(root);
return packages.areRelated(aPackage, p);
}
private <T> Collection<IEObjectDescription> imported(Package fromImporter, Package fromImported, Resource resource,
FinderStrategy<T> strategy, T criteria) {
Set<IEObjectDescription> descriptions = newHashSet();
TreeIterator<Object> contents = getAllContents(resource, true);
while (contents.hasNext()) {
Object next = contents.next();
descriptions.addAll(strategy.imported(fromImporter, fromImported, next, criteria));
}
return descriptions;
}
static interface FinderStrategy<T> {
Collection<IEObjectDescription> imported(Package fromImporter, Package fromImported, Object target, T criteria);
Collection<IEObjectDescription> inDescriptor(Import anImport, T criteria);
Collection<IEObjectDescription> local(Object target, T criteria, int level);
}
}