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