blob: 9605687f45484639bd2e2c7726c341c77d8bbaf0 [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 com.google.eclipse.protobuf.preferences.general.PreferenceNames.DESCRIPTOR_PROTO_PATH;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableList;
import com.google.eclipse.protobuf.model.util.INodes;
import com.google.eclipse.protobuf.preferences.general.GeneralPreferences;
import com.google.eclipse.protobuf.preferences.general.PreferenceNames;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.emf.common.util.URI;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.xtext.parser.IParser;
import org.eclipse.xtext.ui.editor.preferences.IPreferenceStoreAccess;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Provider of <code>{@link ProtoDescriptor}</code>s.
*
* @author Alex Ruiz
*/
@Singleton public class ProtoDescriptorProvider {
private static final String EXTENSION_ID = "com.google.eclipse.protobuf.descriptorSource";
private final IPreferenceStoreAccess storeAccess;
private final IExtensionRegistry registry;
private final IParser parser;
private final INodes nodes;
private final IUriResolver resolver;
private static final URI DEFAULT_DESCRIPTOR_LOCATION =
URI.createURI("platform:/plugin/com.google.eclipse.protobuf/descriptor.proto");
private final ProtoDescriptorInfo openSourceProtoDescriptorInfo;
private final ProtoDescriptorInfo extensionPointDescriptorInfo;
private static final Logger LOG =
Logger.getLogger(ProtoDescriptorProvider.class.getCanonicalName());
private final
LoadingCache<IProject, Map<String, ProtoDescriptorInfo>> descriptorCache = CacheBuilder
.newBuilder().build(new CacheLoader<IProject, Map<String, ProtoDescriptorInfo>>() {
@Override
public Map<String, ProtoDescriptorInfo> load(final IProject project) {
return loadDescriptorInfos(project);
}
});
@Inject
ProtoDescriptorProvider(IPreferenceStoreAccess storeAccess, IExtensionRegistry registry,
IParser parser, INodes nodes, IUriResolver resolver) {
this.storeAccess = storeAccess;
this.registry = registry;
this.parser = parser;
this.nodes = nodes;
this.resolver = resolver;
this.openSourceProtoDescriptorInfo = getOpenSourceProtoDescriptorInfo();
this.extensionPointDescriptorInfo = getExtensionPointDescriptorInfo();
}
public ProtoDescriptor primaryDescriptor(IProject project) {
Map<String, ProtoDescriptorInfo> descriptorInfos = getDescriptorInfosFor(project);
for (ProtoDescriptorInfo descriptorInfo : descriptorInfos.values()) {
return descriptorInfo.protoDescriptor;
}
return openSourceProtoDescriptorInfo.protoDescriptor;
}
public ProtoDescriptor descriptor(IProject project, String importUri) {
Map<String, ProtoDescriptorInfo> descriptorInfos = getDescriptorInfosFor(project);
ProtoDescriptorInfo descriptorInfo = descriptorInfos.get(importUri);
if (descriptorInfo != null) {
return descriptorInfo.protoDescriptor;
}
URI uri = URI.createURI(importUri);
for (Entry<String, ProtoDescriptorInfo> info : descriptorInfos.entrySet()) {
if (info.getValue().location.equals(uri)) {
return descriptor(project, info.getKey());
}
}
return null;
}
public ImmutableList<URI> allDescriptorLocations(IProject project) {
Map<String, ProtoDescriptorInfo> descriptorInfos = getDescriptorInfosFor(project);
ImmutableList.Builder<URI> descriptorLocations = ImmutableList.builder();
for (ProtoDescriptorInfo descriptorInfo : descriptorInfos.values()) {
descriptorLocations.add(descriptorInfo.location);
}
return descriptorLocations.build();
}
public URI descriptorLocation(IProject project, String importUri) {
Map<String, ProtoDescriptorInfo> descriptorInfos = getDescriptorInfosFor(project);
for (ProtoDescriptorInfo descriptorInfo : descriptorInfos.values()) {
if (descriptorInfo.importUri.equals(importUri)) {
return descriptorInfo.location;
}
}
return null;
}
private Map<String, ProtoDescriptorInfo> loadDescriptorInfos(final IProject project) {
Map<String, ProtoDescriptorInfo> descriptorInfos =
new LinkedHashMap<String, ProtoDescriptorProvider.ProtoDescriptorInfo>();
// Add descriptor.proto from preferences
GeneralPreferences preferences = new GeneralPreferences(storeAccess, project);
String descriptorProtoUri = preferences.getDescriptorProtoPath();
if (!PreferenceNames.DEFAULT_DESCRIPTOR_PATH.equals(descriptorProtoUri)) {
String resolvedUri = resolver.resolveUri(descriptorProtoUri, null, project);
if (resolvedUri != null) {
URI descriptorProtoLocation = URI.createURI(resolvedUri);
if (descriptorProtoLocation != null) {
ProtoDescriptor protoDescriptor =
new ProtoDescriptor(descriptorProtoUri, descriptorProtoLocation, parser, nodes);
ProtoDescriptorInfo descriptorInfo =
new ProtoDescriptorInfo(descriptorProtoUri, descriptorProtoLocation, protoDescriptor);
descriptorInfos.put(descriptorProtoUri, descriptorInfo);
}
} else {
LOG.log(Level.WARNING,
"Unable to resolve URI for descriptor proto location: " + descriptorProtoUri);
}
}
// Add the extension point descriptor proto
if (extensionPointDescriptorInfo != null) {
if (!descriptorInfos.containsKey(extensionPointDescriptorInfo.importUri)) {
descriptorInfos.put(extensionPointDescriptorInfo.importUri, extensionPointDescriptorInfo);
}
}
// Add the open source descriptor proto
if (!descriptorInfos.containsKey(PreferenceNames.DEFAULT_DESCRIPTOR_PATH)) {
descriptorInfos.put(PreferenceNames.DEFAULT_DESCRIPTOR_PATH,
openSourceProtoDescriptorInfo);
}
// Set property change listener for this project
storeAccess.getContextPreferenceStore(project).addPropertyChangeListener(
new IPropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent event) {
if (event.getProperty().contains(DESCRIPTOR_PROTO_PATH)) {
descriptorCache.invalidate(project);
}
}
});
return descriptorInfos;
}
private Map<String, ProtoDescriptorInfo> getDescriptorInfosFor(IProject project) {
if (project == null) {
Map<String, ProtoDescriptorInfo> descriptorInfos =
new LinkedHashMap<>();
descriptorInfos.put(PreferenceNames.DEFAULT_DESCRIPTOR_PATH, openSourceProtoDescriptorInfo);
return descriptorInfos;
}
try {
return descriptorCache.get(project);
} catch (ExecutionException e) {
LOG.log(Level.SEVERE, "Error while trying to determine descriptor.proto for project", e);
return null;
}
}
private ProtoDescriptorInfo getOpenSourceProtoDescriptorInfo() {
ProtoDescriptor descriptor = new ProtoDescriptor(PreferenceNames.DEFAULT_DESCRIPTOR_PATH,
DEFAULT_DESCRIPTOR_LOCATION, parser, nodes);
return new ProtoDescriptorInfo(PreferenceNames.DEFAULT_DESCRIPTOR_PATH,
DEFAULT_DESCRIPTOR_LOCATION, descriptor);
}
private ProtoDescriptorInfo getExtensionPointDescriptorInfo() {
IConfigurationElement[] config = registry.getConfigurationElementsFor(EXTENSION_ID);
if (config == null) {
return null;
}
for (IConfigurationElement e : config) {
ProtoDescriptorInfo info = descriptorInfo(e);
if (info != null) {
return info;
}
}
return null;
}
private ProtoDescriptorInfo descriptorInfo(IConfigurationElement e) {
String importUri = e.getAttribute("importUri");
if (importUri.isEmpty()) {
return null;
}
URI location = descriptorLocation(e);
if (location == null) {
return null;
}
try {
return new ProtoDescriptorInfo(importUri, location,
new ProtoDescriptor(importUri, location, parser, nodes));
} catch (IllegalStateException exception) {
LOG.log(Level.WARNING, "Error when initializing descriptor proto from extension point",
exception);
return null;
}
}
private static URI descriptorLocation(IConfigurationElement e) {
String path = e.getAttribute("path");
if (path.isEmpty()) {
return null;
}
StringBuilder uri = new StringBuilder();
uri.append("platform:/plugin/").append(e.getContributor().getName()).append("/").append(path);
return URI.createURI(uri.toString());
}
private static class ProtoDescriptorInfo {
final String importUri;
final URI location;
final ProtoDescriptor protoDescriptor;
ProtoDescriptorInfo(String importUri, URI location, ProtoDescriptor protoDescriptor) {
this.importUri = importUri;
this.location = location;
this.protoDescriptor = protoDescriptor;
}
}
}