text: // MemberAccess.cpp : Implementation of CSsrMemberAccess
  
  #include "stdafx.h"
  #include "SSRTE.h"
  #include "SSRLog.h"
  
  
  #include "MemberAccess.h"
  
  #include "global.h"
  #include "util.h"
  
  
  static bool SsrPIsValidActionType ( DWORD dwType )
  {
      return ( (dwType & SSR_ACTION_PREPARE)  ||
               (dwType & SSR_ACTION_APPLY)
             );
  }
  
  
  
  //
  // returning bool instead of bool is on purpose!
  //
  
  static bool SsrPIsDefaultAction ( DWORD dwType )
  {
      return ( (dwType & SSR_ACTION_PREPARE)  ||
               (dwType & SSR_ACTION_APPLY)
             );
  }
  
  
  static bool SsrPIsSupportedRegValueType (DWORD dwType)
  {
      return (dwType == REG_SZ || dwType == REG_MULTI_SZ || dwType == REG_DWORD);
  }
  
  //---------------------------------------------------------------------
  // CSsrMemberAccess implementation
  //---------------------------------------------------------------------
  
  
  
  /*
  Routine Description: 
  
  Name:
  
      CSsrMemberAccess::Cleanup
  
  Functionality:
      
      Cleanup the resource held by the object
  
  Virtual:
      
      No.
      
  Arguments:
  
      none.
  
  Return Value:
  
      none.
  
  Notes:
      
  
  */
  
  void CSsrMemberAccess::Cleanup()
  {
  
      MapMemberAD::iterator it = m_mapMemAD.begin();
      MapMemberAD::iterator itEnd = m_mapMemAD.end();
  
      while(it != itEnd)
      {
          CMemberAD * pVal = (*it).second;
          delete pVal;
  
          it++;
      }
  
      m_mapMemAD.clear();
      m_bstrName.Empty();
      m_bstrProgID.Empty();
  }
  
  
  /*
  Routine Description: 
  
  Name:
  
      CSsrMemberAccess::GetSupportedActions
  
  Functionality:
      
      Get the names of the actions supported by this member
  
  Virtual:
      
      yes.
      
  Arguments:
  
      bDefault        - If true, then this function queries the default actions
      
      pvarActionNames - The out parameter that receives the names of the actions of
                        the given type supported by this member
  
  Return Value:
  
      Success: S_OK if there are actions of the type and the names are returned
               S_FALSE if there is no such actions supported by the member.
  
      Failure: various error codes.
  
  Notes:
      
  
  */
  
  STDMETHODIMP
  CSsrMemberAccess::GetSupportedActions (
  	IN  BOOL      bDefault,
      OUT VARIANT * pvarActionNames  //[out, retval] 
      )
  {
      if (pvarActionNames == NULL)
      {
          return E_INVALIDARG;
      }
  
      //
      // now, create the array of action names
      //
  
      ::VariantInit(pvarActionNames);
  
      //
      // let's see how many actions of this type exists
      //
  
      MapMemberAD::iterator it = m_mapMemAD.begin();
      MapMemberAD::iterator itEnd = m_mapMemAD.end();
  
      int iCount = 0;
  
      //
      // we need to find out how many actions are of the given type
      //
  
      while(it != itEnd)
      {
          CMemberAD * pAD = (*it).second;
          _ASSERT(pAD != NULL);
  
          //
          // Be aware! Don't simply use bDefault == ::SsrPIsDefaultAction to test
          // because bDefault will be -1 when it comes from scripts!
          //
  
          if (bDefault && ::SsrPIsDefaultAction(pAD->GetType()) ||
              !bDefault && !::SsrPIsDefaultAction(pAD->GetType()))
          {
              ++iCount;
          }
          ++it;
      }
  
      if (iCount == 0)
      {
          return S_FALSE;
      }
  
      //
      // given the count we just get, now we know how big a safearray
      // we need to create.
      //
  
      SAFEARRAYBOUND rgsabound[1];
      rgsabound[0].lLbound = 0;
      rgsabound[0].cElements = iCount;
  
      SAFEARRAY * psa = ::SafeArrayCreate(VT_VARIANT , 1, rgsabound);
  
      HRESULT hr = S_OK;
  
      if (psa == NULL)
      {
          hr = E_OUTOFMEMORY;
      }
      else
      {
          //
          // we only add one name at a time
          //
  
          long indecies[1] = {0};
  
          it = m_mapMemAD.begin();
  
          while(it != itEnd)
          {
              CMemberAD * pAD = (*it).second;
              _ASSERT(pAD != NULL);
  
              //
              // count only those actions that match the requested action type
              //
  
              if (bDefault && ::SsrPIsDefaultAction(pAD->GetType()) ||
                  !bDefault && !::SsrPIsDefaultAction(pAD->GetType()))
              {
                  VARIANT v;
                  v.vt = VT_BSTR;
                  v.bstrVal = ::SysAllocString(pAD->GetActionName());
  
                  if (v.bstrVal != NULL)
                  {
                      hr = ::SafeArrayPutElement(psa, indecies, &v);
                      ::VariantClear(&v);
                  }
                  else
                  {
                      hr = E_OUTOFMEMORY;
                  }
  
                  if (FAILED(hr))
                  {
                      break;
                  }
  
                  indecies[0]++;
              }
  
              ++it;
          }
  
          //
          // only return the safearray if everything goes well
          //
  
          if (SUCCEEDED(hr))
          {
              pvarActionNames->vt = VT_ARRAY | VT_VARIANT;
              pvarActionNames->parray = psa;
          }
          else
          {
              ::SafeArrayDestroy(psa);
          }
  
      }
  
      return hr;
  }
  
  
  
  /*
  Routine Description: 
  
  Name:
  
      CSsrMemberAccess::get_Name
  
  Functionality:
      
      Get the names of the member.
  
  Virtual:
      
      yes.
      
  Arguments:
  
      pbstrName   - the BSTR which is the action of the member.
  
  Return Value:
  
      Success: S_OK as long as pbstrName is not NULL (which is invalid)
  
      Failure: various error codes.
  
  Notes:
      
  
  */
  
  STDMETHODIMP
  CSsrMemberAccess::get_Name (
      OUT BSTR * pbstrName    // [out, retval] 
      )
  {
      if (pbstrName == NULL)
      {
          return E_INVALIDARG;
      }
  
      *pbstrName = ::SysAllocString(m_bstrName);
  
      return (NULL == *pbstrName) ? E_OUTOFMEMORY : S_OK;
  }
  
  
  
  /*
  Routine Description: 
  
  Name:
  
      CSsrMemberAccess::get_SsrMember
  
  Functionality:
      
      Return the SsrMember property - the custom ISsrMember object implemented by 
      the member who desires to implement some custom behavior for certain actions.
  
  
  Virtual:
      
      yes.
      
  Arguments:
  
      pvarSsrMember   - Out parameter receiving the custom ISsrMember object 
                        implemented by the member who desires to implement some
                        custom behavior for certain actions.
  
  Return Value:
  
      Success: S_OK if this member does have a custom implementation
               of ISsrMember. 
  
               S_FALSE if this member doesn't have a custom 
               implementation of ISsrMember.
  
      Failure: various error codes
  
  Notes:
      
  
  */
  
  STDMETHODIMP
  CSsrMemberAccess::get_SsrMember (
      OUT VARIANT * pvarSsrMember //[out, retval] 
      )
  {
      if (pvarSsrMember == NULL)
      {
          return E_INVALIDARG;
      }
  
      ::VariantInit(pvarSsrMember);
  
      HRESULT hr = S_FALSE;
  
      if (m_bstrProgID != NULL)
      {
          //
          // now create the COM object
          //
  
          GUID clsID;
  
          hr = ::CLSIDFromProgID(m_bstrProgID, &clsID);
  
          if (S_OK == hr)
          {
              ISsrMember * pISsrMember = NULL;
              hr = ::CoCreateInstance(clsID, 
                                      NULL, 
                                      CLSCTX_INPROC_SERVER, 
                                      IID_ISsrMember, 
                                      (LPVOID*)&pISsrMember
                                      );
  
              if (S_OK == hr)
              {
                  pvarSsrMember->vt = VT_DISPATCH;
                  pvarSsrMember->pdispVal = pISsrMember;
              }
          }
      }
  
      return hr;
  }
  
  
  /*
  Routine Description: 
  
  Name:
  
      CSsrMemberAccess::Load
  
  Functionality:
      
      Will create this object based on the information available
      from the registry key.
  
  Virtual:
      
      no.
      
  Arguments:
  
      wszMemberFilePath   - The path for the member registration XML file.
  
  Return Value:
  
      Success: S_OK if there are concrete member information being 
               loaded (has action data). 
               
               S_FALSE if there is really nothing this member has registered.
               Such a member should be discarded because it doesn't contain
               anything that SSR can use.
  
      Failure: various error codes.
  
  Notes:
      
  
  */
  
  HRESULT
  CSsrMemberAccess::Load (
      IN LPCWSTR  wszMemberFilePath
      )
  {
      if (wszMemberFilePath == NULL || *wszMemberFilePath == L'\0')
      {
          return E_INVALIDARG;
      }
  
      //
      // just in case, this object is called to Load twice, clean up everything first
      //
  
      Cleanup();
  
      //
      // load the DOM
      //
  
      CComPtr<IXMLDOMDocument2> srpXmlDom;
  
      HRESULT hr = ::CoCreateInstance(CLSID_DOMDocument40, 
                              NULL, 
                              CLSCTX_SERVER, 
                              IID_IXMLDOMDocument2, 
                              (LPVOID*)(&srpXmlDom) 
                              );
  
      if (FAILED(hr))
      {
          return hr;
      }
  
      hr = SsrPLoadDOM(CComBSTR(wszMemberFilePath), SSR_LOADDOM_VALIDATE_ON_PARSE, srpXmlDom);
  
      if (FAILED(hr))
      {
          return hr;
      }
  
      CComPtr<IXMLDOMElement>  srpXMLDocRoot;
      hr = srpXmlDom->get_documentElement(&srpXMLDocRoot);
  
      if (FAILED(hr))
      {
          return hr;
      }
  
      //
      // Get the UniqueName attribute
      //
  
      CComPtr<IXMLDOMNamedNodeMap> srpAttr;
  
      if (SUCCEEDED(hr))
      {
          CComVariant varAttr;
          hr = srpXMLDocRoot->getAttribute(g_bstrAttrUniqueName, &varAttr);
  
          if (FAILED(hr) || varAttr.vt != VT_BSTR)
          {
              //
              // we must have the unique name. This fails, we log and quit
              //
  
              return hr;
          }
          else
          {
              m_bstrName = varAttr.bstrVal;
              varAttr.vt = VT_EMPTY;
          }
  
          //
          // first, let's see if there is a ProgID value. We will ignore
          // any failure of reading the ProgID since it may not be there at all.
          //
  
          if (SUCCEEDED(srpXMLDocRoot->getAttribute(g_bstrAttrProgID, &varAttr)) && 
              varAttr.vt == VT_BSTR)
          {
              m_bstrProgID = varAttr.bstrVal;
              varAttr.vt = VT_EMPTY;
          }
  
          varAttr.Clear();
  
          //
          // Let's grab the major and minor versions. Currently, we don't have any
          // implementation to enforce them other than that the major version must
          // the same as our dll's. Otherwise we quit
          //
  
          if (SUCCEEDED(srpXMLDocRoot->getAttribute(g_bstrAttrMajorVersion, &varAttr)))
          {
              CComVariant varMajor;
  
              if (SUCCEEDED(VariantChangeType(&varMajor, &varAttr, VARIANT_NOVALUEPROP, VT_UI4)))
              {
                  m_ulMajorVersion = varMajor.ulVal;
              }
          }
  
          if (m_ulMajorVersion != g_ulSsrEngineMajorVersion)
          {
              return E_SSR_MAJOR_VERSION_MISMATCH;
          }
  
          varAttr.Clear();
  
          if (SUCCEEDED(srpXMLDocRoot->getAttribute(g_bstrAttrMinorVersion, &varAttr)))
          {
              CComVariant varMinor;
              if (SUCCEEDED(VariantChangeType(&varMinor, &varAttr, VARIANT_NOVALUEPROP, VT_UI4)))
              {
                  m_ulMinorVersion = varMinor.ulVal;
              }
          }
      }
  
      //
      // now, let's load each action
      //
  
      CComPtr<IXMLDOMNode> srpActionNode;
      hr = srpXMLDocRoot->get_firstChild(&srpActionNode);
  
      bool bLoaded = false;
  
      while (SUCCEEDED(hr) && srpActionNode != NULL)
      {
          CComBSTR bstrName;
          srpActionNode->get_nodeName(&bstrName);
  
          if (_wcsicmp(bstrName, g_bstrSupportedAction) == 0)
          {
              //
              // we only care about SupportedAction elements
              //
  
              CMemberAD * pMAD = NULL;
  
              hr = CMemberAD::LoadAD(m_bstrName, srpActionNode, m_bstrProgID, &pMAD);
  
              if (SUCCEEDED(hr))
              {
                  //
                  // we might load some empty procedure
                  //
  
                  if (pMAD != NULL)
                  {
                      const CActionType * pAT = pMAD->GetActionType();
  
                      m_mapMemAD.insert(MapMemberAD::value_type(*pAT, pMAD));
  
                      bLoaded = true;
                  }
              }
              else
              {
                  g_fblog.LogFeedback(SSR_FB_ERROR_LOAD_MEMBER | FBLog_Log, 
                                      hr,
                                      wszMemberFilePath,
                                      IDS_XML_LOADING_MEMBER
                                      );
                  break;
              }
          }
  
  
          CComPtr<IXMLDOMNode> srpNext;
          hr = srpActionNode->get_nextSibling(&srpNext);
          srpActionNode.Release();
          srpActionNode = srpNext;
      }
  
      if (FAILED(hr))
      {
          g_fblog.LogError(hr, L"CSsrMemberAccess::Load", wszMemberFilePath);
      }
      else if (bLoaded)
      {
          hr = S_OK;
      }
  
      return hr;
  }
  
  
  
  /*
  Routine Description: 
  
  Name:
  
      CSsrMemberAccess::GetActionDataObject
  
  Functionality:
      
      Will find and return (if found) the CMemberAD that of the given name.
      This is a helper function
  
  
  Virtual:
      
      no.
      
  Arguments:
  
      lActionVerb  - The action verb in long format.
  
      lActionType  - The action type
  
  Return Value:
  
      If found, then the CMemberAD object pointer is return. It will return NULl
      if the given action is not registered or this operation can't be completed.
  
  Notes:
      
  
  */
  
  CMemberAD* 
  CSsrMemberAccess::GetActionDataObject (
      IN SsrActionVerb lActionVerb,
      IN LONG          lActionType
      )
  {
      CActionType at(lActionVerb, lActionType);
  
      MapMemberAD::iterator it = m_mapMemAD.find(at);
      MapMemberAD::iterator itEnd = m_mapMemAD.end();
  
      if (it != itEnd)
      {
          CMemberAD * pAD = (*it).second;
          return pAD;
      }
  
      return NULL;
  }
  
  
  
  
  
  /*
  Routine Description: 
  
  Name:
  
      CSsrMemberAccess::MoveOutputFiles
  
  Functionality:
      
      Will move/delete all those output files.
  
  
  Virtual:
      
      no.
      
  Arguments:
  
      bstrActionVerb  - the action verb
  
      pwszDirPathSrc  - The directory path from which the files will be moved.
  
      pwszDirPathSrc  - The directory path to which the files will be moved. This will
                        be ignored if the action is a delete
  
      bDelete         - Flag as whether the action is a move or delete.
  
      bLog            - To prevent extraneous logging during restoring (backed up
                        files), if this is not true, then no logging will occur
  
  
  Return Value:
  
      Success: S_OK
  
      Failure: various error codes
  
  Notes:
      
  
  */
  
  HRESULT 
  CSsrMemberAccess::MoveOutputFiles (
      IN SsrActionVerb lActionVerb,
      IN LPCWSTR       pwszDirPathSrc,
      IN LPCWSTR       pwszDirPathDest,
      IN bool          bDelete,
      IN bool          bLog
      )
  {
      if (bLog)
      {
          CComBSTR bstrMsg(L"...");
          bstrMsg += m_bstrName;
  
          if (bstrMsg.m_str != NULL)
          {
              g_fblog.LogString(bstrMsg);
          }
          else
          {
              return E_OUTOFMEMORY;
          }
      }
  
      if (lActionVerb     == ActionInvalid    || 
          pwszDirPathSrc  == NULL             || 
          pwszDirPathDest == NULL && !bDelete )
      {
          return E_INVALIDARG;
      }
  
      //
      // output files are the transformation results. So, we need
      // the transformation result action data, which has the (xsl, output)
      // file pairs
      //
  
      //
      // find the action data for the given action
      //
  
      CActionType at(lActionVerb, SSR_ACTION_PREPARE);
  
      MapMemberAD::iterator it = m_mapMemAD.find(at);
  
      HRESULT hr = S_OK;
  
      //
      // since we will continue the cleanup in case of errors
      // we will return the last error
      //
  
      HRESULT hrLastError = S_OK;
  
      if (it != m_mapMemAD.end())
      {
          CMemberAD * pAD = (*it).second;
  
          _ASSERT(pAD != NULL);
  
          int iProcCount = pAD->GetProcedureCount();
  
          //
          // Each member (CMemberAD) may have multiple procedures for this action
          //
  
          for (int iProcIndex = 0; iProcIndex < iProcCount; iProcIndex++)
          {
              const CSsrProcedure * pProc = pAD->GetProcedure(iProcIndex);
              _ASSERT(pProc != NULL);
  
              int iFilePairsCount = pProc->GetFilePairCount();
  
              CSsrFilePair * pFilePair;
  
              //
              // each procedure may contain multiple file pairs
              //
  
              for (int iFPIndex = 0; iFPIndex < iFilePairsCount; iFPIndex++)
              {
                  pFilePair = pProc->GetFilePair(iFPIndex);
  
                  _ASSERT(pFilePair != NULL);
  
                  //
                  // if no second file (which is the result file) or the
                  // second file is a static file, then don't bother to move them
                  //
  
                  if ( pFilePair->GetSecond() == NULL || pFilePair->IsStatic() )
                  {
                      continue;
                  }
  
                  //
                  // move/delete this file
                  //
  
                  CComBSTR bstrSrcFullPath(pwszDirPathSrc);
                  bstrSrcFullPath += L"\\";
                  bstrSrcFullPath += pFilePair->GetSecond();
                  if (bstrSrcFullPath.m_str == NULL)
                  {
                      return E_OUTOFMEMORY;
                  }
  
                  if (bDelete)
                  {
                      ::DeleteFile(bstrSrcFullPath);
                  }
                  else
                  {
                      CComBSTR bstrDestFullPath(pwszDirPathDest);
                      bstrDestFullPath += L"\\";
                      bstrDestFullPath += pFilePair->GetSecond();
  
                      if (bstrDestFullPath.m_str == NULL)
                      {
                          return E_OUTOFMEMORY;
                      }
  
                      ::MoveFile(bstrSrcFullPath, bstrDestFullPath);
                  }
  
                  DWORD dwErrorCode = GetLastError();
  
                  if (dwErrorCode != ERROR_SUCCESS && 
                      dwErrorCode != ERROR_FILE_NOT_FOUND)
                  {
                      hr = HRESULT_FROM_WIN32(dwErrorCode);
  
                      //
                      // we will continue to delete the others. But log it
                      //
  
                      if (bLog)
                      {
                          hrLastError = hr;
                          g_fblog.LogFeedback(SSR_FB_ERROR_FILE_DEL | FBLog_Log, 
                                              hrLastError,
                                              bstrSrcFullPath,
                                              g_dwResNothing
                                              );
  
                      }
                  }
              }
          }
      }
  
      return hrLastError;
  }
  
  DWORD 
  CSsrMemberAccess::GetActionCost (
      IN SsrActionVerb lActionVerb,
      IN LONG          lActionType
      )const
  {
      CActionType at(lActionVerb, lActionType);
  
      MapMemberAD::iterator it = m_mapMemAD.find(at);
  
      DWORD dwSteps = 0;
  
      if (it != m_mapMemAD.end())
      {
          CMemberAD * pAD = (*it).second;
          _ASSERT(pAD != NULL);
  
          for (int i = 0; i < pAD->GetProcedureCount(); i++)
          {
              const CSsrProcedure * pProc = pAD->GetProcedure(i);
  
              if (pProc->IsDefaultProcedure())
              {
                  //
                  // each file pair will count as two steps
                  //
  
                  dwSteps += 2 * pProc->GetFilePairCount();
              }
              else
              {
                  //
                  // we have to consult with the custom member
                  //
  
                  CComPtr<ISsrMember> srpCusMember;
  
                  GUID clsID;
  
                  HRESULT hr = ::CLSIDFromProgID(pProc->GetProgID(), &clsID);
  
                  if (S_OK == hr)
                  {
                      hr = ::CoCreateInstance(clsID, 
                                              NULL, 
                                              CLSCTX_INPROC_SERVER, 
                                              IID_ISsrMember, 
                                              (LPVOID*)&srpCusMember
                                              );
                  }
  
                  if (SUCCEEDED(hr))
                  {
                      LONG lCost = 0;
                      hr = srpCusMember->get_ActionCost(SsrPGetActionVerbString(lActionVerb),
                                                        lActionType,
                                                        SSR_ACTION_COST_STEPS,
                                                        &lCost
                                                        );
  
                      if (SUCCEEDED(hr))
                      {
                          dwSteps += lCost;
                      }
                  }
  
              }
          }
      }
  
      return dwSteps;
  }
  
  
  /*
  Routine Description: 
  
  Name:
  
      CMemberAD::CMemberAD
  
  Functionality:
      
      Constructor.
  
  
  Virtual:
      
      no.
      
  Arguments:
  
      lActionVerb - The verb of the action.
  
      lActionType - The type of the action
  
  Return Value:
  
      None
  
  Notes:
  
  */
  
  CMemberAD::CMemberAD (
      IN SsrActionVerb lActionVerb,
      IN LONG          lActionType
      ) : m_AT(lActionVerb, lActionType)
  {
  }
  
  
  
  
  /*
  Routine Description: 
  
  Name:
  
      CMemberAD::~CMemberAD
  
  Functionality:
      
      destructor. This will clean up our map which contains BSTRs and VARIANTs,
      both of which are heap objects.
  
  
  Virtual:
      
      no.
      
  Arguments:
  
      None.
  
  Return Value:
  
      None
  
  Notes:
  
  */
  
  CMemberAD::~CMemberAD()
  {
      for (ULONG i = 0; i < m_vecProcedures.size(); i++)
      {
          delete m_vecProcedures[i];
      }
  
      m_vecProcedures.clear();
  }
  
  
  
  
  
  /*
  Routine Description: 
  
  Name:
  
      CMemberAD::LoadAD
  
  Functionality:
      
      Will create an a action data object pertaining to a 
      particular member and an action name.
  
  
  Virtual:
      
      no.
      
  Arguments:
  
      pActionNode     - The SsrAction node
  
      ppMAD           - The out parameter that receives the 
                        heap object created by this function
  
  Return Value:
  
      Success: S_OK if there are concrete action data loaded 
               and in that case the out paramter ppMAD will
               point to the heap object. Otherwise, *ppMAD == NULL;
  
      Failure: various error codes.
  
  Notes:
      
      2. Caller is responsible for releaseing the CMemberAD 
         object passed back by the function.
  
  */
  
  HRESULT
  CMemberAD::LoadAD (
      IN  LPCWSTR       pwszMemberName,
      IN  IXMLDOMNode * pActionNode,
      IN  LPCWSTR       pwszProgID,
      OUT CMemberAD  ** ppMAD
      )
  {
      if (ppMAD == NULL)
      {
          return E_INVALIDARG;
      }
  
      *ppMAD = NULL;
  
      if (pActionNode == NULL)
      {
          return E_INVALIDARG;
      }
  
      CComPtr<IXMLDOMNamedNodeMap> srpAttr;
  
      HRESULT hr = pActionNode->get_attributes(&srpAttr);
  
      //
      // we must have attributes
      //
  
      if (FAILED(hr))
      {
          return hr;
      }
  
      CComBSTR bstrActionName, bstrActionType;
      LONG lActionType;
  
      //
      // ActionName and ActionType are mandatory attributes
      //
  
      hr = SsrPGetBSTRAttrValue(srpAttr, g_bstrAttrActionName, &bstrActionName);
      if (FAILED(hr))
      {
          return hr;
      }
  
      SsrActionVerb lActionVerb = SsrPGetActionVerbFromString(bstrActionName);
      if (lActionVerb == ActionInvalid)
      {
          return E_SSR_INVALID_ACTION_VERB;
      }
  
      hr = SsrPGetBSTRAttrValue(srpAttr, g_bstrAttrActionType, &bstrActionType);
  
      if (FAILED(hr))
      {
          return hr;
      }
  
      if (_wcsicmp(bstrActionType, g_pwszPrepare) == 0)
      {
          lActionType = SSR_ACTION_PREPARE;
      }
      else
      {
          _ASSERT(_wcsicmp(bstrActionType, g_pwszApply) == 0);
          lActionType = SSR_ACTION_APPLY;
      }
  
      *ppMAD = new CMemberAD(lActionVerb, lActionType);
  
      if (*ppMAD == NULL)
      {
          return E_OUTOFMEMORY;
      }
  
      //
      // now, we need to load the each individual procedure
      //
  
      bool bLoaded = false;
  
      if (SUCCEEDED(hr))
      {
          CComPtr<IXMLDOMNode> srpProcedure;
          hr = pActionNode->get_firstChild(&srpProcedure);
  
          while (SUCCEEDED(hr) && srpProcedure != NULL)
          {
              CComBSTR bstrName;
              srpProcedure->get_nodeName(&bstrName);
  
              bool bDefProc = _wcsicmp(bstrName, g_bstrDefaultProc) == 0;
              bool bCusProc = _wcsicmp(bstrName, g_bstrCustomProc) == 0;
  
              //
              // we only care about DefaultProc and CustomProc elements
              //
              if ( bDefProc || bCusProc )
              {
  
                  CSsrProcedure * pNewProc = NULL;
                  if (SUCCEEDED(hr))
                  {
                      hr = CSsrProcedure::StaticLoadProcedure(srpProcedure, bDefProc, pwszProgID, &pNewProc);
                  }
          
                  if (SUCCEEDED(hr))
                  {
                      //
                      // give it to our vector
                      //
  
                      (*ppMAD)->m_vecProcedures.push_back(pNewProc);
                      bLoaded = true;
                  }
                  else
                  {
                      //
                      // will quit loading
                      //
  
                      g_fblog.LogFeedback(SSR_FB_ERROR_LOAD_MEMBER | FBLog_Log, 
                                          hr,
                                          pwszMemberName,
                                          IDS_XML_LOADING_PROCEDURE
                                          );
                      break;
                  }
              }
  
              CComPtr<IXMLDOMNode> srpNext;
              hr = srpProcedure->get_nextSibling(&srpNext);
              srpProcedure.Release();
              srpProcedure = srpNext;
          }
      }
  
      //
      // either failed or loaded nothing
      //
  
      if (FAILED(hr) || !bLoaded)
      {
          delete *ppMAD;
          *ppMAD = NULL;
      }
  
      return SUCCEEDED(hr) ? S_OK : hr;
  }
  
  
  
  CSsrProcedure::CSsrProcedure()
  : m_bIsDefault(true)
  {
  }
  
  CSsrProcedure::~CSsrProcedure()
  {
      for (ULONG i = 0; i < m_vecFilePairs.size(); i++)
      {
          delete m_vecFilePairs[i];
      }
  
      m_vecFilePairs.clear();
  }
  
  HRESULT
  CSsrProcedure::StaticLoadProcedure (
      IN  IXMLDOMNode    * pNode,
      IN  bool             bDefProc,
      IN  LPCWSTR          pwszProgID,
      OUT CSsrProcedure ** ppNewProc
      )
  {
      if (ppNewProc == NULL)
      {
          return E_INVALIDARG;
      }
  
      *ppNewProc = NULL;
  
      if (pNode == NULL)
      {
          return E_INVALIDARG;
      }
  
      *ppNewProc = new CSsrProcedure;
  
      if (*ppNewProc == NULL)
      {
          return E_OUTOFMEMORY;
      }
  
      //
      // let's determine if this procedure is a default proc or custom proc.
      // That is determined by the tag name.
      //
  
      CComPtr<IXMLDOMNamedNodeMap> srpAttr;
      CComBSTR bstrTagName;
  
      HRESULT hr = S_OK;
  
      if (!bDefProc)
      {
          //
          // we are loding custom proc
          //
  
          (*ppNewProc)->m_bIsDefault = false;
  
          //
          // in this case, you should have no more than the ProgID that we care
          //
  
          hr = pNode->get_attributes(&srpAttr);
          _ASSERT(srpAttr);
  
          CComBSTR bstrProgID;
  
          if (SUCCEEDED(hr))
          {
              //
              // will try to get the ProgID attribute, may fail and we
              // don't care if it does
              //
  
              SsrPGetBSTRAttrValue(srpAttr, g_bstrAttrProgID, &bstrProgID);
          }
  
          if (bstrProgID != NULL)
          {
              (*ppNewProc)->m_bstrProgID = bstrProgID;
          }
          else
          {
              (*ppNewProc)->m_bstrProgID = pwszProgID;
          }
      }
      else
      {
          //
          // for default procedures, we should have a list of TransformFiles or ScriptFiles
          // elements. But we will create for both type of elements a CSsrFilePair object
          // and put it in the m_vecFilePairs vector
          //
  
          //
          // for that purpose, we need to traverse through the in sequential order
          // the TransformFiles and ScriptFiles elements
          //
  
          CComPtr<IXMLDOMNode> srpFilePairNode;
          CComPtr<IXMLDOMNode> srpNext;
          hr = pNode->get_firstChild(&srpFilePairNode);
  
          while (SUCCEEDED(hr) && srpFilePairNode != NULL)
          {
              //
              // Get the tag name. Empty the smart pointer so that we can re-use it
              //
      
              CComPtr<IXMLDOMNamedNodeMap> srpFilePairAttr;
  
              bstrTagName.Empty();
  
              hr = srpFilePairNode->get_nodeName(&bstrTagName);
              _ASSERT(SUCCEEDED(hr));
  
              if (FAILED(hr))
              {
                  break;
              }
  
              hr = srpFilePairNode->get_attributes(&srpFilePairAttr);
              _ASSERT(srpAttr);
  
              //
              // we will ignore all other elements because we only know
              // two possible types of elements: TransformInfo and ScriptInfo
              //
  
              if (_wcsicmp(bstrTagName, g_bstrTransformInfo) == 0)
              {
                  //
                  // if it is a TransformFiles element, then, we really have a pair
                  // (xsl, script)
                  //
  
                  //
                  // we have potentially both TemplateFile and ResultFile attributes
                  //
  
                  CComBSTR bstrXsl, bstrResult;
                  hr = SsrPGetBSTRAttrValue(srpFilePairAttr, g_bstrAttrTemplateFile, &bstrXsl);
                  _ASSERT(SUCCEEDED(hr));
  
                  //
                  // may not have this one
                  //
  
                  SsrPGetBSTRAttrValue(srpFilePairAttr, g_bstrAttrResultFile, &bstrResult);
  
                  if (SUCCEEDED(hr))
                  {
                      //
                      // any result file is used as a result file (funny?), which means that the 
                      // file is created during the process of transformation
                      //
  
                      CSsrFilePair * pNewFilePair = new CSsrFilePair(bstrXsl, bstrResult);
                      if (pNewFilePair != NULL)
                      {
                          (*ppNewProc)->m_vecFilePairs.push_back(pNewFilePair);
                      }
                      else
                      {
                          hr = E_OUTOFMEMORY;
                      }
                  }
  
              }
              else if (_wcsicmp(bstrTagName, g_bstrScriptInfo) == 0)
              {
  
                  //
                  // we will only have ScriptFile and usage attributes
                  //
  
                  CComBSTR bstrAttr;
                  CComBSTR bstrScript;
                  hr = SsrPGetBSTRAttrValue(srpFilePairAttr, g_bstrAttrScriptFile, &bstrScript);
                  _ASSERT(SUCCEEDED(hr));
  
                  if (SUCCEEDED(hr))
                  {
  
                      //
                      // may not have this one, but in that case, it is defaulted
                      // to "Launch". Since an executable file will be launched,
                      // it's safer to assume that it is not an executable file.
                      // Likewise, since non-static file will be removed during
                      // preparation, we'd better assume that it is a static.
                      //
  
                      bool bIsExecutable = false;
                      bool bIsStatic = true;
  
                      //
                      // Get the IsExecutable attribute
                      //
  
                      bstrAttr.Empty();
                      if ( SUCCEEDED(SsrPGetBSTRAttrValue(srpFilePairAttr, 
                                                          g_bstrAttrIsExecutable, 
                                                          &bstrAttr)) &&
                           bstrAttr != NULL )
                      {
                          //
                          // err on the side of false is safer
                          //
  
                          if (_wcsicmp(bstrAttr, g_bstrTrue) != 0)
                          {
                              bIsExecutable = false;
                          }
                          else
                          {
                             bIsExecutable = true;
                          }
                      }
  
                      //
                      // Get the IsStatic attribute.
                      //
  
                      bstrAttr.Empty();
                      if ( SUCCEEDED(SsrPGetBSTRAttrValue(srpFilePairAttr, 
                                                          g_bstrAttrIsStatic, 
                                                          &bstrAttr)) &&
                           bstrAttr != NULL )
                      {
                          if (_wcsicmp(bstrAttr, g_bstrFalse) == 0)
                          {
                              bIsStatic = false;
                          }
                      }
  
                      //
                      // the script may be a result of a preparation, or a script file
                      // to launch during the action
                      //
  
                      CSsrFilePair * pNewFilePair = new CSsrFilePair(NULL, bstrScript, bIsStatic, bIsExecutable);
                      if (pNewFilePair != NULL)
                      {
                          (*ppNewProc)->m_vecFilePairs.push_back(pNewFilePair);
                      }
                      else
                      {
                          hr = E_OUTOFMEMORY;
                      }
                  }
  
              }
  
              if (FAILED(hr))
              {
                  break;
              }
  
              //
              // if it is a ScriptFiles element, then, it only has the script file
              //
  
              hr = srpFilePairNode->get_nextSibling(&srpNext);
              srpFilePairNode = srpNext;
              srpNext.Release();
          }
      }
  
      if (FAILED(hr) && *ppNewProc != NULL)
      {
          delete *ppNewProc;
          *ppNewProc = NULL;
      }
  
      return SUCCEEDED(hr) ? S_OK : hr;
  }
  
  
  