/*
 * Copyright (C) 2001-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.bebop.portal;

import com.arsdigita.bebop.PageState;
import com.arsdigita.xml.Element;
import java.util.HashMap;
import java.util.Collections;
import java.util.Map;
import java.util.Iterator;
import java.util.Date;

/**
 *  <p>An abstract
 * implementation of {@link PortletRenderer} meant to capture default
 * behavior for portlets defined by users of the {@link Portal}
 * component.</p>
 *
 * <p>The {@link #generateXML} method in this class provides a default
 * XML dressing around a portlet. This dressing is used by Portal's
 * stylesheet rules to generate a title and frame around each portlet.
 * Programmers looking to implement a portlet should extend this class
 * and override {@link #generateBodyXML}.</p>
 *
 * @see Portal
 * @see PortalModel
 * @see PortalModelBuilder
 * @see PortletRenderer
 * @author Justin Ross
 * @author James Parsons
 * @version $Id: //core-platform/dev/src/com/arsdigita/bebop/portal/AbstractPortletRenderer.java#8 $
 */
public abstract class AbstractPortletRenderer extends Portlet {
    // PortletRenderer renderer => Element root
    private static Map s_cachedXMLMap =
        Collections.synchronizedMap(new HashMap());

    // Portlet portlet => Date dateCached.  Note that we choose not
    // to use a synchronized map, since a race here wouldn't matter
    // much.
    private static Map s_dateCachedMap =
        Collections.synchronizedMap(new HashMap());

    void generateXMLBody(PageState ps, Element parentElement) {
        generateBodyXML(ps, parentElement);
    }

    /**
     * Generate XML for the body, not the frame, of this Portlet.  It's
     * the primary intention of this class that programmers override
     * this particular method.
     *
     * @param pageState the PageState representing the current request.
     * @param parentElement the Element to which to attach the XML
     * this method produces.
     * @pre pageState != null
     * @pre parentElement != null
     */
    protected abstract void generateBodyXML(PageState pageState,
                                            Element parentElement);

    private Element makeXML(PageState state) {
        Element holder = new Element("holder");
        setDateCached(new Date());
        super.generateXML(state, holder);
        return holder;
    }

    public void generateXML(PageState state, Element parent) {
        Object key = getCacheKey();
        Element xml;

        if (isDirty()) {
            xml = makeXML(state);
            s_cachedXMLMap.put(key, xml);
        }

        xml = (Element) s_cachedXMLMap.get(key);

        // For the case where isDirty always returns false.
        if (xml == null) {
            xml = makeXML(state);
            s_cachedXMLMap.put(key, xml);
        }

        Iterator iter = xml.getChildren().iterator();

        while (iter.hasNext()) {
            parent.newChildElement((Element) iter.next());
        }
    }

    /**
     * Developer's may use this method to implement cacheing of a
     * portlet's content.  Portlet's default implementation always
     * returns true.
     *
     * @return whether or not the portlet's content is dirty and must
     * be regenerated.
     */
    public boolean isDirty() {
        return true;
    }

    public Object getCacheKey() {
        return null;
    }

    // Can return null.
    public Date getDateCached() {
        return (Date) s_dateCachedMap.get(getCacheKey());
    }

    // Can take null.
    private void setDateCached(Date dateCached) {
        s_dateCachedMap.put(getCacheKey(), dateCached);
    }
}
