Closed javaserverfaces closed 18 years ago
Reported by @edburns
@edburns said: Mark Roth had this suggestion.
I implemented it straight up, but the content came out the same way. I'll attach a patch with what I had to do to get it mostly working.
MR> Hans says this is because "tag handlers can't tell whether a piece of MR> template text appears before or after a component action." I'm MR> interested in why this is the case in JSF.
MR> I have to refresh my memory and reading of the spec (classic tags are so
MR> complex), but why is it that we cannot add an implicit
MR> Perhaps an algorithm such as the following:
MR> 1. outerTag returns EVAL_BODY_BUFFERED from doStartTag()
MR> 2. setBodyContent() is called - outerTag stores reference to
MR> BodyContent instance
MR> 3. Body of outerTag is evaluated.
MR> a. non-component-1 is added to body content
MR> b. component2 is evaluated, and finds parent tag outerTag.
MR> component2 calls a outerTag.addChild() to add itself as a child
MR> c. outerTag.addChild() gets the current body content first. If it
MR> is not empty, an implicit
MR> I was personally interested in why this approach does not work. Eduardo MR> may have other approaches in mind for us to investigate as well.
@edburns said: Created an attachment (id=3) unpack in jsf-api
@edburns said: First, I found a bug in our HtmlBasicTaglibGenerator. You need to make it so PanelGridTag properly extends UIComponentBodyTag. This is tracked in <https://javaserverfaces.dev.java.net/issues/show_bug.cgi?id=7>. Please apply the patch in that bug before trying to run the fix for this one. I'll move agressivle to get a production quality fix for issue 7 in the tree ASAP.
I had to make the following questionable changes in UIComponentTag:
make this ivar protected
private UIComponent component = null;
make this method protected
private void addChild(UIComponent child) {
UIComponentBodyTag is a base class for all JSP custom @@ -121,6 +127,16 @@ }
/**
UIOutput
child out of*/
public int doEndTag() throws JspException
{ + turnBodyContentIntoAddedComponentIfNecessary(true); + return super.doEndTag(); + }
+
// ------------------------------------------------------ Protected Methods
@@ -143,5 +159,92 @@
}
{@link UIComponent}
{@link UIComponentTag}
s processing this request.
{ + turnBodyContentIntoAddedComponentIfNecessary(false); + super.addChild(child); + }
+
{ + return; + }
+
{ + children.add(verbatim); + }
{ + // JSP_PROBLEM: by the time this gets called, the + // component before which our verbatim component must + // reside has already been added to the tree. + // Therefore, we have to insert ourselves at index (end + // - 1). This feels like a hack. + int size = children.size(); + children.add(0 == size ? 0 : size - 1, verbatim); + }
+
}
This leaves me with one problem. For some reason, the "fifth text" appears both before the table, and in it. I don't want it to appear before the table. Any ideas on why this is so?
Here's what gets rendered after applying the fix (I hope the angle brackets show up right here in issuezilla).
fifth text
first text |
second text |
third text |
fourth text |
fifth text |
I just need to remove the leading "fifth text" and I'll have a functional prototype.
Ed
@edburns said: Created an attachment (id=4) War showing the fix in action.
@edburns said: Created an attachment (id=5) unpack in ../jsf-api. Snapshot. Supercedes previous 6.tar.gz
@edburns said: Added Mark Roth to CC.
@edburns said: Created an attachment (id=6) current snapshot.
@edburns said: Created an attachment (id=8) sample showing that components programmatically added to the form do not get rendered.
@edburns said: Created an attachment (id=9) Current snapshot, I think this is production ready.
@edburns said: Here's the latest change-bundle for your review:
This change-bundle implements Mark Roth's idea for solving the content-interweaving problem using only JSP 1.2 APIs, and without requiring any change to the JSF spec. It uses Adam Winer's idea for how to inject the logic into the existing system.
Here's Mark's idea:
MR> 1. outerTag returns EVAL_BODY_BUFFERED from doStartTag()
MR> 2. setBodyContent() is called - outerTag stores reference to
MR> BodyContent instance
MR> 3. Body of outerTag is evaluated.
MR> a. non-component-1 is added to body content
MR> b. component2 is evaluated, and finds parent tag outerTag.
MR> component2 calls a outerTag.addChild() to add itself as a child
MR> c. outerTag.addChild() gets the current body content first. If it
MR> is not empty, an implicit
The way it works out in practice is slightly different.
We modify UIComponentTag to:
when doStartTag() is called, we call this method:
// We need to do this here because this is as close as we can
// get to the place where we're adding the component to the tree
// that still executes on the postback.
addPrecedingTemplateTextToMyParentComponent(parentComponent,
parentTag);
If there really is template text to be added, and we're inside of a tag that cares about template text (that is, a UIComponentBodyTag), we call getUIOutputForBodyContentAndClearBodyContent() on that UIComponentBodyTag and add the UIOutput to the tree before adding our real component to the tree.
UIComponentBodyTag is a base class for all JSP custom @@ -40,9 +43,19 @@ /** * *
Handle the ending of the nested body content for this tag. The
* * default implementation simply calls getDoAfterBodyValue()
* * to retrieve the flag value to be returned.
Handle the ending of the nested body content for this tag.
+ * The default implementation will take any template text that
+ * exists between the previous {@link UIComponentTag} in the page
+ * and the end of this tag, and turn it into a {@link + * javax.faces.component.UIOutput} instance, which is added to the
+ * end of the child list for the component for this tag. Then, we
+ * call getDoAfterBodyValue()
to retrieve the flag
+ * value to be returned.
Note that our superclass {@link doEndTag} method does the same + * thing, but we must do it here to handle the case where there is + * template text between the previous {@link UIComponentTag} and our + * end tag.
* *It should be noted that if this method returns
* IterationTag.EVAL_BODY_AGAIN
, any nested
@@ -51,6 +64,14 @@
* @exception JspException if an error is encountered
*/
public int doAfterBody() throws JspException {
+ UIComponentTag parentTag = getParentUIComponentTag(pageContext);
+ UIComponent parentComponent = null;
+ if (parentTag != null) { + parentComponent = parentTag.getComponentInstance(); + addPrecedingTemplateTextToMyParentComponent(parentComponent, + parentTag); + parentTag.verbatimCount = 0; + }
return (getDoAfterBodyValue());
@@ -121,6 +142,58 @@
}
+ // --------------------------------------------- Package Private Methods
+
+ /**
+ *
Take our current body content and set it as the value of a
+ * transient
{@link javax.faces.component.UIOutput}
+ * component, with "escape" set to true
. If the body
+ * content consists of nothing but whitespace, no component is
+ * created. In all cases, the body content is cleared.
{@link UIComponentTag} is the base class for all JSP custom @@ -125,6 +126,15 @@ */ private List createdComponents = null; + /** + *
The number of fabricated verbatim components that are children + * of this tag. This is necessary to allow our {@link #getIndex} + * method to come out correctly.
+ * + */ + + int verbatimCount = 0; + /** *The List
of facet names created or located by nested
@@ -489,6 +499,19 @@
* with this tag (via the id
attribute), by following these
* steps.
Return the child list index to use for a new component to be created * * by a nested {@link UIComponentTag} instance.
+ *Return the child list index to use for a new component to be + * created by a nested {@link UIComponentTag} instance. Take into + * account any verbatim UIOutput components created as children of + * this component.
*/ private int getIndex() { if (createdComponents != null) { - return (createdComponents.size()); + return (verbatimCount + createdComponents.size()); } else { - return (0); + return (verbatimCount + 0); } } @@ -1271,6 +1311,56 @@ } createdFacets = null; + } + + + /** + *Find the second from top most {@link UIComponentBodyTag} that + * is our ancestor. If there is no such tag, this method is a + * no-op. If there is such a tag, call its {@link + * UIComponentBodyTag#getUIOutputForBodyContentAndClearBodyContent} + * method. If that method returns non-null, add it to the tree at + * the end of our child list and increment our parent tag's + * verbatimTagCount ivar.
+ * + */ + + void addPrecedingTemplateTextToMyParentComponent(UIComponent parentComponent, UIComponentTag parentTag) { + // if we are the viewRoot, there will never be any + // interweaving problems, so just return. + + if (parentComponent == context.getViewRoot()) { + return; + } + + // find the second from topmost UIComponentBodyTag in the page. + // This is necessary because the ViewTag is the topmost + // UIComponentBodyTag in the page, and we don't care about its + // template text. + + Tag tag = this; + UIComponentBodyTag + secondFromTopMostBodyTag = null, + topMostBodyTag = null; + + if (tag instanceof UIComponentBodyTag) { + topMostBodyTag = (UIComponentBodyTag) tag; + } + + while (null != (tag = tag.getParent())) { + if (tag instanceof UIComponentBodyTag) { + secondFromTopMostBodyTag = topMostBodyTag; + topMostBodyTag = (UIComponentBodyTag) tag; + } + } + + if (null != secondFromTopMostBodyTag) { + UIOutput output = secondFromTopMostBodyTag.getUIOutputForBodyContentAndClearBodyContent(parentComponent); + if (null != output) { + parentComponent.getChildren().add(output); + parentTag.verbatimCount++; + } + } } SECTION: RI Diffs Index: jsf-ri/systest/build-tests.xml =================================================================== RCS file: /cvs/javaserverfaces-sources/jsf-ri/systest/build-tests.xml,v retrieving revision 1.60 diff -u -r1.60 build-tests.xml — jsf-ri/systest/build-tests.xml 16 Jun 2004 19:48:05 -0000 1.60 +++ jsf-ri/systest/build-tests.xml 22 Jul 2004 21:44:16 -0000 @@ -190,6 +190,13 @@ request="$ {context.path}/hello.jsp" golden="${golden.path}/hello.txt" failonerror="${failonerror}"/> + +
The age-old content interweaving problem. Consider this JSP fragment: