Bug 151181 - Support for multiple EL expressions in a attribute value

Content Assist and validation now supports multiple EL expressions in
same attribute value. E.g., <h:outputLabel
value="Hello #{person.surname} #{person.name}"/>

Content Assist now suports EL expressions outside of tags (in text
content). E.g., <h1>Person #{person.name}</h1>
diff --git a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/validation/internal/strategy/AttributeValidatingStrategy.java b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/validation/internal/strategy/AttributeValidatingStrategy.java
index 0ef896d..fe00fe0 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/validation/internal/strategy/AttributeValidatingStrategy.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/validation/internal/strategy/AttributeValidatingStrategy.java
@@ -9,6 +9,7 @@
  * 
  * Contributors:
  *     Oracle Corporation - initial API and implementation
+ *     Reto Weiss Axon Ivy AG - Support for multiple EL expressions in attribute value and outside of tags
  *******************************************************************************/
 package org.eclipse.jst.jsf.validation.internal.strategy;
 
@@ -195,97 +196,16 @@
             final Region2AttrAdapter attrAdapter,
             final IStructuredDocumentContext context)
     {
-        int offsetOfFirstEL = -1;
         final String attrValue = attrAdapter.getValue();
-
-        // TODO: should find and validate all
-        offsetOfFirstEL = attrValue.indexOf('#');
-
-        if (offsetOfFirstEL != -1 && offsetOfFirstEL < attrValue.length() - 1
-                && attrValue.charAt(offsetOfFirstEL + 1) == '{')
-        {
-            offsetOfFirstEL += 2;
-        }
-        else
-        {
-            offsetOfFirstEL = -1;
-        }
-
-        final XMLViewDefnAdapter adapter = DTAppManagerUtil
-        .getXMLViewDefnAdapter(context);
-
+        final XMLViewDefnAdapter adapter = DTAppManagerUtil.getXMLViewDefnAdapter(context);
         boolean isEL = false;
-        if (adapter != null && offsetOfFirstEL != -1)
+        if (adapter != null)
         {
-            try
+            int offsetOfEL = findElStartOffset(attrValue, 0);
+            while(offsetOfEL >= 0) 
             {
-                // use the attribute's context plus the offset into the
-                // whole attribute value to find where we think the el
-                // expression starts. Add one since the attribute value
-                // string returned by attrAdapter will have the value quotes
-                // removed, but the region offsets include the quotes.
-                IStructuredDocumentContext elContext = IStructuredDocumentContextFactory.INSTANCE
-                .getContext(context.getStructuredDocument(), context
-                        .getDocumentPosition()
-                        + offsetOfFirstEL + 1);
-                final DTELExpression elExpression = adapter
-                .getELExpression(elContext);
-                if (elExpression != null)
-                {
-                    final String elText = elExpression.getText();
-
-                    if (DEBUG)
-                    {
-                        System.out.println(addDebugSpacer(3) + "EL attrVal= " //$NON-NLS-1$
-                                + elText);
-                    }
-
-                    elContext = elExpression.getDocumentContext();
-                    // EL validation is user configurable because
-                    // it can be computationally costly.
-                    if (_validationContext.shouldValidateEL())
-                    {
-                        // also, skip the validation if the expression is empty
-                        // or only whitespace, since the parser doesn't handle
-                        // it
-                        // anyway.
-                        if ("".equals(elText.trim())) //$NON-NLS-1$
-                        {
-                            final int offset = elContext.getDocumentPosition() - 1;
-                            final int length = elText.length() + 2;
-                            final Diagnostic diagnostic = _validationContext
-                            .getDiagnosticFactory()
-                            .create_EMPTY_EL_EXPRESSION();
-                            // detected empty EL expression
-                            if (_validationContext.shouldValidateEL())
-                            {
-                                _validationContext.getReporter().report(
-                                        diagnostic, offset, length);
-                            }
-                        }
-                        else
-                        {
-                            final List elVals = MetaDataEnabledProcessingFactory
-                            .getInstance()
-                            .getAttributeValueRuntimeTypeFeatureProcessors(
-                                    IValidELValues.class,
-                                    elContext,
-                                    attrAdapter
-                                    .getAttributeIdentifier());
-                            final String safeELText = elText.replaceAll(
-                                    "[\n\r\t]", " "); //$NON-NLS-1$ //$NON-NLS-2$
-                            validateELExpression(context, elContext, elVals,
-                                    elementAdapter, attrAdapter, safeELText);
-                            isEL = true;
-                        }
-                    } else {
-                        isEL = true;
-                    }
-                }
-            }
-            catch (final ViewHandlerException e)
-            {
-                // fall through to return false
+                isEL |= validateEl(elementAdapter, attrAdapter, context, offsetOfEL, adapter);
+                offsetOfEL = findElStartOffset(attrValue, offsetOfEL);
             }
         }
 
@@ -296,6 +216,94 @@
         return isEL || isEL2;
     }
 
+    private int findElStartOffset(String attrValue, int fromOffset)
+    {
+        int offsetOfEL = attrValue.indexOf('#', fromOffset);
+        if (offsetOfEL != -1 && offsetOfEL < attrValue.length() - 1
+                && attrValue.charAt(offsetOfEL + 1) == '{')
+        {
+            return offsetOfEL + 2;
+        }
+        return -1;
+    }
+
+    private boolean validateEl(final Region2ElementAdapter elementAdapter,
+            final Region2AttrAdapter attrAdapter, final IStructuredDocumentContext context,
+            int offsetOfFirstEL, final XMLViewDefnAdapter adapter)
+    {
+      try
+      {
+          // use the attribute's context plus the offset into the
+          // whole attribute value to find where we think the el
+          // expression starts. Add one since the attribute value
+          // string returned by attrAdapter will have the value quotes
+          // removed, but the region offsets include the quotes.
+          IStructuredDocumentContext elContext = IStructuredDocumentContextFactory.INSTANCE
+          .getContext(context.getStructuredDocument(), context
+                  .getDocumentPosition()
+                  + offsetOfFirstEL + 1);
+          final DTELExpression elExpression = adapter
+          .getELExpression(elContext);
+          if (elExpression != null)
+          {
+              final String elText = elExpression.getText();
+
+              if (DEBUG)
+              {
+                  System.out.println(addDebugSpacer(3) + "EL attrVal= " //$NON-NLS-1$
+                          + elText);
+              }
+
+              elContext = elExpression.getDocumentContext();
+              // EL validation is user configurable because
+              // it can be computationally costly.
+              if (_validationContext.shouldValidateEL())
+              {
+                  // also, skip the validation if the expression is empty
+                  // or only whitespace, since the parser doesn't handle
+                  // it
+                  // anyway.
+                  if ("".equals(elText.trim())) //$NON-NLS-1$
+                  {
+                      final int offset = elContext.getDocumentPosition() - 1;
+                      final int length = elText.length() + 2;
+                      final Diagnostic diagnostic = _validationContext
+                      .getDiagnosticFactory()
+                      .create_EMPTY_EL_EXPRESSION();
+                      // detected empty EL expression
+                      if (_validationContext.shouldValidateEL())
+                      {
+                          _validationContext.getReporter().report(
+                                  diagnostic, offset, length);
+                      }
+                  }
+                  else
+                  {
+                      final List elVals = MetaDataEnabledProcessingFactory
+                      .getInstance()
+                      .getAttributeValueRuntimeTypeFeatureProcessors(
+                              IValidELValues.class,
+                              elContext,
+                              attrAdapter
+                              .getAttributeIdentifier());
+                      final String safeELText = elText.replaceAll(
+                              "[\n\r\t]", " "); //$NON-NLS-1$ //$NON-NLS-2$
+                      validateELExpression(context, elContext, elVals,
+                              elementAdapter, attrAdapter, safeELText);
+                      return true;
+                  }
+              } else {
+                  return true;
+              }
+          }
+      }
+      catch (final ViewHandlerException e)
+      {
+          // fall through to return false
+      }
+      return false;
+    }
+
     /**
      * Checks the region to see if it contains an EL attribute value. If it
      * does, validates it
@@ -371,15 +379,15 @@
         final CompositeType exprType = elValidator.getExpressionType();
         if (exprType != null)
         {
-        	// Ignore the expression whose last two segments are of types Object.
-        	final CompositeType boxedType = TypeTransformer
-            	.transformBoxPrimitives(exprType);
-        	final String[] testSignatures = boxedType.getSignatures();
-        	if (testSignatures.length > 0 && TypeConstants.TYPE_JAVAOBJECT.equals(testSignatures[0])) 
-        	{
-        		if (elText.indexOf('.') != -1) 
-        		{
-        			String elText2 = elText.substring(0, elText.lastIndexOf('.'));
+          // Ignore the expression whose last two segments are of types Object.
+          final CompositeType boxedType = TypeTransformer
+              .transformBoxPrimitives(exprType);
+          final String[] testSignatures = boxedType.getSignatures();
+          if (testSignatures.length > 0 && TypeConstants.TYPE_JAVAOBJECT.equals(testSignatures[0])) 
+          {
+            if (elText.indexOf('.') != -1) 
+            {
+              String elText2 = elText.substring(0, elText.lastIndexOf('.'));
                     final ELExpressionValidator elValidator2 = new ELExpressionValidator(
                             elContext, elText2, _validationContext
                                     .getSymbolResolverFactory(), _validationContext
@@ -387,15 +395,15 @@
                     elValidator2.validateXMLNode();
 
                     final CompositeType exprType2 = elValidator.getExpressionType();
-                	final CompositeType boxedType2 = TypeTransformer.transformBoxPrimitives(exprType2);
-                	final String[] testSignatures2 = boxedType2.getSignatures();
-                	if (testSignatures2.length > 0 && TypeConstants.TYPE_JAVAOBJECT.equals(testSignatures2[0])) 
-                	{
-                		return;
-                	}
-        		}
-        	}
-        	
+                  final CompositeType boxedType2 = TypeTransformer.transformBoxPrimitives(exprType2);
+                  final String[] testSignatures2 = boxedType2.getSignatures();
+                  if (testSignatures2.length > 0 && TypeConstants.TYPE_JAVAOBJECT.equals(testSignatures2[0])) 
+                  {
+                    return;
+                  }
+            }
+          }
+          
             for (final Iterator it = elVals.iterator(); it.hasNext();)
             {
                 final IValidELValues elval = (IValidELValues) it.next();
@@ -412,7 +420,7 @@
                                 expectedType, exprType, elementAdapter,
                                 attrAdapter);
                         if (ELValidationUtil.isProjectEL22(_validationContext.getFile())) {
-                        	expectedType = addEL22Alternatives(expectedType);
+                          expectedType = addEL22Alternatives(expectedType);
                         }
                         status = _typeComparator.calculateTypeCompatibility(
                                 expectedType, exprType);
@@ -435,33 +443,33 @@
     }
 
     private CompositeType addEL22Alternatives(final CompositeType expectedType) {
-    	CompositeType type = expectedType;
-    	if (expectedType != null) {
-    		final int assignmentTypeMask = expectedType.getAssignmentTypeMask();
-    		if ((assignmentTypeMask & IAssignable.ASSIGNMENT_TYPE_RHS) != 0 ||
-    				assignmentTypeMask == IAssignable.ASSIGNMENT_TYPE_NONE) {
-        		int assignmentType = IAssignable.ASSIGNMENT_TYPE_NONE;
-        		if ((assignmentTypeMask & IAssignable.ASSIGNMENT_TYPE_RHS) != 0) {
-        			assignmentType = IAssignable.ASSIGNMENT_TYPE_RHS;
-        		}
-        		final List<String> signatures = new ArrayList<String>();
-        		for (final String signature: expectedType.getSignatures()) {
-        			signatures.add(signature);
-            		final List<String> methodSignatures = new ArrayList<String>();
-            		for (int i = 0; i < 20; i++) {
-            			final String methodSignature = Signature.createMethodSignature(
-            					methodSignatures.toArray(new String[i]),
-            					Signature.getElementType(signature)); 
-            			signatures.add(methodSignature);
-            			methodSignatures.add("Ljava.lang.String;"); //$NON-NLS-1$
-            		}
-        		}
-        		type = new CompositeType(
-        				signatures.toArray(new String[signatures.size()]),
-        				assignmentType);
-    		}
-    	}
-    	return type;
+      CompositeType type = expectedType;
+      if (expectedType != null) {
+        final int assignmentTypeMask = expectedType.getAssignmentTypeMask();
+        if ((assignmentTypeMask & IAssignable.ASSIGNMENT_TYPE_RHS) != 0 ||
+            assignmentTypeMask == IAssignable.ASSIGNMENT_TYPE_NONE) {
+            int assignmentType = IAssignable.ASSIGNMENT_TYPE_NONE;
+            if ((assignmentTypeMask & IAssignable.ASSIGNMENT_TYPE_RHS) != 0) {
+              assignmentType = IAssignable.ASSIGNMENT_TYPE_RHS;
+            }
+            final List<String> signatures = new ArrayList<String>();
+            for (final String signature: expectedType.getSignatures()) {
+              signatures.add(signature);
+                final List<String> methodSignatures = new ArrayList<String>();
+                for (int i = 0; i < 20; i++) {
+                  final String methodSignature = Signature.createMethodSignature(
+                      methodSignatures.toArray(new String[i]),
+                      Signature.getElementType(signature)); 
+                  signatures.add(methodSignature);
+                  methodSignatures.add("Ljava.lang.String;"); //$NON-NLS-1$
+                }
+            }
+            type = new CompositeType(
+                signatures.toArray(new String[signatures.size()]),
+                assignmentType);
+        }
+      }
+      return type;
     }
 
     private boolean disableAlternativeTypes()
@@ -484,7 +492,7 @@
     }
     
     private boolean hasProperty(final String key) {
-    	 String res = System.getProperty(key);
+       String res = System.getProperty(key);
          if (res == null) {
              //check env var also
              res = System.getenv(key);
@@ -512,7 +520,7 @@
                 .getDocumentContext();
         final DTUIViewRoot viewRoot = _validationContext.getViewRootHandle().getCachedViewRoot();
         final IAdaptable serviceAdaptable = viewRoot.getServices();
-        final XMLViewObjectMappingService mappingService = (XMLViewObjectMappingService) serviceAdaptable
+        final XMLViewObjectMappingService mappingService = serviceAdaptable
                 .getAdapter(XMLViewObjectMappingService.class);
         if (mappingService != null)
         {
diff --git a/jsf/plugins/org.eclipse.jst.jsf.facelet.core/src/org/eclipse/jst/jsf/facelet/core/internal/util/ViewUtil.java b/jsf/plugins/org.eclipse.jst.jsf.facelet.core/src/org/eclipse/jst/jsf/facelet/core/internal/util/ViewUtil.java
index a58d550..fb2a1de 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.facelet.core/src/org/eclipse/jst/jsf/facelet/core/internal/util/ViewUtil.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.facelet.core/src/org/eclipse/jst/jsf/facelet/core/internal/util/ViewUtil.java
@@ -9,6 +9,7 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Reto Weiss Axon Ivy AG - Support for multiple EL expressions in attribute value and outside of tags
  *******************************************************************************/
 package org.eclipse.jst.jsf.facelet.core.internal.util;
 
@@ -282,7 +283,7 @@
      * @return the el expression from the context or null if none found.
      */
     public static DTELExpression getDTELExpression(IModelContext genericContext) {
-        final IStructuredDocumentContext context = (IStructuredDocumentContext) genericContext
+        final IStructuredDocumentContext context = genericContext
                 .getAdapter(IStructuredDocumentContext.class);
 
 //        if (context == null)
@@ -290,42 +291,64 @@
 //            throw new ViewHandlerException(Cause.EL_NOT_FOUND);
 //        }
 
-        final ITextRegionContextResolver resolver =
-            IStructuredDocumentContextResolverFactory.INSTANCE
-            .getTextRegionResolver(context);
-
-        if (resolver != null)
-        {
-            final String regionType = resolver.getRegionType();
-            int startOffset = resolver.getStartOffset();
-            int relativeOffset = context.getDocumentPosition() - startOffset;
-            
-            if (DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE.equals(regionType))
-            {
-                final String attributeText = resolver.getRegionText();
-                int elOpenIdx = attributeText.indexOf("#"); //$NON-NLS-1$
-                
-                if (elOpenIdx >= 0 && elOpenIdx < relativeOffset
-                        && elOpenIdx+1 < attributeText.length()
-                        && attributeText.charAt(elOpenIdx+1) == '{')
-                {
-                    // we may have a hit
-                    int elCloseIdx = attributeText.indexOf('}', elOpenIdx+1);
-                    if (elCloseIdx  != -1)
-                    {
-                        final IStructuredDocumentContext elContext =
-                            IStructuredDocumentContextFactory.INSTANCE.getContext(
-                                    context.getStructuredDocument(), resolver
-                                    .getStartOffset()+elOpenIdx+2);
-                        final String elText = attributeText.substring(
-                                elOpenIdx + 2, elCloseIdx);
-                        return new DTELExpression(elContext, elText);
-                    }
-                }
-            }
-        }
-
-        return null;
+        return getDTELExpression(context);
     }
 
+    /**
+     * @param context
+     * @return the el expression from the context or null if none found.
+     */
+    public static DTELExpression getDTELExpression(final IStructuredDocumentContext context)
+    {
+        ITextRegionContextResolver resolver = IStructuredDocumentContextResolverFactory.INSTANCE.getTextRegionResolver(context);
+        if (resolver == null) 
+        {
+            return null;
+        }
+        String regionType = resolver.getRegionType();
+        int startOffset = resolver.getStartOffset();
+        int relativeOffset = context.getDocumentPosition() - startOffset;
+        if (!DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE.equals(regionType) &&
+            !DOMRegionContext.XML_CONTENT.equals(regionType))
+        {
+            return null;
+        }
+        final String text = resolver.getRegionText();
+        if (relativeOffset >= text.length()) 
+        {
+            return null;
+        }
+        int elOpenIdx = findOpenElIndex(text, relativeOffset);
+        if (elOpenIdx < 0) 
+        {
+            return null;
+        }
+        int elCloseIdx = text.indexOf('}', elOpenIdx);
+        if (elCloseIdx  == -1)
+        {
+            return null;
+        }
+        final IStructuredDocumentContext elContext = IStructuredDocumentContextFactory.INSTANCE.getContext(
+                    context.getStructuredDocument(), 
+                    resolver.getStartOffset()+elOpenIdx);
+        final String elText = text.substring(elOpenIdx, elCloseIdx);
+        return new DTELExpression(elContext, elText);
+    }
+
+    private static int findOpenElIndex(String attributeText, int relativeOffset)
+    {
+      for (int pos = relativeOffset - 1; pos > 0; pos--) 
+      {
+        char ch = attributeText.charAt(pos);
+        if (ch == '{' && attributeText.charAt(pos - 1) == '#') 
+        {
+          return pos + 1;
+        } 
+        else if (ch == '}') 
+        {
+          return -1;
+        }
+      }
+      return -1;
+    }
 }
diff --git a/jsf/plugins/org.eclipse.jst.jsf.facelet.core/src/org/eclipse/jst/jsf/facelet/core/internal/view/FaceletViewDefnAdapter.java b/jsf/plugins/org.eclipse.jst.jsf.facelet.core/src/org/eclipse/jst/jsf/facelet/core/internal/view/FaceletViewDefnAdapter.java
index ea5bac1..ccba35f 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.facelet.core/src/org/eclipse/jst/jsf/facelet/core/internal/view/FaceletViewDefnAdapter.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.facelet.core/src/org/eclipse/jst/jsf/facelet/core/internal/view/FaceletViewDefnAdapter.java
@@ -9,6 +9,7 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Reto Weiss Axon Ivy AG - Support for multiple EL expressions in attribute value and outside of tags
  *******************************************************************************/
 package org.eclipse.jst.jsf.facelet.core.internal.view;
 
@@ -23,7 +24,6 @@
 import org.eclipse.jst.jsf.context.IModelContext;
 import org.eclipse.jst.jsf.context.resolver.structureddocument.IDOMContextResolver;
 import org.eclipse.jst.jsf.context.resolver.structureddocument.IStructuredDocumentContextResolverFactory;
-import org.eclipse.jst.jsf.context.resolver.structureddocument.internal.ITextRegionContextResolver;
 import org.eclipse.jst.jsf.context.structureddocument.IStructuredDocumentContext;
 import org.eclipse.jst.jsf.context.structureddocument.IStructuredDocumentContextFactory;
 import org.eclipse.jst.jsf.core.internal.JSFCorePlugin;
@@ -32,9 +32,9 @@
 import org.eclipse.jst.jsf.designtime.internal.view.IDTViewHandler.ViewHandlerException.Cause;
 import org.eclipse.jst.jsf.designtime.internal.view.TaglibBasedViewDefnAdapter;
 import org.eclipse.jst.jsf.designtime.internal.view.model.ITagRegistry;
+import org.eclipse.jst.jsf.facelet.core.internal.util.ViewUtil;
 import org.eclipse.wst.sse.core.StructuredModelManager;
 import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
-import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
 import org.w3c.dom.Attr;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
@@ -95,7 +95,7 @@
     public DTELExpression getELExpression(final IModelContext genericContext)
             throws ViewHandlerException
     {
-        final IStructuredDocumentContext context = (IStructuredDocumentContext) genericContext
+        final IStructuredDocumentContext context = genericContext
                 .getAdapter(IStructuredDocumentContext.class);
 
         if (context == null)
@@ -103,42 +103,7 @@
             throw new ViewHandlerException(Cause.EL_NOT_FOUND);
         }
 
-        final ITextRegionContextResolver resolver =
-            IStructuredDocumentContextResolverFactory.INSTANCE
-            .getTextRegionResolver(context);
-
-        if (resolver != null)
-        {
-            final String regionType = resolver.getRegionType();
-            int startOffset = resolver.getStartOffset();
-            int relativeOffset = context.getDocumentPosition() - startOffset;
-            
-            if (DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE.equals(regionType))
-            {
-                final String attributeText = resolver.getRegionText();
-                int elOpenIdx = attributeText.indexOf("#"); //$NON-NLS-1$
-                
-                if (elOpenIdx >= 0 && elOpenIdx < relativeOffset
-                        && elOpenIdx+1 < attributeText.length()
-                        && attributeText.charAt(elOpenIdx+1) == '{')
-                {
-                    // we may have a hit
-                    int elCloseIdx = attributeText.indexOf('}', elOpenIdx+1);
-                    if (elCloseIdx  != -1)
-                    {
-                        final IStructuredDocumentContext elContext =
-                            IStructuredDocumentContextFactory.INSTANCE.getContext(
-                                    context.getStructuredDocument(), resolver
-                                    .getStartOffset()+elOpenIdx+2);
-                        final String elText = attributeText.substring(
-                                elOpenIdx + 2, elCloseIdx);
-                        return new DTELExpression(elContext, elText);
-                    }
-                }
-            }
-        }
-
-        return null;
+        return ViewUtil.getDTELExpression(context);
     }
 
     @Override
diff --git a/jsf/plugins/org.eclipse.jst.jsf.facelet.ui/src/org/eclipse/jst/jsf/facelet/ui/internal/contentassist/XHTMLContentAssistProcessor.java b/jsf/plugins/org.eclipse.jst.jsf.facelet.ui/src/org/eclipse/jst/jsf/facelet/ui/internal/contentassist/XHTMLContentAssistProcessor.java
index 91557b0..e668e45 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.facelet.ui/src/org/eclipse/jst/jsf/facelet/ui/internal/contentassist/XHTMLContentAssistProcessor.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.facelet.ui/src/org/eclipse/jst/jsf/facelet/ui/internal/contentassist/XHTMLContentAssistProcessor.java
@@ -9,6 +9,7 @@
  *
  * Contributors:
  *     IBM Corporation - initial API and implementation
+ *     Reto Weiss Axon Ivy AG - Support for multiple EL expressions in attribute value and outside of tags
  *******************************************************************************/
 package org.eclipse.jst.jsf.facelet.ui.internal.contentassist;
 
@@ -287,4 +288,55 @@
         }
         return null;
     }
+    
+    @Override
+    protected ContentAssistRequest computeContentProposals(int documentPosition, String matchString,
+          ITextRegion completionRegion, IDOMNode nodeAtOffset, IDOMNode node)
+    {
+      if (isInElExpression(nodeAtOffset.getTextContent(), documentPosition-nodeAtOffset.getStartOffset()))
+      {
+        return newContentAssistRequest(nodeAtOffset, node, getStructuredDocumentRegion(documentPosition), completionRegion, documentPosition, 0, matchString);
+      }
+      return super.computeContentProposals(documentPosition, matchString, completionRegion, nodeAtOffset, node);
+    }
+
+    private boolean isInElExpression(String textContent, int position)
+    {
+      return hasStartTagBefore(textContent, position) && hasEndTagAfter(textContent, position);
+    }
+
+    private boolean hasStartTagBefore(String textContent, int position)
+    {
+      for (int pos = position - 1; pos > 0; pos--) 
+      {
+        char ch = textContent.charAt(pos);
+        if (ch == '}') 
+        {
+          return false;
+        }
+        if (ch == '{' && textContent.charAt(pos - 1) == '#') 
+        {
+          return true;
+        }
+      }
+      return false;
+    }
+    
+    private boolean hasEndTagAfter(String textContent, int position)
+    {
+      for (int pos = position; pos < textContent.length(); pos++) 
+      {
+        char ch = textContent.charAt(pos);
+        if (ch == '}') 
+        {
+          return true;
+        }
+        if (ch == '#' && textContent.charAt(pos + 1) == '{') 
+        {
+          return false;
+        }
+      }
+      return false;
+    }
+
 }
\ No newline at end of file