1
28 package demo.layout.hierarchic;
29
30 import y.base.DataProvider;
31 import y.base.Node;
32 import y.base.NodeCursor;
33 import y.base.NodeMap;
34 import y.geom.YInsets;
35 import y.geom.YRectangle;
36 import y.layout.AbstractLayoutStage;
37 import y.layout.LayoutGraph;
38 import y.layout.Layouter;
39 import y.layout.grid.ColumnDescriptor;
40 import y.layout.grid.PartitionCellId;
41 import y.layout.grid.PartitionGrid;
42 import y.layout.grid.RowDescriptor;
43 import y.layout.grouping.Grouping;
44 import y.layout.grouping.GroupingKeys;
45 import y.util.Maps;
46 import y.util.WrappedObjectDataProvider;
47
48 import java.util.ArrayList;
49 import java.util.HashMap;
50 import java.util.Iterator;
51
52
71 public class GroupNodeTransformerStage extends AbstractLayoutStage {
72
76 public GroupNodeTransformerStage(Layouter core) {
77 super(core);
78 }
79
80
86 public boolean canLayout(LayoutGraph graph) {
87 return canLayoutCore(graph);
88 }
89
90
100 public void doLayout(LayoutGraph graph) {
101 final PartitionGrid grid = PartitionGrid.getPartitionGrid(graph);
102 if (grid == null || !Grouping.isGrouped(graph)) {
103 doLayoutCore(graph);
105 return;
106 }
107
108 final Grouping grouping = new Grouping(graph);
110 final Node tableGroupNode = grouping.getChildren(grouping.getRoot()).firstNode();
112 final ArrayList directGroupCellSpans = new ArrayList();
113 for (NodeCursor nc = grouping.getChildren(tableGroupNode).nodes(); nc.ok(); nc.next()) {
114 final Node n = nc.node();
115 if (grouping.isGroupNode(n)) {
116 directGroupCellSpans.add(new GroupCellSpan(n, graph));
117 }
118 }
119
120 if (!isConsistent(directGroupCellSpans)) {
122 throw new IllegalArgumentException("Found partition cell that is covered by multiple direct group nodes!");
123 }
124
125 final DataProvider origNode2CellIdDP = graph.getDataProvider(PartitionGrid.PARTITION_CELL_DPKEY);
127 final NodeMap newNode2CellId = Maps.createHashedNodeMap();
128 graph.addDataProvider(PartitionGrid.PARTITION_CELL_DPKEY,
129 new WrappedObjectDataProvider(newNode2CellId, origNode2CellIdDP));
130 final HashMap pair2GroupCellSpan = new HashMap();
131 for (int i = 0; i < directGroupCellSpans.size(); i++) {
132 final GroupCellSpan groupCellSpan = (GroupCellSpan) directGroupCellSpans.get(i);
134 final PartitionCellId cellId = groupCellSpan.getCellId();
135 newNode2CellId.set(groupCellSpan.getGroup(), cellId);
136
137 for (Iterator iter = cellId.getCells().iterator(); iter.hasNext(); ) {
139 pair2GroupCellSpan.put(iter.next(), cellId);
140 }
141 }
142 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
144 final Node n = nc.node();
145 if (grouping.isGroupNode(n)) {
146 continue;
147 }
148
149 final PartitionCellId cellId = (PartitionCellId) origNode2CellIdDP.get(n);
151 if (cellId == null) {
152 continue;
153 }
154 final PartitionCellId.Pair pair = (PartitionCellId.Pair) cellId.getCells().iterator().next();
155 if (pair2GroupCellSpan.containsKey(pair)) {
156 newNode2CellId.set(n, pair2GroupCellSpan.get(pair));
157 }
158 }
159 grouping.dispose();
160
161 doLayoutCore(graph);
163
164 graph.addDataProvider(PartitionGrid.PARTITION_CELL_DPKEY, origNode2CellIdDP);
166 }
167
168
175 private static boolean isConsistent(final ArrayList groupCellSpans) {
176 for (int i = 0; i < groupCellSpans.size(); i++) {
177 final GroupCellSpan span1 = (GroupCellSpan) groupCellSpans.get(i);
178 for (int j = i + 1; j < groupCellSpans.size(); j++) {
179 if (GroupCellSpan.doOverlap(span1, (GroupCellSpan) groupCellSpans.get(j))) {
180 return false;
181 }
182 }
183 }
184 return true;
185 }
186
187
191 private static class GroupCellSpan {
192 private final Node group;
193 private final LayoutGraph graph;
194 private final PartitionGrid grid;
195 private final PartitionCellId cellId;
196 private RowDescriptor minRow;
198 private RowDescriptor maxRow;
200 private ColumnDescriptor minColumn;
202 private ColumnDescriptor maxColumn;
204
205 GroupCellSpan(final Node group, final LayoutGraph graph) {
206 this.group = group;
207 this.graph = graph;
208 this.grid = PartitionGrid.getPartitionGrid(graph);
209 determineMinMaxRowAndColumn();
210 this.cellId = grid.createCellSpanId(minRow, minColumn, maxRow, maxColumn);
211 updateCellInsets();
212 }
213
214
217 private void updateCellInsets() {
218 final DataProvider insetsDP = graph.getDataProvider(GroupingKeys.GROUP_NODE_INSETS_DPKEY);
219 final YInsets insets = (insetsDP == null) ? null : (YInsets) insetsDP.get(group);
220 if (insets == null || minRow == null || minColumn == null) {
221 return;
222 }
223
224 if (insets.left > minColumn.getLeftInset()) {
225 minColumn.setLeftInset(insets.left);
226 }
227 if (insets.right > maxColumn.getRightInset()) {
228 maxColumn.setRightInset(insets.right);
229 }
230 if (insets.top > minRow.getTopInset()) {
231 minRow.setTopInset(insets.top);
232 }
233 if (insets.bottom > maxRow.getBottomInset()) {
234 maxRow.setBottomInset(insets.bottom);
235 }
236 }
237
238
243 private void determineMinMaxRowAndColumn() {
244 final YRectangle groupBounds = graph.getRectangle(group);
245
246 final double groupLeftX = groupBounds.getX();
247 final double groupRightX = groupBounds.getX() + groupBounds.getWidth();
248 for (Iterator iter = grid.getColumns().iterator(); iter.hasNext(); ) {
249 final ColumnDescriptor column = (ColumnDescriptor) iter.next();
250 if (column.getOriginalPosition() < groupRightX
251 && groupLeftX < column.getOriginalPosition() + column.getOriginalWidth()) {
252 if (minColumn == null || minColumn.getIndex() > column.getIndex()) {
254 minColumn = column;
255 }
256 if (maxColumn == null || maxColumn.getIndex() < column.getIndex()) {
257 maxColumn = column;
258 }
259 }
260 }
261
262 final double groupTopY = groupBounds.getY();
263 final double groupBottomY = groupBounds.getY() + groupBounds.getHeight();
264 for (Iterator iter = grid.getRows().iterator(); iter.hasNext(); ) {
265 final RowDescriptor row = (RowDescriptor) iter.next();
266 if (row.getOriginalPosition() < groupBottomY
267 && groupTopY < row.getOriginalPosition() + row.getOriginalHeight()) {
268 if (minRow == null || minRow.getIndex() > row.getIndex()) {
270 minRow = row;
271 }
272 if (maxRow == null || maxRow.getIndex() < row.getIndex()) {
273 maxRow = row;
274 }
275 }
276 }
277
278 if (minRow == null || minColumn == null) {
279 throw new IllegalArgumentException();
281 }
282 }
283
284
289 public Node getGroup() {
290 return group;
291 }
292
293
301 public PartitionCellId getCellId() {
302 return cellId;
303 }
304
305
317 public static boolean doOverlap(final GroupCellSpan span1, final GroupCellSpan span2) {
318 return span1.minRow.getIndex() <= span2.maxRow.getIndex()
319 && span2.minRow.getIndex() <= span1.maxRow.getIndex()
320 && span1.minColumn.getIndex() <= span2.maxColumn.getIndex()
321 && span2.minColumn.getIndex() <= span1.maxColumn.getIndex();
322 }
323 }
324 }
325