/******************************************************************************* * Copyright (c) 2014 Pivotal, 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, Inc. - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.boot.properties.editor.util; import java.util.HashMap; import java.util.Map; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IResourceDeltaVisitor; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.springframework.ide.eclipse.boot.properties.editor.FuzzyMap; import org.springframework.ide.eclipse.boot.properties.editor.SpringPropertiesEditorPlugin; import org.springframework.ide.eclipse.boot.properties.editor.SpringPropertyIndex; import org.springframework.ide.eclipse.boot.properties.editor.StsConfigMetadataRepositoryJsonLoader; import org.springframework.ide.eclipse.boot.properties.editor.metadata.PropertyInfo; import org.springframework.ide.eclipse.boot.properties.editor.metadata.ValueProviderRegistry; /** * Support for Reconciling, Content Assist and Hover Text in spring properties * file all make use of a per-project index of spring properties metadata extracted * from project's classpath. This Index manager is responsible for keeping at most * one index per-project and to keep the index up-to-date. * * @author Kris De Volder */ public class SpringPropertiesIndexManager extends ListenerManager<Listener<SpringPropertiesIndexManager>> implements ClasspathListener { //TODO: More precise cache flushing? // right now, any detected change that may affect the cached metadata results. // in clearing the entire cache. // Probably this is okay, since reading the data is pretty fast. private Map<String, SpringPropertyIndex> indexes = null; final private ValueProviderRegistry valueProviders; public SpringPropertiesIndexManager(ValueProviderRegistry valueProviders) { this.valueProviders = valueProviders; SpringPropertiesEditorPlugin.getClasspathListeners().addListener(this); ResourcesPlugin.getWorkspace().addResourceChangeListener(new LiveMetadataListener(), IResourceChangeEvent.POST_CHANGE); } public synchronized FuzzyMap<PropertyInfo> get(IJavaProject jp) { String key = jp.getElementName(); if (indexes==null) { indexes = new HashMap<>(); } SpringPropertyIndex index = indexes.get(key); if (index==null) { index = new SpringPropertyIndex(valueProviders, jp); indexes.put(key, index); } return index; } @Override public synchronized void classpathChanged(IJavaProject jp) { clear(); } private void clear() { if (indexes!=null) { indexes.clear(); for (Listener<SpringPropertiesIndexManager> l : getListeners()) { l.changed(this); } } } /** * Called by LiveMetadataListener when a change to live json metadata file in the * output folder of a IJavaProject is detected. * * @param The project on which the metadata change was detected. * @param jsonFile The IFile in project's output folder that was changed. */ public synchronized void liveMetadataChanged(IJavaProject jp, IFile jsonFile) { clear(); } private class LiveMetadataListener implements IResourceChangeListener, IResourceDeltaVisitor { @Override public void resourceChanged(IResourceChangeEvent event) { try { event.getDelta().accept(this); } catch (Exception e) { SpringPropertiesEditorPlugin.log(e); } } public boolean visit(IResourceDelta delta) throws CoreException { IResource r = delta.getResource(); int t = r.getType(); switch (t) { case IResource.PROJECT: IProject p = (IProject)r; if (p.isAccessible() && p.hasNature(JavaCore.NATURE_ID)) { IJavaProject jp = JavaCore.create(p); IPath outputPath = jp.getOutputLocation().removeFirstSegments(1); //output path without project name. for (String metaLoc : StsConfigMetadataRepositoryJsonLoader.PROJECT_META_DATA_LOCATIONS) { IResourceDelta metaDelta = delta.findMember(outputPath.append(metaLoc)); IFile jsonFile = getFile(metaDelta); if (jsonFile!=null) { //interesting change found. liveMetadataChanged(jp, jsonFile); } } } else { //TODO: not accessible (closed or deleted?) // Do we need to do something with these? } return false; default: break; } return true; } /** * @return IFile if this delta's resource is a file or null otherwise. */ private IFile getFile(IResourceDelta delta) { if (delta!=null) { IResource r = delta.getResource(); if (r!=null) { int t = r.getType(); if ((t|IResource.FILE)!=0) { return (IFile)r; } } } return null; } } }