[ 
https://issues.apache.org/jira/browse/MYFACES-3733?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13776260#comment-13776260
 ] 

Leonardo Uribe commented on MYFACES-3733:
-----------------------------------------

I have committed a solution for "Review how cc:insertChildren works" and the 
ordering problem. There was a problem caused by facelets algorithm. There are 
some points when it tries to remove/add the components on refreshing, but the 
"dynamic component top level" should not be moved because that part is up to 
the user. I found an issue with the optimization done in MYFACES-3774, but it 
was solved easily. The trick with cc:insertChildren is remove/add the inserted 
components to keep them in the same order they were inserted at the first time.

The only issue left in this part before close it is:

- Solve the refresh problem (allow c:if work in programmatic added sections).

The only way to do it correctly is with a new event, a flag that indicates when 
to do it and a visitTree call to traverse the full tree. I think with the 
optimizations done in the refreshing algorithm there is "enough space" to 
include the call and do not affect performance. In fact, the impact of the call 
is minimal, the trick is don't do it if is not necessary. 

I'll let the possible optimization for later, there is no need to worry about 
that for now. With the previous issue solved I hope to close this one as fixed.
                
> Implement vdl.createComponent(...)
> ----------------------------------
>
>                 Key: MYFACES-3733
>                 URL: https://issues.apache.org/jira/browse/MYFACES-3733
>             Project: MyFaces Core
>          Issue Type: Task
>          Components: JSR-344
>            Reporter: Leonardo Uribe
>
> This is a difficult issue to do in JSF 2.2 . I have spent a long time to 
> solve this one, and given the complexity involved and since there is no 
> documentation anywhere about how this should work, I'll let the required 
> explanation here.
> The idea is allow to include generated vdl fragments into pages 
> programatically. This includes normal components, composite components or 
> just fragments of markup. The method signature is this:
> public UIComponent createComponent(FacesContext context, String taglibURI, 
> String tagName, Map<String,Object> attributes)
> Some valid examples of this are:
> // Normal component
> UIComponent component = vdl.createComponent(facesContext, 
>     "http://java.sun.com/jsf/html";, 
>     "outputText", attributes);
> // Composite component
> UIComponent component = vdl.createComponent(facesContext, 
>     "http://java.sun.com/jsf/composite/testComposite";, 
>     "dynComp_1", attributes);
> // Dynamic include
> Map<String, Object> attributes = new HashMap<String, Object>();
> attributes.put("src", "/addSimpleIncludeVDL_1_1.xhtml");
> UIComponent component = vdl.createComponent(facesContext, 
>     "http://java.sun.com/jsf/facelets";, 
>     "include", attributes);
> The javadoc does not suggest the dynamic include is valid, but I think users 
> expect these kind of stuff work. 
> Theoretically it sounds like something easy to do, but unfortunately it is 
> not. The reasons why this is so are:
> - Facelets algorithm wraps html markup into UILeaf instances, which is a 
> special "transient" component. UILeaf instances are never saved or restored 
> from the component tree, but in some points of the algorithm (restore view 
> and before render response when vdl.buildView() is called) the component tree 
> is updated, adding or removing UILeaf instances.
> - Facelets has an algorithm that require id generation to be stable, 
> otherwise a duplicate id exception may arise. A lot of effort has been done 
> to organize this part, and the current solution works very well. But in this 
> case, we need to generate unique ids that can be refreshed somehow.
> - Facelets algorithm has an special logic to deal with dynamic sections like 
> the ones generated by c:if or 
> ui:include src="#{...}" . Add facelets sections programatically could make 
> this algorithm fail, removing sections that should be.
> - Facelets PSS algorithm needs to be taken into account too. The listener 
> that is used to register programmatic changes on the tree in 
> DefaultFaceletsStateManagementStrategy uses  ComponentSupport.MARK_CREATED to 
> identify which component belongs to the component tree and which one was 
> added by outside. Add facelets sections programatically could make this 
> algorithm fail, because it could assume some sections of the tree does not 
> need to be saved fully, even if that's not true.
> The issue in the spec is this:
> https://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-611
> At start the idea was to export FaceletFactory directly, but I told to the EG 
> that it was a bad idea by multiple reasons (That's a Pandora's Box). See:
> https://java.net/projects/javaserverfaces-spec-public/lists/jsr344-experts/archive/2012-11/message/91
> This previous message is useful too:
> https://java.net/projects/javaserverfaces-spec-public/lists/jsr344-experts/archive/2012-06/message/18
> After thinking and trying different strategies to overcome this issue, I 
> finally found the following solution:
> - Use the compiler for generate a custom Facelet "inline" or "on the fly". It 
> is not necessary to create an
> xml document and then parse it, just generate the Tag class and pass it to 
> the compiler to generate an
> Abstract Syntax Tree (AST), with the hierarchy of facelet TagHandler 
> instances.
> - To solve the issue with UILeaf instances, the best is create a stateful 
> ComponentSystemEventListener that on restore view phase it compiles the 
> custom Facelet and apply it over the fragment. The ideal and only event to 
> attach the listener is PostRestoreStateEvent, but we need to add the code in 
> UIComponent.processEvent().
> - In the case of a ui:include, if multiple components are returned, all of 
> them are grouped into a single
> UIPanel. If the code returns one component, it returns that component.
> - If the code generates a branch, or in other words, multiple nested 
> components, it should attach the 
> listener to deal with UILeaf instances, if it just generates one component do 
> not do that because it is
> not necessary.
> - To solve the issue with the ids, just call UIViewRoot.createUniqueId() and 
> use the generated value to derive unique facelets ids. The new algorithm that 
> generate ids is very flexible and it will support this case. This base key 
> should be saved in the state so the same ids are generated for the same 
> fragment.
> - Support composite components needs special treatment. The idea is support 
> something like this:
> UIComponent component = vdl.createComponent(facesContext, 
>     "http://java.sun.com/jsf/composite/testComposite";, 
>     "dynComp_1", attributes);
>     
> // .... add children / facets to the algorithm
> someComponentInTheView.getChildren().add(component);
> In this case the "processing" of the composite component content must be done 
> only when the component is added to the view. The idea is vdl.createComponent 
> only create the root component class, and then use a listener attached to 
> PostAddToViewEvent to process the content. We need to modify the algorithm, 
> because in this case children/facets are created programatically and not 
> using facelets algorithm. The idea is add an extra facelet in the compiler to 
> detect when the result is a composite component. The listener attached to 
> PostAddToViewEvent must be done in a way that only works on the first 
> addition to PostAddToViewEvent.
> - If the call to vdl.createComponent() occur when there is an active 
> FaceletCompositionContext instance, reuse that instance doing the necessary 
> changes in the context, otherwise instantiate a clean context.
> - Facelets PSS algorithm will work just fine as long as the returned 
> component does not have ComponentSupport.MARK_CREATED set when the view is 
> refreshed, saved or restored. It is enough to just use the component 
> attribute map.
> - The two base cases to test are:
>    * Create components programatically inside a "binding" method.
>    * Create components programatically in PreRenderViewEvent or in the 
> Renderer.
>   The difference is the "binding" occur when there is a 
> FaceletCompositionContext instance active, but in the other cases there is no 
> active instance.
> Comply with all previous requirements can be difficult, but it is very 
> important, otherwise the algorithm will not be stable enough.

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators
For more information on JIRA, see: http://www.atlassian.com/software/jira

Reply via email to