/******************************************************************************* * Copyright (c) 2013 Pivotal Software, 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 * * Contributors: * Pivotal Software, Inc. - initial API and implementation *******************************************************************************/ package org.springsource.ide.eclipse.commons.completions.externaltype; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtensionRegistry; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jdt.core.IJavaProject; import org.springsource.ide.eclipse.commons.completions.CompletionsActivator; import org.springsource.ide.eclipse.commons.completions.externaltype.indexing.ExternalTypeIndexer; import org.springsource.ide.eclipse.commons.completions.externaltype.indexing.SimpleExternalTypeIndexer; /** * Only a single instance of this class typically exists. Its purpose is to maintain a mapping between * {@link IJavaProject} instances and their respective ExternalType indexes. * <p> * External types are project specific. * * @author Kris De Volder */ public class ExternalTypeIndexManager { private static final String TYPE_SOURCES_EXTENSION_POINT = "org.spring.ide.eclipse.completions.externaltypes"; private static final ExternalTypeIndexManager INSTANCE = new ExternalTypeIndexManager(); public static synchronized ExternalTypeIndexer indexFor(IJavaProject project) { return INSTANCE.getIndexFor(project); } /** * For now we only keep one index. If more than is need accross the projects in the workspace * this may be very bad becayse the index is thrown away and rebuilt each time we have to switch. * <p> * With the current implementers of this extension point however it is ok because there * is only one and it always provides discovery that is shareable between all projects it applies to. */ private ExternalTypeIndexer index = null; /** * This 'key' should identify the content of an indexer so that if the key changes (not equals) then * the indexer is no longer valid and the index should be rebuilt. * <p> * For now the assumption is that mostly all projects have a non-empty index will be sharing it. * This is true for now, but maybe not always be true in the future if different implementations * of the extension point provide disovery algorithms that vary depending on project type, * library versions etc. */ private Set<ExternalTypeDiscovery> contentKey = null; public synchronized ExternalTypeIndexer getIndexFor(IJavaProject project) { final HashSet<ExternalTypeDiscovery> newContentKey = new HashSet<ExternalTypeDiscovery>(); for (ExternalTypeDiscoveryFactory factory : getFactories()) { ExternalTypeDiscovery discovery = factory.discoveryFor(project); if (discovery!=null) { newContentKey.add(discovery); } } if (newContentKey.isEmpty()) { //A useful special case: if none of the factories applies to our current project... // the empty index is pretty cheap so don't throw away a more valuable index because of it. return ExternalTypeIndexer.EMPTY; } if (index!=null && contentKey.equals(newContentKey)) { return index; } index = new SimpleExternalTypeIndexer(); contentKey = newContentKey; Job rebuildIndex = new Job("Indexing Jar Types") { @Override protected IStatus run(IProgressMonitor mon) { mon.beginTask("Indexing Jar Types", contentKey.size()); try { //Must (re)build the index for (ExternalTypeDiscovery discovery : contentKey) { index.addFrom(discovery); mon.worked(1); } } finally { mon.done(); } return Status.OK_STATUS; } }; rebuildIndex.setPriority(Job.DECORATE); rebuildIndex.schedule(); return index; } private static List<ExternalTypeDiscoveryFactory> factories = null; /** * Read the extension point. */ private static synchronized List<ExternalTypeDiscoveryFactory> getFactories() { if (factories==null) { IExtensionRegistry registry = Platform.getExtensionRegistry(); IConfigurationElement[] confEls = registry.getConfigurationElementsFor(TYPE_SOURCES_EXTENSION_POINT); for (IConfigurationElement confEl : confEls) { String name = confEl.getName(); if ("typeSource".equals(name)) { try { ExternalTypeDiscoveryFactory factory = (ExternalTypeDiscoveryFactory) confEl.createExecutableExtension("class"); if (factories==null) { // We may loose some confEls from errors but confEls.length is still a good guess // for the size of the arraylist factories = new ArrayList<ExternalTypeDiscoveryFactory>(confEls.length); } factories.add(factory); } catch (CoreException e) { CompletionsActivator.log(e); } } } if (factories==null || factories.isEmpty()) { factories = Collections.emptyList(); } } return factories; } }