1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.myfaces.tobago.internal.component;
21
22 import org.apache.myfaces.tobago.apt.annotation.Preliminary;
23 import org.apache.myfaces.tobago.component.Attributes;
24 import org.apache.myfaces.tobago.component.LabelLayout;
25 import org.apache.myfaces.tobago.component.RendererTypes;
26 import org.apache.myfaces.tobago.component.Tags;
27 import org.apache.myfaces.tobago.internal.util.StyleRenderUtils;
28 import org.apache.myfaces.tobago.layout.GridSpan;
29 import org.apache.myfaces.tobago.layout.MeasureList;
30 import org.apache.myfaces.tobago.util.ComponentUtils;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 import javax.faces.component.UIComponent;
35 import javax.faces.component.UIPanel;
36 import javax.faces.context.FacesContext;
37 import javax.faces.event.AbortProcessingException;
38 import javax.faces.event.ComponentSystemEvent;
39 import javax.faces.event.ComponentSystemEventListener;
40 import javax.faces.event.ListenerFor;
41 import javax.faces.event.PreRenderComponentEvent;
42 import java.lang.invoke.MethodHandles;
43 import java.util.List;
44 import java.util.Map;
45
46
47
48
49
50
51
52
53
54 @Preliminary
55 @ListenerFor(systemEventClass = PreRenderComponentEvent.class)
56 public abstract class AbstractUIGridLayout extends AbstractUILayoutBase implements ComponentSystemEventListener {
57
58 private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
59
60 public static final String COMPONENT_FAMILY = "org.apache.myfaces.tobago.GridLayout";
61
62 private static final int STEP = 5;
63
64 protected static final UIComponent SPAN = new UIPanel();
65
66
67
68
69 @Override
70 public void processEvent(final ComponentSystemEvent event) throws AbortProcessingException {
71
72 super.processEvent(event);
73
74 if (!isRendered()) {
75 return;
76 }
77
78 if (event instanceof PreRenderComponentEvent) {
79
80 layout(
81 MeasureList.parse(getColumns()).getSize(),
82 MeasureList.parse(getRows()).getSize(),
83 ComponentUtils.findLayoutChildren(this));
84
85 }
86 }
87
88 public abstract String getRows();
89
90 public abstract void setRows(String rows);
91
92 public abstract String getColumns();
93
94 public abstract void setColumns(String columns);
95
96 protected UIComponent[][] layout(
97 final int columnsCount, final int initialRowsCount, final List<UIComponent> components) {
98 assert columnsCount > 0;
99 assert initialRowsCount > 0;
100
101 final FacesContext facesContext = FacesContext.getCurrentInstance();
102 UIComponent[][] cells = new UIComponent[initialRowsCount][columnsCount];
103
104
105 for (final UIComponent component : components) {
106 final Map<String, Object> attributes = component.getAttributes();
107 final Integer gridColumn = (Integer) attributes.get(Attributes.gridColumn.getName());
108 final Integer gridRow = (Integer) attributes.get(Attributes.gridRow.getName());
109 if (gridColumn != null && gridRow != null) {
110 if (gridColumn > columnsCount) {
111
112 LOG.warn("gridColumn {} > columnsCount {} in component '{}'!", gridColumn, columnsCount,
113 component.getClientId(facesContext));
114 } else {
115 if (gridRow > cells.length) {
116 if (LOG.isDebugEnabled()) {
117 LOG.debug("expanding, because gridRow {} > rowCount {} in component '{}'!", gridRow, cells.length,
118 component.getClientId(facesContext));
119 }
120
121 cells = expand(cells, gridRow);
122 }
123 cells = set(cells, gridColumn - 1, gridRow - 1, component);
124 }
125 } else if (gridColumn != null) {
126 LOG.warn("gridColumn is set to {}, but gridRow not in component '{}'!", gridColumn,
127 component.getClientId(facesContext));
128 } else if (gridRow != null) {
129 LOG.warn("gridRow is set to {}, but gridColumn not in component '{}'!", gridRow,
130 component.getClientId(facesContext));
131 }
132 }
133
134
135 int j = 0;
136 int i = 0;
137 for (final UIComponent component : components) {
138 final Map<String, Object> attributes = component.getAttributes();
139 final Integer gridColumn = (Integer) attributes.get(Attributes.gridColumn.getName());
140 final Integer gridRow = (Integer) attributes.get(Attributes.gridRow.getName());
141
142
143 if (gridColumn == null || gridRow == null) {
144
145 while (cells[j][i] != null) {
146 i++;
147 if (i >= columnsCount) {
148 i = 0;
149 j++;
150 }
151 if (j >= cells.length) {
152 cells = expand(cells, j + STEP);
153 }
154 }
155 cells = set(cells, i, j, component);
156 }
157 }
158
159
160 for (final UIComponent component : components) {
161 final Map<String, Object> attributes = component.getAttributes();
162
163 final int gridColumn = (Integer) attributes.get(Attributes.gridColumn.getName());
164 final Integer columnSpan = (Integer) attributes.get(Attributes.columnSpan.getName());
165 final int gridRow = (Integer) attributes.get(Attributes.gridRow.getName());
166 final Integer rowSpan = (Integer) attributes.get(Attributes.rowSpan.getName());
167 final boolean labeledLeft = LabelLayout.isGridLeft(component);
168 final boolean labeledRight = LabelLayout.isGridRight(component);
169 final boolean labeledHorizontal = labeledLeft || labeledRight;
170 final boolean labeledTop = LabelLayout.isGridTop(component);
171 final boolean labeledBottom = LabelLayout.isGridBottom(component);
172 final boolean labeledVertical = labeledTop || labeledBottom;
173 final boolean labeled = labeledHorizontal || labeledVertical;
174
175
176
177 AbstractUIStyle fieldStyle = ComponentUtils.findChild(component, AbstractUIStyle.class);
178 if (fieldStyle == null) {
179 fieldStyle = (AbstractUIStyle) ComponentUtils.createComponent(
180 facesContext, Tags.style.componentType(), RendererTypes.Style, null);
181 component.getChildren().add(fieldStyle);
182 }
183
184
185 fieldStyle.setTransient(true);
186
187 fieldStyle.setGridColumn(
188 GridSpan.valueOf(labeledLeft ? gridColumn + 1 : gridColumn, labeledHorizontal ? columnSpan - 1 : columnSpan));
189 fieldStyle.setGridRow(
190 GridSpan.valueOf(labeledTop ? gridRow + 1 : gridRow, labeledVertical ? rowSpan - 1 : rowSpan));
191
192
193
194 if (labeled) {
195 final AbstractUIStyle labelStyle = (AbstractUIStyle) ComponentUtils.createComponent(
196 facesContext, Tags.style.componentType(), RendererTypes.Style, null);
197 component.getChildren().add(labelStyle);
198 labelStyle.setTransient(true);
199 labelStyle.setSelector(StyleRenderUtils.encodeIdSelector(
200 component.getClientId(facesContext) + ComponentUtils.SUB_SEPARATOR + "label"));
201
202 labelStyle.setGridColumn(GridSpan.valueOf(labeledRight ? gridColumn + columnSpan - 1 : gridColumn, null));
203 labelStyle.setGridRow(GridSpan.valueOf(labeledBottom ? gridRow + rowSpan - 1 : gridRow, null));
204 }
205
206 }
207
208 return cells;
209 }
210
211
212
213
214
215
216
217
218
219
220
221 private UIComponent[][] set(
222 final UIComponent[][] initialCells, final Integer column, final Integer row, final UIComponent component) {
223
224 UIComponent[][] cells = initialCells;
225
226 final Map<String, Object> attributes = component.getAttributes();
227 int rowSpan = ComponentUtils.getIntAttribute(component, Attributes.rowSpan,
228 LabelLayout.isGridTop(component) || LabelLayout.isGridBottom(component) ? 2 : 1);
229 ComponentUtils.setAttribute(component, Attributes.rowSpan, rowSpan);
230 int columnSpan = ComponentUtils.getIntAttribute(component, Attributes.columnSpan,
231 LabelLayout.isGridLeft(component) || LabelLayout.isGridRight(component) ? 2 : 1);
232 ComponentUtils.setAttribute(component, Attributes.columnSpan, columnSpan);
233
234
235 for (int j = row; j < rowSpan + row; j++) {
236 for (int i = column; i < columnSpan + column; i++) {
237 if (i >= cells[0].length) {
238 LOG.warn("column {} + columnSpan {} - 1 >= columnsCount {} in component '{}'!",
239 column + 1, columnSpan, cells[0].length, component.getClientId(FacesContext.getCurrentInstance()));
240 break;
241 }
242 if (j >= cells.length) {
243 cells = expand(cells, j + STEP);
244 }
245 if (j == row && i == column) {
246 cells[j][i] = component;
247 attributes.put(Attributes.gridRow.getName(), j + 1);
248 attributes.put(Attributes.gridColumn.getName(), i + 1);
249 } else {
250 cells[j][i] = SPAN;
251 }
252 }
253 }
254
255 return cells;
256 }
257
258
259
260
261 @Deprecated
262 protected UIComponent[][] expand(final UIComponent[][] cells, final Integer minRows, final int step) {
263 return expand(cells, minRows);
264 }
265
266 protected UIComponent[][] expand(final UIComponent[][] cells, final int rows) {
267 final int columns = cells[0].length;
268
269 final UIComponent[][] result = new UIComponent[rows][columns];
270 for (int j = 0; j < cells.length; j++) {
271 System.arraycopy(cells[j], 0, result[j], 0, columns);
272 }
273 return result;
274 }
275
276 }