/*******************************************************************************
* Copyright (c) 2012 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.grails.ide.eclipse.core.internal.plugins;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResourceDelta;
import org.grails.ide.eclipse.core.internal.classpath.GrailsPluginParser;
import org.grails.ide.eclipse.core.internal.classpath.GrailsPluginVersion;
import org.grails.ide.eclipse.core.internal.classpath.PerProjectDependencyDataCache;
import org.grails.ide.eclipse.core.internal.classpath.PluginDescriptorParser;
import org.grails.ide.eclipse.core.model.ContributedMethod;
import org.grails.ide.eclipse.core.model.ContributedProperty;
import org.grails.ide.eclipse.runtime.shared.DependencyData;
import org.springsource.ide.eclipse.commons.frameworks.core.internal.plugins.BasePluginData;
/**
* Caches information about plugin.xml for each grails plugin on a per-project
* basis
* @author Andrew Eisenberg
* @author Nieraj Singh
* @author Kris De Volder
* @created Jan 28, 2010
*/
public class PerProjectPluginCache implements IGrailsProjectInfo {
private IProject project;
/**
* Map associating a plugin.xml file/path name to a PluginData object.
*/
private Map<String, GrailsPluginVersion> dependencyPluginDataMap;
private Map<String, ContributedProperty> allControllerProps;
private Map<String, ContributedProperty> allDomainProps;
private Map<String, Set<ContributedMethod>> allControllerMethods;
private Map<String, Set<ContributedMethod>> allDomainMethods;
// for testing, used to store extra dependency files
// so that they remain in the cache after refreshes
private List<IFile> extraPluginFiles = new ArrayList<IFile>();
public PerProjectPluginCache() {
}
private void initializePluginData() {
synchronized (GrailsCore.get().getLockForProject(project)) {
allControllerProps = null;
allDomainProps = null;
allControllerMethods = null;
allDomainMethods = null;
dependencyPluginDataMap = new HashMap<String, GrailsPluginVersion>();
// re-parse the plugin descriptors
DependencyData dependencyData = GrailsCore.get().connect(project, PerProjectDependencyDataCache.class).getData();
if (dependencyData != null) {
Set<String> pluginDescriptorPaths = dependencyData.getPluginDescriptors();
if (pluginDescriptorPaths != null) {
for (String path : pluginDescriptorPaths) {
GrailsPluginVersion data = parseData(path);
if (data != null) {
dependencyPluginDataMap.put(path, data);
}
}
}
}
for (IFile file : extraPluginFiles) {
PluginDescriptorParser parser = new PluginDescriptorParser(file.getLocation().toOSString());
GrailsPluginVersion data = parser.parse();
dependencyPluginDataMap.put(file.getFullPath().toOSString(), data);
}
}
}
/**
* Removes the old cache and generates a new cache based on the latest
* dependency data that can be obtained for the given project
*/
public void refreshDependencyCache() {
initializePluginData();
}
private GrailsPluginVersion parseData(String pluginDescriptor) {
if (pluginDescriptor == null) {
return null;
}
return new GrailsPluginParser(pluginDescriptor).parse();
}
public void dispose() {
project = null;
allControllerProps = null;
allDomainProps = null;
allControllerMethods = null;
allDomainMethods = null;
dependencyPluginDataMap.clear();
}
public IProject getProject() {
return project;
}
public void projectChanged(GrailsElementKind[] changeKinds,
IResourceDelta change) {
boolean foundRelevantChange = false;
for (GrailsElementKind changeKind : changeKinds) {
if (changeKind == GrailsElementKind.PROJECT
|| changeKind == GrailsElementKind.CLASSPATH) {
foundRelevantChange = true;
break;
}
}
if (foundRelevantChange) {
initializePluginData();
}
}
public void setProject(IProject project) {
this.project = project;
initializePluginData();
}
public Map<String, ContributedProperty> getAllControllerProperties() {
if (allControllerProps == null) {
allControllerProps = new HashMap<String, ContributedProperty>();
for (GrailsPluginVersion data : dependencyPluginDataMap.values()) {
if (data.getControllerProperties() != null) {
allControllerProps.putAll(data.getControllerProperties());
}
}
}
return allControllerProps;
}
public Map<String, ContributedProperty> getAllDomainProperties() {
if (allDomainProps == null) {
allDomainProps = new HashMap<String, ContributedProperty>();
for (GrailsPluginVersion data : dependencyPluginDataMap.values()) {
if (data.getDomainProperties() != null) {
allDomainProps.putAll(data.getDomainProperties());
}
}
}
return allDomainProps;
}
public Map<String, Set<ContributedMethod>> getAllControllerMethods() {
if (allControllerMethods == null) {
allControllerMethods = new HashMap<String, Set<ContributedMethod>>();
for (GrailsPluginVersion data : dependencyPluginDataMap.values()) {
Map<String, Set<ContributedMethod>> newMethods = data
.getControllerMethods();
for (Entry<String, Set<ContributedMethod>> newMethod : newMethods
.entrySet()) {
Set<ContributedMethod> existing = allControllerMethods
.get(newMethod.getKey());
if (existing == null) {
existing = new HashSet<ContributedMethod>();
allControllerMethods.put(newMethod.getKey(), existing);
}
existing.addAll(newMethod.getValue());
}
}
}
return allControllerMethods;
}
public Map<String, Set<ContributedMethod>> getAllDomainMethods() {
if (allDomainMethods == null) {
allDomainMethods = new HashMap<String, Set<ContributedMethod>>();
for (GrailsPluginVersion data : dependencyPluginDataMap.values()) {
Map<String, Set<ContributedMethod>> newMethods = data
.getDomainMethods();
for (Entry<String, Set<ContributedMethod>> newMethod : newMethods
.entrySet()) {
Set<ContributedMethod> existing = allDomainMethods
.get(newMethod.getKey());
if (existing == null) {
existing = new HashSet<ContributedMethod>();
allDomainMethods.put(newMethod.getKey(), existing);
}
existing.addAll(newMethod.getValue());
}
}
}
return allDomainMethods;
}
public String getDependencyPluginDescriptor(BasePluginData data) {
if (data == null) {
return null;
}
for (Entry<String, GrailsPluginVersion> entry : dependencyPluginDataMap
.entrySet()) {
GrailsPluginVersion dependencyData = entry.getValue();
if (dependencyData.getName() != null
&& dependencyData.getName().equals(data.getName())) {
return entry.getKey();
}
}
return null;
}
/**
* Given a base plugin data will determine if that plugin data corresponds
* to an installed plugin, and if so, will return the plugin data containing
* contributed information.
*/
public GrailsPluginVersion getInstalled(BasePluginData pluginData) {
if (pluginData == null) {
return null;
}
Collection<GrailsPluginVersion> values = dependencyPluginDataMap.values();
for (GrailsPluginVersion installedData : values) {
if (installedData.getName().equals(pluginData.getName())) {
return installedData;
}
}
return null;
}
/**
* Returns a COPY of the cached list of dependencies. Note that this not
* recalculate dependencies. Dependencies must be refreshed separaterly in
* order to get the latest list of dependencies
*
* @return non-null list of dependencies. May be empty.
*/
public Set<GrailsPluginVersion> getCachedDependencies() {
return new LinkedHashSet<GrailsPluginVersion>(dependencyPluginDataMap.values());
}
/**
* Not API!!! for testing
*/
public void addExtraPluginFile(IFile file) {
extraPluginFiles.add(file);
initializePluginData();
}
/**
* Not API!!! for testing
*/
public void flushExtraPluginFiles() {
extraPluginFiles.clear();
initializePluginData();
}
/**
* Caution do not remove any entries from this map.
* @return the map from plugin file name to plugin data
*/
public Map<String, GrailsPluginVersion> getPluginDataMap() {
return dependencyPluginDataMap;
}
@SuppressWarnings("nls")
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("PerProjectPluginCache [project=");
builder.append(project);
builder.append(", dependencyPluginDataMap=");
builder.append(dependencyPluginDataMap);
builder.append(", allControllerProps=");
builder.append(allControllerProps);
builder.append(", allDomainProps=");
builder.append(allDomainProps);
builder.append(", allControllerMethods=");
builder.append(allControllerMethods);
builder.append(", allDomainMethods=");
builder.append(allDomainMethods);
builder.append("]");
return builder.toString();
}
}