/* Copyright (c) 2000, 2001, Oracle Corporation. All rights reserved. */

package oracle.portal.provider.v2.render;

import java.io.*;
import java.net.*;
import java.util.*;
import java.lang.reflect.Method;

import oracle.portal.provider.v2.*;
import oracle.portal.provider.v2.resource.*;
import oracle.portal.provider.v2.security.AccessControlException;
import oracle.portal.provider.v2.personalize.PortletPersonalizationManager;
import oracle.portal.provider.v2.personalize.NameValuePersonalizationObject;
import oracle.portal.provider.v2.personalize.PortletReference;
import oracle.portal.provider.v2.url.UrlUtils;
import oracle.portal.utils.v2.ContainerException;
import oracle.portal.utils.NameValue;
import oracle.portal.log.LogManager;
import oracle.webdb.utils.SimpleStringBuffer;
import oracle.webdb.utils.HTTPUtils;

/**
 * Contains utilities useful for rendering.
 */
public class PortletRendererUtil
{

    public static final int PAGE_LINK = 0;
    public static final int DESIGN_LINK = 1;
    public static final int REQUEST_LINK = 2;
    public static final int LOGIN_LINK = 3;
    public static final int BACK_LINK = 4;
    public static final int EVENT_LINK = 5;


    private static final String CUSTOMIZE_FORM_NAME = "custform";

    /**
     * prefix used for url parameters that correspond
     * to private portlet parameters.
     */
    public static final String PORTLET_PARAMETER_PREFIX = "_piref";

    /**
     * Container renderer to be used by portlet header/footer rendering methods
     * This is thread safe because this class does not maintain any state
     */
    private static DefaultContainerRenderer sContainerRenderer = new DefaultContainerRenderer();

    /**
     *  Utility method for rendering the Customize form.  This method should be
     *  used in conjunction with renderCustomizeFormFooter() to work correctly.
     *  This routine emits the proper html FORM tag, appropriate hidden fields,
     *  the standard look/feel for the Form container, and the customize form
     *  'APPLY', 'OK', and 'Cancel' button.  A submitted form (via 'OK' or 'APPLY')
     *  will recall the same portlet renderer with the same mode (EDIT or EDIT_DEFAULTS)
     *  passing the action in the hidden field named via <i>actionParam</i>.  Clients
     *  check the value of <i>actionParam</i> to determine if an 'OK' or 'APPLY' was
     *  clicked. A cancelled form isn't resubmitted to the renderer.  Rather the "Cancel" button
     *  links directly back to the page from whence the customize was invoked.
     *
     *  @param request the portlet render request.
     *  @param writer the response writer used to output the customize form header.
     *  @param title the to display in the header (titlebar)
     *  @param actionParam the name of the hidden field that will hold the value
     *  that indicates which button was clicked.  The value is 'OK' if the OK button
     *  was clicked.  The value is 'APPLY' if the APPLY button is clicked
     *  @param additionalHidden a String containing the HTML for additional hidden fields
     *  needed by the client.
     *  @param helpURL  a URL to this customize form's help screen.  If null the help link
     *  is not displayed.
     *
     * @deprecated use <code>DefaultContainerRenderer.renderEditHeader()</code>
     */
    public static void renderCustomizeFormHeader(PortletRenderRequest request,
                                                 Writer writer,
                                                 String title,
                                                 String actionParam,
                                                 String additionalHidden,
                                                 String helpURL)
        throws IOException
    {
        // NOTE: Original implementation did not do anything with the
        //       additionalHidden parameter. Consequently, this value is
        //       not passed into the container renderer

        PrintWriter out = getPrintWriter(request, writer);
        HashMap params = new HashMap(10);
        params.put(PortletContainerRenderer.RENDER_CUSTOMIZE, 
                   new Boolean(true));

        if (title != null)
        {
            params.put(PortletContainerRenderer.PORTLET_TITLE, title);
        }

        if (actionParam != null)
        {
            params.put(PortletContainerRenderer.ACTION_PARAM_NAME, actionParam);
        }

        if (helpURL != null)
        {
            params.put(PortletContainerRenderer.HELP_URL, helpURL);
        }

        try
        {
            getContainerRenderer(request).renderEditHeader(request, out, params);
        }
        catch (PortletException e)
        {
            // can't throw PortletException w/o breaking backwards compatibility.
            // get as much info from exception as we can & pass it on.
            e.printStackTrace();
            throw new IOException(e.getMessage());
        }
    }

    /**
     *  Utility method for completing the rendering of the Customize form.
     *  This method should be
     *  used in conjunction with renderCustomizeFormHeader() to work correctly.
     *  This routine emits the proper html tags to close the FORM including
     *  the standard look/feel for the Form container.
     *
     *  @param request the portlet render request.
     *  @param writer the response writer used to output the customize form header.
     *
     * @deprecated use <code>PortletContainerRenderer.renderEditFooter()</code>
     */
    public static void renderCustomizeFormFooter(PortletRenderRequest request,
                                                 Writer writer)
        throws IOException
    {
        PrintWriter out = getPrintWriter(request, writer);
        String title = getModeTitle(request);

        HashMap params = new HashMap(10);
        if (title != null)
        {
            params.put(PortletContainerRenderer.PORTLET_TITLE, title);
        }
        params.put(PortletContainerRenderer.RENDER_CUSTOMIZE,
                   new Boolean(true));

        try
        {
            getContainerRenderer(request).renderEditFooter(request, out, params);
        }
        catch (PortletException e)
        {
            // can't throw PortletException w/o breaking backwards compatibility.
            // get as much info from exception as we can & pass it on.
            e.printStackTrace();
            throw new IOException(e.getMessage());
        }
    }

    /**
     *  Utility method for rendering the About/Help form.  This method should be
     *  used in conjunction with renderInformationFormFooter() to work correctly.
     *  This routine emits the proper html FORM tag, appropriate hidden fields,
     *  the standard look/feel for the Form container, and the customize form
     *  'Close' button.
     *
     * @deprecated use <code>DefaultContainerRenderer.renderInformationFormHeader()</code>
     */
    public static void renderInformationFormHeader(PortletRenderRequest request,
                                                   Writer writer,
                                                   String formName,
                                                   String title,
                                                   String actionParam,
                                                   String helpURL)
        throws IOException
    {
        PrintWriter out = getPrintWriter(request, writer);
        HashMap params = new HashMap(10);

        if (title != null)
        {
            params.put(PortletContainerRenderer.PORTLET_TITLE, title);
        }

        if (formName != null)
        {
            params.put(PortletContainerRenderer.EDIT_FORM_NAME, formName);
        }

        if (actionParam != null)
        {
            params.put(PortletContainerRenderer.ACTION_PARAM_NAME, actionParam);
        }

        if (helpURL != null)
        {
            params.put(PortletContainerRenderer.HELP_URL, helpURL);
        }

        try
        {
            PortletContainerRenderer pcr = getContainerRenderer(request);

            // renderInformationFormFooter() is a protected method of
            // DefaultContainerRenderer and it's subclasses. If the container
            // specified by the portlet is not a subclass of DefaultContainerRenderer
            // then substitute an instance of DefaultContainerRenderer
            if (pcr instanceof DefaultContainerRenderer)
            {
                // renderer is, or is derived from, DefaultContainerRenderer.
                // cast to DefaultContainerRenderer so we can all the appropriate
                // method
                DefaultContainerRenderer defaultPcr = (DefaultContainerRenderer)pcr;
                defaultPcr.renderInformationFormHeader(request, out, params);
            }
            else
            {
                // portlet's container renderer is not derived from
                // DefaultContainerRenderer so substitute use an instance of
                // DefaultContainerRenderer
                sContainerRenderer.renderInformationFormHeader(request,
                                                               out,
                                                               params);
            }
        }
        catch (PortletException e)
        {
            // can't throw PortletException w/o breaking backwards compatibility.
            // get as much info from exception as we can & pass it on.
            e.printStackTrace();
            throw new IOException(e.getMessage());
        }
    }

    /**
     *  Utility method for completing the rendering of the Help/About information form.
     *  This method should be
     *  used in conjunction with renderInformationFormHeader() to work correctly.
     *  This routine emits the proper html tags to close the FORM including
     *  the standard look/feel for the Form container.
     *
     *  @param request the portlet render request.
     *  @param writer the response writer used to output the customize form header.
     *
     * @deprecated use <code>DefaultContainerRenderer.renderInformationFormFooter()</code>
     */
    public static void renderInformationFormFooter(PortletRenderRequest request,
                                                   Writer writer)
        throws IOException
    {
        PrintWriter out = getPrintWriter(request, writer);
        HashMap params = new HashMap(10);

        try
        {
            PortletContainerRenderer pcr = getContainerRenderer(request);

            // renderInformationFormFooter() is a protected method of
            // DefaultContainerRenderer and it's subclasses. If the container
            // specified by the portlet is not a subclass of DefaultContainerRenderer
            // then substitute an instance of DefaultContainerRenderer
            if (pcr instanceof DefaultContainerRenderer)
            {
                // renderer is, or is derived from, DefaultContainerRenderer.
                // cast to DefaultContainerRenderer so we can all the appropriate
                // method
                DefaultContainerRenderer defaultPcr = (DefaultContainerRenderer)pcr;
                defaultPcr.renderInformationFormFooter(request, out, params);
            }
            else
            {
                // portlet's container renderer is not derived from
                // DefaultContainerRenderer so substitute use an instance of
                // DefaultContainerRenderer
                sContainerRenderer.renderInformationFormFooter(request,
                                                               out,
                                                               params);
            }
        }
        catch (PortletException e)
        {
            // can't throw PortletException w/o breaking backwards compatibility.
            // get as much info from exception as we can & pass it on.
            e.printStackTrace();
            throw new IOException(e.getMessage());
        }
    }

    /**
     *  Opens rendering of the portlet's container. Portlets are responsible for rendering
     *  their container.  This method used
     *  in conjunction with renderPortletFooter renders the standard container including
     *  borders and titlebar.  Use the methods as bookends.  renderPortletHeader should be
     *  called before the portlet renders (its body) and renderPortletFooter should be called
     *  after the portlet renders (its body).
     *
     *  @param request the portlet render request
     *  @param title the titlebar label.  If <b>null</b> the portlet's static title is used.
     *
     * @deprecated use <code>PortletContainerRenderer.renderPortletHeader()</code>
     */
    public static void renderPortletHeader(PortletRenderRequest request,
                                           Writer writer,
                                           String title)
        throws IOException
    {
        renderPortletHeader(request, writer, title, PortletRenderer.MODE_SHOW);
    }

    /**
     *  Opens rendering of the portlet's container. Portlets are responsible for rendering
     *  their container.  This method used
     *  in conjunction with renderPortletFooter renders the standard container including
     *  borders and titlebar.  Use the methods as bookends.  renderPortletHeader should be
     *  called before the portlet renders (its body) and renderPortletFooter should be called
     *  after the portlet renders (its body).
     *
     *  @param request the portlet render request
     *  @param title the titlebar label.  If <b>null</b> the portlet's static title is used.
     *  @param renderMode the mode currently being rendered. Used to control
     *                    rendering of the title bar so it does not include
     *                    active links in preview mode
     *
     * @deprecated use <code>PortletContainerRenderer.renderPortletHeader()</code>
     */
    public static void renderPortletHeader(PortletRenderRequest request,
                                           Writer writer,
                                           String title,
                                           int renderMode) throws IOException
    {
        HashMap params = new HashMap(10);
        if (writer != null)
        {
            PrintWriter printWriter = getPrintWriter(writer);
            params.put(PortletContainerRenderer.PRINT_WRITER, printWriter);
        }
        
        if (title != null)
        {
            params.put(PortletContainerRenderer.PORTLET_TITLE, title);
        }

        params.put(PortletContainerRenderer.RENDER_MODE, 
                   new Integer(renderMode));
                                 
        try
        {
            PortletContainerRenderer pcr = getContainerRenderer(request);
            pcr.renderPortletHeader(request, params);
        }
        catch (PortletException e)
        {
            // can't throw PortletException w/o breaking backwards compatibility.
            // get as much info from exception as we can & pass it on.
            e.printStackTrace();
            throw new IOException(e.getMessage());
        }
    }

    /**
     *  Closes rendering of the portlet container. Portlets are responsible for rendering their container.  This method used
     *  in conjunction with renderPortletHeader renders the standard container including
     *  borders and titlebar.  Use the methods as bookends.  renderPortletHeader should be
     *  called before the portlet renders (its body) and renderPortletFooter should be called
     *  after the portlet renders (its body).
     *
     *  @param request the portlet render request
     *
     * @deprecated use <code>PortletContainerRenderer.renderPortletFooter()</code>
     */
    public static void renderPortletFooter(PortletRenderRequest request,
                                           Writer writer)
        throws IOException
    {
        HashMap params = new HashMap(10);
        if (writer != null)
        {
            PrintWriter printWriter = getPrintWriter(writer);
            params.put(PortletContainerRenderer.PRINT_WRITER, printWriter);
        }
        
        try
        {
            PortletContainerRenderer pcr = getContainerRenderer(request);
            pcr.renderPortletFooter(request, params);
        }
        catch (PortletException e)
        {
            // can't throw PortletException w/o breaking backwards compatibility.
            // get as much info from exception as we can & pass it on.
            e.printStackTrace();
            throw new IOException(e.getMessage());
        }
    }


    /**
     *  Renders a portlet's titlebar. Portlets are responsible for rendering their container.  This method renders
     *  the standard portlet titlebar using information from the request and
     *  Portlet meta-data. Typically, clients will not use this method.  Rather they
     *  wil rely on renderPortletBegin() to render the entire container including the
     *  titlebar.  If however, renderPortletBegin is not used, this method is
     *  provided for convenience.
     *
     *  @param request the portlet render request
     *  @param title the label that should be displayed as the title.  if <b>null</b> then
     *  the method will render the static title retrieved via Portlet.getTitle();
     */
    public static void renderPortletTitlebar(PortletRenderRequest request,
                                             Writer writer,
                                             String title) throws IOException
    {
        renderPortletTitlebar(request, writer, title, PortletRenderer.MODE_SHOW);
    }


    /**
     *  Renders a portlet's titlebar. Portlets are responsible for rendering their container.  This method renders
     *  the standard portlet titlebar using information from the request and
     *  Portlet meta-data. Typically, clients will not use this method.  Rather they
     *  wil rely on renderPortletBegin() to render the entire container including the
     *  titlebar.  If however, renderPortletBegin is not used, this method is
     *  provided for convenience.
     *
     *  @param request the portlet render request
     *  @param title the label that should be displayed as the title.  if <b>null</b> then
     *  the method will render the static title retrieved via Portlet.getTitle();
     */
    public static void renderPortletTitlebar(PortletRenderRequest request,
                                             Writer writer,
                                             String title,
                                             int renderMode) throws IOException
    {
        PrintWriter out = getPrintWriter(request, writer);
        HashMap params = new HashMap(10);

        try
        {
            if (title != null)
            {
                params.put(PortletContainerRenderer.PORTLET_TITLE, title);
            }

            PortletContainerRenderer pcr = getContainerRenderer(request);

            // renderPortletTitlebar() is a protected method of
            // DefaultContainerRenderer and it's subclasses. If the container
            // specified by the portlet is not a subclass of DefaultContainerRenderer
            // then substitute an instance of DefaultContainerRenderer
            if (pcr instanceof DefaultContainerRenderer)
            {
                // renderer is, or is derived from, DefaultContainerRenderer.
                // cast to DefaultContainerRenderer so we can all the appropriate
                // method
                DefaultContainerRenderer defaultPcr = (DefaultContainerRenderer)pcr;
                defaultPcr.renderPortletTitlebar(request, out, params);
            }
            else
            {
                // portlet's container renderer is not derived from
                // DefaultContainerRenderer so substitute use an instance of
                // DefaultContainerRenderer
                sContainerRenderer.renderPortletTitlebar(request,
                                                         out,
                                                         params);
            }
        }
        catch (ContainerException e)
        {
            // can't throw PortletException or AccessControlException (both
            // derived from ContainerException) w/o breaking backwards
            // compatibility.
            // get as much info from exception as we can & pass it on.
            LogManager.throwing("Throwing IOException for compatibility - original:",
                                e);
            throw new IOException(e.getMessage());
        }
    }


    /**
     * Renders the appropriate header based on the render mode contained within
     * the {@link PortletRenderRequest}. Setting <code>renderCustomize</code>
     * to <code>false</code> will give the developer control over rendering the
     * header for the <i>edit</i> and <i>edit defaults</i> modes. If the
     * developer chooses to let this method render the customize headers, a
     * String must be supplied for the action parameter.
     *
     * @param pr a {@link PortletRenderRequest}.
     * @param renderCustomize boolean flag indicating whether or not to render
     * the header for the customize modes.
     * @param param the name of the action parameter for the customize forms.
     *
     * @deprecated use <code>PortletContainerRenderer.renderPortletHeader()</code>
     */
    public static void renderPortletHeader(PortletRenderRequest request,
                                           boolean renderCustomize,
                                           String param)
        throws AccessControlException, IOException
    {
        HashMap params = new HashMap(10);

        params.put(PortletContainerRenderer.RENDER_CUSTOMIZE,
                   new Boolean(renderCustomize));

        if (param != null)
        {
            params.put(PortletContainerRenderer.ACTION_PARAM_NAME, param);
        }

        try
        {
            PortletContainerRenderer pcr = getContainerRenderer(request);
            pcr.renderPortletHeader(request, params);
        }
        catch (PortletException e)
        {
            // can't throw PortletException w/o breaking backwards compatibility.
            // get as much info from exception as we can & pass it on.
            e.printStackTrace();
            throw new IOException(e.getMessage());
        }
    }

    /**
     * Renders the appropriate footer based on the render mode contained within
     * the {@link PortletRenderRequest}. Setting <code>renderCustomize</code>
     * to <code>false</code> will give the developer control over rendering the
     * footer for the <i>edit</i> and <i>edit defaults</i> modes.
     *
     * @param request a {@link PortletRenderRequest}.
     * @param renderCustomize boolean flag indicating whether or not to render
     * the footer for the customize modes.
     *
     * @deprecated use <code>PortletContainerRenderer.renderPortletFooter()</code>
     */
    public static void renderPortletFooter(PortletRenderRequest request,
                                           boolean renderCustomize)
        throws AccessControlException, IOException
    {
        HashMap params = new HashMap(10);
        params.put(PortletContainerRenderer.RENDER_CUSTOMIZE,
                   new Boolean(renderCustomize));

        try
        {
            PortletContainerRenderer pcr = getContainerRenderer(request);
            pcr.renderPortletFooter(request, params);
        }
        catch (PortletException e)
        {
            // can't throw PortletException w/o breaking backwards compatibility.
            // get as much info from exception as we can & pass it on.
            e.printStackTrace();
            throw new IOException(e.getMessage());
        }
    }

    /**
     *  Adds new parameters to an existing link.  There are various links available
     *  to the portlet for use while rendering its response. This utility method
     *  conveniently adds parameters to one of these links.  The method overrides
     *  existing parameters is they exist.  I.e. existing (queryString) parameters
     *  will be removed if it duplicates (by name) a new parameter.
     *
     *  @param pr the request.
     *  @param linkType the id for the link we want to parameterize.  ID constants are defined
     *  in PortletRendererUtil.
     *  @param queryString  the parameters to be added in properly encodes queryString form.
     *  the String should begin with the first parameter name not a "?".
     *
     * @deprecated use {@link UrlUtils#parameterizeLink(PortletRenderRequest, int, String)}
     */
    public static String parameterizeLink(PortletRenderRequest pr, int linkType, String queryString) 
        throws IllegalArgumentException 
    {
        return UrlUtils.parameterizeLink(pr, linkType, queryString);
    }

    /**
     *  Adds new parameters to an existing link.  There are various links available
     *  to the portlet for use while rendering its response. This utility method
     *  conveniently adds parameters to one of these links.  The method overrides
     *  existing parameters is they exist.  I.e. existing (queryString) parameters
     *  will be removed if it duplicates (by name) a new parameter.
     *
     *  @param link the link to add parameters to.
     *  @param queryString  the parameters to be added in properly encodes queryString form.
     *  the String should begin with the first parameter name not a "?".
     *
     * @deprecated use {@link UrlUtils#parameterizeLink(String, String)}
     */
    public static String parameterizeLink(String link, String queryString)
    {
       return UrlUtils.parameterizeLink(link, queryString);
    }

    /**
     *  Returns a fully-qualified URI for a path relative to the current request.
     *  As Web (Http) Portlets are called indirectly via the portal the client
     *  browser's current path is to the portal not to the portlet.  Because of
     *  this all HREFs must be expressed using fully qualified paths vs. the
     *  traditional relative paths.  This method allows you to properly
     *  encode a relative path.  Note: this is equivalent to portletLink().  I.e
     *  the method only differs when the renderer is reached via a "forward" as
     *  is the case with a servlet or JSP.
     *
     *  @param pr the PortletRenderRequest
     *  @param relativePath the path we want to make absolute
     *
     * @deprecated use {@link UrlUtils#absoluteLink(PortletRenderRequest, String)}
     */
    public static String absoluteLink(PortletRenderRequest pr, String relativePath) 
    {
        return UrlUtils.absoluteLink(pr, relativePath);
    }

    /**
     *  Transforms a URI into the appropriate form to be used in a FORM action tag.
     *
     *  @param ref the URI to be transformed
     *
     * @deprecated use {@link UrlUtils#htmlFormActionLink(String)}
     */
    public static String htmlFormActionLink(String ref) 
    {
        return UrlUtils.htmlFormActionLink(ref);
    }

    /**
     *  Transforms one of the various URIs known to the request into the appropriate
     *  form to be used in a FORM action tag.
     *
     *  @param pr the PortletRenderRequest
     *  @param linkType  the link in this request that should be used.
     *
     * @deprecated use {@link UrlUtils#htmlFormActionLink(PortletRenderRequest, int)}
     */
    public static String htmlFormActionLink(PortletRenderRequest pr, int linkType) 
        throws IllegalArgumentException 
    {
        return UrlUtils.htmlFormActionLink(pr, linkType);
    }

    /**
     *  Encodes the parameters in the query string of the passed URL as hidden form fields.
     *  When "linking" back to the portlet via a form,
     *  the URI parameters must be added as hidden fields.  These parameters
     *  are found in the querystring of your URI.  This utility method generates the hidden
     *  field tags from this URL and returns them as a String.
     *
     *  @param pr the request
     *  @param linkType the link to use.  Constants representing these types are defined in PortletRendererUtil.
     *
     * @deprecated use {@link UrlUtils#htmlFormHiddenFields(PortletRenderRequest, int)}
     */
    public static String htmlFormHiddenFields(PortletRenderRequest pr, int linkType) throws IllegalArgumentException 
    {
        return UrlUtils.htmlFormHiddenFields(pr, linkType);
    }

    /**
     *  Encodes the parameters in the query string of the passed URL as hidden form fields.
     *  When "linking" back to the portlet via a form,
     *  the URI parameters must be added as hidden fields.  These parameters
     *  are found in the querystring of your URI.  This utility method generates the hidden
     *  field tags from this URL and returns them as a String.
     *
     *  @param ref the URI
     *
     * @deprecated use {@link UrlUtils#htmlFormHiddenFields(String)}
     */
    public static String htmlFormHiddenFields(String ref) 
    {
        return UrlUtils.htmlFormHiddenFields(ref);
    }


    /**
     *  Create a parameterized link for a portlet detail page
     *
     *  By default, a portlet can have a single "detail" page that allows you
     *  to view portlet information in a full-screen mode. This methods allows
     *  you to create many "detail" links with different sets of parameters.
     *  You can use these links anywhere in the body of your portlet to create
     *  a portlet that has more than one detail page.
     *
     *  When a user clicks on a detail link within your portlet, your portlet
     *  will be called and the renderer associated with "detail" mode will be
     *  invoked. You can use the parameters you add to the detail link to
     *  determine which "detail page" the renderer should emit.
     *
     *  @param pr the render request.
     *  @param params an array of NameValue objects representing the
     *                parameter names and associated values. Names cannot be NULL
     *  @param encodeParams flag indicating whether the parameter names and values
     *                      should be URL encoded using the multibyte URL encoder.
     *
     *  @deprecated use {@link UrlUtils#constructDetailHTMLLink}
     */
    public static String constructDetailLink(PortletRenderRequest pr,
                                             NameValue[] params,
                                             boolean encodeParams,
                                             boolean replaceParams)
        throws PortletException
    {
        try
        {
            // add parameter for SHOW mode
            NameValue[] linkParams = new NameValue[params.length + 2];
            int i;
            for (i=0; i < params.length; i++)
            {
                linkParams[i] = params[i];
            }
            linkParams[i++] = new NameValue("_mode",
                                          Integer.toString(PortletRenderer.MODE_SHOW_DETAILS));
            linkParams[i] = new NameValue("_title", getTitle(pr));
            return UrlUtils.constructLink(pr,
                                 UrlUtils.DESIGN_LINK,
                                 linkParams,
                                 encodeParams,
                                 replaceParams);
        }
        catch (Exception e)
        {
            throw new PortletException(e);
        }
    }

    /**
     *  Create a parameterized link for a portlet help page
     *
     *  By default, a portlet can have a single "help" page that allows you
     *  to display help information for your portlet in a full-screen mode.
     *  This methods allows you to create many "help" links with different
     *  sets of parameters. You can use these links anywhere in the body of
     *  your portlet to create context sensitive help pages.
     *
     *  When a user clicks on a help link within your portlet, your portlet
     *  will be called and the renderer associated with "help" mode will be
     *  invoked. You can use the parameters you add to the detail link to
     *  determine which "help page" the renderer should emit.
     *
     *  @param pr the render request.
     *  @param params an array of NameValue objects representing the
     *                parameter names and associated values. Names cannot be NULL
     *  @param encodeParams flag indicating whether the parameter names and values
     *                      should be URL encoded using the multibyte URL encoder.
     *
     *  @deprecated use {@link UrlUtils#constructHelpHTMLLink}
     */
    public static String constructHelpLink(PortletRenderRequest pr,
                                           NameValue[] params,
                                           boolean encodeParams,
                                           boolean replaceParams)
        throws PortletException
    {
        try
        {
            // add parameter for SHOW mode
            NameValue[] linkParams = new NameValue[params.length + 2];
            int i;
            for (i=0; i < params.length; i++)
            {
                linkParams[i] = params[i];
            }
            linkParams[i++] = new NameValue("_mode",
                                          Integer.toString(PortletRenderer.MODE_HELP));
            linkParams[i] = new NameValue("_title", getTitle(pr));

            return UrlUtils.constructLink(pr,
                                 UrlUtils.DESIGN_LINK,
                                 linkParams,
                                 encodeParams,
                                 replaceParams);
        }
        catch (Exception e)
        {
            throw new PortletException(e);
        }
    }

    /**
     * Generic utility to append parameters to a link.
     *
     * @param pr render request
     * @param link the link to be parameterized
     * @param mode the portlet mode to be associated with this link
     * @param params array of NameValue objects representing the parameters
     * @param encodeParams flag indicating whether parameter names and values
     *                     should be encoded
     * @param replaceParams flag indicating whether parameters contained in the
     *                      params array should replace existing parameters of
     *                      the same name. true = replace or overwrite existing
     *                      parameters. false = simply add parameters
     *
     * @return original link with additional parameters.
     *
     * @deprecated use {@link UrlUtils#constructLink(
     *     PortletRenderRequest, String, NameValue[], boolean, boolean)}
     */
    public static String constructLink(PortletRenderRequest pr,
                                        String link,
                                        NameValue[] params,
                                        boolean encodeParams,
                                        boolean replaceParams)
        throws IllegalArgumentException, IOException
    {
        return UrlUtils.constructLink(pr, link, params, 
                                      encodeParams, replaceParams);
    }


    /**
     *  Returns the name of the render mode given its value.
     *
     *  @param mode render mode value
     *  @return render mode name
     */
    public static String getModeName(int mode) throws IllegalArgumentException {
        switch (mode) {
        case PortletRenderer.MODE_SHOW:
            return PortletRenderer.MODE_NAME_SHOW;
        case PortletRenderer.MODE_ABOUT:
            return PortletRenderer.MODE_NAME_ABOUT;
        case PortletRenderer.MODE_EDIT:
            return PortletRenderer.MODE_NAME_EDIT;
        case PortletRenderer.MODE_HELP:
            return PortletRenderer.MODE_NAME_HELP;
        case PortletRenderer.MODE_EDIT_DEFAULTS:
            return PortletRenderer.MODE_NAME_EDIT_DEFAULTS;
        case PortletRenderer.MODE_SHOW_DETAILS:
            return PortletRenderer.MODE_NAME_SHOW_DETAILS;
        case PortletRenderer.MODE_PREVIEW:
            return PortletRenderer.MODE_NAME_PREVIEW;
        case PortletRenderer.MODE_LINK:
            return PortletRenderer.MODE_NAME_LINK;
        default:
            throw new IllegalArgumentException("Invalid render mode parameter");
        }
    }

    /**
     *  Returns the render mode for the given render mode name.
     *
     *  @param name render mode name
     *  @return the render mode
     */
    public static int getModeValue(String name) throws IllegalArgumentException {
        if (name.equalsIgnoreCase(PortletRenderer.MODE_NAME_SHOW)) {
            return PortletRenderer.MODE_SHOW;
        } else if (name.equalsIgnoreCase(PortletRenderer.MODE_NAME_ABOUT)) {
            return PortletRenderer.MODE_ABOUT;
        } else if (name.equalsIgnoreCase(PortletRenderer.MODE_NAME_EDIT)) {
            return PortletRenderer.MODE_EDIT;
        } else if (name.equalsIgnoreCase(PortletRenderer.MODE_NAME_HELP)) {
            return PortletRenderer.MODE_HELP;
        } else if (name.equalsIgnoreCase(PortletRenderer.MODE_NAME_EDIT_DEFAULTS)) {
            return PortletRenderer.MODE_EDIT_DEFAULTS;
        } else if (name.equalsIgnoreCase(PortletRenderer.MODE_NAME_SHOW_DETAILS)) {
            return PortletRenderer.MODE_SHOW_DETAILS;
        } else if (name.equalsIgnoreCase(PortletRenderer.MODE_NAME_PREVIEW)) {
            return PortletRenderer.MODE_PREVIEW;
        } else if (name.equalsIgnoreCase(PortletRenderer.MODE_NAME_LINK)) {
            return PortletRenderer.MODE_LINK;
        } else {
            throw new IllegalArgumentException(name + " isn't a valid render mode name.");
        }
    }

    /**
     *  Utility for getting the customization data for a particular edit request.
     *  Hides the details of when to use the system/default vs. user customization.
     *
     *  @param pr the request.
     */
    public static Object getEditData(PortletRenderRequest pr)
        throws PortletNotFoundException, IOException, AccessControlException
    {
        PortletInstance inst = pr.getPortletInstance();
        return inst.getEditData(pr.getUser(), pr.getLocale(), pr.getRenderContext());

    }

    /**
     *  Returns the edit defaults data for a particular edit request.
     *
     *  @param pr the request.
     */
    public static Object getEditDefaultData(PortletRenderRequest pr)
        throws PortletNotFoundException, IOException, AccessControlException
    {
        PortletInstance inst = pr.getPortletInstance();
        return inst.getEditDefaultData(pr.getLocale(), pr.getRenderContext());
    }

    /**
    * Convenience method to display a single error message using the
    * standard Portal style sheet
    */
    public static void renderError(PortletRenderRequest request,
                                   Writer writer,
                                   Locale locale,
                                   String errorText) throws IOException
    {
        renderErrorHeader(request, writer);
        renderErrorLine(request, writer, locale, errorText);
        renderErrorFooter(request, writer);
    }

    /**
    * Utility to display a standard header before any error text.
    * Use in conjunction with <code>renderErrorHeader</code> and <code>renderErrorLine</code>
    */
    public static void renderErrorHeader(PortletRenderRequest request, Writer writer) throws IOException
    {
        PrintWriter out = getPrintWriter(request, writer);
        out.println("<BR>");
        out.println("<TABLE BORDER=\"0\" CELLPADDING=\"0\" CELLSPACING=\"0\" WIDTH=\"60%\" ALIGN=\"CENTER\">");
    }

    /**
    * Utility to display a single error message using the standard Portal style sheet.
    * Use in conjunction with <code>renderErrorHeader()</code> and
    * <code>renderErrorFooter()</code> when you need to display multiple error messages.
    */
    public static void renderErrorLine(PortletRenderRequest request,
                                       Writer writer,
                                       Locale locale,
                                       String errorText) throws IOException
    {
        PrintWriter out = getPrintWriter(request, writer);

        // FIXME - change to use styles from CSS when they are available.
        // CSS used by portal pages does not include OraErrorHeader or
        // OraErrorText which are used in other Portal pages when an error
        // is displayed

        out.println("<TR>");
        out.println("<TD ALIGN=\"RIGHT\">");
        out.println("<FONT color=\"#FF0000\" face=\"arial,helvetica\">");
        out.println("<B>");

        // FIXME - use locale to translate ERROR: when translations are available
        out.println("Error:");

        out.println("</B>");
        out.println("&nbsp");
        out.println("</FONT>");
        out.println("</TD>");

        out.println("<TD ALIGN=\"LEFT\">");
        out.println("<FONT face=\"arial,helvetica\">");
        out.println(errorText);
        out.println("</FONT>");
        out.println("</TD>");
        out.println("</TR>");
    }


    /**
    * Utility to display a standard footer for error messages.  Use in conjunction with
    * <code>renderErrorHeader</code> and <code>renderErrorLine</code>
    */
    public static void renderErrorFooter(PortletRenderRequest request, Writer writer) throws IOException
    {
        PrintWriter out = getPrintWriter(request, writer);
        out.println("</TABLE>");
        out.println("<BR>");
    }


    /**
     *  Utility for submitting the customization data for a particular edit request.
     *  Takes care of creating the customization if necessary.
     *
     *  @param pr the request.
     *  @param o object holding the customization data.
     */
    public static void submitEditData(PortletRenderRequest pr, Object o) throws IOException, AccessControlException
    {
        PortletInstance inst = pr.getPortletInstance();
        inst.submitEditData(pr.getUser(), pr.getLocale(), pr.getRenderContext(), o);
    }

    /**
     * Utility that turns a query string parameter name into fully qualified
     * (unique) name.  Portlets on the same page share request parameters.  To ensure
     * each Portlet can locate its specific parameters, query string parameter
     * names must be fully qualified.
     *
     * Note: Url parameter names generated via this method should be later 
     * retrieved using getQualifiedParameter/getQualifiedParameterValues
     * which takes unqualified names. getParameter/getParameterValues using
     * a fully qualified name will not work in general. 
     *
     * @param request  the current request.
     * @param param the name of the parameter to be qualified.
     */
    public static String portletParameter(PortletRenderRequest request, String param)
    {
        try
        {
            // the presence of the portlet page reference
            // will indicate that we were called by a 902+ PPE
            // otherwise it will throw an exception
            String pref = request.getRenderContext().getPortletPageReference();

            // 902+ PPE - use new format

            int size = PORTLET_PARAMETER_PREFIX.length() + param.length() + 1;
            SimpleStringBuffer out = new SimpleStringBuffer(size);

            out.append( PORTLET_PARAMETER_PREFIX )
               .append( pref )
               .append( "." )
               .append( param );

            return out.toString();
        }
        catch( IllegalStateException e )
        {
            // 309 PPE - use old format

            PortletReference ref = request.getPortletReference();

            SimpleStringBuffer out = new SimpleStringBuffer(100);

            out.append( ref.getProviderId() )
               .append( "." ).append( ref.getPortletId() )
               .append( "." ).append( ref.getInstanceName() )
               .append( "." ).append( param );

            return out.toString();
        }
    }

    // Look for a method in the provider called "getUseOldStyleHeaders" and
    // return its boolean value if it can be successfully invoked
    // Decides whether headers portlet headers will be rendered in the 'old'
    // or 'new' style
    private static boolean getUseOldStyleHeaders(PortletRenderRequest request)
    {
        return request.getProviderInstance().getUseOldStyleHeaders();
    }

    /**
     * Retrieves the {@link RenderManager} defaulted/set customize form action
     * parameter name. If this Portlet does not use {@link RenderManager} as
     * its {@link PortletRenderer}, null is returned.
     *
     * @param pr a {@link PortletRenderRequest}
     * @return edit form parameter name.
     */
    public static String getEditFormParameter(PortletRenderRequest pr)
    {
        return pr.getPortletInstance().getActionParameterName();
    }


    /**
     * Retrieves the title of this portlet. If a customized title exists for
     * the current user, this is returned. If not, the default title for the
     * current locale is returned.
     *
     * @param pr a {@link PortletRenderRequest}.
     * @return the portlet's title.
     */
    public static String getTitle(PortletRenderRequest pr)
        throws AccessControlException
    {
        PortletInstance inst = pr.getPortletInstance();
        return inst.getTitle(pr.getUser(), pr.getLocale(),
                             pr.getRenderContext());
    }

    /**
     * Retrieves the short title of this portlet. If a customized short title
     * exists for the current user, this is returned. If not, the default title
     * for the current locale is returned.
     *
     * @param pr a {@link PortletRenderRequest}.
     * @return the portlet's title.  If there is no short title then <code>null
     * </code> is returned.
     */
    public static String getShortTitle(PortletRenderRequest pr)
        throws AccessControlException
    {
        PortletInstance inst = pr.getPortletInstance();
        return inst.getShortTitle(pr.getUser(), pr.getLocale(),
                                  pr.getRenderContext());
    }


    /**
     * gets the default title for a portlet based on the mode being rendered
     */
    public static String getModeTitle(PortletRenderRequest request) {
        ResourceBundle res = request.getProviderResource();
        switch (request.getMode()) {
            case PortletRenderer.MODE_HELP:
                return res.getString(ProviderBundle.HELP_LABEL);
            case PortletRenderer.MODE_ABOUT:
                return res.getString(ProviderBundle.ABOUT_LABEL);
            case PortletRenderer.MODE_EDIT:
            case PortletRenderer.MODE_EDIT_DEFAULTS:
                return res.getString(ProviderBundle.CUSTOMIZE_LABEL);
            default:
                return res.getString(ProviderBundle.GENERAL_LABEL);
        }
    }


    /**
    * Encodes a parameter for inclusion in a URL.  Parameter is encoded
    * using the same character set that was used when the portal encoded
    * the request parameters.  The character sets must be the same so that
    * parameters that may be intended for another portlet implemented in
    * pl/sql can decode them correctly.
    *
    * @param param the parameter to be encoded
    * @param pr PortletRenderRequest object for this request
    * @return encoded parameter, suitable for inclusion in a URL
    *
     * @deprecated use {@link UrlUtils#encodeParameter(String, PortletRenderRequest)}
    */
    public static String encodeParameter(String param,
                                         PortletRenderRequest pr)
        throws java.io.IOException
    {
        return UrlUtils.encodeParameter(param, pr);
    }


    private static PrintWriter getPrintWriter(PortletRenderRequest request, 
                                              Writer writer) throws IOException 
    {
        if (writer == null) 
        {
            // getWriter returns a PrintWriter
            return request.getWriter();
        }

        return getPrintWriter(writer);
    }


    private static PrintWriter getPrintWriter(Writer writer)
    {
        if (writer instanceof PrintWriter)
        {
            return (PrintWriter)writer;
        }
        else
        {
            return new PrintWriter(writer);
        }
    }


    private static PortletContainerRenderer getContainerRenderer(PortletRenderRequest pr)
    {
        PortletContainerRenderer pcr = null;
        PortletDefinition p = pr.getPortletInstance().getPortletDefinition();
        return p.getContainerRenderer();
    }

    /**
     * Utility to emit a single HTML hidden field with a specified name and
     * value.
     *
     * @param name the name of the hidden field
     * @param value the value of the hidden field
     * @return a string containing the HTML hidden field markup
     *
     * @deprecated use {@link UrlUtils#emitHiddenField(String, String)}
     */
    public static String emitHiddenField(String name, String value) 
    {
        return UrlUtils.emitHiddenField(name,value);
    }
}

