/*
 * Copyright (C) 2002-2004 Red Hat Inc. All Rights Reserved.
 *
 * The contents of this file are subject to the CCM Public
 * License (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.redhat.com/licenses/ccmpl.html.
 *
 * Software distributed under the License is distributed on an
 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
 * or implied. See the License for the specific language
 * governing rights and limitations under the License.
 *
 */
package com.arsdigita.portal;

import com.arsdigita.bebop.portal.PortalModel;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.parameters.BigDecimalParameter;
import com.arsdigita.util.Assert;
import org.apache.log4j.Category;
import java.util.HashMap;
import java.util.Iterator;
import java.util.ArrayList;
import java.math.BigDecimal;

/**
 *
 *
 * <p>A default implementation of {@link
 * com.arsdigita.bebop.portal.PortalModel} that provides a stateful
 * backing to the Bebop portal classes.</p>
 *
 * @see com.arsdigita.bebop.portal.PortalModel
 * @see Portal
 * @see Portlet
 * @author Justin Ross
 * @author James Parsons
 * @version $Id: //core-platform/dev/src/com/arsdigita/portal/DefaultPortalModel.java#9 $
 */
public class DefaultPortalModel implements PortalModel {
    public static final String versionId = "$Id: //core-platform/dev/src/com/arsdigita/portal/DefaultPortalModel.java#9 $ by $Author: dennis $, $DateTime: 2004/04/07 16:07:11 $";

    private static Category s_cat = Category.getInstance
        (DefaultPortalModel.class.getName());

    private Portal m_portal;

    /**
     * Create a new DefaultPortalModel based on the {@link Portal}
     * retrievable using portalIDParam.
     *
     * @param pageState represents the current request.
     * @param portalIDParam the parameter holding the ID for this portal.
     * @pre pageState != null
     * @pre portalIDParam != null
     */
    public DefaultPortalModel
        (PageState pageState, BigDecimalParameter portalIDParam) {

        Assert.assertNotNull(pageState);
        Assert.assertNotNull(portalIDParam);

        BigDecimal portalID = (BigDecimal)pageState.getValue
            (portalIDParam);

        m_portal = Portal.retrieve(portalID);

        Assert.assertNotNull(m_portal);
    }

    /**
     * Create a new DefaultPortalModel based on the {@link Portal}
     * retrievable via portalID.
     *
     * @param portalID the ID of the portal you'd like to retrieve.
     * @pre portalID != null
     */
    public DefaultPortalModel(BigDecimal portalID) {
        Assert.assertNotNull(portalID);

        m_portal = Portal.retrieve(portalID);

        Assert.assertNotNull(m_portal);
    }

    /**
     * Create a new DefaultPortalModel from the provided Portal.
     *
     * @param portal the portal around which to build a PortalModel.
     * @pre portal != null
     */
    public DefaultPortalModel(Portal portal) {
        Assert.assertNotNull(portal);

        m_portal = portal;
    }

    /**
     * Returns an iterator over a set of Portlets for this
     * PortalModel's Portal.
     *
     * @return an Iterator over a this Portal's Portlets.
     * @post return != null
     */
    public Iterator getPortletRenderers() {
        PortletCollection portlets = m_portal.getPortlets();
        ArrayList portletList = new ArrayList();

        while (portlets.next()) {
            Portlet portlet = portlets.getPortlet();
            //Portlet portlet = getPossiblyCachedPortlet(portlets);

            portletList.add(portlet.getPortletRenderer());
        }

        return portletList.iterator();
    }

    // Cacheing for getPortletRenderers().  This does no cache
    // eviction right now.
    //
    // NOTE: The cacheing scheme used for portlets is 'opt-out-able'
    // instead of 'opt-in-able'.  I did this on the notion that:
    //
    //   * Many portlets will access the database individually.  Or do
    //     other work individually.  Since these operations cannot be
    //     performed in aggregate, they have the potential to make a
    //     page with 15 portlets pretty slow.
    //
    //   * Many portlets will remain 'fresh' for at least a small
    //     amount of time.
    //
    //   * Portlet authors ought to be able to forget about cacheing
    //     in the common case, which I think the above two points
    //     describe.  Portlets can be made to opt out of cacheing.
    //
    // NOTE: At present we are not using this cacheing because there
    // is not a way to use data objects, and thus domain objects,
    // across transactions, or to set them read-only.  When it becomes
    // possible to do this, we will modify the code below and turn on
    // cacheing.

    // These must be accessed only in the synchronized blocks below.
    private static HashMap s_cachedPortlets = new HashMap();
    // private static HashMap s_portletExpirationDates = new HashMap();

    // This method is not currently used, so I'm commenting it out. --
    // 2002-11-26
//     private Portlet getPossiblyCachedPortlet(PortletCollection portlets) {
//         BigDecimal portletID = portlets.getID();

//         synchronized(s_cachedPortlets) {
//             synchronized(s_portletExpirationDates) {
//                 Long expirationDateLong =
//                     (Long)s_portletExpirationDates.get(portletID);

//                 long expirationDate = 0;

//                 if (expirationDateLong != null) {
//                     expirationDate = expirationDateLong.longValue();
//                 }

//                 long now = System.currentTimeMillis();

//                 boolean cacheHasPortlet = s_cachedPortlets.containsKey
//                     (portletID);

//                 if (cacheHasPortlet && now < expirationDate) {
//                     return (Portlet)s_cachedPortlets.get(portletID);
//                 }
//             }
//         }

//         // If we found something fresh in the cache, we don't get to here.

//         Portlet portlet = portlets.getPortlet();

//         synchronized(s_cachedPortlets) {
//             synchronized(s_portletExpirationDates) {
//                 s_portletExpirationDates.put
//                     (portletID, new Long(portlet.newExpirationDate()));
//                 s_cachedPortlets.put(portletID, portlet);
//             }
//         }

//         return portlet;
//     }

    /**
     * Get the title of this PortalModel's Portal.
     *
     * @return the title of this Portal.
     * @post return != null
     */
    public String getTitle() {
        return m_portal.getTitle();
    }
}
