1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.jetspeed.layout.impl;
18
19 import java.util.HashMap;
20 import java.util.Iterator;
21 import java.util.List;
22 import java.util.Set;
23 import java.util.Vector;
24 import java.util.Map;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.apache.jetspeed.layout.Coordinate;
29 import org.apache.jetspeed.layout.PortletPlacementException;
30 import org.apache.jetspeed.layout.PortletPlacementContext;
31 import org.apache.jetspeed.om.page.Fragment;
32 import org.apache.jetspeed.om.page.Page;
33 import org.apache.jetspeed.request.RequestContext;
34
35
36 /***
37 * Portal Placement Context
38 *
39 * The purpose of the object is to provide an API that
40 * can be used to move a portlet fragment on the page.
41 * This includes moving, adding, removing and getting
42 * information about portlets that are on the page and
43 * portlets that are available to be added to the page.
44 *
45 * An important note about this object:
46 * This object is really only intended to be used to do
47 * a single operation such as "moveabs" or "add". After
48 * performing the operation, the hashmap data structures
49 * are not correct and should not be used for subsequent
50 * operations. The reason they are incorrect is that when
51 * a fragment is moved, the coordinate of fragments below
52 * it are now different. These could be updated, but it
53 * really doesn't serve a purpose since this is a short
54 * lived object.
55 *
56 * @author <a>David Gurney</a>
57 * @author <a href="mailto:taylor@apache.org">David Sean Taylor</a>
58 * @version $Id: $
59 */
60 public class PortletPlacementContextImpl implements PortletPlacementContext
61 {
62 private static final int NO_DEPTH_LIMIT = -1;
63
64 /*** Logger */
65 private Log log = LogFactory.getLog(PortletPlacementContextImpl.class);
66
67
68
69
70
71
72
73
74
75
76
77 protected Vector[] columnsList = null;
78
79
80
81
82 protected Map fragmentCoordinateMap = new HashMap();
83
84
85
86
87 protected Map fragmentMap = new HashMap();
88
89
90 protected int numberOfColumns = -1;
91
92 protected Page page;
93 protected Fragment containerFragment;
94
95 public PortletPlacementContextImpl(RequestContext requestContext)
96 throws PortletPlacementException
97 {
98 init(requestContext, null, NO_DEPTH_LIMIT);
99 }
100
101 public PortletPlacementContextImpl(RequestContext requestContext, Fragment container, int maxdepth)
102 throws PortletPlacementException
103 {
104 init(requestContext, container, maxdepth);
105 }
106
107
108
109 protected void init(RequestContext requestContext, Fragment container, int maxdepth)
110 throws PortletPlacementException
111 {
112 this.page = requestContext.getPage();
113 if ( container == null )
114 {
115 container = page.getRootFragment();
116 }
117 this.containerFragment = container;
118
119
120 processFragment( container, maxdepth, 0 );
121
122
123 populateArray();
124
125
126 }
127
128 /***
129 * Evaluate each portlet fragment and populate the internal data
130 * structures
131 */
132 protected int processFragment( Fragment fragment, int remainingDepth, int rowCount )
133 throws PortletPlacementException
134 {
135
136 if(fragment != null)
137 {
138
139
140 {
141
142 int col = getFragmentCol(fragment);
143 int row = getFragmentRow(fragment);
144
145 if(row < 0)
146 {
147 row = rowCount;
148 }
149
150 addFragmentInternal(fragment, col, row);
151 rowCount++;
152 }
153
154 if ( remainingDepth == NO_DEPTH_LIMIT || remainingDepth > 0 )
155 {
156
157 List children = fragment.getFragments();
158 int childRowCount = 0;
159 for(int ix = 0; ix < children.size(); ix++)
160 {
161 Fragment childFrag = (Fragment)children.get(ix);
162
163 if(childFrag != null)
164 {
165 childRowCount = processFragment(childFrag, ((remainingDepth == NO_DEPTH_LIMIT) ? NO_DEPTH_LIMIT : remainingDepth-1), childRowCount );
166 }
167 }
168 }
169 }
170 return rowCount;
171 }
172
173 public Fragment debugFragments(String debug)
174 {
175 StringBuffer out = new StringBuffer();
176 out.append( "PortletPlacementContext - " ).append( debug ).append( " - container: " ).append( containerFragment == null ? "<null>" : ( containerFragment.getId() + " / " + containerFragment.getType() ) ).append( "\n" );
177 for (int ix = 0; ix < this.columnsList.length; ix++)
178 {
179 Vector column = this.columnsList[ix];
180 out.append( " column " ).append( ix ).append( "\n" );
181 Iterator frags = column.iterator();
182 while ( frags.hasNext() )
183 {
184 Fragment f = (Fragment)frags.next();
185 out.append( " frag " ).append( f == null ? "<null>" : f.getId() );
186 if ( f != null )
187 out.append( " / " ).append( f.getType() ).append( " col=" ).append( f.getLayoutColumn() ).append( " row=" ).append( f.getLayoutRow() );
188 out.append( "\n" );
189 }
190 }
191 log.debug( out.toString() );
192 return containerFragment;
193 }
194
195 /***
196 * Takes the internal portlet placement state and stores back
197 * out to fragment state
198 *
199 * @return the managed page layout with updated fragment state.
200 */
201 public Page syncPageFragments()
202 {
203 for (int col = 0; col < this.columnsList.length; col++)
204 {
205 Vector column = this.columnsList[col];
206 Iterator frags = column.iterator();
207 int row = 0;
208 while (frags.hasNext())
209 {
210 Fragment f = (Fragment)frags.next();
211 if (f == null)
212 continue;
213 f.setLayoutColumn(col);
214 f.setLayoutRow(row);
215 row++;
216 }
217 }
218
219 return page;
220 }
221
222
223
224
225 protected int getFragmentRow(Fragment fragment)
226 {
227 return fragment.getLayoutRow();
228 }
229
230
231
232 protected int getFragmentCol(Fragment fragment)
233 {
234 int col = fragment.getLayoutColumn();
235 if (col < 0)
236 col = 0;
237 return col;
238 }
239
240
241 protected void addFragmentInternal(Fragment fragment, int col, int row)
242 {
243
244 CoordinateImpl coordinate = new CoordinateImpl(col, row);
245
246
247 this.fragmentCoordinateMap.put(fragment, coordinate);
248 this.fragmentMap.put(fragment.getId(), fragment);
249
250
251 if(col > this.numberOfColumns)
252 {
253 this.numberOfColumns = col + 1;
254 }
255 }
256
257 /***
258 * Now that we know the number of columns, the array can be
259 * constructed and populated
260 */
261 protected void populateArray() throws PortletPlacementException
262 {
263 if(this.numberOfColumns == -1)
264 {
265
266 this.numberOfColumns = 1;
267 }
268
269
270
271 this.columnsList = new Vector[this.numberOfColumns + 1];
272
273
274 for(int i = 0; i < this.numberOfColumns + 1; i++)
275 {
276 this.columnsList[i] = new Vector();
277 }
278
279
280 Set keys = this.fragmentCoordinateMap.keySet();
281 Iterator keyIterator = keys.iterator();
282 while(keyIterator.hasNext())
283 {
284
285 Fragment fragment = (Fragment) keyIterator.next();
286
287
288 Coordinate coordinate = (Coordinate)this.fragmentCoordinateMap.get(fragment);
289
290
291 if(fragment != null && coordinate != null)
292 {
293
294 Vector columnArray = this.columnsList[coordinate.getOldCol()];
295
296 int row = coordinate.getOldRow();
297
298
299
300
301 prepareList(columnArray, row);
302
303
304 columnArray.set(row, fragment);
305 }
306 }
307 }
308
309
310 protected void prepareList(Vector list, int row)
311 {
312 if(list != null)
313 {
314 int size = list.size();
315 if(row + 1 > size)
316 {
317
318 for(int i = size; i < row + 1; i++)
319 {
320 list.add(null);
321 }
322 }
323 }
324 }
325
326
327
328 protected List makeSpace(Coordinate newCoordinate)
329 {
330 int newCol = newCoordinate.getNewCol();
331 int newRow = newCoordinate.getNewRow();
332
333
334 List column = this.columnsList[newCol];
335 if(newRow + 1 > column.size())
336 {
337
338 for(int i = column.size(); i < newRow + 1; i++)
339 {
340 column.add(null);
341 }
342 }
343 return column;
344 }
345
346 public int addColumns( int col )
347 throws PortletPlacementException
348 {
349 if ( col > this.numberOfColumns )
350 {
351 if ( col < 100 )
352 {
353
354 int prevNumberOfColumns = this.numberOfColumns;
355 this.numberOfColumns = col + 1;
356
357 Vector [] temp = new Vector[this.numberOfColumns];
358 for (int ix = 0; ix < prevNumberOfColumns; ix++)
359 temp[ix] = this.columnsList[ix];
360 for (int ix = prevNumberOfColumns; ix < temp.length; ix++)
361 temp[ix] = new Vector();
362 this.columnsList = temp;
363 }
364 else
365 {
366 throw new PortletPlacementException( "cannot add column - " + col + " is above the limit of columns that this api supports" );
367 }
368 }
369 return col;
370 }
371
372 public Coordinate add(Fragment fragment, Coordinate coordinate) throws PortletPlacementException
373 {
374 int col = coordinate.getNewCol();
375 int row = coordinate.getNewRow();
376
377 if (this.numberOfColumns == -1)
378 {
379 this.numberOfColumns = 1;
380 this.columnsList = new Vector[this.numberOfColumns];
381 col = 0;
382 }
383 if (col > this.numberOfColumns)
384 {
385 col = addColumns( col );
386 }
387
388 Vector column = this.columnsList[col];
389 if (column != null)
390 {
391 for (int ix = 0; ix < column.size(); ix++)
392 {
393 Fragment frag = (Fragment)column.get(ix);
394 if (frag == null)
395 continue;
396 Coordinate c = (Coordinate)this.fragmentCoordinateMap.get(frag);
397 if (c == null)
398 continue;
399 if (c.getNewCol() == row)
400 {
401 row++;
402 }
403
404 }
405
406 if(row < 0 || row > column.size()) {
407
408 column.addElement(fragment);
409 row = column.size()-1;
410 } else {
411 column.add(row, fragment);
412 }
413 Coordinate newCoord = new CoordinateImpl(col, row, col, row);
414 this.fragmentCoordinateMap.put(fragment, newCoord);
415 return newCoord;
416 }
417 return coordinate;
418 }
419
420
421 protected Coordinate addInternal(Fragment fragment, Coordinate coordinate)
422 throws PortletPlacementException
423 {
424 int newCol = coordinate.getNewCol();
425 int newRow = coordinate.getNewRow();
426
427
428 if(newCol < 0 || newCol > this.columnsList.length)
429 {
430 throw new PortletPlacementException("column out of bounds" + fragment.getName());
431 }
432
433 Vector columnArray = this.columnsList[newCol];
434
435
436 prepareList(columnArray, newRow);
437
438 columnArray.setElementAt(fragment, newRow);
439
440
441 this.fragmentCoordinateMap.put(fragment, coordinate);
442
443 return coordinate;
444 }
445
446 public Fragment getFragment(String fragmentId) throws PortletPlacementException
447 {
448 return (Fragment)this.fragmentMap.get(fragmentId);
449 }
450
451 public Fragment getFragmentAtOldCoordinate(Coordinate coordinate) throws PortletPlacementException
452 {
453 return getFragmentAtCoordinate(coordinate, true);
454 }
455
456 public Fragment getFragmentAtNewCoordinate(Coordinate coordinate) throws PortletPlacementException
457 {
458 return getFragmentAtCoordinate(coordinate, false);
459 }
460
461 protected Fragment getFragmentAtCoordinate(Coordinate coordinate, boolean isOld) throws PortletPlacementException
462 {
463 int col = -1;
464 int row = -1;
465 if (isOld == true)
466 {
467 col = coordinate.getOldCol();
468 row = coordinate.getOldRow();
469 } else
470 {
471 col = coordinate.getNewCol();
472 row = coordinate.getNewRow();
473 }
474
475
476 if(col < 0 || col > this.columnsList.length)
477 {
478 throw new PortletPlacementException("requested column is out of bounds");
479 }
480
481
482 Vector columnArray = this.columnsList[col];
483 if(row < 0 || row > columnArray.size())
484 {
485 throw new PortletPlacementException("requested row is out of bounds");
486 }
487
488 return (Fragment)columnArray.get(row);
489 }
490
491 public Fragment getFragmentById(String fragmentId) throws PortletPlacementException
492 {
493 return (Fragment)this.fragmentMap.get(fragmentId);
494 }
495
496 public int getNumberColumns() throws PortletPlacementException
497 {
498 return numberOfColumns;
499
500 }
501
502 public int getNumberRows(int col) throws PortletPlacementException
503 {
504
505 if(col < 0 || col > this.columnsList.length)
506 {
507 throw new PortletPlacementException("column out of bounds");
508 }
509
510 return this.columnsList[col].size();
511 }
512
513 public Coordinate moveAbsolute(Fragment fragment, Coordinate newCoordinate)
514 throws PortletPlacementException
515 {
516
517 Coordinate oldCoordinate = (Coordinate)this.fragmentCoordinateMap.get(fragment);
518 if(oldCoordinate == null)
519 {
520 throw new PortletPlacementException("could not find fragment");
521 }
522
523
524 int oldCol = oldCoordinate.getOldCol();
525 int oldRow = oldCoordinate.getOldRow();
526
527
528 int newCol = newCoordinate.getNewCol();
529 int newRow = newCoordinate.getNewRow();
530
531
532
533
534 List oldRowList = this.columnsList[oldCol];
535
536
537 oldRowList.remove(oldRow);
538
539
540
541
542 if (newCol > this.numberOfColumns)
543 {
544 newCol = addColumns( newCol );
545 }
546
547 List newRowList = this.columnsList[newCol];
548 int numRowsNewColumn = newRowList.size();
549
550
551 if(newRow > (numRowsNewColumn - 1))
552 {
553 newRow = numRowsNewColumn;
554
555 newRowList.add(fragment);
556 }
557 else
558 {
559
560 ((Vector)newRowList).insertElementAt(fragment, newRow);
561 }
562
563
564
565
566 return new CoordinateImpl(oldCol, oldRow, newCol, newRow);
567 }
568
569 protected Coordinate moveDirection(Fragment fragment, int deltaCol, int deltaRow)
570 throws PortletPlacementException
571 {
572
573 Coordinate foundCoordinate = (Coordinate)this.fragmentCoordinateMap.get(fragment);
574 if(foundCoordinate == null)
575 {
576 throw new PortletPlacementException("could not find fragment");
577 }
578
579
580 int oldCol = foundCoordinate.getOldCol();
581 int oldRow = foundCoordinate.getOldRow();
582
583 Vector columnArray = this.columnsList[oldCol];
584
585
586
587 if((oldRow + deltaRow + 1 > columnArray.size()) || ((oldRow + deltaRow) < 0) ||
588 (oldCol + deltaCol + 1 > this.columnsList.length) || ((oldCol + deltaCol) < 0))
589 {
590
591 Coordinate c = new CoordinateImpl(oldCol, oldRow, oldCol, oldRow);
592
593 return c;
594 }
595 else
596 {
597 Coordinate c = moveAbsolute(fragment, new CoordinateImpl(oldCol, oldRow, oldCol + deltaCol, oldRow + deltaRow));
598
599 return c;
600 }
601 }
602
603 public Coordinate moveDown(Fragment fragment) throws PortletPlacementException
604 {
605 return moveDirection(fragment, 0, 1);
606 }
607
608 public Coordinate moveUp(Fragment fragment) throws PortletPlacementException
609 {
610 return moveDirection(fragment, 0, -1);
611 }
612
613 public Coordinate moveLeft(Fragment fragment) throws PortletPlacementException
614 {
615 return moveDirection(fragment, -1, 0);
616 }
617
618 public Coordinate moveRight(Fragment fragment) throws PortletPlacementException
619 {
620 return moveDirection(fragment, 1, 0);
621 }
622
623 public Coordinate remove(Fragment fragment) throws PortletPlacementException
624 {
625
626 Coordinate coordinate = (Coordinate)this.fragmentCoordinateMap.get(fragment);
627 if(coordinate == null)
628 {
629 throw new PortletPlacementException("fragment not found:" + fragment.getName());
630 }
631
632 int col = coordinate.getOldCol();
633 int row = coordinate.getOldRow();
634
635 if(col < 0 || col > this.columnsList.length)
636 {
637 throw new PortletPlacementException("column out of bounds:" + fragment.getName());
638 }
639
640 Vector columnArray = this.columnsList[col];
641 if(row < 0 || row > columnArray.size())
642 {
643 throw new PortletPlacementException("row out of bounds:" + fragment.getName());
644 }
645
646
647 columnArray.remove(row);
648
649
650 this.fragmentCoordinateMap.remove(fragment);
651 this.fragmentMap.remove(fragment.getId());
652
653 return coordinate;
654 }
655
656 }