GME  13
CoreXmlFile.cpp
Go to the documentation of this file.
00001 #include "stdafx.h"
00002 #include "CommonMath.h"
00003 #include <algorithm>
00004 #include <stdio.h>
00005 #include <io.h>
00006 #include <stdlib.h>
00007 #include <direct.h>
00008 #include <fstream>
00009 #include <time.h>
00010 #include <sys/timeb.h>
00011 #include "CoreXmlFile.h"
00012 #include "CommonCollection.h"
00013 #include "SvnLoginDlg.h"
00014 #include "FilesInUseDlg.h"
00015 #include "FilesInUseDetailsDlg.h"
00016 #include "DomErrorPrinter.h"
00017 #include <xercesc/util/OutOfMemoryException.hpp>
00018 #include <xercesc/framework/LocalFileFormatTarget.hpp> 
00019 #include "HiClient.h"
00020 #include "SvnTestDlg.h"
00021 #include "FileHelp.h"
00022 #include "DirSupplier.h"
00023 #include "Transcoder.h"
00024 #include "CommitDialog.h"
00025 #include "ProgressWindow.h"
00026 #include "Mga.h"
00027 
00028 using namespace XERCES_CPP_NAMESPACE;
00029 using std::string;
00030 
00031 #include "xml_smart_ptr.h"
00032 
00033 #define DETAILS_ABOUT_XMLBACKEND 0
00034 #define RESOLVE_PTRS_2ND_ATTEMPT 0
00035 
00036 std::string g_userName;
00037 std::string g_passWord;
00038 
00039 const char* OpCodeStr[] =
00040 {
00041           "DELETED"
00042         , "MOVED"
00043         , "DERIVED"
00044         , "REFERRED"
00045         , "CONNECTED"
00046         , "TAKESPARTINCONN"
00047         , "REF_REDIRECTED"
00048         , "TOTAL"
00049 }; // keep these in sync with OpCode
00050 
00051 /*static*/ const char * OperatingOptions::m_usrConfName = "config.opt";
00052 /*static*/ const char * OperatingOptions::m_sysConfName = "project.opt";
00053 /*static*/ const char * OperatingOptions::m_sysConfDefContentsPlain = "\
00054 # comments must be prefixed by a hashmark\n\
00055 #UseCache=false\n\
00056 #AutomaticLogin=true\n\
00057 #UseAccountInfo=true\n\
00058 #account=ya\n\
00059 #phrase=\n\
00060 \n\
00061 # specifies maximum time of tolerance (in minutes) for old entries\n\
00062 # if set to 0 then a max time-to-live is not imposed on entries\n\
00063 PurgeDelayed=17.7777222\n\
00064 \n\
00065 # shows checkout status upon load\n\
00066 OnLoadShowStatus = false\n\
00067 \n\
00068 # use this option to avoid seeing the 'Keep files checked out'\n\
00069 DefCheckInOnSave = true\n\
00070 \n\
00071 # use this option to avoid seeing 'Some files need to be checked out'\n\
00072 DefCheckOutOnAction = true\n\
00073 \n\
00074 # to overwrite these settings with user-specific preferences create a file\n\
00075 # called config.opt, in your local checkout directory\n\
00076 ";
00077 
00078 /*static*/ const char * OperatingOptions::m_sysConfDefContentsSvn = "\
00079 # comments must be prefixed by a hashmark\n\
00080 #UseCache=false\n\
00081 #AutomaticLogin=true\n\
00082 #UseAccountInfo=true\n\
00083 #account=ya\n\
00084 #phrase=\n\
00085 \n\
00086 # specifies maximum time of tolerance (in minutes) for old entries\n\
00087 # if set to 0 then a max time-to-live is not imposed on entries\n\
00088 PurgeDelayed=17.7777222\n\
00089 \n\
00090 # shows checkout status upon load\n\
00091 OnLoadShowStatus = false\n\
00092 \n\
00093 # use this option to avoid seeing the 'Keep files checked out'\n\
00094 DefCheckInOnSave = true\n\
00095 \n\
00096 # use this option to avoid seeing 'Some files need to be checked out'\n\
00097 DefCheckOutOnAction = true\n\
00098 \n\
00099 # use this option to override the url or it's protocol part only thus you\n\
00100 # may select from https and svn+ssh protocols and their corresponding urls\n\
00101 #PreferredUrl = https://.... \n\
00102 #PreferredUrl = svn+ssh://... \n\
00103 \n\
00104 # Speed-up commits to the repository by aggregation.\n\
00105 UseBulkCommit = true \n\
00106 \n\
00107 # to overwrite these settings with user-specific preferences create a file\n\
00108 # called config.opt, in your local checkout directory\n\
00109 ";
00110 
00111 /*static*/ const char * HelperFiles::sessionFolderName = "session";
00112 /*static*/ const char * HelperFiles::signFileName = "sign.txt";
00113 /*static*/ const char * HelperFiles::protFileName = "list_";
00114 /*static*/ const char * HelperFiles::protFileExt  = ".txt";
00115 
00116 /*static*/ const XMLCh * ParserLiterals::Main::deleted = L"deleted";
00117 /*static*/ const XMLCh * ParserLiterals::Main::metaId  = L"MetaId";
00118 /*static*/ const XMLCh * ParserLiterals::Main::id      = L"Id";
00119 /*static*/ const XMLCh * ParserLiterals::Main::parent  = L"Parent";
00120 
00121 /*static*/ const XMLCh * ParserLiterals::Signer::users = L"users";
00122 /*static*/ const XMLCh * ParserLiterals::Signer::user  = L"user";
00123 /*static*/ const XMLCh * ParserLiterals::Signer::name  = L"name";
00124 /*static*/ const XMLCh * ParserLiterals::Signer::since = L"since";
00125 /*static*/ const XMLCh * ParserLiterals::Signer::until = L"until";
00126 
00127 /*static*/ const XMLCh * ParserLiterals::Protector::item    = L"item";
00128 /*static*/ const XMLCh * ParserLiterals::Protector::when    = L"when";
00129 /*static*/ const XMLCh * ParserLiterals::Protector::oper    = L"oper";
00130 /*static*/ const XMLCh * ParserLiterals::Protector::gd      = L"gd";
00131 /*static*/ const XMLCh * ParserLiterals::Protector::objects = L"objects";
00132 
00133 /*static*/ const XMLCh * ParserLiterals::newln = L"\n";
00134 /*static*/ const XMLCh * ParserLiterals::empty = L"";
00135 
00136 /*static*/ const char * CCoreXmlFile::m_contentConst = "contents";
00137 
00138 bool XmlObjComp::operator()( XmlObject * p1, XmlObject * p2) const
00139 {
00140         if( (!p1) && (!p2)) return false;
00141     if( !p1) return true;
00142     if( !p2) return false;
00143     return GUID_less()( p1->m_guid, p2->m_guid);
00144 }
00145 
00146 void bin2string( const unsigned char * bytes, int len, std::string& str )
00147 {
00148     char hex[3];
00149     str.clear();
00150     for( int i=0; i<len; ++i )
00151     {
00152         sprintf( hex, "%02x", bytes[i] );
00153         str += hex;
00154     }
00155 }
00156 
00157 unsigned char hexCharToInt( char ch )
00158 {
00159     if( ch>='0' && ch<='9' )
00160         return ch - '0';
00161 
00162     if( ch == 'a' || ch == 'A' )
00163         return 10;
00164     if( ch == 'b' || ch == 'B' )
00165         return 11;
00166     if( ch == 'c' || ch == 'C' )
00167         return 12;
00168     if( ch == 'd' || ch == 'D' )
00169         return 13;
00170     if( ch == 'e' || ch == 'E' )
00171         return 14;
00172     if( ch == 'f' || ch == 'F' )
00173         return 15;
00174     ASSERT(false);
00175     return 0;
00176 }
00177 
00178 void string2bin( const char * str, unsigned char * bytes )
00179 {
00180     int l = strlen(str) / 2;
00181     for( int i=0; i<l; ++i )
00182     {        
00183         unsigned char c1 = hexCharToInt( str[i*2] );
00184         unsigned char c2 = hexCharToInt( str[i*2 + 1] );
00185         //unsigned char c;
00186         //sscanf( str + i*2, "%02x", &c );      // do not work in release veriosn...
00187         bytes[i] = 16 * c1 + c2;
00188     }
00189 }
00190 
00191 void guid2str( GUID guid, std::string& str )
00192 {
00193     if( guid == GUID_NULL )
00194         str = "null";
00195     else
00196         bin2string( (unsigned char*)&guid, sizeof(GUID), str );
00197 }
00198 
00199 GUID str2guid( const char * str )
00200 {    
00201     int len = strlen(str);
00202     if( len == 0 || strcmp( str, "null" ) == 0 )
00203         return GUID_NULL;
00204     else
00205     {
00206         GUID guid;
00207         string2bin( str, (unsigned char*)&guid );
00208         return guid;
00209     }
00210 }
00211 
00212 //
00213 // obsolete since MgaGeneric.cpp does not contain any
00214 // attribTokens (like 'MGA Version' was) with space inside
00215 //
00216 //void replaceSpaceWithUnderscore( std::string& str )
00217 //{
00218 //    for( unsigned int i=0; i < str.size(); ++i )
00219 //        if( str[i] == ' ' )
00220 //            str[i] = '_';
00221 //}
00222 //
00223 //void replaceUnderscoreWithSpace( char * str )
00224 //{
00225 //    if( str == NULL )
00226 //        return;
00227 //
00228 //    int n = strlen(str);
00229 //    for( int i=0; i<n; ++i )
00230 //        if( str[i] == '_' )
00231 //            str[i] = ' ';
00232 //}
00233 
00234 
00236 // XmlAttrBase class
00238 
00239 XmlAttrBase * XmlAttrBase::create(valtype_type valtype)
00240 {
00241     ASSERT( valtype != VALTYPE_NONE );
00242 
00243     XmlAttrBase * xmlattr = NULL;
00244 
00245     switch(valtype)
00246     {
00247     case VALTYPE_LONG:
00248         xmlattr = new XmlAttrLong();
00249         break;
00250 
00251     case VALTYPE_STRING:
00252         xmlattr = new XmlAttrString();
00253         break;
00254 
00255     case VALTYPE_BINARY:
00256         xmlattr = new XmlAttrBinary();
00257         break;
00258 
00259     case VALTYPE_LOCK:
00260         xmlattr = new XmlAttrLock();
00261         break;
00262 
00263     case VALTYPE_POINTER:
00264         xmlattr = new XmlAttrPointer();
00265         break;
00266 
00267     case VALTYPE_COLLECTION:
00268         xmlattr = new XmlAttrCollection();
00269         break;
00270 
00271     case VALTYPE_REAL:
00272         xmlattr = new XmlAttrReal();
00273         break;
00274 
00275         case VALTYPE_DICT:
00276                 xmlattr = new XmlAttrDict();
00277                 break;
00278 
00279     default:
00280         HR_THROW(E_METAPROJECT);
00281     }
00282 
00283     if( xmlattr == NULL )
00284         HR_THROW(E_OUTOFMEMORY);
00285 
00286     return xmlattr;
00287 }
00288 
00289 XmlAttrBase::XmlAttrBase()
00290 { 
00291 }
00292     
00293 XmlAttrBase::~XmlAttrBase() 
00294 { 
00295 }
00296 
00297 XmlAttrDict::XmlAttrDict()
00298 {
00299         CCoreDictionaryAttributeValue *val = NULL;
00300         typedef CComObject< CCoreDictionaryAttributeValue > COMTYPE;
00301         HRESULT hr = COMTYPE::CreateInstance((COMTYPE **)&val);
00302         COMTHROW(hr);
00303         m_value = val;
00304 }
00305 
00306 void XmlAttrDict::fromVariant(VARIANT v)
00307 {
00308         ASSERT(v.vt == VT_DISPATCH);
00309         m_value = 0;
00310         v.pdispVal->QueryInterface(&m_value);
00311 }
00312 
00313 void XmlAttrDict::toVariant(VARIANT* v) const
00314 {
00315         CComVariant ret = m_value;
00316         COMTHROW(ret.Detach(v));
00317 }
00318 
00319 void XmlAttrDict::fromString(const char* str, const wchar_t* strw)
00320 {
00321         DebugBreak();
00322 }
00323 
00324 void XmlAttrDict::toString(std::string& str) const
00325 {
00326         DebugBreak();
00327 }
00328 
00329 
00331 // XmlAttrLong class
00333 
00334 XmlAttrLong::XmlAttrLong()
00335 {
00336     m_value = 0;
00337 }
00338 
00339 valtype_type XmlAttrLong::getType() const
00340 {
00341     return VALTYPE_LONG;
00342 }
00343 
00344 void XmlAttrLong::fromVariant(VARIANT p)
00345 {
00346     CopyTo(p, m_value);
00347 }
00348 
00349 void XmlAttrLong::toVariant(VARIANT *p) const
00350 {
00351     CopyTo(m_value, p);
00352 }
00353 
00354 void XmlAttrLong::fromString(const char * str, const wchar_t* strw)
00355 {
00356     if( str == NULL || strlen(str)==0 )
00357         m_value = 0;
00358     else
00359         m_value = atoi( str );
00360 }
00361 
00362 void XmlAttrLong::toString(std::string& str) const
00363 {
00364     char buf[25];
00365     sprintf( buf, "%ld", m_value );
00366     str = buf;
00367 }
00368 
00370 // XmlAttrReal class
00372 
00373 XmlAttrReal::XmlAttrReal()
00374 {
00375     m_value = 0;
00376 }
00377 
00378 valtype_type XmlAttrReal::getType() const
00379 {
00380     return VALTYPE_REAL;
00381 }
00382 
00383 void XmlAttrReal::fromVariant(VARIANT p)
00384 {
00385     CopyTo(p, m_value);
00386 }
00387 
00388 void XmlAttrReal::toVariant(VARIANT *p) const
00389 {
00390     CopyTo(m_value, p);
00391 }
00392 
00393 void XmlAttrReal::fromString(const char * str, const wchar_t* strw)
00394 {
00395     if( str == NULL || strlen(str)==0 )
00396         m_value = 0;
00397     else
00398         {
00399                 if (!ParseSpecialDouble(strw, m_value))
00400                         m_value = atof( str );
00401         }
00402 }
00403 
00404 void XmlAttrReal::toString(std::string& str) const
00405 {
00406     char buf[100];
00407     sprintf(buf, "%.17g", m_value);
00408     str = buf;
00409 }
00410 
00412 // XmlAttrString class
00414 
00415 valtype_type XmlAttrString::getType() const
00416 {
00417     return VALTYPE_STRING;
00418 }
00419 
00420 void XmlAttrString::fromVariant(VARIANT p)
00421 {
00422     CopyTo(p, m_value);
00423 }
00424 
00425 void XmlAttrString::toVariant(VARIANT *p) const
00426 {
00427     CopyTo(m_value, p);
00428 }
00429 
00430 void XmlAttrString::fromString(const char * str, const wchar_t* strw)
00431 {
00432     if( str == NULL )
00433         m_value = "";
00434     else
00435         m_value = str;
00436 }
00437 
00438 void XmlAttrString::toString(std::string& str) const
00439 {
00440     str = m_value;
00441 }
00442 
00444 // XmlAttrBinary class
00446 
00447 valtype_type XmlAttrBinary::getType() const
00448 {
00449     return VALTYPE_BINARY;
00450 }
00451 
00452 void XmlAttrBinary::fromVariant(VARIANT p)
00453 {
00454     CopyTo(p, m_value);
00455 }
00456 
00457 void XmlAttrBinary::toVariant(VARIANT *p) const
00458 {
00459     CopyTo(m_value, p);
00460 }
00461 
00462 void XmlAttrBinary::fromString(const char * str, const wchar_t* strw)
00463 {   
00464     if( str == NULL || strlen(str) == 0 )
00465         m_value.clear();
00466     else
00467     {
00468         int             len  = strlen(str)/2;
00469         unsigned char * buff = new unsigned char[len];
00470         string2bin( str, buff );
00471         m_value.resize( len );
00472         for( int i=0; i<len; ++i )
00473             m_value [i] = buff[i];
00474         delete [] buff;
00475     }
00476 }
00477 
00478 void XmlAttrBinary::toString(std::string& str) const
00479 {
00480         if (m_value.empty()) {
00481                 str.clear();
00482         }
00483         else {
00484                 bin2string( &m_value[0], m_value.size(), str );
00485         }
00486 }
00487 
00489 // XmlAttrLock class
00491 
00492 XmlAttrLock::XmlAttrLock()
00493 {
00494     m_value = LOCKING_NONE;
00495 }
00496 
00497 valtype_type XmlAttrLock::getType() const
00498 {
00499     return VALTYPE_LOCK;
00500 }
00501 
00502 void XmlAttrLock::fromVariant(VARIANT p)
00503 {
00504     CopyTo(p, m_value);
00505 }
00506 
00507 void XmlAttrLock::toVariant(VARIANT *p) const
00508 {
00509     CopyTo(m_value, p);
00510 }
00511 
00512 void XmlAttrLock::fromString(const char * str, const wchar_t* strw)
00513 {   
00514     if( str == NULL || strlen(str) == 0 )
00515         m_value = LOCKING_NONE;
00516     else
00517         m_value = atoi( str );
00518 }
00519 
00520 void XmlAttrLock::toString(std::string& str) const
00521 {
00522     char buf[25];
00523     sprintf( buf, "%d", m_value );
00524     str = buf;
00525 }
00526 
00528 // XmlAttrPointer class
00530 
00531 XmlAttrPointer::XmlAttrPointer()
00532 {
00533     m_parent = NULL;
00534 }
00535 
00536 valtype_type XmlAttrPointer::getType() const
00537 {
00538     return VALTYPE_POINTER;
00539 }
00540 
00542 // XmlAttrCollection class
00544 
00545 valtype_type XmlAttrCollection::getType() const
00546 {
00547     return VALTYPE_COLLECTION;
00548 }
00549 
00551 // XmlObject class
00553 
00554 XmlObject::XmlObject(ICoreMetaObject *metaobject, bool createAllAttributes )
00555 {
00556     m_deleted         = false;
00557     m_modified        = false;
00558     m_loaded          = createAllAttributes;
00559     
00560     COMTHROW( metaobject->get_MetaID( &m_metaid ) );
00561     if( m_loaded )
00562         createAttributes(metaobject,ATTR_ALL);
00563     else
00564         createAttributes(metaobject,ATTR_PRIMARY);
00565     CoCreateGuid(&m_guid);
00566 }
00567 
00568 XmlObject::~XmlObject()
00569 {
00570     for( AttribMapIter it = m_attributes.begin(); it != m_attributes.end(); ++it )
00571         delete it->second;
00572 }
00573 
00574 bool XmlObject::isContainer()
00575 {
00576     return( m_metaid==METAID_ROOT || m_metaid==DTID_MODEL || m_metaid==DTID_FOLDER );
00577 }
00578 
00579 
00580 void XmlObject::createAttributes(ICoreMetaObject *metaobject, int attrSet )
00581 {
00582     // TODO: memoize
00583 
00584     ASSERT( metaobject != NULL );
00585     ASSERT( attrSet>=ATTR_PRIMARY && attrSet<=ATTR_ALL );
00586 
00587     CComObjPtr<ICoreMetaAttributes> metaattributes;
00588     COMTHROW( metaobject->get_Attributes(PutOut(metaattributes)) );
00589     ASSERT( metaattributes != NULL );
00590 
00591     typedef std::vector< CComObjPtr<ICoreMetaAttribute> > metaattributelist_type;
00592     metaattributelist_type metaattributelist;
00593     GetAll<ICoreMetaAttributes, ICoreMetaAttribute>(metaattributes, metaattributelist);
00594 
00595     for( metaattributelist_type::iterator i=metaattributelist.begin(); i!=metaattributelist.end(); ++i )
00596     {
00597         valtype_type valtype;
00598         COMTHROW( (*i)->get_ValueType(&valtype) );
00599         if( attrSet==ATTR_ALL ||
00600            (attrSet==ATTR_PRIMARY && (valtype==VALTYPE_POINTER || valtype==VALTYPE_COLLECTION || valtype==VALTYPE_LOCK)) ||
00601            (attrSet==ATTR_SECONDARY && (valtype!=VALTYPE_LOCK && valtype!=VALTYPE_POINTER && valtype!=VALTYPE_COLLECTION)))
00602         {
00603             attrid_type attrId = ATTRID_NONE;
00604             COMTHROW( (*i)->get_AttrID(&attrId) );
00605             ASSERT( attrId != ATTRID_NONE );
00606                         // TODO: remove indirection from AttribMap::value_type::value (too many mallocs)
00607             m_attributes.insert( AttribMap::value_type(attrId,XmlAttrBase::create(valtype)) );
00608         }
00609     }
00610 }
00611 
00612 void XmlObject::deleteSecondaryAttribs()
00613 {
00614     for( AttribMapIter it = m_attributes.begin(); it != m_attributes.end(); )
00615     {
00616         valtype_type type =it->second->getType();
00617         if( type!=VALTYPE_LOCK && type!=VALTYPE_POINTER && type!=VALTYPE_COLLECTION )
00618         {
00619             delete it->second;
00620             m_attributes.erase(it++);
00621         }
00622                 else
00623                         ++it;
00624 
00625     }
00626 }
00627 
00628 
00630 // CCoreXmlFile class
00632 
00633 CCoreXmlFile::CCoreXmlFile()
00634         : m_console( true)
00635 {
00636         m_opened                = false;
00637         m_inTransaction         = false;
00638         m_modified              = false;
00639         m_metaAttributeId       = ATTRID_NONE;
00640         m_metaAttributeValType  = VALTYPE_NONE;
00641         m_openedObject          = NULL;
00642         m_sourceControl         = SC_NONE;
00643         m_savedOnce             = false;
00644         m_hashFileNames         = false;
00645         m_hashInfoFound         = false;
00646         m_hashVal               = -1;
00647         m_needsSessionRefresh   = true;
00648         fillParentMap();
00649 
00650         XMLPlatformUtils::Initialize();
00651 }
00652 
00653 CCoreXmlFile::~CCoreXmlFile()
00654 {
00655         clearAll();
00656         XMLPlatformUtils::Terminate();
00657 }
00658 
00659 STDMETHODIMP CCoreXmlFile::get_MetaProject(ICoreMetaProject **p)
00660 {
00661         CHECK_OUT(p);
00662         CopyTo(m_metaProject, p);
00663         return S_OK;
00664 }
00665 
00666 STDMETHODIMP CCoreXmlFile::put_MetaProject(ICoreMetaProject *p)
00667 {
00668         COMTRY
00669         {
00670                 closeMetaProject();
00671                 m_metaProject = p;
00672         }
00673         COMCATCH( closeMetaProject() )
00674 }
00675 
00676 STDMETHODIMP CCoreXmlFile::get_MetaObject(ICoreMetaObject **p)
00677 {
00678         CHECK_OUT(p);
00679         CopyTo(m_metaObject, p);
00680         return S_OK;
00681 }
00682 
00683 STDMETHODIMP CCoreXmlFile::put_MetaObject(ICoreMetaObject *p)
00684 {
00685         if( m_metaProject == NULL )
00686                 COMRETURN(E_INVALID_USAGE);
00687 
00688         if( m_metaObject == p )
00689                 return S_OK;
00690 
00691         COMTRY
00692         {
00693                 if( p != NULL )
00694                 {
00695                         // check the metaproject of the object, it must be the same as the metaproject
00696                         // of the storage
00697                         CComObjPtr<ICoreMetaProject> t;
00698                         COMTHROW( p->get_Project(PutOut(t)) );
00699                         if( !IsEqualObject(m_metaProject, t) )
00700                                 HR_THROW(E_SAMEPROJECT);
00701                 }
00702 
00703                 closeMetaObject();
00704                 m_metaObject = p;
00705                 if( m_metaObject != NULL )
00706                         openMetaObject();
00707         }
00708         COMCATCH( closeMetaObject() )
00709 }
00710 
00711 STDMETHODIMP CCoreXmlFile::get_MetaID(metaid_type *p)
00712 {
00713         CHECK_OUT(p);
00714         *p = m_metaObjectId;
00715         return S_OK;
00716 }
00717 
00718 STDMETHODIMP CCoreXmlFile::put_MetaID(metaid_type metaid)
00719 {
00720         if( m_metaProject == NULL )
00721                 COMRETURN(E_INVALID_USAGE);
00722 
00723         COMTRY
00724         {
00725                 if( metaid != METAID_NONE )
00726                 {
00727                         CComObjPtr<ICoreMetaObject> p;
00728                         COMTHROW( m_metaProject->get_Object(metaid, PutOut(p)) );
00729                         ASSERT( p != NULL );
00730 
00731                         if( m_metaObject != p )
00732                         {
00733                                 closeMetaObject();
00734                                 MoveTo(p, m_metaObject);
00735                                 openMetaObject();
00736                         }
00737                 }
00738                 else
00739                         closeMetaObject();
00740         }
00741         COMCATCH( closeMetaObject() )
00742 }
00743 
00744 STDMETHODIMP CCoreXmlFile::get_MetaAttribute(ICoreMetaAttribute **p)
00745 {
00746         CHECK_OUT(p);
00747         CopyTo(m_metaAttribute, p);
00748         return S_OK;
00749 }
00750 
00751 STDMETHODIMP CCoreXmlFile::put_MetaAttribute(ICoreMetaAttribute *p)
00752 {
00753         if( m_metaObject == NULL )
00754                 COMRETURN(E_INVALID_USAGE);
00755         ASSERT( m_metaProject != NULL );
00756 
00757         if( m_metaAttribute == p )
00758                 return S_OK;
00759 
00760         COMTRY
00761         {
00762                 if( m_metaAttribute != NULL )
00763                 {
00764                         // the metaobject of the given metaattribute must be the same as the metaobject of 
00765                         // the storage
00766                         CComObjPtr<ICoreMetaObject> t;
00767                         COMTHROW( m_metaAttribute->get_Object(PutOut(t)) );
00768                         if( !IsEqualObject(m_metaObject, t) )
00769                         {
00770                                 m_metaAttribute = NULL;
00771                                 return E_INVALIDARG;
00772                         }
00773                 }
00774 
00775                 closeMetaAttribute();
00776                 m_metaAttribute = p;
00777                 if( m_metaAttribute != NULL )
00778                         openMetaAttribute();
00779         }
00780         COMCATCH( closeMetaAttribute() )
00781 }
00782 
00783 STDMETHODIMP CCoreXmlFile::get_AttrID(attrid_type *p)
00784 {
00785         CHECK_OUT(p);
00786         if( m_metaAttribute )
00787                 return m_metaAttribute->get_AttrID(p);
00788         *p = 0;
00789         return S_OK;
00790 }
00791 
00792 STDMETHODIMP CCoreXmlFile::put_AttrID(attrid_type attrid)
00793 {
00794         if( m_metaObject == NULL )
00795                 COMRETURN(E_INVALID_USAGE);
00796         ASSERT( m_metaProject != NULL );
00797 
00798         COMTRY
00799         {
00800                 if( attrid != ATTRID_NONE )
00801                 {
00802                         CComObjPtr<ICoreMetaAttribute> p;
00803                         COMTHROW( m_metaObject->get_Attribute(attrid, PutOut(p)) );
00804                         ASSERT( p != NULL );
00805 
00806                         if( m_metaAttribute != p )
00807                         {
00808                                 closeMetaAttribute();
00809                                 m_metaAttribute = p;
00810                                 openMetaAttribute();
00811                         }
00812                 }
00813                 else
00814                         closeMetaAttribute();
00815         }
00816         COMCATCH( closeMetaAttribute() )
00817 }
00818 
00819 STDMETHODIMP CCoreXmlFile::get_AttributeValue(VARIANT *p)
00820 {
00821         CHECK_OUT(p);
00822 
00823         if( m_openedObject == NULL || !m_inTransaction )
00824                 COMRETURN(E_INVALID_USAGE);
00825 
00826         COMTRY
00827         {
00828                 AttribMapIter it = m_openedObject->m_attributes.find( m_metaAttributeId );
00829                 if( m_metaAttributeValType == VALTYPE_POINTER )
00830                 {
00831                         getPointer( (XmlAttrPointer*)it->second, p );
00832                 }
00833                 else if( m_metaAttributeValType == VALTYPE_COLLECTION )
00834                 {
00835                         getCollection( (XmlAttrCollection*)it->second, p );
00836                 }
00837                 else if( m_metaAttributeValType == VALTYPE_LOCK )
00838                 {
00839                         it->second->toVariant(p);
00840                 }
00841                 else
00842                 {
00843                         if( !m_openedObject->m_loaded )
00844                         {
00845                                 fullReadContainer(getContainer(m_openedObject));
00846                                 it = m_openedObject->m_attributes.find( m_metaAttributeId );
00847 
00848                                 // if the attribute was not found -> throw
00849                                 if( m_openedObject->m_attributes.end() == it)
00850                                         COMTHROW(E_INVALID_USAGE);
00851                         }
00852 
00853                         it->second->toVariant(p);
00854                 }
00855         }
00856         COMCATCH(;)
00857 }
00858 
00859 void CCoreXmlFile::applySmallChange( XmlObjSet& p_conts)
00860 {
00861         for( XmlObjSet::const_iterator it = p_conts.begin(); it != p_conts.end(); ++it)
00862         {
00863                 XmlObject * p = *it;
00864                 AttribMapIter ait = p->m_attributes.find( ATTRID_RELID);
00865                 if( ait != p->m_attributes.end())
00866                 {
00867                         long relid = ((XmlAttrLong*)(ait->second))->m_value;
00868                         ((XmlAttrLong*)(ait->second))->m_value = relid + 1;
00869                         long reljd = ((XmlAttrLong*)(ait->second))->m_value;
00870                         ASSERT( relid + 1 == reljd);
00871                 }
00872         }
00873 }
00874 
00875 bool CCoreXmlFile::specialUserInquiryHandled( VARIANT p)
00876 {
00877         static const std::string magic_str = "UpdateSourceControlInfo";
00878         static const std::string magi2_str = "WhoControlsThisObj";
00879         static const std::string magi3_str = "ShowActiveUsers";
00880 
00881         bool ret = false;
00882 
00883         string str;
00884         CopyTo(p, str);
00885 
00886         bool b1 = str.find( magic_str) == 0;
00887         bool b2 = str.find( magi2_str) == 0;
00888         bool b3 = str.find( magi3_str) == 0;
00889         if( b1 || b2 || b3)
00890         {
00891                 ret = true;
00892 
00893                 if( b1 || b2)
00894                 {
00895                         //DOMBuilder * parser = NULL;
00896                         //DOMImplementationLS * domimpl = DOMImplementationRegistry::getDOMImplementation( XMLString::transcode("XML 1.0"));//NULL
00897                         //ASSERT( domimpl != NULL );
00898 
00899                         //parser = domimpl->createDOMBuilder( DOMImplementationLS::MODE_SYNCHRONOUS, NULL );
00900                         //ASSERT( parser != NULL );
00901 
00902                         //XERCES_CPP_NAMESPACE::DOMDocument * doc = parser->parseURI( "F:\\t\\at\\an\\tesztam2\\f7bed9e2cbf62a418ec4e0df0ffe18a5.1xml");
00903                         //if( 0 == doc->getDocumentElement())
00904                         //{
00905                         //      DOMNodeList* list = doc->getChildNodes();
00906 
00907                         //      int c = (int) list->getLength();
00908                         //      for( int i = c-1; i >= 0 ; --i)
00909                         //      {
00910                         //              DOMNode * node = list->item(i);
00911                         //              if( node->getNodeType() == DOMNode::NodeType::TEXT_NODE)
00912                         //              {
00913                         //                      DOMText * txt  = (DOMText*) node;
00914                         //                      const XMLCh* p = txt->getData();
00915                         //              }
00916                         //              if( node->getNodeType() == DOMNode::ELEMENT_NODE)
00917                         //              {
00918                         //                      DOMElement* elem = (DOMElement*)node;
00919                         //                      const XMLCh* tn = elem->getTagName();
00920                         //              }
00921                         //      }
00922                         //}
00923 
00924                         std::string id = str.substr( b1?magic_str.length():magi2_str.length());
00925                         if( id != "")
00926                         {
00927                                 // must be in sync with the current id creation (format)
00928                                 metaobjidpair_type objid;
00929                                 sscanf( id.c_str(), "id-%04lx-%08lx", &objid.metaid, &objid.objid);
00930 
00931                                 XmlObject * obj = objectFromObjId( objid);
00932                                 if( obj && obj->isContainer()) b1?updateSourceControlInfo( obj) : whoControlsThis( obj);
00933                         }
00934                         else b1?updateSourceControlInfo():whoControlsThis(); // whole project
00935                 }
00936                 else if( b3)
00937                 {
00938                         std::string res( "The following users worked on the project since your log-in:\r\n");
00939                         std::vector< LoggedIn> lus = allusers();
00940                         for( std::vector< LoggedIn>::iterator it = lus.begin(), en = lus.end(); it != en; ++it)
00941                         {
00942                                 res += std::string(1, '\t') + std::string(1, it->m_fl) + "\t\"" + it->m_nm + "\"\r\n";
00943                         }
00944 
00945                         if( lus.size() > 0)
00946                         {
00947                                 //AfxMessageBox( res.c_str(), MB_ICONINFORMATION);
00948                                 CSvnTestDlg d;
00949                                 res.append( "Note: 'I' means Inactive, 'A' means Active users.");
00950                                 d.setContent( res.c_str());
00951                                 d.DoModal();
00952                         }
00953                         else
00954                                 AfxMessageBox( "No user login info found!");
00955                 }
00956         }
00957 
00958         return ret;
00959 }
00960 
00961 STDMETHODIMP CCoreXmlFile::put_AttributeValue(VARIANT p)
00962 {
00963         AFX_MANAGE_STATE(AfxGetStaticModuleState());
00964 
00965         if( m_openedObject == NULL || !m_inTransaction )
00966                 COMRETURN(E_INVALID_USAGE);
00967 
00968         // root's guid changes evry time the project is modified we should igonre it
00969         // it casues some meta interpreter problems!
00970         if( m_metaAttributeId == ATTRID_GUID )
00971                 return S_OK;
00972 
00973         if( 0&&m_metaAttributeId == ATTRID_LASTRELID) // never mind ATTRID_LASTRELID, thus
00974                 return S_OK;                           // adding a child, won't affect parents
00975 
00976         // setting root's ATTRID_MDATE to "updatesourcecontrolinfo" means we have to update sourcecontrolinfo
00977         // and ignore attribute setting
00978         if( m_metaAttributeId == ATTRID_MDATE )
00979         {
00980                 if( specialUserInquiryHandled( p))
00981                         return S_OK;
00982         }
00983 
00984 #ifdef _DEBUG
00985 #if(DETAILS_ABOUT_XMLBACKEND)
00986         time_t time1, time2;
00987         struct tm *tm1, *tm2;
00988         time( &time1); tm1 = localtime( &time1);
00989         //sendMsg( std::string( "CommitBegin ") + asctime( tm1 ), MSG_INFO);
00990 #endif
00991 #endif
00992 
00993         // ignore UpdateSourceControlInfo regnode
00994         if( m_openedObject && m_metaAttributeId == ATTRID_FILESTATUS)
00995                 return S_OK;
00996 
00997         // TODO: return with specific error code
00998         //if( m_openedObject->m_readOnly )
00999         //  COMRETURN(E_INVALID_USAGE);
01000 #ifdef _DEBUG
01001 #if(DETAILS_ABOUT_XMLBACKEND)
01002         std::string iiid, mylo;
01003         std::string gd; guid2str( m_openedObject->m_guid, gd);
01004         char* kind[] = {"100", "M", "A", "R", "C", "S", "F", "connrole", "connseg"
01005                 , "109", "110", "StrAttr", "IntAttr", "FloatAttr", "BoolAttr", "RrefAttr", "116", "117", "118", "119"
01006                 , "Constraint", "RegNode", "SetNode", "123", "124", "125", "126", "127" };
01007 
01008         char buff[5];sprintf( buff, "%i", m_metaAttributeId);
01009 
01010         char *  mavt[] = { "VALTYPE_None", "VALTYPE_COLL", "VALTYPE_Pointer", "VALTYPE_LOCK", "VALTYPE_LONG"
01011                 , "VALTYPE_STR", "VALTYPE_BIN", "VALTYPE_REAL", "VALTYPE_8", "VALTYPE_9", "VALTYPE_10", "VALTYPE_11"
01012                 , "VALTYPE_12", "VALTYPE_13", "VALTYPE_14"};
01013 
01014         if( m_userOpts.m_createLog) iiid = gd + " {" + ((m_openedObject->m_metaid<=100)?"Root": kind[m_openedObject->m_metaid-100]) + "} [ " + buff + " ](" + mavt[m_metaAttributeValType] + ")";
01015 
01016         if( m_userOpts.m_createLog) mylo += iiid;
01017 #endif
01018 #endif
01019 
01020         COMTRY
01021         {
01022                 AttribMapIter it = m_openedObject->m_attributes.find( m_metaAttributeId );
01023                 if( m_metaAttributeValType == VALTYPE_POINTER )
01024                 {   
01025                         if( m_metaAttributeId == ATTRID_PARENT)
01026                         {
01027                                 // --
01028                                 // this could be used to fine tune ATTRID_LASTRELID
01029                                 // m_userOpts.m_doModelParentLock
01030                                 // --
01031                                 // delete handled separately in cases of models and non-model fcos
01032                                 // if a, r, s, c is deleted the parent model is checked out
01033                                 // among these a, r, s can be ports
01034                                 // models are tricky because upon deleting a model its parent
01035                                 // does not get checked out
01036                                 // in addition a model also can be a port
01037                                 metaid_type &mt = m_openedObject->m_metaid;
01038                                 XmlObject * grand_parent = 0; // will be used only if it is determined to be a port
01039                                 XmlObject *       parent = 0; // will be used only if it is determined to be a port
01040 
01041                                 // simply get its father:
01042                                 AttribMapIter it3 = m_openedObject->m_attributes.find( ATTRID_PARENT );
01043                                 ASSERT( it3 != m_openedObject->m_attributes.end() );
01044                                 XmlObject * cont = parent = ((XmlAttrPointer*)(it3->second))->m_parent;
01045 
01046                                 // is removal (delete), is movement or is childbirth :) ?
01047                                 metaobjidpair_type idpair;CopyTo(p, idpair);
01048                                 bool is_removal = idpair.metaid == 0 && idpair.objid == 0; // VARIANT p is 0 => removal
01049                                 bool is_movemnt = !is_removal && parent != 0;
01050 
01051                                 if( is_removal || is_movemnt) // check if latent changes prevent this operation from happening
01052                                 {
01053                                         std::string scapegoat;
01054                                         if( findOnProtectedLists( m_openedObject->m_guid, scapegoat))
01055                                         {
01056                                                 std::string msg = std::string( "Element ") + makelink( m_openedObject) +  " found on a protected list of user \"" + scapegoat + "\" thus it can't be moved/deleted!";
01057                                                 sendMsg( msg, MSG_ERROR);
01058                                                 AfxMessageBox( msg.c_str(), MB_ICONSTOP);
01059                                                 throw hresult_exception(E_FAIL);
01060                                         }
01061                                         protect( m_openedObject, is_removal?ELEM_DELETED:ELEM_MOVED);
01062 
01063                                         ASSERT( m_userOpts.m_doConnEndPointLock); // currently we assume this especially
01064                                         ASSERT( m_userOpts.m_doConnSegmentLock);  // by not dealing too much with ports
01065 
01066 #ifdef _DEBUG
01067 #if(DETAILS_ABOUT_XMLBACKEND)
01068                                         if( m_userOpts.m_createLog) {std::string nn,mm; parent->m_attributes.find( ATTRID_NAME )->second->toString( nn);
01069                                         m_openedObject->m_attributes.find(ATTRID_NAME)->second->toString(mm);
01070                                         mylo += (is_removal?" <br>-deleting ":" <br>-moving ") + mm + " in " + nn + "- <br>" ;}
01071 #endif
01072 #endif
01073 
01074                                 }
01075 
01076                                 if( is_removal) {
01077                                         m_deldObjs.push_back( m_openedObject);
01078                                         if( m_openedObject->m_metaid== DTID_REFERENCE) {
01079                                                 //setPointer( m_openedObject, ATTRID_REFERENCE, 0);
01080                                         }
01081                                 }
01082 
01083                                 // when inserting Atoms, Refs, Sets in a archetype model, this gets down
01084                                 // to deriveds too, so they will be checked out too.
01085                                 // if models are inserted into archetype models, the subtypes of the archetype 
01086                                 // do not necessarily change. That's why we need to deal with these.
01087                                 // LAST_RELID modification is done only on the archetype parent
01088                                 // if this has been derived to SParent, and SSParent, ...
01089                                 // upon inserting a model into Parent LAST_RELID changes are not performed
01090                                 // on SParent, SSParent, because the newly inserted element has different relid (>RELIDSPACE)
01091                                 // so we need to make sure we slightly alter the subtyped parents too
01092                                 if( !is_removal && !is_movemnt && m_openedObject->m_metaid == DTID_MODEL) // a model is inserted
01093                                 {
01094                                         XmlObjSet derd_from_cont;
01095                                         if( cont)
01096                                                 getDeriveds( cont, derd_from_cont); // or from its father
01097                                         else
01098                                                 getDeriveds( objectFromObjId( idpair), derd_from_cont);  
01099                                         if( !derd_from_cont.empty())
01100                                                 applySmallChange( derd_from_cont);
01101                                         for( XmlObjSet::const_iterator it = derd_from_cont.begin(); it != derd_from_cont.end(); ++it)
01102                                                 if( m_modifiedObjects.end() == m_modifiedObjects.find( *it)) // not found
01103                                                         m_modifiedObjects.insert( *it);
01104 
01105                                 }
01106                         }
01107                         //
01108                         // we have alternatives as follows regarding consistency maintanance:
01109                         // 1. We could require any NewTarget object (where the reference will point)
01110                         //    to be neither locked, neither latent changed (aka modified already (by other users))
01111                         //    thus we could be sure, that NewTarget version we see is still the latest one.
01112                         //    BeforeSave: NewTarget has been added as "REFERRED" to the protectList
01113                         //                so it is not possible for others to delete it anymore.
01114                         //                NewTarget is not necessarily checked out (only if we decide so)
01115                         //                Reference will change, so it is needed to be checked out, thus locked.
01116                         //    AfterSave : Reference changed, checked back, for others became a latent change.
01117                         //                NewTarget will remain unchanged, (even if we checked it out, upon checkin
01118                         //                the same file contents are saved back, no new version is created.
01119                         //    No deletion/removal can happen until this protection goes away from the list
01120                         //    Pro:  - upon redirection no complex operations take place
01121                         //    Con:  - NewTarget is required to be untouched by others
01122                         // 2. We could check upon any redirection the state of the protectionlists,
01123                         //    to see if NewTarget was deleted. If was moved elsewhere, that is not too relevant.
01124                         //    BeforeSave: NewTarget has been added as "REFERRED" to the protectionlist, thus
01125                         //                preventing later deletion by others.
01126                         //                Reference will change, so it is needed to be checked out, thus locked.
01127                         //                NewTarget would not be changed, its state is not required to be anything
01128                         //                special.
01129                         //    AfterSave:  Reference changed, checked back, for others became a latent change.
01130                         //                NewTarget will remain unchanged, (unless something special in case it can be performed)
01131                         //    No deletion can happen on NewTarget, because it has been referred, so its on the protection list.
01132                         //    Pro: - NewTarget is not necessarily required to be available to us when referred, allows for better
01133                         //           parralel work of the team.
01134                         //    Con: - checking all users protection lists upon for REFERRED event could be slow
01135                         //         - additional checks might be required to ensure that NewTarget's port children
01136                         //           are not deleted by other users, while we would connect them through Reference's 
01137                         //           reference ports, this might lead to inconsistent connections (src/dst could be non-existent)
01138                         //  Let us implement #1 with the NewTarget checked out option, to make sure we see the latest version of it.
01139                         //  Extension later: if our NewTarget is not the latest (we can't modify it), then we browse the 
01140                         //                   protection list to make sure it was NOT deleted, and only then allow referral to it.
01141                         //                   When the paradigm allows references to point to references, and if NewTarget is also
01142                         //                   a reference, we must make sure, that it hasn't been redirected either.
01143                         //  m_userOpts.m_doRefTargetLock=true must be used in this case
01144                         if( m_metaAttributeId == ATTRID_REFERENCE) // 505
01145                         {
01146                                 // redirection needs to be taken care of !!!
01147                                 // it affects connections, going through these refs
01148                                 metaid_type &mt = m_openedObject->m_metaid;
01149 
01150                                 // simply get its target, if filled
01151                                 AttribMapIter it3 = m_openedObject->m_attributes.find( ATTRID_REFERENCE );
01152                                 ASSERT( it3 != m_openedObject->m_attributes.end() );
01153                                 XmlObject * old_target = ((XmlAttrPointer*)(it3->second))->m_parent;
01154 
01155                                 // is removal (delete), is movement or is childbirth :) ?
01156                                 //metaobjidpair_type idpair;CopyTo(p, idpair);
01157                                 //bool is_removal = idpair.metaid == 0 && idpair.objid == 0; // VARIANT p is 0 => removal
01158                                 bool is_redirection = old_target != 0; // clear is also redirection
01159                                 if( is_redirection)
01160                                 {
01161                                         protect( m_openedObject, ELEM_REF_REDIRECTED); // might be useful for connections
01162 
01163                                         // commented on Sept.3.
01164                                         //XmlObjSet to_be_checked_out_containers;
01165                                         //XmlObjSet fdfdf;
01166                                         // 
01167                                         // find all references to this reference
01168                                         // to prevent breaking connections which
01169                                         //fdfdf.insert( m_openedObject);
01170                                         // upon a reference delete first the ATTRID_PARENT = 0 comes, later comes the
01171                                         // ATTRID_REFERENCE = 0, so this might confuse getMyDepObjConts, it will
01172                                         // find its container be 0
01173                                         if( std::find( m_deldObjs.begin(), m_deldObjs.end(), m_openedObject) != m_deldObjs.end()) // found
01174                                         {
01175                                                 int l = 0;
01176                                                 ++l;
01177                                         }
01178                                         //getMyDepObjConts( fdfdf, to_be_checked_out_containers, true);
01179                                         //m_modifiedObjects.insert( to_be_checked_out_containers.begin(), to_be_checked_out_containers.end());
01180                                 }
01181 
01182                                 metaobjidpair_type idpair;CopyTo(p, idpair);
01183                                 XmlObject * new_target_obj = objectFromObjId( idpair);
01184 
01185                                 if( new_target_obj) protect( new_target_obj, ELEM_REFERRED);
01186 
01187                                 ASSERT( m_userOpts.m_doRefTargetLock);// currently we assume this
01188 
01189                                 if( m_userOpts.m_doRefTargetLock)
01190                                 {
01191                                         // new target also to be locked in this case (is it a model/or nonmodel?)
01192                                         if( new_target_obj) m_modifiedObjects.insert( new_target_obj);
01193                                 }
01194                         }
01195                         // DERIVATION
01196                         // Let B be a base container, derived into D. B has C1 and C2 children. C2 has
01197                         // C3 and C4 as children. B's parent is A.
01198                         // When B is derived locks are required for B, C1, C2, C3, C4.
01199                         // Small modification is made to all these objects (this will prevent
01200                         // further standalone subtyping of Cx-s)
01201                         // A's standalone subtyping will be prevented by the modified inner objects (B, Cx-s)
01202                         // Before Save: objects are locked
01203                         // After  Save: objects become RO (read-only)
01204                         // After reopen: objects are already basetypes of D, DC1 (secondary derived peer of C1)
01205                         // DC2, ..., DC4, thus conflicts are prevented by their presence.
01206                         // Q: What happens if after reopen a user deleted D and derives A?
01207                         if( m_metaAttributeId == ATTRID_DERIVED) // 510, 517?
01208                         {
01209                                 metaobjidpair_type idpair;CopyTo(p, idpair);
01210                                 bool is_derivation = idpair.metaid != 0 && idpair.objid != 0; // VARIANT p is 0 => detach
01211                                 XmlObject * base_obj = objectFromObjId( idpair);
01212                                 if( is_derivation && base_obj != 0)
01213                                 {
01214                                         AttribMapIter it3 = m_openedObject->m_attributes.find( ATTRID_RELID );
01215                                         ASSERT( it3 != m_openedObject->m_attributes.end() );
01216                                         long relid = ((XmlAttrLong*)(it3->second))->m_value;
01217                                         bool secondary_deriv = relid > RELID_BASE_MAX;
01218 
01219 #ifdef _DEBUG
01220 #if(DETAILS_ABOUT_XMLBACKEND)
01221                                         if( m_userOpts.m_createLog) {
01222                                                 if( relid > RELID_BASE_MAX)
01223                                                         mylo += " <br>- !!!! secondary derivation took place !!!! - <br>" ;
01224                                                 else
01225                                                         mylo += " <br>- !!!! primary derivation took place !!!! - <br>" ;
01226                                         }
01227 #endif
01228 #endif
01229                                         if( !secondary_deriv)
01230                                         {
01231                                                 //
01232                                                 // m_userOpts.m_doBaseTypeLock // this could be used too
01233                                                 //
01234                                                 // try to lock the hierarchy of base_obj to prevent concurrent derivations
01235                                                 // of objects on different levels: e.g. parent derd by userA, child derd by userB
01236                                                 XmlObjSet checkOutBases;
01237                                                 XmlObjSet fdfdf;
01238                                                 XmlObjSet to_be_checked_out_containers;
01239                                                 // find all dependents of parent (? and m_openedObject)
01240                                                 // where the deleted port can be seen
01241                                                 //fdfdf.insert( parent); 
01242                                                 //fdfdf.insert( m_openedObject);
01243                                                 fdfdf.insert( base_obj);
01244 
01245                                                 protect( base_obj, ELEM_DERIVED);
01246 
01247                                                 getBasesOf( fdfdf, checkOutBases);
01248                                                 getAllUpAndDown( fdfdf, to_be_checked_out_containers);to_be_checked_out_containers.insert( checkOutBases.begin(), checkOutBases.end());
01249                                                 // perform a dummy operation on all containers
01250                                                 for( XmlObjSet::const_iterator it = to_be_checked_out_containers.begin(); it != to_be_checked_out_containers.end(); ++it)
01251                                                 {
01252                                                         XmlObject * p = *it;
01253                                                         AttribMapIter ait = p->m_attributes.find( ATTRID_RELID);
01254                                                         if( ait != p->m_attributes.end())
01255                                                         {
01256                                                                 // dummy operation
01257                                                                 long relid = ((XmlAttrLong*)(ait->second))->m_value;
01258                                                                 ((XmlAttrLong*)(ait->second))->m_value = relid + 1;
01259                                                                 long reljd = ((XmlAttrLong*)(ait->second))->m_value;
01260                                                                 ASSERT( relid + 1 == reljd);
01261                                                         }
01262                                                 }
01263                                                 m_modifiedObjects.insert( to_be_checked_out_containers.begin(), to_be_checked_out_containers.end());
01264                                         }
01265                                 }
01266                         }
01267                         // CONNECTION
01268                         // Let S and D be source and destination.
01269                         // Four scenarios exist regarding their connectibility:
01270                         // Connection between:
01271                         // 1.S and/or D as standalone objects
01272                         // 2.S and/or D exposed as ports in their parents (MS and MD their parents)
01273                         // 3.S and/or D exposed as ports of a reference to MS respectively MD 
01274                         //       (Let RMS and RMD be these references.)
01275                         // 4.S and/or D exposed as ports of a reference to RMS respectively RMD
01276                         //       (Yes, references to references, which in turn reference a model, are also
01277                         //        suitable for creating connections.)
01278                         //
01279                         // If S and D are checkoutable, that means we see their latest version
01280                         //     (more specifically MS and MD except if S and D are models)
01281                         //     For cases 3 and 4 add RMS and RRMS respectively RMD, RRMD.
01282                         // If checkoutable is false, then protection lists could be analyzed for
01283                         //     DEL/MOVE events.
01284                         //     For cases 3 and 4 add REDIR event checkup on RMS/RRMS resp. RMD/RRMD.
01285                         // Before Save: objects are locked, plus the parent of the connection.
01286                         // After  Save: all objects become lockables again (checkoutable) except
01287                         //              the connection parent.
01288                         // CEnd, CSeq policy means that we lock S, D, RMS, RMD (in 3rd case), 
01289                         // RRMS and RRMD (in 4th case)
01290                         if( m_openedObject->m_metaid == DTID_CONNROLE && m_metaAttributeId == ATTRID_FCOREFATTR) // 525 or ATTRID_XREF
01291                         {
01292                                 metaobjidpair_type idpair;CopyTo(p, idpair);
01293                                 bool is_conn = idpair.metaid != 0 && idpair.objid != 0; // VARIANT p is 0 => owner connection is being deleted
01294                                 XmlObject * end_obj = objectFromObjId( idpair);
01295 
01296                                 protect( end_obj, ELEM_CONNECTED);
01297 
01298                                 if( is_conn && end_obj != 0 && m_userOpts.m_doConnEndPointLock)
01299                                 {
01300                                         m_modifiedObjects.insert( end_obj);
01301 #ifdef _DEBUG
01302 #if(DETAILS_ABOUT_XMLBACKEND)
01303                                         if( m_userOpts.m_createLog) {
01304                                                 std::string nn,mm; end_obj->m_attributes.find( ATTRID_NAME )->second->toString( nn);
01305                                                 end_obj->m_attributes.find(ATTRID_NAME)->second->toString(mm);
01306                                                 mylo += "<br>connecting through endpoint " + mm + " thus locking it <br>" ;
01307                                         }
01308 #endif
01309 #endif
01310 
01311                                 }
01312                         }
01313 
01314                         if( m_openedObject->m_metaid == DTID_CONNROLESEG && m_metaAttributeId == ATTRID_SEGREF) // 511, the Reference Involved
01315                         {
01316                                 metaobjidpair_type idpair;CopyTo(p, idpair);
01317                                 bool is_filled = idpair.metaid != 0 && idpair.objid != 0; // VARIANT p is 0 => owner connection is being deleted
01318                                 bool is_a_ref  = idpair.metaid == DTID_REFERENCE; // a conn seg must point by its SEGREF to a Ref
01319                                 XmlObject * segment_ref = objectFromObjId( idpair);
01320 
01321                                 protect( segment_ref, ELEM_TAKESPARTINCONN);
01322 
01323                                 if( is_filled && is_a_ref && segment_ref != 0 && m_userOpts.m_doConnSegmentLock)
01324                                 {
01325                                         m_modifiedObjects.insert( segment_ref);
01326 #ifdef _DEBUG
01327 #if(DETAILS_ABOUT_XMLBACKEND)
01328                                         if( m_userOpts.m_createLog) {
01329                                                 std::string nn,mm; segment_ref->m_attributes.find( ATTRID_NAME )->second->toString( nn);
01330                                                 segment_ref->m_attributes.find(ATTRID_NAME)->second->toString(mm);
01331                                                 mylo += "<br>connecting through segment ref " + mm + " thus locking it <br>" ;
01332                                         }
01333 #endif
01334 #endif
01335 
01336                                 }
01337                         }
01338 
01339                         m_modifiedObjects.insert( m_openedObject );
01340 
01341                         // handle special case: if this is the containment parent and this object is an inner
01342                         // we have to check out the parent too
01343                         ParentMap::iterator it2 = m_parentMap.find( m_openedObject->m_metaid );
01344                         ASSERT( it2 != m_parentMap.end() );
01345                         if( it2->second == m_metaAttributeId && !m_openedObject->isContainer() )
01346                         {
01347                                 XmlObject * cont = getContainer(m_openedObject);
01348                                 if (NULL != cont) {
01349                                         m_modifiedObjects.insert( cont );
01350                                 }
01351                         }
01352 
01353                         XmlAttrPointer * pointerAttr = (XmlAttrPointer*)it->second;
01354                         if( m_undoMap.find(it->second) == m_undoMap.end() )
01355                         {
01356                                 std::pair<UndoMapIter, bool> t = m_undoMap.insert( UndoMap::value_type(it->second,UndoItem(m_openedObject, m_metaAttributeId, CComVariant()) ));
01357                                 getPointer( pointerAttr, PutOut(t.first->second.m_value) );
01358                         }
01359                         XmlObject * parnt = setPointer( p );
01360                 }
01361                 else if( m_metaAttributeValType == VALTYPE_COLLECTION )
01362                 {
01363                         ASSERT( false );
01364                 }
01365                 else if( m_metaAttributeValType == VALTYPE_LOCK )
01366                 {
01367                         if( m_undoMap.find(it->second) == m_undoMap.end() )
01368                         {
01369                                 std::pair<UndoMapIter, bool> t = m_undoMap.insert( UndoMap::value_type(it->second,UndoItem(m_openedObject, m_metaAttributeId, CComVariant()) ));
01370                                 it->second->toVariant( PutOut(t.first->second.m_value) );
01371                         }
01372 
01373                         it->second->fromVariant(p);
01374 
01375                         //TODO: if locks go down to 0 it could be written out to the file
01376                 }
01377                 else // VALTYPE_BIN, VALTYPE_STR, VALTYPE_LONG, VALTYPE_REAL, VALTYPE_DICT
01378                 {
01379 #ifdef _DEBUG
01380 #if(DETAILS_ABOUT_XMLBACKEND)
01381                         if( m_userOpts.m_createLog) {
01382                                 mylo += " - valtype_StLoRe " ;
01383                         }
01384 #endif
01385 #endif
01386                         m_modifiedObjects.insert( m_openedObject );
01387 
01388                         if( !m_openedObject->m_loaded )
01389                         {
01390                                 fullReadContainer(getContainer(m_openedObject));
01391                                 it = m_openedObject->m_attributes.find( m_metaAttributeId );
01392                         }
01393 
01394                         // save previous value to m_undoMap (if this is the first modification)
01395                         if( m_undoMap.find(it->second) == m_undoMap.end() )
01396                         {
01397                                 std::pair<UndoMapIter, bool> t = m_undoMap.insert( UndoMap::value_type(it->second,UndoItem(m_openedObject, m_metaAttributeId, CComVariant()) ));
01398                                 it->second->toVariant( PutOut(t.first->second.m_value) );
01399                         }
01400                         it->second->fromVariant(p);
01401                 }
01402 
01403 #ifdef _DEBUG
01404 #if(DETAILS_ABOUT_XMLBACKEND)
01405                 if( m_userOpts.m_createLog) {
01406                         mylo += "\r\n";
01407                         if( m_metaAttributeValType != VALTYPE_LOCK) //3
01408                                 mylog += mylo;
01409                 }
01410 //#endif
01411 //#endif
01412                 std::string gd_str; guid2str( m_openedObject->m_guid, gd_str);
01413                 char* kind[] = {"100", "M", "A", "R", "C", "S", "F", "connrole", "connseg"
01414                         , "109", "110", "StrAttr", "IntAttr", "FloatAttr", "BoolAttr", "RrefAttr", "116", "117", "118", "119"
01415                         , "Constraint", "RegNode", "SetNode", "123", "124", "125", "126", "127" };
01416 
01417                 char *  oper[] = { "VALTYPE_None", "VALTYPE_COLL", "VALTYPE_Pointer", "VALTYPE_LOCK", "VALTYPE_LONG"
01418                         , "VALTYPE_STR", "VALTYPE_BIN", "VALTYPE_REAL", "VALTYPE_8", "VALTYPE_9", "VALTYPE_10", "VALTYPE_11"
01419                         , "VALTYPE_12", "VALTYPE_13", "VALTYPE_14"};
01420 
01421                 char mattrid[5];sprintf( mattrid, "%i", m_metaAttributeId);
01422                 std::string kind_str = ((m_openedObject->m_metaid<=100)?"Root": kind[m_openedObject->m_metaid-100]);
01423                 std::string oper_str = oper[m_metaAttributeValType];
01424 
01425                 time( &time2); tm2 = localtime( &time2);
01426                 double dur = difftime( time2, time1);
01427                 char buff[100];
01428                 sprintf( buff, "putAttribute [[%6.0f secs]] %s {%s} [ %s ] (%s)", dur, gd_str.c_str(), kind_str.c_str(), mattrid, oper_str.c_str());
01429 
01430                 if( m_userOpts.m_measureTime)
01431                         sendMsg( buff, MSG_INFO);
01432 #endif
01433 #endif
01434 
01435                 m_openedObject->m_modified = true;
01436                 m_modified = true;
01437         }
01438         COMCATCH(;)
01439 }
01440 
01441 STDMETHODIMP CCoreXmlFile::OpenObject(objid_type objid)
01442 {
01443         if( m_metaObject == NULL || !m_inTransaction )
01444                 COMRETURN(E_INVALID_USAGE);
01445 
01446         metaobjidpair_type idpair;
01447         idpair.metaid = m_metaObjectId;
01448         idpair.objid  = objid;
01449 
01450         COMTRY
01451         {
01452                 m_openedObject = objectFromObjId(idpair);
01453 
01454                 if( !m_openedObject || m_openedObject->m_deleted )
01455                         m_openedObject = NULL;
01456         }
01457         COMCATCH(;)
01458 }
01459 
01460 STDMETHODIMP CCoreXmlFile::CreateObject(objid_type *objid)
01461 {
01462         CHECK_OUT(objid);
01463 
01464         if( m_metaObject == NULL || !m_inTransaction )
01465                 COMRETURN(E_INVALID_USAGE);
01466 
01467         ASSERT( m_metaObjectId != METAID_ROOT );
01468 
01469         COMTRY
01470         {                
01471                 // create and add new object
01472                 XmlObject * obj = new XmlObject(m_metaObject,true);
01473                 addObject( obj );
01474 
01475                 m_createdObjects.push_back(obj);
01476 
01477                 m_modified = true;
01478                 m_openedObject = obj;
01479                 *objid = (long)obj;
01480 
01481                 if( obj->m_metaid==DTID_MODEL || obj->m_metaid==DTID_FOLDER )
01482                 {
01483                         resetSourceControlInfo( obj );
01484                         resetSourceControlStat( obj, true );
01485                 }
01486         }
01487         COMCATCH(;)
01488 }
01489 
01490 STDMETHODIMP CCoreXmlFile::CloseObject()
01491 {
01492         m_openedObject = NULL;
01493         return S_OK;
01494 }
01495 
01496 STDMETHODIMP CCoreXmlFile::LockObject()
01497 {
01498         return S_OK;
01499 }
01500 
01501 STDMETHODIMP CCoreXmlFile::DeleteObject()
01502 {
01503         if( m_openedObject == NULL || !m_inTransaction )
01504                 COMRETURN(E_INVALID_USAGE);
01505 
01506         m_openedObject->m_deleted = true;
01507         m_deletedObjects.insert( m_openedObject );
01508 
01509         m_modified = true;
01510 
01511         //m_modifiedObjects.insert( m_openedObject );
01512         // TODO: add container 
01513         //try {
01514         //      if( m_openedObject->isContainer())
01515         //      {
01516         //              // obj has its own file
01517         //              std::string path;
01518         //              getSourceSafePath( m_openedObject, path);
01519         //              CComBSTR vssPath = path.c_str();
01520 
01521         //              // obtain file handle in VSS
01522         //              CComObjPtr<IVSSItem> item;
01523         //              COMTHROW( m_vssDatabase->get_VSSItem( vssPath, false, &(item.p)) );
01524 
01525         //              FILE * f = fopen( (m_folderPath + "\\tombstone.txt").c_str(), "a+b");
01526         //              if( f)
01527         //              {
01528         //                      std::string fname;
01529         //                      getContainerFileName( m_openedObject, fname, false);
01530         //                      fprintf( f, "%s\n", fname.c_str());
01531         //                      fclose( f);
01532         //              }
01533         //              // this solution is not good, since for all users but this the local .xml file
01534         //              // will be unaffected: if the vss entry is renamed, upon getLatest an obsolete
01535         //              // file won't be overwritten by its newer (which shows that it is deleted) version
01536         //              //CComBSTR nm;
01537         //              //COMTHROW( item->get_Name( &nm));
01538         //              //nm.Append( ".del"); // append this extension to the filename
01539         //              //COMTHROW( item->put_Name( nm));
01540         //      }
01541         //} catch( hresult_exception&) {
01542         //      int l = 0;
01543         //      ++l;
01544         //}
01545         CloseObject();
01546 
01547         return S_OK;
01548 }
01549 
01550 void CCoreXmlFile::resetSettings()
01551 {
01552         m_hashFileNames         = false;
01553         m_hashInfoFound         = false;
01554         m_hashVal               = -1;
01555         m_svn.reset();
01556 }
01557 
01558 STDMETHODIMP CCoreXmlFile::OpenProject(BSTR connection, VARIANT_BOOL *ro_mode)
01559 {
01560         if( m_opened || m_metaProject == NULL )
01561                 COMRETURN(E_INVALID_USAGE);
01562 
01563         AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
01564 
01565         COMTRY {
01566                 resetSettings();
01567 
01568                 parseConnectionString( connection );
01569                 setFileNames();
01570 
01571                 m_userOpts.reset();
01572                 m_userOpts.load( m_folderPath);
01573                 m_userOpts.display( this);
01574 
01575                 readProjectFile();
01576 
01577                 bool cache_loading_succeeded = false;
01578                 if( m_userOpts.m_partialLoad) {
01579                         cache_loading_succeeded = readBinaryCache();
01580                 }
01581                 // if usecache option is false or if cache not read succesfully
01582 
01583                 //if( !m_userOpts.m_partialLoad)
01584                 if( !m_userOpts.m_partialLoad || !cache_loading_succeeded) // part_load was not requested or it was, but failed
01585                         //if( !m_userOpts.m_partialLoad || !readBinaryCache())
01586                         //if( true )
01587                 {                
01588                         // binary cache is not found, get latest and read all
01589                         if( m_sourceControl != SC_NONE ) {
01590                                 getLatestVersion();
01591                         }
01592 
01593                         readAll( true );
01594                         //if( m_userOpts.m_partialLoad) writeBinaryCache();
01595                 }
01596                 else
01597                 {
01598                         if( m_sourceControl != SC_NONE ) {
01599                                 getLatestVersion(); 
01600                         }
01601 
01602                         readAll( false );
01604                         //if( m_sourceControl != SC_NONE )
01605                         //    getLatestAndLoad();
01606                 }
01607 
01608                 // Check for the new session folder, create one on-demand
01609                 std::string sessionFolder =  m_folderPath + "\\" + HelperFiles::sessionFolderName;
01610                 DWORD atts = ::GetFileAttributes(sessionFolder.c_str());
01611                 if (atts == INVALID_FILE_ATTRIBUTES || !(atts & FILE_ATTRIBUTE_DIRECTORY) ) {
01612                         sendMsg( "Detecting old session format. Upgrading to newer one (dedicated session folder).", MSG_INFO);
01613                         
01614                         BOOL  succ = ::CreateDirectory( sessionFolder.c_str(), NULL);
01615                         if( succ != TRUE)
01616                         {
01617                                 sendMsg( "Exception: Could not create session folder: " + sessionFolder, MSG_ERROR);
01618                                 AfxMessageBox( (std::string( "Could not create session folder: ") + sessionFolder).c_str());
01619                                 HR_THROW(E_FILEOPEN);
01620                         }
01621                         // add to server
01622                         if (m_sourceControl == SC_SUBVERSION)
01623                                 succ = addSVN( sessionFolder, true /*=recursive*/); 
01624                         if( !succ) {
01625                                 sendMsg( "Exception: Could not add session folder to server.", MSG_ERROR);
01626                                 AfxMessageBox( "Could not add session folder to server.");
01627                                 HR_THROW(E_FILEOPEN);
01628                         }
01629 
01630                         // initial commit
01631                         if (m_sourceControl == SC_SUBVERSION)
01632                                 succ = commitSVN( m_folderPath, std::string("auto: OpenProject()"), true);
01633                         if( !succ) {
01634                                 sendMsg( "Exception: Could not commit session folder.", MSG_ERROR);
01635                                 AfxMessageBox( "Could not commit session folder.");
01636                                 HR_THROW(E_FILEOPEN);
01637                         }
01638                 }
01639 
01640                 // m_sourceControl has to be filled for these methods below (setParent)
01641                 m_signer.setParent( this);
01642                 m_signer.in(); // signing on does username verification also
01643                 m_protectList.setParent( this);
01644 
01645                 // purge my protect list
01646                 m_protectList.onLoad();
01647 
01648                 m_opened    = true;
01649                 m_modified  = false;
01650                 m_savedOnce = true;
01651 
01652                 if(ro_mode!=NULL) 
01653                         *ro_mode = VARIANT_FALSE;
01654 
01655                 CloseProgressWindow();
01656                 if (m_root == NULL)
01657                         return E_FILEOPEN;
01658 
01659         }
01660         COMCATCH(CloseProgressWindow();)
01661 }
01662 
01663 STDMETHODIMP CCoreXmlFile::CreateProject(BSTR connection)
01664 {
01665         AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
01666 
01667         COMTRY {
01668                 resetSettings();
01669 
01670                 if( m_opened || m_metaProject == NULL )
01671                         COMRETURN(E_INVALID_USAGE);
01672 
01673                 parseConnectionString( connection );
01674                 setFileNames();
01675                 m_hashInfoFound = true;     // upon creation we select the hash/nonhash question
01676                 // user options can't be provided in this scenario
01677                 //m_userOpts.load( m_folderPath, this);
01678 
01679                 // clear data structures
01680                 clearAll();
01681 
01682                 // query the metaobject for the root
01683                 CComObjPtr<ICoreMetaObject> mo;
01684                 COMTHROW( m_metaProject->get_Object(METAID_ROOT, PutOut(mo)) );
01685                 ASSERT( mo != NULL );
01686 
01687                 // create the root
01688                 m_root = new XmlObject(mo,true);
01689                 addObject( m_root );
01690 
01691                 if( m_svnUrl.size() > 0)
01692                         createSubversionedFolder();
01693                 else //AfxMessageBox( "Project has not been created under a source control system");
01694                         createNonversioned();
01695 
01696                 createProjectFile();
01697 
01698                 // m_sourceControl has to be filled for these methods below (setParent)
01699                 m_signer.setParent( this);
01700                 m_signer.in(); // signing on does username verification also
01701                 m_protectList.setParent( this);
01702 
01703                 m_opened   = true;
01704                 m_modified = false;
01705 
01706                 CloseProgressWindow();
01707         } COMCATCH(CloseProgressWindow();)
01708 
01709         return S_OK;
01710 }
01711 
01712 STDMETHODIMP CCoreXmlFile::SaveProject(BSTR connection, VARIANT_BOOL keepoldname = VARIANT_TRUE) 
01713 {
01714         AFX_MANAGE_STATE(AfxGetStaticModuleState());
01715 
01716         COMTRY {
01717                 // reload options in order to allow 'change-of-mind' for users
01718                 m_userOpts.reset();
01719                 m_userOpts.load( m_folderPath);
01720                 m_userOpts.display( this);
01721 
01722                 if( m_userOpts.m_partialLoad) writeBinaryCache();
01723                 writeAll();
01724 
01725                 if( m_sourceControl != SC_NONE )
01726                 {
01727                         if( !m_savedOnce )
01728                                 checkInAll(true);
01729                         else
01730                                 checkInAll();
01731 
01732                 }
01733 
01734                 m_modified = false;
01735                 m_savedOnce = true;
01736 
01737                 CloseProgressWindow();
01738         } COMCATCH(CloseProgressWindow();)
01739 
01740         return S_OK;
01741 }
01742 
01743 STDMETHODIMP CCoreXmlFile::CloseProject( VARIANT_BOOL abort)
01744 {
01745         AFX_MANAGE_STATE(AfxGetStaticModuleState());
01746 
01747         if( !m_opened || m_metaProject == NULL )
01748                 COMRETURN(E_INVALID_USAGE);
01749 
01750         COMTRY
01751         {
01752                 /*if( abort == VARIANT_FALSE && m_modified ) 
01753                 SaveProject(NULL);
01754 
01755                 if( m_sourceControl != SC_NONE )
01756                 checkInAll();*/
01757 
01758                 // FIXME Delete if Creat was no successful?
01759 
01760                 // purge my protect list
01761                 m_protectList.onLoad();
01762 
01763                 m_signer.off();
01764                 clearAll();
01765                 resetSettings();
01766                 XMLPlatformUtils::Terminate();
01767                 CloseProgressWindow();
01768         }
01769         COMCATCH(CloseProgressWindow();)
01770 }
01771 
01772 STDMETHODIMP CCoreXmlFile::DeleteProject()
01773 {
01774         TCHAR dir[MAX_PATH + 1];
01775         _tcscpy(dir, m_folderPath.c_str());
01776         dir[m_folderPath.length()] = L'\0';
01777 
01778         SHFILEOPSTRUCT file_op = {
01779         NULL,
01780         FO_DELETE,
01781         dir,
01782         "",
01783         FOF_NOCONFIRMATION |
01784         FOF_NOERRORUI |
01785         FOF_SILENT,
01786         false,
01787         0,
01788         "" };
01789         return SHFileOperation(&file_op) ? E_FAIL : S_OK;
01790 }
01791 
01792 STDMETHODIMP CCoreXmlFile::BeginTransaction()
01793 {    
01794         AFX_MANAGE_STATE(AfxGetStaticModuleState());
01795 
01796         if( !m_opened || m_inTransaction )
01797                 COMRETURN(E_INVALID_USAGE);
01798         m_inTransaction = true;
01799         m_trivialChanges = true;
01800         m_fullLockNeeded = false;
01801         m_needsSessionRefresh = true;
01802         return S_OK;
01803 }
01804 
01805 STDMETHODIMP CCoreXmlFile::CommitTransaction()
01806 {    
01807         AFX_MANAGE_STATE(AfxGetStaticModuleState());
01808 
01809         COMTRY {
01810                 if( !m_inTransaction )
01811                         COMRETURN(E_INVALID_USAGE);
01812 
01813                 ASSERT( m_opened );
01814 
01815                 bool failed = false;
01816 
01817         #ifdef _DEBUG
01818         #if(DETAILS_ABOUT_XMLBACKEND)
01819                 time_t time1, time2, time3;
01820                 struct tm *tm1, *tm2, *tm3;
01821                 time( &time1); tm1 = localtime( &time1);
01822                 //if( m_userOpts.m_measureTime) sendMsg( std::string( "CommitBegin ") + asctime( tm1 ), MSG_INFO);
01823         #endif
01824         #endif
01825 
01826                 XmlObjSet to_be_checked_out_containers;
01827                 getCheckOutContainers(m_modifiedObjects, to_be_checked_out_containers);
01828 
01829         #ifdef _DEBUG
01830         #if(DETAILS_ABOUT_XMLBACKEND)
01831                 time( &time2); tm2 = localtime( &time2);
01832                 //if( m_userOpts.m_measureTime) sendMsg( std::string( "CommitMidle ") + asctime( tm2 ), MSG_INFO);
01833         #endif
01834         #endif
01835 
01836                 CloseObject();
01837 
01838                 //m_needClose = false;
01839 
01840                 if( !checkOutFiles(to_be_checked_out_containers) )
01841                 {
01842         #ifdef _DEBUG
01843         #if(DETAILS_ABOUT_XMLBACKEND)
01844                         time( &time3);
01845                         tm3 = localtime( &time3);
01846 
01847                         double dur = difftime( time3, time1);
01848                         char buff[100];
01849                         sprintf( buff, " [Took total of  %6.0f secs]", dur );
01850 
01851                         if( m_userOpts.m_measureTime) sendMsg( std::string( "CommitAbort ") + asctime( tm3) + buff, MSG_INFO);
01852         #endif
01853         #endif
01854                         CloseProgressWindow();
01855                         return E_FAIL;
01856                 }
01857                 else
01858                 {
01859                         m_trivialChanges = true;
01860                         m_fullLockNeeded = false;
01861                         m_createdObjects.clear();
01862                         m_deletedObjects.clear();
01863                         m_modifiedObjects.clear();
01864                         m_undoMap.clear();
01865                         m_inTransaction = false;
01866 
01867         #ifdef _DEBUG
01868         #if(DETAILS_ABOUT_XMLBACKEND)
01869 
01870                         if( m_userOpts.m_createLog) {
01871                                 //while( -1 != mylog.find( "\r\n"))
01872                                 //{
01873                                 //    int p = mylog.find( "\r\n");
01874                                 //    mylog.replace( p, 2, "<br>");
01875                                 //}
01876                                 if( mylog.size() != 0 && mylog != "<br>")
01877                                         sendMsg( mylog, MSG_INFO);
01878                                 mylog.clear(); // clear the log
01879                         }
01880         #endif
01881         #endif
01882                         //if( m_needClose )
01883                         //  m_gme->CloseProject( TRUE );
01884 
01885         #ifdef _DEBUG
01886         #if(DETAILS_ABOUT_XMLBACKEND)
01887                         time( &time3);
01888                         tm3 = localtime( &time3);
01889 
01890                         double dur = difftime( time3, time1);
01891                         char buff[100];
01892                         sprintf( buff, " [Took total of  %6.0f secs]", dur );
01893 
01894                         if( m_userOpts.m_measureTime) sendMsg( std::string( "CommitSucce ") + asctime( tm3) + buff, MSG_INFO);
01895         #endif
01896         #endif
01897                         m_protectList.onCommited();
01898                         CloseProgressWindow();
01899                         return S_OK;
01900                 }
01901         } COMCATCH(CloseProgressWindow();)
01902 }
01903 
01904 STDMETHODIMP CCoreXmlFile::AbortTransaction()
01905 {
01906         AFX_MANAGE_STATE(AfxGetStaticModuleState());
01907 
01908         COMTRY {
01909                 if( !m_inTransaction )
01910                         COMRETURN(E_INVALID_USAGE);
01911 
01912                 ASSERT( m_opened );
01913 
01914                 // undelete deleted objects
01915                 for( XmlObjSetIter it=m_deletedObjects.begin(); it!=m_deletedObjects.end(); ++it )
01916                         (*it)->m_deleted = false;
01917 
01918                 // delete created objects
01919                 int minIndex = m_objects.size();
01920                 for( XmlObjVecIter it2=m_createdObjects.begin(); it2!=m_createdObjects.end(); ++it2 )
01921                 {
01922                         XmlObject* obj = *it2;
01923                         m_objectsByGUID.erase(obj->m_guid);
01924                         if( obj->m_index < minIndex )
01925                                 minIndex = obj->m_index;
01926                 }
01927                 m_objects.resize( minIndex );
01928 
01929                 // rollback attributum changes
01930                 for( UndoMapIter it3=m_undoMap.begin(); it3!=m_undoMap.end(); ++it3 )
01931                 {
01932                         XmlObject * obj = it3->second.m_object;
01933                         if( it3->first->getType() == VALTYPE_POINTER )
01934                         {
01935                                 XmlAttrPointer * pointerAttr = (XmlAttrPointer*)it3->first;
01936                                 metaobjidpair_type idpair;
01937                                 CopyTo(it3->second.m_value, idpair);
01938                                 XmlObject * parent = objectFromObjId(idpair);
01939                                 setPointer( obj, it3->second.m_attrId, parent );
01940                         }
01941                         else
01942                         {
01943                                 it3->first->fromVariant(it3->second.m_value);
01944                         }
01945                 }
01946                 m_protectList.onAborted();
01947                 m_undoMap.clear();
01948 
01949                 m_deletedObjects.clear();
01950                 m_createdObjects.clear();
01951                 m_modifiedObjects.clear();
01952                 m_trivialChanges = true;
01953                 m_fullLockNeeded = false;
01954                 CloseObject();    
01955                 m_inTransaction = false;
01956 
01957                 CloseProgressWindow();
01958         } COMCATCH(CloseProgressWindow();)
01959 
01960         return S_OK;
01961 }
01962 
01963 STDMETHODIMP CCoreXmlFile::get_StorageType(long *p)
01964 {
01965         CHECK_OUT(p);
01966         *p = 0;
01967         return S_OK;
01968 }
01969 
01970 void CCoreXmlFile::fillParentMap()
01971 {
01972         m_parentMap.clear();
01973 
01974         m_parentMap.insert( ParentMap::value_type( DTID_CONSTRAINT , ATTRID_CONSTROWNER ) );
01975         m_parentMap.insert( ParentMap::value_type( DTID_REGNODE    , ATTRID_REGNOWNER   ) );
01976         m_parentMap.insert( ParentMap::value_type( DTID_FOLDER     , ATTRID_PARENT      ) );
01977         m_parentMap.insert( ParentMap::value_type( DTID_MODEL      , ATTRID_PARENT      ) );
01978         m_parentMap.insert( ParentMap::value_type( DTID_ATOM       , ATTRID_PARENT      ) );
01979         m_parentMap.insert( ParentMap::value_type( DTID_REFERENCE  , ATTRID_PARENT      ) );
01980         m_parentMap.insert( ParentMap::value_type( DTID_SET        , ATTRID_PARENT      ) );
01981         m_parentMap.insert( ParentMap::value_type( DTID_SETNODE    , ATTRID_SETMEMBER   ) );
01982         m_parentMap.insert( ParentMap::value_type( DTID_CONNECTION , ATTRID_PARENT      ) );
01983         m_parentMap.insert( ParentMap::value_type( DTID_CONNROLE   , ATTRID_CONNROLE    ) );
01984         m_parentMap.insert( ParentMap::value_type( DTID_CONNROLESEG, ATTRID_CONNSEG     ) );
01985         m_parentMap.insert( ParentMap::value_type( DTID_STRATTR    , ATTRID_ATTRPARENT  ) );
01986         m_parentMap.insert( ParentMap::value_type( DTID_INTATTR    , ATTRID_ATTRPARENT  ) );
01987         m_parentMap.insert( ParentMap::value_type( DTID_FLOATATTR  , ATTRID_ATTRPARENT  ) );
01988         m_parentMap.insert( ParentMap::value_type( DTID_BOOLATTR   , ATTRID_ATTRPARENT  ) );
01989         m_parentMap.insert( ParentMap::value_type( DTID_REFATTR    , ATTRID_ATTRPARENT  ) );
01990 }
01991 
01992 void CCoreXmlFile::closeMetaProject()
01993 {
01994         closeMetaObject();
01995         CloseProject();
01996         m_metaProject = NULL;
01997 }
01998 
01999 void CCoreXmlFile::openMetaObject()
02000 {
02001         ASSERT( m_metaObject != NULL );
02002         COMTHROW( m_metaObject->get_MetaID(&m_metaObjectId) );
02003 }
02004 
02005 void CCoreXmlFile::closeMetaObject()
02006 {
02007         CloseObject();
02008         closeMetaAttribute();
02009         m_metaObject   = NULL;
02010         m_metaObjectId = METAID_NONE;
02011 }
02012 
02013 void CCoreXmlFile::openMetaAttribute()
02014 {
02015         ASSERT( m_metaAttribute != NULL );
02016         COMTHROW( m_metaAttribute->get_AttrID(&m_metaAttributeId) );
02017         COMTHROW( m_metaAttribute->get_ValueType(&m_metaAttributeValType) );
02018 
02019         if( m_metaAttributeId == ATTRID_NONE )
02020                 HR_THROW(E_METAPROJECT);
02021 }
02022 
02023 void CCoreXmlFile::closeMetaAttribute()
02024 {
02025         m_metaAttribute        = NULL;
02026         m_metaAttributeId      = ATTRID_NONE;
02027         m_metaAttributeValType = VALTYPE_NONE;
02028 }
02029 
02030 void CCoreXmlFile::parseConnectionString( BSTR connection )
02031 {   
02032         // connection string format:
02033         // START   = PAIRMGX | PAIR2
02034         // PAIR2   = PAIR PAIR2 | empty
02035         // PAIR    = KEY '=' VAL
02036         // PAIRMGX = 'MGX=' string
02037         // KEY     = string
02038         // VAL     = '"' string '"'
02039         //
02040         // valid key values: MGX, 
02041         //
02042         // example: 
02043         //   MGX="C:\temp\test1" vssdatabase="frfre" vsspath="dede" user="fdasfdsfds" password="defrefre"
02044         //   MGX="C:\temp\test1" svn="svn://localhost/"
02045         std::string conn;
02046         CopyTo(connection, conn);
02047         const char * connectionString = conn.c_str();
02048 
02049         if( strncmp( connectionString, "MGX=", 4 ) != 0 )
02050                 HR_THROW(E_INVALID_USAGE);
02051 
02052         int          size = conn.size();
02053         std::string       key;
02054         std::string       val;
02055         bool         keyCollecting = true;
02056         bool         startedValue  = false;
02057 
02058         m_contentPath    = "";
02059         m_folderPath     = "";
02060         m_svnUrl         = "";
02061 
02062         std::string to_hash = "";
02063         std::string to_hash_with_val = "";
02064 
02065         for( int i=0; i<size; ++i )
02066         {
02067                 char ch = connectionString[i];
02068                 if( keyCollecting )
02069                 {
02070                         if( ch == '=' )
02071                         {
02072                                 keyCollecting = false;
02073                                 startedValue  = false;
02074                                 val = "";
02075                         }
02076                         else
02077                         {
02078                                 if( ch != ' ' && ch != '\t' )
02079                                         key += ch;
02080                         }
02081                 }
02082                 else
02083                 {
02084                         if( startedValue )
02085                         {
02086                                 if( ch != '\"' )
02087                                         val += ch;
02088                                 else
02089                                 {
02090                                         // key-val pair is finished
02091                                         if( stricmp( key.c_str(), "MGX" ) == 0 )
02092                                                 m_folderPath = val;
02093                                         else if( stricmp( key.c_str(), "user" ) == 0 )
02094                                                 m_vssUser = val;
02095                                         else if( stricmp( key.c_str(), "password" ) == 0 )
02096                                                 m_vssPassword = val;
02097                                         else if( stricmp( key.c_str(), "svn" ) == 0 )
02098                                                 m_svnUrl = val;
02099                                         else if( stricmp( key.c_str(), "hash") == 0 )
02100                                                 to_hash = val;
02101                                         else if( stricmp( key.c_str(), "hval") == 0 )
02102                                                 to_hash_with_val = val;
02103 
02104                                         keyCollecting = true;
02105                                         key = "";
02106                                 }
02107                         }
02108                         else
02109                         {
02110                                 if( ch == '\"' )
02111                                         startedValue = true;
02112 
02113                         }
02114                 }      
02115         }
02116 
02117         if( to_hash == "true")
02118         {
02119                 m_contentPath = m_folderPath + "\\" + m_contentConst;
02120                 m_hashFileNames = true;
02121                 m_hashInfoFound = true;
02122                 if( to_hash_with_val == "4096")
02123                         m_hashVal = 5;
02124                 else if( to_hash_with_val == "256")
02125                         m_hashVal = 2;
02126                 //else if( to_hash_with_val == "3")
02127                 //      m_hashVal = 3;
02128                 //else if( to_hash_with_val == "4")
02129                 //      m_hashVal = 4;
02130                 else
02131                         HR_THROW( E_FILEOPEN);
02132         }
02133         else
02134                 m_hashFileNames = false;
02135 }
02136 
02137 bool CCoreXmlFile::isUrlSvnSsh()
02138 {
02139         return m_svnUrl.substr( 0, 10) == "svn+ssh://";
02140 }
02141 
02142 std::string CCoreXmlFile::userNameFromSvnSshUrl()
02143 {
02144         size_t at_pos = m_svnUrl.find( '@');
02145         if( m_svnUrl.substr( 0, 10) == "svn+ssh://" && at_pos > 10 && at_pos != std::string::npos) // uname found
02146         {
02147                 return m_svnUrl.substr( 10, at_pos - 10);
02148         }
02149         return "";
02150 }
02151 
02152 void CCoreXmlFile::svnSshHandling()
02153 {
02154         if( m_svnUrl.substr( 0, 10) == "svn+ssh://" && m_svnUrl.find( '@') == std::string::npos) // uname not found
02155         {
02156                 if( m_vssUser.empty())
02157                 {
02158                         AfxMessageBox( "Could not process further with \"svn+ssh://\" scheme if username is not provided\n\
02159 either in the Credential Dialog or embedded in the url as \"svn+ssh://username@host.example.com\".");
02160                         HR_THROW( E_UNKNOWN_STORAGE);
02161                 }
02162                 else
02163                         m_svnUrl = std::string( "svn+ssh://") + m_vssUser + "@" + m_svnUrl.substr( 10);
02164         }
02165 }
02166 
02167 std::string CCoreXmlFile::svnSshMangling( const std::string& p_url)
02168 {
02169         std::string ret;
02170         size_t at_pos = p_url.find( '@');
02171         if( p_url.substr( 0, 10) == "svn+ssh://" && at_pos != std::string::npos) // uname found
02172         {
02173                 ret = std::string( "svn+ssh://") + p_url.substr( at_pos + 1);
02174                 return ret;
02175         }
02176         return p_url;
02177 }
02178 
02179 void CCoreXmlFile::svnOptions()
02180 {
02181         if( !m_svnUrl.empty())
02182         {
02183                 if( !m_hashInfoFound)
02184                 {
02185                         m_hashFileNames = IDYES == AfxMessageBox( "Use hashed subdirectories?", MB_YESNO);
02186                         if( m_hashFileNames)
02187                         {
02188                                 m_hashVal = (IDYES == AfxMessageBox( "Does the project have 4096 subdirectories (1+2 digit hashing)?", MB_YESNO))?5:2;
02189                                 if( m_hashVal == 2) AfxMessageBox( "Defaulting to a project with 256 subdirectories (2 digit hashing).", MB_ICONINFORMATION);
02190                                 //if( m_hashVal == 2)
02191                                 //{
02192                                 //      m_hashVal = (IDYES == AfxMessageBox( "Use 4 digit hashed subdirectories?", MB_YESNO))?4:2;
02193                                 //      if( m_hashVal == 2)
02194                                 //              m_hashVal = (IDYES == AfxMessageBox( "Use 3 digit hashed subdirectories?", MB_YESNO))?3:2;
02195                                 //}
02196                         }
02197                 }
02198         }
02199 }
02200 
02201 void CCoreXmlFile::setFileNames( bool p_reset /* = false */)
02202 {  
02203         char drive[_MAX_DRIVE];
02204         char dir[_MAX_DIR];
02205         char fname[_MAX_FNAME];
02206         char ext[_MAX_EXT];
02207 
02208         // a conn string like 'c:\\t\\v\\p' comes in in m_folderPath
02209         // which is then divided in 'c' as drive, 't\\v' as dir
02210         // and 'p' as fname (although p is a directory) and '' as ext
02211         _splitpath( m_folderPath.c_str(), drive, dir, fname, ext );
02212 
02213         if( p_reset)
02214         {
02215                 // m_projectFileName will be reset to a valid .mgx filename
02216                 char                 buf[_MAX_PATH];
02217                 _finddata_t          fileInfo;
02218 
02219                 sprintf( buf, "%s\\*.mgx", m_folderPath.c_str() );
02220 
02221                 long searchHandle = _findfirst( buf, &fileInfo ); // findfirst in project dir
02222                 long ret = searchHandle;
02223                 long count = 0;
02224                 while( ret != -1 && ++count)
02225                         ret = _findnext( searchHandle, &fileInfo );   // findnext
02226 
02227                 if( count > 1)
02228                         sendMsg( "Multiple .mgx files were found in directory " + m_folderPath, MSG_ERROR);
02229                 else if( count == 0)
02230                         sendMsg( "No .mgx file was found in directory " + m_folderPath, MSG_ERROR);
02231                 else
02232                 {
02233                         // new m_projectFileName value set, a real one
02234                         m_projectFileName = m_folderPath;
02235                         m_projectFileName += "\\";
02236                         m_projectFileName += fileInfo.name; // m_projectName or m_vssPath or m_parentFolderPath do NOT change
02237 
02238                         sendMsg( "Loading project file: " + m_projectFileName, MSG_INFO);
02239                         _findclose( searchHandle );
02240                 }
02241                 return;
02242         }
02243 
02244         m_parentFolderPath = drive;
02245         m_parentFolderPath += dir;
02246         if( m_parentFolderPath[m_parentFolderPath.size()-1] == '\\' )
02247                 m_parentFolderPath.resize(m_parentFolderPath.size()-1);
02248 
02249         // this is the name of the project directory : 'p' in the example above
02250         m_projectName = fname;
02251 
02252         m_cacheFileName = m_folderPath + "\\project.bin";
02253 
02254         // the name of the project file siting in the project dir: 'c:\\t\\u\\p\\project.mgx'
02255         m_projectFileName = m_folderPath + "\\project.mgx";
02256 
02257         m_vssPath = m_vssParentPath;
02258         m_vssPath += "/";
02259         m_vssPath += m_projectName;
02260 }
02261 
02262 void CCoreXmlFile::getContainerFileName(XmlObject * obj, std::string& str, bool fullpath)
02263 {
02264         ASSERT( obj->isContainer() );
02265 
02266         std::string guidStr;
02267         guid2str( obj->m_guid, guidStr );
02268 
02269         if( fullpath )
02270         {
02271                 str = m_folderPath;
02272                 str += "\\";
02273         }
02274         else
02275                 str = "";
02276 
02277         if( m_hashFileNames && obj->m_metaid != METAID_ROOT) // hashing not applied for the root folder
02278         {
02279                 str += m_contentConst;
02280                 str += "\\";
02281                 if( m_hashVal == 5) // 1+2 setup
02282                 {
02283                         str += guidStr.substr( 0, 1);
02284                         str += "\\";
02285                         str += guidStr.substr( 1, 2);
02286                         str += "\\";
02287                 }
02288                 else if( m_hashVal == 2)
02289                 {
02290                         str += guidStr.substr( 0, 2);
02291                         str += "\\";
02292                 }
02293                 else if( m_hashVal == 3)
02294                 {
02295                         str += guidStr.substr( 0, 1) + "\\" + guidStr.substr( 1, 1) + "\\" + guidStr.substr( 2, 1) + "\\";
02296                 }
02297                 else if( m_hashVal == 4)
02298                 {
02299                         str += guidStr.substr( 0, 1) + "\\" + guidStr.substr( 1, 1) + "\\" + guidStr.substr( 2, 1) + "\\" + guidStr.substr( 3, 1) + "\\";
02300                 }
02301                 //str += guidStr.substr( 0, 2);
02302                 //str += "\\";
02303         }
02304 
02305         str += guidStr;
02306         str += ".xml";
02307 }
02308 
02309 void CCoreXmlFile::getContainerName(XmlObject * obj, string& name, string& type)
02310 {
02311         CComObjPtr<ICoreMetaObject>     metaobject;
02312         CComBSTR                        metaToken;
02313 
02314         COMTHROW( m_metaProject->get_Object( obj->m_metaid, PutOut(metaobject) ) );
02315         COMTHROW( metaobject->get_Token( &metaToken ) );
02316 
02317         CopyTo(metaToken, type);
02318 
02319         name = "";
02320         AttribMapIter it;
02321         for( it=obj->m_attributes.begin(); it!=obj->m_attributes.end(); ++it )
02322         {
02323                 XmlAttrBase                    * attr = it->second;
02324                 CComObjPtr<ICoreMetaAttribute>   metaAttrib;
02325                 CComBSTR                         attribToken;
02326                 string                           attribToken2;
02327                 string                           attrVal;
02328 
02329                 COMTHROW( metaobject->get_Attribute( it->first, PutOut(metaAttrib) ) );
02330                 metaAttrib->get_Token( &attribToken );
02331 
02332                 CopyTo(attribToken, attribToken2);
02333 
02334                 if( stricmp( attribToken2.c_str(), "name" ) == 0 && attr->getType() == VALTYPE_STRING )
02335                 {
02336                         XmlAttrBase * attr = it->second;
02337                         attr->toString(name);
02338                         break;
02339                 }
02340         }
02341 }
02342 
02343 // method used to clear all objects before a new attempt to read a project.
02344 // it is used by ~CCoreXmlFile, or ::CloseProject also.
02345 void CCoreXmlFile::clearAll()
02346 {
02347         for( XmlObjVecIter i=m_objects.begin(); i!=m_objects.end(); ++i )       
02348                 delete (*i);
02349         m_objects.clear();
02350         m_objectsByGUID.clear();
02351         m_openedObject = NULL;
02352         m_root = NULL;
02353 }
02354 
02355 void CCoreXmlFile::addObject(XmlObject * obj)
02356 {
02357         obj->m_index = m_objects.size();
02358         m_objects.push_back( obj );
02359         m_objectsByGUID.insert( GUIDToXmlObjectMap::value_type( obj->m_guid, obj ) );
02360 }
02361 
02362 void CCoreXmlFile::deleteObject(XmlObject * obj)
02363 {
02364         // remove pointers
02365         for(AttribMapIter it=obj->m_attributes.begin(); it!=obj->m_attributes.end(); ++it)
02366         {
02367                 if( it->second->getType() == VALTYPE_POINTER )
02368                 {
02369                         // remove it from parent object's collection
02370                         XmlAttrPointer * pointer = (XmlAttrPointer*)it->second;
02371                         if( pointer->m_parent != NULL )
02372                         {
02373                                 AttribMapIter it2 = pointer->m_parent->m_attributes.find( it->first + ATTRID_COLLECTION );
02374                                 ASSERT( it2 != pointer->m_parent->m_attributes.end() );
02375                                 ASSERT( it2->second->getType() == VALTYPE_COLLECTION );
02376                                 ((XmlAttrCollection*)it2->second)->m_children.erase(obj);
02377                         }
02378                 }
02379                 else if( it->second->getType() == VALTYPE_COLLECTION )
02380                 {
02381                         XmlAttrCollection * collection = (XmlAttrCollection*)it->second;
02382                         for( XmlObjSetIter it2=collection->m_children.begin(); it2!=collection->m_children.end(); ++it2 )
02383                         {
02384                                 // set parent of this child to NULL
02385                                 XmlObject * obj2 = (*it2);
02386                                 AttribMapIter it3 = obj2->m_attributes.find( it->first - ATTRID_COLLECTION );
02387                                 ASSERT( it3 != obj2->m_attributes.end() );
02388                                 ASSERT( it3->second->getType() == VALTYPE_POINTER );
02389                                 ((XmlAttrPointer *)(it3->second))->m_parent = NULL;
02390                         }
02391                 }
02392         }
02393 
02394         obj->m_deleted = true;
02395 }
02396 
02397 void CCoreXmlFile::setPointer(XmlObject * obj, attrid_type attribId, XmlObject * parent)
02398 {
02399         ASSERT( obj!=NULL );
02400 
02401         AttribMapIter it = obj->m_attributes.find(attribId);
02402         ASSERT( it!=obj->m_attributes.end() );
02403 
02404         XmlAttrPointer * attr = (XmlAttrPointer*)it->second;
02405 
02406         // remove item from old parent's list
02407         if( attr->m_parent != NULL )
02408         {
02409                 // find collection attribute of parent
02410                 AttribMapIter it2 = attr->m_parent->m_attributes.find( attribId + ATTRID_COLLECTION );
02411                 ASSERT( it2 != attr->m_parent->m_attributes.end() );
02412                 ASSERT( it2->second->getType() == VALTYPE_COLLECTION );
02413 
02414                 // remove this form the list
02415                 ((XmlAttrCollection *)it2->second)->m_children.erase(obj);
02416         }
02417 
02418         // set pointer attribute
02419         attr->m_parent = parent;
02420 
02421         // add item to new parent's list
02422         if( parent != NULL )
02423         {
02424                 AttribMapIter it3 = parent->m_attributes.find( attribId + ATTRID_COLLECTION );
02425                 ASSERT( it3 != parent->m_attributes.end() );
02426                 ASSERT( it3->second->getType() == VALTYPE_COLLECTION );
02427                 ((XmlAttrCollection *)it3->second)->m_children.insert(obj);
02428         }
02429 }
02430 
02431 XmlObject * CCoreXmlFile::setPointer(VARIANT p)
02432 {   
02433         metaobjidpair_type idpair;
02434         CopyTo(p, idpair);
02435         XmlObject * parent = objectFromObjId(idpair);
02436         setPointer( m_openedObject, m_metaAttributeId, parent );
02437         return parent;
02438 }
02439 
02440 void CCoreXmlFile::updateCollections()
02441 {
02442         for( XmlObjVecIter it=m_objects.begin(); it!=m_objects.end(); ++it )
02443         {   
02444                 XmlObject * obj = (*it);
02445                 for( AttribMapIter j=obj->m_attributes.begin(); j!=obj->m_attributes.end(); ++j)
02446                 {
02447                         if( j->second->getType() == VALTYPE_POINTER )
02448                         {
02449                                 XmlAttrPointer * pointer = (XmlAttrPointer*)j->second;
02450                                 if( pointer->m_parent != NULL )
02451                                 {
02452                                         AttribMapIter k = pointer->m_parent->m_attributes.find( j->first + ATTRID_COLLECTION );
02453                                         ASSERT( k != pointer->m_parent->m_attributes.end() );
02454                                         ((XmlAttrCollection*)k->second)->m_children.insert( obj );
02455                                 }
02456                         }
02457                 }
02458         }
02459 }
02460 
02461 void CCoreXmlFile::resolvePointers(UnresolvedPointerVec& pointers)
02462 {
02463 #ifdef DEBUG
02464 #if(RESOLVE_PTRS_2ND_ATTEMPT)
02465         UnresolvedPointerVec again_unresolved;
02466 #endif
02467 #endif
02468 
02469         for( UnresolvedPointerVecIt it=pointers.begin(); it!=pointers.end(); ++it )
02470         {
02471                 if( it->m_pointedObjGuid == GUID_NULL )
02472                 {
02473                         setPointer( it->m_object, it->m_attrib, NULL );
02474                 }
02475                 else
02476                 {
02477                         GUIDToXmlObjectMapIter it2 = m_objectsByGUID.find( it->m_pointedObjGuid );
02478                         if( it2 != m_objectsByGUID.end() )
02479                         {
02480                                 setPointer( it->m_object, it->m_attrib, it2->second );
02481                         }
02482                         else
02483                         {
02484                                 // TODO: invalid pointer, what to do?
02485 #ifdef DEBUG
02486 #if(RESOLVE_PTRS_2ND_ATTEMPT)
02487                                 again_unresolved.push_back( *it);
02488 #endif
02489 #endif
02490                         }
02491                 }
02492         }
02493 
02494         // analyze unresolved pointers in debug mode:
02495 #ifdef DEBUG
02496 #if(RESOLVE_PTRS_2ND_ATTEMPT)
02497         int still_unresolved = again_unresolved.size();
02498         ASSERT( still_unresolved == 0);                        // notify user!!!
02499 
02500         for( UnresolvedPointerVecIt it=again_unresolved.begin(); it!=again_unresolved.end(); ++it )
02501         {
02502                 int k = 0;
02503                 ++k;
02504                 if( it->m_object->m_metaid == DTID_CONNROLE && it->m_attrib == ATTRID_FCOREFATTR) // XREF, 525
02505                 {
02506                         // connEnd can't be resolved
02507                         // find the connection it belongs to -> simply find its parent
02508                         XmlObject * obj = it->m_object; int l = 0;
02509                         while( obj != 0 && (obj->m_metaid != DTID_CONNECTION || obj->m_metaid == DTID_MODEL || obj->m_metaid == DTID_FOLDER))
02510                         {
02511                                 ParentMap::iterator it1 = m_parentMap.find( obj->m_metaid );
02512                                 ASSERT( it1 != m_parentMap.end() );
02513                                 AttribMapIter it2 = obj->m_attributes.find( it1->second );
02514                                 ASSERT( it2 != obj->m_attributes.end() );
02515                                 obj = ((XmlAttrPointer*)(it2->second))->m_parent;
02516                                 ++l;
02517                         }
02518                         ASSERT( l == 1);
02519 
02520                         if( obj != 0 && obj->m_metaid == DTID_CONNECTION) // OK
02521                         {
02522                                 int k = 0;
02523                                 ++k;
02524                                 AttribMapIter pit = obj->m_attributes.find( ATTRID_PARENT);
02525                                 ASSERT( pit != obj->m_attributes.end());
02526                                 XmlObject * parent = ((XmlAttrPointer*)(pit->second))->m_parent;
02527                                 // delete it: set its parent to 0
02528                                 ASSERT(0); // notify user
02529                                 setPointer( obj, ATTRID_PARENT, 0);
02530                         }
02531                 }
02532 
02533                 if( it->m_object->m_metaid == DTID_CONNROLESEG && it->m_attrib == ATTRID_SEGREF) // 511
02534                 {
02535                         XmlObject * obj = it->m_object; int l = 0;
02536                         while( obj != 0 && (obj->m_metaid != DTID_CONNECTION || obj->m_metaid == DTID_MODEL || obj->m_metaid == DTID_FOLDER))
02537                         {
02538                                 ParentMap::iterator it1 = m_parentMap.find( obj->m_metaid );
02539                                 ASSERT( it1 != m_parentMap.end() );
02540                                 AttribMapIter it2 = obj->m_attributes.find( it1->second );
02541                                 ASSERT( it2 != obj->m_attributes.end() );
02542                                 obj = ((XmlAttrPointer*)(it2->second))->m_parent;
02543                                 ++l;
02544                         }
02545                         ASSERT( l == 2);
02546 
02547                         if( obj != 0 && obj->m_metaid == DTID_CONNECTION) // OK
02548                         {
02549                                 int k = 0;
02550                                 ++k;
02551                                 // delete it: set its parent to 0
02552                                 ASSERT(0); // notify user
02553                                 setPointer( obj, ATTRID_PARENT, 0);
02554                         }
02555                 }
02556 
02557                 if( it->m_object->m_metaid == DTID_REFERENCE && it->m_attrib == ATTRID_REFERENCE) // 505
02558                 {
02559                         // referred object is not found
02560                         int k = 0;
02561                         ++k;
02562                         ASSERT(0); // notify user
02563                         // involved connections may have existed... :(
02564                 }
02565                 if( it->m_attrib == ATTRID_PARENT) // 602
02566                 {
02567                         // a child Y may have been lost its parent X, when another user deleted model X, not knowing 
02568                         // that it has a Y child also
02569                         // this refers to the case when children are added without locking parent (when not used ATTRID_LASTRELID)
02570                         int k = 0;
02571                         ++k;
02572                         ASSERT(0); // notify user
02573                 }
02574         }
02575 #endif
02576 #endif
02577 }
02578 
02579 void CCoreXmlFile::resetSourceControlInfo( XmlObject * obj)
02580 {
02581         ASSERT( obj->m_metaid==DTID_MODEL || obj->m_metaid==DTID_FOLDER );
02582 
02583         // set FILESTATUS attributes
02584         AttribMapIter fsit = obj->m_attributes.find( ATTRID_FILESTATUS );
02585         if( obj->m_attributes.end() != fsit)
02586         {
02587                 XmlAttrLong * along = (XmlAttrLong*) fsit->second;
02588                 along->m_value &= 0xFFFFFF00; // clear my bits (the lowest byte)
02589         }
02590 }
02591 
02592 void CCoreXmlFile::resetSourceControlForAll()
02593 {
02594         // collect containers
02595         XmlObjVec containers;
02596         for( XmlObjVecIter it=m_objects.begin(); it!=m_objects.end(); ++it )
02597         {   
02598                 XmlObject * obj = (*it);
02599 
02600                 if( obj->m_metaid==DTID_MODEL || obj->m_metaid==DTID_FOLDER )
02601                         containers.push_back( obj );
02602         }
02603 
02604         for( XmlObjVecIter it=containers.begin(); it!=containers.end(); ++it )
02605         {
02606                 resetSourceControlInfo( *it );
02607                 resetSourceControlStat( *it, false );
02608         }
02609 }
02610 
02611 void CCoreXmlFile::resetSourceControlStat(XmlObject * obj, bool p_freshObj)
02612 {
02613         ASSERT( obj->m_metaid==DTID_MODEL || obj->m_metaid==DTID_FOLDER );
02614 
02615         // set FILESTATUS attributes
02616         AttribMapIter fsit = obj->m_attributes.find( ATTRID_FILESTATUS );
02617         if( obj->m_attributes.end() != fsit)
02618         {
02619                 XmlAttrLong * along = (XmlAttrLong*) fsit->second;
02620                 along->m_value &= 0xFFFF00FF; // clear my flags (the 2nd byte)
02621 
02622                 // new objects get special status,
02623                 // otherwise the default is 0 (only for the 2nd byte)
02624                 int l = 1; 
02625                 l |= p_freshObj ? 4:8;
02626                 along->m_value |= ( p_freshObj ? FS_NOTYETSAVED : 0x0);
02627         }
02628 }
02629 
02630 void CCoreXmlFile::getPointer(XmlAttrPointer * attr, VARIANT * p)
02631 {
02632         ASSERT( attr!= NULL );
02633         metaobjidpair_type id;
02634         objIdFromObject( attr->m_parent, id );
02635         CopyTo(id, p);
02636 }
02637 
02638 void CCoreXmlFile::getCollection(XmlAttrCollection * attr, VARIANT * p)
02639 {
02640         ASSERT( attr!= NULL );
02641         std::vector<metaobjidpair_type> idpairs;
02642         for( XmlObjSetIter it = attr->m_children.begin(); it != attr->m_children.end(); ++it )
02643         {
02644                 metaobjidpair_type id;
02645                 objIdFromObject( *it, id );
02646                 idpairs.push_back( id );
02647         }
02648         CopyTo(idpairs, p);
02649 }
02650 
02651 // TODO: test!
02652 XmlObject * CCoreXmlFile::getContainer(XmlObject * obj)
02653 {
02654         ASSERT( obj != NULL );
02655         XmlObject * container = obj;
02656         while( container!=NULL && !container->isContainer() )
02657         {
02658                 ParentMap::iterator it = m_parentMap.find( container->m_metaid );
02659                 ASSERT( it != m_parentMap.end() );
02660                 AttribMapIter it2 = container->m_attributes.find( it->second );
02661                 ASSERT( it2 != container->m_attributes.end() );
02662                 container = ((XmlAttrPointer*)(it2->second))->m_parent;    
02663         }
02664         return container;
02665 }
02666 
02667 // TODO: test!
02668 void CCoreXmlFile::getContainedObjects(XmlObject * obj, XmlObjVec& vec)
02669 {
02670         ASSERT( obj != NULL );
02671         vec.push_back(obj);
02672 
02673         for(AttribMapIter it=obj->m_attributes.begin(); it!=obj->m_attributes.end(); ++it)
02674         {
02675                 if( it->second->getType() == VALTYPE_COLLECTION )
02676                 {
02677                         XmlAttrCollection * coll = (XmlAttrCollection *)it->second;
02678                         for( XmlObjSetIter it2=coll->m_children.begin(); it2!=coll->m_children.end(); ++it2 )
02679                         {
02680                                 XmlObject * obj2 = (*it2);
02681                                 if( obj2->m_metaid != DTID_MODEL && obj2->m_metaid != DTID_FOLDER )
02682                                 {
02683                                         ParentMap::iterator it3 = m_parentMap.find( obj2->m_metaid );
02684                                         ASSERT( it3 != m_parentMap.end() );
02685                                         if( it3->first + ATTRID_COLLECTION == it->first )
02686                                                 getContainedObjects(obj2,vec);
02687                                 }
02688                         }
02689                 }
02690         }
02691 }
02692 
02693 void CCoreXmlFile::getMyDepObjConts( XmlObjSet& objects, XmlObjSet& containers, bool thorough)
02694 {
02695         XmlObjSet processedObjects;
02696         for( XmlObjSetIter it=objects.begin(); it!=objects.end(); ++it )
02697                 getMyDepObj( *it, containers, processedObjects, thorough );
02698 
02699         for( XmlObjSetIter jt = processedObjects.begin(); jt != processedObjects.end(); ++jt)
02700         {
02701                 XmlObject * cont = getContainer( *jt);
02702                 //ASSERT( cont); 
02703                 // cont can be 0, e.g. if an reference object is deleted, so its parent becomes 0, 
02704                 // then its ATTR_REF pointer is set to 0, that's why end up in this method
02705                 if( cont && containers.find( cont) == containers.end()) // not inserted yet
02706                         containers.insert( cont);
02707         }
02708 }
02709 
02710 void CCoreXmlFile::getMyDepObj(XmlObject * obj, XmlObjSet& containers, XmlObjSet& processedObjects, bool thorough)
02711 {
02712         if( obj!= NULL && processedObjects.find(obj)==processedObjects.end() )  // not processed yet
02713         {
02714                 processedObjects.insert(obj);
02715                 XmlObject * container = obj;//getContainer(obj);
02716                 if( container != NULL )
02717                 {
02718                         //containers.insert( container );
02719                         for(AttribMapIter it=container->m_attributes.begin(); thorough && it!=container->m_attributes.end(); ++it)
02720                         { 
02721                                 attrid_type at = it->first;
02722 
02723                                 // go through the collections only if non-trivial changes happened
02724                                 //
02725                                 // for a generic fco these collections are:
02726                                 //CREATE_COLLECTION(ATTRID_CONSTROWNER, "Constraints", "Constraints");
02727                                 //CREATE_COLLECTION(ATTRID_REGNOWNER, "RegNodes", "Registry Nodes");
02728                                 //CREATE_COLLECTION(ATTRID_ATTRPARENT,"Attributes", "Attributes");
02729                                 //CREATE_COLLECTION(ATTRID_REFERENCE, "References", "Referenced by");
02730                                 //CREATE_COLLECTION(ATTRID_XREF,                "XReferences", "Cross refs");
02731                                 //CREATE_COLLECTION(ATTRID_DERIVED, "SubTypes", "SubTypes/Instances"); 
02732                                 //
02733                                 // for a MODEL additonal collection is:
02734                                 //CREATE_COLLECTION(ATTRID_PARENT, "Children", "Child Objects");
02735                                 //
02736                                 // for a REFERENCE additional collection is:
02737                                 //CREATE_COLLECTION(ATTRID_SEGREF, "Segments", "Connection Segments");
02738                                 //CREATE_COLLECTION(ATTRID_MASTEROBJ, "MasterOf", "Master Of Objects");
02739                                 //
02740                                 // for a SET is
02741                                 //CREATE_COLLECTION(ATTRID_SETMEMBER, "Members", "Set Members");
02742 
02743                                 if(      at == ATTRID_PARENT      + ATTRID_COLLECTION) { } // this time children disregarded
02744                                 else if( at == ATTRID_CONSTROWNER + ATTRID_COLLECTION) { }
02745                                 else if( at == ATTRID_REGNOWNER   + ATTRID_COLLECTION) { }
02746                                 else if( at == ATTRID_ATTRPARENT  + ATTRID_COLLECTION) { }
02747                                 else
02748                                         if( it->second->getType() == VALTYPE_COLLECTION)
02749                                         {
02750                                                 // XREF, DERIVED, REFERENCE, SEGREF, MASTEROBJ, SETMEMBER
02751                                                 XmlAttrCollection * coll = (XmlAttrCollection*)it->second;
02752                                                 for( XmlObjSetIter it2=coll->m_children.begin(); it2!=coll->m_children.end(); ++it2 )
02753                                                 {
02754                                                         XmlObject * obj2 = (*it2);
02755                                                         if( obj2 != NULL )
02756                                                                 getMyDepObj( obj2, containers, processedObjects, thorough );
02757                                                 }
02758                                         }
02759                         }
02760                 }
02761         }
02762 }
02763 
02764 void CCoreXmlFile::getBasesOfObj( XmlObject * obj, XmlObjSet& containers)
02765 {
02766         if( obj!= NULL)
02767         {
02768                 if( obj->m_metaid == DTID_MODEL && containers.find( obj) == containers.end())
02769                         containers.insert( obj);
02770 
02771                 AttribMapIter derattr = obj->m_attributes.find( ATTRID_DERIVED);
02772                 if( derattr != obj->m_attributes.end())
02773                 {
02774                         XmlObject * base = ((XmlAttrPointer*)(derattr->second))->m_parent;
02775                         if( base)
02776                         {
02777                                 getBasesOfObj( base, containers);
02778                         }
02779                 }
02780         }
02781 }
02782 
02783 void CCoreXmlFile::getBasesOf( XmlObjSet& objects, XmlObjSet& containers)
02784 {
02785         // collect baseobj, base of baseobj, ...
02786         for( XmlObjSetIter it=objects.begin(); it!=objects.end(); ++it )
02787                 getBasesOfObj( *it, containers);
02788 }
02789 
02790 void CCoreXmlFile::getDeriveds( XmlObject * obj, XmlObjSet& containers)
02791 {
02792         if( obj!= NULL)
02793         {
02794                 for(AttribMapIter it=obj->m_attributes.begin(); it!=obj->m_attributes.end(); ++it)
02795                 { 
02796                         attrid_type at = it->first;
02797                         valtype_type vt = it->second->getType();
02798 
02799                         if( at == ATTRID_DERIVED + ATTRID_COLLECTION && it->second->getType() == VALTYPE_COLLECTION) // all deriveds
02800                         {
02801                                 XmlAttrCollection * coll = (XmlAttrCollection*)it->second;
02802                                 for( XmlObjSetIter it2=coll->m_children.begin(); it2!=coll->m_children.end(); ++it2 )
02803                                 {
02804                                         XmlObject * obj2 = (*it2);
02805                                         if( obj2 != NULL && obj2->m_metaid == DTID_MODEL) // folders cannot be derived
02806                                         {
02807                                                 if( obj2->m_metaid == DTID_MODEL && containers.find( obj2) == containers.end())
02808                                                         containers.insert( obj2);
02809                                                 getDeriveds( obj2, containers); // find deriveds from deriveds also
02810                                         }
02811                                 }
02812                         }
02813                 }
02814         }
02815 }
02816 
02817 void CCoreXmlFile::getAllUpAndDown( XmlObjSet& objects, XmlObjSet& containers)
02818 {
02819         // first go up
02820         // let's not go up
02821         // we don't go up anymore
02822         for( XmlObjSetIter it=objects.begin(); 0 && it!=objects.end(); ++it )
02823         {
02824                 XmlObject * iobj = *it;
02825                 XmlObject * cont = 0;
02826                 AttribMapIter ait;
02827                 while( iobj)
02828                 {
02829                         ait = iobj->m_attributes.find( ATTRID_PARENT );
02830                         ASSERT( ait != iobj->m_attributes.end() );
02831                         cont = ((XmlAttrPointer*)(ait->second))->m_parent;
02832 
02833                         if( cont && cont->m_metaid == DTID_MODEL)
02834                         {
02835                                 if( cont && containers.find( cont) == containers.end()) // not inserted yet
02836                                         containers.insert( cont);
02837                                 iobj = cont;
02838                         }
02839                         else // 0 or DTID_FOLDER
02840                                 iobj = 0;
02841                 }
02842         }
02843 
02844         // then go down
02845         for( XmlObjSetIter it=objects.begin(); it!=objects.end(); ++it )
02846                 getAllTheWayDown( *it, containers);
02847 }
02848 
02849 void CCoreXmlFile::getAllTheWayDown( XmlObject * obj, XmlObjSet& containers)
02850 {
02851         if( obj!= NULL)
02852         {
02853                 if( obj->m_metaid == DTID_MODEL && containers.find( obj) == containers.end())
02854                         containers.insert( obj);
02855                 for(AttribMapIter it=obj->m_attributes.begin(); it!=obj->m_attributes.end(); ++it)
02856                 { 
02857                         attrid_type at = it->first;
02858                         valtype_type vt = it->second->getType();
02859 
02860                         if( at == ATTRID_PARENT + ATTRID_COLLECTION && it->second->getType() == VALTYPE_COLLECTION)// children
02861                         {
02862                                 XmlAttrCollection * coll = (XmlAttrCollection*)it->second;
02863                                 for( XmlObjSetIter it2=coll->m_children.begin(); it2!=coll->m_children.end(); ++it2 )
02864                                 {
02865                                         XmlObject * obj2 = (*it2);
02866                                         if( obj2 != NULL && ( obj2->m_metaid == DTID_MODEL || obj2->m_metaid == DTID_FOLDER))
02867                                                 getAllTheWayDown( obj2, containers);
02868                                 }
02869                         }
02870                 }
02871         }
02872 }
02873 
02874 void CCoreXmlFile::getCheckOutContainers(XmlObjSet& objects, XmlObjSet& containers, bool thorough)
02875 {
02876         XmlObjSet processedObjects;
02877         for( XmlObjSetIter it=objects.begin(); it!=objects.end(); ++it )
02878                 getDependentContainers( *it, containers, processedObjects, thorough );
02879 }
02880 
02881 void CCoreXmlFile::getDependentContainers(XmlObject * obj, XmlObjSet& containers, XmlObjSet& processedObjects, bool thorough)
02882 {
02883         if( obj!= NULL && processedObjects.find(obj)==processedObjects.end() )  // not processed yet
02884         {
02885                 processedObjects.insert(obj);
02886                 XmlObject * container = getContainer(obj);
02887                 if( container != NULL )
02888                 {
02889                         // TODO: to be though over thoroughly. E.g. if there is deletion
02890                         // do the attributes change in such an order, that not all containers get into
02891                         // the list (as the relationships cease to exist we lose the chance to find 
02892                         // the container)
02893                         // Do we need to store the container??? Think about it!
02894                         containers.insert( container );
02895                         for(AttribMapIter it=container->m_attributes.begin(); (m_userOpts.m_alwaysFullLock || m_fullLockNeeded || thorough) && it!=container->m_attributes.end(); ++it)
02896                         { 
02897                                 attrid_type at = it->first;
02898                                 valtype_type vt = it->second->getType();
02899 
02900                                 // this optimizes it:
02901                                 if(      at == ATTRID_CONSTROWNER + ATTRID_COLLECTION) { } // no need for all of these
02902                                 else if( at == ATTRID_REGNOWNER   + ATTRID_COLLECTION) { }
02903                                 else if( at == ATTRID_ATTRPARENT  + ATTRID_COLLECTION) { }
02904                                 else if( it->second->getType() == VALTYPE_COLLECTION) // go through the collections 
02905                                 {
02906                                         XmlAttrCollection * coll = (XmlAttrCollection*)it->second;
02907                                         for( XmlObjSetIter it2=coll->m_children.begin(); it2!=coll->m_children.end(); ++it2 )
02908                                         {
02909                                                 XmlObject * obj2 = (*it2);
02910                                                 if( obj2 != NULL )
02911                                                         getDependentContainers( obj2, containers, processedObjects );
02912                                         }
02913                                 }
02914                         }
02915                 }
02916         }
02917 }
02918 
02919 /* 
02920 Ask user if files need to be checked out.
02921 
02922 Returns true if all the files has been checked out successfully.
02923 */
02924 bool CCoreXmlFile::checkOutFiles(XmlObjSet& containers)
02925 {
02926         AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
02927 
02928         if( containers.size() == 0 )
02929                 return true;
02930 
02931 #ifdef _DEBUG
02932 #if(DETAILS_ABOUT_XMLBACKEND)
02933         char buff[329];
02934         sprintf( buff, " ..--== size[ModifiedObjects] = %i, size[checkOutContainers] = %i ==--..", m_modifiedObjects.size(), containers.size());
02935         sendMsg( buff, MSG_INFO);
02936 #endif
02937 #endif
02938 
02939         // PETER - SVNSPEEDHACK BEGIN
02940         if( m_sourceControl == SC_SUBVERSION) {
02941                 XmlObjSetIter it;
02942                 std::vector< std::string> readOnlyFiles;
02943 
02944                 for( it=containers.begin(); it!=containers.end(); ++it ) {
02945                         if( isContainerReadOnly(*it) ){
02946                                 string fileName;
02947                                 getContainerFileName( *it, fileName, true );
02948                                 readOnlyFiles.push_back( fileName );
02949                         }
02950                 }
02951 
02952                 if (readOnlyFiles.empty()) {
02953                         return true;
02954                 }
02955 
02956                 std::string msg;
02957                 bool succ = m_svn->speedLock(readOnlyFiles, msg);
02958 
02959                 if (succ) {
02960                         return true;
02961                 }
02962                 else {
02963                         sendMsg( "Could not check out all files needed to complete operation.", MSG_ERROR);
02964                         sendMsg( msg, MSG_INFO);
02965                         sendMsg( "Rollback follows.", MSG_INFO);
02966                         return false;
02967                 }
02968         }
02969         // PETER - SVNSPEEDHACK END
02970 
02971         XmlObjSetIter it;
02972         XmlObjSet     containersUsedByOthers;
02973 
02974         // count the files need to be checked out
02975         XmlObjSet readOnlyFiles;
02976         bool      checkdOutByOthers = false;
02977         for( it=containers.begin(); it!=containers.end(); ++it )
02978         {
02979                 XmlObject * obj = *it;
02980                 ASSERT( obj != NULL );
02981 
02982                 if( isContainerReadOnly(obj) )
02983                 {
02984                         readOnlyFiles.insert( obj );
02985                         if( m_sourceControl != SC_NONE )
02986                         {
02987                                 // check if checked out by other users
02988                                 if( isContinerCheckedOut( obj ) )
02989                                 {
02990                                         containersUsedByOthers.insert( obj );
02991                                         checkdOutByOthers = true;
02992                                 }
02993                         }
02994                 }
02995         }
02996 
02997         if( readOnlyFiles.size() == 0 )
02998                 return true;
02999 
03000         // if there is no source control nothing we can do, notify the user and roll back transaction
03001         if( m_sourceControl == SC_NONE )
03002         {
03003                 AfxMessageBox( "Cannot perform this operation because some read only files need modification. Maybe the project is under source control. Try to open it again and login to the source control database!" );
03004                 return false;
03005         }
03006 
03007         // if there are files checkd out by other we are done, roll back transaction
03008         if( checkdOutByOthers )
03009         {
03010                 CFilesInUseDlg dlg; // plain dlg
03011                 if( dlg.DoModal() == IDOK )
03012                         showUsedFiles( containersUsedByOthers );
03013                 return false;
03014         }
03015 
03016         // ask user for check out confirmation
03017         if( m_userOpts.m_defCheckOutOnAction || AfxMessageBox( "To perform this operation some files will be checked out. Do you want to continue?", MB_YESNO ) == IDYES )
03018         {
03019                 if( m_userOpts.m_defCheckOutOnAction)
03020                         sendMsg( std::string( "ACCELERATION: Files checked out automatically based on policy configured."), MSG_INFO);
03021 
03022                 // don't create a mess by checking out modifications done by others
03023                 // commented by zolmol
03024                 //getLatestVersion();
03025 
03026                 XmlObjSet latent_files;
03027                 bool needClose = filesModifiedByOthersV3( readOnlyFiles, latent_files);
03028                 if( needClose)
03029                 {
03030                         //sendMsg( "This part of the project has been modified by other users, it is highly recomended to close and reopen your project to synchronize it!", MSG_ERROR );
03031                         //sendMsg( "Precise conflict, big chance of overlapping modifications. Not allowed!", MSG_ERROR );
03032                         sendMsg( "Update with save operation by other users changed this part of the project. Your changes are not allowed unless you close and reopen the project!", MSG_ERROR );
03033 
03034                         CFilesInUseDlg dlg( 0, true); // dlg with latent msg
03035                         if( dlg.DoModal() == IDOK )
03036                                 showUsedFiles( latent_files, true );
03037                         return false;
03038                 }
03039 
03040 
03041                 // check out files
03042                 try
03043                 {
03044                         for( it=readOnlyFiles.begin(); it!=readOnlyFiles.end(); ++it )
03045                                 checkOutContainer( *it );
03046 
03047                 }
03048                 catch(...)
03049                 {
03050                         // couldn't checkout all things, what's next?
03051                         // undocheckout or checkoutrollback needed for those which succesfully were checkedout
03052                         // roll back transaction, and notify user about what happened
03053                         sendMsg( "Could not check out all files needed to complete operation.", MSG_ERROR);
03054                         sendMsg( "Rollback follows.", MSG_INFO);
03055 
03056                         for( it=readOnlyFiles.begin(); it!=readOnlyFiles.end(); ++it )
03057                                 rollBackTheCheckOutContainer( *it ); // it has a try catch block inside, thus it is safe
03058 
03059                         sendMsg( "Rollback finished. See details above.", MSG_INFO);
03060                         return false;
03061                 }
03062 
03063 
03064                 return true;
03065         }
03066         else
03067                 return false;
03068 }
03069 
03070 XmlObject * CCoreXmlFile::objectFromObjId(metaobjidpair_type idpair)
03071 {
03072         if( idpair.metaid == METAID_NONE && idpair.objid == OBJID_NONE )
03073                 return NULL;
03074 
03075         if( idpair.metaid == METAID_ROOT )
03076                 return m_root;
03077         else
03078                 return (XmlObject*)idpair.objid;
03079 }
03080 
03081 void CCoreXmlFile::objIdFromObject(XmlObject * obj, metaobjidpair_type& idpair)
03082 {
03083         if( obj == NULL )
03084         {
03085                 idpair.metaid = METAID_NONE;
03086                 idpair.objid  = OBJID_NONE;
03087         }
03088         else
03089         {
03090                 idpair.metaid = obj->m_metaid;
03091                 if( idpair.metaid == METAID_ROOT )
03092                         idpair.objid = 1;
03093                 else
03094                         idpair.objid = (long)obj; // pointer to long conversion, is a truncation on 64bit systems
03095         }
03096 }
03097 
03098 void CCoreXmlFile::timestampOfCache( FILETIME* p_fTime)
03099 {
03100         WIN32_FILE_ATTRIBUTE_DATA attr;
03101         if( GetFileAttributesEx( m_cacheFileName.c_str(), GetFileExInfoStandard, &attr ) )
03102         {
03103                 *p_fTime = attr.ftLastWriteTime;
03104         }
03105 }
03106 
03107 void CCoreXmlFile::writeBinaryCache()
03108 {
03109         FILE * f = fopen( m_cacheFileName.c_str(), "wb" );
03110         if( f==NULL )
03111         {
03112                 sendMsg( "Exception: Could not create binary cache file!", MSG_ERROR);
03113                 HR_THROW(E_FILEOPEN);
03114         }
03115 
03116         XmlObjVecIter i;
03117         metaid_type   mid;
03118 
03119         // write out GUIDs and metaids
03120         int n = m_objects.size();
03121         fwrite( &n, sizeof(n), 1, f );
03122         for( i = m_objects.begin(); i != m_objects.end(); ++i )
03123         {
03124                 mid = (*i)->m_metaid;
03125                 fwrite( &mid, sizeof(mid), 1, f );
03126                 fwrite( &((*i)->m_guid), sizeof(GUID), 1, f );
03127         }
03128 
03129         // write out pointers
03130         int x;
03131         for( i=m_objects.begin(); i!=m_objects.end(); ++i )
03132         {   
03133                 XmlObject * obj = (*i);
03134                 for( AttribMapIter j=obj->m_attributes.begin(); j!=obj->m_attributes.end(); ++j)
03135                 {
03136                         if( j->second->getType() == VALTYPE_POINTER )
03137                         {
03138                                 XmlAttrPointer * pointer = (XmlAttrPointer*)j->second;
03139                                 if( pointer->m_parent == NULL )
03140                                         x = -1;
03141                                 else
03142                                         x = pointer->m_parent->m_index;
03143                                 fwrite( &x, sizeof(x), 1, f );
03144                         }
03145                 }
03146         }
03147 
03148         fclose(f);
03149 }
03150 
03151 bool CCoreXmlFile::readBinaryCache()
03152 {    
03153         FILE * f = fopen( m_cacheFileName.c_str(), "rb" );
03154         if( f==NULL )
03155                 return false;
03156 
03157         clearAll();
03158 
03159         // read GUIDs and metaids and create objects
03160         int n,i;
03161         fread( &n, sizeof(n), 1, f );
03162         for( i=0; i<n; ++i )
03163         {
03164                 metaid_type  metaid;
03165                 GUID         guid;
03166 
03167                 fread( &metaid, sizeof(metaid), 1, f );
03168                 fread( &guid, sizeof(GUID), 1, f );
03169 
03170                 CComObjPtr<ICoreMetaObject> metaobject;
03171                 COMTHROW( m_metaProject->get_Object(metaid, PutOut(metaobject)) );
03172 
03173                 XmlObject * obj = new XmlObject(metaobject,false);
03174                 obj->m_guid   = guid;
03175                 addObject(obj);
03176 
03177                 if( metaid == METAID_ROOT )
03178                         m_root = obj;
03179         }
03180 
03181         UnresolvedPointerVec pointers;
03182         int x;
03183         for( XmlObjVecIter it=m_objects.begin(); it!=m_objects.end(); ++it )
03184         {   
03185                 XmlObject * obj = (*it);
03186                 for( AttribMapIter j=obj->m_attributes.begin(); j!=obj->m_attributes.end(); ++j)
03187                 {
03188                         if( j->second->getType() == VALTYPE_POINTER )
03189                         {
03190                                 XmlAttrPointer * pointer = (XmlAttrPointer*)j->second;
03191 
03192                                 UnresolvedPointer p;
03193                                 p.m_object = obj;
03194                                 p.m_attrib = j->first;
03195 
03196                                 fread( &x, sizeof(x), 1, f );
03197                                 if( x == -1 )
03198                                         p.m_pointedObjGuid = GUID_NULL;
03199                                 else
03200                                         p.m_pointedObjGuid = m_objects[x]->m_guid;
03201 
03202                                 pointers.push_back( p );
03203                         }
03204                 }
03205         }
03206 
03207         resolvePointers( pointers );
03208 
03209         // read and set pointers
03210         /*int x;
03211         for( XmlObjVecIter it=m_objects.begin(); it!=m_objects.end(); ++it )
03212         {   
03213         XmlObject * obj = (*it);
03214         for( AttribMapIter j=obj->m_attributes.begin(); j!=obj->m_attributes.end(); ++j)
03215         {
03216         if( j->second->getType() == VALTYPE_POINTER )
03217         {
03218         XmlAttrPointer * pointer = (XmlAttrPointer*)j->second;
03219 
03220         fread( &x, sizeof(x), 1, f );
03221         if( x == 0 )
03222         pointer->m_parent = NULL;
03223         else
03224         pointer->m_parent = m_objects[x];
03225         }
03226         }
03227         }*/
03228 
03229 
03230 
03231         fclose(f);
03232 
03233         //updateCollections();
03234 
03235         return true;
03236 }
03237 
03238 void CCoreXmlFile::createProjectFile()
03239 {    
03240         // create projet file
03241         FILE * f = fopen( m_projectFileName.c_str(), "wt" );
03242         if( f == NULL )
03243         {
03244                 sendMsg( "Exception: Could not create project file '" + m_projectFileName + "'!", MSG_ERROR);
03245                 AfxMessageBox( (std::string( "Could not create file ") + m_projectFileName).c_str());
03246                 HR_THROW(E_FILEOPEN);
03247         }
03248         fprintf( f, "<GME " );
03249         if( m_svnUrl.size() > 0)
03250                 fprintf( f, "svn=\"%s\"", svnSshMangling( m_svnUrl).c_str());
03251         if( m_hashFileNames)
03252                 fprintf( f, " hash=\"true\" hval=\"%s\"", (m_hashVal == 5)?"1+2":(m_hashVal == 2)?"2": (m_hashVal == 3)? "3":"4");
03253         else
03254                 fprintf( f, " hash=\"false\"" );
03255         fprintf( f, ">\n" );
03256         fprintf( f, "</GME>\n" );
03257         fclose( f );
03258 
03259         if( isSV())
03260         {
03261                 bool s1 = addSVN( m_projectFileName);
03262                 bool s2 = commitSVN( m_projectFileName, std::string("auto: createProjectFile()"), true);
03263 
03264                 if( !s1 || !s2)
03265                 {
03266                         AfxMessageBox( "Subversion error! Cannot add project to Subversion. Errocode = 2!");
03267                 }
03268         }
03269 
03270         makeSureFileExistsInVerSys( OperatingOptions::m_sysConfName, isSV() ? OperatingOptions::m_sysConfDefContentsSvn : OperatingOptions::m_sysConfDefContentsPlain);
03271 }
03272 
03273 void CCoreXmlFile::readProjectFile()
03274 {
03275         // Project file is an xml file, with a GME tag.
03276         // possible attributes: VSSDatabase, VSSPath
03277         // example: <GME VSSDatabase="\\bogyom\GMEXMLBackEndTest\sorucesafedb\srcsafe.ini" VSSPath="$\test1"></GME>
03278 
03279         std::auto_ptr<DOMLSParser> parser(getFreshParser( "ProjectFileReader"));
03280 
03281         ASSERT( parser.get() != NULL );
03282         if(parser.get() == NULL)
03283         {
03284                 sendMsg( "Exception: Could not create parser!", MSG_ERROR);
03285                 HR_THROW(E_FILEOPEN);
03286         }
03287 
03288         std::auto_ptr<DOMErrorHandler> err_handler(new DOMErrorPrinter( &m_console));
03289         parser->getDomConfig()->setParameter(XMLUni::fgDOMErrorHandler, err_handler.get());
03290 
03291         bool fexists   = FileHelp::fileExist( m_projectFileName);
03292         bool suc       = false;
03293         XERCES_CPP_NAMESPACE::DOMDocument * doc = 0;
03294         if( fexists) doc = enclosedParse( m_projectFileName, parser.get(), &suc);
03295         if( !doc || !suc)
03296         {
03297                 //sendMsg( "Could not find or parse project file " + m_projectFileName, MSG_ERROR);
03298                 setFileNames( true); // reset the file name to a newly found one
03299                 doc = enclosedParse( m_projectFileName, parser.get(), &suc);
03300                 if( !doc || !suc)
03301                 {
03302                         if( fexists) sendMsg( "Could not parse project file '" + m_projectFileName + "'!", MSG_ERROR);
03303                         else         sendMsg( "Could not find project file '" + m_projectFileName + "' in directory " + m_folderPath, MSG_ERROR);
03304                         HR_THROW(E_FILEOPEN);
03305                 }
03306         }
03307         DOMElement * e = doc->getDocumentElement();
03308         if( e == NULL)
03309         {
03310                 sendMsg( "Null document element error during parsing of '" + m_projectFileName + "'!", MSG_ERROR);
03311                 HR_THROW(E_FILEOPEN);
03312         }
03313 
03314         smart_XMLCh x_vssDatabase = XMLString::transcode("VSSDatabase");
03315         smart_XMLCh x_svnLocator  = XMLString::transcode("svn");
03316         smart_XMLCh x_hashedDirs  = XMLString::transcode("hash");
03317         smart_XMLCh x_hashValue   = XMLString::transcode("hval");
03318 
03319         smart_Ch vssDatabase = XMLString::transcode(e->getAttribute( x_vssDatabase));
03320         smart_Ch svnLocator  = XMLString::transcode(e->getAttribute( x_svnLocator));
03321         smart_Ch hashedDirs  = XMLString::transcode(e->getAttribute( x_hashedDirs));
03322         smart_Ch hashValue   = XMLString::transcode(e->getAttribute( x_hashValue));
03323 
03324         if( !strcmp( hashedDirs, "true")) 
03325         {
03326                 m_hashFileNames = true;
03327                 m_hashInfoFound = true; // this will skip asking questions about hashing
03328 
03329                 if     ( !strcmp( hashValue, "4096"))m_hashVal = 5;
03330                 else if( !strcmp( hashValue, "256")) m_hashVal = 2;
03331                 else if( !strcmp( hashValue, "1+2")) m_hashVal = 5;
03332                 else if( !strcmp( hashValue, "2"))   m_hashVal = 2;
03333                 else if( !strcmp( hashValue, "3"))   m_hashVal = 3;
03334                 else if( !strcmp( hashValue, "4"))   m_hashVal = 4;
03335                 else m_hashInfoFound = false;     // unkown value
03336         }
03337         else if( !strcmp( hashedDirs, "false"))
03338         {
03339                 m_hashFileNames = false;
03340                 m_hashInfoFound = true; // this will skip asking questions about hashing
03341         }
03342 
03343         if( strlen( svnLocator) != 0 )
03344         {
03345                 m_svnUrl = svnLocator;
03346                 if( m_svnUrl != m_userOpts.m_prefUrl && !m_userOpts.m_prefUrl.empty()) // pref not empty and !equal
03347                 {
03348                         sendMsg( "Preferred url substitutes that loaded from the project file!", MSG_INFO);
03349                         m_svnUrl = m_userOpts.m_prefUrl;
03350                 }
03351 
03352                 m_vssUser = m_userOpts.m_useAccountInfo? m_userOpts.m_defUserName.c_str(): userNameFromSvnSshUrl();
03353                 m_vssPassword = m_userOpts.m_useAccountInfo? m_userOpts.m_defPassword.c_str(): "";
03354 
03355                 m_sourceControl = SC_SUBVERSION;
03356 
03357                 svnSetup( false); //false => won't throw if cancelled
03358                 // fills m_vssUser, m_vssPassword
03359 
03360                 // upon Open, the connection string might not have the full URL in it, so 
03361                 // that information is only available after readProjectFile (m_svnUrl)
03362         }
03363 }
03364 
03365 void CCoreXmlFile::writeAll()
03366 {
03367         for( XmlObjVecIter it=m_objects.begin(); it!=m_objects.end(); ++it )
03368         {
03369                 XmlObject * obj = (*it);
03370                 if( obj->isContainer() && obj->m_loaded )
03371                         writeXMLFile( obj );
03372 
03373                 // we would need to write only those which have been modified, right?
03374                 // yes! inside that method, files are open with 'w', which succeeds
03375                 // only if the file is checked out, so non-modified containers are not
03376                 // written out. Although not all checked out files are modified.
03377         }
03378 }
03379 
03380 void CCoreXmlFile::timeSync( const char * fileName, XmlObject * container)
03381 {
03382         // get last write time of the recently updated and _closed_ (!) file
03383         WIN32_FILE_ATTRIBUTE_DATA attr;        
03384         if( GetFileAttributesEx( fileName, GetFileExInfoStandard, &attr ))
03385         {
03386                 applyLastWrTime( container, true, CTime( attr.ftLastWriteTime ));
03387         }
03388 }
03389 
03390 void CCoreXmlFile::writeXMLFile(XmlObject * container)
03391 {
03392         if( !container->isContainer() )
03393                 HR_THROW(E_INVALID_USAGE);
03394 
03395         std::string fileName;
03396         getContainerFileName(container, fileName);
03397 
03398         // we wish we could predict the time the file will be closed
03399         // because that will become the file's 'Modified At' attribute
03400         // the obj->m_lastWriteTime needs to reflect exactly this time
03401         time_t        currentTime1 = time( &currentTime1 );
03402         CTime         currentTime2( currentTime1 + 1 );
03403 
03404         bool f_existed = false;
03405         if( FileHelp::isFileReadOnly2( fileName, &f_existed))
03406         {
03407                 return; // file exists, is read-only, no chance of writing into it
03408                         // it also means there was no change
03409         }
03410 
03411         // open file
03412         // previously the "w" mode used by fopen guarranteed that only read-write files are opened
03413         // now with Transcoder, exceptions will be thrown whenever a file can't be opened because it is read-only.
03414         // That's why it is a good thing to check (above) for read-onlyness
03415         Transcoder ofs;
03416         try
03417         {
03418                 bool deleted = false;
03419                 // Is this object deleted according to its current state?
03420                 // That's important to understand: if user does undo on a deleted object
03421                 // its state might return to normal, thus the deleted object might become
03422                 // undeleted at any time until the user closes the project.
03423                 // During projectclose the deleted state objects -at that time- are really
03424                 // discarded, see ::DeleteObject code for handling these.
03425                 AttribMapIter parit = container->m_attributes.find( ATTRID_PARENT);
03426                 if( parit != container->m_attributes.end() && container->m_metaid != DTID_ROOT)
03427                 {
03428                         // if a model has its parent 0, then it is deleted:
03429                         deleted = 0 == ((XmlAttrPointer*)(parit->second))->m_parent;
03430                 }
03431 
03432                 if( !deleted)
03433                 {
03434                         ofs.init( fileName.c_str(), "UTF-8"); // create a transcoder fstream
03435                         // Transcoder dumps by default the xml version and encoding
03436 
03437                         // write objects recursively
03438                         writeObject( container, ofs, true, "", currentTime2 );
03439                         ofs.finalize();
03440                 }
03441                 // deleted containers will have a file of 0 size
03442                 if( deleted) // really overwrite the file
03443                 {
03444                         std::ofstream zero; 
03445                         zero.open( fileName.c_str(), std::ios_base::out | std::ios_base::trunc); // 'w'
03446                         zero.close();
03447                 }
03448 
03449                 // now it's time to get file's exact 'Modified At' attribute
03450                 // which can be set into the objects as the real m_lastWriteTime
03451                 // alternative: if we could set the file's modification attribute
03452                 // to a certain time, we'd save the effort-time to go over the
03453                 // hierarchy (but the good news is that only the container's
03454                 // m_lastWriteTime needs to be updated, subobjects don't matter)
03455                 timeSync( fileName.c_str(), container);
03456 
03457                 // add to source control if not added yet
03458                 try
03459                 {
03460                         // TODO: a good candidate for another thread
03461                         addToSourceControl( container, f_existed );
03462                 }
03463                 catch(...)
03464                 {
03465                         sendMsg( "Failed while adding to version control file: " + fileName, MSG_WARNING);
03466                 }
03467         }
03468         catch( hresult_exception& e)
03469         {
03470                 if( e.hr == E_INVALID_FILENAME) // file could not be opened, thrown by Transcoder::init()
03471                         sendMsg( "Error while saving. Could not open file " + fileName, MSG_ERROR);
03472                 else
03473                         sendMsg( "Unknown error while saving file " + fileName, MSG_ERROR);
03474         }
03475         catch( ...)
03476         {
03477                 sendMsg( "Generic error while saving file " + fileName, MSG_ERROR);
03478         }
03479 }
03480 
03481 void CCoreXmlFile::applyLastWrTime(XmlObject * obj, bool container, CTime lastWriteTime )
03482 {
03483         obj->m_lastWriteTime = lastWriteTime;
03484 
03485         // apply the proper time for children too? currently only the container 
03486         // file times are compared (Versioning System's last checked in version vs.
03487         // the local file OR local file vs. the XmlObject's m_lastWriteTime)
03488         // so kids with no file associated need not have so precise time
03489 }
03490 
03491 void CCoreXmlFile::writeObject(XmlObject * obj, Transcoder& ofs, bool container, const char * prefix, CTime lastWriteTime )
03492 {        
03493         std::string                     str;
03494         CComObjPtr<ICoreMetaObject>     metaobject;
03495         CComBSTR                        metaToken;
03496 
03497         obj->m_lastWriteTime = lastWriteTime;
03498 
03499         COMTHROW( m_metaProject->get_Object( obj->m_metaid, PutOut(metaobject) ) );
03500         COMTHROW( metaobject->get_Token( &metaToken ) );
03501         guid2str( obj->m_guid, str );
03502 
03503         bool spec_root = metaToken == "Root"; // or: m_metaid==METAID_ROOT
03504 
03505         // the multiline case? for details see Mga\MgaGeneric.cpp
03506         // and http://escher.isis.vanderbilt.edu/JIRA/browse/GME-151 
03507         // those VALTYPE_STRING type of attributes which are shown in the GME 
03508         // environment as multiline strings are affected: StrValue and RegNodeValue
03509         // the objects which own these kinds of attibutes are: StrAttr and RegNode
03510         bool spec_care = metaToken == "StrAttr" || metaToken == "RegNode" || spec_root;
03511         std::string spec_value;
03512         std::string spec_preamble( spec_root?"Comment=":"");
03513         // there might be multiple attrs of <Root> which will be multiline (in the future)
03514         // thus, we with regard to that we need to distinguish the CDATA sections somehow.
03515         // only when Root is the owner, we will use a preable in the CDATA section for Comment
03516 
03517         ofs << Transcoder::NoEscape << prefix << "<" << metaToken << " MetaId=\"" << obj->m_metaid << "\" Id=\"" << str << "\"";
03518         if( obj->m_deleted )
03519                 ofs << " deleted=\"true\"";
03520 
03521         // write pointers, attributes
03522         AttribMapIter it;
03523         for( it=obj->m_attributes.begin(); it!=obj->m_attributes.end(); ++it )
03524         {
03525                 XmlAttrBase                    * attr = it->second;
03526                 CComObjPtr<ICoreMetaAttribute>   metaAttrib;
03527                 CComBSTR                         attribToken;
03528                 std::string                           attrVal;
03529 
03530                 COMTHROW( metaobject->get_Attribute( it->first, PutOut(metaAttrib) ) );
03531                 metaAttrib->get_Token( &attribToken );
03532                 if( attribToken == "fstate")
03533                 {   // do not dump file state attr into files
03534                         continue;
03535                 }
03536 
03537                 if( attr->getType() == VALTYPE_POINTER )
03538                 {
03539                         ParentMap::iterator it2 = m_parentMap.find( obj->m_metaid );
03540                         if( it2==m_parentMap.end() || it2->second!=it->first || container )
03541                         {
03542                                 XmlAttrPointer * pointer = (XmlAttrPointer*)attr;
03543                                 if( pointer->m_parent != NULL )
03544                                         guid2str( pointer->m_parent->m_guid, attrVal );
03545                         }
03546                 }
03547                 else if( attr->getType() != VALTYPE_COLLECTION && attr->getType() != VALTYPE_LOCK
03548                         && attr->getType() != VALTYPE_DICT)
03549                 {
03550                         XmlAttrBase * attr = it->second;
03551                         attr->toString(attrVal);
03552                 }
03553 
03554                 if( !attrVal.empty() )
03555                 {
03556                         // attribToken not converted any more with replaceSpaceWithUnderscore -- zolmol
03557 #ifdef DEBUG
03558                         std::string at; CopyTo( attribToken, at);
03559                         ATLASSERT(("Check MgaGeneric.cpp for attribute tokens containing spaces", at.find(' ') == -1));
03560 #endif
03561 
03562                         bool spec_attr = spec_care && (attribToken == "StrValue" || attribToken == "RegNodeValue" || attribToken == "Comment");
03563 
03564                         if( spec_attr) // spec_care is also true
03565                                 spec_value = attrVal; // store the (possibly multiline) original value, without encoding
03566                         else // regular dump:
03567                                 ofs << " " << attribToken << "=\"" << Transcoder::StdEscape << attrVal << Transcoder::NoEscape << "\"";
03568                 }
03569         }
03570         ofs << Transcoder::NoEscape << ">";
03571         if( spec_care)                                                    // right after the element, without whitespace, (in sync with readObject())
03572         {
03573                 // only a little attention is paid to possbile
03574                 // occurences of ']]>' in the data
03575                 if( spec_value.find( "]]>") != std::string::npos)
03576                 {
03577                         sendMsg( "Special character string ']]>' found among one element's properties, will be replaced by ']] >'", MSG_INFO);
03578                         spec_value.replace( spec_value.find( "]]>"), 3, "]] >");
03579                 }
03580                 // encoding not needed since it goes to CDATA
03581                 ofs << "<![CDATA[" << spec_preamble << spec_value << "]]>";
03582         }
03583         ofs << "\n";
03584 
03585 
03586         // write out children
03587         // child is written if it is not the root, a model or a folder
03588         // and the parent of the child is a us according to m_parentMap
03589         std::string newPrefix = prefix;
03590         newPrefix += "\t";
03591         for( it=obj->m_attributes.begin(); it!=obj->m_attributes.end(); ++it )
03592         {
03593                 XmlAttrBase * attr = it->second;
03594                 if( attr->getType() == VALTYPE_COLLECTION )
03595                 {
03596                         XmlAttrCollection * coll = (XmlAttrCollection*)attr;
03597                         for( XmlObjSetIter it2=coll->m_children.begin(); it2!=coll->m_children.end(); ++it2 )
03598                         {
03599                                 XmlObject * obj2 = (*it2);
03600                                 if( obj2!=NULL && !obj2->isContainer() )
03601                                 {                    
03602                                         ParentMap::iterator it3 = m_parentMap.find( obj2->m_metaid );
03603                                         ASSERT( it3 != m_parentMap.end() );
03604                                         if( it3->second + ATTRID_COLLECTION == it->first )
03605                                                 writeObject( obj2, ofs, false, newPrefix.c_str(), lastWriteTime );
03606                                 }
03607                         }
03608                 } 
03609                 else if (attr->getType() == VALTYPE_DICT)
03610                 {
03611                         CComObjPtr<ICoreMetaAttribute> metaAttrib;
03612                         CComBSTR attribToken;
03613                         std::string attrVal;
03614 
03615                         COMTHROW( metaobject->get_Attribute( it->first, PutOut(metaAttrib) ) );
03616                         metaAttrib->get_Token( &attribToken );
03617                         XmlAttrDict* dict = (XmlAttrDict*)it->second;
03618                         CCoreDictionaryAttributeValue* dictValue = (CCoreDictionaryAttributeValue*)(ICoreDictionaryAttributeValue*)dict->m_value;
03619                         ofs << Transcoder::NoEscape << "<Dict token=\"" << attribToken << "\">";
03620                         for (auto i = dictValue->m_dict.begin(); i != dictValue->m_dict.end(); i++)
03621                         {
03622                                 ofs << Transcoder::NoEscape << "<Key>";
03623                                 ofs << Transcoder::StdEscape << static_cast<const char*>(_bstr_t(i->first));
03624                                 ofs << Transcoder::NoEscape << "</Key>";
03625                                 ofs << Transcoder::NoEscape << "<Value>";
03626                                 ofs << Transcoder::StdEscape << static_cast<const char*>(_bstr_t(i->second));
03627                                 ofs << Transcoder::NoEscape << "</Value>";
03628                         }
03629                         ofs << Transcoder::NoEscape << "</Dict>";
03630                 }
03631 
03632         }
03633 
03634         ofs << Transcoder::NoEscape << prefix << "</" << metaToken << ">\n";
03635 }
03636 
03637 
03638 void CCoreXmlFile::fullReadContainer(XmlObject * container)
03639 {
03640         std::string fileName;
03641         getContainerFileName(container, fileName);
03642 
03643         UnresolvedPointerVec pointers;
03644         readXMLFile( fileName.c_str(), pointers, true );
03645         // shouldn't we call this? probably not, since pointer skeletons have been loaded already
03646         // and all pointers are considered unresolved after readXMLFile()
03647         //resolvePointers( pointers);
03648         //ASSERT( pointers.size() == 0);
03649 }
03650 
03651 void CCoreXmlFile::readXMLFile( const char * fileName, UnresolvedPointerVec& pointers, bool fullLoad )
03652 {
03653         // get last write time
03654         WIN32_FILE_ATTRIBUTE_DATA attr;        
03655         BOOL res = GetFileAttributesEx( fileName, GetFileExInfoStandard, &attr );
03656 
03657         // attr is valid and filesize = 0
03658         if( res && attr.nFileSizeHigh == 0 && attr.nFileSizeLow == 0)
03659                 return;
03660 
03661         CTime lastWriteTime( attr.ftLastWriteTime );
03662 
03663         std::auto_ptr<DOMLSParser> parser;
03664         std::auto_ptr<DOMErrorHandler> err_handler;
03665         try
03666         {
03667                 DOMImplementationLS * domimpl = 0;
03668                 newDOMObjs( &domimpl, parser, err_handler);
03669 
03670                 if( !domimpl || !parser.get())
03671                 {
03672                         sendMsg( std::string( "Could not create parser for file ") + fileName + "!", MSG_ERROR);
03673                         HR_THROW(E_FILEOPEN);
03674                 }
03675 
03676                 bool suc;
03677                 // TODO: a good candidate for another thread...
03678                 XERCES_CPP_NAMESPACE::DOMDocument * doc = enclosedParse( fileName, parser.get(), &suc);
03679                 if( !doc || !suc)
03680                 {
03681                         sendMsg( std::string( "Could not parse file ") + fileName + "!", MSG_ERROR);
03682                         HR_THROW(E_FILEOPEN);
03683                 }
03684 
03685                 DOMElement * doc_e = doc->getDocumentElement();
03686                 if( doc_e == NULL)
03687                 {
03688                         // todo linenumbers
03689                         // this might be totally useless
03690                         bool might_be_ok = false;
03691                         DOMNodeList* list = doc->getChildNodes();
03692 
03693                         if( list) for( int i = (int) list->getLength() - 1; i >= 0 ; --i)
03694                         {
03695                                 might_be_ok = true;
03696                                 DOMNode * node = list->item(i);
03697                                 if( node->getNodeType() == DOMNode::TEXT_NODE)
03698                                 {
03699                                         DOMText * txt  = (DOMText*) node;
03700                                         const XMLCh* p = txt->getData();
03701                                 }
03702                                 if( node->getNodeType() == DOMNode::ELEMENT_NODE)
03703                                 {
03704                                         DOMElement* elem = (DOMElement*)node;
03705                                         const XMLCh* tn = elem->getTagName();
03706                                 }
03707                         }
03708                         if( !might_be_ok)
03709                                 sendMsg( std::string( "Null document element was found while parsing file ") + fileName + "!", MSG_ERROR);
03710 
03711                         // reload it, might be nonzero now (only in fairy tales probably)
03712                         doc_e = doc->getDocumentElement();
03713                 }
03714 
03715                 if( doc_e == NULL)
03716                 {
03717                         sendMsg( std::string( "Exception: Null document element during parsing of ") + fileName + "!", MSG_ERROR);
03718                         HR_THROW(E_FILEOPEN);
03719                 }
03720 
03721                 readObject( doc_e, pointers, NULL, fullLoad, lastWriteTime );
03722         }
03723         catch(...)
03724         {
03725                 sendMsg( std::string( "Exception during reading ") + fileName + " file!", MSG_ERROR);
03726                 HR_THROW(E_FILEOPEN);
03727         }
03728 }
03729 
03730 void CCoreXmlFile::readObject(DOMElement * e, UnresolvedPointerVec& pointers, XmlObject * parent, bool fullLoad, CTime lastWriteTime )
03731 {   
03732         if( e == NULL)
03733         {
03734                 sendMsg( "Exception: readObject invoked with null element!", MSG_ERROR);
03735                 HR_THROW(E_FILEOPEN);
03736         }
03737         // what is the deleted container policy? if XML attribute is used code below is needed
03738 #ifdef DEBUG
03739         // if the object is deleted do not deal with it
03740         // possible optimization: limit analysis to ( parent == 0) case only (when incoming parameter is 0)
03741         const XMLCh* x_deleted = ParserLiterals::Main::deleted;
03742         smart_Ch deletedStr = XMLString::transcode( e->getAttribute( x_deleted));
03743         bool deleted = (strcmp( deletedStr, "true" ) == 0);
03744         if( deleted)
03745         {
03746                 ASSERT(0); // deleted XML attribute is no longer present in deleted XML files
03747                 return;
03748         }
03749 #endif
03750 
03751         const XMLCh* x_metaId = ParserLiterals::Main::metaId;
03752         const XMLCh* x_id     = ParserLiterals::Main::id;
03753 
03754         // get metaid, and id
03755         smart_Ch metaIdStr  = XMLString::transcode( e->getAttribute( x_metaId));
03756         smart_Ch objGUIDStr = XMLString::transcode( e->getAttribute( x_id));
03757 
03758         long metaid = atoi( metaIdStr);
03759         GUID   guid = str2guid( objGUIDStr );
03760 
03761         // is container deleted/obsolete? 
03762         // if nonrootfolder and its explicit parent must be 0
03763         if( metaid != METAID_ROOT && parent == 0)
03764         {
03765                 // is its XML Parent attribute empty
03766                 const XMLCh* x_parent       = ParserLiterals::Main::parent;
03767                 smart_Ch parent_xml_attr = XMLString::transcode( e->getAttribute( x_parent));
03768 
03769                 // is object obsolete?
03770                 bool obs = parent_xml_attr == 0 || strcmp( parent_xml_attr, "") == 0;
03771 
03772                 if( obs) return;
03773         }
03774 
03775         // get meta object
03776         CComObjPtr<ICoreMetaObject> metaobject;
03777 #pragma warning( disable: 4244) // conversion from 'long' to 'short', possible loss of data
03778         COMTHROW( m_metaProject->get_Object( metaid, PutOut(metaobject) ) );
03779 #pragma warning( default: 4244) // conversion from 'long' to 'short', possible loss of data
03780 
03781         CComBSTR metaobj_token;
03782         COMTHROW(metaobject->get_Token(&metaobj_token));
03783 
03784         // the multiline case?  -----  for details see writeObject()
03785         bool          spec_care = !wcscmp(metaobj_token, L"StrAttr") || !wcscmp(metaobj_token, L"RegNode") || !wcscmp(metaobj_token, L"Root");
03786         std::string   spec_value;
03787         std::wstring  spec_valuew;
03788 
03789         // find or create object
03790         XmlObject * obj = NULL;
03791         GUIDToXmlObjectMapIter it = m_objectsByGUID.find( guid );
03792         if( it != m_objectsByGUID.end() )
03793         {
03794                 obj = it->second;
03795                 if( !obj->m_loaded && fullLoad )
03796                 {
03797                         obj->createAttributes(metaobject,XmlObject::ATTR_SECONDARY);
03798                         obj->m_loaded = true;
03799                 }
03800         }
03801         else
03802         {
03803                 obj = new XmlObject(metaobject,fullLoad);
03804                 obj->m_guid = guid;
03805                 addObject( obj );
03806                 if( metaid == METAID_ROOT )
03807                         m_root = obj;
03808         }
03809 
03810         obj->m_deleted = false;
03811         obj->m_lastWriteTime = lastWriteTime;
03812 
03813         // read attributes
03814         AttribMapIter it2;
03815         for( it2 = obj->m_attributes.begin(); it2 != obj->m_attributes.end(); ++it2 )
03816         {        
03817                 CComObjPtr<ICoreMetaAttribute>  metaAttrib;
03818                 CComBSTR                        attribToken;
03819 
03820                 COMTHROW( metaobject->get_Attribute( it2->first, PutOut(metaAttrib) ) );
03821                 // TODO: memoize
03822                 COMTHROW( metaAttrib->get_Token( &attribToken ));        
03823 
03824                 // see http://escher.isis.vanderbilt.edu/JIRA/browse/GME-152
03825                 // it won't find 'MGA Version' of rootfolder if space2underscore and
03826                 // underscore2space conversions are in effect
03827                 const wchar_t* attrValW = e->getAttribute(attribToken);
03828                 smart_Ch attrVal = XMLString::transcode(attrValW);
03829 
03830                 // multiline case?
03831                 bool spec_value_found = false; // will ensure smooth upgrade from old style xmlbackend
03832                 bool spec_attr = spec_care && ( !wcscmp(attribToken, L"StrValue") || !wcscmp(attribToken, L"RegNodeValue") || !wcscmp(attribToken, L"Comment"));
03833                 if( spec_attr)
03834                 {
03835                         // the implementation below with getChildNodes() is more tolerant of XML COMMENTs, XML Whitespaces...
03836                         // than using the getFirstChild() method, which can be easily tricked by whitespaces
03837                         DOMNodeList * children = e->getChildNodes();
03838                         // find the first CDATA section among the kids: it should be the first node! (based on writeObject()'s impl)
03839                         for( int i = 0; i < (int) children->getLength(); ++i )
03840                         {
03841                                 DOMNode * node = children->item(i);
03842                                 if( node->getNodeType() == DOMNode::CDATA_SECTION_NODE ) // the first CDATA element is taken
03843                                 {
03844                                         const XMLCh* text = ((DOMCDATASection*)node)->getTextContent();
03845                                         smart_Ch sp_va = XMLString::transcode(text);
03846 
03847                                         spec_valuew = text;
03848                                         spec_value = sp_va;
03849                                         spec_value_found = true;
03850 
03851                                         break;
03852                                 }
03853                         }
03854                 }
03855 
03856                 // removed unnecessary conversion of attrVal with replaceUnderscoreWithSpace! -- zolmol
03857 
03858                 XmlAttrBase * attr = it2->second;
03859                 if( attr->getType() == VALTYPE_POINTER )
03860                 {
03861                         UnresolvedPointer p;
03862                         p.m_object = obj;
03863                         p.m_attrib = it2->first;          
03864                         if( attrVal==NULL || strlen(attrVal)==0 )
03865                                 p.m_pointedObjGuid = GUID_NULL;
03866                         else
03867                                 p.m_pointedObjGuid = str2guid( attrVal );
03868                         pointers.push_back( p );
03869                 }
03870                 else if( attr->getType() != VALTYPE_LOCK && attr->getType() != VALTYPE_COLLECTION
03871                         && attr->getType() != VALTYPE_DICT)
03872                 {
03873                         it2->second->fromString(attrVal, attrValW);
03874 
03875                         // use the spec_value only if really found the CDATA node
03876                         if( spec_attr && spec_value_found) // spec_care is also true
03877                         {
03878                                 if (!wcscmp(attribToken, L"Comment"))
03879                                 {
03880                                         const std::string comm_spec = "Comment=";
03881                                         if( 0 == spec_value.find( comm_spec)) // CDATA section looks like this: <![CDATA[Comment=....]]>
03882                                                 it2->second->fromString( spec_value.substr( comm_spec.length()).c_str(), NULL);
03883                                         else
03884                                                 ASSERT(0);
03885                                 }
03886                                 else
03887                                         it2->second->fromString( spec_value.c_str(), NULL);
03888                         }
03889                 }
03890         }
03891 
03892         // implicit parent pointer
03893         if( parent != NULL )
03894         {
03895                 ParentMap::iterator it3 = m_parentMap.find( obj->m_metaid );
03896                 UnresolvedPointer p;
03897                 p.m_object = obj;
03898                 p.m_attrib = it3->second;
03899                 p.m_pointedObjGuid = parent->m_guid;
03900                 pointers.push_back(p);
03901         }
03902 
03903         // read children
03904         DOMNodeList * children = e->getChildNodes();
03905         for( int i=0; i< (int) children->getLength(); ++i )
03906         {
03907                 DOMNode * node = children->item(i);
03908                 //if spec_care was true the first child was CDATA, but we process anyway the ELEMENTs only
03909                 if( node->getNodeType() == DOMNode::ELEMENT_NODE )
03910                 {
03911                         if (wcscmp(node->getLocalName(), L"Dict") == 0)
03912                         {
03913                                 // TODO: read token attribute (but only regnodes are stored in Dict for now)
03914                                 auto it = obj->m_attributes.find(ATTRID_REGNODE);
03915                                 if (it == obj->m_attributes.end())
03916                                         COMTHROW(E_FILEOPEN);
03917                                 XmlAttrDict* dict = (XmlAttrDict*)it->second;
03918                                 CCoreDictionaryAttributeValue* dictValue = (CCoreDictionaryAttributeValue*)(ICoreDictionaryAttributeValue*)dict->m_value;
03919                                 DOMNodeList* dictEntries = node->getChildNodes();
03920                                 for (XMLSize_t i = 0; i+1 < dictEntries->getLength(); i += 2)
03921                                 {
03922                                         if (dictEntries->item(i)->getNodeType() != DOMNode::ELEMENT_NODE)
03923                                                 COMTHROW(E_FILEOPEN);
03924                                         if (dictEntries->item(i+1)->getNodeType() != DOMNode::ELEMENT_NODE)
03925                                                 COMTHROW(E_FILEOPEN);
03926                                         if (((DOMElement*)dictEntries->item(i))->getChildNodes()->getLength() != 1)
03927                                                 COMTHROW(E_FILEOPEN);
03928                                         CComBSTR value;
03929                                         if (((DOMElement*)dictEntries->item(i+1))->getChildNodes()->getLength() == 1)
03930                                         {
03931                                                 if (((DOMElement*)dictEntries->item(i+1))->getChildNodes()->item(0)->getNodeType() != DOMNode::TEXT_NODE)
03932                                                         COMTHROW(E_FILEOPEN);
03933                                                 DOMText* valueText = (DOMText*)((DOMElement*)dictEntries->item(i+1))->getChildNodes()->item(0);
03934                                                 value = valueText->getData();
03935                                         }
03936                                         if (((DOMElement*)dictEntries->item(i))->getChildNodes()->item(0)->getNodeType() != DOMNode::TEXT_NODE)
03937                                                 COMTHROW(E_FILEOPEN);
03938                                         DOMText* key = (DOMText*)((DOMElement*)dictEntries->item(i))->getChildNodes()->item(0);
03939                                         dictValue->m_dict.insert(CCoreDictionaryAttributeValue::map_type::value_type(key->getData(), std::move(value)));
03940                                 }
03941                         }
03942                         else
03943                                 readObject( (DOMElement*)node, pointers, obj, fullLoad, lastWriteTime );
03944                 }
03945         }
03946 }
03947 
03948 void CCoreXmlFile::loadFrom( const std::string& p_dir, UnresolvedPointerVec& p_pointers, bool p_fullLoad )
03949 {
03950         char                 buf[_MAX_PATH];
03951         _finddata_t          fileInfo;
03952 
03953         sprintf( buf, "%s\\*.xml", p_dir.c_str() );
03954 
03955         long searchHandle = _findfirst( buf, &fileInfo );
03956         long ret = searchHandle;
03957         while( ret != -1 )
03958         {
03959                 sprintf( buf, "%s\\%s", p_dir.c_str(), fileInfo.name );
03960                 readXMLFile( buf, p_pointers, p_fullLoad );
03961                 ret = _findnext( searchHandle, &fileInfo );
03962         }
03963         _findclose( searchHandle );
03964 }
03965 
03966 void CCoreXmlFile::loadDirs( const std::string& p_dir, UnresolvedPointerVec& p_pointers, bool p_fullLoad )
03967 {
03968         char                 buf[_MAX_PATH];
03969         _finddata_t          item;
03970 
03971         loadFrom( p_dir, p_pointers, p_fullLoad); // load .xml files if any
03972 
03973         // find all subdirs
03974         sprintf( buf, "%s\\*", p_dir.c_str() );
03975         long searchHandle = _findfirst( buf, &item );
03976         long ret = searchHandle;
03977         while( ret != -1 )
03978         {
03979                 if( (item.attrib & _A_SUBDIR) == _A_SUBDIR)
03980                 {
03981                         std::string f( item.name);
03982                         if( f != ".." && f != ".")
03983                         {
03984                                 sprintf( buf, "%s\\%s", p_dir.c_str(), item.name );
03985                                 //sendMsg( buf, MSG_INFO);
03986                                 //readXMLFile( buf, pointers, fullLoad );
03987 
03988                                 // invoke loadDirs for the subdir
03989                                 loadDirs( buf, p_pointers, p_fullLoad);
03990                         }
03991                 }
03992                 ret = _findnext( searchHandle, &item );
03993         }
03994         _findclose( searchHandle );
03995 }
03996 
03997 
03998 void CCoreXmlFile::readAll( bool fullLoad )
03999 {
04000         UnresolvedPointerVec pointers;
04001 
04002         UpdateProgress(_T("Parse XML database"));
04003 
04004         // todo: preconditions
04005 #ifdef _DEBUG
04006 #if(DETAILS_ABOUT_XMLBACKEND)
04007         if( m_userOpts.m_measureTime) 
04008         {  
04009                 sendMsg( std::string( "Loading ") + m_folderPath, MSG_INFO);
04010         }
04011 #endif
04012 #endif
04013 
04014         clearAll();
04015 
04016         // load all dirs
04017         loadDirs( m_folderPath, pointers, fullLoad);
04018 
04019 #ifdef _DEBUG
04020 #if(DETAILS_ABOUT_XMLBACKEND)
04021         if( m_userOpts.m_measureTime) { sendMsg( "resolvePointers", MSG_INFO); }
04022 #endif
04023 #endif
04024 
04025         resolvePointers( pointers );
04026 
04027 #ifdef _DEBUG
04028 #if(DETAILS_ABOUT_XMLBACKEND)
04029         if( m_userOpts.m_measureTime) { sendMsg( "resetSourceControl", MSG_INFO); }
04030 
04031         _timeb b1, b2;
04032         _ftime( &b1);
04033 #endif
04034 #endif
04035 
04036         resetSourceControlForAll();
04037 
04038 #ifdef _DEBUG
04039 #if(DETAILS_ABOUT_XMLBACKEND)
04040         _ftime( &b2);
04041 
04042         if( m_userOpts.m_measureTime) {
04043                 char msg_buf[250];
04044                 sprintf( msg_buf, "[createSourceControlInfoRegNodes took = %li secs %hi millisecs]", b2.time-b1.time, b2.millitm-b1.millitm);
04045                 sendMsg( std::string( msg_buf), MSG_INFO);
04046         }
04047 #endif
04048 #endif
04049 
04050         if( m_userOpts.m_onLoadShowStatus)
04051         {
04052 #ifdef _DEBUG
04053 #if(DETAILS_ABOUT_XMLBACKEND)
04054                 if( m_userOpts.m_measureTime) { sendMsg( "updateSourceControlRegnodes begn", MSG_INFO); }
04055 
04056                 _timeb b3, b4; 
04057 
04058                 _ftime( &b3);
04059 #endif
04060 #endif
04061                 updateSourceControlInfo(); // show current status upon 'OpenProject'
04062 
04063 #ifdef _DEBUG
04064 #if(DETAILS_ABOUT_XMLBACKEND)
04065                 _ftime( &b4);
04066 
04067                 if( m_userOpts.m_measureTime) { sendMsg( "updateSourceControlRegnodes done", MSG_INFO); }
04068 
04069                 if( m_userOpts.m_measureTime) { 
04070                         char msg_buf[250];
04071                         sprintf( msg_buf, "[UpdateSourceControlInfo took = %li secs %hi millisecs]", b4.time-b3.time, b4.millitm-b3.millitm);
04072                         sendMsg( std::string( msg_buf), MSG_INFO);
04073                 }
04074 #endif
04075 #endif
04076         }
04077         UpdateProgress(_T(" DONE.\r\n"));
04078 }
04079 
04080 void CCoreXmlFile::getLatestAndLoad()
04081 {
04082         char          buf[_MAX_PATH];
04083         _finddata_t   fileInfo;
04084         time_t        currentTime1 = time( &currentTime1 );
04085         CTime         currentTime2( currentTime1 - 100 );
04086 
04087         FILETIME      last_cache_write_time;
04088         timestampOfCache( &last_cache_write_time);
04089 
04090         // get latest version from source control
04091         getLatestVersion();
04092 
04093         // itarete on files and read new and modified files
04094         sprintf( buf, "%s\\*.xml", m_folderPath.c_str() );
04095         long searchHandle = _findfirst( buf, &fileInfo );
04096         long ret = searchHandle;
04097         while( ret != -1 )
04098         {
04099                 sprintf( buf, "%s\\%s", m_folderPath.c_str(), fileInfo.name );
04100 
04101                 WIN32_FILE_ATTRIBUTE_DATA attr;        
04102                 if( GetFileAttributesEx( buf, GetFileExInfoStandard, &attr ) )
04103                 {
04104                         // A GOOD TEST STILL NEEDED BELOW::::
04105                         // this test is also not good, because the cache write time although is older 
04106                         // then modelopen time (in which session the cache file was written out)
04107                         // but the cache file reflects only the state of the project in modelopen time
04108                         if( 1 == CompareFileTime( &attr.ftLastWriteTime, &last_cache_write_time)) // attr.ftLastWriteTime > last_cache_write_time
04109                         {
04110                                 // this is a new or modified file, read it
04111                                 UnresolvedPointerVec pointers;
04112                                 readXMLFile( buf, pointers, false );
04113                                 resolvePointers( pointers);
04114                         }
04115                         // earlier this was used:
04116                         // this might bave been a bad test:
04117                         //CTime modTime( attr.ftLastWriteTime );
04118                         //if( modTime >= currentTime2 ) { }
04119                 }
04120 
04121                 ret = _findnext( searchHandle, &fileInfo );
04122         }
04123         _findclose( searchHandle );
04124 }
04125 
04126 bool CCoreXmlFile::getUserCredentialInfo( int p_svnText, bool p_requireLogin)
04127 {
04128         bool aborted = false;
04129         if( p_svnText != 0)
04130         {
04131                 bool is_ssh_hinted = isUrlSvnSsh();
04132                 CSvnLoginDlg dlg( is_ssh_hinted? 2 : 0);
04133                 dlg.m_project  = m_projectFileName.c_str();
04134                 dlg.m_database = m_svnUrl.c_str();
04135                 dlg.m_user     = m_userOpts.m_useAccountInfo? m_userOpts.m_defUserName.c_str(): userNameFromSvnSshUrl().c_str();
04136                 dlg.m_password = m_userOpts.m_useAccountInfo? m_userOpts.m_defPassword.c_str(): "";
04137                 if( !is_ssh_hinted) // https etc.
04138                         dlg.disableSshOption();
04139 
04140                 if( m_userOpts.m_useAccountInfo && m_userOpts.m_automaticLogin || dlg.DoModal() == IDOK )
04141                 {
04142                         m_vssUser     = dlg.m_user;
04143                         m_vssPassword = dlg.m_password;
04144                         return true;
04145                 }
04146                 aborted = dlg.wasAborted();
04147         }
04148 
04149         // we are sure that not IDOK was pressed
04150         if( p_requireLogin)
04151                 AfxMessageBox( "Could not process further without login information.", MB_ICONEXCLAMATION);
04152 
04153         if( aborted || p_requireLogin)
04154                 HR_THROW( E_UNKNOWN_STORAGE); // this will imply a relatively silent abort, with no further assertions
04155 
04156         return false;
04157 }
04158 
04159 bool CCoreXmlFile::isContainerReadOnly(XmlObject * obj)
04160 {
04161         ASSERT( obj != NULL );
04162 
04163         std::string fileName;
04164         getContainerFileName( obj, fileName );
04165 
04166         if( FileHelp::fileExist( fileName))
04167                 return FileHelp::isFileReadOnly( fileName);
04168         else
04169         {
04170                 // the file does not exiest (not written out yet)
04171                 return false;
04172         }
04173 }
04174 
04175 bool CCoreXmlFile::isContinerCheckedOut(XmlObject * obj)
04176 {
04177         ASSERT( m_sourceControl != SC_NONE );
04178 
04179         if( isSV())
04180         {
04181                 // freshly added, test this. 98765
04182                 std::string fileName;
04183                 getContainerFileName( obj, fileName, true );
04184                 return isCheckedOutByElseSVN( fileName);
04185         }
04186 
04187         return false;
04188 }
04189 
04190 void CCoreXmlFile::checkOutContainer(XmlObject * obj)
04191 {
04192         ASSERT( m_sourceControl != SC_NONE );
04193 
04194         if( isSV())
04195         {
04196                 std::string file_name;
04197                 getContainerFileName( obj, file_name, true);
04198                 applyLockSVN( file_name);
04199         }
04200 }
04201 
04202 void CCoreXmlFile::rollBackTheCheckOutContainer(XmlObject * obj)
04203 {
04204         ASSERT( m_sourceControl != SC_NONE );
04205 
04206         std::string fileName;
04207         getContainerFileName( obj, fileName);
04208 
04209         try
04210         {
04211                 if( isSV())
04212                 {
04213                         bool sc = removeLockSVN( fileName);
04214                         if( !sc) HR_THROW( E_FAIL);
04215                 }
04216         }
04217         catch(...)
04218         {
04219                 sendMsg( "Could not rollback the lock for " + fileName, MSG_WARNING);
04220         }
04221 }
04222 
04223 void CCoreXmlFile::addToSourceControl(XmlObject * container, bool p_fileExisted)
04224 {
04225         ASSERT( container->isContainer() );
04226 
04227         std::string fileName;
04228         getContainerFileName(container, fileName);
04229 
04230         if( isSV())
04231         {
04232                 bool sc_add = true;
04233                 bool sc_pro = true;
04234                 bool sc_com = true;
04235                 if( !p_fileExisted) // if( !isVersionedInSVN( fileName)) // do add and lockable only for new files
04236                 {
04237                         sc_add = addSVN( fileName);
04238                         sc_pro = lockablePropertySVN( fileName);
04239                         // TODO: use svn_client_propset_local to add locking to all the files
04240                 }
04241                 
04242                 if( !m_userOpts.m_useBulkCommit)                         // if bulk commit then avoid individual commits
04243                         sc_com = commitSVN( fileName, std::string("auto: addToSourceControl()"), !p_fileExisted);
04244 
04245                 if( !(sc_com && sc_pro && sc_add))
04246                 {
04247                         if(      !sc_add) sendMsg( "Could not add file " + fileName, MSG_ERROR);
04248                         else if( !sc_pro) sendMsg( "Could not set lockable property for " + fileName, MSG_ERROR);
04249                         else if( !sc_com) sendMsg( "Could not commit file " + fileName, MSG_ERROR);
04250                         throw hresult_exception(E_FAIL);
04251                 }
04252         }
04253 }
04254 
04255 /* ****************************************************************************** */
04256 /*        C L A S S     P R O T E C T E N T R Y                                   */
04257 /* ****************************************************************************** */
04258 ProtectEntry::ProtectEntry( GUID p_gd, OpCode p_op, CTime p_time)
04259 : m_guid( p_gd)
04260 , m_op( p_op)
04261 , m_time( p_time)
04262 { }
04263 
04264 /* ****************************************************************************** */
04265 /*        C L A S S     P U B L I C S T O R A G E                                 */
04266 /* ****************************************************************************** */
04267 PublicStorage::PublicStorage()
04268 : m_parent( 0)
04269 {
04270 }
04271 
04272 void PublicStorage::setParent( CCoreXmlFile* p_parent)
04273 {
04274         m_parent = p_parent;
04275 }
04276 
04277 void PublicStorage::init( const std::string& p_initialContent)
04278 {
04279         if( !m_parent)
04280                 return;
04281 
04282         if( !m_parent->makeSureFileExistsInVerSys( m_fileName, p_initialContent))
04283         {
04284                 m_parent->sendMsg( "Could not find files in Versioning System", MSG_ERROR);
04285                 return;
04286         }
04287 
04288         if( isSV())
04289                 m_ccsItem = m_localFileName.c_str(); // ok, as long as m_localFileName doesn't change
04290 }
04291 
04292 void PublicStorage::acquireSVN( const char * obj)
04293 {
04294         if( FileHelp::isFileReadOnly( obj))
04295         {
04296                 m_parent->updateSVN( obj); // SVN needs updating before any lock can be placed
04297                 m_parent->applyLockSVN( obj);
04298                 //ASSERT( !m_parent->isFileReadOnly( obj));
04299         }
04300 }
04301 
04302 void PublicStorage::releaseSVN( const char * obj)
04303 {
04304         bool sc = m_parent->commitSVN( obj, std::string("auto: PublicStorage::releaseSVN()"), false);
04305         ASSERT( sc);
04306         if( !sc)
04307         {
04308                 m_parent->sendMsg( std::string( "Could not commit file ") + obj, MSG_ERROR);
04309         }
04310 
04311         if( !FileHelp::isFileReadOnly( obj)) // probably the file did not change, so that's why the commit did not remove the lock
04312         {
04313                 bool sc = m_parent->removeLockSVN( obj);
04314                 ASSERT( sc);
04315                 ASSERT( FileHelp::isFileReadOnly( obj));
04316         }
04317 }
04318 
04319 void PublicStorage::acquireFile()
04320 {
04321         if( isSV())
04322         {
04323                 acquireSVN( m_ccsItem);
04324         }
04325 }
04326 
04327 void PublicStorage::releaseFile()
04328 {
04329         if( isSV())
04330         {
04331                 releaseSVN( m_ccsItem);
04332         }
04333 }
04334 
04335 std::string PublicStorage::userName() { return m_parent->userName(); }
04336 bool        PublicStorage::isSV()     { return m_parent->isSV();     }
04337 
04338 
04339 /* ****************************************************************************** */
04340 /*        C L A S S     S I G N M A N A G E R                                     */
04341 /* ****************************************************************************** */
04342 
04343 void SignManager::setParent( CCoreXmlFile* p_parent)
04344 {
04345         PublicStorage::setParent( p_parent);
04346 
04347         m_fileName = std::string(HelperFiles::sessionFolderName) + "/" + HelperFiles::signFileName;
04348         m_localFileName = m_parent->m_folderPath + "\\" + HelperFiles::sessionFolderName + "\\" + HelperFiles::signFileName;
04349 
04350         PublicStorage::init( "<users/>");
04351 }
04352 
04353 bool SignManager::anybodyElseHolding()
04354 {
04355         if( isSV())
04356         {
04357                 return m_parent->isCheckedOutByElseSVN( m_ccsItem);
04358         }
04359         return false;
04360 }
04361 
04362 void SignManager::in_or_off( bool in)
04363 {
04364         const char * msg_in = "Could not sign in yet. Press OK to try again.";
04365         const char * msg_out= "Could not sign out yet. Press OK to try again.";
04366 
04367         if(!isSV()) return;
04368         try
04369         {
04370                 bool lost_patience          = false; // once this will turn true further attempts will cease
04371                 bool successful_acquisition = false;
04372 
04373                 while( !successful_acquisition && !lost_patience)
04374                 {
04375                         bool chance_for_acquisition = false;
04376 
04377                         while( !chance_for_acquisition && !lost_patience)
04378                         {
04379                                 chance_for_acquisition = !anybodyElseHolding();
04380                                 if( !chance_for_acquisition)
04381                                 {
04382                                         // others hold a lock to the signin file
04383                                         // notify user and retry if needed
04384                                         if( IDCANCEL == AfxMessageBox( in?msg_in:msg_out, MB_OKCANCEL))
04385                                                 lost_patience = true; // quit, no further attempts
04386                                 }
04387                         }
04388 
04389                         if( chance_for_acquisition) // user did not give up, no one holding the file, chance to acquire it
04390                         {
04391                                 try {
04392                                         acquireFile();
04393                                         successful_acquisition = true;
04394                                 }
04395                                 catch(hresult_exception&) {
04396                                         // somebody else acquired it in the meantime
04397                                         if( IDCANCEL == AfxMessageBox( in?msg_in:msg_out, MB_OKCANCEL))
04398                                                 lost_patience = true; // quit, no further attempts
04399                                 }
04400                         }
04401                         // else: lost_patience turned true, so this loop will terminate too
04402                 }
04403 
04404                 if( successful_acquisition)
04405                 {
04406                         if( isSV()) {
04407                                 std::string owner;
04408                                 m_parent->infoSVN( this->m_ccsItem, false, std::string(), std::string(), owner);
04409                                 if( owner != userName()) {
04410                                         if( IDYES == AfxMessageBox( CString( "Username mismatch found. The signature file recently locked by you, reports to be locked by user: '") + owner.c_str() + "' while you have identified initially yourself as '" + userName().c_str() + "'.\nWould you like to continue with '" + owner.c_str() + "' username?", MB_YESNO)) {
04411                                                 m_parent->replaceUserName( owner);
04412                                         }
04413                                 }
04414                         }
04415 
04416                         try {
04417                                 update( in, SignFileEntry( userName(), CTime::GetCurrentTime()));
04418                         } catch(hresult_exception&) { // handled cases throw HRESULT from inside
04419                                 m_parent->sendMsg( "Could not update signature file! Exception happened.", MSG_ERROR);
04420                         }
04421 
04422                         releaseFile();
04423                 }
04424         }
04425         catch(...)
04426         {
04427                 AfxMessageBox( "Could not proceed with sign in/off. Exception happened.");
04428         }
04429 }
04430 
04431 void SignManager::update( bool p_in, const SignFileEntry& p_entry)
04432 {
04433         DOMImplementationLS    * domimpl = NULL;
04434         DOMLSParser             * parser  = m_parent->getFreshParser( "SignatureFileUpdater", &domimpl);
04435 
04436         ASSERT( parser != NULL );
04437         if( !parser) {
04438                 m_parent->sendMsg( "DOMBuilder pointer is NULL!", MSG_ERROR);
04439                 HR_THROW(E_FILEOPEN);
04440         }
04441 
04442         //
04443         // Parsing
04444         XERCES_CPP_NAMESPACE::DOMDocument * doc = 0;
04445         try {
04446 
04447                 doc = parser->parseURI( m_localFileName.c_str() );
04448 
04449         } 
04450         catch( const OutOfMemoryException&) {
04451 
04452                 doc = 0;
04453 
04454                 m_parent->sendMsg( "OutOfMemoryException during parsing.", MSG_ERROR);
04455         }
04456         catch (const SAXException&) { 
04457 
04458                 doc = 0;
04459 
04460                 m_parent->sendMsg( "SAXException during parsing.", MSG_ERROR);
04461         }
04462         catch (const XMLException& e) 
04463         {
04464                 doc = 0;
04465 
04466                 smart_Ch e_msg = XMLString::transcode( e.getMessage());
04467                 m_parent->sendMsg( (std::string( "XMLException during parsing. Message: ") + e_msg).c_str(), MSG_ERROR);
04468         }
04469         catch (const DOMException& e) 
04470         {
04471                 doc = 0;
04472 
04473                 const unsigned int maxChars = 2047;
04474                 XMLCh              errText[maxChars + 1];
04475 
04476                 if( DOMImplementation::loadDOMExceptionMsg( e.code, errText, maxChars))
04477                 {
04478                         smart_Ch e_msg = XMLString::transcode( errText);
04479                         m_parent->sendMsg( (std::string( "DOMException during parsing. Message: ") + e_msg).c_str(), MSG_ERROR);
04480                 }
04481                 else
04482                 {
04483                         smart_Ch e_msg = XMLString::transcode( e.getMessage());
04484                         m_parent->sendMsg( (std::string( "DOMException during parsing. Message: ") + e_msg).c_str(), MSG_ERROR);
04485                 }
04486         }
04487         catch (...) {
04488 
04489                 doc = 0;
04490 
04491                 m_parent->sendMsg( "GenException during parsing.", MSG_ERROR);
04492         }
04493 
04494         if( doc == NULL )
04495         {
04496                 m_parent->sendMsg( "DOMDocument pointer is NULL. Parsing of signature file failed.", MSG_ERROR);
04497                 HR_THROW(E_FILEOPEN);
04498         }
04499 
04500         //
04501         // The updating process itself:
04502         try
04503         {
04504                 DOMElement * doc_e = 0;
04505                 try {
04506                         doc_e = doc->getDocumentElement();
04507                 }
04508                 catch( const XMLException& ) { doc_e = 0; }
04509                 catch( const DOMException& ) { doc_e = 0; }
04510                 catch( ... )                  { doc_e = 0; }
04511 
04512                 if( doc_e == NULL)
04513                 {
04514                         m_parent->sendMsg( "Exception: Null document element in signature file!", MSG_ERROR);
04515                         HR_THROW(E_FILEOPEN);
04516                 }
04517 
04518                 const XMLCh* x_users = ParserLiterals::Signer::users;
04519                 ASSERT( XMLString::equals( x_users, doc_e->getTagName()));
04520 
04521                 const XMLCh* x_user = ParserLiterals::Signer::user;
04522 
04523                 DOMNodeList *uss = doc_e->getElementsByTagName( x_user);
04524 
04525                 const XMLCh* x_name = ParserLiterals::Signer::name;
04526                 const XMLCh* x_since= ParserLiterals::Signer::since;
04527                 const XMLCh* x_until= ParserLiterals::Signer::until;
04528                 const XMLCh* x_empty= ParserLiterals::empty;
04529                 const XMLCh* x_newln= ParserLiterals::newln;
04530 
04531                 smart_XMLCh x_time = XMLString::transcode( (LPCTSTR) p_entry.m_time.Format( _T("[%Y-%m-%d %H:%M:%S]")));
04532                 smart_XMLCh x_username = XMLString::transcode( p_entry.m_username.c_str());
04533 
04534                 bool found_already = false;
04535                 int len = (int) uss->getLength();
04536                 for( int i = 0; i < len; ++i)
04537                 {
04538                         DOMNode * node = uss->item(i);
04539                         DOMElement* us = (DOMElement*) node; // user
04540 
04541                         smart_Ch name = XMLString::transcode( us->getAttribute( x_name));
04542 
04543                         if( 0 == stricmp( name, p_entry.m_username.c_str())) // user info found
04544                         {
04545                                 if( found_already) // this is the 2nd entry with that username
04546                                 {
04547                                         doc_e->removeChild( node);
04548                                         continue;
04549                                 }
04550 
04551                                 // update accordingly
04552                                 if( p_in) // sign on
04553                                 {
04554                                         // found: already signed in
04555                                         // update this element
04556                                         us->setAttribute( x_since, x_time);
04557                                         us->setAttribute( x_until, x_empty);
04558                                         found_already = true; // no need for adding a new entry
04559                                 }
04560                                 else // sign off
04561                                 {
04562                                         // if entry is removed:
04563                                         //doc_e->removeChild( node);
04564 
04565                                         // update the logoff attribute
04566                                         us->setAttribute( x_until, x_time);
04567                                         found_already = true;
04568                                 }
04569                         }
04570                 }
04571 
04572                 // if this is the first time the user logs in (entry not found yet):
04573                 if( !found_already && p_in)
04574                 {
04575                         if( len == 0) // first ever user
04576                         {
04577                                 DOMText* ntxt = doc->createTextNode( x_newln);
04578                                 doc_e->appendChild( ntxt);
04579                         }
04580 
04581                         DOMElement *nch = doc->createElement( x_user);
04582                         nch->setAttribute( x_name , x_username);
04583                         nch->setAttribute( x_since, x_time);
04584                         nch->setAttribute( x_until, x_empty);
04585                         doc_e->appendChild( nch);
04586 
04587                         DOMText* ntxt = doc->createTextNode( x_newln);
04588                         doc_e->appendChild( ntxt);
04589                 }
04590 
04591         }
04592         catch(...)
04593         {
04594                 if( parser) delete parser;
04595                 m_parent->sendMsg( "Exception during signature file update!", MSG_ERROR);
04596 
04597                 HR_THROW(E_FILEOPEN);
04598         }
04599 
04600         //
04601         // do a DOM save as follows:
04602         try
04603         {
04604                 smart_XMLCh x_filenm = XMLString::transcode( m_localFileName.c_str());
04605                 XMLFormatTarget* outfile = new LocalFileFormatTarget( x_filenm);
04606                 DOMLSOutput* theOutput = domimpl->createLSOutput();
04607                 theOutput->setByteStream(outfile);
04608 
04609                 DOMLSSerializer* writer = domimpl->createLSSerializer();
04610                 if( writer && writer->getDomConfig()->canSetParameter( XMLUni::fgDOMXMLDeclaration, false))
04611                         writer->getDomConfig()->setParameter( XMLUni::fgDOMXMLDeclaration, false);
04612 
04613                 
04614                 
04615                 writer->write( doc, theOutput );
04616                 delete outfile;
04617                 delete writer;
04618                 theOutput->release();
04619                 // delete the parser object
04620                 delete parser;
04621         }
04622         catch(...)
04623         {
04624                 if( parser) delete parser;
04625                 m_parent->sendMsg( "DOMWriter exception during signature file update!", MSG_ERROR);
04626                 
04627                 
04628                 HR_THROW(E_FILEOPEN);
04629         }
04630 }
04631 
04632 SignManager::SignFileDataVec SignManager::getUserData()
04633 {
04634         SignFileDataVec res;
04635 
04636         DOMLSParser * parser = m_parent->getFreshParser( "SignatureFileAnalyzer");
04637 
04638         ASSERT( parser != NULL );
04639         if( !parser)
04640         {
04641                 m_parent->sendMsg( "Exception: Could not create parser!", MSG_ERROR);
04642                 return res;
04643         }
04644 
04645         try {
04646                 XERCES_CPP_NAMESPACE::DOMDocument * doc = parser->parseURI( m_localFileName.c_str());
04647                 if( doc == NULL )
04648                 {
04649                         m_parent->sendMsg( "Exception: Could not parse signature file!", MSG_ERROR);
04650                         HR_THROW(E_FILEOPEN);
04651                 }
04652 
04653                 DOMElement * doc_e = doc->getDocumentElement();
04654                 if( doc_e == NULL)
04655                 {
04656                         m_parent->sendMsg( "Exception: Null document element in signature file!", MSG_ERROR);
04657                         HR_THROW(E_FILEOPEN);
04658                 }
04659 
04660                 const XMLCh* x_users = ParserLiterals::Signer::users;
04661                 ASSERT( XMLString::equals( x_users, doc_e->getTagName()));
04662 
04663                 const XMLCh* x_user = ParserLiterals::Signer::user;
04664                 const XMLCh* x_name = ParserLiterals::Signer::name;
04665                 const XMLCh* x_since= ParserLiterals::Signer::since;
04666                 const XMLCh* x_until= ParserLiterals::Signer::until;
04667 
04668                 DOMNodeList *uss = doc_e->getElementsByTagName( x_user);
04669 
04670                 for( int i = 0; i < (int) uss->getLength(); ++i)
04671                 {
04672                         DOMNode * node = uss->item(i);
04673                         DOMElement* us = (DOMElement*) node; // user
04674 
04675                         smart_Ch name = XMLString::transcode( us->getAttribute( x_name));
04676                         smart_Ch since= XMLString::transcode( us->getAttribute( x_since));
04677                         smart_Ch until= XMLString::transcode( us->getAttribute( x_until));
04678 
04679                         SignFileData data = SignFileData(std::string(name), std::string(since), std::string(until));
04680                         if( std::find( res.begin(), res.end(), data) == res.end()) // not found
04681                                 res.push_back( data);
04682 
04683                 }
04684 
04685                 // delete the parser object
04686                 delete parser;
04687 
04688         } catch(...) {
04689                 if( parser) delete parser;
04690                 m_parent->sendMsg( "Parser exception during singature file analysis!", MSG_ERROR);
04691         }
04692 
04693         return res;
04694 }
04695 
04696 /* ****************************************************************************** */
04697 /*        C L A S S     P R O T E C T L I S T                                     */
04698 /* ****************************************************************************** */
04699 
04700 void ProtectList::setParent( CCoreXmlFile* p_parent)
04701 {
04702         PublicStorage::setParent( p_parent);
04703 
04704         m_fileName = getProtListFileName( userName());
04705         m_localFileName = m_parent->m_folderPath + "\\" + HelperFiles::sessionFolderName + "\\" + HelperFiles::protFileName + userName() + HelperFiles::protFileExt;
04706 
04707         PublicStorage::init( "<objects/>");
04708 }
04709 
04710 std::string ProtectList::getProtListFileName( const std::string& p_username)
04711 {
04712         return std::string( HelperFiles::sessionFolderName) + "/" + HelperFiles::protFileName + p_username + HelperFiles::protFileExt;
04713 }
04714 
04715 void ProtectList::onLoad()
04716 {
04717         ASSERT( m_parent);
04718         if( !m_parent) return;
04719         if(!isSV()) return;
04720 
04721         try
04722         {
04723                 acquireFile();
04724                 // those items which are older than the last sync time for all users
04725                 // can be removed from the list, the last sync time is the earliest of 
04726                 // all currently active logins
04727                 purgeProtList( m_parent->lastSyncTimeForAllUsers());
04728                 releaseFile();
04729         } catch(hresult_exception&) {
04730                 m_parent->sendMsg( "Could not purge old items from protection loglist.", MSG_ERROR);
04731         }
04732 }
04733 
04734 void ProtectList::onAborted()
04735 {
04736         if(!isSV()) return;
04737         clearProtList();
04738 }
04739 
04740 void ProtectList::onCommited()
04741 {
04742         if(!isSV()) return;
04743         if( !needed()) return;
04744 
04745         try {
04746                 acquireFile(); // this file is better checkedin after modifications, because other
04747                 // users strictly need to see the last (most up to date) version of it
04748 
04749                 writeProtList(); // much costly than text based writeProtLisp()
04750 
04751                 releaseFile();
04752 
04753                 clearProtList();
04754 
04755         } catch(hresult_exception&) {
04756                 m_parent->sendMsg( "Could not save <item> entries to my loglist.", MSG_ERROR);
04757         }
04758 }
04759 
04760 void ProtectList::addEntry( const ProtectEntry& p_pe)
04761 {
04762         if(!isSV()) return;
04763 
04764         m_list.push_back( p_pe);
04765 }
04766 
04767 bool ProtectList::needed()
04768 {
04769         return !m_list.empty();
04770 }
04771 
04772 void ProtectList::clearProtList()
04773 {
04774         // called when Abort or Commit happens
04775         m_list.clear(); // clears the whole list
04776 }
04777 
04778 // let's just dump items into the file, without the enclosing 'objects' tag
04779 // a text based, fast approach
04780 void ProtectList::writeProtLisp()
04781 {
04782         FILE * f = fopen( (m_localFileName+"3").c_str(), "a+b");
04783         if( !f)  return;
04784 
04785         // create <items> for all entries found in m_list
04786         for( unsigned int i = 0; i < m_list.size(); ++i)
04787         {
04788                 string gd;
04789                 guid2str( m_list[i].m_guid, gd );
04790 
04791                 fprintf( f, "\r\n<item gd=\"%s\" oper=\"%s\" when=\"%s\"/>", gd.c_str(), OpCodeStr[ m_list[i].m_op], (LPCTSTR) m_list[i].m_time.Format( _T("[%Y-%m-%d %H:%M:%S]")));
04792         }
04793 
04794         fclose(f);
04795 }
04796 
04797 void ProtectList::writeProtList()
04798 {
04799         DOMImplementationLS          * domimpl = NULL; 
04800         DOMLSParser                   * parser  = NULL;
04801 
04802         try {
04803 
04804                 parser = m_parent->getFreshParser( "ProtectionListWriter", &domimpl);
04805 
04806                 if( !domimpl || !parser)
04807                 {
04808                         m_parent->sendMsg( "Exception: Could not create parser!", MSG_ERROR);
04809                         HR_THROW(E_FILEOPEN);
04810                 }
04811 
04812                 XERCES_CPP_NAMESPACE::DOMDocument * doc = parser->parseURI( m_localFileName.c_str() );
04813                 if( doc == NULL )
04814                 {
04815                         m_parent->sendMsg( "Exception: Could not parse file '" + m_localFileName + "'!", MSG_ERROR);
04816                         HR_THROW(E_FILEOPEN);
04817                 }
04818 
04819                 DOMElement * doc_e = doc->getDocumentElement();
04820                 if( doc_e == NULL)
04821                 {
04822                         m_parent->sendMsg( "Exception: Null document element in file '" + m_localFileName + "'!", MSG_ERROR);
04823                         HR_THROW(E_FILEOPEN);
04824                 }
04825 
04826                 const XMLCh* ITEM_xiteral = ParserLiterals::Protector::item;
04827                 const XMLCh* WHEN_xiteral = ParserLiterals::Protector::when;
04828                 const XMLCh* OPER_xiteral = ParserLiterals::Protector::oper;
04829                 const XMLCh* GUID_xiteral = ParserLiterals::Protector::gd;
04830                 const XMLCh* OBJS_xiteral = ParserLiterals::Protector::objects;
04831 
04832                 ASSERT( XMLString::equals( OBJS_xiteral, doc_e->getTagName()));
04833 
04834                 DOMNodeList *uss = doc_e->getElementsByTagName( ITEM_xiteral);
04835 
04836                 // create <items> for all entries found in m_list
04837                 for( unsigned int i = 0; i < m_list.size(); ++i)
04838                 {
04839                         string gd;
04840                         guid2str( m_list[i].m_guid, gd );
04841 
04842                         smart_XMLCh val_gd = XMLString::transcode( gd.c_str());
04843                         smart_XMLCh val_tm = XMLString::transcode( (LPCTSTR) m_list[i].m_time.Format( _T("[%Y-%m-%d %H:%M:%S]")));
04844                         smart_XMLCh val_op = XMLString::transcode( OpCodeStr[ m_list[i].m_op]);
04845                         smart_XMLCh x_newln= XMLString::transcode( "\n");
04846 
04847                         DOMElement *nch = doc->createElement( ITEM_xiteral);
04848                         nch->setAttribute( GUID_xiteral, val_gd);
04849                         nch->setAttribute( WHEN_xiteral, val_tm);
04850                         nch->setAttribute( OPER_xiteral, val_op);
04851 
04852                         doc_e->appendChild( nch);
04853 
04854                         DOMText* ntxt = doc->createTextNode( x_newln); // as fprintf(f, "\n");
04855                         doc_e->appendChild( ntxt);
04856                 }
04857 
04858                 //do a DOM save as follows:
04859                 smart_XMLCh x_fname = XMLString::transcode( m_localFileName.c_str());
04860                 XMLFormatTarget* outfile = new LocalFileFormatTarget( x_fname);
04861                 DOMLSOutput* theOutput = domimpl->createLSOutput();
04862                 theOutput->setByteStream(outfile);
04863 
04864                 DOMLSSerializer * writer = domimpl->createLSSerializer();
04865 
04866 
04867                 if( writer->getDomConfig()->canSetParameter( XMLUni::fgDOMWRTDiscardDefaultContent, true))
04868                         writer->getDomConfig()->setParameter( XMLUni::fgDOMWRTDiscardDefaultContent, true);
04869                 if( writer->getDomConfig()->canSetParameter( XMLUni::fgDOMXMLDeclaration, false))
04870                         writer->getDomConfig()->setParameter( XMLUni::fgDOMXMLDeclaration, false);
04871 
04872                 doc->normalizeDocument();
04873 
04874                 writer->write(doc, theOutput);
04875 
04876                 delete outfile;
04877                 delete writer;
04878                 theOutput->release();
04879                 // delete the parser object
04880                 delete parser;
04881         }
04882         catch(...)
04883         {
04884                 m_parent->sendMsg( "Exception while writing protection file '" + m_localFileName + "'!", MSG_ERROR);
04885                 if( parser) delete parser;
04886                 return;
04887         }
04888 }
04889 
04890 void ProtectList::purgeProtList( CTime& p_lastSyncTime)
04891 {
04892         DOMImplementationLS          * domimpl = NULL; 
04893         DOMLSParser                   * parser  = NULL;
04894 
04895         try {
04896 
04897                 parser = m_parent->getFreshParser( "ProtectionListPurger", &domimpl);
04898 
04899                 if( !domimpl || !parser)
04900                 {
04901                         m_parent->sendMsg( "Exception: Could not create parser!", MSG_ERROR);
04902                         HR_THROW(E_FILEOPEN);
04903                 }
04904 
04905                 XERCES_CPP_NAMESPACE::DOMDocument * doc = parser->parseURI( m_localFileName.c_str() );
04906                 if( doc == NULL )
04907                 {
04908                         m_parent->sendMsg( "Exception: Could not parse file '" + m_localFileName + "'!", MSG_ERROR);
04909                         HR_THROW(E_FILEOPEN);
04910                 }
04911 
04912                 DOMElement * doc_e = doc->getDocumentElement();
04913                 if( doc_e == NULL)
04914                 {
04915                         m_parent->sendMsg( "Exception: Null document element in file '" + m_localFileName + "'!", MSG_ERROR);
04916                         HR_THROW(E_FILEOPEN);
04917                 }
04918 
04919                 const XMLCh* ITEM_xiteral = ParserLiterals::Protector::item;
04920                 const XMLCh* WHEN_xiteral = ParserLiterals::Protector::when;
04921                 const XMLCh* OBJS_xiteral = ParserLiterals::Protector::objects;
04922                 const XMLCh* x_newline    = ParserLiterals::newln;
04923 
04924                 ASSERT( XMLString::equals( OBJS_xiteral, doc_e->getTagName()));
04925 
04926                 DOMNodeList *uss = doc_e->getElementsByTagName( ITEM_xiteral);
04927 
04928                 // handle outdated items
04929                 bool outdated;
04930 
04931                 for( int i = (int) uss->getLength() - 1; i >= 0; --i)
04932                 {
04933                         DOMNode * node = uss->item(i);
04934                         DOMElement* us = (DOMElement*) node; // user
04935 
04936                         outdated = false;
04937 
04938                         smart_Ch when = XMLString::transcode( us->getAttribute( WHEN_xiteral));
04939 
04940                         int y(-1), M(-1), d(-1), h(-1), m(-1), s(-1);
04941                         if( 6 == sscanf( when, "[%u-%u-%u %u:%u:%u]", &y, &M, &d, &h, &m, &s))
04942                         {
04943                                 CTime whn(y, M, d, h, m, s); // when did happen that event
04944                                 if( whn < p_lastSyncTime)
04945                                 {
04946                                         outdated = true;
04947                                         doc_e->removeChild( node); // because of this we loop --i
04948                                 }
04949                         }
04950                 }
04951 
04952                 doc->normalizeDocument();
04953                 DOMElement * doc_f = doc->getDocumentElement();
04954                 if( doc_f == NULL)
04955                 {
04956                         m_parent->sendMsg( "Exception: Null document element in file '" + m_localFileName + "' after normalization!", MSG_ERROR);
04957                         HR_THROW(E_FILEOPEN);
04958                 }
04959 
04960                 // replace sequences of '\n' with just one
04961                 DOMNodeList* list = doc_f->getChildNodes();
04962 
04963                 for( int i = (int) list->getLength() - 1; i >= 0 ; --i)
04964                 {
04965                         DOMNode * node = list->item(i);
04966                         if( node->getNodeType() == DOMNode::TEXT_NODE)
04967                         {
04968                                 DOMText * txt  = (DOMText*) node;
04969 
04970                                 smart_Ch nlines = XMLString::transcode( txt->getData());
04971                                 std::string newlines( nlines);
04972 
04973                                 if( newlines.size() > 1 && std::string::npos == newlines.find_first_not_of( '\n')) // nothing else just '\n' characters
04974                                 {
04975                                         txt->setData( x_newline);
04976                                 }
04977                         }
04978                 }
04979 
04980                 //do a DOM save as follows:
04981                 smart_XMLCh x_fname = XMLString::transcode( m_localFileName.c_str());
04982                 XMLFormatTarget* outfile = new LocalFileFormatTarget( x_fname);
04983 
04984                 DOMLSOutput* theOutput = domimpl->createLSOutput();
04985                 theOutput->setByteStream(outfile);
04986 
04987                 DOMLSSerializer* writer = domimpl->createLSSerializer();
04988 
04989                 if( writer == NULL)
04990                 {
04991                         m_parent->sendMsg( "Exception: Could not create DOM Writer for '" + m_localFileName + "'!", MSG_ERROR);
04992                         HR_THROW(E_FILEOPEN);
04993                 }
04994                 
04995                 if( writer->getDomConfig()->canSetParameter( XMLUni::fgDOMWRTDiscardDefaultContent, true))
04996                         writer->getDomConfig()->setParameter( XMLUni::fgDOMWRTDiscardDefaultContent, true);
04997                 if( writer->getDomConfig()->canSetParameter( XMLUni::fgDOMXMLDeclaration, false))
04998                         writer->getDomConfig()->setParameter( XMLUni::fgDOMXMLDeclaration, false);
04999                 doc->normalizeDocument();
05000 
05001                 writer->write( doc, theOutput );
05002                 delete outfile;
05003                 delete writer;
05004 
05005                 // delete the parser object
05006                 delete parser;
05007         }
05008         catch(...)
05009         {
05010                 m_parent->sendMsg( "Exception while purging protection file '" + m_localFileName + "'!", MSG_ERROR);
05011                 if( parser) delete parser;
05012                 return;
05013         }
05014 }
05015 
05016 /* ****************************************************************************** */
05017 /*        C L A S S                                                               */
05018 /* ****************************************************************************** */
05019 
05020 void CCoreXmlFile::protect( XmlObject * obj, OpCode oc)
05021 {
05022         // the time stamp could be aqcuired only once, when commitToDisk happens
05023         if( obj) m_protectList.addEntry( ProtectEntry( obj->m_guid, oc, CTime::GetCurrentTime()));
05024 }
05025 
05026 bool CCoreXmlFile::findOnProtectedLists( GUID p_gd, std::string& p_scapegoatUser)
05027 {
05028         if(!isSV()) return false;
05029 
05030         std::string str_gd; guid2str( p_gd, str_gd);
05031 
05032         std::vector< LoggedIn> ulist = allusers();
05033 
05034         bool found = false;
05035         for( std::vector< LoggedIn>::iterator it = ulist.begin()
05036                 ; !found && it != ulist.end()
05037                 ; ++it)
05038         {
05039                 if( it->m_nm == userName()) continue; // ignore my file
05040                 // std::string fname = refreshProtectionFile( it->m_nm); // refresh my copy of this user's prot file
05041                 std::string fname = ProtectList::getProtListFileName( it->m_nm );
05042 
05043                 if( found = findInFile( m_folderPath + "\\" + fname, str_gd))
05044                         p_scapegoatUser = it->m_nm;
05045         }
05046 
05047         return found;
05048 }
05049 
05050 std::vector< LoggedIn> CCoreXmlFile::allusers()
05051 {
05052         // refreshSignFile();
05053         refreshSessionFolder();
05054         return getUsersFromSignFile();
05055 }
05056 
05057 /*
05058 bool CCoreXmlFile::refreshSignFile()
05059 {
05060         return refreshOneFile( HelperFiles::signFileName);
05061 }
05062 */
05063 
05064 void CCoreXmlFile::replaceUserName( const std::string& p_userName)
05065 {
05066         m_vssUser = p_userName;
05067         m_svn->replaceUserName(p_userName);
05068 }
05069 
05070 std::string CCoreXmlFile::userName()
05071 {
05072         if( isSV())
05073         {
05074                 return m_vssUser;
05075         }
05076         else
05077                 return "";
05078 }
05079 
05080 bool CCoreXmlFile::isSV() { return m_sourceControl == CCoreXmlFile::SC_SUBVERSION; }
05081 
05082 bool CCoreXmlFile::userFilter( CTimeSpan& p_elapsed)
05083 {
05084         //return p_elapsed.GetTotalMinutes() < 60*24*2; // less than 2 days ago
05085         return true;
05086 }
05087 
05088 std::vector< LoggedIn> CCoreXmlFile::getUsersFromSignFile()
05089 {
05090         std::vector< LoggedIn>  res;
05091         SignManager::SignFileDataVec udata = m_signer.getUserData();
05092         for( SignManager::SignFileDataVec::const_iterator it = udata.begin();
05093                 it != udata.end();
05094                 ++it)
05095         {
05096                 //const char * date_last_logged_out = it->m_until.c_str();
05097                 char status = it->m_until == ""?'A':'I'; // if logged in its logout date is ""
05098 
05099                 const char * date_last_logged_in = it->m_since.c_str();
05100                 int y(-1), M(-1), d(-1), h(-1), m(-1), s(-1);
05101                 if( 6 == sscanf( date_last_logged_in, "[%u-%u-%u %u:%u:%u]", &y, &M, &d, &h, &m, &s))
05102                 {
05103                         CTime whn(y, M, d, h, m, s); // when did last login occur
05104                         CTime now = CTime::GetCurrentTime();
05105                         CTimeSpan elapsed = now - whn;
05106                         if( userFilter( elapsed))
05107                                 if( std::find( res.begin(), res.end(), LoggedIn( it->m_user, status)) == res.end()) // not found
05108                                         res.push_back( LoggedIn( it->m_user, status)); // store its name
05109                 }
05110         }
05111         return res;
05112 }
05113 
05114 CTime CCoreXmlFile::lastSyncTimeForAllUsers()
05115 {
05116         // 
05117         //refreshSignFile(); // not needed now, we just signed on
05118         return findEarliestLogin( 0, 0, (int) ( m_userOpts.m_purgeDelayFactor * 60)); // days, hours, minutes
05119 }
05120 
05121 CTime CCoreXmlFile::findEarliestLogin( int p_nbOfDays, int p_nbOfHours, int p_nbOfMinutes)
05122 {
05123         CTime earliest = CTime::GetCurrentTime();
05124         CTime earliest_at_most( earliest - CTimeSpan( p_nbOfDays, p_nbOfHours, p_nbOfMinutes, 0)); // no further back than p_nbOfDays days / p_nbOfHours
05125         bool avoid_limitation = earliest == earliest_at_most; // if no timespan provided we will not limit the earliest time
05126 
05127         SignManager::SignFileDataVec udata = m_signer.getUserData();
05128         for( std::vector< SignFileData>::const_iterator it = udata.begin();
05129                 it != udata.end();
05130                 ++it)
05131         {
05132                 const char * date_last_logged_in = it->m_since.c_str();
05133                 const std::string &date_last_logged_out = it->m_until;
05134                 int y(-1), M(-1), d(-1), h(-1), m(-1), s(-1);
05135                 if( 6 == sscanf( date_last_logged_in, "[%u-%u-%u %u:%u:%u]", &y, &M, &d, &h, &m, &s))
05136                 {
05137                         CTime whn(y, M, d, h, m, s); // when did last login occur
05138                         if( whn < earliest && "" == date_last_logged_out) // means user is currently active
05139                                 earliest = whn;
05140                 }
05141         }
05142 
05143         // we have now the earliest login
05144         // but we might limit its range if we have been provided a proper timespan
05145         if( earliest < earliest_at_most && !avoid_limitation)
05146                 earliest = earliest_at_most;
05147 
05148         return earliest;
05149 }
05150 
05151 void CCoreXmlFile::refreshSessionFolder()
05152 {
05153         if( isSV() && m_needsSessionRefresh)
05154         {
05155                 updateSVN( HelperFiles::sessionFolderName );
05156                 m_needsSessionRefresh = false;
05157         }
05158 }
05159 
05160 /*
05161 bool CCoreXmlFile::refreshOneFile( const std::string& p_fname)
05162 {
05163         if( isSV())
05164         {
05165                 updateSVN( p_fname);
05166         }
05167         return false;
05168 }
05169 
05170 
05171 std::string CCoreXmlFile::refreshProtectionFile( const std::string& p_username)
05172 {
05173         std::string fname = ProtectList::getProtListFileName( p_username);
05174         refreshOneFile( fname);
05175         return fname;
05176 }
05177 
05178 */
05179 
05180 bool CCoreXmlFile::findInFile( const std::string& fname, const std::string& str_gd)
05181 {
05182         std::ifstream g;
05183         g.open( fname.c_str(), std::ios_base::in| std::ios_base::binary);
05184         if( !g.is_open()) return false;
05185         char buff[1024];
05186         const char * to_find = str_gd.c_str();// why won't work this? (" gd=\"" + str_gd + "\"").c_str();
05187         const char * to_fin2 = " oper=\"";
05188         char oper_buff[100];
05189         bool found = false;
05190         while( !g.eof() && !found)
05191         {
05192                 g.getline( &buff[0], 1024, '\n');
05193                 if( strlen( buff) < 1023)
05194                 {
05195                         //if( g.rdstate() != 0)
05196                         //      g.clear();
05197 
05198                         found = 0 != strstr( buff, to_find);
05199                         if( found)
05200                         {
05201                                 char * oper = strstr( buff, to_fin2);
05202                                 if( strlen(oper) < 100)
05203                                         sscanf( oper, " oper=\"%s\" ", &oper_buff);
05204                         }
05205                 }
05206                 else
05207                         ASSERT(0);
05208         }
05209         g.close();
05210 
05211         return found;
05212 }
05213 
05214 bool CCoreXmlFile::makeSureFileExistsInVerSys( const std::string& p_fname, const std::string& p_initialcontent, bool p_needsLock /*= true*/)
05215 {
05216         std::string fulllocalfname = m_folderPath + "\\" + p_fname;
05217         bool found = false;
05218 
05219         try
05220         {
05221                 if( isSV())
05222                 {
05223                         found = FileHelp::fileExist( fulllocalfname) && isVersionedInSVN( fulllocalfname);
05224                 }
05225 
05226                 if( !found)
05227                 {
05228                         FILE * f = fopen( fulllocalfname.c_str(), "w");
05229                         if( !f) throw hresult_exception(E_FAIL);
05230 
05231                         fprintf( f, "%s", p_initialcontent.c_str());
05232                         fclose( f);
05233 
05234                         // add newly created file
05235                         if( isSV())
05236                         {
05237                                 //bool ok = isVersionedInSVN( fulllocalfname);
05238                                 bool sc_add = true;
05239                                 bool sc_pro = true;
05240                                 bool sc_com = true;
05241                                 sc_add = addSVN( fulllocalfname);
05242                                 if( p_needsLock) // apply lock attribute except if directed otherwise
05243                                         sc_pro = lockablePropertySVN( fulllocalfname);
05244                                 sc_com = commitSVN( fulllocalfname, std::string("auto: makeSureFileExistsInVerSys()"), true);
05245 
05246                                 if( !( sc_add && sc_pro && sc_com))
05247                                 {
05248                                         if(      !sc_add) sendMsg( "Could not add file " + fulllocalfname, MSG_ERROR);
05249                                         else if( !sc_pro) sendMsg( "Could not apply lockable property for file " + fulllocalfname, MSG_ERROR);
05250                                         else if( !sc_com) sendMsg( "Could not commit file " + fulllocalfname, MSG_ERROR);
05251                                         throw hresult_exception(E_FAIL);
05252                                 }
05253                         }
05254 
05255                         found = true;
05256                 }
05257         }
05258         catch( hresult_exception& e)
05259         {
05260                 char buff[200]; sprintf( buff, "Could not get \"%s\" file from source control. Exception code: 0x%x", fulllocalfname.c_str(), e.hr);
05261                 sendMsg( buff, MSG_ERROR);
05262                 return false;
05263         }
05264         return found;
05265 }
05266 
05267 void CCoreXmlFile::getLatestVersion()
05268 {
05269         if( isSV())
05270         {
05271                 bool succ = m_svn->getLatest(m_folderPath);
05272                 if (!succ) {
05273                         AfxMessageBox( "Could not get latest version from server!");
05274                         HR_THROW( E_UNKNOWN_STORAGE); // furthermore will be silently handled
05275                 }
05276         }
05277 }
05278 
05279 void CCoreXmlFile::checkInAll()
05280 {
05281         AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
05282 
05283         if( !m_userOpts.m_defCheckInOnSave && AfxMessageBox( "Project saved. Keep modified files checked out, thus hold locks explicitly?", MB_YESNO ) == IDYES ) // easier question???
05284                 checkInAll( true ); // keep files checked out
05285         else
05286         {
05287                 if( m_userOpts.m_defCheckInOnSave)
05288                         sendMsg( std::string( "ACCELERATION: No file is left checked out based on policy configured."), MSG_INFO);
05289 
05290                 checkInAll( false ); // check in
05291         }
05292 }
05293 
05294 void CCoreXmlFile::checkInAll( bool keepCheckedOut )
05295 {
05296         if( isSV())
05297         {// sometimes there is nothing to commit here, let's try for a while to not execute this
05298                 // if keepCheckedOut => use --no-unlock: won't unlock the targets
05299                 //bool sc_com = commitSVN( m_folderPath, true, keepCheckedOut);
05300                 //if( !sc_com)
05301                 {
05302                         //sendMsg( "Nothing committed or could not commit all in directory " + m_folderPath, MSG_WARNING);
05303                 }
05304                 std::string comment;
05305                 CCommitDialog cDlg;
05306                 if (m_userOpts.m_autoCommit) {
05307                         comment = "AutoCommit";
05308                 } else if (cDlg.DoModal() == IDOK) {
05309                         CT2CA comment_str(cDlg.m_comment);
05310                         comment = comment_str;
05311                 }
05312                 if( m_userOpts.m_useBulkCommit)
05313                 {
05314                         bool sc_com = bulkCommitSVN( m_folderPath, std::string(comment), keepCheckedOut);
05315                         if( !sc_com)
05316                         {
05317                                 sendMsg( "Nothing committed or could not commit all in directory " + m_folderPath, MSG_WARNING);
05318                         }
05319                 }
05320         }
05321 }
05322 
05323 void CCoreXmlFile::createNonversioned()
05324 {
05325         int  succ = CreateDirectory( m_folderPath.c_str(), NULL);
05326 
05327         if( succ)
05328                 succ = createHashedFolders();
05329 
05330         if( !succ)
05331         {
05332                 sendMsg( "Exception: Could not create initial directory structure!", MSG_ERROR);
05333                 AfxMessageBox( "Could not create initial directory structure");
05334                 HR_THROW(E_FILEOPEN);
05335         }
05336 }
05337 
05338 int CCoreXmlFile::createHashedFolders()
05339 {
05340         if( !m_hashFileNames) return 1;
05341 
05342         int   succ = CreateDirectory( m_contentPath.c_str(), NULL);
05343 
05344         DirSupplier     ds( m_hashFileNames, m_hashVal);
05345         if( m_hashVal == 2)
05346         {
05347                 for( Dir256Iterator it = ds.begin256(); succ && it != ds.end256(); ++it)
05348                         succ = CreateDirectory( (m_contentPath + "\\" + *it).c_str(), NULL);
05349         }
05350         else if( m_hashVal == 5)
05351         {
05352                 for( Dir16Iterator it = ds.begin16(); succ && it != ds.end16(); ++it)
05353                 {
05354                         succ = CreateDirectory( (m_contentPath + "\\" + *it).c_str(), NULL);
05355                         for( Dir256Iterator jt = ds.begin256(); succ && jt != ds.end256(); ++jt)
05356                                 succ = CreateDirectory( (m_contentPath + "\\" + *it + "\\" + *jt).c_str(), NULL);
05357                 }
05358         }
05359 
05360         return succ;
05361 }
05362 
05363 void CCoreXmlFile::commitHashedFolders()
05364 {
05365         if( !m_hashFileNames) return;
05366 
05367         if( m_hashVal == 5)
05368         {
05369                 // we will create 4096 dirs like 0/00, 0/01, ... 0/ff, 1/00, 1/01, ... f/ff
05370                 const char ans[] = "0123456789abcdef";
05371                 char       lev1[2] = { 'x', 0};
05372                 char       lev2[5] = { 'y', '\\', 'z', 't', 0};
05373                 int        err     = 0;
05374 
05375                 for( short i = 0; !err && i != 16; ++i)
05376                 {
05377                         lev1[0] = ans[i]; // form a name 
05378                         socoAdd( lev1, true /*=recursive*/); 
05379                         socoCommit( lev1, std::string("auto: commitHashedFolders()"), true);
05380                 }
05381         }
05382         else if( m_hashVal == 2)
05383         {
05384                 // we will create 256 dirs like 00, 01, ..ff
05385                 const char ans[] = "0123456789abcdef";
05386                 char       lev[3] = { 'z', 't', 0};
05387                 int        err     = 0;
05388 
05389 
05390                 // add and commit
05391                 for( short i = 0; !err && i != 16; ++i)
05392                 {
05393                         lev[0] = ans[i]; // form a name 
05394                         for( short j = 0; !err && j != 16; ++j)
05395                         {
05396                                 lev[1] = ans[j];
05397                                 socoAdd( lev, true /*=recursive*/); 
05398                                 socoCommit( lev, std::string("auto: commitHashedFolders()"), true);
05399                         }
05400                 }
05401         }
05402 }
05403 
05404 void CCoreXmlFile::socoAdd     ( const std::string& p_path, bool p_recursive)
05405 {
05406         if( isSV())
05407         {
05408                 addSVN( p_path, p_recursive);
05409         }
05410         else
05411         {
05412                 // nop
05413         }
05414 }
05415 
05416 void CCoreXmlFile::socoCommit( const std::string& p_path, const std::string& p_comment, bool p_initial)
05417 {
05418         if( isSV())
05419         {
05420                 bool sc = commitSVN( p_path, p_comment, p_initial /*initial commit*/);
05421                 if( !sc)
05422                 {
05423                         sendMsg( "Could not commit " + p_path + " into versioning system.", MSG_ERROR);
05424                 }
05425         }
05426         else
05427         {
05428                 // nop
05429         }
05430 }
05431 
05432 
05433 void CCoreXmlFile::testSubversion()
05434 {
05435         //if( IDNO == AfxMessageBox( "Would you like to skip the test of svn connection?", MB_YESNO))
05436         //{
05437         //      std::string tst, last_author, curr_owner;
05438         //      bool sc = infoSVN( m_svnUrl, false /*no recursive*/, tst, last_author, std::string() /* =curr_owner*/);
05439         //      if( sc)
05440         //      {
05441         //              AfxMessageBox( tst.c_str());
05442         //              if( last_author != m_vssUser)
05443         //                      AfxMessageBox( (std::string( "There is a username confusion here!\n'") + last_author + "' is told to be the last modifier of " + m_svnUrl + ", while the credentials provided indicate '" + m_vssUser + "'!").c_str());
05444         //      }
05445         //      else
05446         //              AfxMessageBox( "svn info command failed");
05447         //}
05448 }
05449 
05450 void CCoreXmlFile::createSubversionClientImpl()
05451 {
05452         m_svn = std::auto_ptr<HiClient>(new HiClient(m_vssUser, m_vssPassword));
05453         m_svn->setLog(m_userOpts.m_createSvnLog, m_userOpts.m_svnLogFileName);
05454 }
05455 
05456 void CCoreXmlFile::svnSetup( bool p_createOrOpen)
05457 {
05458         if( getUserCredentialInfo( 1, p_createOrOpen)) // fills m_vssUser, m_vssPassword, p_createOrOpen == requireLogin
05459         {
05460                 svnSshHandling();
05461                 svnOptions();
05462                 createSubversionClientImpl();
05463         }
05464         else
05465         {
05466                 AfxMessageBox( "You did not provide login data. You may work locally but might lose synchronization with the version controlled project. You won't be able to modify read-only files.", MB_ICONEXCLAMATION );
05467                 m_sourceControl = SC_NONE;
05468         }
05469         //testSubversion();
05470 }
05471 
05472 void CCoreXmlFile::createSubversionedFolder()
05473 {
05474         m_sourceControl = SC_SUBVERSION;
05475         //m_hashInfoFound = false;
05476 
05477         svnSetup( true); // true => strictly requires login data, throws if dlg is canceled
05478         // fills m_vssUser, m_vssPassword
05479 
05480         if( !isVersionedInSVN( m_svnUrl, /*isDir = */true))
05481         {
05482                 sendMsg( "Exception: Location directory '" + m_svnUrl + "' does not exist on the SVN server!", MSG_ERROR);
05483                 AfxMessageBox( (std::string( "Location directory '") + m_svnUrl + "' does not exist on the SVN server").c_str());
05484                 HR_THROW(E_FILEOPEN);
05485         }
05486         if( isVersionedInSVN( m_svnUrl + "/" + m_projectName, /*isDir = */true, /* suppressErrorMsg = */true))
05487         {
05488                 sendMsg( "Exception: Project '" + m_projectName + "' already found at '" + m_svnUrl + "'. Project creation aborted.", MSG_ERROR);
05489                 AfxMessageBox( (std::string( "Project '") + m_projectName + "' already found at '" + m_svnUrl + "'. Project creation aborted.").c_str());
05490                 HR_THROW(E_FILEOPEN);
05491         }
05492         bool main_created = mkdirSVN( m_svnUrl, m_projectName, m_folderPath);
05493         if( !main_created)
05494         {
05495                 sendMsg( "Exception: Could not create on server directory: " + m_svnUrl + "/" + m_projectName, MSG_ERROR);
05496                 AfxMessageBox( (std::string( "Could not create on server directory: ") + m_svnUrl + "/" + m_projectName).c_str());
05497                 HR_THROW(E_FILEOPEN);
05498         }
05499 
05500 
05501         // session folder
05502         std::string sessionFolder =  m_folderPath + "\\" + HelperFiles::sessionFolderName;
05503         BOOL  succ = ::CreateDirectory( sessionFolder.c_str(), NULL);
05504         if( succ != TRUE)
05505         {
05506                 sendMsg( "Exception: Could not create session folder: " + sessionFolder, MSG_ERROR);
05507                 AfxMessageBox( (std::string( "Could not create session folder: ") + sessionFolder).c_str());
05508                 HR_THROW(E_FILEOPEN);
05509         }
05510 
05511         // add session folder to server
05512         succ = addSVN( sessionFolder, true /*=recursive*/); 
05513         if( !succ) {
05514                 sendMsg( "Exception: Could not add session folder to server.", MSG_ERROR);
05515                 AfxMessageBox( "Could not add session folder to server.");
05516                 HR_THROW(E_FILEOPEN);
05517         }
05518         // hashed folder structure (optional)
05519         if( m_hashFileNames)
05520         {
05521 
05522                 // commit session folder (see previous comment)
05523                 succ = commitSVN( sessionFolder, std::string("auto: createSubversionedFolder()"), true);
05524                 if( !succ) {
05525                         sendMsg( "Exception: Could not commit session folder.", MSG_ERROR);
05526                         AfxMessageBox( "Could not commit session folder.");
05527                         HR_THROW(E_FILEOPEN);
05528                 }
05529 
05530                 succ = createHashedFolders();
05531                 if( !succ) {
05532                         sendMsg( "Exception: Could not create initial directory structure.", MSG_ERROR);
05533                         AfxMessageBox( "Could not create initial directory structure.");
05534                         HR_THROW(E_FILEOPEN);
05535                 }
05536 
05537                 // add to server
05538                 succ = addSVN( m_contentPath, true /*=recursive*/); 
05539                 if( !succ) {
05540                         sendMsg( std::string("Exception: Could not add initial directory '") + m_folderPath + "' to server.", MSG_ERROR);
05541                         AfxMessageBox( "Could not add initial directory structure to server.");
05542                         HR_THROW(E_FILEOPEN);
05543                 }
05544         }
05545 
05546         // initial commit
05547         succ = commitSVN( m_folderPath, std::string("auto: createSubversionedFolder()"), true);
05548         if( !succ) {
05549                 sendMsg( "Exception: Could not commit initial directory structure.", MSG_ERROR);
05550                 AfxMessageBox( "Could not commit initial directory structure.");
05551                 HR_THROW(E_FILEOPEN);
05552         }
05553 }
05554 
05555 void CCoreXmlFile::getSVLastCommiter(XmlObject * obj, string& user)
05556 {
05557         ASSERT( m_sourceControl == SC_SUBVERSION );
05558         ASSERT( obj->isContainer() );
05559 
05560         std::string fname;
05561         getContainerFileName( obj, fname, true);
05562 
05563         m_svn->info(fname, false, false, std::string(), user, std::string());
05564 }
05565 
05566 void CCoreXmlFile::getSVCurrentOwner(XmlObject * obj, string& user, bool& newfile) // getSVCheckOutUser
05567 {
05568         ASSERT( m_sourceControl == SC_SUBVERSION );
05569         ASSERT( obj->isContainer() );
05570 
05571         std::string fname;
05572         getContainerFileName( obj, fname, true);
05573 
05574         std::string holder;
05575         bool        ret = false;
05576 
05577         bool verd = m_svn->isVersioned(fname, false, true);
05578         if(verd)
05579         {
05580                 newfile = false;
05581                 ret = m_svn->isLockedByUser(fname, holder);
05582         }
05583         else
05584                 newfile = true;
05585 
05586         if(ret)
05587         {
05588                 user    = holder;
05589         }
05590 
05591         long lInfo( 0x0), lStat( 0x0);
05592         if( !holder.empty())
05593         {
05594                 if( holder != m_vssUser)
05595                         lInfo = FS_OTHER; // checked out by other user
05596                 else // readonly, but we could apply a lock onto it (then we released the lock)
05597                         lInfo = FS_LOCAL; // checked out by local user
05598         }
05599         else
05600         {
05601                 lInfo = 0x0;  // not checked out
05602         }
05603 
05604 
05605         bool ismodbyothers = fileModifiedByOthers( obj); // fileModifiedByOthers( fname, obj->m_lastWriteTime);
05606 
05607         if( ismodbyothers)
05608                 lStat = FS_MODIFIEDBYOTHERS;
05609         else if( newfile)
05610                 lStat = FS_NOTYETSAVED;
05611         else
05612                 lStat = 0x0;
05613 
05614         setSourceControlNodes( obj, lInfo, lStat);
05615 }
05616 
05617 bool CCoreXmlFile::isCheckedOutByElseSVN( const std::string& p_file)
05618 {
05619         bool versioned = m_svn->isVersioned(p_file, false, false);
05620         ASSERT(versioned);
05621         //ASSERT( m_svn->isVersioned( p_file));
05622         std::string holder;
05623         bool locked = m_svn->isLockedByUser(p_file, holder);
05624         if(locked)
05625         {
05626                 return holder != m_vssUser; // not us
05627         }
05628         return false;
05629         //return m_svn->isLockedByOthers( p_file);
05630 }
05631 
05632 //void CCoreXmlFile::checkOutSVN( const std::string& p_file)
05633 //{
05634 //      // todo simplify, streamline this
05635 //      if( applyLockSVN( p_file)) 
05636 //      {
05637 //              // not checked out by somebody else
05638 //      }
05639 //      else
05640 //      {
05641 //              ASSERT( 0);
05642 //              throw "checked out file";
05643 //      }
05644 //}
05645 
05646 bool CCoreXmlFile::applyLockSVN( const std::string& p_file) // throws hresult_exception
05647 {
05648         bool succ = false;
05649 
05650         succ = m_svn->tryLock(p_file);
05651         if(!succ)
05652         {
05653                 AfxMessageBox( (p_file + " lock() returned false in applyLockSVN").c_str());
05654                 HR_THROW(E_FAIL);
05655         }
05656 
05657         return succ;
05658 }
05659 
05660 bool CCoreXmlFile::removeLockSVN( const std::string& p_file)
05661 {
05662         return m_svn->unLock(p_file);
05663 }
05664 
05665 bool CCoreXmlFile::mkdirSVN( const std::string& p_url, const std::string& p_dirName, const std::string& p_localDestPath)
05666 {
05667         std::string dir = p_url + "/" + p_dirName;
05668 
05669         bool sc = m_svn->mkDirOnServer( dir);
05670         if(!sc) return false;
05671 
05672         sc = m_svn->lightCheckOut( dir, p_localDestPath);
05673         return sc;
05674 }
05675 
05676 bool CCoreXmlFile::lockablePropertySVN( const std::string& p_file)
05677 {
05678         return m_svn->lockableProp(p_file);
05679 }
05680 
05681 bool CCoreXmlFile::addSVN( const std::string& p_entity, bool p_recursive /*= false*/)
05682 {
05683         return m_svn->add( p_entity, p_recursive);
05684 }
05685 
05686 //
05687 // IDL: UseTheseStrings( [in] short size, [in, out, size_is(size)] BSTR names[]);
05688 
05689 HRESULT UseTheseStrings( short size, BSTR names[])
05690 {
05691         for (int i = 0; i < size; ++i) {
05692                 CW2A name(names[i]);
05693                 MessageBox(NULL, name, "Msg", MB_OK);
05694         }
05695         return S_OK;
05696 }
05697 
05698 void CCoreXmlFile::findAllRwObjs( const std::string& p_folderPath, std::vector< std::string>& rw_file_vec)
05699 {
05700         for( XmlObjVecIter it=m_objects.begin(); it!=m_objects.end(); ++it )
05701         {
05702                 XmlObject * obj = (*it);
05703                 //if( obj->isContainer() && obj->m_loaded )
05704                 //      writeXMLFile( obj );
05705                 if( !obj->isContainer() )
05706                         continue;
05707 
05708                 std::string fileName;
05709                 getContainerFileName( obj, fileName);
05710 
05711                 // we wish we could predict the time the file will be closed
05712                 // because that will become the file's 'Modified At' attribute
05713                 // the obj->m_lastWriteTime needs to reflect exactly this time
05714                 bool f_existed = false;
05715                 if( FileHelp::isFileReadOnly2( fileName, &f_existed))
05716                 {
05717                         continue; // file exists, is read-only, no chance of writing into it
05718                                         // it also means there was no change
05719                 }
05720 
05721                 rw_file_vec.push_back( fileName);
05722         }
05723 }
05724 
05725 bool CCoreXmlFile::bulkCommitSVN( const std::string& p_dir, const std::string& p_comment, bool p_noUnlock /* = false*/) // noUnlock <==> keeplocked
05726 {
05727         bool res = m_svn->commitAll(p_dir, p_comment, p_noUnlock);
05728         if( !p_noUnlock) // if noUnlock was not requested, then a file should be unlocked after commit
05729                 // except, when it was not changed: in this case it needs a manual unlock
05730         {
05731                 // find all 'rw' files, those need to be unlocked
05732                 //std::vector< std::string> rwfiles;
05733                 //findAllRwObjs( p_dir, rwfiles);
05734 
05735                 //if( 0 < rwfiles.size())
05736                 //{
05737                 //      VARIANT var_arr;
05738                 //      fillUpVariantArray( rwfiles, &var_arr);
05739 
05740                 //      VARIANT_BOOL succ_vt;
05741                 //      m_comSvn->BulkUnLock( var_arr, &succ_vt);
05742                 //}
05743                 // the approach above does not work, because Client::sub_unlock will
05744                 // not unlock several files at once, even though its parameter Target 
05745                 // would allow this.
05746                 //
05747                 // we will unlock files one by one:
05748                 std::string fileName;
05749                 for( XmlObjVecIter it=m_objects.begin(); it!=m_objects.end(); ++it )
05750                 {
05751                         if( !(*it)->isContainer() )
05752                                 continue;
05753 
05754                         getContainerFileName( *it, fileName);
05755 
05756                         bool f_existed = false;
05757                         if( FileHelp::isFileReadOnly2( fileName, &f_existed))
05758                                 continue; // file exists, is read-only, means no lock on it
05759 
05760                         // unlock one file at a time
05761                         // FIXME: fails for newly-added files when the commit fails
05762                         bool succ = m_svn->unLock(fileName);
05763                         if(!succ)
05764                                 AfxMessageBox( "Commit/Unlock pair failed");
05765                 }
05766         }
05767         return true;
05768 }
05769 
05770 bool CCoreXmlFile::commitSVN( const std::string& p_dirOrFile, const std::string& p_comment, bool p_initialCommit /* = false*/, bool p_noUnlock /* = false*/) // noUnlock <==> keeplocked
05771 {
05772         bool sc = m_svn->commitAll( p_dirOrFile, p_comment, p_noUnlock);
05773         // if noUnlock was not requested, then a file should be unlocked after commit
05774         // except, when it was not changed: in this case it needs a manual unlock
05775         if( !sc && !p_noUnlock)
05776         {
05777                 if( FileHelp::isFile( p_dirOrFile) && !FileHelp::isFileReadOnly( p_dirOrFile))
05778                 {
05779                         bool succ = m_svn->unLock( p_dirOrFile);
05780                         if (succ)
05781                         {
05782                                 sc = true;
05783                         }
05784                         else
05785                         {
05786                                 AfxMessageBox("Commit/Unlock pair failed");
05787                         }
05788                 }
05789         }
05790         return sc;
05791 }
05792 
05793 bool CCoreXmlFile::updateSVN( const std::string& p_dirOrFile)
05794 {
05795         return m_svn->getLatest( p_dirOrFile);
05796 }
05797 
05798 bool CCoreXmlFile::isVersionedInSVN( const std::string& p_file, bool p_isADir /*= false*/, bool p_suppressErrorMsg /*=false*/)
05799 {
05800         return m_svn->isVersioned( p_file, p_isADir, p_suppressErrorMsg);
05801 }
05802 
05803 bool CCoreXmlFile::infoSVN( const std::string& p_url, bool p_recursive, std::string& p_resultMsg, std::string& p_author, std::string& p_holder)
05804 {
05805         return m_svn->info(p_url, p_recursive, true, p_resultMsg, p_author, p_holder);
05806 }
05807 
05808 void CCoreXmlFile::showUsedFiles( XmlObjSet& containers, bool p_latentMessage /* = false */ )
05809 {
05810         if(isSV())
05811         {
05812                 CFilesInUseDetailsDlg dlg( 0, p_latentMessage);
05813                 char buf[300];
05814                 XmlObjSet::iterator it;
05815                 for( it=containers.begin(); it!=containers.end(); ++it )
05816                 {
05817                         string user; bool nfile;
05818                         if( p_latentMessage)                    // info about latently changed files
05819                         {
05820                                 getSVLastCommiter( *it, user);
05821                         }
05822                         else                                    // info about owned files
05823                         {
05824                                 getSVCurrentOwner( *it, user, nfile); // to handle when nfile is true
05825                         }
05826 
05827                         if( user.size() > 0 )
05828                         {
05829                                 string name, type;
05830                                 getContainerName( *it, name, type );
05831                                 sprintf( buf, "%s\t%s (%s)", user.c_str(), name.c_str(), type.c_str() );
05832                                 dlg.m_fileList.push_back( buf );
05833                         }
05834                         else dlg.m_fileList.push_back( "A file not yet found in the Versioning System"); // by zolmol
05835                 }
05836                 dlg.DoModal();
05837         }
05838         else
05839         {
05840                 AfxMessageBox( "No detailed information available." );
05841         }
05842 }
05843 
05844 void CCoreXmlFile::setSourceControlNodes( XmlObject * container, long lInfo, long lStat)
05845 {
05846         ASSERT( container->isContainer() );
05847 
05848         AttribMapIter itfs = container->m_attributes.find( ATTRID_FILESTATUS);
05849         if( itfs != container->m_attributes.end())
05850         {
05851                 ASSERT( itfs->second->getType() == VALTYPE_LONG);
05852                 XmlAttrLong * along = (XmlAttrLong*)  itfs->second;
05853                 along->m_value = lInfo + lStat;
05854         }
05855 }
05856 
05857 void CCoreXmlFile::whoControlsThis( XmlObject * container /*= 0*/)
05858 {
05859         if( !container) return;
05860         ASSERT( container->isContainer() );
05861 
05862         if(isSV())
05863         {
05864                 std::string user, nm, msg;
05865                 bool newfile = false;
05866 
05867                 getSVCurrentOwner( container, user, newfile ); // does a status info refresh too
05868 
05869                 AttribMapIter itnm = container->m_attributes.find( ATTRID_NAME);
05870                 if( itnm != container->m_attributes.end()) 
05871                         itnm->second->toString( nm);
05872                 //nm = makelink( container);
05873 
05874                 if( newfile)
05875                         msg = "Container \"" + nm + "\" is not yet saved into the repository.";
05876                 else if( user.empty())
05877                         msg = "Container \"" + nm + "\" is not held exclusively.";
05878                 else
05879                         msg = "Container \"" + nm + "\" is held exclusively by \"" + user + "\"";
05880 
05881                 AfxMessageBox( msg.c_str(), MB_ICONINFORMATION);
05882                 //bool ismodbyothers = fileModifiedByOthers( container);
05883         }
05884         else 
05885                 AfxMessageBox( "Container is not held exclusively since the project is not under sourcecontrol.");
05886 }
05887 
05888 void CCoreXmlFile::updateSourceControlInfo( XmlObject * container )
05889 {
05890         ASSERT( container->isContainer() );
05891 
05892         if( isSV())
05893         {
05894                 string file_name;
05895                 getContainerFileName( container, file_name, true );
05896 
05897                 bool fexists = FileHelp::fileExist( file_name);
05898 
05899                 long lInfo( 0x0), lStat( 0x0);
05900 
05901                 if( fexists && FileHelp::isFileReadOnly( file_name))
05902                 {
05903                         if( isCheckedOutByElseSVN( file_name))
05904                         {
05905                                 lInfo = FS_OTHER; // checked out by other user
05906                         }
05907                         else // readonly, but we could apply a lock onto it (then we released the lock)
05908                         {
05909                                 lInfo = 0x0;  // not checked out
05910                         }
05911                 }
05912                 else if( fexists)
05913                 {
05914                         lInfo = FS_LOCAL; // checked out by local user
05915                 }
05916 
05917 
05918                 bool newfile = !fexists;
05919                 bool ismodbyothers = fexists && fileModifiedByOthers( container);
05920 
05921                 if( ismodbyothers)
05922                         lStat = FS_MODIFIEDBYOTHERS;
05923                 else if( newfile)
05924                         lStat = FS_NOTYETSAVED;
05925                 else
05926                         lStat = 0x0;
05927 
05928                 setSourceControlNodes( container, lInfo, lStat);
05929         }
05930 }
05931 
05932 void CCoreXmlFile::updateSourceControlInfo()
05933 {
05934         for( XmlObjVecIter it=m_objects.begin(), en = m_objects.end(); it!=en; ++it )
05935         {   
05936                 XmlObject * obj = (*it);
05937                 if( obj->isContainer() )
05938                         updateSourceControlInfo( obj );
05939         }
05940 }
05941 
05942 void CCoreXmlFile::dumpSourceControlInfo()
05943 {
05944         FILE * f = fopen( "c:\\temp\\out.txt", "w" );
05945 
05946         for( XmlObjVecIter it=m_objects.begin(); it!=m_objects.end(); ++it )
05947         {   
05948                 XmlObject * obj = (*it);
05949                 if( obj->isContainer() )
05950                 {
05951                         string guid, str;
05952                         guid2str( obj->m_guid, guid );
05953                         //getSourceControlInfo( obj, str );
05954 
05955                         string st2;
05956                         //getSourceControlStat( obj, st2 );
05957 
05958                         fprintf( f, "%s\t%s\t%s\n", guid.c_str(), str.c_str(), st2.c_str() );
05959                 }
05960         }
05961 
05962         fclose(f);
05963 }
05964 
05965 bool CCoreXmlFile::filesModifiedByOthers()
05966 {
05967         for( XmlObjVecIter it=m_objects.begin(); it!=m_objects.end(); ++it )
05968         {   
05969                 XmlObject * obj = (*it);
05970                 if( obj->isContainer() )
05971                 {
05972                         string filename;
05973                         getContainerFileName(obj, filename);
05974 
05975                         // get last write time
05976                         WIN32_FILE_ATTRIBUTE_DATA attr;        
05977                         BOOL res = GetFileAttributesEx( filename.c_str(), GetFileExInfoStandard, &attr );
05978                         // inserted by zolmol:
05979                         DWORD dwerror = res ? ERROR_SUCCESS : GetLastError(); // res == 0 in case of failure
05980                         if( dwerror == ERROR_FILE_NOT_FOUND) // != ERROR_SUCCESS)
05981                         {
05982                                 // in case of new objects have been introduced the respective files 
05983                                 // are not found (FILE_NOT_FOUND) and the obj->lastwritetime is 0
05984                                 ASSERT( obj->m_lastWriteTime == 0);
05985                                 continue;
05986                         } // end of zolmol code
05987 
05988                         CTime lastWriteTime( attr.ftLastWriteTime );
05989 
05990                         if( lastWriteTime > obj->m_lastWriteTime )
05991                                 return true;           
05992                 }
05993         }
05994         return false;
05995 }
05996 
05997 bool CCoreXmlFile::filesModifiedByOthersV3( XmlObjSet& p_readOnlyFiles, XmlObjSet& p_latentFiles)
05998 {
05999         // the p_readOnlyFiles param indicates which files
06000         // are intended to be checked out at all (if any)
06001         // this will help determining more permissively
06002         // whether a safe checkout is to be allowed or not
06003         bool ret_prm = false;
06004 
06005         // the permissive return_value calculation
06006         for( XmlObjSetIter it=p_readOnlyFiles.begin(); it!=p_readOnlyFiles.end(); ++it )
06007         {   
06008                 XmlObject * obj = (*it);
06009                 if( obj->isContainer() )
06010                 {
06011                         bool ret = false;
06012                         try {
06013                                 ret = fileModifiedByOthers( obj);
06014                         } catch( hresult_exception& ) {
06015                                 ret = false;
06016                                 std::string link = makelink( obj);
06017                                 sendMsg( std::string( "Orphan found: ") + link, MSG_ERROR);
06018                         }
06019 
06020                         // collect latent changed files
06021                         if( ret && p_latentFiles.end() == p_latentFiles.find( obj))
06022                                 p_latentFiles.insert( obj);
06023 
06024                         ret_prm = ret_prm || ret; // once became true, should remain true
06025                 }
06026         }
06027 
06028         return ret_prm;
06029 }
06030 
06031 bool CCoreXmlFile::fileModifiedByOthers( XmlObject * obj )
06032 {
06033         ASSERT( obj);
06034         ASSERT( obj->isContainer());
06035 
06036         string filename;
06037         getContainerFileName(obj, filename);
06038 
06039         // get last write time
06040         WIN32_FILE_ATTRIBUTE_DATA attr;        
06041         BOOL res = GetFileAttributesEx( filename.c_str(), GetFileExInfoStandard, &attr );
06042         // inserted by zolmol:
06043         DWORD dwerror = res ? ERROR_SUCCESS : GetLastError(); // res == 0 in case of failure
06044         if( dwerror == ERROR_FILE_NOT_FOUND) // != ERROR_SUCCESS)
06045         {
06046                 // in case of new objects have been introduced the respective files 
06047                 // are not found (FILE_NOT_FOUND) and the obj->lastwritetime is 0
06048                 ASSERT( obj->m_lastWriteTime == 0);
06049                 return false;
06050         } // end of zolmol code
06051         bool mydec = false;
06052         CTime lastWriteTime( attr.ftLastWriteTime );
06053         bool rv = lastWriteTime > obj->m_lastWriteTime;
06054         if( isSV())
06055         {
06056                 // FIXME: this may fail
06057                 bool repo_entry_modified;
06058                 m_svn->statusOnServer(filename, false, std::string(), &rv, &repo_entry_modified);
06059         }
06060         return rv;
06061 }
06062 
06063 void CCoreXmlFile::sendMsg( const std::string& p_msgStr, int p_msgType)
06064 {
06065         m_console.sendMsg( p_msgStr, p_msgType);
06066 }
06067 
06068 std::string CCoreXmlFile::makelink( XmlObject * ptr)
06069 {
06070         if( !ptr) return "nullobject";
06071 
06072         // name
06073         std::string nm;
06074         ptr->m_attributes.find( ATTRID_NAME)->second->toString( nm);
06075 
06076         // objid
06077         metaobjidpair_type idpr;
06078         objIdFromObject( ptr, idpr);
06079 
06080         // something similar to FCO::get_ID() found in MgaFCO.cpp
06081         char id[20];
06082         sprintf( id, "id-%04lx-%08lx", idpr.metaid, idpr.objid);
06083 
06084         return std::string( "<A HREF=\"mga:") + id + "\">" + (nm.size()>0?nm:"noname") + "</A>";
06085 }
06086 
06087 void CCoreXmlFile::newDOMObjs(  XERCES_CPP_NAMESPACE::DOMImplementationLS** p_domImpl, std::auto_ptr<XERCES_CPP_NAMESPACE::DOMLSParser>& p_domParser, std::auto_ptr<XERCES_CPP_NAMESPACE::DOMErrorHandler>& p_domErrHandler)
06088 {
06089         DOMImplementationLS * domimpl = DOMImplementationRegistry::getDOMImplementation( smart_XMLCh(XMLString::transcode("XML 1.0")));//NULL
06090         ASSERT( domimpl != NULL );
06091         
06092         DOMLSParser * parser = !domimpl? 0: domimpl->createLSParser( DOMImplementationLS::MODE_SYNCHRONOUS, NULL );
06093         ASSERT( parser != NULL );
06094 
06095         DOMErrorHandler* err_handler = new DOMErrorPrinter( &m_console);
06096         ASSERT( err_handler != NULL);
06097         if( parser && err_handler)
06098                 parser->getDomConfig()->setParameter(XMLUni::fgDOMErrorHandler, err_handler);
06099 
06100         *p_domImpl = domimpl;
06101         p_domParser = std::auto_ptr<XERCES_CPP_NAMESPACE::DOMLSParser>(parser);
06102         p_domErrHandler = std::auto_ptr<XERCES_CPP_NAMESPACE::DOMErrorHandler>(err_handler);
06103 }
06104 
06105 XERCES_CPP_NAMESPACE::DOMDocument* CCoreXmlFile::enclosedParse( const std::string& p_fileName, DOMLSParser* p_parser, bool *p_success)
06106 {
06107         ASSERT( p_parser);
06108         ASSERT( p_success);
06109         if( p_success) *p_success = p_parser != 0;
06110         if( !p_parser) return 0;
06111 
06112         XERCES_CPP_NAMESPACE::DOMDocument* doc = 0;
06113 
06114         try {
06115                 doc = p_parser->parseURI( p_fileName.c_str());
06116         }
06117         catch( const OutOfMemoryException&) {
06118                 sendMsg( "Exception: Out of memory during parsing of " + p_fileName + "!", MSG_ERROR);
06119                 if( p_success) *p_success = false;
06120         }
06121         catch (const XMLException& e) {
06122                 smart_Ch e_msg = XMLString::transcode( e.getMessage());
06123                 sendMsg( "An error occurred during parsing of " + p_fileName + ". Message: " + e_msg, MSG_ERROR);
06124                 if( p_success) *p_success = false;
06125         }
06126         catch (const DOMException& e) {
06127                 const unsigned int maxChars = 2047;
06128                 XMLCh errText[maxChars + 1];
06129                 if( DOMImplementation::loadDOMExceptionMsg( e.code, errText, maxChars))
06130                 {
06131                         smart_Ch e_msg = XMLString::transcode( errText);
06132                         sendMsg( "An error occurred during parsing of " + p_fileName + ". Message: " + e_msg, MSG_ERROR);
06133                 }
06134                 else
06135                 {
06136                         smart_Ch e_msg = XMLString::transcode( e.getMessage());
06137                         sendMsg( "An error occurred during parsing of " + p_fileName + ". Message: " + e_msg, MSG_ERROR);
06138                 }
06139                 
06140                 if( p_success) *p_success = false;
06141         }
06142         catch (...)
06143         {
06144                 sendMsg( "Unknown exception occurred during parsing of " + p_fileName + "!", MSG_ERROR);
06145                 if( p_success) *p_success = false;
06146         }
06147 
06148         return doc;
06149 }
06150 
06151 DOMLSParser* CCoreXmlFile::getFreshParser( const std::string& p_whoIsTheUser, DOMImplementationLS ** p_ptrRetDomImpl /* = 0 */)
06152 {
06153         DOMImplementationLS * domimpl = NULL;
06154         DOMLSParser*           parser  = NULL;
06155         
06156         
06157         //
06158         // DOM implementation factory creation
06159         //
06160         const char            di_msg[] = "Exception: Could not create DOMImplementation for ";
06161         try {
06162 
06163                 domimpl = DOMImplementationRegistry::getDOMImplementation(NULL);
06164 
06165         } catch(...) {
06166 
06167                 // was not initialized already? let's do our best
06168                 XMLPlatformUtils::Initialize();
06169 
06170                 // try again
06171                 try {
06172 
06173                         domimpl = DOMImplementationRegistry::getDOMImplementation(NULL); // 2nd attempt, hoping that XMLPlatformUtils::Initialize() was missing the time before
06174                         
06175                         if( domimpl) sendMsg( "Warning: DOMImplementation created in the second attempt only for " + p_whoIsTheUser, MSG_WARNING);
06176 
06177                 } catch(...) {
06178 
06179                         domimpl = 0;
06180 
06181                         sendMsg( di_msg + p_whoIsTheUser, MSG_ERROR);
06182 
06183                 }
06184         }
06185 
06186         ASSERT( domimpl != NULL );
06187 
06188         //
06189         // can the DOMBuilder/parser be created ?
06190         //
06191         try {
06192 
06193                 parser = !domimpl? 0: domimpl->createLSParser( DOMImplementationLS::MODE_SYNCHRONOUS, NULL );
06194 
06195         }
06196         catch(const DOMException& e) {
06197 
06198                 parser = 0;
06199 
06200                 const unsigned int maxChars = 2047;
06201                 XMLCh errText[maxChars + 1];
06202                 const char         s_msg[]  = "DOMException during parser object creation for ";
06203                 const char         m_msg[]  = ". Message: ";
06204 
06205                 if( DOMImplementation::loadDOMExceptionMsg( e.code, errText, maxChars))
06206                 {
06207                         smart_Ch e_msg = XMLString::transcode( errText);
06208                         sendMsg( s_msg + p_whoIsTheUser + m_msg + e_msg, MSG_ERROR);
06209                 }
06210                 else
06211                 {
06212                         smart_Ch e_msg = XMLString::transcode( e.getMessage());
06213                         sendMsg( s_msg + p_whoIsTheUser + m_msg + e_msg, MSG_ERROR);
06214                 }
06215         }
06216         catch(...) {
06217 
06218                 parser = 0;
06219 
06220                 sendMsg( "Exception: Could not create parser for " + p_whoIsTheUser, MSG_ERROR);
06221         }
06222 
06223         if( domimpl != 0 && parser != 0)
06224         {
06225                 if( p_ptrRetDomImpl != 0)             // if user interested in the DomImpl
06226                         *p_ptrRetDomImpl = domimpl;       // then fill the ptr
06227 
06228                 return parser;
06229         }
06230         return 0;
06231 }