| /* |
| * 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; |
| } |
| } |
| } |