/* * AbiCollab - Code to enable the modification of remote documents. * Copyright (C) 2005 by Martin Sevior * Copyright (C) 2006 by Marc Maurer * Copyright (C) 2007 One Laptop Per Child * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include #include #include "ut_vector.h" #include "pd_Document.h" #include "px_ChangeRecord.h" #include "px_CR_SpanChange.h" #include "px_CR_FmtMarkChange.h" #include "px_CR_SpanChange.h" #include "px_CR_FmtMark.h" #include "px_CR_Span.h" #include "px_CR_Glob.h" #include "px_CR_StruxChange.h" #include "px_CR_ObjectChange.h" #include "px_CR_Strux.h" #include "px_CR_Object.h" #include "AbiCollab_Packet.h" #include "pt_Types.h" #include #include const gchar * szAbiCollab_Packet_PTName[] = { PT_STYLE_ATTRIBUTE_NAME, PT_LEVEL_ATTRIBUTE_NAME, PT_LISTID_ATTRIBUTE_NAME, PT_PARENTID_ATTRIBUTE_NAME, PT_NAME_ATTRIBUTE_NAME, PT_TYPE_ATTRIBUTE_NAME, PT_BASEDON_ATTRIBUTE_NAME, PT_FOLLOWEDBY_ATTRIBUTE_NAME, PT_ID_ATTRIBUTE_NAME, PT_HEADER_ATTRIBUTE_NAME, PT_HEADEREVEN_ATTRIBUTE_NAME, PT_HEADERFIRST_ATTRIBUTE_NAME, PT_HEADERLAST_ATTRIBUTE_NAME, PT_FOOTER_ATTRIBUTE_NAME, PT_FOOTEREVEN_ATTRIBUTE_NAME, PT_FOOTERFIRST_ATTRIBUTE_NAME, PT_FOOTERLAST_ATTRIBUTE_NAME, PT_REVISION_ATTRIBUTE_NAME, PT_ID_ATTRIBUTE_NAME, PT_STRUX_IMAGE_DATAID, PT_XID_ATTRIBUTE_NAME, PT_DATAITEM_ATTRIBUTE_NAME, PT_IMAGE_DATAID, PT_IMAGE_TITLE, PT_IMAGE_DESCRIPTION, PT_DATA_PREVIEW, PT_HYPERLINK_TARGET_NAME, PT_ANNOTATION_NUMBER }; UT_sint16 getPacket_PTName_Index( const gchar* name ) { for (UT_uint8 i=0; i= _PCT_FirstChange && packet.getClassType() <= _PCT_LastChange); } std::string SessionPacket::toStr() const { return Packet::toStr() + str(boost::format("SessionPacket: m_sSessionId: %1%, m_sDocUUID: %2%\n") % m_sSessionId.utf8_str() % m_sDocUUID.utf8_str()); } /* ***************************************************** */ /* * AbstractChangeRecordSessionPacket */ /* ***************************************************** */ bool AbstractChangeRecordSessionPacket::isInstanceOf(const SessionPacket& packet) { return (packet.getClassType() == PCT_GlobSessionPacket) || (packet.getClassType() >= _PCT_FirstChangeRecord && packet.getClassType() <= _PCT_LastChangeRecord); } /* ***************************************************** */ /* * ChangeRecordSessionPacket */ /* ***************************************************** */ ChangeRecordSessionPacket::ChangeRecordSessionPacket( const UT_UTF8String& sSessionId, PX_ChangeRecord::PXType cType, const UT_UTF8String& sDocUUID, int iPos, int iRev, int iRemoteRev) : AbstractChangeRecordSessionPacket(sSessionId, sDocUUID), m_cType(cType), m_iLength(0), m_iAdjust(0), m_iPos(iPos), m_iRev(iRev), m_iRemoteRev(iRemoteRev) { } void ChangeRecordSessionPacket::serialize( Archive& ar ) { SessionPacket::serialize( ar ); ar << (int&)m_cType; ar << COMPACT_INT(m_iPos); ar << COMPACT_INT(m_iLength); ar << COMPACT_INT(m_iAdjust); ar << COMPACT_INT(m_iRev); ar << COMPACT_INT(m_iRemoteRev); } static const std::string& getPXTypeStr( PX_ChangeRecord::PXType t ) { bool safeGuard; UT_ASSERT( safeGuard = (t>=PX_ChangeRecord::PXT_GlobMarker && t<=PX_ChangeRecord::PXT_ChangeDocProp) ); if (safeGuard) { static std::string pxTypeStrs[] = { "PXT_GlobMarker", "PXT_InsertSpan", "PXT_DeleteSpan", "PXT_ChangeSpan", "PXT_InsertStrux", "PXT_DeleteStrux", "PXT_ChangeStrux", "PXT_InsertObject", "PXT_DeleteObject", "PXT_ChangeObject", "PXT_InsertFmtMark", "PXT_DeleteFmtMark", "PXT_ChangeFmtMark", "PXT_ChangePoint", "PXT_ListUpdate", "PXT_StopList", "PXT_UpdateField", "PXT_RemoveList", "PXT_UpdateLayout", "PXT_AddStyle", "PXT_RemoveStyle", "PXT_CreateDataItem", "PXT_ChangeDocProp", }; return pxTypeStrs[ int(t)+1 ]; } else { static std::string invalidValue; invalidValue = str(boost::format( "" ) % int(t) ); return invalidValue; } } std::string ChangeRecordSessionPacket::toStr() const { return SessionPacket::toStr() + str(boost::format("ChangeRecordSessionPacket: m_cType: %1%(%2%), m_iLength: %3%, m_iAdjust: %4%, m_iPos: %5%, m_iRev: %6%, m_iRemoteRev: %7%\n") % getPXTypeStr(m_cType).c_str() % m_cType % m_iLength % m_iAdjust % m_iPos % m_iRev % m_iRemoteRev ); } /* ***************************************************** */ /* * *_ChangeRecordSessionPacket */ /* ***************************************************** */ Props_ChangeRecordSessionPacket::Props_ChangeRecordSessionPacket( const Props_ChangeRecordSessionPacket& Other ) : ChangeRecordSessionPacket( Other ) , m_szAtts( NULL ) , m_szProps( NULL ) , m_sAtts( Other.m_sAtts ) , m_sProps( Other.m_sProps ) { _fillProps(); _fillAtts(); } void Props_ChangeRecordSessionPacket::serialize( Archive& ar ) { ChangeRecordSessionPacket::serialize( ar ); ar << m_sProps << m_sAtts; if (ar.isLoading()) { _fillProps(); _fillAtts(); } } void Props_ChangeRecordSessionPacket::_freeProps() { if (m_szProps == NULL) return; UT_sint32 i = 0; while(m_szProps[i] != NULL) { FREEP(m_szProps[i]); i++; } delete [] m_szProps; m_szProps = NULL; } void Props_ChangeRecordSessionPacket::_freeAtts() { if (m_szAtts == NULL) return; UT_sint32 i = 0; while(m_szAtts[i] != NULL) { FREEP(m_szAtts[i]); i++; } delete [] m_szAtts; m_szAtts = NULL; } void Props_ChangeRecordSessionPacket::_fillProps() { _freeProps(); m_szProps = new gchar* [m_sProps.size()*2 + 1]; UT_uint32 i = 0; for (std::map::iterator it=m_sProps.begin(); it!=m_sProps.end(); ++it) { m_szProps[i] = g_strdup((*it).first.utf8_str()); m_szProps[i+1] = g_strdup((*it).second.utf8_str()); i += 2; } m_szProps[i] = NULL; } void Props_ChangeRecordSessionPacket::_fillAtts() { _freeAtts(); m_szAtts = new gchar* [m_sAtts.size()*2 + 1]; UT_uint32 i = 0; for (std::map::iterator it=m_sAtts.begin(); it!=m_sAtts.end(); ++it) { UT_uint8 attIndex = (*it).first; if (attIndex < (sizeof(szAbiCollab_Packet_PTName)/sizeof(szAbiCollab_Packet_PTName[0]))) { m_szAtts[i] = g_strdup( szAbiCollab_Packet_PTName[attIndex] ); m_szAtts[i+1] = g_strdup( (*it).second.utf8_str() ); i += 2; } else { // invalid index, who sent this?? UT_ASSERT(UT_SHOULD_NOT_HAPPEN); } } m_szAtts[i] = NULL; } gchar* Props_ChangeRecordSessionPacket::getAttribute( const gchar* attr ) const { UT_sint16 idx = getPacket_PTName_Index( attr ); if (idx==-1) return NULL; // guaranteed not in map std::map::const_iterator it = m_sAtts.find( idx ); if (it==m_sAtts.end()) return NULL; return static_cast( const_cast( (*it).second.utf8_str() ) ); } std::string Props_ChangeRecordSessionPacket::toStr() const { std::string propStr = ChangeRecordSessionPacket::toStr() + "Props_ChangeRecordSessionPacket: "; if (m_szAtts) { propStr += "attrs: "; UT_sint32 i = 0; while (m_szAtts[i] != NULL) { propStr += str(boost::format("%1%:%2%;") % m_szAtts[i] % m_szAtts[i+1]); i += 2; } } if (m_szProps) { propStr += " props: "; UT_sint32 i = 0; while (m_szProps[i] != NULL) { propStr += str(boost::format("%1%:%2%;") % m_szProps[i] % m_szProps[i+1]); i += 2; } } propStr += "\n"; return propStr; } /* ***************************************************** */ /* * *_ChangeRecordSessionPacket */ /* ***************************************************** */ static const std::string& getPTStruxTypeStr( PTStruxType p ) { bool safeGuard; UT_ASSERT( safeGuard = (p>=PTX_Section && p<=PTX_StruxDummy) ); if (safeGuard) { static std::string PacketSessionTypeStrs[] = { "PTX_Section", "PTX_Block", "PTX_SectionHdrFtr", "PTX_SectionEndnote", "PTX_SectionTable", "PTX_SectionCell", "PTX_SectionFootnote", "PTX_SectionMarginnote", "PTX_SectionAnnotation", "PTX_SectionFrame", "PTX_SectionTOC", "PTX_EndCell", "PTX_EndTable", "PTX_EndFootnote", "PTX_EndMarginnote", "PTX_EndEndnote", "PTX_EndAnnotation", "PTX_EndFrame", "PTX_EndTOC", "PTX_StruxDummy", }; return PacketSessionTypeStrs[ int(p) ]; } else { static std::string invalidValue; invalidValue = str(boost::format( "" ) % int(p) ); return invalidValue; } } void InsertSpan_ChangeRecordSessionPacket::serialize( Archive& ar ) { Props_ChangeRecordSessionPacket::serialize( ar ); ar << m_sText; } std::string InsertSpan_ChangeRecordSessionPacket::toStr() const { return Props_ChangeRecordSessionPacket::toStr() + str(boost::format("InsertSpan_ChangeRecordSessionPacket: m_sText: %1%\n") % m_sText.utf8_str()); } void ChangeStrux_ChangeRecordSessionPacket::serialize( Archive& ar ) { Props_ChangeRecordSessionPacket::serialize( ar ); UT_ASSERT(sizeof(m_eStruxType)==4); ar << (int&)m_eStruxType; } std::string ChangeStrux_ChangeRecordSessionPacket::toStr() const { return Props_ChangeRecordSessionPacket::toStr() + str(boost::format("ChangeStrux_ChangeRecordSessionPacket: m_eStruxType: %1%(%2%)\n") % getPTStruxTypeStr(m_eStruxType).c_str() % m_eStruxType ); } void DeleteStrux_ChangeRecordSessionPacket::serialize( Archive& ar ) { ChangeRecordSessionPacket::serialize( ar ); UT_ASSERT(sizeof(m_eStruxType)==4); ar << (int&)m_eStruxType; } std::string DeleteStrux_ChangeRecordSessionPacket::toStr() const { return ChangeRecordSessionPacket::toStr() + str(boost::format("DeleteStrux_ChangeRecordSessionPacket: m_eStruxType: %1%(%2%)\n") % getPTStruxTypeStr(m_eStruxType).c_str() % m_eStruxType ); } void Object_ChangeRecordSessionPacket::serialize( Archive& ar ) { Props_ChangeRecordSessionPacket::serialize( ar ); UT_ASSERT(sizeof(m_eObjectType)==4); ar << (int&)m_eObjectType; } static const std::string& getPTObjectTypeStr( PTObjectType p ) { bool safeGuard; UT_ASSERT( safeGuard = (p>=PTO_Image && p<=PTO_Embed) ); if (safeGuard) { static std::string PTObjectTypeStrs[] = { "PTO_Image", "PTO_Field", "PTO_Bookmark", "PTO_Hyperlink", "PTO_Annotation", "PTO_Math", "PTO_Embed" }; return PTObjectTypeStrs[ int(p) ]; } else { static std::string invalidValue; invalidValue = str(boost::format( "" ) % int(p) ); return invalidValue; } } std::string Object_ChangeRecordSessionPacket::toStr() const { return Props_ChangeRecordSessionPacket::toStr() + str(boost::format("Object_ChangeRecordSessionPacket: m_eObjectType: %1%\n") % getPTObjectTypeStr(m_eObjectType).c_str() ); } void Data_ChangeRecordSessionPacket::serialize( Archive& ar ) { Props_ChangeRecordSessionPacket::serialize( ar ); ar << m_vecData << m_bTokenSet; if (m_bTokenSet) ar << m_sToken; } std::string Data_ChangeRecordSessionPacket::toStr() const { return ChangeRecordSessionPacket::toStr() + str(boost::format("Data_ChangeRecordSessionPacket: m_vecData: %1%\n") % "[DATA]"); } void Glob_ChangeRecordSessionPacket::serialize( Archive& ar ) { ChangeRecordSessionPacket::serialize( ar ); ar << m_iGLOBType; } std::string Glob_ChangeRecordSessionPacket::toStr() const { return ChangeRecordSessionPacket::toStr() + str(boost::format("Glob_ChangeRecordSessionPacket: m_iGLOBType: %1%\n") % ((UT_sint32)m_iGLOBType)); } /* ***************************************************** */ /* * GlobSessionPacket */ /* ***************************************************** */ GlobSessionPacket::GlobSessionPacket( const GlobSessionPacket& Other ) : AbstractChangeRecordSessionPacket( Other ) { UT_DEBUGMSG(("GlobSessionPacket::GlobSessionPacket: copying %u sub packets\n", Other.m_pPackets.size())); m_pPackets.resize( Other.m_pPackets.size() ); for (size_t i=0; i( Other.m_pPackets[i]->clone() ); } } GlobSessionPacket::~GlobSessionPacket() { for (size_t i=0; isetParent( this ); } UT_sint32 GlobSessionPacket::getPos() const { UT_sint32 iGlobPos = 0; for (size_t i = 0; i < m_pPackets.size(); i++) { UT_continue_if_fail(m_pPackets[i]); SessionPacket* pPacket = m_pPackets[i]; if (pPacket->getClassType() >= _PCT_FirstChangeRecord && pPacket->getClassType() <= _PCT_LastChangeRecord) { ChangeRecordSessionPacket* crp = static_cast( pPacket ); if (crp->getPos() > 0) // TODO: check the types, instead of a '0' position check to see if this member contains a real position - MARCM { if (iGlobPos == 0 || crp->getPos() < iGlobPos) { iGlobPos = crp->getPos(); } } } } return iGlobPos; } UT_sint32 GlobSessionPacket::getLength() const { // UT_sint32 iGlobLength = 0; ChangeRecordSessionPacket* pFirstPacket = NULL; ChangeRecordSessionPacket* pLastPacket = NULL; for (size_t i = 0; i < m_pPackets.size(); i++) { SessionPacket* pPacket = m_pPackets[i]; UT_continue_if_fail(pPacket); switch (pPacket->getClassType()) { /* misc. session packets */ case PCT_SignalSessionPacket: // these packets don't contribute to the length of a glob break; case PCT_RevertSessionPacket: case PCT_RevertAckSessionPacket: // these packets should never be in a glob UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN); break; case PCT_GlobSessionPacket: // we don't allow embedded globs in globs UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN); break; /* changerecord session packets */ case PCT_ChangeRecordSessionPacket: case PCT_Props_ChangeRecordSessionPacket: case PCT_InsertSpan_ChangeRecordSessionPacket: case PCT_DeleteStrux_ChangeRecordSessionPacket: case PCT_Object_ChangeRecordSessionPacket: case PCT_Data_ChangeRecordSessionPacket: case PCT_ChangeStrux_ChangeRecordSessionPacket: { ChangeRecordSessionPacket* crp = static_cast(pPacket); UT_ASSERT_HARMLESS(crp->getLength() >= 0); UT_ASSERT_HARMLESS(crp->getPos() >= 0); if (!pFirstPacket || crp->getPos() < pFirstPacket->getPos()) pFirstPacket = crp; if (!pLastPacket || crp->getPos() + crp->getLength() > pLastPacket->getPos() + pLastPacket->getLength()) pLastPacket = crp; } break; case PCT_Glob_ChangeRecordSessionPacket: // this packet doesn't contribute to the length of a glob break; default: UT_ASSERT_HARMLESS(UT_NOT_IMPLEMENTED); break; } } if (!pFirstPacket) return 0; UT_return_val_if_fail(pLastPacket, 0); return pLastPacket->getPos() + pLastPacket->getLength() - pFirstPacket->getPos(); } UT_sint32 GlobSessionPacket::getAdjust() const { UT_sint32 iGlobAdjust = 0; for (size_t i = 0; i < m_pPackets.size(); i++) { UT_continue_if_fail(m_pPackets[i]); SessionPacket* pPacket = m_pPackets[i]; if (pPacket->getClassType() >= _PCT_FirstChangeRecord && pPacket->getClassType() <= _PCT_LastChangeRecord) { ChangeRecordSessionPacket* crp = static_cast( pPacket ); iGlobAdjust += crp->getAdjust(); } } return iGlobAdjust; } UT_sint32 GlobSessionPacket::getRev() const { for (size_t i = 0; i < m_pPackets.size(); i++) { UT_continue_if_fail(m_pPackets[i]); SessionPacket* pPacket = m_pPackets[i]; if (pPacket->getClassType() >= _PCT_FirstChangeRecord && pPacket->getClassType() <= _PCT_LastChangeRecord) { ChangeRecordSessionPacket* crp = static_cast( pPacket ); return crp->getRev(); } } UT_return_val_if_fail(false, 0); } UT_sint32 GlobSessionPacket::getRemoteRev(void) const { for (size_t i = 0; i < m_pPackets.size(); i++) { UT_continue_if_fail(m_pPackets[i]); SessionPacket* pPacket = m_pPackets[i]; if (pPacket->getClassType() >= _PCT_FirstChangeRecord && pPacket->getClassType() <= _PCT_LastChangeRecord) { ChangeRecordSessionPacket* crp = static_cast( pPacket ); return crp->getRemoteRev(); } } UT_return_val_if_fail(false, 0); } void GlobSessionPacket::serialize( Archive& ar ) { SessionPacket::serialize( ar ); unsigned int count; if (ar.isLoading()) { // load packet count ar << COMPACT_INT(count); m_pPackets.resize( count, NULL ); // load packets for (size_t i=0; i( Packet::createPacket( (PClassType)classId ) ); UT_ASSERT(newPacket); // should this be safer? newPacket->setParent( this ); ar << *newPacket; m_pPackets[i] = newPacket; // for efficiency reasons, childs of a glob don't serialize their session // and document id's; therefor we set them now manually newPacket->setSessionId(getSessionId()); newPacket->setDocUUID(getDocUUID()); } } else { // save packet count count = m_pPackets.size(); ar << COMPACT_INT(count); // save packets for (size_t i=0; igetClassType(); ar << classId << *sp; } } } std::string GlobSessionPacket::toStr() const { std::string globStr = SessionPacket::toStr() + "GlobSessionPacket:\n"; for (std::vector::const_iterator cit = m_pPackets.begin(); cit != m_pPackets.end(); cit++) { globStr += "\n* "; globStr += (*cit)->toStr(); globStr += "\n"; } globStr += str(boost::format("Glob functions: getPos(): %1%, getLength(): %2%, getAdjust(): %3%, getRev(): %4%, getRemoteRev(): %5%\n") % getPos() % getLength() % getAdjust() % getRev() % getRemoteRev()); return globStr; } /* ***************************************************** */ /* * SignalSessionPacket */ /* ***************************************************** */ SignalSessionPacket::SignalSessionPacket(const UT_UTF8String& sSessionId, const UT_UTF8String& sDocUUID, UT_uint32 iSignal) : SessionPacket(sSessionId, sDocUUID), m_iSignal(iSignal) { } void SignalSessionPacket::serialize( Archive& ar ) { SessionPacket::serialize( ar ); ar << COMPACT_INT(m_iSignal); } std::string SignalSessionPacket::toStr() const { return SessionPacket::toStr() + str(boost::format("SignalSessionPacket: m_iSignal: %1%\n") % m_iSignal); } /* ***************************************************** */ /* * RevertSessionPacket */ /* ***************************************************** */ RevertSessionPacket::RevertSessionPacket(const UT_UTF8String& sSessionId, const UT_UTF8String& sDocUUID, UT_sint32 iRev) : SessionPacket(sSessionId, sDocUUID), m_iRev(iRev) { } void RevertSessionPacket::serialize( Archive& ar ) { SessionPacket::serialize( ar ); ar << COMPACT_INT(m_iRev); } std::string RevertSessionPacket::toStr() const { return SessionPacket::toStr() + str(boost::format("RevertSessionPacket: m_iRev: %1%\n") % m_iRev); } /* ***************************************************** */ /* * RevertAckSessionPacket */ /* ***************************************************** */ RevertAckSessionPacket::RevertAckSessionPacket(const UT_UTF8String& sSessionId, const UT_UTF8String& sDocUUID, UT_sint32 iRev) : SessionPacket(sSessionId, sDocUUID), m_iRev(iRev) { } void RevertAckSessionPacket::serialize( Archive& ar ) { SessionPacket::serialize( ar ); ar << COMPACT_INT(m_iRev); } std::string RevertAckSessionPacket::toStr() const { return SessionPacket::toStr() + str(boost::format("RevertAckSessionPacket: m_iRev: %1%\n") % m_iRev); }