// Copyright (c) 2001 Oracle Corporation.
package oracle.portal.provider.v1.http;

import java.io.*;
import java.util.*;

import javax.servlet.*;
import javax.servlet.http.*;

import oracle.portal.provider.v1.*;
import oracle.webdb.utils.SimpleStringBuffer;

/**
 * Base class that holds the various attributes for registering {@link
 * ManagedRenderer}s for each render mode.
 *
 * Accessor methods exist for each render mode. These methods allow you to set
 * or get the <code>ManagedRenderer</code> for a specific content type and
 * render mode.
 */
public abstract class PortletPageLocator
{
    private static final int DEFAULT_PAGE_INDEX = 0;

    private static final String[] DEFAULT_TYPES = {PortletRenderer.TYPE_HTML};

    private String mAppPath;
    private String mAppRoot;
    private String mCharSet = PortletRenderer.DEFAULT_CHARACTER_ENCODING;
    private String mContentType = PortletRenderer.TYPE_HTML;
    private int mPageExpires;
    private String mParamName;

    // array of ManagedRenderer Vectors -- one per show mode
    private Vector[] mManagedRendererVectors =
        new Vector[PortletRenderer.TOTAL_MODES + 1];

    /**
     * Null Constructor
     */
    public PortletPageLocator()
    {
    }

    /**
     *  Sets the MIME type to be used as the default content type for all
     *  {@link BaseManagedRenderer}s. This parameter will be inherited by any
     *  of this renderer's <code>BaseManagedRenderer</code>s for which the
     *  corresponding parameter has not been directly specified.

     *  @param contentType MIME type to be used as the default content type for
     *  {@link BaseManagedRenderer}s.
     */
    public void setContentType(String contentType)
    {
        mContentType = contentType;
    }

    /**
     *  Gets the MIME type to be used as the default content type for all
     *  {@link BaseManagedRenderer}s.
     *
     *  @return the MIME type to be used as the default content type for all
     *  {@link BaseManagedRenderer}s.
     */
    public String getContentType()
    {
        return mContentType;
    }

    /**
     *  Sets the default expiration time (in minutes) of a page produced by a
     *  {@link BaseManagedRenderer}. This parameter will be inherited by any of
     *  this renderer's <code>BaseManagedRenderer</code>s for which the
     *  corresponding parameter has not been directly specified.
     *
     *  @param expires the default integer number of minutes that the Portal
     *  should cache the output of a {@link BaseManagedRenderer}. If zero,
     *  indicates that the output should not be cached by default.
     */
    public void setPageExpires(int expires)
    {
        mPageExpires = expires;
    }

    /**
     *  Sets the default expiration time (in minutes) of a page produced by a
     *  {@link BaseManagedRenderer}. This parameter will be inherited by any of
     *  this renderer's <code>BaseManagedRenderer</code>s for which the
     *  corresponding parameter has not been directly specified.
     *
     *  @param expires String representation of the default integer number of
     *  minutes that the Portal should cache the output of a {@link
     *  BaseManagedRenderer}. If zero, indicates that the output should not be
     *  cached by default.
     */
    public void setPageExpires(String expires)
    {
        try
        {
            setPageExpires(Integer.parseInt(expires));
        }
        catch (NumberFormatException nfe)
        {
            setPageExpires(0);
            // no place to log right now -- so just eat this
        }
    }

    /**
     *  Gets the default expiration time (in minutes) of a page produced by a
     *  {@link BaseManagedRenderer}.
     *
     *  @return default expiration time in minutes for output from a {@link
     *  BaseManagedRenderer}. If non-zero, indicates the number of minutes the
     *  page should be cached by the Portal.
     */
    public int getPageExpires()
    {
        return mPageExpires;
    }

    /**
     *  Sets the default root virtual path for {@link Page}
     *  <code>ManagedRenderer</code>s. This parameter will be inherited by any
     *  of this renderer's <code>Page</code>s for which the corresponding
     *  parameter has not been directly specified.
     *
     *  @param path the default root virtual path for {@link Page}s.
     */
    public void setAppPath(String path)
    {
        mAppPath = path;
    }

    /**
     *  Gets the default root virtual path for {@link Page}
     *  <code>ManagedRenderer</code>s.
     *
     *  @return the default root virtual path for {@link Page}s.
     */
    public String getAppPath()
    {
        return mAppPath;
    }

    /**
     *  Sets the default root physical path for {@link Page}
     *  <code>ManagedRenderer</code>s. This parameter will be inherited by any
     *  of this renderer's <code>Page</code>s for which the corresponding
     *  parameter has not been directly specified.
     *
     *  @param path the default root physical path for {@link Page}s.
     */
    public void setAppRoot(String path)
    {
        mAppRoot = path;
    }

    /**
     *  Gets the default root virtual path for {@link Page}
     *  <code>ManagedRenderer</code>s.
     *
     *  @return the default root physical path for {@link Page}s.
     */
    public String getAppRoot()
    {
        return mAppRoot;
    }

    /**
     *  Sets the Default IANA character encoding to be used for for {@link
     *  Page} <code>ManagedRenderer</code>s. This parameter will be inherited
     *  by any of this renderer's <code>Page</code>s for which the
     *  corresponding parameter has not been directly specified.
     *
     *  @param charSet default IANA character encoding for {@link Page}s.
     */
    public void setCharSet(String charSet)
    {
        mCharSet = charSet;
    }

    /**
     *  Gets the Default IANA character encoding to be used for for {@link Page}
     *  <code>ManagedRenderer</code>s.
     *
     *  @return default IANA character encoding for {@link Page}s.
     */
    public String getCharSet()
    {
        return mCharSet;
    }

    /**
     *  Sets the default name of the request parameter that is used to override
     *  the name of the page used by a {@link Page}
     *  <code>ManagedRenderer</code>. Used to communicate the "next" page in a
     *  portlet that uses multiple pages to render itself. This parameter will
     *  be inherited by any of this renderer's <code>Page</code>s for which the
     *  corresponding parameter has not been directly specified.
     *
     *  @param name the default name of the request parameter that is used to
     *  override the page name for {@link Page}s.
     */
    public void setPageParameterName(String name)
    {
        mParamName = name;
    }

    /**
     *  Gets the default name of the request parameter that is used to override
     *  the name of the page used by a {@link Page}
     *  <code>ManagedRenderer</code>.
     *
     *  @return the default name of the request parameter that is used to
     *  override the page name for {@link Page}s.
     */
    public String getPageParameterName()
    {
        return mParamName;
    }

    /**
     * Registers a <code>ManagedRenderer</code> that renders this portlet's
     * "show" mode. The <code>ManagedRenderer</code> will be a {@link
     * JspRenderer} for a page with the given name if the name is suffixed by
     * ".jsp" or ".sqljsp", or a {@link FileRenderer} for a static page with
     * the given name otherwise. The renderer will inherit all this object's
     * 'defaults'.
     *
     * @param page an <code>appPath</code> relative name of a page that
     * renders this portlet's "show" mode.
     * @exception PortletException if an error occurs while initializing a
     * <code>ManagedRenderer</code>.
     */
    public void setShowPage(String page) throws PortletException
    {
        Page p = constructPage(page);
        setShowPage(p);
    }

    /**
     * Registers a <code>ManagedRenderer</code> that renders this portlet's
     * "show" mode.
     *
     * @param renderer a {@link ManagedRenderer} object.
     * @exception PortletException if the given object is not a valid {@link
     * ManagedRenderer} object.
     */
    public void setShowPage(Object renderer) throws PortletException
    {
        if(!(renderer instanceof ManagedRenderer))
        {
            throw new PortletException("setShowPage requires a ManagedRenderer object");
        }
        ManagedRenderer mr = (ManagedRenderer)renderer;
        putEntry(PortletRenderer.MODE_SHOW, mr);
    }

    /**
     * Gets the name of page displayed by a {@link Page}
     * <code>ManagedRenderer</code> for this portlet's "show" mode with the
     * 'default' content type, as returned by the {@link #getContentType()}
     * method.
     *
     * @return an <code>appPath</code> relative name of the corresponding page,
     * or <code>null</code> if no such page exists.
     * @deprecated Use {@link #getShowPage(String)} instead.
     */
    public String getShowPage()
    {
        ManagedRenderer mr = getShowPage(getContentType());
        if (mr != null && mr instanceof Page)
        {
            return ((Page)mr).getName();
        }
        return null;
    }

    /**
     * Gets the <code>ManagedRenderer</code> that renders this portlet's "show" mode with
     * the given content type.
     *
     * @return the <code>ManagedRenderer</code> that renders this portlet's "show" mode with
     * the given content type or <code>null</code> if no such renderer exists.
     */
    public ManagedRenderer getShowPage(String type)
    {
        return getEntry(PortletRenderer.MODE_SHOW, type);
    }

    /**
     * Registers a <code>ManagedRenderer</code> that renders this portlet's
     * "showDetails" mode. The <code>ManagedRenderer</code> will be a {@link
     * JspRenderer} for a page with the given name if the name is suffixed by
     * ".jsp" or ".sqljsp", or a {@link FileRenderer} for a static page with
     * the given name otherwise. The renderer will inherit all this object's
     * 'defaults'.
     *
     * @param page an <code>appPath</code> relative name of a page that
     * renders this portlet's "showDetails" mode.
     * @exception PortletException if an error occurs while initializing a
     * <code>ManagedRenderer</code>.
     */

    public void setShowDetailsPage(String page) throws PortletException
    {
        Page p = constructPage(page);
        setShowDetailsPage(p);
    }

    /**
     * Registers a <code>ManagedRenderer</code> that renders this portlet's "showDetails"
     * mode. If not set, the "show" renderer is used by default.
     *
     * @param renderer a {@link ManagedRenderer} object.
     * @exception PortletException if the given object is not a valid {@link
     * ManagedRenderer} object.
     */
    public void setShowDetailsPage(Object renderer) throws PortletException
    {
        if(!(renderer instanceof ManagedRenderer))
        {
            throw new PortletException("setShowDetailsPage requires a ManagedRenderer object");
        }
        ManagedRenderer mr = (ManagedRenderer)renderer;
        putEntry(PortletRenderer.MODE_SHOW_DETAILS, mr);
    }

    /**
     * Gets the name of page displayed by a {@link Page}
     * <code>ManagedRenderer</code> for this portlet's "showDetails" mode with
     * the 'default' content type, as returned by the {@link #getContentType()}
     * method.
     *
     * @return an <code>appPath</code> relative name of the corresponding page,
     * or <code>null</code> if no such page exists.
     * @deprecated Use {@link #getShowDetailsPage(String)} instead.
     */
    public String getShowDetailsPage()
    {
        ManagedRenderer mr = getShowDetailsPage(getContentType());
        if (mr != null && mr instanceof Page)
        {
            return ((Page)mr).getName();
        }
        return null;
    }

    /**
     * Gets the <code>ManagedRenderer</code> that renders this portlet's "showDetails" mode
     * with the given content type.
     *
     * @return the <code>ManagedRenderer</code> that renders this portlet's "showDetails"
     * mode with the given content type or <code>null</code> if no such
     * renderer exists.
     */
    public ManagedRenderer getShowDetailsPage(String type)
    {
        return getEntry(PortletRenderer.MODE_SHOW_DETAILS, type);
    }

    /**
     * Registers a <code>ManagedRenderer</code> that renders this portlet's
     * "edit" mode. The <code>ManagedRenderer</code> will be a {@link
     * JspRenderer} for a page with the given name if the name is suffixed by
     * ".jsp" or ".sqljsp", or a {@link FileRenderer} for a static page with
     * the given name otherwise. The renderer will inherit all this object's
     * 'defaults'.
     *
     * @param page an <code>appPath</code> relative name of a page that
     * renders this portlet's "edit" mode.
     * @exception PortletException if an error occurs while initializing a
     * <code>ManagedRenderer</code>.
     */
    public void setEditPage(String page) throws PortletException
    {
        Page p = constructPage(page);
        setEditPage(p);
    }

    /**
     * Registers a <code>ManagedRenderer</code> that renders this portlet's "edit" mode. If
     * not set, the "editDefault" renderer is used by default.
     *
     * @param renderer a {@link ManagedRenderer} object.
     * @exception PortletException if the given object is not a valid {@link
     * ManagedRenderer} object.
     */
    public void setEditPage(Object renderer) throws PortletException
    {
        if(!(renderer instanceof ManagedRenderer))
        {
            throw new PortletException("setEditPage requires a ManagedRenderer object");
        }
        ManagedRenderer mr = (ManagedRenderer)renderer;
        putEntry(PortletRenderer.MODE_EDIT, mr);
    }

    /**
     * Gets the name of page displayed by a {@link Page}
     * <code>ManagedRenderer</code> for this portlet's "edit" mode with the
     * 'default' content type, as returned by the {@link #getContentType()}
     * method.
     *
     * @return an <code>appPath</code> relative name of the corresponding page,
     * or <code>null</code> if no such page exists.
     * @deprecated Use {@link #getEditPage(String)} instead.
     */
    public String getEditPage()
    {
        ManagedRenderer mr = getEditPage(getContentType());
        if (mr != null && mr instanceof Page)
        {
            return ((Page)mr).getName();
        }
        return null;
    }

    /**
     * Gets the <code>ManagedRenderer</code> that renders this portlet's "edit" mode with
     * the given content type.
     *
     * @return the <code>ManagedRenderer</code> that renders this portlet's "edit" mode with
     * the given content type or <code>null</code> if no such renderer exists.
     */
    public ManagedRenderer getEditPage(String type)
    {
        return getEntry(PortletRenderer.MODE_EDIT, type);
    }

    /**
     * Registers a <code>ManagedRenderer</code> that renders this portlet's
     * "editDefault" mode. The <code>ManagedRenderer</code> will be a {@link
     * JspRenderer} for a page with the given name if the name is suffixed by
     * ".jsp" or ".sqljsp", or a {@link FileRenderer} for a static page with
     * the given name otherwise. The renderer will inherit all this object's
     * 'defaults'.
     *
     * @param page an <code>appPath</code> relative name of a page that renders
     * this portlet's "editDefault" mode.
     * @exception PortletException if an error occurs while initializing a
     * <code>ManagedRenderer</code>.
     */
    public void setEditDefaultsPage(String page) throws PortletException
    {
        Page p = constructPage(page);
        setEditDefaultsPage(p);
    }

    /**
     * Registers a <code>ManagedRenderer</code> that renders this portlet's "editDefault"
     * mode. If not set, the "edit" renderer is used by default.
     *
     * @param renderer a {@link ManagedRenderer} object.
     * @exception PortletException if the given object is not a valid {@link
     * ManagedRenderer} object.
     */
    public void setEditDefaultsPage(Object renderer) throws PortletException
    {
        if(!(renderer instanceof ManagedRenderer))
        {
            throw new PortletException("setEditDefaultsPage requires a ManagedRenderer object");
        }
        ManagedRenderer mr = (ManagedRenderer)renderer;
        putEntry(PortletRenderer.MODE_EDIT_DEFAULTS, mr);
    }

    /**
     * Gets the name of page displayed by a {@link Page}
     * <code>ManagedRenderer</code> for this portlet's "editDefault" mode with the
     * 'default' content type, as returned by the {@link #getContentType()}
     * method.
     *
     * @return an <code>appPath</code> relative name of the corresponding page,
     * or <code>null</code> if no such page exists.
     * @deprecated Use {@link #getEditDefaultsPage(String)} instead.
     */
    public String getEditDefaultsPage()
    {
        ManagedRenderer mr = getEditDefaultsPage(getContentType());
        if (mr != null && mr instanceof Page)
        {
            return ((Page)mr).getName();
        }
        return null;
    }

    /**
     * Gets the <code>ManagedRenderer</code> that renders this portlet's "editDefault" mode
     * with the given content type.
     *
     * @return the <code>ManagedRenderer</code> that renders this portlet's "editDefault"
     * mode with the given content type or <code>null</code> if no such
     * renderer exists.
     */
    public ManagedRenderer getEditDefaultsPage(String type)
    {
        return getEntry(PortletRenderer.MODE_EDIT_DEFAULTS, type);
    }

    /**
     * Registers a <code>ManagedRenderer</code> that renders this portlet's
     * "preview" mode. The <code>ManagedRenderer</code> will be a {@link
     * JspRenderer} for a page with the given name if the name is suffixed by
     * ".jsp" or ".sqljsp", or a {@link FileRenderer} for a static page with
     * the given name otherwise. The renderer will inherit all this object's
     * 'defaults'.
     *
     * @param page an <code>appPath</code> relative name of a page that
     * renders this portlet's "preview" mode.
     * @exception PortletException if an error occurs while initializing a
     * <code>ManagedRenderer</code>.
     */
    public void setPreviewPage(String page) throws PortletException
    {
        Page p = constructPage(page);
        setPreviewPage(p);
    }

    /**
     * Registers a <code>ManagedRenderer</code> that renders this portlet's "preview" mode.
     *
     * @param renderer a {@link ManagedRenderer} object.
     * @exception PortletException if the given object is not a valid {@link
     * ManagedRenderer} object.
     */
    public void setPreviewPage(Object renderer) throws PortletException
    {
        if(!(renderer instanceof ManagedRenderer))
        {
            throw new PortletException("setPreviewPage requires a ManagedRenderer object");
        }
        ManagedRenderer mr = (ManagedRenderer)renderer;
        putEntry(PortletRenderer.MODE_PREVIEW, mr);
    }

    /**
     * Gets the name of page displayed by a {@link Page}
     * <code>ManagedRenderer</code> for this portlet's "preview" mode with the
     * 'default' content type, as returned by the {@link #getContentType()}
     * method.
     *
     * @return an <code>appPath</code> relative name of the corresponding page,
     * or <code>null</code> if no such page exists.
     * @deprecated Use {@link #getPreviewPage(String)} instead.
     */
    public String getPreviewPage()
    {
        ManagedRenderer mr = getPreviewPage(getContentType());
        if (mr != null && mr instanceof Page)
        {
            return ((Page)mr).getName();
        }
        return null;
    }

    /**
     * Gets the <code>ManagedRenderer</code> that renders this portlet's "preview" mode with
     * the given content type.
     *
     * @return the <code>ManagedRenderer</code> that renders this portlet's "preview" mode
     * with the given content type or <code>null</code> if no such renderer
     * exists.
     */
    public ManagedRenderer getPreviewPage(String type)
    {
        return getEntry(PortletRenderer.MODE_PREVIEW, type);
    }

    /**
     * Registers a <code>ManagedRenderer</code> that renders this portlet's
     * "about" mode. The <code>ManagedRenderer</code> will be a {@link
     * JspRenderer} for a page with the given name if the name is suffixed by
     * ".jsp" or ".sqljsp", or a {@link FileRenderer} for a static page with
     * the given name otherwise. The renderer will inherit all this object's
     * 'defaults'.
     *
     * @param page an <code>appPath</code> relative name of a page that
     * renders this portlet's "about" mode.
     * @exception PortletException if an error occurs while initializing a
     * <code>ManagedRenderer</code>.
     */
    public void setAboutPage(String page) throws PortletException
    {
        Page p = constructPage(page);
        setAboutPage(p);
    }

    /**
     * Registers a <code>ManagedRenderer</code> that renders this portlet's "about" mode.
     *
     * @param renderer a {@link ManagedRenderer} object.
     * @exception PortletException if the given object is not a valid {@link
     * ManagedRenderer} object.
     */
    public void setAboutPage(Object renderer) throws PortletException
    {
        if(!(renderer instanceof ManagedRenderer))
        {
            throw new PortletException("setAboutPage requires a ManagedRenderer object");
        }
        ManagedRenderer mr = (ManagedRenderer)renderer;
        putEntry(PortletRenderer.MODE_ABOUT, mr);
    }

    /**
     * Gets the name of page displayed by a {@link Page}
     * <code>ManagedRenderer</code> for this portlet's "about" mode with the
     * 'default' content type, as returned by the {@link #getContentType()}
     * method.
     *
     * @return an <code>appPath</code> relative name of the corresponding page,
     * or <code>null</code> if no such page exists.
     * @deprecated Use {@link #getAboutPage(String)} instead.
     */
    public String getAboutPage()
    {
        ManagedRenderer mr = getAboutPage(getContentType());
        if (mr != null && mr instanceof Page)
        {
            return ((Page)mr).getName();
        }
        return null;
    }

    /**
     * Gets the <code>ManagedRenderer</code> that renders this portlet's "about" mode with
     * the given content type.
     *
     * @return the <code>ManagedRenderer</code> that renders this portlet's "about" mode
     * with the given content type or <code>null</code> if no such renderer
     * exists.
     */
    public ManagedRenderer getAboutPage(String type)
    {
        return getEntry(PortletRenderer.MODE_ABOUT, type);
    }

    /**
     * Registers a <code>ManagedRenderer</code> that renders this portlet's
     * "help" mode. The <code>ManagedRenderer</code> will be a {@link
     * JspRenderer} for a page with the given name if the name is suffixed by
     * ".jsp" or ".sqljsp", or a {@link FileRenderer} for a static page with
     * the given name otherwise. The renderer will inherit all this object's
     * 'defaults'.
     *
     * @param page an <code>appPath</code> relative name of a page that
     * renders this portlet's "show" mode.
     * @exception PortletException if an error occurs while initializing a
     * <code>ManagedRenderer</code>.
     */
    public void setHelpPage(String page) throws PortletException
    {
        Page p = constructPage(page);
        setHelpPage(p);
    }

    /**
     * Registers a <code>ManagedRenderer</code> that renders this portlet's "help" mode.
     *
     * @param renderer a {@link ManagedRenderer} object.
     * @exception PortletException if the given object is not a valid {@link
     * Page} object.
     */
    public void setHelpPage(Object renderer) throws PortletException
    {
        if(!(renderer instanceof ManagedRenderer))
        {
            throw new PortletException("setHelpPage requires a ManagedRenderer object");
        }
        ManagedRenderer mr = (ManagedRenderer)renderer;
        putEntry(PortletRenderer.MODE_HELP, mr);
    }

    /**
     * Gets the name of page displayed by a {@link Page}
     * <code>ManagedRenderer</code> for this portlet's "help" mode with the
     * 'default' content type, as returned by the {@link #getContentType()}
     * method.
     *
     * @return an <code>appPath</code> relative name of the corresponding page,
     * or <code>null</code> if no such page exists.
     * @deprecated Use {@link #getHelpPage(String)} instead.
     */
    public String getHelpPage()
    {
        ManagedRenderer mr = getHelpPage(getContentType());
        if (mr != null && mr instanceof Page)
        {
            return ((Page)mr).getName();
        }
        return null;
    }

    /**
     * Gets the <code>ManagedRenderer</code> that renders this portlet's "help" mode with
     * the given content type.
     *
     * @return the <code>ManagedRenderer</code> that renders this portlet's "help" mode with
     * the given content type or <code>null</code> if no such renderer exists.
     */
    public ManagedRenderer getHelpPage(String type)
    {
        return getEntry(PortletRenderer.MODE_HELP, type);
    }

    /**
     * Registers a <code>ManagedRenderer</code> that renders this portlet's
     * "link" mode. The <code>ManagedRenderer</code> will be a {@link
     * JspRenderer} for a page with the given name if the name is suffixed by
     * ".jsp" or ".sqljsp", or a {@link FileRenderer} for a static page with
     * the given name otherwise. The renderer will inherit all this object's
     * 'defaults'.
     *
     * @param page an <code>appPath</code> relative name of a page that
     * renders this portlet's "show" mode.
     * @exception PortletException if an error occurs while initializing a
     * <code>ManagedRenderer</code>.
     */
    public void setLinkPage(String page) throws PortletException
    {
        Page p = constructPage(page);
        setLinkPage(p);
    }

    /**
     * Registers a <code>ManagedRenderer</code> that renders this portlet's "link" mode.
     *
     * @param renderer a {@link ManagedRenderer} object.
     * @exception PortletException if the given object is not a valid {@link
     * Page} object.
     */
    public void setLinkPage(Object renderer) throws PortletException
    {
        if(!(renderer instanceof ManagedRenderer))
        {
            throw new PortletException("setLinkPage requires a ManagedRenderer object");
        }
        ManagedRenderer mr = (ManagedRenderer)renderer;
        putEntry(PortletRenderer.MODE_LINK, mr);
    }

    /**
     * Gets the name of page displayed by a {@link Page}
     * <code>ManagedRenderer</code> for this portlet's "link" mode with the
     * 'default' content type, as returned by the {@link #getContentType()}
     * method.
     *
     * @return an <code>appPath</code> relative name of the corresponding page,
     * or <code>null</code> if no such page exists.
     * @deprecated Use {@link #getLinkPage(String)} instead.
     */
    public String getLinkPage()
    {
        ManagedRenderer mr = getLinkPage(getContentType());
        if (mr != null && mr instanceof Page)
        {
            return ((Page)mr).getName();
        }
        return null;
    }

    /**
     * Gets the <code>ManagedRenderer</code> that renders this portlet's "link" mode with
     * the given content type.
     *
     * @return the <code>ManagedRenderer</code> that renders this portlet's "link" mode with
     * the given content type or <code>null</code> if no such renderer exists.
     */
    public ManagedRenderer getLinkPage(String type)
    {
        return getEntry(PortletRenderer.MODE_LINK, type);
    }

    /**
     * Registers a <code>ManagedRenderer</code> that renders this portlet's
     * 'default' page. This page is used when no <code>ManagedRenderer</code>
     * has been specified for the specific mode of the render request. The
     * <code>ManagedRenderer</code> will be a {@link JspRenderer} for a page
     * with the given name if the name is suffixed by ".jsp" or ".sqljsp", or a
     * {@link FileRenderer} for a static page with the given name otherwise.
     * The renderer will inherit all this object's 'defaults'.
     *
     * @param page an <code>appPath</code> relative name of a page that
     * renders this portlet's 'default' page.
     * @exception PortletException if an error occurs while initializing a
     * <code>ManagedRenderer</code>.
     */
    public void setDefaultPage(String page) throws PortletException
    {
        Page p = constructPage(page);
        setDefaultPage(p);
    }

    /**
     * Registers a <code>ManagedRenderer</code> that renders this portlet's 'default page'.
     * This renderer is used when no <code>ManagedRenderer</code> has been specified for the
     * specific mode of the render request.
     *
     * @param renderer a {@link ManagedRenderer} object.
     * @exception PortletException if the given object is not a valid {@link
     * Page} object.
     */
    public void setDefaultPage(Object renderer) throws PortletException
    {
        if(!(renderer instanceof ManagedRenderer))
        {
            throw new PortletException("setDefaultPage requires a ManagedRenderer object");
        }
        ManagedRenderer mr = (ManagedRenderer)renderer;
        putEntry(DEFAULT_PAGE_INDEX, mr);
    }

    /**
     * Gets the name of page displayed by a {@link Page}
     * <code>ManagedRenderer</code> for this portlet's 'default' page with the
     * 'default' content type, as returned by the {@link #getContentType()}
     * method.
     *
     * @return an <code>appPath</code> relative name of the corresponding page,
     * or <code>null</code> if no such page exists.
     * @deprecated Use {@link #getDefaultPage(String)} instead.
     */
    public String getDefaultPage()
    {
        ManagedRenderer mr = getDefaultPage(getContentType());
        if (mr != null && mr instanceof Page)
        {
            return ((Page)mr).getName();
        }
        return null;
    }

    /**
     * Gets the <code>ManagedRenderer</code> that renders this portlet's 'default' page with
     * the given content type. This renderer is used when no <code>ManagedRenderer</code>
     * has been specified for the specific mode of the render request.
     *
     * @return the <code>ManagedRenderer</code> that renders this 'default' page with the
     * given content type or <code>null</code> if no such renderer exists.
     */
    public ManagedRenderer getDefaultPage(String type)
    {
        return getEntry(DEFAULT_PAGE_INDEX, type);
    }

    /**
     *  Returns the virtual path used by the {@link Page}
     *  <code>ManagedRenderer</code> that renders this request.
     *
     *  @param pr the current render request.
     *  @return the virtual path to the page that should be used to render this
     *  request.
     *  @exception PortletException if a page isn't implicitly or explicitly
     *  defined.
     *
     *  @deprecated Use {@link #getManagedRenderer(PortletRenderRequest)}, and
     *  the appropriate accessor methods of the returned {@link
     *  ManagedRenderer}.
     */
    public String getPagePath(PortletRenderRequest pr)
        throws PortletException
    {
        ManagedRenderer mr = getManagedRenderer(pr);
        if(!(mr instanceof Page))
        {
            String tagName = getTagNameForMode(pr.getMode());
            throw new PortletException("Unable to get page path: " + 
                                       tagName +
                                       " renderer is not a Page");
        }
        return ((Page)mr).getPagePath(pr);
    }

    /**
     *  Returns the physical path used by the {@link Page}
     *  <code>ManagedRenderer</code> that renders this request.
     *
     *  @param pr the current render request.
     *  @return the physical path to the page that should be used to render
     *  this request.
     *  @exception PortletException if a page isn't implicitly or explicitly
     *  defined.
     *
     *  @deprecated Use {@link #getManagedRenderer(PortletRenderRequest)}, and
     *  the appropriate accessor methods of the returned {@link
     *  ManagedRenderer}.
     */
    public String getPageRealPath(PortletRenderRequest pr)
        throws PortletException
    {
        ManagedRenderer mr = getManagedRenderer(pr);
        if(!(mr instanceof Page))
        {
            String tagName = getTagNameForMode(pr.getMode());
            throw new PortletException("Unable to get page real path: " + 
                                       tagName +
                                       " renderer is not a Page");
        }
        return ((Page)mr).getPageRealPath(pr);
    }

    /**
     *  Returns the {@link ManagedRenderer} object to be used to render this
     *  request. The method looks for a <code>ManagedRenderer</code> registered
     *  for the appropriate mode whose content type matches the preferred
     *  content type of the render request. If no such
     *  <code>ManagedRenderer</code> exists, the method returns the
     *  <code>ManagedRenderer</code> registered as the 'default' for that
     *  content type, if one exists.
     *
     *  @param pr the current render request.
     *  @return a {@link ManagedRenderer} to be used to render this request.
     *  @exception PortletException if no suitable <code>ManagedRenderer</code>
     *  has been registered.
     */
    public ManagedRenderer getManagedRenderer(PortletRenderRequest pr)
        throws PortletException
    {
        int renderMode = pr.getMode();
        String[] t = pr.getAcceptContentTypes();
        if (t == null) t = DEFAULT_TYPES;

        ManagedRenderer mr = null;
        for (int i = 0; i < t.length && mr == null; i++)
        {
            mr = getEntry(renderMode, t[i]);
        // If we still don't have a page, we may fall back to the page for a
        // different mode, depending on the mode
            if(mr == null)
            {
                switch (renderMode)
                {
                case PortletRenderer.MODE_SHOW_DETAILS:
                    mr = getEntry(PortletRenderer.MODE_SHOW, t[i]);
                    break;
                case PortletRenderer.MODE_EDIT:
                    mr = getEntry(PortletRenderer.MODE_EDIT_DEFAULTS, t[i]);
                    break;
                case PortletRenderer.MODE_EDIT_DEFAULTS:
                    mr = getEntry(PortletRenderer.MODE_EDIT, t[i]);
                    break;
                }
            }
        }
        // If all else fails, fall back to the 'default' renderer
        if(mr == null)
        {
            for (int i = 0; i < t.length && mr == null; i++)
            {
                mr = getEntry(DEFAULT_PAGE_INDEX, t[i]);
            }
        }
        // On error, try to work out the name of the defining tag from the mode
        // name
        if (mr == null)
        {
            String tagName = getTagNameForMode(renderMode);
            throw new PortletException("Renderer improperly configured. " +
                                       "It's missing a " + tagName +
                                       "for type " + t);

        }
        return mr;
    }

    protected Page constructPage(String page) throws PortletException
    {
        String testPath = page.toLowerCase();
        if (testPath.endsWith(".jsp") || testPath.endsWith(".sqljsp"))
        {
            return new JspRenderer(this, page);
        }
        else
        {
            return new FileRenderer(this, page);
        }        
    }

    private String getTagNameForMode(int renderMode) throws PortletException
    {
        String modeName = null;
        try
        {
            modeName = PortletRendererUtil.getModeName(renderMode);
        }
        catch (IllegalArgumentException iae)
        {
            throw new PortletException(iae);
        }
        StringTokenizer tkn = new StringTokenizer(modeName.toLowerCase(), "_");
        SimpleStringBuffer tagName = new SimpleStringBuffer();
        if(tkn.hasMoreTokens())
        {
            tagName.append(tkn.nextToken());
        }
        while(tkn.hasMoreTokens())
        {
            String token = tkn.nextToken();
            tagName.append(Character.toUpperCase(token.charAt(0)));
            tagName.append(token.substring(1));
        }
        tagName.append("Page");
        return tagName.toString();
    }

    private void putEntry(int renderMode, ManagedRenderer mr)
    {
        if(renderMode >= 0 &&
           renderMode <= PortletRenderer.TOTAL_MODES)
        {
            Vector rendererVector = mManagedRendererVectors[renderMode];
            if( rendererVector == null)
            {
                rendererVector = new Vector();
                mManagedRendererVectors[renderMode] = rendererVector;
            }
            rendererVector.addElement(mr);
        }
    }

    private ManagedRenderer getEntry(int renderMode, String contentType)
    {
        if(renderMode >= 0 &&
           renderMode <= PortletRenderer.TOTAL_MODES)
        {
            Vector rendererVector = mManagedRendererVectors[renderMode];
            if( rendererVector != null)
            {
                Enumeration e = rendererVector.elements();
                while(e.hasMoreElements())
                {
                    ManagedRenderer mr = (ManagedRenderer)e.nextElement();
                    if(mr.getContentType().equals(contentType))
                    {
                        return mr;
                    }
                }
            }
        }
        return null;
    }
}
