/*
 * @(#)Utils.java
 *
 * Copyright 2001-2002 by Oracle Corporation,
 * 500 Oracle Parkway, Redwood Shores, California, 94065, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information
 * of Oracle Corporation.
 */

package oracle.jbo.html.jsp.datatags;

import java.net.URLEncoder;
import javax.servlet.ServletContext;
import javax.servlet.jsp.PageContext;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.tagext.Tag;
import javax.servlet.jsp.tagext.TagSupport;
import oracle.jbo.JboException;
import oracle.jbo.Row;
import oracle.jbo.ApplicationModule;
import oracle.jbo.common.ampool.ApplicationModuleRef;
import oracle.jbo.common.ampool.SessionCookie;
import oracle.jbo.html.jsp.JSPApplicationRegistry;
import oracle.jbo.html.DataSource;
import oracle.jbo.html.DataSourceImpl;
import oracle.jbo.http.HttpContainer;

/**
 */
public final class Utils
{
   public static String buildUrl(String url, String[] names, String[] values)
   {
      StringBuffer newUrl = new StringBuffer(url);
      boolean isFirst = true;

      if (url != null && url.indexOf('?') > 0)
      {
         isFirst = false;
      }

      if (names != null && values != null)
      {
         for (int i = 0; i < names.length; i++)
         {
            if (values[i] == null)
            {
               continue;
            }
            
            if (isFirst)
            {
               newUrl.append('?');
               isFirst =false;
            }
            else
            {
               newUrl.append('&');
            }
            newUrl.append(names[i]);
            newUrl.append('=');
            newUrl.append(URLEncoder.encode(values[i]));
         }
      }

      return newUrl.toString();
   }

   public static boolean isTrue(String param)
   {
      return (param != null && (param.equalsIgnoreCase("yes") || param.equalsIgnoreCase("true")));
   }

   public static boolean isFalse(String param)
   {
      return (param != null && (param.equalsIgnoreCase("no") || param.equalsIgnoreCase("false")));
   }

   public static boolean isValidJavaVariable(String name)
   {
      if (name == null || name.length() == 0)
      {
         return false;
      }
      
      if (!Character.isJavaIdentifierStart(name.charAt(0)))
      {
         return false;
      }

      for (int nIndex = 0 ; nIndex < name.length() ; nIndex++)
      {
         if (!Character.isJavaIdentifierPart(name.charAt(nIndex)))
         {
            return false;
         }
      }

      return true;
   }

   /**
    * Return attribute value by only looking at PAGE and REQUEST scope
    */
   public static Object getAttributeFromContext(PageContext pageCtx, String id)
   {
      Object dsObj = pageCtx.getAttribute(id); 
      
      if (dsObj == null)
      {
         dsObj = pageCtx.getAttribute(id, PageContext.REQUEST_SCOPE);
      }

      return dsObj;
   }
   
   public static DataSource getDataSourceFromContext(PageContext pageCtx, String dsName)
   {
      // Changed from getAttribute to getAttribute a PAGE and REQUEST scope.
      // This is to accomodate datasource set by a servlet at the request scope
      // because they don't have the page context.
      // We do not do a find here because session and application scope should not
      // get in the picture (don't want to hold reference to BC4J object between passivation).
      Object dsObj = getAttributeFromContext(pageCtx, dsName); 
      
      if (dsObj == null)
      {
         DataSource ds = null;      
         
         // This might because we use the so called "am notation": amName.voName#rsName
         int index = dsName.indexOf('.');
         if (index >= 0)
         {
            String appName = dsName.substring(0, index);
            String voName = dsName.substring(index + 1);
            String rsName = null;
            index = voName.indexOf("#");
            if (index >=0)
            {
               rsName = voName.substring(index + 1);
               voName = voName.substring(0, index);
            }
               
            ds = createDataSource(pageCtx, appName, voName, rsName, dsName, false);

            // Set the attribute on the pageContext for the scriptable variable (PAGE_SCOPE because of Orion)
            pageCtx.setAttribute(dsName, ds);
      
            // Set the attribute on the pageContext at REQUEST_SCOPE for DataSourceRef tag
            pageCtx.setAttribute(dsName, ds, PageContext.REQUEST_SCOPE);
         }
   
         if (ds == null)
         {
            throw new JboException(Res.format(Res.DATA_SOURCE_NOT_FOUND, dsName));
         }

         return ds;
      }
      
      return (DataSource) dsObj;
   }

   public static DataSource createDataSource(PageContext pageCtx, String amId, String voName, String rsName, String id, boolean createRowset)
   {
      DataSourceImpl ds = null;

      ApplicationModuleRef amRef = Utils.getAMRefFromContext(pageCtx, amId);
      ApplicationModule am = amRef.useApplicationModule();

      ds = new DataSourceImpl((HttpServletRequest) pageCtx.getRequest(), voName);
      ds.setApplicationModule(amId, am, rsName, createRowset);

      return ds;
   }

   public static ApplicationModuleRef getAMRefFromContext(PageContext pageCtx, String amId)
   {   
      // Use the cookie saved in the pageContext during the ApplicationModuleTag
      ApplicationModuleRef amRef = (ApplicationModuleRef) pageCtx.getAttribute(amId);
 
      // If not found (like for included page), try the container
      if (amRef == null)
      {
         HttpContainer container = HttpContainer.getInstanceFromSession(pageCtx.getSession());
         
         amRef = (ApplicationModuleRef) container.getSessionCookie(amId);
         if (amRef == null)
         {
            throw new JboException(Res.format(Res.COOKIE_NOT_FOUND, amId));
         }
      }

      return amRef;
   }

   public static Tag findAncestorWithClassForDataSource(Tag from, String dataSourceName, java.lang.Class klass)
   {      
      Tag tag;
      for (;;)
      {
         tag = TagSupport.findAncestorWithClass(from, klass);
         if (tag == null)
         {
            break;
         } 

         DataBinder binder = (DataBinder) tag;
         // If there is no datasource defined, take the first one in the hierarchy.
         // Otherwise try to match the datasource name with the one in the parent tag.
         if (dataSourceName == null || dataSourceName.equals(binder.getDataSourceName()))
         {
            break;
         }

         from = tag;
      }

      return tag;
   }
   
   
   // Look for the first RowsetIterate tag or Row tag matching our datasource name
   // and return their row.
   public static Row getRowFromAncestor(Tag from, String dataSourceName)
   {
      Tag tag;
      
      for (;;)
      {
         tag = from.getParent();
         
         if (tag == null)
         {
            return null;
         } 

         if (tag instanceof RowsetIterateTag)
         {
            DataBinder binder = (DataBinder) tag;
            // If there is no datasource defined, take the first one in the hierarchy.
            // Otherwise try to match the datasource name with the one in the parent tag.
            if (dataSourceName == null || dataSourceName.equals(binder.getDataSourceName()))
            {
               return ((RowsetIterateTag) tag).getRow();
            }
         }
         
         if (tag instanceof RowTag)
         {
            DataBinder binder = (DataBinder) tag;
            // If there is no datasource defined, take the first one in the hierarchy.
            // Otherwise try to match the datasource name with the one in the parent tag.
            if (dataSourceName == null || dataSourceName.equals(binder.getDataSourceName()))
            {
               return ((RowTag) tag).getRow();
            }
         }

         from = tag;
      }
   }

   public static DataSource getDataSourceFromAncestor(Tag from)
   {
      // Look for the first AttributeIterateTag tag
      AttributeIterateTag attrIterTag = (AttributeIterateTag) findAncestorWithClassForDataSource(from, null, AttributeIterateTag.class);

      if (attrIterTag != null)
      {
         return attrIterTag.getDataSource();
      }
      else
      {
         // Look for the first RowsetIterate tag
         RowsetIterateTag rowSITag = (RowsetIterateTag) findAncestorWithClassForDataSource(from, null, RowsetIterateTag.class);

         if (rowSITag != null)
         {
            return rowSITag.getDataSource();
         }
         // If RowSetIterate failed, retrieve the datasource from the Row tag if any
         else
         {
            RowTag rowTag = (RowTag) findAncestorWithClassForDataSource(from, null, RowTag.class);
            if (rowTag != null)
            {
               return rowTag.getDataSource();
            }
         }
      }

      return null;
   }


   public static void releaseApplicationModule(ServletContext servletContext, HttpServletResponse response, SessionCookie cookie, String releaseMode)
   {
      // This is necessary to retain backwards compatibility
      if (releaseMode == null || releaseMode.length() <= 0)
      {
         throw new JboException(Res.getString(Res.RELEASEPAGERESOURCE_NO_RELEASEMODE));
      }
      
      if (JSPApplicationRegistry.RESERVED.equals(releaseMode))
      {
         servletContext.log("ApplicationModule release mode is:  Reserved");

         // 2142108 Changed manageState from true to false after applying
         // failover support for reserved mode.  This was necessary to
         // maintain backwards compatibility.
         cookie.releaseApplicationModule(
            false // checkin
            , false); // manageState
      }
      // If a stateful release mode has been specified then passivate
      // the application module and return it to the application pool.  This
      // mode is not supported if a response object has not been specified.
      else if (JSPApplicationRegistry.STATEFUL.equals(releaseMode))
      {
         servletContext.log("ApplicationModule release mode is:  Stateful");

         cookie.releaseApplicationModule(
            true // checkin
            , true); // manageState
         cookie.writeValue(response);
      }
      // If the release mode is stateless then simply return the application
      // module to the pool.
      else
      {
         servletContext.log("ApplicationModule release mode is:  Stateless");
         cookie.releaseApplicationModule(
            true // checkin
            , false); // manageState
         cookie.writeValue(response);
      }
   }

}
