NCBI C++ ToolKit
gb_ui_data_source.cpp
Go to the documentation of this file.
00001 /*  $Id: gb_ui_data_source.cpp 25238 2012-02-14 16:11:43Z ucko $
00002  * ===========================================================================
00003  *
00004  *                            PUBLIC DOMAIN NOTICE
00005  *               National Center for Biotechnology Information
00006  *
00007  *  This software/database is a "United States Government Work" under the
00008  *  terms of the United States Copyright Act.  It was written as part of
00009  *  the author's official duties as a United States Government employee and
00010  *  thus cannot be copyrighted.  This software/database is freely available
00011  *  to the public for use. The National Library of Medicine and the U.S.
00012  *  Government have not placed any restriction on its use or reproduction.
00013  *
00014  *  Although all reasonable efforts have been taken to ensure the accuracy
00015  *  and reliability of the software and data, the NLM and the U.S.
00016  *  Government do not and cannot warrant the performance or results that
00017  *  may be obtained by using this software or data. The NLM and the U.S.
00018  *  Government disclaim all warranties, express or implied, including
00019  *  warranties of performance, merchantability or fitness for any particular
00020  *  purpose.
00021  *
00022  *  Please cite the author in any work or product based on this material.
00023  *
00024  * ===========================================================================
00025  *
00026  * Authors:  Andrey Yazhuk
00027  *
00028  */
00029 
00030 #include <ncbi_pch.hpp>
00031 
00032 #include <corelib/ncbiexec.hpp>
00033 #include <corelib/ncbi_process.hpp>
00034 #include <corelib/ncbi_system.hpp>
00035 
00036 #include <gui/packages/pkg_sequence/gb_ui_data_source.hpp>
00037 
00038 #include "gb_load_option_panel.hpp"
00039 
00040 #include <gui/core/app_tasks.hpp>
00041 #include <gui/core/app_explorer_service.hpp>
00042 #include <gui/core/named_pipe.hpp>
00043 
00044 #include <gui/framework/workbench.hpp>
00045 #include <gui/framework/app_task_service.hpp>
00046 #include <gui/framework/app_job_task.hpp>
00047 
00048 #include <gui/widgets/wx/wx_utils.hpp>
00049 #include <gui/widgets/wx/fileartprov.hpp>
00050 #include <gui/widgets/wx/ui_command.hpp>
00051 #include <gui/widgets/wx/message_box.hpp>
00052 #include <gui/widgets/wx/wx_utils.hpp>
00053 #include <gui/widgets/wx/sys_path.hpp>
00054 
00055 #include <objmgr/object_manager.hpp>
00056 #include <objtools/data_loaders/genbank/gbloader.hpp>
00057 #include <connect/services/neticache_client.hpp>
00058 
00059 #include <gui/objects/ProjectItem.hpp>
00060 #include <gui/objutils/registry.hpp>
00061 #include <gui/utils/extension_impl.hpp>
00062 
00063 
00064 #include <wx/menu.h>
00065 #include <wx/filename.h>
00066 
00067 BEGIN_NCBI_SCOPE
00068 USING_SCOPE(objects);
00069 
00070 static const char* kGenBankLoadOption = "Data from GenBank";
00071 
00072 ///////////////////////////////////////////////////////////////////////////////
00073 /// CGenBankDSEvtHandler - wxEvtHandler-derived adapter for GenBank data source.
00074 
00075 class CGenBankDSEvtHandler : public wxEvtHandler
00076 {
00077     DECLARE_EVENT_TABLE();
00078 public:
00079     CGenBankDSEvtHandler(IWorkbench* workbench)
00080         :   m_Workbench(workbench)  {
00081     }
00082     void OnLoadFromGenBank(wxCommandEvent& event)
00083     {
00084         if(m_Workbench)    {
00085             COpenDlgTask* task = new COpenDlgTask(m_Workbench, kGenBankLoadOption);
00086 
00087             CAppTaskService* task_srv = m_Workbench->GetServiceByType<CAppTaskService>();
00088             task_srv->AddTask(*task);
00089         }
00090     }
00091 protected:
00092     IWorkbench* m_Workbench;
00093 };
00094 
00095 
00096 BEGIN_EVENT_TABLE(CGenBankDSEvtHandler, wxEvtHandler)
00097     EVT_MENU(eCmdLoadFromGenBank, CGenBankDSEvtHandler::OnLoadFromGenBank)
00098 END_EVENT_TABLE();
00099 
00100 
00101 ///////////////////////////////////////////////////////////////////////////////
00102 /// CGenBankUIDataSource
00103 
00104 static const char* kGB_DS_Icon = "icon::gb_data_source";
00105 
00106 CGenBankUIDataSource::CGenBankUIDataSource(CGenBankUIDataSourceType& type)
00107 :   m_Type(&type),
00108     m_Descr("GenBank", kGB_DS_Icon),
00109     m_SrvLocator(NULL),
00110     m_Open(false)
00111 {
00112 }
00113 
00114 CGenBankUIDataSource::~CGenBankUIDataSource()
00115 {
00116 }
00117 
00118 
00119 string CGenBankUIDataSource::GetExtensionIdentifier() const
00120 {
00121     return "genbank_data_source";
00122 }
00123 
00124 
00125 string CGenBankUIDataSource::GetExtensionLabel() const
00126 {
00127     return "GenBank Data Source";
00128 }
00129 
00130 
00131 void CGenBankUIDataSource::SetServiceLocator(IServiceLocator* locator)
00132 {
00133     m_SrvLocator = locator;
00134 }
00135 
00136 
00137 IExplorerItemCmdContributor::TContribution
00138     CGenBankUIDataSource::GetMenu(TItemRefVector& items,
00139                                    CAppExplorerService& app_service)
00140 {
00141     IExplorerItemCmdContributor::TContribution contrib;
00142 
00143     /// this is not a good solution, but simple
00144     IWorkbench* workbench = dynamic_cast<IWorkbench*>(m_SrvLocator);
00145 
00146     if(items.size() != 1  ||  workbench == NULL)   {
00147         return contrib; // return empty object - nothin to contribute
00148     }
00149 
00150     CUICommandRegistry& cmd_reg = CUICommandRegistry::GetInstance();
00151 
00152     CExplorerItem* item = items[0].GetPointer();
00153     int type = app_service.GetItemType(*item);
00154 
00155     if(type == CAppExplorerService::eDataSource)    {
00156         CAppExplorerService::TDataSourceTreeItem* ds_item =
00157             dynamic_cast<CAppExplorerService::TDataSourceTreeItem*>(item);
00158 
00159         if(ds_item) {
00160             CIRef<IUIDataSource> ds = ds_item->GetData();
00161             CGenBankUIDataSource* gb_ds = dynamic_cast<CGenBankUIDataSource*>(ds.GetPointer());
00162 
00163             if(gb_ds)  {
00164                 wxMenu* menu = new wxMenu;
00165                 menu->Append(wxID_SEPARATOR, wxT("Top Actions"));
00166                 cmd_reg.AppendMenuItem(*menu, eCmdLoadFromGenBank);
00167 
00168                 contrib.first = menu;
00169                 contrib.second = new CGenBankDSEvtHandler(workbench);
00170             }
00171         }
00172     }
00173     return contrib;
00174 }
00175 
00176 
00177 IUIDataSourceType& CGenBankUIDataSource::GetType() const
00178 {
00179     return *m_Type;
00180 }
00181 
00182 
00183 const IUIObject& CGenBankUIDataSource::GetDescr()
00184 {
00185     return m_Descr;
00186 }
00187 
00188 
00189 bool CGenBankUIDataSource::IsOpen()
00190 {
00191     return m_Open;
00192 }
00193 
00194 //static const char* kCmdExtPoint = "scoped_objects::cmd_contributor";
00195 static const char* kAppExpCmdExtPoint = "project_tree_view::context_menu::item_cmd_contributor";
00196 
00197 bool CGenBankUIDataSource::Open()
00198 {
00199     if (m_Open) {
00200         LOG_POST(Error << "CGenBankUIDataSource::Open(): "
00201             "attempt to open already open data source");
00202         return false;
00203     }
00204 
00205     CStopWatch sw;
00206     sw.Start();
00207 
00208     /// register itself as menu contributor
00209     CIRef<IExtensionRegistry> reg = CExtensionRegistry::GetInstance();
00210     reg->AddExtension(kAppExpCmdExtPoint, *this);
00211 
00212     /// initialize the object manager
00213     CGuiRegistry& registry = CGuiRegistry::GetInstance();
00214     CGuiRegistry::TReadWriteView view =
00215         registry.GetReadWriteView("GBENCH.Services.ObjectManager");
00216 
00217     m_ObjMgr = CObjectManager::GetInstance();
00218 
00219     // general genbank loader options
00220     int priority  = view.GetInt("Priority", 99);
00221 
00222     // caching options
00223     string cache_path = FnToStdString(CSysPath::ResolvePath(wxT("<home>/cache")));
00224     wxString system_cache_path = FnToWxString(cache_path);
00225     if (sizeof(void*) == 8)
00226         system_cache_path.append(wxT("64"));
00227 
00228     {{
00229         /// make sure we can write in the cache path directory
00230         if ( !wxFileName::DirExists(system_cache_path) ) {
00231             if ( !wxFileName::Mkdir(system_cache_path) ) {
00232                 LOG_POST(Error << "Error creating cache path ("
00233                     << system_cache_path.ToUTF8() << "); caching will be disabled");
00234             } else {
00235                 LOG_POST(Info << "Created cache path: " << system_cache_path.ToUTF8());
00236             }
00237         } else {
00238             // clean sqlite cash if previous launch of GBench ended with crash
00239             if (registry.GetBool("GBENCH.Application.CrashDetected", false)) {
00240                 ::wxRemoveFile(wxFileName(system_cache_path, wxT("cache_ids.db")).GetFullPath());
00241                 ::wxRemoveFile(wxFileName(system_cache_path, wxT("cache_blobs.db")).GetFullPath());
00242             }
00243         }
00244     }}
00245 
00246     int cache_age = view.GetInt("CacheAge", 5);
00247     if (cache_age == 0) {
00248         cache_age = 5;   // keep objects for 5 days (default)
00249     }
00250 
00251     /// ID resolution time in hours
00252     int id_resolution_time = view.GetInt("IdResolutionTime", 24);
00253     if (id_resolution_time > 24 * 3) {
00254         id_resolution_time = 24 * 3; //  correct the unreasonable value
00255     }
00256 
00257     wxString objmgr_config_path =
00258         CSysPath::ResolvePathExisting(wxT("<home>/gbench-objmgr.ini, ")
00259                                       wxT("<std>/etc/gbench-objmgr.ini"));
00260 
00261     if (objmgr_config_path.empty()) {
00262         CGBDataLoader::RegisterInObjectManager(*m_ObjMgr);
00263     } else {
00264         CMemoryRegistry reg;
00265         CNcbiIfstream istr(objmgr_config_path.fn_str(), ios::binary|ios::in);
00266         reg.Read(istr);
00267 
00268         // disable cache in the second instance
00269         if (!CGBenchPipe::IsOpen()) { 
00270             reg.Set("genbank", "loader_method", "id2");
00271         } else {
00272             // override variables as needed
00273             reg.Set("genbank/cache/id_cache/sqlite3", "database",
00274                     cache_path + "/cache_ids.db");
00275             reg.Set("genbank/cache/id_cache/sqlite3", "cache_age",
00276                     NStr::IntToString(id_resolution_time * (60*60)));
00277             reg.Set("genbank/cache/blob_cache/sqlite3", "database",
00278                     cache_path + "/cache_blobs.db");
00279             reg.Set("genbank/cache/blob_cache/sqlite3", "cache_age",
00280                     NStr::IntToString(cache_age * (24*60*60)));
00281         }
00282 
00283         // now, create the GenBank data loader
00284         string msg;
00285         CGBDataLoader::TRegisterLoaderInfo rinfo;
00286         try {
00287             CConfig cfg(reg);
00288             rinfo =
00289                 CGBDataLoader::RegisterInObjectManager
00290                     (*m_ObjMgr,
00291                      *cfg.GetTree(), CObjectManager::eDefault, priority);
00292         }
00293         catch (CException& e) {
00294             msg = "An error occurred while creating the GenBank connection\n(";
00295             msg += e.GetMsg();
00296             msg += ")";
00297         }
00298         catch (std::exception& e) {
00299             msg = "An error occurred while creating the GenBank connection\n(";
00300             msg += e.what();
00301             msg += ")";
00302         }
00303 
00304         if ( !msg.empty() ) {
00305             /// fall back to standard initialization
00306             LOG_POST(Error << msg);
00307             CGBDataLoader::RegisterInObjectManager(*CObjectManager::GetInstance());
00308         }
00309     }
00310 
00311     string t = NStr::DoubleToString(sw.Elapsed(), 3);
00312     LOG_POST(Info << "Registered GenBank Data Source  - " << t << " sec");
00313 
00314     m_Open = true;
00315     return true;
00316 }
00317 
00318 
00319 bool CGenBankUIDataSource::Close()
00320 {
00321     if (m_Open) {
00322 
00323         /// remove itself from menu contribution points
00324         CIRef<IExtensionRegistry> reg = CExtensionRegistry::GetInstance();
00325         reg->RemoveExtension(kAppExpCmdExtPoint, *this);
00326 
00327         try {
00328             bool revoked = m_ObjMgr->RevokeDataLoader("GBLOADER");
00329             if (!revoked) {
00330                 CDataLoader* dl = m_ObjMgr->FindDataLoader("GBLOADER");
00331                 CGBDataLoader* gbdl = dynamic_cast<CGBDataLoader*>(dl);
00332                 if (gbdl) {
00333                     gbdl->CloseCache();
00334                 }
00335             }
00336         } 
00337         catch(std::exception&)
00338         {
00339             LOG_POST(Error << "Cannot revoke genbank dataloader");
00340         }
00341 
00342         /// finally, drop the object manager
00343         CObjectManager* ptr = m_ObjMgr.Release();
00344 
00345         if (ptr) {
00346             if ( !ptr->ReferencedOnlyOnce() ) {
00347                 LOG_POST(Error << "CGenBankUIDataSource::ShutDownService(): "
00348                          "object manager still referenced");
00349             }
00350         }
00351 
00352         m_Open = false;
00353         return true;
00354     }
00355     return false;
00356 }
00357 
00358 
00359 void CGenBankUIDataSource::EditProperties()
00360 {
00361     //TODO
00362 }
00363 
00364 
00365 IUIToolManager* CGenBankUIDataSource::GetLoadManager()
00366 {
00367     // TODO may need to link the manager to this particular datasource
00368     return new CGenBankUILoadManager();
00369 }
00370 
00371 
00372 ///////////////////////////////////////////////////////////////////////////////
00373 /// CGenBankUIDataSourceType
00374 CGenBankUIDataSourceType::CGenBankUIDataSourceType()
00375 :   m_Descr("GenBank Connection", "")
00376 {
00377    wxFileArtProvider* provider = GetDefaultFileArtProvider();
00378    provider->RegisterFileAlias(ToWxString(kGB_DS_Icon),
00379                                wxT("gb_data_source.png"));
00380 }
00381 
00382 
00383 const IUIObject& CGenBankUIDataSourceType::GetDescr()
00384 {
00385     return m_Descr;
00386 }
00387 
00388 
00389 IUIDataSource* CGenBankUIDataSourceType::CreateDataSource()
00390 {
00391     return new CGenBankUIDataSource(*this);
00392 }
00393 
00394 
00395 bool CGenBankUIDataSourceType::AutoCreateDefaultDataSource()
00396 {
00397     return true; // we want to create default "GenBank" datasource
00398 }
00399 
00400 
00401 string CGenBankUIDataSourceType::GetExtensionIdentifier() const
00402 {
00403     static string ext_id("genbank_data_source_type");
00404     return ext_id;
00405 }
00406 
00407 
00408 string CGenBankUIDataSourceType::GetExtensionLabel() const
00409 {
00410     return m_Descr.GetLabel();
00411 }
00412 
00413 
00414 
00415 ///////////////////////////////////////////////////////////////////////////////
00416 /// CGenBankUILoadManager
00417 CGenBankUILoadManager::CGenBankUILoadManager()
00418 :   m_SrvLocator(NULL),
00419     m_ParentWindow(NULL),
00420     m_Descriptor(kGenBankLoadOption, ""),
00421     m_State(eInvalid),
00422     m_OptionPanel(NULL),
00423     m_ProjectSelPanel(NULL)
00424 {
00425 }
00426 
00427 
00428 void CGenBankUILoadManager::SetServiceLocator(IServiceLocator* srv_locator)
00429 {
00430     m_SrvLocator = srv_locator;
00431 }
00432 
00433 
00434 void CGenBankUILoadManager::SetParentWindow(wxWindow* parent)
00435 {
00436     m_ParentWindow = parent;
00437 }
00438 
00439 
00440 const IUIObject& CGenBankUILoadManager::GetDescriptor() const
00441 {
00442     return m_Descriptor;
00443 }
00444 
00445 
00446 void CGenBankUILoadManager::InitUI()
00447 {
00448     m_State = eSelectAcc;
00449 }
00450 
00451 
00452 void CGenBankUILoadManager::CleanUI()
00453 {
00454     m_State = eInvalid;
00455     if(m_OptionPanel)   {
00456         m_SavedInput = m_OptionPanel->GetInput();
00457         m_OptionPanel = NULL; // window is destroyed by the system
00458     }
00459     m_ProjectSelPanel = NULL;
00460 }
00461 
00462 
00463 wxPanel* CGenBankUILoadManager::GetCurrentPanel()
00464 {
00465     if(m_State == eSelectAcc)   {
00466         if(m_OptionPanel == NULL)   {
00467             m_OptionPanel = new CGenBankLoadOptionPanel(m_ParentWindow);
00468             m_OptionPanel->SetInput(m_SavedInput);
00469         }
00470         return m_OptionPanel;
00471     }  else if(m_State == eSelectProject)   {
00472         if(m_ProjectSelPanel == NULL)   {
00473             CIRef<CProjectService> srv = m_SrvLocator->GetServiceByType<CProjectService>();
00474 
00475             m_ProjectSelPanel = new CProjectSelectorPanel(m_ParentWindow);
00476             m_ProjectSelPanel->SetProjectService(srv);
00477             m_ProjectSelPanel->SetParams(m_ProjectParams);
00478             m_ProjectSelPanel->TransferDataToWindow();
00479         }
00480         return m_ProjectSelPanel;
00481     }
00482     return NULL;
00483 }
00484 
00485 
00486 bool CGenBankUILoadManager::CanDo(EAction action)
00487 {
00488     switch(m_State) {
00489     case eSelectAcc:
00490         return action == eNext;
00491     case eSelectProject:
00492         return action == eBack  ||  action == eNext;
00493     case eCompleted:
00494         return false; // nothing left to do
00495     default:
00496         _ASSERT(false);
00497         return false;
00498     }
00499 }
00500 
00501 
00502 bool CGenBankUILoadManager::IsFinalState()
00503 {
00504     return m_State == eSelectProject;
00505 }
00506 
00507 
00508 bool CGenBankUILoadManager::IsCompletedState()
00509 {
00510     return m_State == eCompleted;
00511 }
00512 
00513 
00514 bool CGenBankUILoadManager::DoTransition(EAction action, wxPanel* currPanel)
00515 {
00516     if(m_State == eSelectAcc  &&  action == eNext)    {
00517         if (m_OptionPanel->IsInputValid())  {
00518             m_State = eSelectProject;
00519             return true;
00520         }
00521         return false;
00522     } else if( m_State == eSelectProject) {
00523         if(action == eBack)  {
00524             m_State = eSelectAcc;
00525             return true;
00526         } else if(action == eNext)  {
00527             if(m_ProjectSelPanel->TransferDataFromWindow()) {
00528                 m_State = eCompleted;
00529                 return true;
00530             }
00531             return false;
00532         }
00533     }
00534     _ASSERT(false);
00535     return false;
00536 }
00537 
00538 
00539 IAppTask* CGenBankUILoadManager::GetTask()
00540 {
00541     // extract parameters from the dialog panels
00542     vector< CRef<CObject> > ids = m_OptionPanel->GetSeqIds();
00543 
00544     m_ProjectSelPanel->GetParams(m_ProjectParams);
00545     string folder_name = m_ProjectParams.m_CreateFolder ? m_ProjectParams.m_FolderName : "";
00546 
00547     // create loading Job
00548     CIRef<CProjectService> srv = m_SrvLocator->GetServiceByType<CProjectService>();
00549     CGenBankLoadingJob* job = new CGenBankLoadingJob(srv.GetPointer(), ids);
00550 
00551     m_ProjectSelPanel->GetParams(m_ProjectParams);
00552 
00553     CDataLoadingOptions& options = job->GetOptions();
00554     m_ProjectParams.ToLoadingOptions(options);
00555 
00556     // create a wrapper task
00557     CDataLoadingAppTask* task = new CDataLoadingAppTask(*job);
00558     return task;
00559 }
00560 
00561 
00562 void CGenBankUILoadManager::SetRegistryPath(const string& path)
00563 {
00564     m_RegPath = path; // store for later use
00565 }
00566 
00567 
00568 static const char* kIdsInputTag = "IdsInput";
00569 static const char* kProjectParamsTag = "ProjectParams";
00570 
00571 
00572 void CGenBankUILoadManager::SaveSettings() const
00573 {
00574     if( ! m_RegPath.empty())   {
00575         CGuiRegistry& gui_reg = CGuiRegistry::GetInstance();
00576         CGuiRegistry::TReadWriteView view = gui_reg.GetReadWriteView(m_RegPath);
00577 
00578         /// remember the selected Format (only if m_OptionPanel exists)
00579         if(m_OptionPanel)
00580             m_SavedInput = m_OptionPanel->GetInput();
00581 
00582         string encoded = NStr::URLEncode(m_SavedInput);
00583         if (encoded.size() < 10000) {
00584             // do not save large texts
00585             view.Set(kIdsInputTag, encoded);
00586         }
00587 
00588         /// save Project Panel settings
00589         m_ProjectParams.SaveSettings(view, kProjectParamsTag);
00590     }
00591 }
00592 
00593 
00594 void CGenBankUILoadManager::LoadSettings()
00595 {
00596     if( ! m_RegPath.empty())   {
00597         CGuiRegistry& gui_reg = CGuiRegistry::GetInstance();
00598         CGuiRegistry::TReadView view = gui_reg.GetReadView(m_RegPath);
00599 
00600         string encoded = view.GetString(kIdsInputTag, kEmptyStr);
00601         m_SavedInput = NStr::URLDecode(encoded);
00602 
00603         /// load Project Panel settings
00604         m_ProjectParams.LoadSettings(view, kProjectParamsTag);
00605     }
00606 }
00607 
00608 
00609 ///////////////////////////////////////////////////////////////////////////////
00610 /// CGenBankLoadingJob
00611 
00612 CGenBankLoadingJob::CGenBankLoadingJob(CProjectService* service, const IIdsVec& ids)
00613 :   CDataLoadingAppJob(service),
00614     m_Ids(ids)
00615 {
00616     CFastMutexGuard lock(m_Mutex);
00617 
00618     int n = m_Ids.size();
00619     string s = NStr::IntToString(n) + (n == 1 ? " id" : " ids");
00620     m_Descr = "Loading " + s + " from GenBank";
00621 }
00622 
00623 
00624 void CGenBankLoadingJob::x_CreateProjectItems()
00625 {
00626     /// a scope for ID validation
00627     CRef<CScope> scope(new CScope(*CObjectManager::GetInstance()));
00628     scope->AddDefaults();
00629 
00630     // iterate by ids and create items
00631     for( size_t i = 0;  i < m_Ids.size();  i++  )   {
00632         CSeq_id*  seq_id = dynamic_cast<CSeq_id*> (m_Ids[i].GetPointer());
00633         CSeq_loc* seq_loc = dynamic_cast<CSeq_loc*> (m_Ids[i].GetPointer());
00634 
00635         if (seq_id == 0 && seq_loc == 0) {
00636             LOG_POST(Error << "CGenBankLoadingJob::x_CreateProjectItems(): "
00637                     "Invalid object type to open.");
00638             continue;
00639         }
00640 
00641         // validate the ID
00642         /*CScope::TIds ids = scope->GetIds(*id);
00643         if (ids.size() == 0) {
00644             ERR_POST("CGenBankLoadingJob::x_CreateProjectItems() - " <<
00645                      "Cannot resolve id '" + id->AsFastaString() + "'");
00646         } else */{
00647             // create a Project Item
00648             CRef<CProjectItem> item(new CProjectItem());
00649             if (seq_id)
00650                 item->SetItem().SetId(*seq_id);
00651             else
00652                 item->SetObject(*seq_loc);
00653 
00654             string label = item->GetLabelByData(scope.GetPointer());
00655             item->SetLabel(label);
00656 
00657             m_Items.push_back(item);
00658         }
00659     }
00660 }
00661 
00662 
00663 END_NCBI_SCOPE
Modified on Wed May 23 13:07:36 2012 by modify_doxy.py rev. 337098