Package org.apache.jetspeed.layout.impl

Source Code of org.apache.jetspeed.layout.impl.PortletPlacementContextImpl

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jetspeed.layout.impl;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.jetspeed.layout.Coordinate;
import org.apache.jetspeed.layout.PortletPlacementException;
import org.apache.jetspeed.layout.PortletPlacementContext;
import org.apache.jetspeed.om.page.Fragment;
import org.apache.jetspeed.om.page.Page;
import org.apache.jetspeed.request.RequestContext;


/**
* Portal Placement Context
*
* The purpose of the object is to provide an API that
* can be used to move a portlet fragment on the page.
* This includes moving, adding, removing and getting
* information about portlets that are on the page and
* portlets that are available to be added to the page.
*
* An important note about this object:
* This object is really only intended to be used to do
* a single operation such as "moveabs" or "add".  After
* performing the operation, the hashmap data structures
* are not correct and should not be used for subsequent
* operations.  The reason they are incorrect is that when
* a fragment is moved, the coordinate of fragments below
* it are now different.  These could be updated, but it
* really doesn't serve a purpose since this is a short
* lived object.
*
* @author <a>David Gurney</a>
* @author <a href="mailto:taylor@apache.org">David Sean Taylor</a>
* @version $Id: $
*/
public class PortletPlacementContextImpl implements PortletPlacementContext
{
    private static final int NO_DEPTH_LIMIT = -1;

    /** Logger */
    private Log log = LogFactory.getLog(PortletPlacementContextImpl.class);

  // Columns are reference by index, the rows are held
  // in the array list as shown below:
  //
  // [0]        [1]          [2]
  // ArrayList  ArrayList    ArrayList
  //  Row1Frag   Row1Frag     Row1Frag
  //  Row2Frag   Row2Frag     Row2Frag
  //  Row3Frag   Row3Frag     Row3Frag
  //  ...
  //
  protected Vector[] columnsList = null;
 
  // Used as a convience when looking up a particular fragment
  //
  // Key is Fragment, value is a Coordinate object
  protected Map fragmentCoordinateMap = new HashMap();
 
  // Used as a convience when looking up a particular fragment by id
  //
  // Key is the Fragment id (String), value is the Fragment
  protected Map fragmentMap = new HashMap();
 
  // Number of columns found
  protected int numberOfColumns = -1;
 
    protected Page page;
    protected Fragment containerFragment;
       
  public PortletPlacementContextImpl(RequestContext requestContext)
    throws PortletPlacementException
    {
    init(requestContext, null, NO_DEPTH_LIMIT);
  }
   
    public PortletPlacementContextImpl(RequestContext requestContext, Fragment container, int maxdepth)
    throws PortletPlacementException
    {
        init(requestContext, container, maxdepth);
    }
 
  // Initialize the data structures by getting the fragments
  // from the page manager
  protected void init(RequestContext requestContext, Fragment container, int maxdepth)
    throws PortletPlacementException
    {
        this.page = requestContext.getPage();
        if ( container == null )
        {
            container = page.getRootFragment();
        }
        this.containerFragment = container;
       
        // Recursively process each fragment
        processFragment( container, maxdepth, 0 );

        // The final step is to populate the array with the fragments
    populateArray();
       
        //debugFragments( "init" );
  }
 
  /**
   * Evaluate each portlet fragment and populate the internal data
   * structures
   */
  protected int processFragment( Fragment fragment, int remainingDepth, int rowCount )
    throws PortletPlacementException
    {
        // Process this fragment, then its children
    if(fragment != null)
        {
      // Only process portlet fragments
      //if(fragment.getType().equalsIgnoreCase("portlet"))
            {
        // Get the column and row of this fragment
        int col = getFragmentCol(fragment);
        int row = getFragmentRow(fragment);
           
            if(row < 0)
                {
                    row = rowCount;
                }
            // Add this fragment to the data structures
            addFragmentInternal(fragment, col, row);
                rowCount++;
      }
     
            if ( remainingDepth == NO_DEPTH_LIMIT || remainingDepth > 0 )
            {
                // Process the children
                List children = fragment.getFragments();
                int childRowCount = 0;
                for(int ix = 0; ix < children.size(); ix++)
                {
                    Fragment childFrag = (Fragment)children.get(ix);
       
                    if(childFrag != null)
                    {
                        childRowCount = processFragment(childFrag, ((remainingDepth == NO_DEPTH_LIMIT) ? NO_DEPTH_LIMIT : remainingDepth-1), childRowCount );
                    }
                }
      }
    }
        return rowCount;
  }
   
    public Fragment debugFragments(String debug)
    {      
        StringBuffer out = new StringBuffer();
        out.append( "PortletPlacementContext - " ).append( debug ).append( " - container: " ).append( containerFragment == null ? "<null>" : ( containerFragment.getId() + " / " + containerFragment.getType() ) ).append( "\n" );
        for (int ix = 0; ix < this.columnsList.length; ix++)
        {
            Vector column = this.columnsList[ix];
            out.append( "   column " ).append( ix ).append( "\n" );
            Iterator frags = column.iterator();
            while ( frags.hasNext() )
            {
                Fragment f = (Fragment)frags.next();
                out.append( "      frag " ).append( f == null ? "<null>" : f.getId() );
                if ( f != null )
                    out.append( " / " ).append( f.getType() ).append( " col=" ).append( f.getLayoutColumn() ).append( " row=" ).append( f.getLayoutRow() );
                out.append( "\n" );
            }
        }
        log.debug( out.toString() );
        return containerFragment;
    }

    /**
     * Takes the internal portlet placement state and stores back
     * out to fragment state
     *
     * @return the managed page layout with updated fragment state.
     */
    public Page syncPageFragments()
    {       
        for (int col = 0; col < this.columnsList.length; col++)
        {
            Vector column = this.columnsList[col];
            Iterator frags = column.iterator();
            int row = 0;
            while (frags.hasNext())
            {
                Fragment f = (Fragment)frags.next();
                if (f == null)
                    continue;
                f.setLayoutColumn(col);
                f.setLayoutRow(row);
                row++;
            }
        }
        //debugFragments( "syncPageFragments" );
        return page;
    }
   
  // Helper method
  // The implementation will probably change to get this information
  // directly from the fragment via fragment.getFragmentCol()
  protected int getFragmentRow(Fragment fragment)
    {
        return fragment.getLayoutRow();
  }
 
  // The implementation will probably change to get this information
  // directly from the fragment via fragment.getFragmentRow()
  protected int getFragmentCol(Fragment fragment)
    {
        int col = fragment.getLayoutColumn();
        if (col < 0)
            col = 0;
        return col;
  }
 
  // Adds the fragment to the internal data structures
  protected void addFragmentInternal(Fragment fragment, int col, int row)
    {
    // Create a Coordinate object to hold the row and column
    CoordinateImpl coordinate = new CoordinateImpl(col, row);
   
    // Save the fragment in the lookup hash
    this.fragmentCoordinateMap.put(fragment, coordinate);
    this.fragmentMap.put(fragment.getId(), fragment);
   
    // Establish the maximum column number
    if(col > this.numberOfColumns)
        {
      this.numberOfColumns = col + 1;
    }
  }
 
  /**
   * Now that we know the number of columns, the array can be
   * constructed and populated
   */
  protected void populateArray() throws PortletPlacementException
    {
    if(this.numberOfColumns == -1)
        {
      //throw new PortletPlacementException("no columns found");
            this.numberOfColumns = 1; // create a new column
    }
   
    // Allocate the memory for the array of ArrayLists
    // Add one since it is zero based
    this.columnsList = new Vector[this.numberOfColumns + 1];
   
    // Put an array list into each index
    for(int i = 0; i < this.numberOfColumns + 1; i++)
        {
      this.columnsList[i] = new Vector();
    }
   
    // Get all of the fragments from the hashmap
    Set keys = this.fragmentCoordinateMap.keySet();
    Iterator keyIterator = keys.iterator();
    while(keyIterator.hasNext())
        {
      // The key is a Fragment
      Fragment fragment = (Fragment) keyIterator.next();
     
      // Get the Coordinate associated with this fragment
      Coordinate coordinate = (Coordinate)this.fragmentCoordinateMap.get(fragment);
     
      // Make sure we have both
      if(fragment != null && coordinate != null)
            {
        // Get the array list for the column
        Vector columnArray = this.columnsList[coordinate.getOldCol()];
       
        int row = coordinate.getOldRow();
       
        // Before setting the fragment in the array it might
        // be necessary to add blank rows before this row
        // An ArrayList can only set an element that already exists
        prepareList(columnArray, row);
       
        // Place the fragment in the array list using the row
        columnArray.set(row, fragment);
      }
    }
  }
 
  // Ensures that the array list has at least row number of rows
  protected void prepareList(Vector list, int row)
    {
    if(list != null)
        {
      int size = list.size();
      if(row + 1 > size)
            {
        // Add one to the row since it is zero based
        for(int i = size; i < row + 1; i++)
                {
          list.add(null);
        }
      }
    }
  }
 
  // Ensures that there is room for the fragment at the given row
  // This method will insert null rows as necessary
  protected List makeSpace(Coordinate newCoordinate)
    {
    int newCol = newCoordinate.getNewCol();
    int newRow = newCoordinate.getNewRow();
   
    // Find the column. Note that a new column will never be created
    List column = this.columnsList[newCol];
    if(newRow + 1 > column.size())
        {
      // Need to add rows
      for(int i = column.size(); i < newRow + 1; i++)
            {
        column.add(null);
      }
    }
    return column;
  }
 
    public int addColumns( int col )
        throws PortletPlacementException
    {
        if ( col > this.numberOfColumns )
        {           
            if ( col < 100 ) // arbitrary limit of columns
            {
                // expand
                int prevNumberOfColumns = this.numberOfColumns;
                this.numberOfColumns = col + 1;
               
                Vector [] temp = new Vector[this.numberOfColumns];
                for (int ix = 0; ix < prevNumberOfColumns; ix++)
                    temp[ix] = this.columnsList[ix];
                for (int ix = prevNumberOfColumns; ix < temp.length; ix++)
                    temp[ix] = new Vector();
                this.columnsList = temp;
            }
            else
            {
                throw new PortletPlacementException( "cannot add column - " + col + " is above the limit of columns that this api supports" );
            }
        }
        return col;
    }

  public Coordinate add(Fragment fragment, Coordinate coordinate) throws PortletPlacementException
    {
        int col = coordinate.getNewCol();
        int row = coordinate.getNewRow();
       
        if (this.numberOfColumns == -1)
        {
            this.numberOfColumns = 1;
            this.columnsList = new Vector[this.numberOfColumns];
            col = 0;
        }       
        if (col > this.numberOfColumns)
        {   
            col = addColumns( col );
        }
       
        Vector column = this.columnsList[col];
        if (column != null)
        {
            for (int ix = 0; ix < column.size(); ix++)
            {               
                Fragment frag = (Fragment)column.get(ix);
                if (frag == null)
                    continue;
                Coordinate c = (Coordinate)this.fragmentCoordinateMap.get(frag);
                if (c == null)
                    continue;
                if (c.getNewCol() == row)
                {
                    row++;
                }
               
            }
            // Make sure that the column has room to add the row
            if(row < 0 || row > column.size()) {
              // Add to the end
              column.addElement(fragment);
              row = column.size()-1;
            } else {
              column.add(row, fragment);
            }
            Coordinate newCoord = new CoordinateImpl(col, row, col, row);
            this.fragmentCoordinateMap.put(fragment, newCoord);
            return newCoord;
        }
        return coordinate;
  }
 
  // Adds an existing fragment to the coordinate position
  protected Coordinate addInternal(Fragment fragment, Coordinate coordinate)
    throws PortletPlacementException
    {
    int newCol = coordinate.getNewCol();
    int newRow = coordinate.getNewRow();
   
    // Check to see if the column exists
    if(newCol < 0 || newCol > this.columnsList.length)
        {
      throw new PortletPlacementException("column out of bounds" + fragment.getName());
    }
   
    Vector columnArray = this.columnsList[newCol];

    // Make sure the list has enough room for the set
    prepareList(columnArray, newRow);
   
    columnArray.setElementAt(fragment, newRow);
   
    // Add the fragment to the hash map
    this.fragmentCoordinateMap.put(fragment, coordinate);
   
    return coordinate;
  }

  public Fragment getFragment(String fragmentId) throws PortletPlacementException
    {
    return (Fragment)this.fragmentMap.get(fragmentId);
  }
 
  public Fragment getFragmentAtOldCoordinate(Coordinate coordinate) throws PortletPlacementException
    {
    return getFragmentAtCoordinate(coordinate, true);
  }

  public Fragment getFragmentAtNewCoordinate(Coordinate coordinate) throws PortletPlacementException
    {
    return getFragmentAtCoordinate(coordinate, false);
  }

  protected Fragment getFragmentAtCoordinate(Coordinate coordinate, boolean isOld) throws PortletPlacementException
    {
    int col = -1;
    int row = -1;
    if (isOld == true)
        {
      col = coordinate.getOldCol();
      row = coordinate.getOldRow();
    } else
        {
      col = coordinate.getNewCol();
      row = coordinate.getNewRow();
    }
   
    // Do some sanity checking about the request
    if(col < 0 || col > this.columnsList.length)
        {
      throw new PortletPlacementException("requested column is out of bounds");
    }
   
    // Get the array list associated with the column
    Vector columnArray = this.columnsList[col];
    if(row < 0 || row > columnArray.size())
        {
      throw new PortletPlacementException("requested row is out of bounds");
    }
   
    return (Fragment)columnArray.get(row);
  }
 
  public Fragment getFragmentById(String fragmentId) throws PortletPlacementException
    {
    return (Fragment)this.fragmentMap.get(fragmentId);
  }

  public int getNumberColumns() throws PortletPlacementException
    {
        return numberOfColumns;
    //return this.columnsList.length;
  }

  public int getNumberRows(int col) throws PortletPlacementException
    {
    // Sanity check the column
    if(col < 0 || col > this.columnsList.length)
        {
      throw new PortletPlacementException("column out of bounds");
    }
   
    return this.columnsList[col].size();
  }

  public Coordinate moveAbsolute(Fragment fragment, Coordinate newCoordinate)
    throws PortletPlacementException
    {
    // Find the fragment
    Coordinate oldCoordinate = (Coordinate)this.fragmentCoordinateMap.get(fragment);
    if(oldCoordinate == null)
        {
      throw new PortletPlacementException("could not find fragment");
    }
   
    // Save the old coordinates
    int oldCol = oldCoordinate.getOldCol();
    int oldRow = oldCoordinate.getOldRow();

    // Create a new coordinate object with both the old and new positions
    int newCol = newCoordinate.getNewCol();
    int newRow = newCoordinate.getNewRow();
   
    // Make sure there is a place for the move
    //List oldRowList = makeSpace(newCoordinate);

    List oldRowList = this.columnsList[oldCol];
   
    // Remove the fragment from it's old position
    oldRowList.remove(oldRow);

    // The next two lines must occur after the remove above.  This is
    // because the new and old columns might be the same and the remove
    // will change the number of rows
        if (newCol > this.numberOfColumns)
        {   
            newCol = addColumns( newCol );
        }

    List newRowList = this.columnsList[newCol];
    int numRowsNewColumn = newRowList.size();
   
    // Decide whether an insert or an add is appropriate
    if(newRow > (numRowsNewColumn - 1))
        {
      newRow = numRowsNewColumn;
      // Add a new row
      newRowList.add(fragment);
    }
        else
        {
      // Insert the fragment at the new position
      ((Vector)newRowList).insertElementAt(fragment, newRow);   
    }

        //debugFragments("move absolute ");
       
    // New coordinates after moving
    return new CoordinateImpl(oldCol, oldRow, newCol, newRow);
  }

  protected Coordinate moveDirection(Fragment fragment, int deltaCol, int deltaRow)
    throws PortletPlacementException
    {
    // Find the fragment
    Coordinate foundCoordinate = (Coordinate)this.fragmentCoordinateMap.get(fragment);
    if(foundCoordinate == null)
        {
      throw new PortletPlacementException("could not find fragment");
    }

    // Check the coordinates to make sure that there is room to move down
    int oldCol = foundCoordinate.getOldCol();
    int oldRow = foundCoordinate.getOldRow();
   
    Vector columnArray = this.columnsList[oldCol];
   
    // Check the row and column boundaries to make sure there is room
    // to do the move
    if((oldRow + deltaRow + 1 > columnArray.size()) || ((oldRow + deltaRow) < 0) ||
       (oldCol + deltaCol + 1 > this.columnsList.length) || ((oldCol + deltaCol) < 0))
        {
      // Out of bounds, don't do the move
      Coordinate c = new CoordinateImpl(oldCol, oldRow, oldCol, oldRow);
            //debugFragments("move direction (1)");
            return c;
    }
        else
        {
      Coordinate c = moveAbsolute(fragment, new CoordinateImpl(oldCol, oldRow, oldCol + deltaCol, oldRow + deltaRow));
            //debugFragments("move direction (2)");
            return c;
    }       
  }
 
  public Coordinate moveDown(Fragment fragment) throws PortletPlacementException
    {
    return moveDirection(fragment, 0, 1);
  }

  public Coordinate moveUp(Fragment fragment) throws PortletPlacementException
    {
    return moveDirection(fragment, 0, -1);
  }

  public Coordinate moveLeft(Fragment fragment) throws PortletPlacementException
    {
    return moveDirection(fragment, -1, 0);
  }

  public Coordinate moveRight(Fragment fragment) throws PortletPlacementException
    {
    return moveDirection(fragment, 1, 0);
  }

  public Coordinate remove(Fragment fragment) throws PortletPlacementException
    {
    // Locate the fragment
    Coordinate coordinate = (Coordinate)this.fragmentCoordinateMap.get(fragment);
    if(coordinate == null)
        {
      throw new PortletPlacementException("fragment not found:" + fragment.getName());
    }
   
    int col = coordinate.getOldCol();
    int row = coordinate.getOldRow();
   
    if(col < 0 || col > this.columnsList.length)
        {
      throw new PortletPlacementException("column out of bounds:" + fragment.getName());
    }
   
    Vector columnArray = this.columnsList[col];
    if(row < 0 || row > columnArray.size())
        {
      throw new PortletPlacementException("row out of bounds:" + fragment.getName());
    }
   
    // Remove the fragment from the array
    columnArray.remove(row);
   
    // Remove the fragment from the hashmap
    this.fragmentCoordinateMap.remove(fragment);
    this.fragmentMap.remove(fragment.getId());
   
    return coordinate;
  }

}
TOP

Related Classes of org.apache.jetspeed.layout.impl.PortletPlacementContextImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.