eclipse-ee4j / mojarra

Mojarra, a Jakarta Faces implementation
Other
160 stars 109 forks source link

ye-olde content interweaving issue #1571

Closed ren-zhijun-oracle closed 18 years ago

ren-zhijun-oracle commented 20 years ago

The age-old content interweaving problem. Consider this JSP fragment:

first text third text fifth text You'd expect first text second text third text fourth text fifth text What you get instead is second text fourth text first text third text fifth text This is well documented in Hans's article at <[http://www.onjava.com/pub/a/onjava/2004/06/09/jsf.html](http://www.onjava.com/pub/a/onjava/2004/06/09/jsf.html)>. #### Environment Operating System: All Platform: All #### Affected Versions [1.0]
ren-zhijun-oracle commented 6 years ago
ren-zhijun-oracle commented 20 years ago

@javaserverfaces Commented Reported by @edburns

ren-zhijun-oracle commented 20 years ago

@javaserverfaces Commented @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> around non-component-1, 3 and 5?

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 that wraps the body MR> content is added as a child before component2. Body content is MR> cleared before continuing evaluation. MR> d. non-component-3 is added to body content ... go to (a) ... MR> 4. doEndTag() is called. Any remaining characters in bodyContent are MR> wrapped in an implicit and added as a child of outerTag. MR> EVAL_PAGE is returned.

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.

ren-zhijun-oracle commented 20 years ago

@javaserverfaces Commented @edburns said: Created an attachment (id=3) unpack in jsf-api

ren-zhijun-oracle commented 20 years ago

@javaserverfaces Commented @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:

{ + 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

ren-zhijun-oracle commented 20 years ago

@javaserverfaces Commented @edburns said: Created an attachment (id=4) War showing the fix in action.

ren-zhijun-oracle commented 20 years ago

@javaserverfaces Commented @edburns said: Created an attachment (id=5) unpack in ../jsf-api. Snapshot. Supercedes previous 6.tar.gz

ren-zhijun-oracle commented 20 years ago

@javaserverfaces Commented @edburns said: Added Mark Roth to CC.

ren-zhijun-oracle commented 20 years ago

@javaserverfaces Commented @edburns said: Created an attachment (id=6) current snapshot.

ren-zhijun-oracle commented 20 years ago

@javaserverfaces Commented @edburns said: Created an attachment (id=8) sample showing that components programmatically added to the form do not get rendered.

ren-zhijun-oracle commented 20 years ago

@javaserverfaces Commented @edburns said: Created an attachment (id=9) Current snapshot, I think this is production ready.

ren-zhijun-oracle commented 20 years ago

@javaserverfaces Commented @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 that wraps the body MR> content is added as a child before component2. Body content is MR> cleared before continuing evaluation. MR> d. non-component-3 is added to body content ... go to (a) ... MR> 4. doEndTag() is called. Any remaining characters in bodyContent are MR> wrapped in an implicit and added as a child of outerTag. MR> EVAL_PAGE is returned.

The way it works out in practice is slightly different.

We modify UIComponentTag to:

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.

1 3 4 6 We modify UIComponentBodyTag to: * add the getUIOutputForBodyContentAndClearBodyContent() method * in doAfterBody(), do the same thing we do in doEndTag() in UIComponentTag. This is necessary to catch cases like this (case b): 7 9 11 There is a fair amount of complexity to support case a. I don't see a way around it. SECTION: API Changes M jsf-api/src/javax/faces/webapp/UIComponentBodyTag.java M jsf-api/src/javax/faces/webapp/UIComponentTag.java SECTION: RI Changes M jsf-ri/systest/build-tests.xml * add comment about how to update a golden file M jsf-ri/systest/web/golden/taglib/commandLink_multiform_test.txt M jsf-ri/systest/web/golden/taglib/commandLink_test.txt M jsf-ri/web/test/RenderResponse_correct M jsf-ri/web/test/TestRenderResponsePhase.jsp * test case changes. SECTION: Tools changes M jsf-tools/src/com/sun/faces/generate/HtmlTaglibGenerator.java SECTION: API Diffs Index: jsf-api/src/javax/faces/webapp/UIComponentBodyTag.java =================================================================== RCS file: /cvs/javaserverfaces-sources/jsf-api/src/javax/faces/webapp/UIComponentBodyTag.java,v retrieving revision 1.4 diff -u -r1.4 UIComponentBodyTag.java — jsf-api/src/javax/faces/webapp/UIComponentBodyTag.java 6 May 2004 16:12:24 -0000 1.4 +++ jsf-api/src/javax/faces/webapp/UIComponentBodyTag.java 22 Jul 2004 21:44:00 -0000 @@ -16,6 +16,9 @@ import javax.servlet.jsp.tagext.BodyTag; import javax.servlet.jsp.tagext.Tag; +import javax.faces.component.UIComponent; +import javax.faces.component.UIOutput; +import javax.faces.context.FacesContext; /** *

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.

+ */ + + + UIOutput getUIOutputForBodyContentAndClearBodyContent(UIComponent parentComponent) { + FacesContext context = getFacesContext(); + String bodyContentStr = null; + UIOutput verbatim = null; + + // if we have some template text inside of ourselves + if (null != bodyContent && + null != (bodyContentStr = bodyContent.getString())) { + + // don't wrap whitespace only strings. + int len = bodyContentStr.length(); + boolean isWhitespaceOnly = true; + for (int i = 0; i < len; i++) { + if (!Character.isWhitespace(bodyContentStr.charAt![](https://java.net/jira/images/icons/emoticons/information.gif))) { + isWhitespaceOnly = false; + break; + } + } + + if (!isWhitespaceOnly) { + String newId = context.getViewRoot().createUniqueId(); + + // if there is currently no verbatim component for this + // particular piece of template text + + // JSP_ISSUE: this check is necessary to correctly handle + // the case where we're re-displaying the same page several + // times. + if (null == parentComponent.findComponent(newId)) { + verbatim = new UIOutput(); + verbatim.setRendererType("javax.faces.Text"); + verbatim.getAttributes().put("escape", Boolean.FALSE); + verbatim.setTransient(true); + verbatim.setValue(bodyContentStr); + verbatim.setId(newId); + } + } + bodyContent.clearBody(); + } + return verbatim; + } // ------------------------------------------------------ Protected Methods Index: jsf-api/src/javax/faces/webapp/UIComponentTag.java =================================================================== RCS file: /cvs/javaserverfaces-sources/jsf-api/src/javax/faces/webapp/UIComponentTag.java,v retrieving revision 1.48 diff -u -r1.48 UIComponentTag.java — jsf-api/src/javax/faces/webapp/UIComponentTag.java 6 May 2004 16:12:24 -0000 1.48 +++ jsf-api/src/javax/faces/webapp/UIComponentTag.java 22 Jul 2004 21:44:07 -0000 @@ -31,6 +31,7 @@ import java.util.HashMap; import java.util.Map; +import javax.faces.component.UIOutput; /** *

{@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.

*