/******************************************************************************* * Copyright (c) 2010 Spring IDE Developers * 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: * Spring IDE Developers - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.beans.core.internal.model.namespaces; import java.util.ArrayList; import java.util.List; import org.eclipse.core.resources.IProject; import org.eclipse.jdt.core.ElementChangedEvent; import org.eclipse.jdt.core.IElementChangedListener; import org.eclipse.jdt.core.IJavaElementDelta; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.springframework.ide.eclipse.beans.core.model.INamespaceDefinitionResolver; import org.springframework.ide.eclipse.core.java.JdtUtils; /** * @author Christian Dupuis */ public class ProjectClasspathNamespaceDefinitionResolverCache { private static final int CACHE_SIZE = 12; private static List<ResolvlerCacheEntry> RESOLVER_CACHE = new ArrayList<ResolvlerCacheEntry>(CACHE_SIZE); private static INamespaceDefinitionResolver addResolverToCache(IProject project) { synchronized (RESOLVER_CACHE) { int nEntries = RESOLVER_CACHE.size(); if (nEntries >= CACHE_SIZE) { // find obsolete entries or remove entry that was least recently accessed ResolvlerCacheEntry oldest = null; List<ResolvlerCacheEntry> obsoleteClassLoaders = new ArrayList<ResolvlerCacheEntry>(CACHE_SIZE); for (int i = 0; i < nEntries; i++) { ResolvlerCacheEntry entry = RESOLVER_CACHE.get(i); IProject curr = entry.getProject(); if (!curr.exists() || !curr.isAccessible() || !curr.isOpen()) { obsoleteClassLoaders.add(entry); } else { if (oldest == null || entry.getLastAccess() < oldest.getLastAccess()) { oldest = entry; } } } if (!obsoleteClassLoaders.isEmpty()) { for (int i = 0; i < obsoleteClassLoaders.size(); i++) { removeResolverEntryFromCache(obsoleteClassLoaders.get(i)); } } else if (oldest != null) { removeResolverEntryFromCache(oldest); } } ResolvlerCacheEntry newEntry = new ResolvlerCacheEntry(project); RESOLVER_CACHE.add(newEntry); return newEntry.getResolver(); } } private static INamespaceDefinitionResolver findResolverInCache(IProject project) { synchronized (RESOLVER_CACHE) { for (int i = RESOLVER_CACHE.size() - 1; i >= 0; i--) { ResolvlerCacheEntry entry = RESOLVER_CACHE.get(i); IProject curr = entry.getProject(); if (!curr.exists() || !curr.isAccessible() || !curr.isOpen()) { removeResolverEntryFromCache(entry); } else { if (entry.matches(project)) { entry.markAsAccessed(); return entry.getResolver(); } } } } return null; } private static void removeResolverEntryFromCache(ResolvlerCacheEntry entry) { synchronized (RESOLVER_CACHE) { entry.dispose(); RESOLVER_CACHE.remove(entry); } } public synchronized static INamespaceDefinitionResolver getResolver(IProject project) { INamespaceDefinitionResolver resolver = findResolverInCache(project); if (resolver == null) { resolver = addResolverToCache(project); } return resolver; } /** * Internal cache entry */ private static class ResolvlerCacheEntry implements IElementChangedListener { private long lastAccess; private IProject project; private ProjectClasspathNamespaceDefinitionResolver resolver; public ResolvlerCacheEntry(IProject project) { this.project = project; this.resolver = new ProjectClasspathNamespaceDefinitionResolver(project); markAsAccessed(); JavaCore.addElementChangedListener(this, ElementChangedEvent.POST_CHANGE); } public void dispose() { JavaCore.removeElementChangedListener(this); resolver.dispose(); resolver = null; } public void elementChanged(ElementChangedEvent event) { IJavaProject javaProject = JdtUtils.getJavaProject(project); if (javaProject != null) { for (IJavaElementDelta delta : event.getDelta().getAffectedChildren()) { if ((delta.getFlags() & IJavaElementDelta.F_RESOLVED_CLASSPATH_CHANGED) != 0 || (delta.getFlags() & IJavaElementDelta.F_CLASSPATH_CHANGED) != 0) { if (javaProject.equals(delta.getElement()) || javaProject.isOnClasspath(delta.getElement())) { removeResolverEntryFromCache(this); } } } } } public INamespaceDefinitionResolver getResolver() { return resolver; } public long getLastAccess() { return lastAccess; } public IProject getProject() { return this.project; } public void markAsAccessed() { lastAccess = System.currentTimeMillis(); } public boolean matches(IProject project) { return this.project.equals(project); } } }