View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.custom.datascroller;
20  
21  import java.io.IOException;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Map;
25  
26  import javax.faces.application.Application;
27  import javax.faces.component.UIComponent;
28  import javax.faces.component.UIOutput;
29  import javax.faces.component.UIParameter;
30  import javax.faces.component.UIViewRoot;
31  import javax.faces.component.behavior.ClientBehavior;
32  import javax.faces.component.html.HtmlOutputText;
33  import javax.faces.context.FacesContext;
34  import javax.faces.context.ResponseWriter;
35  
36  import org.apache.myfaces.component.html.ext.HtmlCommandLink;
37  import org.apache.myfaces.shared_tomahawk.renderkit.JSFAttr;
38  import org.apache.myfaces.shared_tomahawk.renderkit.RendererUtils;
39  import org.apache.myfaces.shared_tomahawk.renderkit.html.HTML;
40  import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRenderer;
41  import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils;
42  
43  /**
44   * Renderer for the HtmlDataScroller component.
45   * 
46   * @JSFRenderer
47   *   renderKitId = "HTML_BASIC" 
48   *   family = "javax.faces.Panel"
49   *   type = "org.apache.myfaces.DataScroller"
50   * 
51   * @author Thomas Spiegl (latest modification by $Author: lu4242 $)
52   * @version $Revision: 987302 $ $Date: 2010-08-19 15:40:05 -0500 (Jue, 19 Ago 2010) $
53   */
54  public class HtmlDataScrollerRenderer extends HtmlRenderer
55  {
56      public static final String RENDERER_TYPE = "org.apache.myfaces.DataScroller";
57  
58      protected static final String PAGE_NAVIGATION = "idx".intern();
59  
60      public boolean getRendersChildren()
61      {
62          return true;
63      }
64  
65      /**
66       * Determine which datascroller navigation option the user chose (if any),
67       * and if they did then queue the appropriate ScrollerActionEvent for
68       * later execution.
69       */
70      public void decode(FacesContext context, UIComponent component)
71      {
72          RendererUtils.checkParamValidity(context, component, HtmlDataScroller.class);
73  
74          Map parameter = context.getExternalContext().getRequestParameterMap();
75          String param = (String) parameter.get(component.getClientId(context));
76          if (param != null && param.length() > 0)
77          {
78              if (param.startsWith(PAGE_NAVIGATION))
79              {
80                  // the user chose a specific page# to jump to
81                  component.queueEvent(new ScrollerActionEvent(component, Integer.parseInt(param
82                                  .substring(PAGE_NAVIGATION.length(), param.length()))));
83              }
84              else
85              {
86                  // the user chose first/last/prev/next/fastrewind/fastforward
87                  component.queueEvent(new ScrollerActionEvent(component, param));
88              }
89          }
90      }
91  
92      /**
93       * Expose much of the internal state of this component so that UIComponents
94       * nested within this component can very flexibly display the component state
95       * to the user.
96       */
97      protected void setVariables(FacesContext facescontext, HtmlDataScroller scroller)
98                      throws IOException
99      {
100         Map requestMap = facescontext.getExternalContext().getRequestMap();
101 
102         String pageCountVar = scroller.getPageCountVar();
103         if (pageCountVar != null)
104         {
105             int pageCount = scroller.getPageCount();
106             requestMap.put(pageCountVar, new Integer(pageCount));
107         }
108         String pageIndexVar = scroller.getPageIndexVar();
109         if (pageIndexVar != null)
110         {
111             int pageIndex = (scroller.getRowCount() > 0)? scroller.getPageIndex() : 0;
112             requestMap.put(pageIndexVar, new Integer(pageIndex));
113         }
114         String rowsCountVar = scroller.getRowsCountVar();
115         if (rowsCountVar != null)
116         {
117             int rowsCount = scroller.getRowCount();
118             requestMap.put(rowsCountVar, new Integer(rowsCount));
119         }
120         String displayedRowsCountVar = scroller.getDisplayedRowsCountVar();
121         if (displayedRowsCountVar != null)
122         {
123             int displayedRowsCount = scroller.getRows();
124             int max = scroller.getRowCount() - scroller.getFirstRow();
125             if (displayedRowsCount > max)
126                 displayedRowsCount = max;
127             requestMap.put(displayedRowsCountVar, new Integer(displayedRowsCount));
128         }
129         String firstRowIndexVar = scroller.getFirstRowIndexVar();
130         if (firstRowIndexVar != null)
131         {
132             int firstRowIndex = (scroller.getRowCount() > 0)? scroller.getFirstRow() + 1 : 0;
133             requestMap.put(firstRowIndexVar, new Integer(firstRowIndex));
134         }
135         String lastRowIndexVar = scroller.getLastRowIndexVar();
136         if (lastRowIndexVar != null)
137         {
138             int lastRowIndex = scroller.getFirstRow() + scroller.getRows();
139             int count = scroller.getRowCount();
140             if (lastRowIndex > count)
141                 lastRowIndex = count;
142             requestMap.put(lastRowIndexVar, new Integer(lastRowIndex));
143         }
144     }
145 
146     public void removeVariables(FacesContext facescontext, HtmlDataScroller scroller)
147                     throws IOException
148     {
149         Map requestMap = facescontext.getExternalContext().getRequestMap();
150 
151         String pageCountVar = scroller.getPageCountVar();
152         if (pageCountVar != null)
153         {
154             requestMap.remove(pageCountVar);
155         }
156         String pageIndexVar = scroller.getPageIndexVar();
157         if (pageIndexVar != null)
158         {
159             requestMap.remove(pageIndexVar);
160         }
161         String rowsCountVar = scroller.getRowsCountVar();
162         if (rowsCountVar != null)
163         {
164             requestMap.remove(rowsCountVar);
165         }
166         String displayedRowsCountVar = scroller.getDisplayedRowsCountVar();
167         if (displayedRowsCountVar != null)
168         {
169             requestMap.remove(displayedRowsCountVar);
170         }
171         String firstRowIndexVar = scroller.getFirstRowIndexVar();
172         if (firstRowIndexVar != null)
173         {
174             requestMap.remove(firstRowIndexVar);
175         }
176         String lastRowIndexVar = scroller.getLastRowIndexVar();
177         if (lastRowIndexVar != null)
178         {
179             requestMap.remove(lastRowIndexVar);
180         }
181     }
182 
183     public void encodeBegin(FacesContext facesContext, UIComponent uiComponent) throws IOException
184     {
185         super.encodeBegin(facesContext, uiComponent);
186 
187         RendererUtils.checkParamValidity(facesContext, uiComponent, HtmlDataScroller.class);
188 
189         HtmlDataScroller scroller = (HtmlDataScroller) uiComponent;
190 
191         setVariables(facesContext, scroller);
192         
193         if (scroller.getFirst() == null && scroller.getFastRewind() == null
194             && scroller.getPrevious() == null && !scroller.isPaginator()
195             && scroller.getNext() == null && scroller.getFastForward() == null
196             && scroller.getLast() == null)
197         {
198             ResponseWriter writer = facesContext.getResponseWriter();
199             writer.startElement(HTML.DIV_ELEM, scroller);
200             Map<String, List<ClientBehavior>> clientBehaviors = scroller.getClientBehaviors();
201             if (!clientBehaviors.isEmpty() ||
202                     (scroller.getId() != null && !scroller.getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX)))
203             {
204                 writer.writeAttribute(HTML.ID_ATTR, scroller.getClientId(facesContext), null);
205             }
206         }
207     }
208 
209     public void encodeChildren(FacesContext facescontext, UIComponent uicomponent)
210                     throws IOException
211     {
212         RendererUtils.checkParamValidity(facescontext, uicomponent, HtmlDataScroller.class);
213 
214         // TOMAHAWK-1463 Don't render paginator links twice!
215         if (uicomponent.getChildCount() > 0)
216         {
217             HtmlDataScroller scroller = (HtmlDataScroller) uicomponent;
218             String scrollerIdPagePrefix = scroller.getId() + HtmlDataScrollerRenderer.PAGE_NAVIGATION;
219 
220             for (Iterator it = uicomponent.getChildren().iterator(); it.hasNext(); )
221             {
222                 UIComponent child = (UIComponent)it.next();
223                 String childId = child.getId();
224 
225                 if (childId != null && !childId.startsWith(scrollerIdPagePrefix))
226                 {
227                     RendererUtils.renderChild(facescontext, child);
228                 }
229             }
230         }
231     }
232 
233     public void encodeEnd(FacesContext facesContext, UIComponent uiComponent) throws IOException
234     {
235         RendererUtils.checkParamValidity(facesContext, uiComponent, HtmlDataScroller.class);
236 
237         HtmlDataScroller scroller = (HtmlDataScroller) uiComponent;
238 
239         if (scroller.getUIData() == null)
240         {
241             return;
242         }
243 
244         renderScroller(facesContext, scroller);
245         removeVariables(facesContext, scroller);
246     }
247 
248     protected void renderScroller(FacesContext facesContext, HtmlDataScroller scroller)
249                     throws IOException
250     {
251         ResponseWriter writer = facesContext.getResponseWriter();
252 
253         if (scroller.getFirst() == null && scroller.getFastRewind() == null
254             && scroller.getPrevious() == null && !scroller.isPaginator()
255             && scroller.getNext() == null && scroller.getFastForward() == null
256             && scroller.getLast() == null)
257         {
258             writer.endElement(HTML.DIV_ELEM);
259             return;
260         }
261         
262         if (!scroller.isRenderFacetsIfSinglePage() && scroller.getPageCount() <= 1)
263             return;
264 
265         writeScrollerStart(writer, scroller);
266         
267         Map<String, List<ClientBehavior>> clientBehaviors = scroller.getClientBehaviors();
268         
269         if (!clientBehaviors.isEmpty() ||
270             (scroller.getId() != null && !scroller.getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX)))
271         {
272             writer.writeAttribute(HTML.ID_ATTR, scroller.getClientId(facesContext), null);
273         }
274         
275         String styleClass = scroller.getStyleClass();
276         if (styleClass != null)
277         {
278             writer.writeAttribute(HTML.CLASS_ATTR, styleClass, JSFAttr.STYLE_CLASS_ATTR);
279         }
280         String style = scroller.getStyle();
281         if (style != null)
282         {
283             writer.writeAttribute(HTML.STYLE_ATTR, style, JSFAttr.STYLE_ATTR);
284         }
285         writeScrollerRowStart(writer, scroller);
286 
287         boolean startActive = (scroller.getPageIndex() != 1);
288 
289         boolean endActive = (scroller.getPageIndex() != scroller.getPageCount());
290         
291         UIComponent facetComp = scroller.getFirst();
292         if (facetComp != null)
293         {
294             writeScrollerElementStart(writer, scroller);
295             writeStyleClass("firstStyleClass", scroller.getFirstStyleClass(), writer);
296             renderFacet(facesContext, scroller, facetComp, HtmlDataScroller.FACET_FIRST, startActive,
297                     scroller.isRenderFacetLinksIfFirstPage(), scroller.isDisableFacetLinksIfFirstPage());
298             writeScrollerElementEnd(writer, scroller);
299         }
300         facetComp = scroller.getFastRewind();
301         if (facetComp != null)
302         {
303             writeScrollerElementStart(writer, scroller);
304             writeStyleClass("fastrStyleClass", scroller.getFastrStyleClass(), writer);
305             renderFacet(facesContext, scroller, facetComp, HtmlDataScroller.FACET_FAST_REWIND, startActive,
306                     scroller.isRenderFacetLinksIfFirstPage(), scroller.isDisableFacetLinksIfFirstPage());
307             writeScrollerElementEnd(writer, scroller);
308         }
309         facetComp = scroller.getPrevious();
310         if (facetComp != null)
311         {
312             writeScrollerElementStart(writer, scroller);
313             writeStyleClass("previous", scroller.getPreviousStyleClass(), writer);
314             renderFacet(facesContext, scroller, facetComp, HtmlDataScroller.FACET_PREVIOUS, startActive,
315                     scroller.isRenderFacetLinksIfFirstPage(), scroller.isDisableFacetLinksIfFirstPage());
316             writeScrollerElementEnd(writer, scroller);
317         }
318         if (scroller.isPaginator())
319         {
320             if(!scroller.isSingleElementLayout())
321             {
322                 writeScrollerElementStart(writer, scroller);
323             }
324             renderPaginator(facesContext, scroller);
325             if(!scroller.isSingleElementLayout())
326             {
327                 writeScrollerElementEnd(writer, scroller);
328             }
329         }
330         facetComp = scroller.getNext();
331         if (facetComp != null)
332         {
333             writeScrollerElementStart(writer, scroller);
334             writeStyleClass("next", scroller.getNextStyleClass(), writer);
335             renderFacet(facesContext, scroller, facetComp, HtmlDataScroller.FACET_NEXT, endActive,
336                     scroller.isRenderFacetLinksIfLastPage(), scroller.isDisableFacetLinksIfLastPage());
337             writeScrollerElementEnd(writer, scroller);
338         }
339         facetComp = scroller.getFastForward();
340         if (facetComp != null)
341         {
342             writeScrollerElementStart(writer, scroller);
343             writeStyleClass("fastf", scroller.getFastfStyleClass(), writer);
344             renderFacet(facesContext, scroller, facetComp, HtmlDataScroller.FACET_FAST_FORWARD, endActive,
345                     scroller.isRenderFacetLinksIfLastPage(), scroller.isDisableFacetLinksIfLastPage());
346             writeScrollerElementEnd(writer, scroller);
347         }
348         facetComp = scroller.getLast();
349         if (facetComp != null)
350         {
351             writeScrollerElementStart(writer, scroller);
352             writeStyleClass("last", scroller.getLastStyleClass(), writer);
353             renderFacet(facesContext, scroller, facetComp, HtmlDataScroller.FACET_LAST, endActive,
354                     scroller.isRenderFacetLinksIfLastPage(), scroller.isDisableFacetLinksIfLastPage());
355             writeScrollerElementEnd(writer, scroller);
356         }
357 
358         writeScrollerRowEnd(writer, scroller);
359         writeScrollerEnd(writer, scroller);
360     }
361 
362     private void writeStyleClass(String jsfAttrName, String styleClass, ResponseWriter writer) throws IOException {
363         if (styleClass != null)
364         {
365             writer.writeAttribute(HTML.CLASS_ATTR, styleClass, jsfAttrName);
366         }
367     }
368 
369     private boolean isListLayout(HtmlDataScroller scroller) {
370         return scroller.isListLayout();
371     }
372 
373     protected void writeScrollerEnd(ResponseWriter writer, HtmlDataScroller scroller) throws IOException {
374         writer.endElement(isListLayout(scroller)?HTML.UL_ELEM:HTML.TABLE_ELEM);
375     }
376 
377     protected void writeScrollerRowEnd(ResponseWriter writer, HtmlDataScroller scroller) throws IOException {
378         if(!isListLayout(scroller)) writer.endElement(HTML.TR_ELEM);
379     }
380 
381     protected void writeScrollerElementEnd(ResponseWriter writer, HtmlDataScroller scroller) throws IOException {
382         writer.endElement(isListLayout(scroller)?HTML.LI_ELEM:HTML.TD_ELEM);
383     }
384 
385     protected void writeScrollerElementStart(ResponseWriter writer, HtmlDataScroller scroller) throws IOException {
386         writer.startElement(isListLayout(scroller)?HTML.LI_ELEM:HTML.TD_ELEM, scroller);
387     }
388 
389     protected void writeScrollerRowStart(ResponseWriter writer, HtmlDataScroller scroller) throws IOException {
390         if(!isListLayout(scroller)) writer.startElement(HTML.TR_ELEM, scroller);
391     }
392 
393     protected void writeScrollerStart(ResponseWriter writer, HtmlDataScroller scroller) throws IOException {
394         writer.startElement(isListLayout(scroller)?HTML.UL_ELEM:HTML.TABLE_ELEM, scroller);
395     }
396 
397     protected void renderFacet(FacesContext facesContext, HtmlDataScroller scroller,
398                                UIComponent facetComp, String facetName, boolean active, boolean renderLinks, boolean disableLinks) throws IOException
399     {
400         String onclick = scroller.getOnclick();
401         String ondblclick = scroller.getOndblclick();
402 
403         HtmlCommandLink link = getLink(facesContext, scroller, facetName);
404 
405         if(onclick != null){
406             link.setOnclick(onclick);
407         }
408 
409         if(ondblclick != null){
410             link.setOndblclick(ondblclick);
411         }
412 
413         if (active) {
414             if (disableLinks && link.isDisabled())
415             {
416                 //Enable because the facet is active
417                 link.setDisabled(false);
418             }
419             link.encodeBegin(facesContext);
420         }
421         else if (renderLinks)
422         {
423             if (disableLinks && !link.isDisabled())
424             {
425                 //Disable because the facet is not active
426                 link.setDisabled(true);
427             }
428             link.encodeBegin(facesContext);
429         }
430 
431         facetComp.encodeBegin(facesContext);
432         if (!facetComp.getRendersChildren())
433             facetComp.encodeChildren(facesContext);
434         facetComp.encodeEnd(facesContext);
435 
436         if (active || renderLinks) {
437             link.encodeEnd(facesContext);
438         }
439     }
440 
441     /**
442      * The "paginator" is a sequence of page numbers which the user can click
443      * on to leap directly to a specific page of data.
444      */
445     protected void renderPaginator(FacesContext facesContext, HtmlDataScroller scroller)
446                     throws IOException
447     {
448         ResponseWriter writer = facesContext.getResponseWriter();
449 
450         int maxPages = scroller.getPaginatorMaxPages();
451         if (maxPages <= 1)
452         {
453             maxPages = 2;
454         }
455         int pageCount = scroller.getPageCount();
456         if (pageCount <= 1)
457         {
458             return;
459         }
460         int pageIndex = scroller.getPageIndex();
461         int delta = maxPages / 2;
462 
463         int pages;
464         int start;
465         if (pageCount > maxPages && pageIndex > delta)
466         {
467             pages = maxPages;
468             start = pageIndex - pages / 2 - 1;
469             if (start + pages > pageCount)
470             {
471                 start = pageCount - pages;
472             }
473         }
474         else
475         {
476             pages = pageCount < maxPages ? pageCount : maxPages;
477             start = 0;
478         }
479 
480         if(!scroller.isSingleElementLayout())
481         {
482             writePaginatorStart(writer, scroller);
483 
484             String styleClass = scroller.getPaginatorTableClass();
485             if (styleClass != null)
486             {
487                 writer.writeAttribute(HTML.CLASS_ATTR, styleClass, "paginatorTableClass");
488             }
489             String style = scroller.getPaginatorTableStyle();
490             if (style != null)
491             {
492                 writer.writeAttribute(HTML.STYLE_ATTR, style, "paginatorTableStyle");
493             }
494 
495             writePaginatorRowStart(writer, scroller);
496         }
497 
498         String onclick = scroller.getOnclick();
499            String ondblclick = scroller.getOndblclick();
500            
501         // TOMAHAWK-596 TOMAHAWK-1249 Duplicate id exception for HtmlDataScrollerRenderer
502         // Prevent render old paginator links removing it from tree.
503         // Note that this only happens when transient components are not removed
504         // from component tree on save and restore phase.
505         if (scroller.getChildCount() != 0)
506         {
507             String scrollerIdPagePrefix = scroller.getId() + HtmlDataScrollerRenderer.PAGE_NAVIGATION; 
508             for(Iterator it = scroller.getChildren().iterator(); it.hasNext();)
509             {
510                 UIComponent child = (UIComponent) it.next();
511                 String childId = child.getId();
512                 if (childId != null && childId.startsWith(scrollerIdPagePrefix))
513                 {
514                     try
515                     {
516                         int p = Integer.parseInt(childId.substring(scrollerIdPagePrefix.length()));
517                         if (p < start && p >= start + pages)
518                         {
519                             //Remove from child list
520                             it.remove();
521                         }
522                     }
523                     catch(NumberFormatException e)
524                     {
525                         //Do nothing because this component does not have the expected id format
526                     }
527                 }
528             }
529         }
530 
531         for (int i = start, size = start + pages; i < size; i++)
532         {
533             int idx = i + 1;
534             writePaginatorElementStart(writer, scroller);
535             String cStyleClass;
536             String cStyle;
537             if (idx == pageIndex)
538             {
539                 cStyleClass = scroller.getPaginatorActiveColumnClass();
540                 cStyle = scroller.getPaginatorActiveColumnStyle();
541             }
542             else
543             {
544                 cStyleClass = scroller.getPaginatorColumnClass();
545                 cStyle = scroller.getPaginatorColumnStyle();
546             }
547             if (cStyleClass != null)
548             {
549                 writer.writeAttribute(HTML.CLASS_ATTR, cStyleClass, idx==pageIndex?"paginatorActiveColumnClass":"paginatorColumnClass");
550             }
551             if (cStyle != null)
552             {
553                 writer.writeAttribute(HTML.STYLE_ATTR, cStyle, idx==pageIndex?"paginatorActiveColumnStyle":"paginatorColumnStyle");
554             }
555 
556             if(idx == pageIndex && !scroller.isPaginatorRenderLinkForActive())
557             {
558                 writer.write(Integer.toString(idx));
559             }
560             else
561             {
562                 HtmlCommandLink link = getLink(facesContext, scroller, Integer.toString(idx), idx);
563                 if(onclick != null){
564                     link.setOnclick(onclick);
565                 }
566                 if(ondblclick != null){
567                     link.setOndblclick(ondblclick);
568                 }
569 
570                 link.encodeBegin(facesContext);
571                 link.encodeChildren(facesContext);
572                 link.encodeEnd(facesContext);
573             }
574 
575             writePaginatorElementEnd(writer, scroller);
576         }
577 
578         if(!scroller.isSingleElementLayout())
579         {
580             writePaginatorRowEnd(writer, scroller);
581             writePaginatorEnd(writer, scroller);
582         }
583     }
584 
585     protected void writePaginatorEnd(ResponseWriter writer, HtmlDataScroller scroller) throws IOException {
586         writer.endElement(isListLayout(scroller)?HTML.UL_ELEM:HTML.TABLE_ELEM);
587     }
588 
589     protected void writePaginatorRowEnd(ResponseWriter writer, HtmlDataScroller scroller) throws IOException {
590         if(!isListLayout(scroller)) writer.endElement(HTML.TR_ELEM);
591     }
592 
593     protected void writePaginatorElementEnd(ResponseWriter writer, HtmlDataScroller scroller) throws IOException {
594         writer.endElement(isListLayout(scroller)?HTML.LI_ELEM:HTML.TD_ELEM);
595     }
596 
597     protected void writePaginatorElementStart(ResponseWriter writer, HtmlDataScroller scroller) throws IOException {
598         writer.startElement(isListLayout(scroller)?HTML.LI_ELEM:HTML.TD_ELEM, scroller);
599     }
600 
601     protected void writePaginatorRowStart(ResponseWriter writer, HtmlDataScroller scroller) throws IOException {
602         if(!isListLayout(scroller)) writer.startElement(HTML.TR_ELEM, scroller);
603     }
604 
605     protected void writePaginatorStart(ResponseWriter writer, HtmlDataScroller scroller) throws IOException {
606         writer.startElement(isListLayout(scroller)?HTML.UL_ELEM:HTML.TABLE_ELEM, scroller);
607     }
608 
609     protected HtmlCommandLink getLink(FacesContext facesContext, HtmlDataScroller scroller,
610                                       String text, int pageIndex)
611     {
612         String id = HtmlDataScrollerRenderer.PAGE_NAVIGATION + Integer.toString(pageIndex);
613 
614         Application application = facesContext.getApplication();
615 
616         // TOMAHAWK-596 Duplicate id exception for HtmlDataScrollerRenderer
617         // For prevent this condition, try to detect if there is a component.
618         // Theorically this method should always return null, but it is known
619         // than in some cases (portlet) when the state manager is different from
620         // the default one, transient components are saved on the component tree.
621         // Really the error is on the jsf portlet bridge implementation used, but
622         // do this does not cause any side effect.
623         HtmlCommandLink link = (HtmlCommandLink) scroller.findComponent(scroller.getId() + id);
624         if (link == null)
625         {
626             // See Jira Issue TOMAHAWK-117 http://issues.apache.org/jira/browse/TOMAHAWK-117
627             //     and http://issues.apache.org/jira/browse/MYFACES-1809
628             link = new org.apache.myfaces.component.html.ext.HtmlCommandLink();
629 
630             //Copy all client behaviors 
631             for (Map.Entry<String,List<ClientBehavior>> entry : scroller.getClientBehaviors().entrySet())
632             {
633                 List<ClientBehavior> list = entry.getValue();
634                 if (list != null && !list.isEmpty())
635                 {
636                     for (ClientBehavior cb : list)
637                     {
638                         link.addClientBehavior(entry.getKey(), cb);
639                     }
640                 }
641             }
642 
643             link.setId(scroller.getId() + id);
644             link.setTransient(true);
645             UIParameter parameter = (UIParameter) application
646                             .createComponent(UIParameter.COMPONENT_TYPE);
647             parameter.setId(scroller.getId() + id + "_param");
648             parameter.setTransient(true);
649             parameter.setName(scroller.getClientId(facesContext));
650             parameter.setValue(id);
651             List children = link.getChildren();
652             children.add(parameter);
653             if (text != null)
654             {
655                 HtmlOutputText uiText = (HtmlOutputText) application
656                                 .createComponent(HtmlOutputText.COMPONENT_TYPE);
657                 uiText.setId(scroller.getId() + id + "_text");
658                 uiText.setTransient(true);
659                 uiText.setValue(text);
660                 children.add(uiText);
661             }
662             scroller.getChildren().add(link);
663         }
664         else
665         {
666             UIOutput uiText = (UIOutput) link.findComponent(scroller.getId() + id + "_text");
667             if (uiText != null)
668             {
669                 //Update text value
670                 uiText.setValue(text);
671             }
672         }
673         return link;
674     }
675 
676     protected HtmlCommandLink getLink(FacesContext facesContext, HtmlDataScroller scroller,
677                                       String facetName)
678     {
679         Application application = facesContext.getApplication();
680 
681         // TOMAHAWK-596 TOMAHAWK-1249 Duplicate id exception for HtmlDataScrollerRenderer
682         // For prevent this condition, try to detect if there is a component.
683         // Theorically this method should always return null, but it is known
684         // than in some cases (portlet) when the state manager is different from
685         // the default one, transient components are saved on the component tree.
686         // Really the error is on the jsf portlet bridge implementation used, but
687         // do this does not cause any side effect.
688         HtmlCommandLink link = (HtmlCommandLink) scroller.findComponent(scroller.getId() + facetName);
689         if (link == null)
690         {
691             // See Jira Issue TOMAHAWK-117 http://issues.apache.org/jira/browse/TOMAHAWK-117
692             //     and http://issues.apache.org/jira/browse/MYFACES-1809
693             link = new org.apache.myfaces.component.html.ext.HtmlCommandLink();
694     
695             //Copy all client behaviors 
696             for (Map.Entry<String,List<ClientBehavior>> entry : scroller.getClientBehaviors().entrySet())
697             {
698                 List<ClientBehavior> list = entry.getValue();
699                 if (list != null && !list.isEmpty())
700                 {
701                     for (ClientBehavior cb : list)
702                     {
703                         link.addClientBehavior(entry.getKey(), cb);
704                     }
705                 }
706             }
707             
708             link.setId(scroller.getId() + facetName);
709             link.setTransient(true);
710             UIParameter parameter = (UIParameter) application
711                             .createComponent(UIParameter.COMPONENT_TYPE);
712             parameter.setId(scroller.getId() + facetName + "_param");
713             parameter.setTransient(true);
714             
715             // TOMAHAWK-96 Data table Scroller not working the dataTable which was actually 
716             // contained in other DataTable.
717             // Since the parent link is always the scroller, we can just use a value expression
718             // that calculates its id, so when the datascroller is rendered per row, the clientId
719             // is calculated based on the row position.
720             //parameter.setName(scroller.getClientId(facesContext));
721             parameter.setValueExpression("name", facesContext.getApplication().
722                     getExpressionFactory().createValueExpression(facesContext.getELContext(),
723                             "#{component.parent.clientId}", String.class));
724             parameter.setValue(facetName);
725             List children = link.getChildren();
726             children.add(parameter);
727             scroller.getChildren().add(link);
728         }
729         return link;
730     }
731 }