Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorcbateman2009-01-28 01:46:55 +0000
committercbateman2009-01-28 01:46:55 +0000
commit2e889f0d5a9629d813e4f0d5cb206aa8f7cbb8bb (patch)
tree005abbf15aca1080517adaed9c387cda10c472ab
parentec60fe6b761922741e360e68f2cf6d87b758f36a (diff)
downloadwebtools.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.
-rw-r--r--jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/context/symbol/internal/impl/IMapSourceInfo.java30
-rw-r--r--jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/context/symbol/internal/impl/IMapTypeDescriptorImpl.java182
-rw-r--r--jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/designtime/internal/symbols/ResourceBundleMapSource.java199
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

Back to the top