diff options
author | cbateman | 2009-01-28 01:46:55 +0000 |
---|---|---|
committer | cbateman | 2009-01-28 01:46:55 +0000 |
commit | 2e889f0d5a9629d813e4f0d5cb206aa8f7cbb8bb (patch) | |
tree | 005abbf15aca1080517adaed9c387cda10c472ab | |
parent | ec60fe6b761922741e360e68f2cf6d87b758f36a (diff) | |
download | webtools.jsf-2e889f0d5a9629d813e4f0d5cb206aa8f7cbb8bb.tar.gz webtools.jsf-2e889f0d5a9629d813e4f0d5cb206aa8f7cbb8bb.tar.xz webtools.jsf-2e889f0d5a9629d813e4f0d5cb206aa8f7cbb8bb.zip |
Fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=249480.
3 files changed, 321 insertions, 90 deletions
diff --git a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/context/symbol/internal/impl/IMapSourceInfo.java b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/context/symbol/internal/impl/IMapSourceInfo.java new file mode 100644 index 000000000..a5de57969 --- /dev/null +++ b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/context/symbol/internal/impl/IMapSourceInfo.java @@ -0,0 +1,30 @@ +package org.eclipse.jst.jsf.context.symbol.internal.impl; + +/** + * Information about the map source used by the IMapTypeDescriptor + * + */ +public interface IMapSourceInfo +{ + + /** + * @param key + * @return true if the map source has changed since key was last set + */ + boolean hasChanged(final Object key); + + /** + * The key is used in the standard HashMap way. + * + * @param key + * @return the cached value for the key. + */ + Object getCachedValue(final Object key); + /** + * Add cached value for key. + * + * @param key + * @param value + */ + void putCachedValue(final Object key, final Object value); +} diff --git a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/context/symbol/internal/impl/IMapTypeDescriptorImpl.java b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/context/symbol/internal/impl/IMapTypeDescriptorImpl.java index 462d0cb22..161102b8a 100644 --- a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/context/symbol/internal/impl/IMapTypeDescriptorImpl.java +++ b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/context/symbol/internal/impl/IMapTypeDescriptorImpl.java @@ -11,10 +11,12 @@ ********************************************************************************/ package org.eclipse.jst.jsf.context.symbol.internal.impl; +import java.util.ArrayList; import java.util.HashMap; -import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Set; +import java.util.StringTokenizer; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.util.BasicEList; @@ -87,6 +89,8 @@ public class IMapTypeDescriptorImpl extends ITypeDescriptorImpl implements IMapT */ protected static final boolean IMMUTABLE_EDEFAULT = true; + private static final Object MAP_TYPE_DESCRIPTOR_PROP_KEY = new Object(); + /** * The cached value of the '{@link #isImmutable() <em>Immutable</em>}' attribute. * <!-- begin-user-doc --> @@ -190,16 +194,32 @@ public class IMapTypeDescriptorImpl extends ITypeDescriptorImpl implements IMapT /** * @see org.eclipse.jst.jsf.context.symbol.internal.impl.ITypeDescriptorImpl#getProperties() */ - public EList getProperties() + public EList getProperties() { - final BasicEList list = new BasicEList(); + final BasicEList list = new BasicEList(); final Map source = getMapSource(); - final Set keys = source.keySet(); - final Map segmentMap = processSegments(keys, source); + if (source instanceof IMapSourceInfo) + { + if (!((IMapSourceInfo) source).hasChanged(MAP_TYPE_DESCRIPTOR_PROP_KEY)) + { + EList cachedList = (EList) ((IMapSourceInfo)source).getCachedValue(MAP_TYPE_DESCRIPTOR_PROP_KEY); + + if (cachedList != null) + { + return cachedList; + } + } + } + final Map segmentMap = processSegments(source); list.addAll(segmentMap.values()); + + if (source instanceof IMapSourceInfo) + { + ((IMapSourceInfo)source).putCachedValue(MAP_TYPE_DESCRIPTOR_PROP_KEY, list); + } return list; } - + public EList getMethods() { // TODO: should this return the methods on a Map? @@ -207,99 +227,149 @@ public class IMapTypeDescriptorImpl extends ITypeDescriptorImpl implements IMapT } /** - * @generated NOT + * @generated NOT */ - public IObjectSymbol getArrayElement() { - return null; - } + public IObjectSymbol getArrayElement() + { + return null; + } /** - * @generated NOT + * @generated NOT */ - public boolean isArray() { - // a map is never an array - return false; - } + public boolean isArray() + { + // a map is never an array + return false; + } - private Map processSegments(final Set keys, final Map source) + private Map processSegments(final Map source) { final Map segmentMap = new HashMap(); - - for (final Iterator it = keys.iterator(); it.hasNext();) + final Set<Map.Entry<String, Object>> entrySet = source.entrySet(); + for (final Map.Entry<String, Object> entry : entrySet) { - final String key = (String) it.next(); - final String segments[] = key.split("\\."); //$NON-NLS-1$ - - IPropertySymbol property = - (IPropertySymbol) segmentMap.get(segments[0]); - + + final String key = entry.getKey(); + final String segments[] = fastTokenSplit(key); + if (segments.length == 0) + { + continue; + } + IPropertySymbol property = (IPropertySymbol) segmentMap + .get(segments[0]); + if (property == null) { - final Object propValue = source.get(key); + final Object propValue = entry.getValue(); property = SymbolFactory.eINSTANCE.createIPropertySymbol(); property.setName(segments[0]); ITypeDescriptor typeDesc = null; - + // TODO: need wrapper object to rationalize if (propValue != null) { if (propValue instanceof IType) { - typeDesc = SymbolFactory.eINSTANCE.createIJavaTypeDescriptor2(); - ((IJavaTypeDescriptor2)typeDesc).setType((IType)propValue); - } - else if (propValue instanceof IInstanceSymbol) + typeDesc = SymbolFactory.eINSTANCE + .createIJavaTypeDescriptor2(); + ((IJavaTypeDescriptor2) typeDesc) + .setType((IType) propValue); + } else if (propValue instanceof IInstanceSymbol) { - typeDesc = ((IInstanceSymbol)propValue).getTypeDescriptor(); - } - else if (propValue instanceof IPropertySymbol) + typeDesc = ((IInstanceSymbol) propValue) + .getTypeDescriptor(); + } else if (propValue instanceof IPropertySymbol) { - typeDesc = ((IPropertySymbol)propValue).getTypeDescriptor(); - } - else + typeDesc = ((IPropertySymbol) propValue) + .getTypeDescriptor(); + } else { - String className = propValue.getClass().getName(); - String typeSignature = Signature.createTypeSignature(className, true); - typeDesc = SymbolFactory.eINSTANCE.createIMapTypeDescriptor(); - ((IMapTypeDescriptor)typeDesc).setMapSource(new HashMap()); - ((IMapTypeDescriptor)typeDesc).setTypeSignatureDelegate(typeSignature); + final String className = propValue.getClass().getName(); + final String typeSignature = Signature + .createTypeSignature(className, true); + typeDesc = SymbolFactory.eINSTANCE + .createIMapTypeDescriptor(); + ((IMapTypeDescriptor) typeDesc) + .setMapSource(new HashMap()); + ((IMapTypeDescriptor) typeDesc) + .setTypeSignatureDelegate(typeSignature); // inherit this descriptor's mutability - ((IMapTypeDescriptor)typeDesc).setImmutable(isImmutable()); - property.setIntermediate(true); // set the property as intermediate until we find out different + ((IMapTypeDescriptor) typeDesc) + .setImmutable(isImmutable()); + property.setIntermediate(true); // set the property as + // intermediate until we + // find out different } - + property.setTypeDescriptor(typeDesc); property.setReadable(true); // is only writable if map is not immutable property.setWritable(!isImmutable()); } - + segmentMap.put(segments[0], property); } - - final ITypeDescriptor typeDesc = property.getTypeDescriptor(); - + + final ITypeDescriptor typeDesc = property.getTypeDescriptor(); + if (typeDesc instanceof IMapTypeDescriptor) { if (segments.length == 1) { - // TODO: not always allowed ((IMapTypeDescriptor)typeDesc).getMapSource().put(null, source.get(key)); + // TODO: not always allowed + // ((IMapTypeDescriptor)typeDesc).getMapSource().put(null, + // source.get(key)); // property is more than simply intermediate property.setIntermediate(false); - } - else + } else { - ((IMapTypeDescriptor)typeDesc).getMapSource(). - put(key.substring(key.indexOf('.')+1), source.get(key)); + ((IMapTypeDescriptor) typeDesc).getMapSource().put( + key.substring(key.indexOf('.') + 1), + entry.getValue()); } } } - + return segmentMap; } - - + /** + * Based on measurements, this beats Pattern.split by 15-30% even with + * a pre-compiled pattern. + * + * @param splitValue + * @return the array of strings split by the '.' token + */ + private static String[] fastTokenSplit(final String splitValue) + { + if (splitValue == null || splitValue.length() == 0) + { + return new String[0]; + } + if (splitValue.indexOf('.') > -1) + { + return tokenizerSplit(splitValue); + } + return new String[] {splitValue}; + } + + private static String[] tokenizerSplit(final String splitValue) + { + StringTokenizer stringTokenizer = new StringTokenizer(splitValue, "."); + // initialize to a large size, since we're just going to truncate + // it once at the end and want to reduce the chance of resize during + // the loop. + final List<String> splitValues = new ArrayList<String>(32); + + while (stringTokenizer.hasMoreTokens()) + { + splitValues.add(stringTokenizer.nextToken()); + } + + return splitValues.toArray(new String[0]); + } + /** * <!-- begin-user-doc --> * @param featureID diff --git a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/designtime/internal/symbols/ResourceBundleMapSource.java b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/designtime/internal/symbols/ResourceBundleMapSource.java index 4e7d66c4b..5bf92533f 100644 --- a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/designtime/internal/symbols/ResourceBundleMapSource.java +++ b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/designtime/internal/symbols/ResourceBundleMapSource.java @@ -7,7 +7,7 @@ * * Contributors: * Cameron Bateman/Oracle - initial API and implementation - * + * ********************************************************************************/ package org.eclipse.jst.jsf.designtime.internal.symbols; @@ -31,70 +31,144 @@ import org.eclipse.jst.jsf.common.internal.resource.IResourceLifecycleListener; import org.eclipse.jst.jsf.common.internal.resource.LifecycleListener; import org.eclipse.jst.jsf.common.internal.resource.ResourceLifecycleEvent; import org.eclipse.jst.jsf.common.internal.resource.ResourceLifecycleEvent.EventType; +import org.eclipse.jst.jsf.context.symbol.internal.impl.IMapSourceInfo; import org.eclipse.jst.jsf.core.internal.JSFCorePlugin; import org.eclipse.jst.jsf.core.internal.tld.LoadBundleUtil; -class ResourceBundleMapSource extends AbstractMap +class ResourceBundleMapSource extends AbstractMap implements IMapSourceInfo { - private static final String PROPERTY_QUALIFIER = + private static final String PROPERTY_QUALIFIER = "org.eclipse.jst.jsf.designtime.internal.jsp"; //$NON-NLS-1$ - private static final String SESSION_PROPERTY_NAME_PROJECT = + private static final String SESSION_PROPERTY_NAME_PROJECT = "ResourceBundleMapSource"; //$NON-NLS-1$ - private static final QualifiedName SESSION_PROPERTY_KEY_PROJECT = + private static final QualifiedName SESSION_PROPERTY_KEY_PROJECT = new QualifiedName(PROPERTY_QUALIFIER, SESSION_PROPERTY_NAME_PROJECT); + private static final Object STATIC_LOCK = new Object(); private static IFile getCachedBundleFile(final IProject project, final String baseName) { if (project != null) { - Map bundleFileCache = getBundleFileCache(project); - + final Map<String, BundleFileCacheInfo> bundleFileCache = getBundleFileCache(project); + if (bundleFileCache != null) { - return (IFile) bundleFileCache.get(baseName); + final BundleFileCacheInfo info = bundleFileCache.get(baseName); + if (info != null) + { + return info.getFile(); + } } } return null; } - private static Map getBundleFileCache(final IProject project) + private static class BundleFileCacheInfo + { + private final IFile _file; + private final Map<Object,CachedDataItem> _cachedData; + public BundleFileCacheInfo(final IFile file) + { + super(); + _file = file; + _cachedData = + Collections.synchronizedMap(new HashMap<Object, CachedDataItem>()); + } + public IFile getFile() + { + return _file; + } + public Object getCachedData(final Object key) + { + CachedDataItem item = _cachedData.get(key); + + if (item != null) + { + return item.getData(); + } + return null; + } + + public Object putCachedData(final Object key, final Object value, final long timestamp) + { + CachedDataItem item = new CachedDataItem(value, timestamp); + CachedDataItem oldItem = _cachedData.put(key, item); + if (oldItem != null) + { + return oldItem.getData(); + } + return null; + } + + public boolean hasChanged(final Object key, final long timestamp) + { + CachedDataItem item = _cachedData.get(key); + if (item != null) + { + return item.getTimestamp() != timestamp; + } + return false; + } + + private static class CachedDataItem + { + private final Object _data; + private final long _timestamp; + public CachedDataItem(Object data, long timestamp) + { + super(); + _data = data; + _timestamp = timestamp; + } + public final Object getData() + { + return _data; + } + public final long getTimestamp() + { + return _timestamp; + } + } + } + + private static Map<String, BundleFileCacheInfo> getBundleFileCache(final IProject project) { - synchronized(project) + synchronized(STATIC_LOCK) { - Map bundleFileCache = null; + Map<String, BundleFileCacheInfo> bundleFileCache = null; try { - bundleFileCache = - (Map) project.getSessionProperty(SESSION_PROPERTY_KEY_PROJECT); + bundleFileCache = + (Map<String, BundleFileCacheInfo>) project.getSessionProperty(SESSION_PROPERTY_KEY_PROJECT); if (bundleFileCache == null) { - bundleFileCache = new HashMap(); - LifecycleListener listener = new LifecycleListener(project); + bundleFileCache = new HashMap<String, BundleFileCacheInfo>(); + final LifecycleListener listener = new LifecycleListener(project); listener.addListener(new IResourceLifecycleListener() { - public EventResult acceptEvent(ResourceLifecycleEvent event) + public EventResult acceptEvent(final ResourceLifecycleEvent event) { EventResult result = EventResult.getDefaultEventResult(); - + if (event.getEventType() == EventType.RESOURCE_INACCESSIBLE) { try { - Map bundleCache = - (Map) project.getSessionProperty(SESSION_PROPERTY_KEY_PROJECT); + final Map<String, BundleFileCacheInfo> bundleCache = + (Map<String, BundleFileCacheInfo>) project.getSessionProperty(SESSION_PROPERTY_KEY_PROJECT); bundleCache.clear(); project.setSessionProperty(SESSION_PROPERTY_KEY_PROJECT, null); } - catch (CoreException ce) + catch (final CoreException ce) { JSFCorePlugin.log("Error clearing bundle file cache", ce); //$NON-NLS-1$ } result = EventResult.getDisposeAfterEventResult(); } - + return result; } } @@ -103,7 +177,7 @@ class ResourceBundleMapSource extends AbstractMap project.setSessionProperty(SESSION_PROPERTY_KEY_PROJECT, bundleFileCache); } } - catch (CoreException ce) + catch (final CoreException ce) { JSFCorePlugin.log("Error creating bundle file cache", ce); //$NON-NLS-1$ } @@ -112,11 +186,11 @@ class ResourceBundleMapSource extends AbstractMap } } - private static IFile createCachedBundleFile(final IProject project, + private static IFile createCachedBundleFile(final IProject project, final String resourcePathStr) throws IOException, CoreException { - IStorage storage = + final IStorage storage = LoadBundleUtil.getLoadBundleResource(project, resourcePathStr); IFile bundleRes = null; @@ -125,7 +199,26 @@ class ResourceBundleMapSource extends AbstractMap && storage.getAdapter(IFile.class) != null) { bundleRes = (IFile) storage.getAdapter(IFile.class); - getBundleFileCache(project).put(resourcePathStr, bundleRes); + // if file is removed, clear the bundle from the store. + final LifecycleListener listener = new LifecycleListener(bundleRes); + listener.addListener(new IResourceLifecycleListener() + { + public EventResult acceptEvent(final ResourceLifecycleEvent event) + { + EventResult result = EventResult.getDefaultEventResult(); + + if (event.getEventType() == EventType.RESOURCE_INACCESSIBLE) + { + Map<String, BundleFileCacheInfo> bundleFileCache = getBundleFileCache(project); + bundleFileCache.remove(resourcePathStr); + result = EventResult.getDisposeAfterEventResult(); + } + return result; + } + } + ); + + getBundleFileCache(project).put(resourcePathStr, new BundleFileCacheInfo(bundleRes)); return bundleRes; } @@ -135,11 +228,11 @@ class ResourceBundleMapSource extends AbstractMap private Properties _resourceBundle; // = null; set on first access or changes private final IFile _bundleFile; // the resource private final String _resourcePathStr; // the key used in the file cache - // as returned by IResource.getModificationStamp() + // as returned by IResource.getModificationStamp() // the last time _resourceBundle was loaded private long _lastModificationStamp; - ResourceBundleMapSource(final IProject context, + ResourceBundleMapSource(final IProject context, final String resourcePathStr) throws IOException, JavaModelException, CoreException { @@ -174,11 +267,11 @@ class ResourceBundleMapSource extends AbstractMap _resourceBundle.load(bundleStream); _lastModificationStamp = _bundleFile.getModificationStamp(); } - catch (CoreException ce) + catch (final CoreException ce) { JSFCorePlugin.log("Error refreshing bundle", ce); //$NON-NLS-1$ } - catch (IOException ioe) + catch (final IOException ioe) { JSFCorePlugin.log("Error refreshing bundle", ioe); //$NON-NLS-1$ } @@ -190,7 +283,7 @@ class ResourceBundleMapSource extends AbstractMap { bundleStream.close(); } - catch (IOException ioe) + catch (final IOException ioe) { JSFCorePlugin.log("Error closing bundle", ioe); //$NON-NLS-1$ } @@ -201,7 +294,7 @@ class ResourceBundleMapSource extends AbstractMap else { // bundle no longer exists so remove it - Map bundleFileCache = getBundleFileCache(_bundleFile.getProject()); + final Map<String, BundleFileCacheInfo> bundleFileCache = getBundleFileCache(_bundleFile.getProject()); if (bundleFileCache != null && bundleFileCache.containsKey(_resourcePathStr)) @@ -217,7 +310,8 @@ class ResourceBundleMapSource extends AbstractMap } } - public Set entrySet() + @Override + public Set entrySet() { checkAndRefreshBundle(); @@ -228,12 +322,15 @@ class ResourceBundleMapSource extends AbstractMap return _resourceBundle.entrySet(); } - /** + /** + * @param key + * @return the value * @see java.util.AbstractMap#get(java.lang.Object) * @overrride to optimize for the fact that we are doing a hash get */ // - public Object get(Object key) + @Override + public Object get(final Object key) { checkAndRefreshBundle(); @@ -243,4 +340,38 @@ class ResourceBundleMapSource extends AbstractMap } return _resourceBundle.get(key); } + + public final boolean hasChanged(final Object key) + { + final BundleFileCacheInfo cache = getBundleFileCache( + _bundleFile.getProject()).get(_resourcePathStr); + if (cache != null) + { + return cache.hasChanged(key, _bundleFile.getModificationStamp()); + } + // return true since if there is nothing cached, the caller will want + // to react. + return true; + } + + public Object getCachedValue(final Object key) + { + final BundleFileCacheInfo cache = getBundleFileCache( + _bundleFile.getProject()).get(_resourcePathStr); + if (cache != null) + { + return cache.getCachedData(key); + } + return null; + } + + public void putCachedValue(final Object key, final Object value) + { + final BundleFileCacheInfo cache = getBundleFileCache( + _bundleFile.getProject()).get(_resourcePathStr); + if (cache != null) + { + cache.putCachedData(key, value, _lastModificationStamp); + } + } }
\ No newline at end of file |