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