/*  Copyright (c) 2001, Oracle Corporation. All Rights Reserved. */

package oracle.portal.provider.v2;

import java.util.*;
import java.io.IOException;
import oracle.portal.provider.v2.security.AccessControlException;
import oracle.portal.provider.v2.render.RenderContext;
import oracle.portal.provider.v2.personalize.NameValuePersonalizationObject;
import oracle.portal.provider.v2.preference.PreferenceStore;
import oracle.portal.provider.v2.preference.PreferenceStoreException;
import oracle.portal.provider.v2.monitor.PerfMonitor;
import oracle.portal.log.LogManager;

/**
 *  A <code>PortletInstance</code> is the entity that carries out the
 *  responsibilities of a single 'instance' of a portlet on a single Portal
 *  page. Generally, a <code>PortletInstance</code> is driven by meta data
 *  information in a {@link PortletDefinition} that may be shared with other
 *  <code>PortletInstance</code>s, perhaps on the same page or other Portal
 *  pages.<p>
 */
public abstract class PortletInstance
{
    protected ProviderInstance mProviderInstance; // ProviderInstance that manages this instances Portlet
    protected PortletDefinition mPortletDefinition;  // PortletDefinition that manages this instance ID
    protected String mInstanceName; // portlet instance name
    protected Locale mDefaultLocale; // Portal's default locale
    private PerfMonitor mPerfMonitor;


    /**
     *  Initialize the portlet instance after construction.
     *
     * @param providerInstance Provider of this instance
     * @param portletDefinition definition of this instance
     * @param instanceName Portal generated name of this instance
     * @param defLocale the default language/locale setting of the portal, if
     * available, or <code>null</code> if the default locale defined on the
     * <code>PortletDefinition</code> should be used.
     * @exception PortletException if an error occurs during initialization.
     *
     * @deprecated Replaced by {@link #init(ProviderInstance, PortletDefinition, String, Locale, PerfMonitor)}
     */
    public void init(ProviderInstance providerInstance,
                     PortletDefinition portletDefinition,
                     String instanceName,
                     Locale defLocale)
        throws PortletException
    {
        mProviderInstance = providerInstance;
        mPortletDefinition = portletDefinition;
        mInstanceName = instanceName;
        if (defLocale != null)
        {
            mDefaultLocale = defLocale;
        }
        else
        {
            mDefaultLocale = mPortletDefinition.getDefaultLocale();
        }                   
    }

    /**
     *  Initialize the portlet instance after construction.
     *
     * @param providerInstance Provider of this instance
     * @param portletDefinition definition of this instance
     * @param instanceName Portal generated name of this instance
     * @param defLocale the default language/locale setting of the portal, if
     * available, or <code>null</code> if the default locale defined on the
     * <code>PortletDefinition</code> should be used.
     * @param tracker used to track the request execution progress against
     * the timeouts defined in the provider properties.
     * @exception PortletException if an error occurs during initialization.
     */
    public void init(ProviderInstance providerInstance,
                     PortletDefinition portletDefinition,
                     String instanceName,
                     Locale defLocale,
                     PerfMonitor monitor)
        throws PortletException
    {
        init(providerInstance, portletDefinition, instanceName, defLocale);
        mPerfMonitor = monitor;
    }

    /**
     * Gets the <code>ProviderInstance</code> which owns this
     * <code>PortletInstance</code>.
     * @return the <code>ProviderInstance</code> which owns this
     * <code>PortletInstance</code>.
     */
    public ProviderInstance getProviderInstance()
    {
        return mProviderInstance;
    }

    /**
     * Gets the ID of the <code>ProviderInstance</code> which owns this
     * <code>PortletInstance</code>. This is a convenience method for calling
     * {@link ProviderInstance#getProviderId
     * getProviderInstance().getProviderId()}.
     * @return the ID of the <code>ProviderInstance</code> which owns this
     * <code>PortletInstance</code>.
     */
    public String getProviderId()
    {
        return mProviderInstance.getProviderId();
    }


    /**
     * Gets the <code>PortletDefinition</code> which this
     * <code>PortletInstance</code> is an instance of.
     * @return the <code>PortletDefinition</code> which this
     * <code>PortletInstance</code> is an instance of.
     */
    public PortletDefinition getPortletDefinition()
    {
        return mPortletDefinition;
    }

    /**
     * Gets the ID of the <code>PortletDefinition</code> which this
     * <code>PortletInstance</code> is an instance of. This is a convenience
     * method for calling {@link PortletDefinition#getId
     * getPortletDefinition().getId()}.
     * @return the ID of the <code>PortletDefinition</code> which this
     * <code>PortletInstance</code> is an instance of.
     */
    public long getPortletId()
    {
        return mPortletDefinition.getId();
    }


    /**
     * Gets the Portal generated name of this <code>PortletInstance</code>.
     * @return the Portal generated name of this <code>PortletInstance</code>.
     */
    public String getInstanceName()
    {
        return mInstanceName;
    }

    /**
     *  Gets the default Locale that should be used on behalf of this instance.
     *  This is generally the Portal's default locale.  However if not set /
     *  passed by the portal then it is the default locale of the
     *  <code>PortletDefinition</code>.
     *  @see PortletDefinition#getDefaultLocale
     *  @return the default Locale that should be used on behalf of this instance.
     */
    public Locale getDefaultLocale()
    {
        return mDefaultLocale;
    }

    /**
     * Retrieves the name of the parameter to contain the name of the 'action'
     * submitted by the customize form of this portlet instance.
     *
     * @return action parameter name.
     */
    public String getActionParameterName()
    {
        return "JPDK_ACTION_" + mInstanceName;
    }

    /**
     * Gets the default <code>PreferenceStore</code> that should be used by the
     * <code>PortletInstance</code> and all its child objects for which a
     * preference store has not been chosen explicitly.
     * @return the default preference store for this
     * <code>PortletInstance</code>
     * @exception PreferenceStoreException if the <code>PortletInstance</code>
     * does not have a default preference store.
     */
    public PreferenceStore getPreferenceStore()
        throws PreferenceStoreException
    {
        PreferenceStore ps =  mPortletDefinition.getPreferenceStore();
        if(ps == null)
        {
            throw new PreferenceStoreException("No preference store defined");
        }
        return ps;
    }

    /**
     * Called when the portlet instance is first created on a page. This gives
     * the <code>PortletInstance</code> the opportunity to initialize defaults
     * for persisted customization data. This default implementation does
     * nothing. Subclasses should override.
     * @param user the user performing this operation.
     * @exception IOException if an I/O error occurs whilst accessing
     * persistent storage.
     * @exception PortletAlreadyExistsException if this instance has already
     * been registered.
     * @exception AccessControlException if the given user isn't authorized to
     * access this instance.
     */
    public void register(ProviderUser user)
        throws IOException, PortletAlreadyExistsException, AccessControlException
    {
    }

    /**
     * Called when the portlet instance is removed from the page. This gives
     * the <code>PortletInstance</code> the opportunity to remove any persisted
     * customization data. This default implementation does nothing. Subclasses
     * should override.
     * @param user the user performing this operation.
     * @exception IOException if an I/O error occurs whilst accessing
     * persistent storage.
     * @exception PortletNotFoundException if this instance isn't known to
     * exist in the Portal, i.e. hasn't been registered.
     * @exception AccessControlException if the given user isn't authorized to
     * access this instance.
     */
    public void deregister(ProviderUser user)
        throws IOException, PortletNotFoundException, AccessControlException
    {
    }

    /**
     * Called when the portlet instance is copied to another page. This gives
     * the <code>PortletInstance</code> the opportunity to export defaults for
     * persisted customization data to the new instance. This default
     * implementation does nothing. Subclasses should override.
     * @param user the user performing this operation.
     * @param dest the <code>PortletInstance</code> to export data to.
     * @exception IOException if an I/O error occurs whilst accessing
     * persistent storage.
     * @exception PortletNotFoundException if this instance or the destination
     * instance isn't known to exist in the Portal, i.e. hasn't been
     * registered.
     * @exception AccessControlException if the given user isn't authorized to
     * access this instance or the destination instance.
     */
    public void copyTo(ProviderUser user, PortletInstance dest)
        throws IOException, PortletNotFoundException, AccessControlException
    {
    }

    /**
     *  Verifies whether the user is authorized to use this particular portlet
     *  instance.
     *
     *  @param user the current user we are being asked to authorize.
     *  @param locale the language setting with which the user has logged on to
     *  portal.
     *  @return <code>true</code> if the user is authorized to access this
     *  portlet instance. This default implementation always returns
     *  <code>true</code>. Subclasses should override.
     *  @exception PortletNotFoundException if this instance isn't known to
     *  exist in the Portal, i.e. hasn't been registered.
     */
    public boolean hasAccess(ProviderUser user, Locale locale)
        throws PortletNotFoundException
    {
        return true;
    }

    /**
     * Retrieves an object containing the customizable data for the given user
     * in the given locale within the given context. If the context is "Edit
     * Defaults" mode, the data should correspond to the defaults for that
     * locale, rather than the data for that specific user.
     * @param user the user for whom the data is being retrieved.
     * @param locale the language setting with which the user has logged on to
     * portal.
     * @param rc an object describing the context in which this
     * <code>PortletInstance</code> is being rendered.
     * @return an object containing the customizable data for the given user in
     * the given locale within the given context, or <code>null</code> if none
     * exists. This default implementation always returns
     * <code>null</code>. Subclasses should override.
     * @exception IOException if an I/O error occurs whilst retrieving the data
     * @exception PortletNotFoundException if this instance isn't known to
     * exist in the Portal, i.e. hasn't been registered.
     * @exception AccessControlException if the given user isn't authorized to
     * access this instance.
     */
    public Object getEditData(ProviderUser user, Locale locale, RenderContext rc)
        throws IOException, PortletNotFoundException, AccessControlException
    {
        return null;
    }

    /**
     * Retrieves an object containing the default data for the 
     * <code>PortletInstance</code> in the given locale.
     *
     * @param locale the language setting with which the user 
     *        has logged on to Portal.
     * @param rc an object describing the context in which this
     *        <code>PortletInstance</code> is being rendered.
     * @return an object containing the default data for the 
     *         <code>PortletInstance</code> in the given locale within the 
     *         given context, or <code>null</code> if none
     *         exists. This default implementation always returns
     *         <code>null</code>. Subclasses should override.
     * @exception IOException if an I/O error occurs whilst retrieving the data
     * @exception PortletNotFoundException if this instance isn't known to
     *            exist in the Portal, i.e. hasn't been registered.
     * @exception AccessControlException if the given user isn't authorized to
     *            access this instance.
     */
    public Object getEditDefaultData(Locale locale, RenderContext rc)
        throws IOException, PortletNotFoundException, AccessControlException
    {
        return null;
    }

    /**
     * Stores an object containing the customizable data for the given user in
     * the given locale within the given context. If the context is "Edit
     * Defaults" mode, the data should be stored as the defaults for that
     * locale, rather than the data for that specific user. This default
     * implementation does nothing. Subclasses should override.
     * @param user the user for whom the data is being stored.
     * @param locale the language setting with which the user has logged on to
     * portal.
     * @param rc an object describing the context in which this <code>PortletInstance</code>
     * is being rendered.
     * @param o an object containing the customizable data for the given user
     * in the given locale within the given context.
     * @exception IOException if an I/O error occurs whilst retrieving the data
     * @exception AccessControlException if the given user isn't authorized to
     * access this instance.
     */
    public void submitEditData(ProviderUser user, Locale locale, RenderContext rc, 
                               Object o)
        throws IOException, AccessControlException
    {
    }

    /**
     * Renders a visual representation of this portlet instance for the given
     * user in the given locale in the given context. Subclasses must define.
     *
     * @param user the user for whom this <code>PortletInstance</code> is being
     * rendered.
     * @param locale the language setting with which the user has logged on to
     * portal.
     * @param rc an object describing the context in which this
     * <code>PortletInstance</code> is being rendered.
     * @exception AccessControlException if the given user isn't authorized to
     * access this instance.
     * @exception PortletException if a general portlet exception occurs whilst
     * rendering.
     */ 
    public abstract void render(ProviderUser user, Locale locale, RenderContext rc)
        throws AccessControlException, PortletException;

    /**
     * Returns boolean indicating whether the warning timeout for a request
     * has elapsed.
     *
     * @return true if the warning timeout has elapsed.
     */
    public boolean hasTimeoutElapsed()
    {
        if (mPerfMonitor == null)
        {
            String msg = "This portlet instance has no associated monitor" +
                         " object.";
            IllegalStateException ise = new IllegalStateException(msg);
            LogManager.throwing(msg, ise);
            throw ise;
        }
        return (mPerfMonitor.hasTimeoutElapsed());
    }

    /**
     * Retrieves the title for the <code>PortletInstance</code>. If this
     * portlet provides personalization via the {@link
     * oracle.portal.provider.v2.personalize.NameValuePersonalizationObject}
     * class, the customized title is returned. If no customized title can be
     * found, the title declared in the {@link PortletDefinition} is returned.
     * 
     * @param user the {@link ProviderUser} for whom the portlet is being
     * called.
     * @param locale the <code>java.util.Locale</code> of the user.
     * @param rc the {@link oracle.portal.provider.v2.render.RenderContext}.
     */
    public String getTitle(ProviderUser user, Locale locale, RenderContext rc)
        throws AccessControlException
    {
        try
        {
            Object o = getEditData(user, locale, rc);
            if (o != null && o instanceof NameValuePersonalizationObject)
            {
                String custTitle =
                    ((NameValuePersonalizationObject)o).getPortletTitle();
                if (custTitle != null)
                {
                    return custTitle;
                }
            }
        }
        catch (IOException ioe)
        {
            // Use the declared title, so just fall through.
            LogManager.warning("Unable to access portlet personalization data");
        }
        catch (PortletNotFoundException pnfe)
        {
            // Use the declared title, so just fall through.
            LogManager.warning("Unable to access portlet personalization data");            
        }
        return getPortletDefinition().getTitle(locale);
    }
    
    /**
     * Retrieves the short title for the <code>PortletInstance</code>. If this
     * portlet provides personalization via the {@link
     * oracle.portal.provider.v2.personalize.NameValuePersonalizationObject}
     * class, the customized short title is returned. If no customized short
     * title can be found, the short title declared in the
     * {@link PortletDefinition} is returned.
     * 
     * @param user the {@link ProviderUser} for whom the portlet is being
     * called.
     * @param locale the <code>java.util.Locale</code> of the user.
     * @param rc the {@link oracle.portal.provider.v2.render.RenderContext}.
     */
    public String getShortTitle(ProviderUser user, Locale locale,
                                RenderContext rc)
        throws AccessControlException
    {
        try
        {
            Object o = getEditData(user, locale, rc);
            if (o != null && o instanceof NameValuePersonalizationObject)
            {
                String custShortTitle =
                    ((NameValuePersonalizationObject)o).getPortletShortTitle();
                if (custShortTitle != null)
                {
                    return custShortTitle;
                }
            }
        }
        catch (IOException ioe)
        {
            // Use the declared title, so just fall through.
            LogManager.warning("Unable to access portlet personalization data");
        }
        catch (PortletNotFoundException pnfe)
        {
            // Use the declared title, so just fall through.
            LogManager.warning("Unable to access portlet personalization data");            
        }
        return getPortletDefinition().getShortTitle(locale);
    }
}
