001    package edu.nrao.sss.webapp.faces.component;
002    
003    import edu.nrao.sss.webapp.faces.renderer.RendererUtils;
004    
005    import javax.faces.component.UIData;
006    import javax.faces.component.UIComponent;
007    import javax.faces.context.ResponseWriter;
008    import javax.faces.context.FacesContext;
009    
010    import java.io.IOException;
011    import java.io.StringWriter;
012    import java.util.List;
013    import java.util.Iterator;
014    
015    /**
016     * This class implements a ForEach loop tag by partially overriding the default
017     * table renderer. By partially, I mean those parts of the rendering that dealt
018     * solely with rendering UIColumns are overridden or replaced to deal with all
019     * child elements and no table tags are written. This allows us to borrow the
020     * default renderer's handling of child id's and the data model and all that.
021     */
022    public class ForEach extends UIData
023    {
024            private static final int PROCESS_DECODES = 1;
025            private static final int PROCESS_VALIDATORS = 2;
026            private static final int PROCESS_UPDATES = 3;
027    
028            /** empty constructor */
029            public ForEach()
030            {
031            }
032    
033            /**
034             * This method is overridden to prevent the default renderer from writing
035             * out table tags. This is done by temporarily replacing the ResponseWriter
036             * in context, calling super (because super must be called to correctly
037             * handle some model interaction), and then putting the real writer back.
038             */
039            public void encodeBegin(FacesContext context) throws IOException
040            {
041                    // Ensure that the "current row" is set to "no row", so that the
042                    // correct clientId is set for this component etc. User code may
043                    // have left this in some other state before rendering began...
044                    setRowIndex(-1);
045    
046                    ResponseWriter realWriter = context.getResponseWriter();
047                    ResponseWriter fake = realWriter.cloneWithWriter(new StringWriter());
048    
049                    context.setResponseWriter(fake);
050                    super.encodeBegin(context);
051                    context.setResponseWriter(realWriter);
052            }
053    
054            /**
055             * Overridden to prevent end table tag output
056             */
057            public void encodeEnd(FacesContext context) throws IOException
058            {
059            }
060    
061            /**
062             * Overridden to render our child UI components even though they are not
063             * UIColumns.
064             */
065            public void encodeChildren(FacesContext context) throws IOException
066            {
067                    int first = getFirst();
068                    int rows = getRows();
069                    int last;
070                    if (rows <= 0)
071                    {
072                            last = getRowCount();
073                    }
074                    else
075                    {
076                            last = first + rows;
077                    }
078                    for (int rowIndex = first; last==-1 || rowIndex < last; rowIndex++)
079                    {
080                            setRowIndex(rowIndex);
081    
082                            //scrolled past the last row
083                            if (!isRowAvailable())
084                                    break;
085    
086                            List children = getChildren();
087                            for (int j = 0, size = getChildCount(); j < size; j++)
088                            {
089                                    UIComponent child = (UIComponent) children.get(j);
090                                    RendererUtils.renderChild(context, child);
091                            }
092                    }
093            }
094    
095            /**
096             * Overridden to properly decode our child components
097             */
098            public void processDecodes(FacesContext context)
099            {
100                    if (context == null)
101                            throw new NullPointerException("context");
102                    if (!isRendered())
103                            return;
104                    setRowIndex(-1);
105                    processChildren(context, PROCESS_DECODES);
106                    setRowIndex(-1);
107                    try
108                    {
109                            decode(context);
110                    }
111                    catch (RuntimeException e)
112                    {
113                            context.renderResponse();
114                            throw e;
115                    }
116            }
117    
118            /**
119             * Overridden to properly decode our child components
120             */
121            public void processValidators(FacesContext context)
122            {
123                    if (context == null)
124                            throw new NullPointerException("context");
125                    if (!isRendered())
126                            return;
127                    setRowIndex(-1);
128                    processChildren(context, PROCESS_VALIDATORS);
129                    setRowIndex(-1);
130    
131                    //We have no uicolumns, but we need to run super.processValidators()
132                    //anyway so that the correct model actions will take place when data is not
133                    //valid.
134                    super.processValidators(context);
135            }
136    
137            /**
138             * Overridden to properly decode our child components
139             */
140            public void processUpdates(FacesContext context)
141            {
142                    if (context == null)
143                            throw new NullPointerException("context");
144                    if (!isRendered())
145                            return;
146                    setRowIndex(-1);
147                    processChildren(context, PROCESS_UPDATES);
148                    setRowIndex(-1);
149    
150                    //We have no uicolumns, but we need to run super.processUpdates()
151                    //anyway so that the correct model actions will take place when data is not
152                    //valid.
153                    super.processUpdates(context);
154            }
155    
156            /**
157             * Helper method to process our children
158             */
159            private void processChildren(FacesContext context, int processAction)
160            {
161                    int first = getFirst();
162                    int rows = getRows();
163                    int last;
164                    if (rows == 0)
165                    {
166                            last = getRowCount();
167                    }
168                    else
169                    {
170                            last = first + rows;
171                    }
172    
173                    for (int rowIndex = first; last==-1 || rowIndex < last; rowIndex++)
174                    {
175                            setRowIndex(rowIndex);
176    
177                            //scrolled past the last row
178                            if (!isRowAvailable())
179                                    break;
180    
181                            for (Iterator childIter = getChildren().iterator(); childIter.hasNext();)
182                            {
183                                    UIComponent child = (UIComponent)childIter.next();
184                                    process(context, child, processAction);
185                            }
186                    }
187            }
188    
189            /**
190             * Helper method to process our children
191             */
192            private void process(FacesContext context, UIComponent component, int processAction)
193            {
194                    switch (processAction)
195                    {
196                            case PROCESS_DECODES:
197                                    component.processDecodes(context);
198                                    break;
199                            case PROCESS_VALIDATORS:
200                                    component.processValidators(context);
201                                    break;
202                            case PROCESS_UPDATES:
203                                    component.processUpdates(context);
204                                    break;
205                    }
206            }
207    }