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 }