Bug 569656 Cache resolved types of JSF beans to make validation faster

- JDTTypeResolver caches each resolved type of a bean type
- JDTBeanIntrospector is using JDTTypeResolver to resolve types
- JDTBeanIntrospector is cached in TypeInfoCache
- JDTBeanIntrospector caches all methods of type so that it can be
resused for properties and methods
- JDTBeanIntrospector does no longer compute type hierarchy since this
is already cached in type info cache. Super types will be provided from
there
- Use cached JDTBeanIntrospector everywhere instead of creating new
instances of it
- AnnotationSearchRequestor uses cached JDTBeanIntrospector to resolve
annotations. It has a new white list of types that it will resolve

Change-Id: Icc29fd971dec3bc833c3bf43a35dbd268a5868ef
Signed-off-by: Reto Weiss <reto.weiss@axonivy.com>
diff --git a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/internal/types/TypeInfo.java b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/internal/types/TypeInfo.java
index 32e4027..4e4d599 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/internal/types/TypeInfo.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/internal/types/TypeInfo.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2001, 2007 Oracle Corporation and others.
+ * Copyright (c) 2001, 2021 Oracle Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
  * which accompanies this distribution, and is available at
@@ -9,10 +9,12 @@
  * 
  * Contributors:
  *     Oracle Corporation - initial API and implementation
+ *     Reto Weiss/Axon Ivy      Cache resolved types
  *******************************************************************************/
 package org.eclipse.jst.jsf.common.internal.types;
 
 import org.eclipse.jdt.core.IType;
+import org.eclipse.jst.jsf.common.util.JDTBeanIntrospector;
 import org.eclipse.jst.jsf.context.symbol.IBeanMethodSymbol;
 import org.eclipse.jst.jsf.context.symbol.IBeanPropertySymbol;
 
@@ -32,6 +34,7 @@
     private IType[] supertypes = null;
     private IType[] interfaceTypes = null;
     private String[] missingSupertypeNames = null;
+    private JDTBeanIntrospector beanIntrospector = null;
     
     /**Creates an empty TypeInfo object
      */
@@ -109,4 +112,11 @@
         this.missingSupertypeNames = missingSupertypeNames;
     }
     
+    public JDTBeanIntrospector getBeanIntrospector() {
+      return beanIntrospector;
+    }
+    
+    public void setBeanIntrospector(JDTBeanIntrospector beanIntrospector) {
+      this.beanIntrospector = beanIntrospector;
+    }
 }
diff --git a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/internal/types/TypeInfoCache.java b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/internal/types/TypeInfoCache.java
index d7da0fe..9bcc991 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/internal/types/TypeInfoCache.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/internal/types/TypeInfoCache.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2001, 2009 Oracle Corporation and others.
+ * Copyright (c) 2001, 2021 Oracle Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
  * which accompanies this distribution, and is available at
@@ -8,7 +8,8 @@
  * SPDX-License-Identifier: EPL-2.0
  * 
  * Contributors:
- *     Oracle Corporation - initial API and implementation
+ *     Oracle Corporation  - initial API and implementation
+ *     Reto Weiss/Axon Ivy - Cache resolved types
  *******************************************************************************/
 package org.eclipse.jst.jsf.common.internal.types;
 
@@ -36,6 +37,7 @@
 import org.eclipse.jdt.core.JavaCore;
 import org.eclipse.jdt.core.JavaModelException;
 import org.eclipse.jst.jsf.common.JSFCommonPlugin;
+import org.eclipse.jst.jsf.common.util.JDTBeanIntrospector;
 import org.eclipse.jst.jsf.context.symbol.IBeanMethodSymbol;
 import org.eclipse.jst.jsf.context.symbol.IBeanPropertySymbol;
 
@@ -84,7 +86,7 @@
     public static void disposeInstance(final TypeInfoCache cache)
     {
         if (cache != null 
-                && cache != instance)        
+                && cache != instance)
         {
             JavaCore.removeElementChangedListener(cache);
             
@@ -181,6 +183,24 @@
         return methods;
     }
     
+
+    /**
+     * @param beanType
+     * @return cached bean introspector or null
+     */
+    public JDTBeanIntrospector getCachedBeanIntrospector(IType beanType)
+    {
+      if (beanType != null)
+      {
+          TypeInfo typeInfo = getTypeInfo(beanType);
+          if (typeInfo != null)
+          {
+              return typeInfo.getBeanIntrospector();
+          }
+      }
+      return null;
+    }
+    
     /**Returns the cached supertypes for a given type. Will return null if no supertypes
      * have been cached for this type or if the type/something it depends on has changed since
      * then.
@@ -189,18 +209,16 @@
      * @see TypeInfoCache#cacheSupertypesFor(IType)
      */
     public synchronized IType[] getCachedSupertypes(IType type) {
-        IType[] types = null;
-        
         if (type != null)
         {
             TypeInfo typeInfo = getTypeInfo(type);
             if (typeInfo != null)
             {
-                types = typeInfo.getSupertypes();
+                return typeInfo.getSupertypes();
             }
         }
         
-        return types;
+        return null;
     }
     
     /**Returns the cached implemented interfaces for a given type. Will return null if no interfaces
@@ -253,6 +271,22 @@
             }
         }
     }
+
+    /**
+     * Chaches the given bean introspector for the given type
+     * @param beanType
+     * @param beanIntrospector
+     */
+    public void cacheBeanIntrospector(IType beanType, JDTBeanIntrospector beanIntrospector)
+    {
+      if (beanType != null)
+      {
+          TypeInfo typeInfo = getOrCreateTypeInfo(beanType);
+          if (typeInfo != null) {
+              typeInfo.setBeanIntrospector(beanIntrospector);
+          }
+      }
+    }
     
     /**Caches the supertypes for the given type. The supertypes will be calculated (and also returned)
      * by this method.
@@ -601,5 +635,4 @@
             }
         }
     }
-
 }
diff --git a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/util/JDTBeanIntrospector.java b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/util/JDTBeanIntrospector.java
index f5411ac..47616d9 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/util/JDTBeanIntrospector.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/util/JDTBeanIntrospector.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007, 2010 Oracle Corporation.
+ * Copyright (c) 2007, 2021 Oracle Corporation.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
  * which accompanies this distribution, and is available at
@@ -9,6 +9,7 @@
  *
  * Contributors:
  *    Cameron Bateman/Oracle - initial API and implementation
+ *    Reto Weiss/Axon Ivy      Cache resolved types
  *    
  ********************************************************************************/
 package org.eclipse.jst.jsf.common.util;
@@ -21,14 +22,13 @@
 import java.util.Map;
 import java.util.Map.Entry;
 
-import org.eclipse.core.runtime.NullProgressMonitor;
 import org.eclipse.jdt.core.Flags;
 import org.eclipse.jdt.core.IMethod;
 import org.eclipse.jdt.core.IType;
-import org.eclipse.jdt.core.ITypeHierarchy;
 import org.eclipse.jdt.core.JavaModelException;
 import org.eclipse.jdt.core.Signature;
 import org.eclipse.jst.jsf.common.JSFCommonPlugin;
+import org.eclipse.jst.jsf.common.internal.types.TypeInfoCache;
 
 /**
  * A class that does bean introspection on a JDT IType
@@ -44,176 +44,242 @@
  * @author cbateman
  *
  */
-public class JDTBeanIntrospector 
+public class JDTBeanIntrospector
 {
-	private final static String GET_PREFIX = "get"; //$NON-NLS-1$
-	private final static String SET_PREFIX = "set"; //$NON-NLS-1$
-	private final static String IS_PREFIX = "is"; //$NON-NLS-1$
-	
-	private final IType 	_type;
-	private final HashMap<String, String> _resolvedSignatures;
+  private final static IType[] EMPTY_SUPER_TYPES = new IType[0];
+  private final static String GET_PREFIX = "get"; //$NON-NLS-1$
+  private final static String SET_PREFIX = "set"; //$NON-NLS-1$
+  private final static String IS_PREFIX = "is"; //$NON-NLS-1$
+  private final static IMethod[] EMTPY_METHODS = new IMethod[0];
 
-	/**
-	 * @param type
-	 */
-	public JDTBeanIntrospector(IType type)
-	{
-		_type = type;
-		_resolvedSignatures = new HashMap<String, String>();
-	}
-	
-	/**
-	 * @return an map of all properties with the property names
-     * as keys and the values being JDTBeanProperty objects representing
-     * the properties.
-	 */
-	public Map<String, JDTBeanProperty> getProperties()
-	{
-		_resolvedSignatures.clear();
+  private final IType type;
+  private final JDTTypeResolver typeResolver;
+  private IMethod[] methods;
+  private final IType[] superTypes;
 
-		final Map<String, JDTBeanProperty>   propertiesWorkingCopy = 
-		    new HashMap<String, JDTBeanProperty>();
-		final IMethod[] methods = getAllMethods();
-		
-		for (int i = 0; i < methods.length; i++)
-		{
-			final IMethod  method = methods[i];
-
-			try
-			{
-				processPropertyMethod(method, propertiesWorkingCopy);
-			}
-			catch (JavaModelException jme)
-			{
-				// log and then proceed to next method
-				JSFCommonPlugin.log(jme, "Error processing IMethod for bean property info"); //$NON-NLS-1$
-			}
-		}
-
-        final Map properties = new HashMap();
-        
-        for (Entry<String, JDTBeanProperty> entry : propertiesWorkingCopy.entrySet())
-        {
-            final String key = entry.getKey();
-            JDTBeanPropertyWorkingCopy  wcopy = (JDTBeanPropertyWorkingCopy) entry.getValue();
-            properties.put(key, wcopy.toValueObject());
-        }
-
-		return properties;
-	}
-
-	private void processPropertyMethod(IMethod method, Map<String, JDTBeanProperty> properties) throws JavaModelException
-	{
-		// to be a bean method, it must not a constructor, must be public
-		// and must not be static
-		if (!method.isConstructor()
-				&& ( Flags.isPublic(method.getFlags())
-				        || _type.isInterface())
-				&& !Flags.isStatic(method.getFlags()))
-		{
-			final String methodName = method.getElementName();
-			final String returnType = method.getReturnType();
-			
-			// either starts with get or is boolean and starts with is
-			
-			// is access must start with 'is', have a boolean return type and no parameters
-			final boolean  startsWithIs = methodName.startsWith(IS_PREFIX) 
-					&& Signature.SIG_BOOLEAN.equals(returnType)
-					&& method.getNumberOfParameters() == 0
-                    && methodName.length() > IS_PREFIX.length();
-			
-			// get accessor must start with 'get', have no parameters and return non-void
-			final boolean  startsWithGet = (methodName.startsWith(GET_PREFIX)
-											&& method.getNumberOfParameters() == 0)
-											&& !Signature.SIG_VOID.equals(returnType)                    
-                                            && methodName.length() > GET_PREFIX.length();
-			
-			// mutator must start with 'set' and have one parameter and a void return type
-			final boolean  startsWithSet = methodName.startsWith(SET_PREFIX)
-											&& method.getNumberOfParameters() == 1
-											&& Signature.SIG_VOID.equals(returnType)
-                                            && methodName.length() > SET_PREFIX.length();
-
-			if (startsWithGet || startsWithSet || startsWithIs)
-			{
-				final String propertyName = 
-					Introspector.decapitalize(methodName.substring(startsWithIs ? 2 : 3));
-
-				JDTBeanPropertyWorkingCopy workingCopy = 
-					(JDTBeanPropertyWorkingCopy) properties.get(propertyName);
-
-				if (workingCopy == null)
-				{
-					workingCopy = new JDTBeanPropertyWorkingCopy(_type, _resolvedSignatures);
-					properties.put(propertyName, workingCopy);
-				}
-
-				if  (startsWithIs)
-				{
-					workingCopy.setIsGetter(method);
-				}
-				else if (startsWithGet)
-				{
-					workingCopy.setGetter(method);
-				}
-				else if (startsWithSet)
-				{
-					workingCopy.addSetter(method);
-				}
-			}
-		}
-	}
-
-	/**
-	 * @return all methods for the type including inherited ones
-	 */
-	public IMethod[] getAllMethods()
-	{
-		IMethod[] methods = new IMethod[0];
-		
-		try
-		{
-            // type not resolved so don't proceed
-            if (_type != null)
-            {
-	            // TODO: type hierarchy is potentially expensive, should
-	            // cache once and listen for changes
-	            ITypeHierarchy  hierarchy = _type.newSupertypeHierarchy(new NullProgressMonitor());
-	            
-				methods = getAllMethods(hierarchy, _type);
-            }
-		}
-		catch(JavaModelException jme)
-		{
-            JSFCommonPlugin.log(jme, "Error getting type information for bean"); //$NON-NLS-1$
-		}
-
-		return methods;
-	}
-
-    /**
-     * @param typeHierarchy
-     * @param type
-     * @return all methods of the type and it's super types
-     */
-    private static IMethod[] getAllMethods(final ITypeHierarchy typeHierarchy, final IType type)
+  /**
+   * @param type
+   * @return cached JDT bean introspector for the given type
+   */
+  public static JDTBeanIntrospector forType(IType type)
+  {
+    TypeInfoCache cache = TypeInfoCache.getInstance();
+    JDTBeanIntrospector beanIntrospector = cache.getCachedBeanIntrospector(type);
+    if (beanIntrospector != null)
     {
-        final List<IMethod>   methods = new ArrayList<IMethod>();
-        final IType[] superTypes = typeHierarchy.getAllSuperclasses(type);
-        final IType[] closure = new IType[superTypes.length+1];
-        closure[0] = type;
-        System.arraycopy(superTypes, 0, closure, 1, superTypes.length);
-        
-        for (int i = 0; i < closure.length; i++)
-        {
-            try {
-                final IType superType = closure[i];
-                methods.addAll(Arrays.asList(superType.getMethods()));
-            } catch (JavaModelException e) {
-                JSFCommonPlugin.log(e, "Error getting super type information for bean"); //$NON-NLS-1$
-            }
-        }
-            
-        return methods.toArray(new IMethod[methods.size()]);
+      return beanIntrospector;
     }
+    IType[] superTypes = cache.getCachedSupertypes(type);
+    if (superTypes == null)
+    {
+      superTypes = cache.cacheSupertypesFor(type);
+    }
+    beanIntrospector = new JDTBeanIntrospector(type, superTypes);
+    cache.cacheBeanIntrospector(type, beanIntrospector);
+    return beanIntrospector;
+  }
+  
+  /**
+   * @param type
+   */
+  private JDTBeanIntrospector(IType type, IType[] superTypes)
+  {
+    this.type = type;
+    this.superTypes = superTypes;
+    if (superTypes == null)
+    {
+      superTypes = EMPTY_SUPER_TYPES;
+    }
+      
+    this.typeResolver = new JDTTypeResolver(type, superTypes);
+  }
+
+  /**
+   * @return an map of all properties with the property names as keys and the
+   *         values being JDTBeanProperty objects representing the properties.
+   */
+  public Map<String, JDTBeanProperty> getProperties()
+  {
+    final Map<String, JDTBeanPropertyWorkingCopy> propertiesWorkingCopy = new HashMap<String, JDTBeanPropertyWorkingCopy>();
+    final IMethod[] mthds = methods();
+
+    for (int i = 0; i < mthds.length; i++)
+    {
+      try
+      {
+        processPropertyMethod(mthds[i], propertiesWorkingCopy);
+      }
+      catch (JavaModelException jme)
+      {
+        // log and then proceed to next method
+        JSFCommonPlugin.log(jme, "Error processing IMethod for bean property info"); //$NON-NLS-1$
+      }
+    }
+
+    final Map<String, JDTBeanProperty> properties = new HashMap<String, JDTBeanProperty>();
+
+    for (Entry<String, JDTBeanPropertyWorkingCopy> entry : propertiesWorkingCopy.entrySet())
+    {
+      final String key = entry.getKey();
+      JDTBeanPropertyWorkingCopy wcopy = entry.getValue();
+      properties.put(key, wcopy.toValueObject());
+    }
+
+    return properties;
+  }
+
+  private void processPropertyMethod(IMethod method, Map<String, JDTBeanPropertyWorkingCopy> properties)
+          throws JavaModelException
+  {
+    // to be a bean method, it must not a constructor, must be public
+    // and must not be static
+    if (!method.isConstructor()
+            && (Flags.isPublic(method.getFlags())
+                    || type.isInterface())
+            && !Flags.isStatic(method.getFlags()))
+    {
+      final String methodName = method.getElementName();
+      final String returnType = method.getReturnType();
+
+      // either starts with get or is boolean and starts with is
+
+      // is access must start with 'is', have a boolean return type and no
+      // parameters
+      final boolean startsWithIs = methodName.startsWith(IS_PREFIX)
+              && Signature.SIG_BOOLEAN.equals(returnType)
+              && method.getNumberOfParameters() == 0
+              && methodName.length() > IS_PREFIX.length();
+
+      // get accessor must start with 'get', have no parameters and return
+      // non-void
+      final boolean startsWithGet = (methodName.startsWith(GET_PREFIX)
+              && method.getNumberOfParameters() == 0)
+              && !Signature.SIG_VOID.equals(returnType)
+              && methodName.length() > GET_PREFIX.length();
+
+      // mutator must start with 'set' and have one parameter and a void return
+      // type
+      final boolean startsWithSet = methodName.startsWith(SET_PREFIX)
+              && method.getNumberOfParameters() == 1
+              && Signature.SIG_VOID.equals(returnType)
+              && methodName.length() > SET_PREFIX.length();
+
+      if (startsWithGet || startsWithSet || startsWithIs)
+      {
+        final String propertyName = Introspector.decapitalize(methodName.substring(startsWithIs ? 2 : 3));
+
+        JDTBeanPropertyWorkingCopy workingCopy = properties.get(propertyName);
+
+        if (workingCopy == null)
+        {
+          workingCopy = new JDTBeanPropertyWorkingCopy(type, typeResolver, propertyName);
+          properties.put(propertyName, workingCopy);
+        }
+
+        if (startsWithIs)
+        {
+          workingCopy.setIsGetter(method);
+        }
+        else if (startsWithGet)
+        {
+          workingCopy.setGetter(method);
+        }
+        else if (startsWithSet)
+        {
+          workingCopy.addSetter(method);
+        }
+      }
+    }
+  }
+
+  /**
+   * @return methods
+   */
+  public JDTBeanMethod[] getMethods()
+  {
+    List<JDTBeanMethod> beanMethods = new ArrayList<JDTBeanMethod>();
+    for (IMethod method : methods())
+    {
+      JDTBeanMethod beanMethod = toBeanMethod(method);
+      if (beanMethod != null)
+      {
+        beanMethods.add(beanMethod);
+      }
+    }
+    return beanMethods.toArray(new JDTBeanMethod[beanMethods.size()]);
+  }
+  
+  private JDTBeanMethod toBeanMethod(IMethod method)
+  {
+    try
+    {
+      String resMethodSigErased = typeResolver.resolveMethodEraseTypeParams(method.getSignature());
+      String resMethodSigUnerased = typeResolver.resolveMethodKeepTypeParams(method.getSignature());
+      return new JDTBeanMethod(method, resMethodSigErased, resMethodSigUnerased);
+    }
+    catch(JavaModelException jme)
+    {
+      // log and then proceed to next method
+      JSFCommonPlugin.log(jme, "Error processing IMethod for bean method info"); //$NON-NLS-1$
+      return null;
+    }
+  }
+  
+  /**
+   * @return all methods for the type including inherited ones
+   */
+  private IMethod[] methods()
+  {
+    // type not resolved so don't proceed
+    if (type == null)
+    {
+      return EMTPY_METHODS;
+    }
+    if (methods == null)
+    {
+      methods = allMethods();
+    }
+    return methods;
+  }
+
+  /**
+   * @param typeHierarchy
+   * @param type
+   * @return all methods of the type and it's super types
+   */
+  private IMethod[] allMethods()
+  {
+    final List<IMethod> allMethods = new ArrayList<IMethod>();
+    final IType[] closure = new IType[superTypes.length + 1];
+    closure[0] = type;
+    System.arraycopy(superTypes, 0, closure, 1, superTypes.length);
+
+    for (IType t : closure)
+    {
+      try
+      {
+        allMethods.addAll(Arrays.asList(t.getMethods()));
+      }
+      catch (JavaModelException e)
+      {
+        JSFCommonPlugin.log(e, "Error getting super type information for bean"); //$NON-NLS-1$
+      }
+    }
+
+    return allMethods.toArray(new IMethod[allMethods.size()]);
+  }
+
+  /**
+   * @param simpleName simple type name
+   * @return full qualified type name or null
+   */
+  public String resolveFullQualifiedTypeName(String simpleName)
+  {
+    IType resolvedType = typeResolver.resolveTypeName(simpleName);
+    if (resolvedType == null)
+    {
+      return null;
+    }
+    return resolvedType.getFullyQualifiedName();
+  }
 }
diff --git a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/util/JDTBeanMethod.java b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/util/JDTBeanMethod.java
new file mode 100644
index 0000000..9d1edbd
--- /dev/null
+++ b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/util/JDTBeanMethod.java
@@ -0,0 +1,131 @@
+/*******************************************************************************
+ * Copyright (c) 2020, 2021 Axon Ivy.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Reto Weiss/Axon Ivy      Cache resolved types
+ *    
+ ********************************************************************************/
+package org.eclipse.jst.jsf.common.util;
+
+
+import org.eclipse.jdt.core.IMethod;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.Signature;
+
+/**
+ * Represents a single bean method backed by JDT data
+ * @author rwei
+ * @since 27.11.2020
+ */
+public class JDTBeanMethod
+{
+  private final IMethod method;
+  private final String resolvedMethodSignatureErased;
+  private final String resolvedMethodSignatureUnerased;
+
+  JDTBeanMethod(IMethod method, String resolvedMethodSignatureErased, String resolvedMethodSignatureUnerased)
+  {
+    this.method = method;
+    this.resolvedMethodSignatureErased = resolvedMethodSignatureErased;
+    this.resolvedMethodSignatureUnerased = resolvedMethodSignatureUnerased;
+  }
+  
+  /**
+   * @return type that declares this method
+   */
+  public IType getDeclaringType()
+  {
+    return method.getDeclaringType();
+  }
+
+  /**
+   * @return jdt method that is behind this bean method
+   */
+  public IMethod getMethod()
+  {
+    return method;
+  }
+
+  /**
+   * @return method name
+   */
+  public String getElementName()
+  {
+    return method.getElementName();
+  }
+
+  /**
+   * @return true if method is a constructor
+   * @throws JavaModelException
+   */
+  public boolean isConstructor() throws JavaModelException
+  {
+    return method.isConstructor();
+  }
+
+  /**
+   * @return flags
+   * @throws JavaModelException
+   */
+  public int getFlags() throws JavaModelException
+  {
+    return method.getFlags();
+  }
+
+  /**
+   * @return method signature resolved (e.g. L signatures only no unresolved signatures with Q) and erased (no type parameters) 
+   */
+  public String getResolvedSignatureErased()
+  {
+    return resolvedMethodSignatureErased;
+  }
+
+  /**
+   * @return parameters types unresolved (e.g. may contain Q signatures) and unerased (with type parameters)
+   */
+  public String[] getUnresolvedParameterTypesUnerased()
+  {
+    return method.getParameterTypes();
+  }
+  
+  /**
+   * @return paramters types resolved (e.g. L signatures only no unresolved signatures with Q) and unerased (with type parameters)
+   */
+  public String[] getResolvedParameterTypesUnerased()
+  {
+    return Signature.getParameterTypes(resolvedMethodSignatureUnerased);
+  }
+
+  /**
+   * @return returns type resolved (e.g. L signatures only no unresolved signatures with Q) and unerased (with type parameters)
+   */
+  public String getResolvedReturnTypeUnerased() 
+  {
+    return Signature.getReturnType(resolvedMethodSignatureUnerased);
+  }
+  
+  /**
+   * @return return type unresolved (e.g Q signatures) and unerased (with type parameters)
+   * @throws JavaModelException
+   */
+  public String getUnresolvedReturnTypeUnerased() throws JavaModelException
+  {
+    return method.getReturnType();
+  }
+  
+  @Override
+  public String toString()
+  {
+    return "JDTBeanMethod [name="+getElementName()+ //$NON-NLS-1$
+            "erasedSignature="+resolvedMethodSignatureErased+ //$NON-NLS-1$
+            "unerasedSignature="+resolvedMethodSignatureUnerased+ //$NON-NLS-1$
+            "]"; //$NON-NLS-1$
+  }
+}
diff --git a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/util/JDTBeanProperty.java b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/util/JDTBeanProperty.java
index ec3d49d..3b26475 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/util/JDTBeanProperty.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/util/JDTBeanProperty.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007, 2009 Oracle Corporation.
+ * Copyright (c) 2007, 2021 Oracle Corporation.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
  * which accompanies this distribution, and is available at
@@ -9,18 +9,16 @@
  *
  * Contributors:
  *    Cameron Bateman/Oracle - initial API and implementation
+ *    Reto Weiss/Axon Ivy      Cache resolved types
  *    
  ********************************************************************************/
 package org.eclipse.jst.jsf.common.util;
 
-import java.util.ArrayList;
 import java.util.List;
 
 import org.eclipse.jdt.core.IMethod;
 import org.eclipse.jdt.core.IType;
-import org.eclipse.jdt.core.JavaModelException;
 import org.eclipse.jdt.core.Signature;
-import org.eclipse.jst.jsf.common.JSFCommonPlugin;
 
 /**
  * Represents a single bean property backed by JDT data
@@ -30,226 +28,125 @@
  * @author cbateman
  *
  */
-public class JDTBeanProperty 
+public class JDTBeanProperty
 {
-	/**
-	 * the IMethod for the accessor  (either is or get)
-	 */
-	private IMethod   _getter;
-	
-	/**
-	 * the IMethod for a "set" accessor method
-	 */
-	private IMethod   _setter;
+  private final IType type;
+  private final String typeSignature;
+  private final List<String> typeParameterSignatures;
+  private final IMethod getter;
+  private final IMethod setter;
+  private final String propertyName;
 
-	/**
-	 * The IType that this property belongs to
-	 */
-	protected final IType    _type;
-    
-    /**
-     * @param type
-     */
-    protected JDTBeanProperty(IType type)
-    {
-        _type = type;
-    }
+  JDTBeanProperty(String propertyName, IType type, String typeSignature, List<String> typeParameterSignatures, IMethod getter, IMethod setter)
+  {
+    this.propertyName = propertyName;
+    this.type = type;
+    this.typeSignature = typeSignature;
+    this.typeParameterSignatures = typeParameterSignatures;
+    this.getter = getter;
+    this.setter = setter;
+  }
 
-    /**
-	 * @return true if this property is readable
-	 */
-	public boolean isReadable()
-	{
-		return  _getter != null;
-	}
-	
-	/**
-	 * @return true if this property is writable
-	 */
-	public boolean isWritable()
-	{
-		return _setter != null;
-	}
-	
-	
-	/**
-	 * @return the get accessor IMethod or null if none
-	 */
-	public IMethod getGetter() {
-		return _getter;
-	}
+  /**
+   * @return true if property can be read
+   */
+  public boolean isReadable()
+  {
+    return getter != null;
+  }
 
-	
-	
-	/**
-	 * Set the get accessor IMethod
-	 * @param getter -- may be null to indicate none
-	 */
-	void setGetter(IMethod getter) {
-		_getter = getter;
-	}
+  /**
+   * @return true if property can be written
+   */
+  public boolean isWritable()
+  {
+    return setter != null;
+  }
 
+  /**
+   * @return JDT getter method
+   */
+  public IMethod getGetter()
+  {
+    return getter;
+  }
 
-	/**
-	 * @return the set mutator IMethod or null if none
-	 */
-	public IMethod getSetter() {
-		return _setter;
-	}
+  /**
+   * @return JDT setter metod
+   */
+  public IMethod getSetter()
+  {
+    return setter;
+  }
 
-	/**
-	 * @param setter
-	 */
-	void setSetter(IMethod setter) {
-		_setter = setter;
-	}
-	
-    /**
-     * @return the IType for this property's type or null if it
-     * cannot determined.  Note that null does not necessarily indicate an error
-     * since some types like arrays of things do not have corresponding JDT IType's
-     * If typeSignature represents an array, the base element IType is returned
-     * if possible
-     */
-    public IType getType()
-    {
-        final String typeSignature = Signature.getElementType(getTypeSignature());
-        return TypeUtil.resolveType(_type, typeSignature);
-    }
-	
-    /**
-     * @return the number of array nesting levels in typeSignature.
-     * Returns 0 if not an array.
-     */
-    public int getArrayCount()
-    {
-    	final String sig = getTypeSignature();
-    	if (sig == null)
-    		return 0;
-        return Signature.getArrayCount(sig);
-    }
-    
-    /**
-     * @return true if property is an enum type, false otherwise or if cannot be resolved
-     */
-    public boolean isEnumType()
-    {
-        return TypeUtil.isEnumType(getType());
-    }
-    
-	/**
-	 * Fully equivalent to:
-	 * 
-	 * getTypeSignature(true)
-	 * 
-	 * @return the fully resolved (if possible) type signature for
-     * the property or null if unable to determine.
-     * 
-     * NOTE: this is the "type erasure" signature, so any type parameters
-     * will be removed and only the raw type signature will be returned.
-	 */
-	public String getTypeSignature()
-    {
-	    return getTypeSignature(true);
-    }
-	
-	
-    /**
-     * @param eraseTypeParameters if true, the returned type has type parameters
-     * erased. If false, template types are resolved. 
-     * 
-     * @see org.eclipse.jst.jsf.common.util.TypeUtil#resolveTypeSignature(IType, String, boolean)
-     * for more information on how specific kinds of unresolved generics are resolved
-     * 
-     * @return the fully resolved (if possible) type signature for
-     * the property or null if unable to determine.
-     */
-    public String getTypeSignature(boolean eraseTypeParameters)
-    {
-        try
-        {
-            String unResolvedSig = getUnresolvedType();
-            return TypeUtil.resolveTypeSignature(_type, unResolvedSig, eraseTypeParameters);
-        }
-        catch (JavaModelException jme)
-        {
-            JSFCommonPlugin.log(jme, "Error resolving bean property type signature"); //$NON-NLS-1$
-            return null;
-        }
-    }
-    
-	/**
-	 * For example, if this property was formed from: List<String> getListOfStrings()
-	 * then the list would consist of the signature "Ljava.lang.String;".  All 
-	 * nested type paramters are resolved
-	 * 
-     * @see org.eclipse.jst.jsf.common.util.TypeUtil#resolveTypeSignature(IType, String, boolean)
-     * for more information on how specific kinds of unresolved generics are resolved
-	 * 
-	 * @return a list of type signatures (fully resolved if possible)
-	 * of this property's bounding type parameters.
-	 */
-	public List<String> getTypeParameterSignatures()
-	{
-	    List<String>  signatures = new ArrayList<String>();
-	    
-	    try
-	    {
-	        final String[] typeParameters = Signature.getTypeArguments(getUnresolvedType());
-	        //System.err.println(getUnresolvedType());
-	        for (String parameter : typeParameters)
-	        {
-	            //System.out.println(parameter);
-	            signatures.add(TypeUtil.resolveTypeSignature(_type, parameter, false));
-	        }
-	    }
-	    catch (JavaModelException jme)
-	    {
-            JSFCommonPlugin.log(jme, "Error resolving bean property type signature"); //$NON-NLS-1$
-            // fall-through and return empty array
-	    }
+  /**
+   * @return the IType for this property's type or null if it cannot determined.
+   *         Note that null does not necessarily indicate an error since some
+   *         types like arrays of things do not have corresponding JDT IType's
+   *         If typeSignature represents an array, the base element IType is
+   *         returned if possible
+   */
+  public IType getType()
+  {
+    return type;
+  }
 
-	    return signatures;
-	}
+  /**
+   * @return the number of array nesting levels in typeSignature. Returns 0 if
+   *         not an array.
+   */
+  public int getArrayCount()
+  {
+    final String sig = getTypeSignature();
+    if (sig == null)
+      return 0;
+    return Signature.getArrayCount(sig);
+  }
 
-//	public Map<String, String> getTypeParameterSignatureMap()
-//	{
-//	    Map<String, String>  signatures = new HashMap<String, String>();
-//        
-//        try
-//        {
-//            final String[] typeParameters = Signature.getTypeArguments(getUnresolvedType());
-//            
-//            for (String parameter : typeParameters)
-//            {
-//                signatures.add(TypeUtil.resolveTypeSignature(_type, parameter, false));
-//            }
-//        }
-//        catch (JavaModelException jme)
-//        {
-//            JSFCommonPlugin.log(jme, "Error resolving bean property type signature"); //$NON-NLS-1$
-//            // fall-through and return empty array
-//        }
-//
-//        return signatures;
-//	}
-	
-    private String getUnresolvedType() throws JavaModelException
-    {
-        String   typeSig = null;
-        
-        // first decide which method to use; getter always gets precendence
-        if (_getter != null)
-        {
-            typeSig = _getter.getReturnType();
-        }
-        // TODO: if no getter or setter could we have been created?
-        // use setter
-        else
-        {
-            typeSig = _setter.getParameterTypes()[0];
-        }
-        
-        return typeSig;
-    }
+  /**
+   * @return true if property is an enum type, false otherwise or if cannot be
+   *         resolved
+   */
+  public boolean isEnumType()
+  {
+    return TypeUtil.isEnumType(getType());
+  }
+
+  /**
+   * Fully equivalent to:
+   * 
+   * getTypeSignature(true)
+   * 
+   * @return the fully resolved (if possible) type signature for the property or
+   *         null if unable to determine.
+   * 
+   *         NOTE: this is the "type erasure" signature, so any type parameters
+   *         will be removed and only the raw type signature will be returned.
+   */
+  public String getTypeSignature()
+  {
+    return typeSignature;
+  }
+
+  /**
+   * For example, if this property was formed from: List<String>
+   * getListOfStrings() then the list would consist of the signature
+   * "Ljava.lang.String;". All nested type paramters are resolved
+   * 
+   * @return a list of type signatures (fully resolved if possible) of this
+   *         property's bounding type parameters.
+   */
+  public List<String> getTypeParameterSignatures()
+  {
+    return typeParameterSignatures;
+  }
+  
+  @Override
+  public String toString()
+  {    
+    return "JDTBeanProperty [name="+propertyName+ //$NON-NLS-1$
+           " typeSignature="+typeSignature+ //$NON-NLS-1$
+           " typeParameterSignatures "+typeParameterSignatures+ //$NON-NLS-1$
+           "]";  //$NON-NLS-1$
+  }
 }
diff --git a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/util/JDTBeanPropertyWorkingCopy.java b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/util/JDTBeanPropertyWorkingCopy.java
index 14ef80f..aa06b55 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/util/JDTBeanPropertyWorkingCopy.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/util/JDTBeanPropertyWorkingCopy.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2001, 2010 Oracle Corporation and others.
+ * Copyright (c) 2001, 2021 Oracle Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
  * which accompanies this distribution, and is available at
@@ -9,18 +9,19 @@
  * 
  * Contributors:
  *     Oracle Corporation - initial API and implementation
+ *     Reto Weiss/Axon Ivy	Cache resolved types
  *******************************************************************************/
 package org.eclipse.jst.jsf.common.util;
 
 import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 
 import org.eclipse.jdt.core.IMethod;
 import org.eclipse.jdt.core.IType;
 import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.Signature;
 import org.eclipse.jst.jsf.common.JSFCommonPlugin;
 
 /**
@@ -31,165 +32,154 @@
  * @author cbateman
  *
  */
-public class JDTBeanPropertyWorkingCopy extends JDTBeanProperty 
+public class JDTBeanPropertyWorkingCopy
 {
-	private final List		_setters;
-	
-	/**
-	 * the IMethod for the boolean "is" accessor method
-	 */
-	private IMethod        _isGetter;
+  private final List<IMethod> setters = new ArrayList<IMethod>();
+  private IMethod isGetter;
+  private IMethod getter;
+  private final IType type;
+  private final JDTTypeResolver typeResolver;
+  private final String propertyName;
 
-	private final Map<String, String> _resolvedSignatureMap;
-	
-	/**
-	 * @param type
-	 * @param resolvedSignatureMap
-	 */
-	public JDTBeanPropertyWorkingCopy(IType type, Map<String, String> resolvedSignatureMap)
-	{
-		super(type);
-		_setters = new ArrayList();
-		_resolvedSignatureMap = resolvedSignatureMap;
-	}
-	/**
-	 * Constructor
-	 * @param type 
-	 */
-	public JDTBeanPropertyWorkingCopy(IType type)
-	{
-        super(type);
-		_setters = new ArrayList();
-		_resolvedSignatureMap = new HashMap<String, String>();
-	}
-	
-	/**
-	 * @return the bean properties spawned from this working copy
-	 * Normally, there is only one property in the array, however,
-	 * since this working copy represents all properties with the same
-	 * name, there could be multiple properties since setters can
-	 * be overloaded by name and could result in zero or one readable
-	 * properties plus zero or more write-only properties with the same
-	 * name.  I can't see anywhere in the spec that covers this 
-	 * boundary case
-	 */
-	public JDTBeanProperty toValueObject()
-	{
-		// if the isGetter is present that it takes precedence
-		// over the the normal getter
-		IMethod  getter = getIsGetter() != null ? 
-							getIsGetter() : getGetter();
-		IMethod  matchedSetter = null;
+  /**
+   * @param type
+   * @param typeResolver
+   * @param propertyName 
+   */
+  public JDTBeanPropertyWorkingCopy(IType type, JDTTypeResolver typeResolver, String propertyName)
+  {
+    this.type = type;
+    this.typeResolver = typeResolver;
+    this.propertyName = propertyName;
+  }
 
-		if (getter != null)
-		{
-			matchedSetter = determineMatchedSetter(getter);
-		}
-		// if there's no getter than pick any setter: there
-		// are bigger problem when there's no getter than
-		// ambiguous setters
-		else if (_setters.size() > 0)
-		{
-			matchedSetter = (IMethod) _setters.get(0);
-		}
-		
-		JDTBeanProperty beanProp = new JDTBeanProperty(_type);
-		beanProp.setGetter(getter);
-		beanProp.setSetter(matchedSetter);
-		return beanProp;
-		
-	}
-	
-	private IMethod determineMatchedSetter(IMethod getter)
-	{
-		IMethod matchedSetter = null;
-		
-		// if there are no setters, there is no point in proceeding
-		if (_setters.size() < 1)
-		{
-			return null;
-		}
+  /**
+   * @param getter
+   */
+  public void setGetter(IMethod getter)
+  {
+    this.getter = getter;
+  }
 
-		try
-		{
-			final String getterSig = getResolvedSignature(_type, getter.getReturnType());
-			FIND_MATCHING_SETTER:for 
-				(final Iterator it = _setters.iterator(); it.hasNext();)
-			{
-				final IMethod  setter = (IMethod) it.next();
-				assert (setter.getNumberOfParameters() == 1);
-				final String paramSig = 
-					getResolvedSignature
-						(_type,setter.getParameterTypes()[0]);
-				
-				if (paramSig.equals(getterSig))
-				{
-					// we've found our match since only one
-					// setter with the same name as the getter
-					// can have the same matching type for a
-					// single arg method
-					matchedSetter = setter;
-					break FIND_MATCHING_SETTER;
-				}
-			}
-		}
-		catch (JavaModelException jme)
-		{
-            JSFCommonPlugin.log(jme, "Error determining getter return type, bean properties analysis may be inaccurate"); //$NON-NLS-1$
-		}
+  /**
+   * @param isGetter
+   */
+  public void setIsGetter(IMethod isGetter)
+  {
+    this.isGetter = isGetter;
+  }
 
-		return matchedSetter;
-	}
-	
-	//@Override
-	public void setGetter(IMethod getter) {
-		super.setGetter(getter);
-	}
-
-	/**
-	 * @param isGetter
-	 */
-	public void setIsGetter(IMethod isGetter) {
-		_isGetter = isGetter;
-	}
-
-	/**
-	 * @param setter
-	 */
-	public void addSetter(IMethod setter) {
-        if (setter != null
-                && setter.getNumberOfParameters() == 1)
-        {
-            _setters.add(setter);
-        }
-	}
-
-    /**
-     * Not supported on working copy.  This is synthetically generated
-     * on toValueObject()
-     * @return nothing; throws exception
-     */
-    public final IMethod getSetter()
+  /**
+   * @param setter
+   */
+  public void addSetter(IMethod setter)
+  {
+    if (setter != null
+            && setter.getNumberOfParameters() == 1)
     {
-        throw new UnsupportedOperationException("Setter not calculated in working copy.  Call toValueObject().getSetter()"); //$NON-NLS-1$
+      setters.add(setter);
     }
-    
-	/**
-	 * @return the "is" getter method or null if not found
-	 */
-	public IMethod getIsGetter() {
-		return _isGetter;
-	}
-	
-	private String getResolvedSignature(final IType type, final String unresolved)
-	{
-		String resolved = _resolvedSignatureMap.get(unresolved);
-		
-		if (resolved == null)
-		{
-			resolved = TypeUtil.resolveTypeSignature(_type, unresolved);
-			_resolvedSignatureMap.put(unresolved, resolved);
-		}
-		return resolved;
-	}
+  }
+
+  /**
+   * @return the bean properties spawned from this working copy Normally, there
+   *         is only one property in the array, however, since this working copy
+   *         represents all properties with the same name, there could be
+   *         multiple properties since setters can be overloaded by name and
+   *         could result in zero or one readable properties plus zero or more
+   *         write-only properties with the same name. I can't see anywhere in
+   *         the spec that covers this boundary case
+   */
+  public JDTBeanProperty toValueObject()
+  {
+    IMethod choosenGetter = isGetter != null ? isGetter : getter;
+    IMethod choosenSetter = chooseSetter(choosenGetter);
+    String unresolvedTypeSig = getUnresolvedType(choosenGetter, choosenSetter);
+    if (unresolvedTypeSig == null)
+    {
+      return new JDTBeanProperty(propertyName, null, null, Collections.EMPTY_LIST, choosenGetter, choosenSetter);
+    }
+    String resolvedTypeSig = typeResolver.resolveEraseTypeParams(unresolvedTypeSig);
+    IType resolvedType = resolveType(resolvedTypeSig);
+    List<String> parameterizedTypeSignatures = getParameterizedTypeSignature(unresolvedTypeSig);
+    return new JDTBeanProperty(
+            propertyName,
+            resolvedType,
+            resolvedTypeSig,
+            parameterizedTypeSignatures,
+            choosenGetter,
+            choosenSetter);
+  }
+
+  private IMethod chooseSetter(IMethod choosenGetter)
+  {
+    if (choosenGetter != null)
+    {
+      return determineMatchedSetter(choosenGetter);
+    }
+    else if (! setters.isEmpty())
+    {
+      return setters.get(0);
+    }
+    return null;
+  }
+
+  private IMethod determineMatchedSetter(IMethod choosenGetter)
+  {
+    if (setters.isEmpty())
+    {
+      return null;
+    }
+  
+    final String getterSig = typeResolver.resolveEraseTypeParams(getUnresolvedType(choosenGetter, null));
+    if (getterSig == null)
+    {
+      return null;
+    }
+    for (final Iterator it = setters.iterator(); it.hasNext();)
+    {
+      final IMethod setter = (IMethod) it.next();
+      assert (setter.getNumberOfParameters() == 1);
+      final String paramSig = typeResolver.resolveEraseTypeParams(setter.getParameterTypes()[0]);
+
+      if (paramSig.equals(getterSig))
+      {
+        return setter;
+      }
+    }
+    return null;
+  }
+
+  private static String getUnresolvedType(IMethod choosenGetter, IMethod choosenSetter)
+  {
+    try
+    {
+      if (choosenGetter != null)
+      {
+        return choosenGetter.getReturnType();
+      }
+      return choosenSetter.getParameterTypes()[0];
+    }
+    catch (JavaModelException jme)
+    {
+      JSFCommonPlugin.log(jme, "Error resolving bean property type signature"); //$NON-NLS-1$
+      return null;
+    }
+  }
+
+  private IType resolveType(String resolvedTypeSig)
+  {
+    final String typeSig = Signature.getElementType(resolvedTypeSig);
+    return TypeUtil.resolveType(type, typeSig);
+  }
+
+  private List<String> getParameterizedTypeSignature(String unresolvedTypeSignature)
+  {
+    List<String> typeSig = new ArrayList<String>();
+    for (String typeArg : Signature.getTypeArguments(unresolvedTypeSignature))
+    {
+      typeSig.add(typeResolver.resolveKeepTypeParams(typeArg));
+    }
+    return typeSig;
+  }
 }
diff --git a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/util/JDTTypeResolver.java b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/util/JDTTypeResolver.java
new file mode 100644
index 0000000..5f7e69b
--- /dev/null
+++ b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/util/JDTTypeResolver.java
@@ -0,0 +1,315 @@
+/*******************************************************************************
+ * Copyright (c) 2020, 2021 Axon Ivy.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Reto Weiss/Axon Ivy      Cache resolved types
+ *    
+ ********************************************************************************/
+package org.eclipse.jst.jsf.common.util;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.ITypeParameter;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.Signature;
+import org.eclipse.jst.jsf.common.internal.types.TypeConstants;
+
+/**
+ * Resolves types of a type and caches them
+ * @author rwei
+ * @since 27.11.2020
+ */
+public class JDTTypeResolver
+{
+  private final Map<String, IType> resolvedTypes = new HashMap<String, IType>();
+  private final IType type;
+  private final IType[] superTypes;
+
+  /**
+   * Constructor
+   * @param type
+   * @param superTypes
+   */
+  public JDTTypeResolver(IType type, IType[] superTypes)
+  {
+    this.type = type;
+    this.superTypes = superTypes;
+  }
+
+  /**
+   * @param unresolvedSignature
+   * @return resolved method signatures with erased type parameters
+   */
+  public String resolveMethodEraseTypeParams(String unresolvedSignature)
+  {
+    return resolveMethod(unresolvedSignature, TypeParameters.ERASE);
+  }
+
+  /**
+   * @param unresolvedSignature
+   * @return resolve method signature with type parameters
+   */
+  public String resolveMethodKeepTypeParams(String unresolvedSignature)
+  {
+    return resolveMethod(unresolvedSignature, TypeParameters.KEEP);
+  }
+
+  private String resolveMethod(String unresolvedSignature, TypeParameters eraseTypeParameters)
+  {
+    final String unresolvedSignatureNormalized = unresolvedSignature.replaceAll("/", "."); //$NON-NLS-1$ //$NON-NLS-2$
+
+    // get the list of parameters
+    final String[] parameters = Signature.getParameterTypes(unresolvedSignatureNormalized);
+
+    for (int i = 0; i < parameters.length; i++)
+    {
+      // try to full resolve the type
+      parameters[i] = resolve(parameters[i], eraseTypeParameters);
+    }
+
+    // resolve return type
+    final String resolvedReturn = resolve(Signature.getReturnType(unresolvedSignatureNormalized),
+            eraseTypeParameters);
+    return Signature.createMethodSignature(parameters, resolvedReturn);
+  }
+
+  /**
+   * @param unresolvedSignature
+   * @return resolved signature with erased type params
+   */
+  public String resolveEraseTypeParams(String unresolvedSignature)
+  {
+    return resolve(unresolvedSignature, TypeParameters.ERASE);
+  }
+
+  /**
+   * @param unresolvedSignature
+   * @return resolved signature with type params
+   */
+  public String resolveKeepTypeParams(String unresolvedSignature)
+  {
+    return resolve(unresolvedSignature, TypeParameters.KEEP);
+  }
+
+  private String resolve(String unresolvedSignature, TypeParameters eraseTypeParameters)
+  {
+    final int sigKind = Signature.getTypeSignatureKind(unresolvedSignature);
+
+    switch (sigKind)
+    {
+      case Signature.BASE_TYPE_SIGNATURE:
+        return unresolvedSignature;
+
+      case Signature.ARRAY_TYPE_SIGNATURE:
+      {
+        final String elementType = Signature.getElementType(unresolvedSignature);
+
+        if (Signature.getTypeSignatureKind(elementType) == Signature.BASE_TYPE_SIGNATURE)
+        {
+          return unresolvedSignature;
+        }
+
+        final String resolvedElementType = resolveRelative(elementType, eraseTypeParameters);
+        String resultType = ""; //$NON-NLS-1$
+        for (int i = 0; i < Signature.getArrayCount(unresolvedSignature); i++)
+        {
+          resultType += Signature.C_ARRAY;
+        }
+
+        return resultType + resolvedElementType;
+      }
+
+      case Signature.TYPE_VARIABLE_SIGNATURE:
+        return resolveRelative(unresolvedSignature, eraseTypeParameters);
+
+      case Signature.CLASS_TYPE_SIGNATURE:
+        return resolveRelative(unresolvedSignature, eraseTypeParameters);
+
+      case Signature.WILDCARD_TYPE_SIGNATURE:
+        // strip the wildcard and try again. Too bad Signature doesn't seem to
+        // have a method
+        // for this
+        if (unresolvedSignature.charAt(0) == Signature.C_STAR)
+        {
+          return TypeConstants.TYPE_JAVAOBJECT;
+        }
+        return resolve(unresolvedSignature.substring(1), eraseTypeParameters);
+
+      case Signature.CAPTURE_TYPE_SIGNATURE:
+        // strip the capture and try again
+        return resolve(Signature.removeCapture(unresolvedSignature), eraseTypeParameters);
+      // case Signature.TYPE_VARIABLE_SIGNATURE:
+      // resolveSignatureRelative(owningType, typeSignature,
+      // eraseTypeParameters);
+
+      default:
+        return unresolvedSignature;
+    }
+  }
+
+  /**
+   * @param owningType -- type relative to which typeSignature will be resolved
+   * @param typeSignature -- non-array type signature
+   * @return the resolved type signature if possible or typeSignature if not
+   */
+  private String resolveRelative(final String typeSignature, final TypeParameters eraseTypeParameters)
+  {
+    // if already fully resolved, return the input
+    if (typeSignature.charAt(0) == Signature.C_RESOLVED)
+    {
+      return typeSignature;
+    }
+
+    List<String> typeParameters = new ArrayList<String>();
+
+    IType resolvedType = resolveType(typeSignature);
+
+    if (resolvedType != null)
+    {
+      if (eraseTypeParameters == TypeParameters.KEEP)
+      {
+        // ensure that type parameters are resolved recursively
+        for (String typeParam : Signature.getTypeArguments(typeSignature))
+        {
+          typeParam = Signature.removeCapture(typeParam);
+          // check and remove bound wildcarding (extends/super/?)
+          if (Signature.getTypeSignatureKind(typeParam) == Signature.WILDCARD_TYPE_SIGNATURE)
+          {
+            // convert ? to Object, strip extends/super
+            if (typeParam.charAt(0) == Signature.C_STAR)
+            {
+              typeParam = TypeConstants.TYPE_JAVAOBJECT;
+            }
+            else
+            {
+              typeParam = typeParam.substring(1);
+            }
+          }
+          final String resolvedParameter = resolveRelative(
+                  // use the enclosing type,
+                  // *not* the resolved type because
+                  // we need to resolve in that context
+                  typeParam, eraseTypeParameters);
+          typeParameters.add(resolvedParameter);
+        }
+      }
+
+      final String resolvedTypeSignature = Signature.createTypeSignature(resolvedType.getFullyQualifiedName(),
+              true);
+
+      if (typeParameters.size() > 0 && eraseTypeParameters == TypeParameters.KEEP)
+      {
+        StringBuffer sb = new StringBuffer(resolvedTypeSignature);
+
+        if (sb.charAt(sb.length() - 1) == ';')
+        {
+          sb = sb.delete(sb.length() - 1, sb.length());
+        }
+
+        sb.append("<"); //$NON-NLS-1$
+        for (String param : typeParameters)
+        {
+          // System.out.println("type param:
+          // "+resolvedType.getTypeParameter(param));
+          sb.append(param);
+        }
+
+        // replace the dangling ',' with the closing ">"
+        sb.append(">;"); //$NON-NLS-1$
+        return sb.toString();
+      }
+
+      return resolvedTypeSignature;
+    }
+
+    if (Signature.getTypeSignatureKind(typeSignature) == Signature.CLASS_TYPE_SIGNATURE
+            || Signature.getTypeSignatureKind(typeSignature) == Signature.TYPE_VARIABLE_SIGNATURE)
+    {
+      // if we are unable to resolve, check to see if the owning type has
+      // a parameter by this name
+      ITypeParameter typeParam = type.getTypeParameter(Signature.getSignatureSimpleName(typeSignature));
+
+      // if we have a type parameter and it hasn't been resolved to a type,
+      // then assume it is a method template placeholder (i.e. T in ArrayList).
+      // at runtime these unresolved parameter variables are effectively
+      // turned into Object's. For example, think List.add(E o). At runtime,
+      // E will behave exactly like java.lang.Object in that signature
+      if (typeParam.exists())
+      {
+        return TypeConstants.TYPE_JAVAOBJECT;
+      }
+
+      // TODO: is there a better way to handle a failure to resolve
+      // than just garbage out?
+      // JSFCommonPlugin.log(new Exception("Failed to resolve type:
+      // "+typeSignature), "Failed to resolve type: "+typeSignature);
+      // //$NON-NLS-1$ //$NON-NLS-2$
+    }
+
+    return typeSignature;
+  }
+
+  private IType resolveType(final String typeSignature)
+  {
+    final String typeName = TypeUtil.getFullyQualifiedName(typeSignature);
+    return resolveTypeName(typeName);
+  }
+  
+  IType resolveTypeName(String typeName)
+  {
+    IType resolved = resolvedTypes.get(typeName);
+    if (resolved != null)
+    {
+      return resolved;
+    }
+    resolved = resolveTypeNameRelative(typeName);
+    resolvedTypes.put(typeName, resolved);
+    return resolved;
+  }
+
+  private IType resolveTypeNameRelative(String typeName)
+  {
+    try
+    {
+      String[][] resolved = type.resolveType(typeName);
+      if (resolved != null && resolved.length > 0)
+      {
+        return type.getJavaProject().findType(resolved[0][0], resolved[0][1]);
+      }
+      return resolveInParents(typeName);
+    }
+    catch (JavaModelException jme)
+    {
+      return null;
+    }
+  }
+
+  private IType resolveInParents(String fullyQualifiedName)
+          throws JavaModelException
+  {
+    for (IType superType : superTypes)
+    {
+      String[][] resolved = superType.resolveType(fullyQualifiedName);
+      if (resolved != null && resolved.length > 0)
+      {
+        return type.getJavaProject().findType(resolved[0][0], resolved[0][1]);
+      }
+    }
+    return null;
+  }
+
+  private enum TypeParameters
+  {
+    ERASE, KEEP
+  }
+}
diff --git a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/util/TypeUtil.java b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/util/TypeUtil.java
index 134d95b..067d520 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/util/TypeUtil.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/common/util/TypeUtil.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2006, 2010 Oracle Corporation.
+ * Copyright (c) 2006, 2021 Oracle Corporation.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
  * which accompanies this distribution, and is available at
@@ -9,12 +9,12 @@
  *
  * Contributors:
  *    Cameron Bateman/Oracle - initial API and implementation
+ *    Reto Weiss/Axon Ivy    - Cache resolved types
  *    
  ********************************************************************************/
 
 package org.eclipse.jst.jsf.common.util;
 
-import java.util.ArrayList;
 import java.util.List;
 
 import org.eclipse.jdt.core.IField;
@@ -45,236 +45,20 @@
         		|| (Signature.getTypeSignatureKind(typeSignature) == Signature.ARRAY_TYPE_SIGNATURE
         			&& Signature.getElementType(typeSignature).charAt(0) == Signature.C_RESOLVED))
         {
-            IType type = null;
-            
             try
             {
-                type = owningType.getJavaProject().
+                return owningType.getJavaProject().
                            findType(getFullyQualifiedName(typeSignature));
             }
             catch (JavaModelException jme)
             {
                 // do nothing; return type == null;
             }
-            
-            return type;
+            return null;
         }
-        
-        
         return resolveTypeRelative(owningType, typeSignature);
     }
 
-    /**
-     * Fully equivalent to:
-     * 
-     * #resolveTypeSignature(owningType, typeSignature, true)
-     * 
-     * If resolved, type signature has generic type parameters erased (absent).
-     * 
-     * @param owningType
-     * @param typeSignature
-     * @return the resolved type signature for typeSignature in owningType or
-     * typeSignature unchanged if cannot resolve.
-     */
-    public static String resolveTypeSignature(final IType owningType, final String typeSignature)
-    {
-        return resolveTypeSignature(owningType, typeSignature, true);
-    }
-    
-    /**
-     * Resolve typeSignature in the context of owningType.  This method will return 
-     * a type erased signture if eraseTypeParameters == true and will attempt to
-     * resolve and include parameters if eraseTypeParamters == false
-     * 
-     * NOTE: special rules apply to the way unresolved type parameters and wildcards
-     * are resolved:
-     * 
-     * 1) If a fully unresolved type parameter is found, then it will be replaced with Ljava.lang.Object;
-     * 
-     * i.e.  List<T>  -> Ljava.util.List<Ljava.lang.Object;>;  for any unresolved T.
-     * 
-     * 2) Any bounded wildcard will be replaced by the bound:
-     * 
-     * i.e. List<? extends String> -> Ljava.util.List<Ljava.lang.String;>;
-     * i.e. List<? super String> -> Ljava.util.List<Ljava.lang.String;>;
-     * 
-     * Note limitation here: bounds that use 'super' will take the "best case" scenario that the list
-     * type is of that type.
-     * 
-     * 3) The unbounded wildcard will be replaced by Ljava.lang.Object;
-     * 
-     * i.e. List<?> -> Ljava.util.List<Ljava.lang.Object;>;
-     * 
-     * 
-     * The reason for this substition is to return the most accurate reasonable approximation
-     * of the type within what is known by owningType
-     * 
-     * @param owningType
-     * @param typeSignature
-     * @param eraseTypeParameters if set to false, type parameters are resolved included
-     * in the signature
-     * @return the resolved type signature for typeSignature in owningType or
-     * typeSignature unchanged if cannot resolve.
-     */
-    public static String resolveTypeSignature(final IType owningType, final String typeSignature, boolean eraseTypeParameters)
-    {
-        final int sigKind = Signature.getTypeSignatureKind(typeSignature);
-    
-        switch (sigKind)
-        {
-            case Signature.BASE_TYPE_SIGNATURE:
-                return typeSignature;
-                
-            case Signature.ARRAY_TYPE_SIGNATURE:
-            {
-                final String elementType = Signature.getElementType(typeSignature);
-                
-                if (Signature.getTypeSignatureKind(elementType) == Signature.BASE_TYPE_SIGNATURE)
-                {
-                    return typeSignature;
-                }
-
-                final String resolvedElementType = resolveSignatureRelative(owningType, elementType, eraseTypeParameters);
-                String resultType = ""; //$NON-NLS-1$
-                for (int i = 0; i < Signature.getArrayCount(typeSignature);i++)
-                {
-                    resultType+=Signature.C_ARRAY;
-                }
-                
-                return resultType+resolvedElementType;
-            }
-
-            case Signature.TYPE_VARIABLE_SIGNATURE:
-            	return resolveSignatureRelative(owningType, typeSignature, eraseTypeParameters);
-            
-            case Signature.CLASS_TYPE_SIGNATURE:
-                return resolveSignatureRelative(owningType, typeSignature, eraseTypeParameters);
-
-            case Signature.WILDCARD_TYPE_SIGNATURE:
-                // strip the wildcard and try again.  Too bad Signature doesn't seem to have a method
-                // for this
-                if (typeSignature.charAt(0) == Signature.C_STAR)
-                {
-                    return TypeConstants.TYPE_JAVAOBJECT;
-                }
-                return resolveTypeSignature(owningType, typeSignature.substring(1), eraseTypeParameters);
-            
-            case Signature.CAPTURE_TYPE_SIGNATURE:
-                // strip the capture and try again
-                return resolveTypeSignature(owningType, Signature.removeCapture(typeSignature), eraseTypeParameters);
-//            case Signature.TYPE_VARIABLE_SIGNATURE:
-//                resolveSignatureRelative(owningType, typeSignature, eraseTypeParameters);
-
-            default:
-                return typeSignature;
-        }
-    }
-    
-    /**
-     * @param owningType -- type relative to which typeSignature will be resolved
-     * @param typeSignature -- non-array type signature
-     * @return the resolved type signature if possible or typeSignature if not
-     */
-    private static String resolveSignatureRelative(final IType owningType, final String typeSignature, final boolean eraseTypeParameters)
-    {
-        // if already fully resolved, return the input
-        if (typeSignature.charAt(0) == Signature.C_RESOLVED)
-        {
-            return typeSignature;
-        }
-
-        List<String> typeParameters = new ArrayList<String>();
-
-        IType resolvedType = resolveTypeRelative(owningType, typeSignature);
-
-        if (resolvedType != null)
-        {
-            if (!eraseTypeParameters)
-            {
-                // ensure that type parameters are resolved recursively
-                for (String typeParam : Signature.getTypeArguments(typeSignature))
-                {
-                    typeParam = Signature.removeCapture(typeParam);
-                    // check and remove bound wildcarding (extends/super/?)
-                    if (Signature.getTypeSignatureKind(typeParam) == Signature.WILDCARD_TYPE_SIGNATURE)
-                    {
-                        // convert ? to Object, strip extends/super
-                        if (typeParam.charAt(0) == Signature.C_STAR)
-                        {
-                            typeParam = TypeConstants.TYPE_JAVAOBJECT;
-                        }
-                        else
-                        {
-                            typeParam = typeParam.substring(1);
-                        }
-                    }
-                    final String resolvedParameter = 
-                    	resolveSignatureRelative(
-                    			// use the enclosing type, 
-                    			// *not* the resolved type because 
-                    			// we need to resolve in that context
-                    			owningType, 
-                    				typeParam, eraseTypeParameters);
-                    typeParameters.add(resolvedParameter);
-                }
-            }
-
-            final String  resolvedTypeSignature = 
-                Signature.createTypeSignature
-                    (resolvedType.getFullyQualifiedName(), true);
-           
-
-            if (typeParameters.size() > 0 && !eraseTypeParameters)
-            {
-                StringBuffer sb = new StringBuffer(resolvedTypeSignature);
-
-                if (sb.charAt(sb.length()-1) == ';')
-                {
-                    sb = sb.delete(sb.length()-1, sb.length());
-                }
-                
-                sb.append("<"); //$NON-NLS-1$
-                for(String param : typeParameters)
-                {
-                    //System.out.println("type param: "+resolvedType.getTypeParameter(param));
-                    sb.append(param);
-                }
-                
-                // replace the dangling ',' with the closing ">"
-                sb.append(">;"); //$NON-NLS-1$
-                return sb.toString();
-            }
-            
-            return resolvedTypeSignature;
-        }
-
-        if (Signature.getTypeSignatureKind(typeSignature) == 
-                Signature.CLASS_TYPE_SIGNATURE
-            || Signature.getTypeSignatureKind(typeSignature)
-                == Signature.TYPE_VARIABLE_SIGNATURE)
-        {
-            // if we are unable to resolve, check to see if the owning type has
-            // a parameter by this name
-            ITypeParameter typeParam = owningType.getTypeParameter(Signature.getSignatureSimpleName(typeSignature));
-            
-            // if we have a type parameter and it hasn't been resolved to a type,
-            // then assume it is a method template placeholder (i.e. T in ArrayList).
-            // at runtime these unresolved parameter variables are effectively 
-            // turned into Object's.  For example, think List.add(E o).  At runtime,
-            // E will behave exactly like java.lang.Object in that signature
-            if (typeParam.exists())
-            {
-                return TypeConstants.TYPE_JAVAOBJECT;
-            }
-            
-            // TODO: is there a better way to handle a failure to resolve
-            // than just garbage out?
-            //JSFCommonPlugin.log(new Exception("Failed to resolve type: "+typeSignature), "Failed to resolve type: "+typeSignature); //$NON-NLS-1$ //$NON-NLS-2$
-        }
-        
-        return typeSignature;
-    }
-
     private static IType resolveTypeRelative(final IType owningType, final String typeSignature)
     {
         final String fullName = getFullyQualifiedName(typeSignature);
@@ -313,37 +97,6 @@
         return Signature.createTypeSignature(fullyQualifiedName, true);
     }
 
-    
-    /**
-     * @param owner
-     * @param unresolvedSignature
-     * @return the resolved method signature for unresolvedSignature in owner
-     */
-    public static String resolveMethodSignature(final IType  owner, 
-                                         final String unresolvedSignature)
-    {
-        
-        final String unresolvedSignatureNormalized =
-            unresolvedSignature.replaceAll("/", "."); //$NON-NLS-1$ //$NON-NLS-2$
-        
-        // get the list of parameters
-        final String[] parameters = 
-            Signature.getParameterTypes(unresolvedSignatureNormalized);
-        
-        for (int i = 0; i < parameters.length; i++)
-        {
-            // try to full resolve the type
-            parameters[i] = resolveTypeSignature(owner, parameters[i]);
-        }
-        
-        // resolve return type
-        final String resolvedReturn = 
-            resolveTypeSignature(owner, 
-                                  Signature.getReturnType(unresolvedSignatureNormalized));
-        
-        return Signature.createMethodSignature(parameters, resolvedReturn);
-    }
-    
     /**
      * @param typeSignature     
      * @return a fully qualified Java class name from a type signature
@@ -355,7 +108,7 @@
         final String typeName = Signature.getSignatureSimpleName(typeSignature);
         return "".equals(packageName) ? typeName : packageName + "." + typeName;  //$NON-NLS-1$//$NON-NLS-2$
     }
-    
+
     private static IType resolveInParents(IType  childType, String fullyQualifiedName)
                                 throws JavaModelException
     {
@@ -407,7 +160,7 @@
             return null;
         }
     }
-    
+
     /**
      * @param type
      * @param typeParamSignature -- must be a Type Variable Signature
@@ -452,7 +205,7 @@
         
         return null;
     }
-    
+
     /**
      * @param type
      * @param fieldName
@@ -493,7 +246,7 @@
         
         return false;
     }
-    
+
     /**
      * @param typeSig1 the type signature of the first enum. Must be non-null, fully resolved enum type.
      * @param typeSig2 the type signature of the second enum.  Must be non-null, fully resolved enum type.
@@ -525,7 +278,7 @@
         // only comparable if is the same class
         return typeSig1.equals(typeSig2);
     }
-    
+
     /**
      * @param typeSig1 the type signature of the first enum. Must be non-null, fully resolved enum type.
      * @param typeSig2 the type signature of the second enum. Must be non-null, fully resolved enum type.
@@ -556,7 +309,6 @@
         // can never be equal
         return !typeSig1.equals(typeSig2);
     }
-    
 
     /**
      * NOTE: we diverge from IType.isEnum() because we also return true if the base type
@@ -591,7 +343,7 @@
         // if unresolved assume false
         return false;
     }
-    
+
     private TypeUtil()
     {
         // no external instantiation
diff --git a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/context/symbol/internal/impl/IBoundedListTypeDescriptorImpl.java b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/context/symbol/internal/impl/IBoundedListTypeDescriptorImpl.java
index e7b7a00..7061268 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/context/symbol/internal/impl/IBoundedListTypeDescriptorImpl.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/context/symbol/internal/impl/IBoundedListTypeDescriptorImpl.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2006, 2019 IBM Corporation and others.
+ * Copyright (c) 2006, 2021 IBM Corporation and others.
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
  * which accompanies this distribution, and is available at
@@ -8,7 +8,8 @@
  * SPDX-License-Identifier: EPL-2.0
  *
  * Contributors:
- *     IBM Corporation - initial API and implementation
+ *     IBM Corporation     - initial API and implementation
+ *     Reto Weiss/Axon Ivy - Cache JDTBeanIntrospector
  *******************************************************************************/
 /**
  * <copyright>
@@ -24,14 +25,11 @@
 
 import org.eclipse.emf.common.util.EList;
 import org.eclipse.emf.ecore.EClass;
-import org.eclipse.jdt.core.IMethod;
 import org.eclipse.jdt.core.IType;
-import org.eclipse.jdt.core.JavaModelException;
 import org.eclipse.jdt.core.Signature;
-import org.eclipse.jst.jsf.common.JSFCommonPlugin;
 import org.eclipse.jst.jsf.common.internal.types.TypeConstants;
 import org.eclipse.jst.jsf.common.util.JDTBeanIntrospector;
-import org.eclipse.jst.jsf.common.util.TypeUtil;
+import org.eclipse.jst.jsf.common.util.JDTBeanMethod;
 import org.eclipse.jst.jsf.context.symbol.IBoundedListTypeDescriptor;
 import org.eclipse.jst.jsf.context.symbol.IJavaTypeDescriptor2;
 import org.eclipse.jst.jsf.context.symbol.IPropertySymbol;
@@ -73,6 +71,7 @@
      * <!-- end-user-doc -->
      * @generated
      */
+    @Override
     protected EClass eStaticClass() {
         return SymbolPackage.Literals.IBOUNDED_LIST_TYPE_DESCRIPTOR;
     }
@@ -137,68 +136,55 @@
         
         if (type != null)
         {
-            final JDTBeanIntrospector introspector = 
-                new JDTBeanIntrospector(type);
+            final JDTBeanIntrospector introspector = JDTBeanIntrospector.forType(type);
             
-            final IMethod callMethod = 
-                matchMethod(methodName, methodArguments, introspector.getAllMethods());
+            final JDTBeanMethod callMethod = 
+                matchMethod(methodName, methodArguments, introspector.getMethods());
             
             if (callMethod != null)
             {
-                try 
-                {
-                    // resolve the method's return type; don't erase parameters
-                    final String retTypeSignature = 
-                        TypeUtil.resolveTypeSignature
-                            (type, callMethod.getReturnType(), false) ;
-                    
-                    final IPropertySymbol  propSymbol = 
-                        SymbolFactory.eINSTANCE.createIPropertySymbol();
+                // resolve the method's return type; don't erase parameters
+                final String retTypeSignature = callMethod.getResolvedReturnTypeUnerased();
+                final IPropertySymbol  propSymbol = 
+                    SymbolFactory.eINSTANCE.createIPropertySymbol();
 
-                    // TODO: there is a possible problem here for non-string keyed maps
-                    propSymbol.setName(symbolName);
-                    propSymbol.setReadable(true);
-                    IJavaTypeDescriptor2 typeDesc = 
-                        SymbolFactory.eINSTANCE.createIJavaTypeDescriptor2();
-                    
-                    typeDesc.setArrayCount(Signature.getArrayCount(retTypeSignature));
-                    
-                    // may be null
-                    typeDesc.setType(resolveType(retTypeSignature));
-                    typeDesc.setTypeSignatureDelegate(retTypeSignature);
-                    propSymbol.setTypeDescriptor(typeDesc);
-                    result = propSymbol;
-                } 
-                catch (JavaModelException e) 
-                {
-                    JSFCommonPlugin.log(e);
-                    // fall-through and return null result
-                }
+                // TODO: there is a possible problem here for non-string keyed maps
+                propSymbol.setName(symbolName);
+                propSymbol.setReadable(true);
+                IJavaTypeDescriptor2 typeDesc = 
+                    SymbolFactory.eINSTANCE.createIJavaTypeDescriptor2();
+                
+                typeDesc.setArrayCount(Signature.getArrayCount(retTypeSignature));
+                
+                // may be null
+                typeDesc.setType(resolveType(retTypeSignature));
+                typeDesc.setTypeSignatureDelegate(retTypeSignature);
+                propSymbol.setTypeDescriptor(typeDesc);
+                result = propSymbol;
             }
         }
         
         return result;
     }
     
-    private IMethod matchMethod(String name, List methodArguments, IMethod[] allMethods)
+    private JDTBeanMethod matchMethod(String name, List methodArguments, JDTBeanMethod[] allMethods)
     {
         final List argSigs = convertArgsToSignatures(methodArguments);
-        IMethod matchedMethod = null;
         
         for (int i = 0; i < allMethods.length; i++)
         {
-            final IMethod method = allMethods[i];
+            final JDTBeanMethod method = allMethods[i];
             
             // check for names and argument count match
-            if (method.getParameterTypes().length == argSigs.size()
+            if (method.getUnresolvedParameterTypesUnerased().length == argSigs.size()
                     && method.getElementName().equals(name))
             {
-                String[] methods = method.getParameterTypes();
+                String[] paramSigs = method.getUnresolvedParameterTypesUnerased(); // RWEI seems not be correct to get unresolved types here
                 // need to verify argument matches
                 boolean isMatched = true;
-                CHECK_ARGUMENTS: for (int j = 0; j < methods.length; j++)
+                CHECK_ARGUMENTS: for (int j = 0; j < paramSigs.length; j++)
                 {
-                    if (!methods[j].equals(argSigs.get(j)))
+                    if (!paramSigs[j].equals(argSigs.get(j)))
                     {
                         // not a match
                         isMatched = false;
@@ -213,7 +199,7 @@
             }
         }
 
-        return matchedMethod;
+        return null;
     }
 
     private List convertArgsToSignatures(List methodArgs)
diff --git a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/context/symbol/internal/impl/IJavaTypeDescriptor2Impl.java b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/context/symbol/internal/impl/IJavaTypeDescriptor2Impl.java
index 95fbaa8..d1588ef 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/context/symbol/internal/impl/IJavaTypeDescriptor2Impl.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/context/symbol/internal/impl/IJavaTypeDescriptor2Impl.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2006, 2010 Oracle Corporation.
+ * Copyright (c) 2006, 2021 Oracle Corporation.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
  * which accompanies this distribution, and is available at
@@ -9,6 +9,7 @@
  *
  * Contributors:
  *    Cameron Bateman/Oracle - initial API and implementation
+ *    Reto Weiss/Axon Ivy    - Cache JDTBeanIntrospector
  *    
  ********************************************************************************/
 package org.eclipse.jst.jsf.context.symbol.internal.impl;
@@ -28,13 +29,13 @@
 import org.eclipse.jdt.core.Flags;
 import org.eclipse.jdt.core.IJavaElement;
 import org.eclipse.jdt.core.IJavaProject;
-import org.eclipse.jdt.core.IMethod;
 import org.eclipse.jdt.core.IType;
 import org.eclipse.jdt.core.JavaModelException;
 import org.eclipse.jdt.core.Signature;
 import org.eclipse.jst.jsf.common.JSFCommonPlugin;
 import org.eclipse.jst.jsf.common.internal.types.TypeInfoCache;
 import org.eclipse.jst.jsf.common.util.JDTBeanIntrospector;
+import org.eclipse.jst.jsf.common.util.JDTBeanMethod;
 import org.eclipse.jst.jsf.common.util.JDTBeanProperty;
 import org.eclipse.jst.jsf.common.util.TypeUtil;
 import org.eclipse.jst.jsf.context.symbol.IBeanMethodSymbol;
@@ -70,27 +71,27 @@
      * @generated
      */
     @SuppressWarnings("hiding")
-	public static final String copyright = "Copyright 2006 Oracle";  //$NON-NLS-1$
+    public static final String copyright = "Copyright 2006 Oracle";  //$NON-NLS-1$
 
     /**
      * The default value of the '{@link #getType() <em>Type</em>}' attribute.
      * <!-- begin-user-doc -->
-	 * <!-- end-user-doc -->
+     * <!-- end-user-doc -->
      * @see #getType()
      * @generated
      * @ordered
      */
-	protected static final IType TYPE_EDEFAULT = null;
+    protected static final IType TYPE_EDEFAULT = null;
 
     /**
      * The cached value of the '{@link #getType() <em>Type</em>}' attribute.
      * <!-- begin-user-doc -->
-	 * <!-- end-user-doc -->
+     * <!-- end-user-doc -->
      * @see #getType()
      * @generated
      * @ordered
      */
-	protected IType type = TYPE_EDEFAULT;
+    protected IType type = TYPE_EDEFAULT;
 
     /**
      * The default value of the '{@link #getArrayCount() <em>Array Count</em>}' attribute.
@@ -125,7 +126,7 @@
      * @ordered
      */
     @SuppressWarnings("hiding")
-	protected static final IJavaElement JDT_CONTEXT_EDEFAULT = null;
+    protected static final IJavaElement JDT_CONTEXT_EDEFAULT = null;
 
     /**
      * The cached value of the '{@link #getJdtContext() <em>Jdt Context</em>}' attribute.
@@ -136,24 +137,25 @@
      * @ordered
      */
     @SuppressWarnings("hiding")
-	protected IJavaElement jdtContext = JDT_CONTEXT_EDEFAULT;
+    protected IJavaElement jdtContext = JDT_CONTEXT_EDEFAULT;
 
     /**
      * <!-- begin-user-doc -->
-	 * <!-- end-user-doc -->
+     * <!-- end-user-doc -->
      * @generated
      */
-	protected IJavaTypeDescriptor2Impl() {
+    protected IJavaTypeDescriptor2Impl() {
         super();
     }
 
     /**
      * <!-- begin-user-doc -->
      * @return the static class 
-	 * <!-- end-user-doc -->
+     * <!-- end-user-doc -->
      * @generated
      */
-	protected EClass eStaticClass() {
+    @Override
+    protected EClass eStaticClass() {
         return SymbolPackage.Literals.IJAVA_TYPE_DESCRIPTOR2;
     }
 
@@ -161,31 +163,32 @@
      * <!-- begin-user-doc -->
      * @return the JDT type descriptor; if type is an array then this type
      * represent's the array base type only
-	 * <!-- end-user-doc -->
+     * <!-- end-user-doc -->
      * @generated
      */
-	public IType getType() {
+    public IType getType() {
         return type;
     }
 
     /**
      * <!-- begin-user-doc -->
      * @param newType 
-	 * <!-- end-user-doc -->
+     * <!-- end-user-doc -->
      * @generated
      */
-	public void setType(IType newType) {
+    public void setType(IType newType) {
         IType oldType = type;
         type = newType;
         if (eNotificationRequired())
             eNotify(new ENotificationImpl(this, Notification.SET, SymbolPackage.IJAVA_TYPE_DESCRIPTOR2__TYPE, oldType, type));
     }
 
-	/**
-	 * @see org.eclipse.jst.jsf.context.symbol.internal.impl.ITypeDescriptorImpl#getInterfaceTypeSignatures()
-	 * @generated NOT
-	 */
-	public EList getInterfaceTypeSignatures() 
+    /**
+     * @see org.eclipse.jst.jsf.context.symbol.internal.impl.ITypeDescriptorImpl#getInterfaceTypeSignatures()
+     * @generated NOT
+     */
+    @Override
+    public EList getInterfaceTypeSignatures() 
     {
         EList  interfaces = new BasicEList();
         
@@ -208,6 +211,7 @@
      * @see org.eclipse.jst.jsf.context.symbol.internal.impl.ITypeDescriptorImpl#getSuperTypeSignatures()
      * @generated NOT
      */
+    @Override
     public EList getSuperTypeSignatures() 
     {
         EList  interfaces = new BasicEList();
@@ -247,64 +251,66 @@
         }
     }
 
+    @Override
     public EList getProperties() 
     {
         return getBeanProperties();
     }
 
     
+    @Override
     public EList getMethods() 
     {
         return getBeanMethods();
     }
 
     /**
-	 * <!-- begin-user-doc -->
+     * <!-- begin-user-doc -->
      * @return the bean props for this java type 
-	 * <!-- end-user-doc -->
-	 * @generated NOT
-	 */
-	public EList getBeanProperties() 
-	{
-	    TypeInfoCache typeInfoCache = TypeInfoCache.getInstance();
-	    IBeanPropertySymbol[] properties = typeInfoCache.getCachedPropertySymbols(type);
-	    Collection propertyColl;
-	    if (properties == null) {
-	        propertyColl = getPropertiesInternal();
-	        properties = (IBeanPropertySymbol[]) propertyColl.toArray(new IBeanPropertySymbol[propertyColl.size()]);
-	        typeInfoCache.cachePropertySymbols(type, properties);
-	    } 
-	    else 
-	    {
+     * <!-- end-user-doc -->
+     * @generated NOT
+     */
+    public EList getBeanProperties() 
+    {
+        TypeInfoCache typeInfoCache = TypeInfoCache.getInstance();
+        IBeanPropertySymbol[] properties = typeInfoCache.getCachedPropertySymbols(type);
+        Collection propertyColl;
+        if (properties == null) {
+            propertyColl = getPropertiesInternal();
+            properties = (IBeanPropertySymbol[]) propertyColl.toArray(new IBeanPropertySymbol[propertyColl.size()]);
+            typeInfoCache.cachePropertySymbols(type, properties);
+        } 
+        else 
+        {
             propertyColl = new ArrayList(properties.length);
             Collections.addAll(propertyColl, (Object[])properties);
-	    }
-	    BasicEList list = new BasicEList(propertyColl);
-	    return list;
-	}
+        }
+        BasicEList list = new BasicEList(propertyColl);
+        return list;
+    }
 
-	/**
-	 * <!-- begin-user-doc -->
+    /**
+     * <!-- begin-user-doc -->
      * @return the bean methods for this type  
-	 * <!-- end-user-doc -->
-	 * @generated NOT
-	 */
-	public EList getBeanMethods() {
-	    TypeInfoCache typeInfoCache = TypeInfoCache.getInstance();
-	    IBeanMethodSymbol[] methods = typeInfoCache.getCachedMethodSymbols(type);
-	    Collection methodColl;
-	    if (methods == null) 
-	    {
-	        methodColl = getMethodsInternal();
-	        methods = (IBeanMethodSymbol[]) methodColl.toArray(new IBeanMethodSymbol[methodColl.size()]);
-	        typeInfoCache.cacheMethodSymbols(type, methods);
-	    } else {
-	        methodColl = new ArrayList(methods.length);
-	        Collections.addAll(methodColl, (Object[])methods);
-	    }
-	    BasicEList list = new BasicEList(methodColl);
-		return list;
-	}
+     * <!-- end-user-doc -->
+     * @generated NOT
+     */
+    public EList getBeanMethods() {
+        TypeInfoCache typeInfoCache = TypeInfoCache.getInstance();
+        IBeanMethodSymbol[] methods = typeInfoCache.getCachedMethodSymbols(type);
+        Collection methodColl;
+        if (methods == null) 
+        {
+            methodColl = getMethodsInternal();
+            methods = (IBeanMethodSymbol[]) methodColl.toArray(new IBeanMethodSymbol[methodColl.size()]);
+            typeInfoCache.cacheMethodSymbols(type, methods);
+        } else {
+            methodColl = new ArrayList(methods.length);
+            Collections.addAll(methodColl, (Object[])methods);
+        }
+        BasicEList list = new BasicEList(methodColl);
+        return list;
+    }
 
     
     /**
@@ -328,11 +334,12 @@
             eNotify(new ENotificationImpl(this, Notification.SET, SymbolPackage.IJAVA_TYPE_DESCRIPTOR2__ARRAY_COUNT, oldArrayCount, arrayCount));
     }
 
-	/**
+    /**
      * <!-- begin-user-doc -->
      * <!-- end-user-doc -->
      * @generated
      */
+    @Override
     public IJavaElement getJdtContext() {
         return jdtContext;
     }
@@ -342,6 +349,7 @@
      * <!-- end-user-doc -->
      * @generated
      */
+    @Override
     public void setJdtContext(IJavaElement newJdtContext) {
         IJavaElement oldJdtContext = jdtContext;
         jdtContext = newJdtContext;
@@ -360,6 +368,7 @@
      * <!-- end-user-doc -->
      * @generated NOT
      */
+    @Override
     public IType resolveType(String resolvedTypeSignature) 
     {
         IType resolvedType = null;
@@ -401,6 +410,7 @@
      * <!-- end-user-doc -->
      * @generated
      */
+    @Override
     public Object eGet(int featureID, boolean resolve, boolean coreType) {
         switch (featureID) {
             case SymbolPackage.IJAVA_TYPE_DESCRIPTOR2__TYPE:
@@ -420,6 +430,7 @@
      * <!-- end-user-doc -->
      * @generated
      */
+    @Override
     public void eSet(int featureID, Object newValue) {
         switch (featureID) {
             case SymbolPackage.IJAVA_TYPE_DESCRIPTOR2__TYPE:
@@ -445,6 +456,7 @@
      * <!-- end-user-doc -->
      * @generated
      */
+    @Override
     public void eUnset(int featureID) {
         switch (featureID) {
             case SymbolPackage.IJAVA_TYPE_DESCRIPTOR2__TYPE:
@@ -468,6 +480,7 @@
      * <!-- end-user-doc -->
      * @generated
      */
+    @Override
     public boolean eIsSet(int featureID) {
         switch (featureID) {
             case SymbolPackage.IJAVA_TYPE_DESCRIPTOR2__TYPE:
@@ -482,71 +495,74 @@
         return super.eIsSet(featureID);
     }
 
+    @Override
     public boolean isArray() 
     {
-	    return getArrayCount() > 0;
+        return getArrayCount() > 0;
     }
 
     /**
-	 * @generated NOT
-	 */
-	public IObjectSymbol getArrayElement() 
-	{
-		if (isArray())
-		{
-			final String typeSignature = getTypeSignature();
-			final int arrayCount_ = Signature.getArrayCount(typeSignature);
-			final String baseType = Signature.getElementType(typeSignature);
-			final String elementTypeSignature = Signature.createArraySignature(baseType, arrayCount_-1);
+     * @generated NOT
+     */
+    @Override
+    public IObjectSymbol getArrayElement() 
+    {
+        if (isArray())
+        {
+            final String typeSignature = getTypeSignature();
+            final int arrayCount_ = Signature.getArrayCount(typeSignature);
+            final String baseType = Signature.getElementType(typeSignature);
+            final String elementTypeSignature = Signature.createArraySignature(baseType, arrayCount_-1);
 
-			final IJavaTypeDescriptor2 elementTypeDesc = 
-				SymbolFactory.eINSTANCE.createIJavaTypeDescriptor2();
-			final String fullyQualifiedElementType = TypeUtil.getFullyQualifiedName(baseType);
-			
-			IType elementType = null;
+            final IJavaTypeDescriptor2 elementTypeDesc = 
+                SymbolFactory.eINSTANCE.createIJavaTypeDescriptor2();
+            final String fullyQualifiedElementType = TypeUtil.getFullyQualifiedName(baseType);
+            
+            IType elementType = null;
 
-			try 
-			{
-			    IType myType = getType();
-				if (myType != null)
-				{
-					elementType = getType().getJavaProject()
-					                 .findType(fullyQualifiedElementType);
-				}
-			} 
-			catch (JavaModelException e) 
-			{
-				// suppress
-			}
+            try 
+            {
+                IType myType = getType();
+                if (myType != null)
+                {
+                    elementType = getType().getJavaProject()
+                                     .findType(fullyQualifiedElementType);
+                }
+            } 
+            catch (JavaModelException e) 
+            {
+                // suppress
+            }
 
-			if (elementType != null)
-			{
-				elementTypeDesc.setType(elementType);
-			}
-			else
-			{
-				elementTypeDesc.setTypeSignatureDelegate(elementTypeSignature);
-			}
+            if (elementType != null)
+            {
+                elementTypeDesc.setType(elementType);
+            }
+            else
+            {
+                elementTypeDesc.setTypeSignatureDelegate(elementTypeSignature);
+            }
             
             elementTypeDesc.setArrayCount(Signature.getArrayCount(elementTypeSignature));
-			
-			IPropertySymbol newPropertySymbol = 
-				SymbolFactory.eINSTANCE.createIPropertySymbol();
-			newPropertySymbol.setTypeDescriptor(elementTypeDesc);
-			newPropertySymbol.setWritable(true);
-			newPropertySymbol.setReadable(true);
-			newPropertySymbol.setName(fullyQualifiedElementType);
+            
+            IPropertySymbol newPropertySymbol = 
+                SymbolFactory.eINSTANCE.createIPropertySymbol();
+            newPropertySymbol.setTypeDescriptor(elementTypeDesc);
+            newPropertySymbol.setWritable(true);
+            newPropertySymbol.setReadable(true);
+            newPropertySymbol.setName(fullyQualifiedElementType);
             return newPropertySymbol;
-		}
+        }
 
-		return null;
-	}
+        return null;
+    }
 
-	/* (non-Javadoc)
-	 * @see org.eclipse.jst.jsf.context.symbol.internal.impl.ITypeDescriptorImpl#getTypeSignature()
+    /* (non-Javadoc)
+     * @see org.eclipse.jst.jsf.context.symbol.internal.impl.ITypeDescriptorImpl#getTypeSignature()
      * @generated NOT
-	 */
-	public String getTypeSignature() 
+     */
+    @Override
+    public String getTypeSignature() 
     {
         if (getType() == null)
         {
@@ -564,35 +580,34 @@
     }
 
     private Collection getPropertiesInternal()
-	{
+    {
         // if I'm an array then I have no bean properties
         if (isArray())
         {
             return Collections.EMPTY_LIST;
         }
         
-        final JDTBeanIntrospector  introspector = 
-            new JDTBeanIntrospector(getType());
+        final JDTBeanIntrospector  introspector = JDTBeanIntrospector.forType(type); 
         
-		final Map<String, JDTBeanProperty> properties = introspector.getProperties();
-		
-		final Collection calculatedProps = new ArrayList(properties.size());
+        final Map<String, JDTBeanProperty> properties = introspector.getProperties();
         
-		for (final Iterator<Map.Entry<String, JDTBeanProperty>> it = properties.entrySet().iterator(); it.hasNext();)
-		{
-		    Map.Entry<String, JDTBeanProperty> entry = it.next();
-		    final String propertyName = entry.getKey();
+        final Collection calculatedProps = new ArrayList(properties.size());
+        
+        for (final Iterator<Map.Entry<String, JDTBeanProperty>> it = properties.entrySet().iterator(); it.hasNext();)
+        {
+            Map.Entry<String, JDTBeanProperty> entry = it.next();
+            final String propertyName = entry.getKey();
             final JDTBeanProperty property = entry.getValue();
 
-			final IBeanPropertySymbol workingCopy =
-			    SymbolFactory.eINSTANCE.createIBeanPropertySymbol();
-			workingCopy.setName(propertyName);
-			workingCopy.setOwner(this);
+            final IBeanPropertySymbol workingCopy =
+                SymbolFactory.eINSTANCE.createIBeanPropertySymbol();
+            workingCopy.setName(propertyName);
+            workingCopy.setOwner(this);
                         
             final IJavaTypeDescriptor2 workingCopyDesc = 
                 SymbolFactory.eINSTANCE.createIJavaTypeDescriptor2();
             workingCopy.setTypeDescriptor(workingCopyDesc);
-			workingCopy.setReadable(property.isReadable());
+            workingCopy.setReadable(property.isReadable());
             workingCopy.setWritable(property.isWritable());
                             
             workingCopyDesc.setArrayCount(property.getArrayCount());
@@ -612,61 +627,58 @@
             }
             
             calculatedProps.add(workingCopy);
-		}
+        }
 
-		return calculatedProps;
-	}
+        return calculatedProps;
+    }
 
     private Collection getMethodsInternal()
-	{
-        JDTBeanIntrospector introspector =
-            new JDTBeanIntrospector(getType());
+    {
+        JDTBeanIntrospector introspector = JDTBeanIntrospector.forType(type);
         
-		IMethod[] methods = introspector.getAllMethods();
+        JDTBeanMethod[] methods = introspector.getMethods();
 
         List methodSymbols = new ArrayList();
 
-		for (int i = 0; i < methods.length; i++)
-		{
-			IMethod method = methods[i];
-			
-			try
-			{
-				// to be a bean method, it must not a constructor, must be public
-				// and must not be static
-				if (!method.isConstructor()
-						&& Flags.isPublic(method.getFlags())
-						&& !Flags.isStatic(method.getFlags()))
-				{
-					String methodName = method.getElementName();
-					IBeanMethodSymbol workingCopy = SymbolFactory.eINSTANCE.createIBeanMethodSymbol();
-					workingCopy.setName(methodName);
-					workingCopy.setOwner(this);
-                    workingCopy.setSignature(TypeUtil.
-                                                resolveMethodSignature
-                                                    (getType(), 
-                                                     method.getSignature()));
-					methodSymbols.add(workingCopy);
-				}
-			}
-			catch (JavaModelException jme)
-			{
-				// error reading meta-data.  Skip to next one
+        for (int i = 0; i < methods.length; i++)
+        {
+            JDTBeanMethod method = methods[i];
+            
+            try
+            {
+                // to be a bean method, it must not a constructor, must be public
+                // and must not be static
+                if (!method.isConstructor()
+                        && Flags.isPublic(method.getFlags())
+                        && !Flags.isStatic(method.getFlags()))
+                {
+                    String methodName = method.getElementName();
+                    IBeanMethodSymbol workingCopy = SymbolFactory.eINSTANCE.createIBeanMethodSymbol();
+                    workingCopy.setName(methodName);
+                    workingCopy.setOwner(this);
+                    workingCopy.setSignature(method.getResolvedSignatureErased());
+                    methodSymbols.add(workingCopy);
+                }
+            }
+            catch (JavaModelException jme)
+            {
+                // error reading meta-data.  Skip to next one
                 JSFCommonPlugin.log(jme);
-			}
-		}
-		
-		return methodSymbols;
-	}
+            }
+        }
+        
+        return methodSymbols;
+    }
 
 
     /**
      * <!-- begin-user-doc -->
      * @return the default string rep 
-	 * <!-- end-user-doc -->
+     * <!-- end-user-doc -->
      * @generated
      */
-	public String toString() {
+    @Override
+    public String toString() {
         if (eIsProxy()) return super.toString();
 
         StringBuffer result = new StringBuffer(super.toString());
diff --git a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/context/symbol/internal/impl/JavaUtil.java b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/context/symbol/internal/impl/JavaUtil.java
index e7549c1..822438d 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/context/symbol/internal/impl/JavaUtil.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/context/symbol/internal/impl/JavaUtil.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2001, 2007 Oracle Corporation and others.
+ * Copyright (c) 2001, 2021 Oracle Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
  * which accompanies this distribution, and is available at
@@ -8,7 +8,8 @@
  * SPDX-License-Identifier: EPL-2.0
  * 
  * Contributors:
- *     Oracle Corporation - initial API and implementation
+ *     Oracle Corporation  - initial API and implementation
+ *     Reto Weiss/Axon Ivy - Cache JDTBeanIntrospector
  *******************************************************************************/
 package org.eclipse.jst.jsf.context.symbol.internal.impl;
 
@@ -22,8 +23,8 @@
 import org.eclipse.jdt.ui.JavadocContentAccess;
 import org.eclipse.jst.jsf.common.JSFCommonPlugin;
 import org.eclipse.jst.jsf.common.util.JDTBeanIntrospector;
+import org.eclipse.jst.jsf.common.util.JDTBeanMethod;
 import org.eclipse.jst.jsf.common.util.JDTBeanProperty;
-import org.eclipse.jst.jsf.common.util.TypeUtil;
 import org.eclipse.jst.jsf.context.symbol.IBeanMethodSymbol;
 import org.eclipse.jst.jsf.context.symbol.IBeanPropertySymbol;
 
@@ -76,19 +77,13 @@
      */
     public static IMethod findCorrespondingMethod(final IBeanMethodSymbol symbol) {
         final IType type = symbol.getOwner().getType();
-        final JDTBeanIntrospector introspector = new JDTBeanIntrospector(type);
-        final IMethod[] methods = introspector.getAllMethods();
-        for (final IMethod method : methods) {
+        final JDTBeanIntrospector introspector = JDTBeanIntrospector.forType(type);
+        final JDTBeanMethod[] methods = introspector.getMethods();
+        for (final JDTBeanMethod method : methods) {
             if (method.getElementName().equals(symbol.getName())) {
-                try {
-                    final String currentMethodsSignature = TypeUtil.resolveMethodSignature(method.getDeclaringType(),
-                            method.getSignature());
-                    if (currentMethodsSignature.equals(symbol.getSignature())) {
-                        return method;
-                    }
-                } catch (final JavaModelException e) {
-                    JSFCommonPlugin.log(IStatus.WARNING, "error determining for method '" //$NON-NLS-1$
-                            + method.getElementName() + "'.", e); //$NON-NLS-1$
+                final String currentMethodsSignature = method.getResolvedSignatureErased();
+                if (currentMethodsSignature.equals(symbol.getSignature())) {
+                    return method.getMethod();
                 }
             }
         }
@@ -100,7 +95,7 @@
      * @return a JDTBeanProperty for the given property symbol
      */
     public static JDTBeanProperty findCorrespondingJDTProperty(final IBeanPropertySymbol propertySymbol) {
-        final JDTBeanIntrospector introspector = new JDTBeanIntrospector(propertySymbol.getOwner().getType());
+        final JDTBeanIntrospector introspector = JDTBeanIntrospector.forType(propertySymbol.getOwner().getType());
         return introspector.getProperties().get(propertySymbol.getName());
     }
 }
diff --git a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/context/symbol/internal/impl/Util.java b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/context/symbol/internal/impl/Util.java
index da955c8..7f57e7f 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/context/symbol/internal/impl/Util.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.common/src/org/eclipse/jst/jsf/context/symbol/internal/impl/Util.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007 Oracle Corporation.
+ * Copyright (c) 2007, 2021 Oracle Corporation.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
  * which accompanies this distribution, and is available at
@@ -9,6 +9,7 @@
  *
  * Contributors:
  *    Cameron Bateman/Oracle - initial API and implementation
+ *    Reto Weiss/Axon Ivy    - Cache JDTBeanIntrospector
  *    
  ********************************************************************************/
 package org.eclipse.jst.jsf.context.symbol.internal.impl;
@@ -17,7 +18,6 @@
 import java.util.List;
 
 import org.eclipse.emf.common.util.EList;
-import org.eclipse.jdt.core.IMethod;
 import org.eclipse.jdt.core.IType;
 import org.eclipse.jdt.core.JavaModelException;
 import org.eclipse.jdt.core.Signature;
@@ -25,6 +25,7 @@
 import org.eclipse.jst.jsf.common.internal.types.TypeConstants;
 import org.eclipse.jst.jsf.common.internal.types.ValueType;
 import org.eclipse.jst.jsf.common.util.JDTBeanIntrospector;
+import org.eclipse.jst.jsf.common.util.JDTBeanMethod;
 import org.eclipse.jst.jsf.common.util.TypeUtil;
 import org.eclipse.jst.jsf.context.symbol.IJavaTypeDescriptor2;
 import org.eclipse.jst.jsf.context.symbol.IPropertySymbol;
@@ -46,96 +47,90 @@
         // the call then do this the hard way...
         if (type != null && result == null)
         {
-            final JDTBeanIntrospector introspector = 
-                new JDTBeanIntrospector(type);
+            final JDTBeanIntrospector introspector = JDTBeanIntrospector.forType(type);
 
-            final IMethod callMethod = 
-                Util.matchMethod(methodName, methodArguments, introspector.getAllMethods(),typeDesc.getTypeParameterSignatures());
+            final JDTBeanMethod callMethod = 
+                Util.matchMethod(methodName, methodArguments, introspector.getMethods(),typeDesc.getTypeParameterSignatures());
 
             if (callMethod != null)
             {
-                try 
+                try
                 {
-                    // resolve the method's return type; don't erase parameters
-                    String retTypeSignature = callMethod.getReturnType();
-                        
-                    // if we have a type variable, try to parameter match it
-                    if (Signature.getTypeSignatureKind(retTypeSignature) == Signature.TYPE_VARIABLE_SIGNATURE)
-                    {
-                        retTypeSignature = TypeUtil.matchTypeParameterToArgument
-                            (type
-                               , retTypeSignature, typeDesc.getTypeParameterSignatures());
-                        
-                        if (retTypeSignature == null)
-                        {
-                            retTypeSignature = TypeConstants.TYPE_JAVAOBJECT;
-                        }
-                    }
-                    // otherwise, try and resolve it in type
-                    else
-                    {
-                    	retTypeSignature = TypeUtil.resolveTypeSignature
-                    		(type, callMethod.getReturnType(), false);
-                    }
-
-                    final IPropertySymbol  propSymbol = 
-                        SymbolFactory.eINSTANCE.createIPropertySymbol();
-
-                    // TODO: there is a possible problem here for non-string keyed maps
-                    propSymbol.setName(symbolName);
-                    propSymbol.setReadable(true);
-                    
-                    {
-                        IJavaTypeDescriptor2 newTypeDesc = null;
-                        
-                        if (retTypeSignature.equals(TypeConstants.TYPE_JAVAOBJECT))
-                        {
-                            newTypeDesc = SymbolFactory.eINSTANCE.createIBoundedJavaTypeDescriptor();
-                        }
-                        else
-                        {
-                            newTypeDesc = SymbolFactory.eINSTANCE.createIJavaTypeDescriptor2();
-                        }
-                        
-                        newTypeDesc.setArrayCount(Signature.getArrayCount(retTypeSignature));
-                        
-                        // may be null
-                        newTypeDesc.setType(typeDesc.resolveType(retTypeSignature));
-                        newTypeDesc.setTypeSignatureDelegate(retTypeSignature);
-                        propSymbol.setTypeDescriptor(newTypeDesc);
-                    }
-                    
-                    result = propSymbol;
-                } 
-                catch (JavaModelException e) 
-                {
-                    JSFCommonPlugin.log(e);
-                    // fall-through and return null result
-                }
+                  // resolve the method's return type; don't erase parameters
+                  String retTypeSignature = callMethod.getUnresolvedReturnTypeUnerased();
+                      
+                  // if we have a type variable, try to parameter match it
+                  if (Signature.getTypeSignatureKind(retTypeSignature) == Signature.TYPE_VARIABLE_SIGNATURE)
+                  {
+                      retTypeSignature = TypeUtil.matchTypeParameterToArgument
+                          (type
+                             , retTypeSignature, typeDesc.getTypeParameterSignatures());
+                      
+                      if (retTypeSignature == null)
+                      {
+                          retTypeSignature = TypeConstants.TYPE_JAVAOBJECT;
+                      }
+                  }
+                  else
+                  {
+                    retTypeSignature = callMethod.getResolvedReturnTypeUnerased();
+                  }
+  
+                  final IPropertySymbol  propSymbol = 
+                      SymbolFactory.eINSTANCE.createIPropertySymbol();
+  
+                  // TODO: there is a possible problem here for non-string keyed maps
+                  propSymbol.setName(symbolName);
+                  propSymbol.setReadable(true);
+                  
+                  {
+                      IJavaTypeDescriptor2 newTypeDesc = null;
+                      
+                      if (retTypeSignature.equals(TypeConstants.TYPE_JAVAOBJECT))
+                      {
+                          newTypeDesc = SymbolFactory.eINSTANCE.createIBoundedJavaTypeDescriptor();
+                      }
+                      else
+                      {
+                          newTypeDesc = SymbolFactory.eINSTANCE.createIJavaTypeDescriptor2();
+                      }
+                      
+                      newTypeDesc.setArrayCount(Signature.getArrayCount(retTypeSignature));
+                      
+                      // may be null
+                      newTypeDesc.setType(typeDesc.resolveType(retTypeSignature));
+                      newTypeDesc.setTypeSignatureDelegate(retTypeSignature);
+                      propSymbol.setTypeDescriptor(newTypeDesc);
+                  }
+                  
+                  result = propSymbol;
+              } 
+              catch (JavaModelException e) 
+              {
+                  JSFCommonPlugin.log(e);
+                  // fall-through and return null result
+              }
             }
         }
 
         return result;
     }
 
-    static IMethod matchMethod(String methodName, List methodArguments, IMethod[] allMethods, List typeParameterSignatures)
+    static JDTBeanMethod matchMethod(String methodName, List methodArguments, JDTBeanMethod[] allMethods, List typeParameterSignatures)
     {
-//        final List argSigs = convertArgsToSignatures(methodArguments);
-        IMethod matchedMethod = null;
-
         for (int i = 0; i < allMethods.length; i++)
         {
-            final IMethod method = allMethods[i];
+            final JDTBeanMethod method = allMethods[i];
             
             // check for names and argument count match
-            if (method.getParameterTypes().length == methodArguments.size()
+            if (method.getResolvedParameterTypesUnerased().length == methodArguments.size()
                     && method.getElementName().equals(methodName))
             {
-                List<String> methods = resolveMethodParameters(method, typeParameterSignatures);
+                List<String> params = resolveMethodParameters(method, typeParameterSignatures);
                 
                 // need to verify argument matches
                 boolean isMatched = true;
-                CHECK_ARGUMENTS: for (int j = 0; j < methods.size(); j++)
+                CHECK_ARGUMENTS: for (int j = 0; j < params.size(); j++)
                 {
                     final ValueType valueType = (ValueType) methodArguments.get(j);
 
@@ -145,8 +140,8 @@
                     // - method name overloading
                     // - autoboxing primitives
                     // - certain kinds of parameterized args
-                    if (!methods.get(j).equals(valueType.getSignature())
-                         && !(methods.get(j).equals(TypeConstants.TYPE_JAVAOBJECT)
+                    if (!params.get(j).equals(valueType.getSignature())
+                         && !(params.get(j).equals(TypeConstants.TYPE_JAVAOBJECT)
                                  && Signature.getTypeSignatureKind(valueType.getSignature())==Signature.CLASS_TYPE_SIGNATURE))
                     {
                         // not a match
@@ -162,18 +157,15 @@
             }
         }
 
-        return matchedMethod;
+        return null;
     }
     
-    static List<String> resolveMethodParameters(IMethod method, List typeParametersSignatures)
+    static List<String> resolveMethodParameters(JDTBeanMethod method, List typeParametersSignatures)
     {
         List<String>   resolved = new ArrayList<String>();
-        String[] parameterTypes = method.getParameterTypes();
+        String[] parameterTypes = method.getResolvedParameterTypesUnerased();
         for (String parameter : parameterTypes)
         { 
-            parameter = TypeUtil.resolveTypeSignature(method.getDeclaringType()
-                    , parameter, false);
-            
             if (Signature.getTypeSignatureKind(parameter) == Signature.TYPE_VARIABLE_SIGNATURE)
             {
                 parameter = TypeUtil.matchTypeParameterToArgument
diff --git a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/jsfappconfig/AnnotationSearchRequestor.java b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/jsfappconfig/AnnotationSearchRequestor.java
index 2ce5af6..7d2ed42 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/jsfappconfig/AnnotationSearchRequestor.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/jsfappconfig/AnnotationSearchRequestor.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2011, 2012 Oracle Corporation.
+ * Copyright (c) 2011, 2021 Oracle Corporation.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
  * which accompanies this distribution, and is available at
@@ -8,31 +8,33 @@
  * SPDX-License-Identifier: EPL-2.0
  *
  * Contributors:
- *    Andrew McCulloch - initial API and implementation
- *    Ian Trimble - maintenance
+ *        Andrew McCulloch        - initial API and implementation
+ *        Ian Trimble                 - maintenance
+ *        Reto Weiss/Axon Ivy - Use cached JDTBeanIntrospector to resolve annotation types
  *******************************************************************************/ 
 package org.eclipse.jst.jsf.core.jsfappconfig;
 
-import static org.eclipse.jst.jsf.core.jsfappconfig.AnnotationJSFAppConfigProvider.MANAGED_BEAN_ANNOTATION_CLASS;
-import static org.eclipse.jst.jsf.core.jsfappconfig.AnnotationJSFAppConfigProvider.REFERENCED_BEAN_ANNOTATION_CLASS;
+import static org.eclipse.jst.jsf.core.jsfappconfig.AnnotationJSFAppConfigProvider.APPLICATION_SCOPED_ANNOTATION_CLASS;
+import static org.eclipse.jst.jsf.core.jsfappconfig.AnnotationJSFAppConfigProvider.CDI_APPLICATION_SCOPED_ANNOTATION_CLASS;
+import static org.eclipse.jst.jsf.core.jsfappconfig.AnnotationJSFAppConfigProvider.CDI_CONVERSATION_SCOPED_ANNOTATION_CLASS;
+import static org.eclipse.jst.jsf.core.jsfappconfig.AnnotationJSFAppConfigProvider.CDI_MODEL_BEAN_ANNOTATION_CLASS;
+import static org.eclipse.jst.jsf.core.jsfappconfig.AnnotationJSFAppConfigProvider.CDI_NAMED_BEAN_ANNOTATION_CLASS;
+import static org.eclipse.jst.jsf.core.jsfappconfig.AnnotationJSFAppConfigProvider.CDI_REQUEST_SCOPED_ANNOTATION_CLASS;
+import static org.eclipse.jst.jsf.core.jsfappconfig.AnnotationJSFAppConfigProvider.CDI_SESSION_SCOPED_ANNOTATION_CLASS;
+import static org.eclipse.jst.jsf.core.jsfappconfig.AnnotationJSFAppConfigProvider.CUSTOM_SCOPED_ANNOTATION_CLASS;
 import static org.eclipse.jst.jsf.core.jsfappconfig.AnnotationJSFAppConfigProvider.FACES_COMPONENT_ANNOTATION_CLASS;
 import static org.eclipse.jst.jsf.core.jsfappconfig.AnnotationJSFAppConfigProvider.FACES_CONVERTER_ANNOTATION_CLASS;
 import static org.eclipse.jst.jsf.core.jsfappconfig.AnnotationJSFAppConfigProvider.FACES_RENDERER_ANNOTATION_CLASS;
 import static org.eclipse.jst.jsf.core.jsfappconfig.AnnotationJSFAppConfigProvider.FACES_VALIDATOR_ANNOTATION_CLASS;
-
+import static org.eclipse.jst.jsf.core.jsfappconfig.AnnotationJSFAppConfigProvider.MANAGED_BEAN_ANNOTATION_CLASS;
 import static org.eclipse.jst.jsf.core.jsfappconfig.AnnotationJSFAppConfigProvider.NONE_SCOPED_ANNOTATION_CLASS;
-import static org.eclipse.jst.jsf.core.jsfappconfig.AnnotationJSFAppConfigProvider.VIEW_SCOPED_ANNOTATION_CLASS;
+import static org.eclipse.jst.jsf.core.jsfappconfig.AnnotationJSFAppConfigProvider.REFERENCED_BEAN_ANNOTATION_CLASS;
 import static org.eclipse.jst.jsf.core.jsfappconfig.AnnotationJSFAppConfigProvider.SESSION_SCOPED_ANNOTATION_CLASS;
-import static org.eclipse.jst.jsf.core.jsfappconfig.AnnotationJSFAppConfigProvider.APPLICATION_SCOPED_ANNOTATION_CLASS;
-import static org.eclipse.jst.jsf.core.jsfappconfig.AnnotationJSFAppConfigProvider.CUSTOM_SCOPED_ANNOTATION_CLASS;
+import static org.eclipse.jst.jsf.core.jsfappconfig.AnnotationJSFAppConfigProvider.VIEW_SCOPED_ANNOTATION_CLASS;
 
-import static org.eclipse.jst.jsf.core.jsfappconfig.AnnotationJSFAppConfigProvider.CDI_NAMED_BEAN_ANNOTATION_CLASS;
-import static org.eclipse.jst.jsf.core.jsfappconfig.AnnotationJSFAppConfigProvider.CDI_MODEL_BEAN_ANNOTATION_CLASS;
-
-import static org.eclipse.jst.jsf.core.jsfappconfig.AnnotationJSFAppConfigProvider.CDI_REQUEST_SCOPED_ANNOTATION_CLASS;
-import static org.eclipse.jst.jsf.core.jsfappconfig.AnnotationJSFAppConfigProvider.CDI_CONVERSATION_SCOPED_ANNOTATION_CLASS;
-import static org.eclipse.jst.jsf.core.jsfappconfig.AnnotationJSFAppConfigProvider.CDI_SESSION_SCOPED_ANNOTATION_CLASS;
-import static org.eclipse.jst.jsf.core.jsfappconfig.AnnotationJSFAppConfigProvider.CDI_APPLICATION_SCOPED_ANNOTATION_CLASS;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
 
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.emf.common.util.EList;
@@ -40,8 +42,10 @@
 import org.eclipse.jdt.core.IMemberValuePair;
 import org.eclipse.jdt.core.IType;
 import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.Signature;
 import org.eclipse.jdt.core.search.SearchMatch;
 import org.eclipse.jdt.core.search.SearchRequestor;
+import org.eclipse.jst.jsf.common.util.JDTBeanIntrospector;
 import org.eclipse.jst.jsf.facesconfig.emf.ComponentClassType;
 import org.eclipse.jst.jsf.facesconfig.emf.ComponentFamilyType;
 import org.eclipse.jst.jsf.facesconfig.emf.ComponentType;
@@ -71,76 +75,163 @@
 /**
  * SearchRequestor that looks at annotations for JSF configuration.
  * 
- * <p><b>Provisional API - subject to change</b></p>
+ * <p>
+ * <b>Provisional API - subject to change</b>
+ * </p>
  * 
  * @author Andrew McCulloch - Oracle
  */
-public class AnnotationSearchRequestor extends SearchRequestor {
-    
+public class AnnotationSearchRequestor extends SearchRequestor
+{
     private final FacesConfigType facesConfig;
     
-    AnnotationSearchRequestor(final FacesConfigType facesConfig) {
-         this.facesConfig = facesConfig;
-    }
+    private static final Set<String> ANNOATION_CANDIDATES = ofAnnotation(
+            MANAGED_BEAN_ANNOTATION_CLASS, 
+            REFERENCED_BEAN_ANNOTATION_CLASS, 
+            FACES_COMPONENT_ANNOTATION_CLASS, 
+            FACES_CONVERTER_ANNOTATION_CLASS, 
+            FACES_RENDERER_ANNOTATION_CLASS,
+            FACES_VALIDATOR_ANNOTATION_CLASS, 
+            CDI_NAMED_BEAN_ANNOTATION_CLASS, 
+            CDI_MODEL_BEAN_ANNOTATION_CLASS);
     
-    public void acceptSearchMatch(SearchMatch match) throws CoreException {
-        if (match.getAccuracy() == SearchMatch.A_ACCURATE) {
-            Object element = match.getElement();
-            if (element instanceof IType) {
-                IType type = (IType) element;
-                IAnnotation[] annotations = type.getAnnotations();
-                if (annotations != null) {
-                    for (int i = 0, k = annotations.length; i < k; i++) {
-                        if (annotations[i].exists()) {
-                            String annotationType = annotations[i].getElementName();
-                            String[][] resolvedAnnotationTypes = type.resolveType(annotationType);
-                            if (resolvedAnnotationTypes != null) {
-                                String resolvedAnnotationClassName = new StringBuffer(resolvedAnnotationTypes[0][0]).append('.').append(resolvedAnnotationTypes[0][1]).toString();
-                                if (MANAGED_BEAN_ANNOTATION_CLASS.equals(resolvedAnnotationClassName)) {
-                                    addManagedBean(annotations[i], type);
-                                } else if (REFERENCED_BEAN_ANNOTATION_CLASS.equals(resolvedAnnotationClassName)) {
-                                    addReferencedBean(annotations[i], type);
-                                } else if (FACES_COMPONENT_ANNOTATION_CLASS.equals(resolvedAnnotationClassName)) {
-                                    addComponent(annotations[i], type);
-                                } else if (FACES_CONVERTER_ANNOTATION_CLASS.equals(resolvedAnnotationClassName)) {
-                                    addConverter(annotations[i], type);
-                                } else if (FACES_RENDERER_ANNOTATION_CLASS.equals(resolvedAnnotationClassName)) {
-                                    addRenderer(annotations[i], type);
-                                } else if (FACES_VALIDATOR_ANNOTATION_CLASS.equals(resolvedAnnotationClassName)) {
-                                    addValidator(annotations[i], type);
-                                } else if (CDI_NAMED_BEAN_ANNOTATION_CLASS.equals(resolvedAnnotationClassName)) {
-                                	addCDINamedBean(annotations[i], type);
-                                } else if (CDI_MODEL_BEAN_ANNOTATION_CLASS.equals(resolvedAnnotationClassName)) {
-                                	addCDIModelBean(type);
-                                }
-                            }
-                        }
-                    }
-                }
+    private static final Set<String> SCOPED_ANNOTATION_CANDIDATES = ofAnnotation(
+            NONE_SCOPED_ANNOTATION_CLASS,
+            VIEW_SCOPED_ANNOTATION_CLASS,
+            SESSION_SCOPED_ANNOTATION_CLASS,
+            APPLICATION_SCOPED_ANNOTATION_CLASS,
+            CUSTOM_SCOPED_ANNOTATION_CLASS);
+
+    private static final Set<String> CDI_SCOPED_ANNOTATION_CANDIDATES = ofAnnotation(
+            CDI_REQUEST_SCOPED_ANNOTATION_CLASS,
+            CDI_CONVERSATION_SCOPED_ANNOTATION_CLASS,
+            CDI_SESSION_SCOPED_ANNOTATION_CLASS,
+            CDI_APPLICATION_SCOPED_ANNOTATION_CLASS);
+ 
+    private static Set<String> ofAnnotation(String... annotationClasses)
+    {
+        Set<String> candidates = new HashSet<String>();
+        for (String annotationClass : annotationClasses)
+        {
+            candidates.add(annotationClass);
+            candidates.add(Signature.getSimpleName(annotationClass));
+        }
+        return Collections.unmodifiableSet(candidates);
+    }
+
+    AnnotationSearchRequestor(final FacesConfigType facesConfig)
+    {
+        this.facesConfig = facesConfig;
+    }
+
+    @Override
+    public void acceptSearchMatch(SearchMatch match) throws CoreException
+    {
+        if (match.getAccuracy() != SearchMatch.A_ACCURATE)
+        {
+            return;
+        }
+
+        Object element = match.getElement();
+        if (!(element instanceof IType))
+        {
+            return;
+        }
+        IType type = (IType) element;
+        JDTBeanIntrospector beanIntrospector = JDTBeanIntrospector.forType(type);
+        IAnnotation[] annotations = type.getAnnotations();
+        if (annotations == null)
+        {
+            return;
+        }
+        for (IAnnotation annotation : annotations)
+        {
+            processAnnotation(type, beanIntrospector, annotation);
+        }
+    }
+
+    private void processAnnotation(IType type, JDTBeanIntrospector beanIntrospector, IAnnotation annotation) throws CoreException
+    {
+        if (!annotation.exists())
+        {
+            return;
+        }
+        String annotationType = annotation.getElementName();
+        if (! isAnnotationCandidate(annotationType))
+        {
+            return;
+        }
+        String annotationClassName = beanIntrospector.resolveFullQualifiedTypeName(annotationType);
+        if (annotationClassName != null)
+        {
+            if (MANAGED_BEAN_ANNOTATION_CLASS.equals(annotationClassName))
+            {
+                addManagedBean(annotation, type, beanIntrospector);
+            }
+            else if (REFERENCED_BEAN_ANNOTATION_CLASS.equals(annotationClassName))
+            {
+                addReferencedBean(annotation, type);
+            }
+            else if (FACES_COMPONENT_ANNOTATION_CLASS.equals(annotationClassName))
+            {
+                addComponent(annotation, type);
+            }
+            else if (FACES_CONVERTER_ANNOTATION_CLASS.equals(annotationClassName))
+            {
+                addConverter(annotation, type);
+            }
+            else if (FACES_RENDERER_ANNOTATION_CLASS.equals(annotationClassName))
+            {
+                addRenderer(annotation, type);
+            }
+            else if (FACES_VALIDATOR_ANNOTATION_CLASS.equals(annotationClassName))
+            {
+                addValidator(annotation, type);
+            }
+            else if (CDI_NAMED_BEAN_ANNOTATION_CLASS.equals(annotationClassName))
+            {
+                addCDINamedBean(annotation, beanIntrospector, type);
+            }
+            else if (CDI_MODEL_BEAN_ANNOTATION_CLASS.equals(annotationClassName))
+            {
+                addCDIModelBean(type);
             }
         }
     }
 
-    private void addReferencedBean(IAnnotation referencedBeanAnnotation, IType beanType) throws JavaModelException {
+    private boolean isAnnotationCandidate(String annotationType)
+    {
+        return ANNOATION_CANDIDATES.contains(annotationType);
+    }
+
+    private void addReferencedBean(IAnnotation referencedBeanAnnotation, IType beanType)
+                    throws JavaModelException
+    {
         IMemberValuePair[] pairs = referencedBeanAnnotation.getMemberValuePairs();
         String beanNameString = null;
-        if (pairs != null) {
-            for (IMemberValuePair pair : pairs) {
-                if ("name".equals(pair.getMemberName()) && pair.getValueKind() == IMemberValuePair.K_STRING) { //$NON-NLS-1$
-                    beanNameString = (String)pair.getValue();
+        if (pairs != null)
+        {
+            for (IMemberValuePair pair : pairs)
+            {
+                if ("name".equals(pair.getMemberName()) && pair.getValueKind() == IMemberValuePair.K_STRING) //$NON-NLS-1$
+                {
+                    beanNameString = (String) pair.getValue();
                 }
             }
         }
-        if (beanNameString == null) {
+        if (beanNameString == null)
+        {
             beanNameString = beanType.getElementName();
-            if (beanNameString != null && beanNameString.length() > 0) {
+            if (beanNameString != null && beanNameString.length() > 0)
+            {
                 StringBuffer casedName = new StringBuffer(String.valueOf(beanNameString.charAt(0)).toUpperCase());
                 beanNameString = casedName.append(beanNameString.substring(1)).toString();
             }
         }
         String beanClassName = beanType.getFullyQualifiedName();
 
-        if (beanNameString != null && beanClassName != null) {
+        if (beanNameString != null && beanClassName != null)
+        {
             ReferencedBeanType bean = FacesConfigFactory.eINSTANCE.createReferencedBeanType();
             ReferencedBeanNameType beanName = FacesConfigFactory.eINSTANCE.createReferencedBeanNameType();
             beanName.setTextContent(beanNameString);
@@ -152,57 +243,40 @@
         }
     }
 
-    private void addManagedBean(IAnnotation beanAnnotation, IType beanType) throws JavaModelException {
+    private void addManagedBean(IAnnotation beanAnnotation, IType beanType, JDTBeanIntrospector beanIntrospector) throws JavaModelException
+    {
         IMemberValuePair[] pairs = beanAnnotation.getMemberValuePairs();
         String beanNameString = null;
         Boolean isBeanEager = Boolean.FALSE;
-        if (pairs != null) {
-            for (IMemberValuePair pair : pairs) {
-                if ("name".equals(pair.getMemberName()) && pair.getValueKind() == IMemberValuePair.K_STRING) { //$NON-NLS-1$
-                    beanNameString = (String)pair.getValue();
-                } else if ("eager".equals(pair.getMemberName()) && pair.getValueKind() == IMemberValuePair.K_BOOLEAN) { //$NON-NLS-1$
-                    isBeanEager = (Boolean)pair.getValue();
+        if (pairs != null)
+        {
+            for (IMemberValuePair pair : pairs)
+            {
+                if ("name".equals(pair.getMemberName()) && pair.getValueKind() == IMemberValuePair.K_STRING) //$NON-NLS-1$
+                {
+                    beanNameString = (String) pair.getValue();
+                }
+                else if ("eager".equals(pair.getMemberName()) && pair.getValueKind() == IMemberValuePair.K_BOOLEAN) //$NON-NLS-1$
+                {
+                    isBeanEager = (Boolean) pair.getValue();
                 }
             }
         }
-        if (beanNameString == null || beanNameString.length() < 1) {
+        if (beanNameString == null || beanNameString.length() < 1)
+        {
             beanNameString = beanType.getElementName();
-            if (beanNameString != null && beanNameString.length() > 0) {
+            if (beanNameString != null && beanNameString.length() > 0)
+            {
                 StringBuffer casedName = new StringBuffer(String.valueOf(beanNameString.charAt(0)).toLowerCase());
                 beanNameString = casedName.append(beanNameString.substring(1)).toString();
             }
         }
         String beanClassName = beanType.getFullyQualifiedName();
 
-        String beanScopeString = "request"; //$NON-NLS-1$
-        IAnnotation[] annotations = beanType.getAnnotations();
-        if (annotations != null) {
-	        for (int i = 0, k = annotations.length; i < k; i++) {
-                if (annotations[i].exists()) {
-                    String annotationType = annotations[i].getElementName();
-                    String[][] resolvedAnnotationTypes = beanType.resolveType(annotationType);
-                    if (resolvedAnnotationTypes != null) {
-                        String resolvedAnnotationClassName = new StringBuffer(resolvedAnnotationTypes[0][0]).append('.').append(resolvedAnnotationTypes[0][1]).toString();
-                        if (NONE_SCOPED_ANNOTATION_CLASS.equals(resolvedAnnotationClassName)) {
-                        	beanScopeString = "none"; //$NON-NLS-1$
-                        } else if (VIEW_SCOPED_ANNOTATION_CLASS.equals(resolvedAnnotationClassName)) {
-                        	beanScopeString = "view"; //$NON-NLS-1$
-                        } else if (SESSION_SCOPED_ANNOTATION_CLASS.equals(resolvedAnnotationClassName)) {
-                        	beanScopeString = "session"; //$NON-NLS-1$
-                        } else if (APPLICATION_SCOPED_ANNOTATION_CLASS.equals(resolvedAnnotationClassName)) {
-                        	beanScopeString = "application"; //$NON-NLS-1$
-                        } else if (CUSTOM_SCOPED_ANNOTATION_CLASS.equals(resolvedAnnotationClassName)) {
-                            IMemberValuePair[] scopePairs = annotations[i].getMemberValuePairs();
-                            if (scopePairs != null && scopePairs.length == 1 && scopePairs[0].getValueKind() == IMemberValuePair.K_STRING) {
-                                beanScopeString = (String)scopePairs[0].getValue();
-                            }
-                        }
-                    }
-                }
-	        }
-        }
+        String beanScopeString = processScopeAnnotations(beanType, beanIntrospector);
 
-        if (beanNameString != null && beanClassName != null) {
+        if (beanNameString != null && beanClassName != null)
+        {
             ManagedBeanType bean = FacesConfigFactory.eINSTANCE.createManagedBeanType();
             ManagedBeanNameType beanName = FacesConfigFactory.eINSTANCE.createManagedBeanNameType();
             beanName.setTextContent(beanNameString);
@@ -219,88 +293,176 @@
         }
     }
 
-    private void addValidator(IAnnotation validatorAnnotation, IType validatorType) throws JavaModelException {
+    private String processScopeAnnotations(IType beanType, JDTBeanIntrospector beanIntrospector) throws JavaModelException
+    {
+        String beanScopeString = "request"; //$NON-NLS-1$
+        IAnnotation[] annotations = beanType.getAnnotations();
+        if (annotations == null)
+        {
+            return beanScopeString;
+        }
+        for (IAnnotation annotation : annotations)
+        {
+            String scope = processScopeAnnoation(beanType, beanIntrospector, annotation);
+            if (scope != null)
+            {
+                beanScopeString = scope;
+            }
+        }
+        return beanScopeString;
+    }
+
+    private String processScopeAnnoation(IType beanType, JDTBeanIntrospector beanIntrospector, IAnnotation annotation) throws JavaModelException
+    {
+        if (! annotation.exists())
+        {
+            return null;
+        }
+        String annotationType = annotation.getElementName();
+        if (! isScopeAnnotationCandidate(annotationType))
+        {
+            return null;
+        }
+        String annotationClassName = beanIntrospector.resolveFullQualifiedTypeName(annotationType);
+        if (annotationClassName == null)
+        {
+            return null;
+        }
+        if (NONE_SCOPED_ANNOTATION_CLASS.equals(annotationClassName))
+        {
+            return "none"; //$NON-NLS-1$
+        }
+        else if (VIEW_SCOPED_ANNOTATION_CLASS.equals(annotationClassName))
+        {
+            return "view"; //$NON-NLS-1$
+        }
+        else if (SESSION_SCOPED_ANNOTATION_CLASS.equals(annotationClassName))
+        {
+            return "session"; //$NON-NLS-1$
+        }
+        else if (APPLICATION_SCOPED_ANNOTATION_CLASS.equals(annotationClassName))
+        {
+            return "application"; //$NON-NLS-1$
+        }
+        else if (CUSTOM_SCOPED_ANNOTATION_CLASS.equals(annotationClassName))
+        {
+            IMemberValuePair[] scopePairs = annotation.getMemberValuePairs();
+            if (scopePairs != null && scopePairs.length == 1
+                            && scopePairs[0].getValueKind() == IMemberValuePair.K_STRING)
+            {
+                return (String) scopePairs[0].getValue();
+            }
+        }
+        return null;
+    }
+
+    private boolean isScopeAnnotationCandidate(String annotationType)
+    {
+        return SCOPED_ANNOTATION_CANDIDATES.contains(annotationType);
+    }
+
+    private void addValidator(IAnnotation validatorAnnotation, IType validatorType) throws JavaModelException
+    {
         String validatorClassName = validatorType.getFullyQualifiedName();
         IMemberValuePair[] pairs = validatorAnnotation.getMemberValuePairs();
         String validatorIDString = null;
-//        Boolean isDefaultBoolean = null;
-        if (pairs != null) {
-            for (IMemberValuePair pair : pairs) {
-                if ("value".equals(pair.getMemberName()) && pair.getValueKind() == IMemberValuePair.K_STRING) { //$NON-NLS-1$
-                    validatorIDString = (String)pair.getValue();
-                    //isDefault not used in emf model
-//                } else if ("isDefault".equals(pair.getMemberName()) && pair.getValueKind() == IMemberValuePair.K_BOOLEAN) {  //$NON-NLS-1$
-//                    isDefaultBoolean = (Boolean)pair.getValue();
+        // Boolean isDefaultBoolean = null;
+        if (pairs != null)
+        {
+            for (IMemberValuePair pair : pairs)
+            {
+                if ("value".equals(pair.getMemberName()) && pair.getValueKind() == IMemberValuePair.K_STRING) //$NON-NLS-1$
+                {
+                    validatorIDString = (String) pair.getValue();
+                    // isDefault not used in emf model
+                    // } else if ("isDefault".equals(pair.getMemberName()) &&
+                    // pair.getValueKind() == IMemberValuePair.K_BOOLEAN) { //$NON-NLS-1$
+                    // isDefaultBoolean = (Boolean)pair.getValue();
                 }
             }
         }
-        
-        if (validatorClassName != null && validatorIDString != null) {
+
+        if (validatorClassName != null && validatorIDString != null)
+        {
             ValidatorType validator = FacesConfigFactory.eINSTANCE.createValidatorType();
             ValidatorClassType validatorClass = FacesConfigFactory.eINSTANCE.createValidatorClassType();
             validatorClass.setTextContent(validatorClassName);
             validator.setValidatorClass(validatorClass);
-            
+
             ValidatorIdType validatorID = FacesConfigFactory.eINSTANCE.createValidatorIdType();
             validatorID.setTextContent(validatorIDString);
             validator.setValidatorId(validatorID);
-            
 
-//          if (isDefaultBoolean == null) {
-//              isDefaultBoolean = Boolean.FALSE;
-//          }
-            
+            // if (isDefaultBoolean == null) {
+            // isDefaultBoolean = Boolean.FALSE;
+            // }
+
             facesConfig.getValidator().add(validator);
         }
     }
 
-    private void addRenderer(IAnnotation rendererAnnotation, IType rendererType) throws JavaModelException {
+    private void addRenderer(IAnnotation rendererAnnotation, IType rendererType) throws JavaModelException
+    {
         String rendererClassName = rendererType.getFullyQualifiedName();
         IMemberValuePair[] pairs = rendererAnnotation.getMemberValuePairs();
         String rendererTypeString = null;
         String componentFamilyString = null;
         String renderKitIDString = null;
-        if (pairs != null) {
-            for (IMemberValuePair pair : pairs) {
-                if ("rendererType".equals(pair.getMemberName()) && pair.getValueKind() == IMemberValuePair.K_STRING) { //$NON-NLS-1$
-                    rendererTypeString = (String)pair.getValue();
-                } else if ("componentFamily".equals(pair.getMemberName()) && pair.getValueKind() == IMemberValuePair.K_STRING) {  //$NON-NLS-1$
-                    componentFamilyString = (String)pair.getValue();
-                } else if ("renderKitId".equals(pair.getMemberName()) && pair.getValueKind() == IMemberValuePair.K_STRING) {  //$NON-NLS-1$
-                    renderKitIDString = (String)pair.getValue();
+        if (pairs != null)
+        {
+            for (IMemberValuePair pair : pairs)
+            {
+                if ("rendererType".equals(pair.getMemberName()) && pair.getValueKind() == IMemberValuePair.K_STRING) //$NON-NLS-1$
+                {
+                    rendererTypeString = (String) pair.getValue();
+                }
+                else if ("componentFamily".equals(pair.getMemberName()) //$NON-NLS-1$
+                                && pair.getValueKind() == IMemberValuePair.K_STRING)
+                {
+                    componentFamilyString = (String) pair.getValue();
+                }
+                else if ("renderKitId".equals(pair.getMemberName()) //$NON-NLS-1$
+                                && pair.getValueKind() == IMemberValuePair.K_STRING)
+                {
+                    renderKitIDString = (String) pair.getValue();
                 }
             }
         }
-        
-        if (rendererClassName != null && rendererTypeString != null && componentFamilyString != null) {
+
+        if (rendererClassName != null && rendererTypeString != null && componentFamilyString != null)
+        {
             RendererType renderer = FacesConfigFactory.eINSTANCE.createRendererType();
             RendererClassType rendererClass = FacesConfigFactory.eINSTANCE.createRendererClassType();
             rendererClass.setTextContent(rendererClassName);
             renderer.setRendererClass(rendererClass);
 
-
             RendererTypeType rendererTypeType = FacesConfigFactory.eINSTANCE.createRendererTypeType();
             rendererTypeType.setTextContent(rendererTypeString);
             renderer.setRendererType(rendererTypeType);
-            
+
             ComponentFamilyType componentFamily = FacesConfigFactory.eINSTANCE.createComponentFamilyType();
             componentFamily.setTextContent(componentFamilyString);
             renderer.setComponentFamily(componentFamily);
 
-            
-            if (renderKitIDString == null) {
-                //use the default
+            if (renderKitIDString == null)
+            {
+                // use the default
                 renderKitIDString = "HTML_BASIC"; //$NON-NLS-1$
             }
             EList renderKits = facesConfig.getRenderKit();
-            if (renderKits != null) {
+            if (renderKits != null)
+            {
                 RenderKitType renderKit = null;
-                for (int i = 0, k = renderKits.size(); i < k; i++) {
-                    if (((RenderKitType)renderKits.get(i)).getRenderKitId() != null && renderKitIDString.equals(((RenderKitType)renderKits.get(i)).getRenderKitId().getTextContent())) {
-                        renderKit = (RenderKitType)(renderKits.get(i));
+                for (int i = 0, k = renderKits.size(); i < k; i++)
+                {
+                    if (((RenderKitType) renderKits.get(i)).getRenderKitId() != null && renderKitIDString
+                                    .equals(((RenderKitType) renderKits.get(i)).getRenderKitId().getTextContent()))
+                    {
+                        renderKit = (RenderKitType) (renderKits.get(i));
                     }
                 }
-                if (renderKit == null) {
+                if (renderKit == null)
+                {
                     renderKit = FacesConfigFactory.eINSTANCE.createRenderKitType();
                     RenderKitIdType renderKitID = FacesConfigFactory.eINSTANCE.createRenderKitIdType();
                     renderKitID.setTextContent(renderKitIDString);
@@ -312,34 +474,43 @@
         }
     }
 
-    private void addConverter(IAnnotation converterAnnotation, IType converterType) throws JavaModelException {
+    private void addConverter(IAnnotation converterAnnotation, IType converterType) throws JavaModelException
+    {
         String converterClassName = converterType.getFullyQualifiedName();
         IMemberValuePair[] pairs = converterAnnotation.getMemberValuePairs();
         String converterIDString = null;
         String converterForClassString = null;
-        if (pairs != null) {
-            for (IMemberValuePair pair : pairs) {
-                if ("value".equals(pair.getMemberName()) && pair.getValueKind() == IMemberValuePair.K_STRING) { //$NON-NLS-1$
-                    converterIDString = (String)pair.getValue();
-                } else if ("forClass".equals(pair.getMemberName()) && pair.getValueKind() == IMemberValuePair.K_CLASS) {  //$NON-NLS-1$
-                    converterForClassString = (String)pair.getValue();
+        if (pairs != null)
+        {
+            for (IMemberValuePair pair : pairs)
+            {
+                if ("value".equals(pair.getMemberName()) && pair.getValueKind() == IMemberValuePair.K_STRING) //$NON-NLS-1$
+                {
+                    converterIDString = (String) pair.getValue();
+                }
+                else if ("forClass".equals(pair.getMemberName()) && pair.getValueKind() == IMemberValuePair.K_CLASS) //$NON-NLS-1$
+                {
+                    converterForClassString = (String) pair.getValue();
                 }
             }
         }
-        if (converterClassName != null) {
+        if (converterClassName != null)
+        {
             ConverterType converter = FacesConfigFactory.eINSTANCE.createConverterType();
             ConverterClassType converterClass = FacesConfigFactory.eINSTANCE.createConverterClassType();
             converterClass.setTextContent(converterClassName);
             converter.setConverterClass(converterClass);
 
-            if (converterIDString != null) {
+            if (converterIDString != null)
+            {
                 ConverterIdType converterID = FacesConfigFactory.eINSTANCE.createConverterIdType();
                 converterID.setTextContent(converterIDString);
                 converter.setConverterId(converterID);
-            }  
-            
-            if (converterForClassString == null) {
-                //use the default
+            }
+
+            if (converterForClassString == null)
+            {
+                // use the default
                 converterForClassString = "java.lang.Object"; //$NON-NLS-1$
             }
             ConverterForClassType converterForClass = FacesConfigFactory.eINSTANCE.createConverterForClassType();
@@ -349,45 +520,56 @@
         }
     }
 
-    private void addComponent(IAnnotation componentAnnotation, IType componentType) throws JavaModelException {
+    private void addComponent(IAnnotation componentAnnotation, IType componentType) throws JavaModelException
+    {
         String componentClassName = componentType.getFullyQualifiedName();
         IMemberValuePair[] pairs = componentAnnotation.getMemberValuePairs();
         String componentTypeString = null;
-        if (pairs != null) {
-            for (IMemberValuePair pair : pairs) {
-                if ("value".equals(pair.getMemberName()) && pair.getValueKind() == IMemberValuePair.K_STRING) { //$NON-NLS-1$
-                    componentTypeString = (String)pair.getValue();
+        if (pairs != null)
+        {
+            for (IMemberValuePair pair : pairs)
+            {
+                if ("value".equals(pair.getMemberName()) && pair.getValueKind() == IMemberValuePair.K_STRING) //$NON-NLS-1$
+                {
+                    componentTypeString = (String) pair.getValue();
                 }
             }
         }
-        if (componentTypeString != null && componentClassName != null) {
-            ComponentType component =  FacesConfigFactory.eINSTANCE.createComponentType();
+        if (componentTypeString != null && componentClassName != null)
+        {
+            ComponentType component = FacesConfigFactory.eINSTANCE.createComponentType();
             ComponentClassType componentClass = FacesConfigFactory.eINSTANCE.createComponentClassType();
             componentClass.setTextContent(componentClassName);
             component.setComponentClass(componentClass);
-            
+
             ComponentTypeType componentTypeType = FacesConfigFactory.eINSTANCE.createComponentTypeType();
             componentTypeType.setTextContent(componentTypeString);
             component.setComponentType(componentTypeType);
-            
+
             facesConfig.getComponent().add(component);
         }
     }
 
-    private void addCDINamedBean(IAnnotation beanAnnotation, IType beanType) throws JavaModelException {
+    private void addCDINamedBean(IAnnotation beanAnnotation, JDTBeanIntrospector beanIntrospector, IType beanType) throws JavaModelException
+    {
         IMemberValuePair[] pairs = beanAnnotation.getMemberValuePairs();
 
         String beanNameString = null;
-        if (pairs != null) {
-            for (IMemberValuePair pair : pairs) {
-                if ("value".equals(pair.getMemberName()) && pair.getValueKind() == IMemberValuePair.K_STRING) { //$NON-NLS-1$
-                    beanNameString = (String)pair.getValue();
+        if (pairs != null)
+        {
+            for (IMemberValuePair pair : pairs)
+            {
+                if ("value".equals(pair.getMemberName()) && pair.getValueKind() == IMemberValuePair.K_STRING) //$NON-NLS-1$
+                {
+                    beanNameString = (String) pair.getValue();
                 }
             }
         }
-        if (beanNameString == null || beanNameString.length() < 1) {
+        if (beanNameString == null || beanNameString.length() < 1)
+        {
             beanNameString = beanType.getElementName();
-            if (beanNameString != null && beanNameString.length() > 0) {
+            if (beanNameString != null && beanNameString.length() > 0)
+            {
                 StringBuffer casedName = new StringBuffer(String.valueOf(beanNameString.charAt(0)).toLowerCase());
                 beanNameString = casedName.append(beanNameString.substring(1)).toString();
             }
@@ -395,30 +577,10 @@
 
         String beanClassName = beanType.getFullyQualifiedName();
 
-        String beanScopeString = "dependent"; //$NON-NLS-1$
-        IAnnotation[] annotations = beanType.getAnnotations();
-        if (annotations != null) {
-	        for (int i = 0, k = annotations.length; i < k; i++) {
-                if (annotations[i].exists()) {
-                    String annotationType = annotations[i].getElementName();
-                    String[][] resolvedAnnotationTypes = beanType.resolveType(annotationType);
-                    if (resolvedAnnotationTypes != null) {
-                        String resolvedAnnotationClassName = new StringBuffer(resolvedAnnotationTypes[0][0]).append('.').append(resolvedAnnotationTypes[0][1]).toString();
-                        if (CDI_REQUEST_SCOPED_ANNOTATION_CLASS.equals(resolvedAnnotationClassName)) {
-                        	beanScopeString = "request"; //$NON-NLS-1$
-                        } else if (CDI_CONVERSATION_SCOPED_ANNOTATION_CLASS.equals(resolvedAnnotationClassName)) {
-                        	beanScopeString = "conversation"; //$NON-NLS-1$
-                        } else if (CDI_SESSION_SCOPED_ANNOTATION_CLASS.equals(resolvedAnnotationClassName)) {
-                        	beanScopeString = "session"; //$NON-NLS-1$
-                        } else if (CDI_APPLICATION_SCOPED_ANNOTATION_CLASS.equals(resolvedAnnotationClassName)) {
-                        	beanScopeString = "application"; //$NON-NLS-1$
-                        }
-                    }
-                }
-	        }
-        }
+        String beanScopeString = processCDIScopeAnnotations(beanType, beanIntrospector);
 
-        if (beanNameString != null && beanClassName != null) {
+        if (beanNameString != null && beanClassName != null)
+        {
             ManagedBeanType bean = FacesConfigFactory.eINSTANCE.createManagedBeanType();
             ManagedBeanNameType beanName = FacesConfigFactory.eINSTANCE.createManagedBeanNameType();
             beanName.setTextContent(beanNameString);
@@ -435,9 +597,70 @@
         }
     }
 
-    private void addCDIModelBean(IType beanType) throws JavaModelException {
+    private String processCDIScopeAnnotations(IType beanType, JDTBeanIntrospector beanIntrospector) throws JavaModelException
+    {
+        String beanScopeString = "dependent"; //$NON-NLS-1$
+        IAnnotation[] annotations = beanType.getAnnotations();
+        if (annotations == null)
+        {
+            return beanScopeString;
+        }
+        for (IAnnotation annotation : annotations)
+        {
+            String scope = processCDIScopeAnnotation(beanType, beanIntrospector, annotation);
+            if (scope != null)
+            {
+                beanScopeString = scope;
+            }
+        }
+        return beanScopeString;
+    }
+    
+    private String processCDIScopeAnnotation(IType beanType, JDTBeanIntrospector beanIntrospector, IAnnotation annotation)
+    {
+        if (!annotation.exists())
+        {
+            return null;
+        }
+        String annotationType = annotation.getElementName();
+        if (! isCDIScopeAnnotationCandidate(annotationType))
+        {
+            return null;
+        }
+        String annotationClassName = beanIntrospector.resolveFullQualifiedTypeName(annotationType);
+        if (annotationClassName == null)
+        {
+            return null;
+        }
+        if (CDI_REQUEST_SCOPED_ANNOTATION_CLASS.equals(annotationClassName))
+        {
+            return "request"; //$NON-NLS-1$
+        }
+        else if (CDI_CONVERSATION_SCOPED_ANNOTATION_CLASS.equals(annotationClassName))
+        {
+            return "conversation"; //$NON-NLS-1$
+        }
+        else if (CDI_SESSION_SCOPED_ANNOTATION_CLASS.equals(annotationClassName))
+        {
+            return "session"; //$NON-NLS-1$
+        }
+        else if (CDI_APPLICATION_SCOPED_ANNOTATION_CLASS.equals(annotationClassName))
+        {
+            return "application"; //$NON-NLS-1$
+        }
+        return null;
+    }
+
+    private boolean isCDIScopeAnnotationCandidate(String annotationType)
+    {
+        return CDI_SCOPED_ANNOTATION_CANDIDATES.contains(annotationType);
+    }
+
+    private void addCDIModelBean(IType beanType) throws JavaModelException
+    {
         String beanNameString = beanType.getElementName();
-        if (beanNameString != null && beanNameString.length() > 0) {
+        if (beanNameString != null && beanNameString.length() > 0)
+        {
             StringBuffer casedName = new StringBuffer(String.valueOf(beanNameString.charAt(0)).toLowerCase());
             beanNameString = casedName.append(beanNameString.substring(1)).toString();
         }
@@ -446,26 +669,37 @@
 
         String beanScopeString = "request"; //$NON-NLS-1$
         IAnnotation[] annotations = beanType.getAnnotations();
-        if (annotations != null) {
-	        for (int i = 0, k = annotations.length; i < k; i++) {
-                if (annotations[i].exists()) {
+        if (annotations != null)
+        {
+            for (int i = 0, k = annotations.length; i < k; i++)
+            {
+                if (annotations[i].exists())
+                {
                     String annotationType = annotations[i].getElementName();
                     String[][] resolvedAnnotationTypes = beanType.resolveType(annotationType);
-                    if (resolvedAnnotationTypes != null) {
-                        String resolvedAnnotationClassName = new StringBuffer(resolvedAnnotationTypes[0][0]).append('.').append(resolvedAnnotationTypes[0][1]).toString();
-                        if (CDI_CONVERSATION_SCOPED_ANNOTATION_CLASS.equals(resolvedAnnotationClassName)) {
-                        	beanScopeString = "conversation"; //$NON-NLS-1$
-                        } else if (CDI_SESSION_SCOPED_ANNOTATION_CLASS.equals(resolvedAnnotationClassName)) {
-                        	beanScopeString = "session"; //$NON-NLS-1$
-                        } else if (CDI_APPLICATION_SCOPED_ANNOTATION_CLASS.equals(resolvedAnnotationClassName)) {
-                        	beanScopeString = "application"; //$NON-NLS-1$
+                    if (resolvedAnnotationTypes != null)
+                    {
+                        String resolvedAnnotationClassName = new StringBuffer(resolvedAnnotationTypes[0][0]).append('.')
+                                        .append(resolvedAnnotationTypes[0][1]).toString();
+                        if (CDI_CONVERSATION_SCOPED_ANNOTATION_CLASS.equals(resolvedAnnotationClassName))
+                        {
+                            beanScopeString = "conversation"; //$NON-NLS-1$
+                        }
+                        else if (CDI_SESSION_SCOPED_ANNOTATION_CLASS.equals(resolvedAnnotationClassName))
+                        {
+                            beanScopeString = "session"; //$NON-NLS-1$
+                        }
+                        else if (CDI_APPLICATION_SCOPED_ANNOTATION_CLASS.equals(resolvedAnnotationClassName))
+                        {
+                            beanScopeString = "application"; //$NON-NLS-1$
                         }
                     }
                 }
-	        }
+            }
         }
 
-        if (beanNameString != null && beanClassName != null) {
+        if (beanNameString != null && beanClassName != null)
+        {
             ManagedBeanType bean = FacesConfigFactory.eINSTANCE.createManagedBeanType();
             ManagedBeanNameType beanName = FacesConfigFactory.eINSTANCE.createManagedBeanNameType();
             beanName.setTextContent(beanNameString);
diff --git a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/designtime/internal/view/DTComponentIntrospector.java b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/designtime/internal/view/DTComponentIntrospector.java
index 3a9b89a..8a12976 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/designtime/internal/view/DTComponentIntrospector.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/designtime/internal/view/DTComponentIntrospector.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2001, 2011 Oracle Corporation and others.
+ * Copyright (c) 2001, 2021 Oracle Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
  * which accompanies this distribution, and is available at
@@ -8,7 +8,8 @@
  * SPDX-License-Identifier: EPL-2.0
  * 
  * Contributors:
- *     Oracle Corporation - initial API and implementation
+ *     Oracle Corporation  - initial API and implementation
+ *     Reto Weiss/Axon Ivy - Use cached JDTBeanIntrospector 
  *******************************************************************************/
 package org.eclipse.jst.jsf.designtime.internal.view;
 
@@ -357,9 +358,7 @@
 
             if (type != null)
             {
-                // TODO: should be able to use type cache for this
-                final JDTBeanIntrospector introspector = new JDTBeanIntrospector(
-                        type);
+                final JDTBeanIntrospector introspector = JDTBeanIntrospector.forType(type);
                 properties = introspector.getProperties();
             }
         }
diff --git a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/validation/internal/appconfig/PropertyNameValidationVisitor.java b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/validation/internal/appconfig/PropertyNameValidationVisitor.java
index 3c3d3f2..0d949a0 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/validation/internal/appconfig/PropertyNameValidationVisitor.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/validation/internal/appconfig/PropertyNameValidationVisitor.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2001, 2010 Oracle Corporation and others.
+ * Copyright (c) 2001, 2021 Oracle Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
  * which accompanies this distribution, and is available at
@@ -8,7 +8,8 @@
  * SPDX-License-Identifier: EPL-2.0
  * 
  * Contributors:
- *     Oracle Corporation - initial API and implementation
+ *     Oracle Corporation  - initial API and implementation
+ *     Reto Weiss/Axon Ivy - Use cached JDTBeanIntrospector 
  *******************************************************************************/
 package org.eclipse.jst.jsf.validation.internal.appconfig;
 
@@ -54,11 +55,13 @@
         _typeCache = new HashMap<String, IType>();
     }
 
+    @Override
     protected EObjectValidationVisitor[] getChildNodeValidators() 
     {
         return PropertyValidationVisitor.NO_CHILDREN;
     }
 
+    @Override
     protected void doValidate(EObject object, List messages, IFile file) 
     {
         final String parentClassType = getParentClassType(object);
@@ -144,7 +147,7 @@
 
 	private Map<String, JDTBeanProperty> getProperties(final IType type, final IProject project) 
 	{
-		final JDTBeanIntrospector introspector = new JDTBeanIntrospector(type);
+		final JDTBeanIntrospector introspector = JDTBeanIntrospector.forType(type);
 		return introspector.getProperties();
 	}
 	
diff --git a/jsf/tests/org.eclipse.jst.jsf.context.symbol.tests/src/org/eclipse/jst/jsf/context/symbol/tests/TestIJavaTypeDescriptor2_ChangeStability.java b/jsf/tests/org.eclipse.jst.jsf.context.symbol.tests/src/org/eclipse/jst/jsf/context/symbol/tests/TestIJavaTypeDescriptor2_ChangeStability.java
index a623456..a92f1a1 100644
--- a/jsf/tests/org.eclipse.jst.jsf.context.symbol.tests/src/org/eclipse/jst/jsf/context/symbol/tests/TestIJavaTypeDescriptor2_ChangeStability.java
+++ b/jsf/tests/org.eclipse.jst.jsf.context.symbol.tests/src/org/eclipse/jst/jsf/context/symbol/tests/TestIJavaTypeDescriptor2_ChangeStability.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2001, 2007 Oracle Corporation and others.
+ * Copyright (c) 2001, 2021 Oracle Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
  * which accompanies this distribution, and is available at
@@ -8,7 +8,8 @@
  * SPDX-License-Identifier: EPL-2.0
  * 
  * Contributors:
- *     Oracle Corporation - initial API and implementation
+ *     Oracle Corporation  - initial API and implementation
+ *     Reto Weiss/Axon Ivy - Cache resolved types
  *******************************************************************************/
 package org.eclipse.jst.jsf.context.symbol.tests;
 
@@ -20,6 +21,7 @@
 import org.eclipse.jdt.core.ICompilationUnit;
 import org.eclipse.jdt.core.IMethod;
 import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaModelException;
 import org.eclipse.jdt.core.Signature;
 import org.eclipse.jdt.core.dom.AST;
 import org.eclipse.jdt.core.dom.ASTParser;
@@ -29,7 +31,7 @@
 import org.eclipse.jdt.core.dom.TypeDeclaration;
 import org.eclipse.jface.text.Document;
 import org.eclipse.jst.jsf.common.internal.types.TypeConstants;
-import org.eclipse.jst.jsf.common.util.TypeUtil;
+import org.eclipse.jst.jsf.common.util.JDTTypeResolver;
 import org.eclipse.jst.jsf.context.symbol.IBeanInstanceSymbol;
 import org.eclipse.jst.jsf.context.symbol.IBeanMethodSymbol;
 import org.eclipse.jst.jsf.context.symbol.IJavaTypeDescriptor2;
@@ -52,6 +54,7 @@
  */
 public class TestIJavaTypeDescriptor2_ChangeStability extends ModelBaseTestCase 
 {
+    private static final IType[] EMPTY_SUPER_TYPES = new IType[0];
     private IBeanInstanceSymbol     _testBean1Symbol;
     private IBeanInstanceSymbol     _testBean2Symbol;
 
@@ -64,6 +67,7 @@
     private final static String testBeanName1 = "TestBean1";
     private final static String testBeanName2 = "TestBean2";
 
+    @Override
     protected void setUp() throws Exception 
     {
         super.setUp();
@@ -111,6 +115,7 @@
         return bean;
     }
     
+    @Override
     protected void tearDown() throws Exception 
     {
         super.tearDown();
@@ -129,7 +134,7 @@
         IType type = _testBean1Symbol.getJavaTypeDescriptor().getType();
         IMethod newMethod = type.createMethod(NON_BEAN_METHOD_CONTENTS, null, true, null);
         assertNotNull(newMethod);
-        assertEquals(NON_BEAN_METHOD_SIG, TypeUtil.resolveMethodSignature(type, newMethod.getSignature()));
+        assertEquals(NON_BEAN_METHOD_SIG, resolveMethodSignature(type, newMethod));
         assertTrue(newMethod.exists());
         
         // post-cond: bean has new method 
@@ -184,7 +189,7 @@
         IMethod newMethod = type.createMethod(BEAN_METHOD_CONTENTS, null, true, null);
         assertNotNull(newMethod);
         assertTrue(newMethod.exists());
-        assertEquals(BEAN_METHOD_SIG, TypeUtil.resolveMethodSignature(type, newMethod.getSignature()));
+        assertEquals(BEAN_METHOD_SIG, resolveMethodSignature(type, newMethod));
         
         {
             // post-cond: bean has new method 
@@ -218,7 +223,7 @@
         IType type = _testBean1Symbol.getJavaTypeDescriptor().getType();
         IMethod method = type.getMethod(EXISTING_BEAN_METHOD, new String[0]);
         assertNotNull(method);
-        assertEquals(EXISTING_BEAN_METHOD_SIG, TypeUtil.resolveMethodSignature(type, method.getSignature()));
+        assertEquals(EXISTING_BEAN_METHOD_SIG, resolveMethodSignature(type, method));
         assertTrue(method.exists());
         method.delete(true, null);
         assertFalse(method.exists());
@@ -253,7 +258,7 @@
         IType type = _testBean1Symbol.getJavaTypeDescriptor().getType();
         IMethod method = type.getMethod(EXISTING_BEAN_METHOD, new String[0]);
         assertNotNull(method);
-        assertEquals(EXISTING_BEAN_METHOD_SIG, TypeUtil.resolveMethodSignature(type, method.getSignature()));
+        assertEquals(EXISTING_BEAN_METHOD_SIG, resolveMethodSignature(type, method));
         assertTrue(method.exists());
         method.rename(NEW_READONLY_BEAN_METHOD_NAME, false, null);
         method = type.getMethod(NEW_READONLY_BEAN_METHOD_NAME, new String[0]);
@@ -283,7 +288,7 @@
         _beanProperties.put(prop.getName(), prop);
         compareSymbolMaps(_beanProperties, newProperties);
     }
-    
+
     private static final String  READONLY_BEAN_REPLACEMENT_METHOD =
         "public Integer "+EXISTING_BEAN_METHOD+"(){return new Integer(4);}";
     private static final String  READONLY_BEAN_REPLACEMENT_SIG =
@@ -518,4 +523,10 @@
         cu.getBuffer().setContents(newSource);
         cu.commitWorkingCopy(true, null);
     }
+    
+    private String resolveMethodSignature(IType type, IMethod method) throws JavaModelException
+    {
+      return new JDTTypeResolver(type, EMPTY_SUPER_TYPES).resolveMethodEraseTypeParams(method.getSignature());
+    }
+
 }
diff --git a/jsf/tests/org.eclipse.jst.jsf.core.tests/src/org/eclipse/jst/jsf/core/tests/util/TestJDTBeanIntrospector.java b/jsf/tests/org.eclipse.jst.jsf.core.tests/src/org/eclipse/jst/jsf/core/tests/util/TestJDTBeanIntrospector.java
index 69259f1..d878784 100644
--- a/jsf/tests/org.eclipse.jst.jsf.core.tests/src/org/eclipse/jst/jsf/core/tests/util/TestJDTBeanIntrospector.java
+++ b/jsf/tests/org.eclipse.jst.jsf.core.tests/src/org/eclipse/jst/jsf/core/tests/util/TestJDTBeanIntrospector.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2006, 2009 Oracle Corporation.
+ * Copyright (c) 2006, 2021 Oracle Corporation.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
  * which accompanies this distribution, and is available at
@@ -9,6 +9,7 @@
  *
  * Contributors:
  *    Cameron Bateman/Oracle - initial API and implementation
+*     Reto Weiss/Axon Ivy    - Cache resolved types 
  * 
  ********************************************************************************/
 
@@ -16,8 +17,6 @@
 
 import java.util.Map;
 
-import junit.framework.TestCase;
-
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.jdt.core.IType;
@@ -25,13 +24,14 @@
 import org.eclipse.jst.jsf.common.internal.types.TypeConstants;
 import org.eclipse.jst.jsf.common.util.JDTBeanIntrospector;
 import org.eclipse.jst.jsf.common.util.JDTBeanProperty;
-import org.eclipse.jst.jsf.common.util.JDTBeanPropertyWorkingCopy;
 import org.eclipse.jst.jsf.core.tests.TestsPlugin;
 import org.eclipse.jst.jsf.test.util.JDTTestEnvironment;
 import org.eclipse.jst.jsf.test.util.JSFTestUtil;
 import org.eclipse.jst.jsf.test.util.TestFileResource;
 import org.eclipse.jst.jsf.test.util.WebProjectTestEnvironment;
 
+import junit.framework.TestCase;
+
 /**
  * Tester fot he JDTBeanIntrospector class
  * 
@@ -144,17 +144,16 @@
         
         // introspect after classes loaded to ensure all dependencies
         // are in the project
-        JDTBeanIntrospector beanIntrospector =
-            new JDTBeanIntrospector(_testBean1Type);
+        JDTBeanIntrospector beanIntrospector = JDTBeanIntrospector.forType(_testBean1Type);
         _properties = beanIntrospector.getProperties();
 
-        beanIntrospector = new JDTBeanIntrospector(_testBeanSubclassType);
+        beanIntrospector = JDTBeanIntrospector.forType(_testBeanSubclassType);
         _subClassProperties = beanIntrospector.getProperties();
 
-        beanIntrospector = new JDTBeanIntrospector(_testBeanGenericType);
+        beanIntrospector = JDTBeanIntrospector.forType(_testBeanGenericType);
         _genericTypeProperties = beanIntrospector.getProperties();
 
-        beanIntrospector = new JDTBeanIntrospector(_testInterface);
+        beanIntrospector = JDTBeanIntrospector.forType(_testInterface);
         _interfaceProperties = beanIntrospector.getProperties();
     }
 
@@ -197,13 +196,6 @@
                 .size());
         assertNull("Empty string is invalid property name", properties.get(""));
         assertNull("Null is not a valid property name", properties.get(null));
-
-        // ensure type correctness of all values
-        for (final JDTBeanProperty value : properties.values())
-        {
-            // no working copies should slip their way in
-            assertFalse(value instanceof JDTBeanPropertyWorkingCopy);
-        }
     }
 
     /**
@@ -622,9 +614,7 @@
     {
         final JDTBeanProperty property =
             _genericTypeProperties.get("listOfListOfStrings");
-        assertEquals("Ljava.util.List;", property.getTypeSignature(true));
-        assertEquals("Ljava.util.List<Ljava.util.List<Ljava.lang.String;>;>;",
-                property.getTypeSignature(false));
+        assertEquals("Ljava.util.List;", property.getTypeSignature());
 
         assertEquals(1, property.getTypeParameterSignatures().size());
         assertEquals("Ljava.util.List<Ljava.lang.String;>;", property
@@ -635,9 +625,7 @@
     {
         final JDTBeanProperty property =
             _genericTypeProperties.get("mapOfString_String");
-        assertEquals("Ljava.util.Map;", property.getTypeSignature(true));
-        assertEquals("Ljava.util.Map<Ljava.lang.String;Ljava.lang.String;>;",
-                property.getTypeSignature(false));
+        assertEquals("Ljava.util.Map;", property.getTypeSignature());
 
         assertEquals(2, property.getTypeParameterSignatures().size());
         assertEquals("Ljava.lang.String;", property
@@ -650,9 +638,7 @@
     public void testUnboundedProperty_List() throws Exception
     {
         final JDTBeanProperty property = _genericTypeProperties.get("unboundedList");
-        assertEquals("Ljava.util.List;", property.getTypeSignature(true));
-        assertEquals("Ljava.util.List<Ljava.lang.Object;>;", property
-                .getTypeSignature(false));
+        assertEquals("Ljava.util.List;", property.getTypeSignature());
 
         assertEquals(1, property.getTypeParameterSignatures().size());
         assertEquals("Ljava.lang.Object;", property
diff --git a/jsf/tests/org.eclipse.jst.jsf.core.tests/src/org/eclipse/jst/jsf/core/tests/util/TestJDTBeanPropertyWorkingCopy.java b/jsf/tests/org.eclipse.jst.jsf.core.tests/src/org/eclipse/jst/jsf/core/tests/util/TestJDTBeanPropertyWorkingCopy.java
index 522e0f2..4562267 100644
--- a/jsf/tests/org.eclipse.jst.jsf.core.tests/src/org/eclipse/jst/jsf/core/tests/util/TestJDTBeanPropertyWorkingCopy.java
+++ b/jsf/tests/org.eclipse.jst.jsf.core.tests/src/org/eclipse/jst/jsf/core/tests/util/TestJDTBeanPropertyWorkingCopy.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2006, 2008 Oracle Corporation.
+ * Copyright (c) 2006, 2021 Oracle Corporation.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
  * which accompanies this distribution, and is available at
@@ -9,12 +9,11 @@
  *
  * Contributors:
  *    Cameron Bateman/Oracle - initial API and implementation
+ *    Reto Weiss/Axon Ivy    - Cache resolved types *    
  * 
  ********************************************************************************/
 package org.eclipse.jst.jsf.core.tests.util;
 
-import junit.framework.TestCase;
-
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.jdt.core.IMethod;
@@ -22,12 +21,15 @@
 import org.eclipse.jdt.core.Signature;
 import org.eclipse.jst.jsf.common.util.JDTBeanProperty;
 import org.eclipse.jst.jsf.common.util.JDTBeanPropertyWorkingCopy;
+import org.eclipse.jst.jsf.common.util.JDTTypeResolver;
 import org.eclipse.jst.jsf.core.tests.TestsPlugin;
 import org.eclipse.jst.jsf.test.util.JDTTestEnvironment;
 import org.eclipse.jst.jsf.test.util.JSFTestUtil;
 import org.eclipse.jst.jsf.test.util.TestFileResource;
 import org.eclipse.jst.jsf.test.util.WebProjectTestEnvironment;
 
+import junit.framework.TestCase;
+
 /**
  * Tests basic operations on the jdt bean property
  * 
@@ -95,8 +97,7 @@
      */
     public void testSimpleBeanProperty()
     {
-        final JDTBeanPropertyWorkingCopy workingCopy =
-            new JDTBeanPropertyWorkingCopy(_testBean1Type);
+        final JDTBeanPropertyWorkingCopy workingCopy = createPropertyWorkingCopy();
 
         final IMethod simpleGetter =
             _testBean1Type.getMethod("getStringProp1", new String[0]);
@@ -132,8 +133,7 @@
      */
     public void testSimpleIsBeanProperty()
     {
-        final JDTBeanPropertyWorkingCopy workingCopy =
-            new JDTBeanPropertyWorkingCopy(_testBean1Type);
+        final JDTBeanPropertyWorkingCopy workingCopy = createPropertyWorkingCopy();
 
         final IMethod simpleGetter =
             _testBean1Type.getMethod("isBooleanIsProp1", new String[0]);
@@ -171,8 +171,7 @@
      */
     public void testIsAccessorTakesPrecedence()
     {
-        final JDTBeanPropertyWorkingCopy workingCopy =
-            new JDTBeanPropertyWorkingCopy(_testBean1Type);
+        final JDTBeanPropertyWorkingCopy workingCopy = createPropertyWorkingCopy();
 
         final IMethod simpleIsGetter =
             _testBean1Type.getMethod("isBooleanIsProp2", new String[0]);
@@ -254,8 +253,7 @@
      */
     public void testDoNotUseSetterIfDoesNotMatchGetterType()
     {
-        final JDTBeanPropertyWorkingCopy workingCopy =
-            new JDTBeanPropertyWorkingCopy(_testBean1Type);
+        final JDTBeanPropertyWorkingCopy workingCopy = createPropertyWorkingCopy();
 
         final IMethod simpleGetter =
             _testBean1Type.getMethod("getStringProperty2", new String[0]);
@@ -295,8 +293,7 @@
     public void testReadonlyBeanProperty()
     {
         // readonly get
-        JDTBeanPropertyWorkingCopy workingCopy =
-            new JDTBeanPropertyWorkingCopy(_testBean1Type);
+        JDTBeanPropertyWorkingCopy workingCopy = createPropertyWorkingCopy();
 
         final IMethod simpleGetter =
             _testBean1Type.getMethod("getReadonlyStringProperty",
@@ -326,7 +323,7 @@
         // should be an IType for a String
         assertNotNull(beanProperty.getType());
 
-        workingCopy = new JDTBeanPropertyWorkingCopy(_testBean1Type);
+        workingCopy = createPropertyWorkingCopy();
 
         // readonly is getter
         final IMethod isGetter =
@@ -365,8 +362,7 @@
     public void testWriteonlyBeanProperty()
     {
         // readonly get
-        final JDTBeanPropertyWorkingCopy workingCopy =
-            new JDTBeanPropertyWorkingCopy(_testBean1Type);
+        final JDTBeanPropertyWorkingCopy workingCopy = createPropertyWorkingCopy();
 
         final IMethod simpleSetter =
             _testBean1Type.getMethod("setWriteonlyStringProperty",
@@ -403,8 +399,7 @@
     public void testStringArrayProperty()
     {
         // readonly get
-        final JDTBeanPropertyWorkingCopy workingCopy =
-            new JDTBeanPropertyWorkingCopy(_testBean1Type);
+        final JDTBeanPropertyWorkingCopy workingCopy = createPropertyWorkingCopy();
 
         final IMethod simpleGetter =
             _testBean1Type.getMethod("getStringArrayProperty",
@@ -444,8 +439,7 @@
     public void testCollectionProperty()
     {
         // readonly get
-        final JDTBeanPropertyWorkingCopy workingCopy =
-            new JDTBeanPropertyWorkingCopy(_testBean1Type);
+        final JDTBeanPropertyWorkingCopy workingCopy = createPropertyWorkingCopy();
 
         final IMethod simpleGetter =
             _testBean1Type
@@ -485,8 +479,7 @@
     public void testMapProperty()
     {
         // readonly get
-        final JDTBeanPropertyWorkingCopy workingCopy =
-            new JDTBeanPropertyWorkingCopy(_testBean1Type);
+        final JDTBeanPropertyWorkingCopy workingCopy = createPropertyWorkingCopy();
 
         final IMethod simpleGetter =
             _testBean1Type.getMethod("getMapProperty", new String[0]);
@@ -518,4 +511,9 @@
         assertNotNull(beanProperty.getType());
     }
 
+    private JDTBeanPropertyWorkingCopy createPropertyWorkingCopy()
+    {
+      return new JDTBeanPropertyWorkingCopy(_testBean1Type, new JDTTypeResolver(_testBean1Type, new IType[0]), "StringProp1");
+    }
+
 }
diff --git a/jsf/tests/org.eclipse.jst.jsf.core.tests/src/org/eclipse/jst/jsf/core/tests/util/TestTypeUtil.java b/jsf/tests/org.eclipse.jst.jsf.core.tests/src/org/eclipse/jst/jsf/core/tests/util/TestTypeUtil.java
index 62036e7..6f90630 100644
--- a/jsf/tests/org.eclipse.jst.jsf.core.tests/src/org/eclipse/jst/jsf/core/tests/util/TestTypeUtil.java
+++ b/jsf/tests/org.eclipse.jst.jsf.core.tests/src/org/eclipse/jst/jsf/core/tests/util/TestTypeUtil.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007, 2008 Oracle Corporation.
+ * Copyright (c) 2007, 2021 Oracle Corporation.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
  * which accompanies this distribution, and is available at
@@ -9,6 +9,7 @@
  *
  * Contributors:
  *    Cameron Bateman/Oracle - initial API and implementation
+ *    Reto Weiss/Axon Ivy    - Cache resolved types    
  * 
  ********************************************************************************/
 package org.eclipse.jst.jsf.core.tests.util;
@@ -16,12 +17,11 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import junit.framework.TestCase;
-
 import org.eclipse.jdt.core.IClasspathEntry;
 import org.eclipse.jdt.core.IMethod;
 import org.eclipse.jdt.core.IType;
 import org.eclipse.jst.jsf.common.internal.types.TypeConstants;
+import org.eclipse.jst.jsf.common.util.JDTTypeResolver;
 import org.eclipse.jst.jsf.common.util.TypeUtil;
 import org.eclipse.jst.jsf.core.tests.TestsPlugin;
 import org.eclipse.jst.jsf.test.util.JDTTestEnvironment;
@@ -29,8 +29,12 @@
 import org.eclipse.jst.jsf.test.util.TestFileResource;
 import org.eclipse.jst.jsf.test.util.WebProjectTestEnvironment;
 
+import junit.framework.TestCase;
+
 public class TestTypeUtil extends TestCase
 {
+  private static final IType[] EMPTY_SUPER_TYPES = new IType[0];
+  
     private JDTTestEnvironment  _jdtTestEnvironment;
     private IType               _testBean1Type;
     private IType               _testBeanSubclassType;
@@ -52,6 +56,7 @@
 
     // private final static String testEnumName2 = "TestEnum2";
 
+    @Override
     protected void setUp() throws Exception
     {
         super.setUp();
@@ -153,6 +158,7 @@
         // new JDTBeanIntrospector(_testBeanGenericType);
     }
 
+    @Override
     protected void tearDown() throws Exception
     {
         super.tearDown();
@@ -169,28 +175,28 @@
     public void testResolveTypeSignatureITypeString()
     {
         // this one should be the same regardless of type erasure
-        assertEquals(TypeConstants.TYPE_STRING, TypeUtil.resolveTypeSignature(
-                _testBean1Type, "QString;", true));
-        assertEquals(TypeConstants.TYPE_STRING, TypeUtil.resolveTypeSignature(
-                _testBean1Type, "QString;", false));
+        assertEquals(TypeConstants.TYPE_STRING, 
+                resolver(_testBean1Type).resolveEraseTypeParams("QString;"));
+        assertEquals(TypeConstants.TYPE_STRING, 
+                resolver(_testBean1Type).resolveKeepTypeParams("QString;"));
 
         // no parameters are provided here, so we should see the same result
-        assertEquals(TypeConstants.TYPE_COLLECTION, TypeUtil
-                .resolveTypeSignature(_testBean1Type, "QCollection;", true));
-        assertEquals(TypeConstants.TYPE_COLLECTION, TypeUtil
-                .resolveTypeSignature(_testBean1Type, "QCollection;", false));
-        assertEquals(TypeConstants.TYPE_MAP, TypeUtil.resolveTypeSignature(
-                _testBean1Type, "QMap;", true));
-        assertEquals(TypeConstants.TYPE_MAP, TypeUtil.resolveTypeSignature(
-                _testBean1Type, "QMap;", false));
+        assertEquals(TypeConstants.TYPE_COLLECTION, 
+                resolver(_testBean1Type).resolveEraseTypeParams("QCollection;"));
+        assertEquals(TypeConstants.TYPE_COLLECTION, 
+                resolver(_testBean1Type).resolveKeepTypeParams("QCollection;"));
+        assertEquals(TypeConstants.TYPE_MAP, 
+                resolver(_testBean1Type).resolveEraseTypeParams("QMap;"));
+        assertEquals(TypeConstants.TYPE_MAP, 
+                resolver(_testBean1Type).resolveKeepTypeParams("QMap;"));
 
         // in this case, the provided signature has type erasure, so the answer
         // will different depending on typeErasure flag
-        final String typeSigWithErasure = TypeUtil.resolveTypeSignature(
-                _testBean1Type, "QMap<QString;QString;>;", true);
+        final String typeSigWithErasure = resolver(_testBean1Type)
+                .resolveEraseTypeParams("QMap<QString;QString;>;");
         assertEquals(TypeConstants.TYPE_MAP, typeSigWithErasure);
-        final String typeSigNoErasure = TypeUtil.resolveTypeSignature(
-                _testBean1Type, "QMap<QString;QString;>;", false);
+        final String typeSigNoErasure = resolver(_testBean1Type)
+                .resolveKeepTypeParams("QMap<QString;QString;>;");
         assertEquals("Ljava.util.Map<Ljava.lang.String;Ljava.lang.String;>;",
                 typeSigNoErasure);
 
@@ -198,28 +204,28 @@
         final IType mapType = TypeUtil.resolveType(_jdtTestEnvironment
                 .getJavaProject(), "Ljava.util.Map;");
         assertNotNull(mapType);
-        assertEquals(TypeConstants.TYPE_JAVAOBJECT, TypeUtil
-                .resolveTypeSignature(mapType, "TV;", false));
+        assertEquals(TypeConstants.TYPE_JAVAOBJECT, 
+                resolver(mapType).resolveKeepTypeParams("TV;"));
 
         // unfound signature
-        assertEquals("QSomeNotRealClass;", TypeUtil.resolveTypeSignature(
-                _testBean1Type, "QSomeNotRealClass;", false));
+        assertEquals("QSomeNotRealClass;", 
+                resolver(_testBean1Type).resolveKeepTypeParams("QSomeNotRealClass;"));
 
         // arrays
-        assertEquals("[I", TypeUtil.resolveTypeSignature(_testBean1Type, "[I"));
+        assertEquals("[I", resolver(_testBean1Type).resolveEraseTypeParams("[I"));
 
-        assertEquals("[Ljava.lang.String;", TypeUtil.resolveTypeSignature(
-                _testBean1Type, "[QString;"));
+        assertEquals("[Ljava.lang.String;", 
+                resolver(_testBean1Type).resolveKeepTypeParams("[QString;"));
 
-        assertEquals("[Ljava.util.Map;", TypeUtil.resolveTypeSignature(
-                _testBean1Type, "[QMap;"));
+        assertEquals("[Ljava.util.Map;", 
+                resolver(_testBean1Type).resolveKeepTypeParams("[QMap;"));
 
-        assertEquals("[Ljava.util.Collection;", TypeUtil.resolveTypeSignature(
-                _testBean1Type, "[QCollection;"));
+        assertEquals("[Ljava.util.Collection;", 
+                resolver(_testBean1Type).resolveKeepTypeParams("[QCollection;"));
 
         // array of arrays
-        assertEquals("[[[Ljava.lang.String;", TypeUtil.resolveTypeSignature(
-                _testBean1Type, "[[[QString;"));
+        assertEquals("[[[Ljava.lang.String;", 
+                resolver(_testBean1Type).resolveKeepTypeParams("[[[QString;"));
 
         // cover cases where wildcards and/or capture are used. All should be
         // equivalent to the case for
@@ -235,50 +241,50 @@
             final boolean typeErasure)
     {
         // extends
-        assertEquals(expected, TypeUtil.resolveTypeSignature(_testBean1Type,
+        assertEquals(expected, resolveTypeSignature(_testBean1Type,
                 "QMap<+QString;+QString;>;", typeErasure));
-        assertEquals(expected, TypeUtil.resolveTypeSignature(_testBean1Type,
+        assertEquals(expected, resolveTypeSignature(_testBean1Type,
                 "QMap<+QString;QString;>;", typeErasure));
-        assertEquals(expected, TypeUtil.resolveTypeSignature(_testBean1Type,
+        assertEquals(expected, resolveTypeSignature(_testBean1Type,
                 "QMap<QString;+QString;>;", typeErasure));
         // super
-        assertEquals(expected, TypeUtil.resolveTypeSignature(_testBean1Type,
+        assertEquals(expected, resolveTypeSignature(_testBean1Type,
                 "QMap<-QString;-QString;>;", typeErasure));
-        assertEquals(expected, TypeUtil.resolveTypeSignature(_testBean1Type,
+        assertEquals(expected, resolveTypeSignature(_testBean1Type,
                 "QMap<-QString;QString;>;", typeErasure));
-        assertEquals(expected, TypeUtil.resolveTypeSignature(_testBean1Type,
+        assertEquals(expected, resolveTypeSignature(_testBean1Type,
                 "QMap<QString;-QString;>;", typeErasure));
         // star
         // this one is distinct because * are converted to Object
         final String expected1 = "Ljava.util.Map"
                 + (typeErasure ? ";"
                         : "<Ljava.lang.Object;Ljava.lang.Object;>;");
-        assertEquals(expected1, TypeUtil.resolveTypeSignature(_testBean1Type,
+        assertEquals(expected1, resolveTypeSignature(_testBean1Type,
                 "QMap<**>;", typeErasure));
         final String expected2 = "Ljava.util.Map"
                 + (typeErasure ? ";"
                         : "<Ljava.lang.Object;Ljava.lang.String;>;");
-        assertEquals(expected2, TypeUtil.resolveTypeSignature(_testBean1Type,
+        assertEquals(expected2, resolveTypeSignature(_testBean1Type,
                 "QMap<*QString;>;", typeErasure));
         final String expected3 = "Ljava.util.Map"
                 + (typeErasure ? ";"
                         : "<Ljava.lang.String;Ljava.lang.Object;>;");
-        assertEquals(expected3, TypeUtil.resolveTypeSignature(_testBean1Type,
+        assertEquals(expected3, resolveTypeSignature(_testBean1Type,
                 "QMap<QString;*>;", typeErasure));
         // capture extends
-        assertEquals(expected, TypeUtil.resolveTypeSignature(_testBean1Type,
+        assertEquals(expected, resolveTypeSignature(_testBean1Type,
                 "QMap<!+QString;!+QString;>;", typeErasure));
-        assertEquals(expected, TypeUtil.resolveTypeSignature(_testBean1Type,
+        assertEquals(expected, resolveTypeSignature(_testBean1Type,
                 "QMap<!+QString;QString;>;", typeErasure));
-        assertEquals(expected, TypeUtil.resolveTypeSignature(_testBean1Type,
+        assertEquals(expected, resolveTypeSignature(_testBean1Type,
                 "QMap<QString;!+QString;>;", typeErasure));
 
-        assertEquals("Ljava.lang.String;", TypeUtil.resolveTypeSignature(
+        assertEquals("Ljava.lang.String;", resolveTypeSignature(
                 _testBean1Type, "!+QString;", typeErasure));
 
         // test regression on
         // https://bugs.eclipse.org/bugs/show_bug.cgi?id=197506
-        assertEquals("Ljava.lang.Object;", TypeUtil.resolveTypeSignature(
+        assertEquals("Ljava.lang.Object;", resolveTypeSignature(
                 _testBean1Type, "*", typeErasure));
     }
 
@@ -322,21 +328,21 @@
 
     public void testResolveMethodSignature() throws Exception
     {
-        assertEquals("()Ljava.lang.String;", TypeUtil.resolveMethodSignature(
-                _testBean1Type, "()QString;"));
-        assertEquals("(Ljava.lang.String;)V", TypeUtil.resolveMethodSignature(
-                _testBean1Type, "(QString;)V"));
-        assertEquals("(Ljava.lang.String;Z)V", TypeUtil.resolveMethodSignature(
-                _testBean1Type, "(QString;Z)V"));
+        assertEquals("()Ljava.lang.String;", 
+                resolver(_testBean1Type).resolveMethodEraseTypeParams("()QString;"));
+        assertEquals("(Ljava.lang.String;)V", 
+                resolver(_testBean1Type).resolveMethodEraseTypeParams("(QString;)V"));
+        assertEquals("(Ljava.lang.String;Z)V", 
+                resolver(_testBean1Type).resolveMethodEraseTypeParams("(QString;Z)V"));
 
         IMethod method = _testBean1Type.getMethod("getStringProp1", null);
-        assertEquals("()Ljava.lang.String;", TypeUtil.resolveMethodSignature(
-                _testBean1Type, method.getSignature()));
+        assertEquals("()Ljava.lang.String;", 
+                resolver(_testBean1Type).resolveMethodEraseTypeParams(method.getSignature()));
 
         method = _testBean1Type.getMethod("setStringProperty2", new String[]
         { "I" });
-        assertEquals("(I)V", TypeUtil.resolveMethodSignature(_testBean1Type,
-                method.getSignature()));
+        assertEquals("(I)V", 
+                resolver(_testBean1Type).resolveMethodEraseTypeParams(method.getSignature()));
 
         // test binary methods
         // the framework returns method signatures using "/" instead of "."
@@ -345,38 +351,36 @@
         // sanity: _binaryType must a JDT BinaryType impl for this test to
         // be valid
         assertTrue(_binaryType.isBinary());
-        assertEquals("()Ljava.lang.String;", TypeUtil.resolveMethodSignature(
-                _binaryType, _binaryType.getMethod("getStringProperty", null)
+        assertEquals("()Ljava.lang.String;", resolver(_testBean1Type).resolveMethodEraseTypeParams(
+                _binaryType.getMethod("getStringProperty", null)
                         .getSignature()));
 
-        assertEquals("(Ljava.lang.String;)V", TypeUtil.resolveMethodSignature(
-                _binaryType, _binaryType.getMethod("setStringProperty",
+        assertEquals("(Ljava.lang.String;)V", resolver(_testBean1Type).resolveMethodEraseTypeParams(
+                _binaryType.getMethod("setStringProperty",
                         new String[]
                         { "Ljava.lang.String;" }).getSignature()));
 
-        assertEquals("()I", TypeUtil.resolveMethodSignature(_binaryType,
+        assertEquals("()I", resolver(_testBean1Type).resolveMethodEraseTypeParams(
                 _binaryType.getMethod("getIntegerProperty", null)
                         .getSignature()));
 
-        assertEquals("(I)V", TypeUtil.resolveMethodSignature(_binaryType,
+        assertEquals("(I)V", resolver(_testBean1Type).resolveMethodEraseTypeParams(
                 _binaryType.getMethod("setIntegerProperty", new String[]
                 { "I" }).getSignature()));
 
-        assertEquals("()Lcom.test.BinaryPropertyAndMethodType;", TypeUtil
-                .resolveMethodSignature(_binaryType, _binaryType.getMethod(
+        assertEquals("()Lcom.test.BinaryPropertyAndMethodType;", 
+                resolver(_testBean1Type).resolveMethodEraseTypeParams(_binaryType.getMethod(
                         "getUserDefined", null).getSignature()));
 
-        assertEquals("(Lcom.test.BinaryPropertyAndMethodType;)V", TypeUtil
-                .resolveMethodSignature(_binaryType, _binaryType.getMethod(
+        assertEquals("(Lcom.test.BinaryPropertyAndMethodType;)V", 
+                resolver(_testBean1Type).resolveMethodEraseTypeParams(_binaryType.getMethod(
                         "setUserDefined", new String[]
                         { "Lcom.test.BinaryPropertyAndMethodType;" })
                         .getSignature()));
 
         assertEquals(
                 "(Ljava.lang.String;Lcom.test.BinaryPropertyAndMethodType;I)I",
-                TypeUtil
-                        .resolveMethodSignature(
-                                _binaryType,
+                resolver(_testBean1Type).resolveMethodEraseTypeParams(
                                 _binaryType
                                         .getMethod(
                                                 "methodWithMultipleArgs",
@@ -443,4 +447,21 @@
         assertTrue(TypeUtil.isEnumMember(type, "deadbeef"));
 
     }
+        
+    String resolveTypeSignature(IType type, String typeSignature, boolean erased)
+    {
+      if (erased)
+      {
+        return new JDTTypeResolver(type, EMPTY_SUPER_TYPES).resolveEraseTypeParams(typeSignature);
+      }
+      else
+      {
+        return new JDTTypeResolver(type, EMPTY_SUPER_TYPES).resolveKeepTypeParams(typeSignature);
+      }
+    }
+    
+    JDTTypeResolver resolver(IType type)
+    {
+      return new JDTTypeResolver(type, EMPTY_SUPER_TYPES);
+    }
 }
diff --git a/jsf/tests/org.eclipse.jst.jsf.designtime.tests/src/org/eclipse/jst/jsf/designtime/tests/TestDefaultPropertyResolver.java b/jsf/tests/org.eclipse.jst.jsf.designtime.tests/src/org/eclipse/jst/jsf/designtime/tests/TestDefaultPropertyResolver.java
index cdf0161..bb811d1 100644
--- a/jsf/tests/org.eclipse.jst.jsf.designtime.tests/src/org/eclipse/jst/jsf/designtime/tests/TestDefaultPropertyResolver.java
+++ b/jsf/tests/org.eclipse.jst.jsf.designtime.tests/src/org/eclipse/jst/jsf/designtime/tests/TestDefaultPropertyResolver.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2007, 2011 Oracle Corporation.
+ * Copyright (c) 2007, 2021 Oracle Corporation.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
  * which accompanies this distribution, and is available at
@@ -9,6 +9,7 @@
  *
  * Contributors:
  *    Cameron Bateman/Oracle - initial API and implementation
+ *    Reto Weiss/Axon Ivy    - Cache JDTBeanIntrospector
  * 
  ********************************************************************************/
 package org.eclipse.jst.jsf.designtime.tests;
@@ -17,8 +18,6 @@
 import java.util.HashMap;
 import java.util.Map;
 
-import junit.framework.TestCase;
-
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.jdt.core.IType;
@@ -44,6 +43,8 @@
 import org.eclipse.jst.jsf.test.util.TestFileResource;
 import org.eclipse.jst.jsf.test.util.WebProjectTestEnvironment;
 
+import junit.framework.TestCase;
+
 /**
  * Unit tests for the default property resolver
  * 
@@ -199,44 +200,43 @@
      */
     public void testSanity()
     {
-        JDTBeanIntrospector beanIntrospector =
-            new JDTBeanIntrospector(_testBean1Type);
+        JDTBeanIntrospector beanIntrospector = JDTBeanIntrospector.forType(_testBean1Type);
         Map<String, JDTBeanProperty> props = beanIntrospector.getProperties();
         assertEquals(NUM_PROPERTIES_TEST_BEAN_1, props.size());
         assertTrue(props.containsKey("stringProp1"));
         assertTrue(props.containsKey("booleanIsProp1"));
 
-        beanIntrospector = new JDTBeanIntrospector(_testMapBean1Type);
+        beanIntrospector = JDTBeanIntrospector.forType(_testMapBean1Type);
         props = beanIntrospector.getProperties();
         // has 2 as a bean: isEmpty -> empty property + class
         assertEquals(2, props.size());
 
-        beanIntrospector = new JDTBeanIntrospector(_testBean2Type);
+        beanIntrospector = JDTBeanIntrospector.forType(_testBean2Type);
         props = beanIntrospector.getProperties();
         // two props: myBean3, class
         assertEquals(2, props.size());
 
-        beanIntrospector = new JDTBeanIntrospector(_testBean3Type);
+        beanIntrospector = JDTBeanIntrospector.forType(_testBean3Type);
         props = beanIntrospector.getProperties();
         // two props: one of type TestBean2 + class
         assertEquals(2, props.size());
 
-        beanIntrospector = new JDTBeanIntrospector(_testBeanWithMapProp);
+        beanIntrospector = JDTBeanIntrospector.forType(_testBeanWithMapProp);
         props = beanIntrospector.getProperties();
         // two props: one of type Map + class
         assertEquals(2, props.size());
 
-        beanIntrospector = new JDTBeanIntrospector(_testListBeanType);
+        beanIntrospector = JDTBeanIntrospector.forType(_testListBeanType);
         props = beanIntrospector.getProperties();
         // includes isEmpty and class
         assertEquals(3, props.size());
 
-        beanIntrospector = new JDTBeanIntrospector(_testBeanWithListPropType);
+        beanIntrospector = JDTBeanIntrospector.forType(_testBeanWithListPropType);
         props = beanIntrospector.getProperties();
         assertEquals(2, props.size());
 
         beanIntrospector =
-            new JDTBeanIntrospector(_testBeanWithGenericProperties);
+            JDTBeanIntrospector.forType(_testBeanWithGenericProperties);
         props = beanIntrospector.getProperties();
         assertEquals(3, props.size());
     }