| /******************************************************************************* |
| * Copyright (c) 2010 BSI Business Systems Integration AG. |
| * 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: |
| * BSI Business Systems Integration AG - initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.scout.sdk.util.internal.typecache; |
| |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.IJavaElementDelta; |
| import org.eclipse.jdt.core.IRegion; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.scout.commons.CollectionUtility; |
| import org.eclipse.scout.sdk.util.internal.SdkUtilActivator; |
| import org.eclipse.scout.sdk.util.jdt.JdtEvent; |
| import org.eclipse.scout.sdk.util.type.TypeUtility; |
| import org.eclipse.scout.sdk.util.typecache.ICachedTypeHierarchy; |
| import org.eclipse.scout.sdk.util.typecache.ICachedTypeHierarchyResult; |
| import org.eclipse.scout.sdk.util.typecache.IHierarchyCache; |
| import org.eclipse.scout.sdk.util.typecache.ITypeHierarchy; |
| import org.eclipse.scout.sdk.util.typecache.TypeHierarchyConstraints; |
| |
| public final class HierarchyCache implements IHierarchyCache { |
| |
| private static final HierarchyCache INSTANCE = new HierarchyCache(); |
| |
| private final Map<Object, ICacheableTypeHierarchyResult> m_cachedHierarchyResults; |
| |
| public static HierarchyCache getInstance() { |
| return INSTANCE; |
| } |
| |
| private HierarchyCache() { |
| m_cachedHierarchyResults = new HashMap<Object, ICacheableTypeHierarchyResult>(); |
| } |
| |
| @Override |
| public synchronized void dispose() { |
| invalidateAll(); // to ensure the resources are released. |
| m_cachedHierarchyResults.clear(); |
| } |
| |
| public synchronized List<ICacheableTypeHierarchyResult> getAllCachedHierarchies() { |
| return getHierarchiesSafe(); |
| } |
| |
| @Override |
| public ICachedTypeHierarchyResult getProjectContextTypeHierarchy(TypeHierarchyConstraints constraints) { |
| ICacheableTypeHierarchyResult h = null; |
| synchronized (this) { |
| h = m_cachedHierarchyResults.get(constraints); |
| if (h != null && !TypeUtility.exists(h.getBaseType())) { |
| h.invalidate(); |
| m_cachedHierarchyResults.remove(constraints); |
| h = null; |
| } |
| if (h == null) { |
| h = new ProjectContextTypeHierarchyResult(constraints); |
| m_cachedHierarchyResults.put(constraints, h); |
| } |
| } |
| return h; |
| } |
| |
| @Override |
| public ICachedTypeHierarchy getTypeHierarchy(IType type) { |
| if (!TypeUtility.exists(type) || !type.getJavaProject().exists()) { |
| throw new IllegalArgumentException("type does not exist!"); |
| } |
| ICacheableTypeHierarchyResult hierarchy = null; |
| synchronized (this) { |
| hierarchy = m_cachedHierarchyResults.get(type); |
| if (hierarchy != null && (!TypeUtility.exists(hierarchy.getBaseType()) || !TypeUtility.exists(hierarchy.getBaseType().getJavaProject()))) { |
| // discard old create new |
| hierarchy.invalidate(); |
| m_cachedHierarchyResults.remove(type); |
| hierarchy = null; |
| } |
| if (hierarchy == null) { |
| hierarchy = new CachedTypeHierarchy(type); |
| m_cachedHierarchyResults.put(type, hierarchy); |
| } |
| } |
| return (ICachedTypeHierarchy) hierarchy; |
| } |
| |
| @Override |
| public ICachedTypeHierarchy getPrimaryTypeHierarchy(IType type) { |
| if (type != null && TypeUtility.exists(type.getDeclaringType())) { |
| throw new IllegalArgumentException("type '" + type.getFullyQualifiedName() + "' must be a primary type."); |
| } |
| return new CachedPrimaryTypeHierarchy(getTypeHierarchy(type)); |
| } |
| |
| synchronized void removeCachedHierarchy(IType type) { |
| m_cachedHierarchyResults.remove(type); |
| } |
| |
| synchronized void replaceCachedHierarchy(Object oldKey, Object newKey, ICacheableTypeHierarchyResult hierarchyToAdd) { |
| m_cachedHierarchyResults.remove(oldKey); |
| m_cachedHierarchyResults.put(newKey, hierarchyToAdd); |
| } |
| |
| @Override |
| public ITypeHierarchy getLocalTypeHierarchy(IRegion region) { |
| try { |
| return new TypeHierarchy(null, JavaCore.newTypeHierarchy(region, null, null)); |
| } |
| catch (JavaModelException e) { |
| if (!e.isDoesNotExist()) { |
| SdkUtilActivator.logWarning("could not build hierarchy of region '" + region + "'.", e); |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public ITypeHierarchy getSupertypeHierarchy(IType type) { |
| if (!TypeUtility.exists(type)) { |
| return null; |
| } |
| try { |
| return new TypeHierarchy(type, type.newSupertypeHierarchy(null)); |
| } |
| catch (JavaModelException e) { |
| SdkUtilActivator.logWarning("could not build super hierarchy '" + type.getFullyQualifiedName() + "'.", e); |
| } |
| return null; |
| } |
| |
| @Override |
| public synchronized void invalidateAll() { |
| List<ICacheableTypeHierarchyResult> hierarchies = getHierarchiesSafe(); // get a copy to prevent ConcurrentModificationException |
| for (ICacheableTypeHierarchyResult h : hierarchies) { |
| if (h.isCreated()) { |
| h.invalidate(); |
| } |
| } |
| } |
| |
| private synchronized List<ICacheableTypeHierarchyResult> getHierarchiesSafe() { |
| return CollectionUtility.arrayList(m_cachedHierarchyResults.values()); |
| } |
| |
| private void handleTypeChange(IType t, ITypeHierarchy superTypeHierarchy) { |
| if (superTypeHierarchy != null) { |
| List<ICacheableTypeHierarchyResult> hierarchies = getHierarchiesSafe(); |
| Set<IType> superTypes = superTypeHierarchy.getAllSupertypes(t); |
| for (ICacheableTypeHierarchyResult h : hierarchies) { |
| if (h.isCreated() && h.contains(t) != h.isTypeAccepted(t, superTypes)) { |
| h.invalidate(); |
| } |
| } |
| } |
| } |
| |
| private void handleTypeRemoved(IType type) { |
| List<ICacheableTypeHierarchyResult> hierarchies = getHierarchiesSafe(); |
| for (ICacheableTypeHierarchyResult h : hierarchies) { |
| if (h.isCreated() && h.contains(type)) { |
| h.invalidate(); |
| } |
| } |
| } |
| |
| private void handleJavaElementRemoved(IJavaElement element) { |
| if (element.getElementType() < IJavaElement.TYPE) { |
| List<ICacheableTypeHierarchyResult> hierarchies = getHierarchiesSafe(); |
| for (ICacheableTypeHierarchyResult h : hierarchies) { |
| if (h.isCreated()) { |
| if (TypeUtility.isAncestor(element, h.getBaseType())) { |
| h.invalidate(); |
| } |
| else { |
| for (IType candidate : h) { |
| if (TypeUtility.isAncestor(element, candidate)) { |
| h.invalidate(); |
| break; |
| } |
| } |
| } |
| } |
| } |
| } |
| else if (element.getElementType() == IJavaElement.TYPE) { |
| handleTypeRemoved((IType) element); |
| } |
| } |
| |
| private void handleCompilationUnitChanged(ICompilationUnit icu) { |
| if (!TypeUtility.exists(icu)) { |
| return; |
| } |
| |
| IRegion region = JavaCore.newRegion(); |
| region.add(icu); |
| try { |
| ITypeHierarchy hierarchy = getLocalTypeHierarchy(region); |
| if (hierarchy != null) { |
| for (IType t : icu.getTypes()) { |
| reqTypeChangedFromExternal(t, hierarchy); |
| } |
| } |
| } |
| catch (JavaModelException e) { |
| SdkUtilActivator.logWarning("could not find types in compilation unit '" + icu.getElementName() + "'.", e); |
| } |
| } |
| |
| private void reqTypeChangedFromExternal(IType type, ITypeHierarchy hierarchy) { |
| handleTypeChange(type, hierarchy); |
| try { |
| for (IType subType : type.getTypes()) { |
| reqTypeChangedFromExternal(subType, hierarchy); |
| } |
| } |
| catch (JavaModelException e) { |
| SdkUtilActivator.logWarning("could not find subtypes of type '" + type.getElementName() + "'.", e); |
| } |
| } |
| |
| /** |
| * will be notified before events are passed through the event listener list from {@link JavaResourceChangedEmitter} |
| */ |
| void elementChanged(JdtEvent e) { |
| switch (e.getEventType()) { |
| case IJavaElementDelta.ADDED: |
| case IJavaElementDelta.CHANGED: { |
| if (e.getElementType() == IJavaElement.TYPE && e.getDeclaringType() == null) { |
| handleTypeChange((IType) e.getElement(), e.getSuperTypeHierarchy()); |
| } |
| else if (e.getElementType() == IJavaElement.COMPILATION_UNIT) { |
| handleCompilationUnitChanged((ICompilationUnit) e.getElement()); |
| } |
| else if (e.getElementType() == IJavaElement.JAVA_PROJECT) { |
| if ((e.getFlags() & IJavaElementDelta.F_OPENED) != 0 || e.getFlags() == 0) { |
| // a new java project has been created/imported/opened/added in the workspace |
| invalidateAll(); |
| } |
| else if ((e.getFlags() & IJavaElementDelta.F_CLOSED) != 0) { |
| // a project has been closed |
| handleJavaElementRemoved(e.getElement()); |
| } |
| } |
| else if (e.getElementType() == IJavaElement.PACKAGE_FRAGMENT_ROOT) { |
| if ((e.getFlags() & (IJavaElementDelta.F_ADDED_TO_CLASSPATH | IJavaElementDelta.F_REMOVED_FROM_CLASSPATH | IJavaElementDelta.F_ARCHIVE_CONTENT_CHANGED | IJavaElementDelta.F_REORDER)) != 0 || e.getFlags() == 0) { |
| // the classpath has been changed |
| invalidateAll(); |
| } |
| } |
| break; |
| } |
| case IJavaElementDelta.REMOVED: { |
| if (TypeUtility.exists(e.getElement().getParent())) { |
| handleJavaElementRemoved(e.getElement()); |
| } |
| break; |
| } |
| case JavaResourceChangedEmitter.CHANGED_EXTERNAL: |
| if (e.getElementType() == IJavaElement.COMPILATION_UNIT) { |
| handleCompilationUnitChanged((ICompilationUnit) e.getElement()); |
| } |
| else if (e.getElementType() == IJavaElement.TYPE) { |
| handleTypeChange((IType) e.getElement(), e.getSuperTypeHierarchy()); |
| } |
| break; |
| } |
| } |
| } |