查看单个帖子
旧 2009-05-05, 12:35 PM   #1
yang686526
高级会员
 
注册日期: 06-11
帖子: 14579
精华: 1
现金: 224494 标准币
资产: 234494 标准币
yang686526 向着好的方向发展
默认 【转帖】how to add extended entity data

how to add extended entity data
how to add extended entity data
can some one point me in the right direction please.
i have a oddbpointptr object and i want to add a text string as extended entity data.
the type of data is "datum" and it would have a value like "10-11-1234"
thank you.
yuu can use eighter xdata or an extension dictionary.
the xdata functions are oddbobject::setxdata() and oddbobject::xdata().
the dict functions are oddbobject::extensiondictionary(), oddbobject::createextensiondictionary() and oddbobject::releaseextensiondictionary(); after creating such a dictionary, you can associate a string name serving as a key to some database object identified by oddbobjectid - in the common case this can be oddbxrecord.
extension dictionary is more flexible way, but the xdata are more optimized.
hope this helps.
examples can be found in the odwriteex example.
regards
chudomir
i see setxdata mentioned in help and i see it used in the sample dbfiller source code file.
but i am still not sure..
i want to add xdata in such a way so that later on, in another program or something i can see if the entity has "datum" extended entity data and if so, to get the "datum value" text string....
not quite sure what i should be adding into the odresbufptr object...
andrew
you mean in another program or utility, but will this other program have access to dwgdirect?
regards
chudomir
what is wrong with this:
oddbpointptr ppoint;
ppoint = oddbpoint::createobject();
pmodelspaceblock->appendoddbentity( ppoint );
ppoint->setposition(
odgepoint3d( m_sinsertion.x,
m_sinsertion.y,
m_sinsertion.z ) );
[snip]
addextendedattributedata( ppoint, strattribdesc, strattribvalue );
[snip]
void csequentialentity::addextendedattributedata(oddbob jectptr pobject, cstring strattribute, cstring strvalue)
{
odresbufptr xiter = odresbuf::newrb( 1001 );
odresbufptr temp = xiter;
temp->setstring( (lpctstr)strattribute ); // application
temp->setnext( odresbuf::newrb( 1000 ) );
temp->setstring( (lpctstr)strvalue );
pobject->setxdata( xiter );
}
it crashes when doing the setxdata...
}
when you set the xdata to an object you must associate the list with xdata with a certain application - which must be registered as a record in the reg app table in the initialization of yuur database.
for example: pdb->newregapp("myregapp");
second:
you should check whether a list already exists for that object - i.e. to iterate it manually. this is quite a boring task. i'll post an example xdata manager in a while.
to create properly the data here:
odresbufptr xiter = odresbuf::newrb(odresbuf::kdxfregappname);
odresbufptr temp = xiter;
temp->setstring("myregapp"); // application - choose your name
temp->setnext( odresbuf::newrb( odresbuf::kdxfxdasciistring ) );
/*!!*/ temp = temp->next();
temp->setstring( (lpctstr)strattribute );
temp->setnext( odresbuf::newrb( odresbuf::kdxfxdasciistring ) );
/*!!*/ temp = temp->next();
temp->setstring( (lpctstr)strvalue );
pobject->setxdata( xiter );
that now works, thanks.
this thing about the app...
if i were to call the app datum then i only need to assign the attribute value as data?
what i am trying to work out is what the app should be called.
eventually, another program (from whatever source, lisp, dwgdirect) will examine each entity and want to say "does it have datum extended data (which is the value, not the word "datum"). hence maybe using a reg app of datum would be good?
xdata manager
here is a xdata manager - it associates a integer (enum) key to a given value.
anyone feel free to use it. (think it is working...)
it is a template class and you can organize all your xdata through it:
code:
template <int code> struct getxdatainfo
{ _assertstatic(false,pleasespecialize); };

#define _getxdatainfo(_code,_type,_defvalue)\
template<> struct getxdatainfo<_code> {\
typedef _type type;\
static type defaultvalue() { return _defvalue; }\
};

template <class type> struct getxdatatypecode { _assertstatic(false,pleasespecialize); };
#define _getxdatatypecode(_type,_typecode)\
template <> struct getxdatatypecode<_type> { enum {result=_typecode}; };
// do not change the order in this enum!
// only add new entries at the end;
enum xdatanames
{
xdatanames_dummyfirst,

layerindex,
layertouched,
layersnappable,
linetypeindex,
linetypepixeldata,
entmarked,
entconelement,
pointindex,
dictusrdataver,
xrecordusrdataver,
layerpassword,
layerispsprotected,
groupentitiesowner,
blockcursorposition,
xrecordusrdatatype,
entplottable,
blockscopemode,
blockscopegroupsxrec,
blockscopetypesxrec,
blockscopeseltoexclude,

xdatanames_dummylast
};
_getxdatainfo(layerindex,odint32,-1);
_getxdatainfo(layertouched,bool,false);
_getxdatainfo(layersnappable,bool,true);
_getxdatainfo(linetypeindex,odint32,-1); // assume there is no index if -1 is returned
_getxdatainfo(linetypepixeldata,odint32,0); // assume that if we have linepattern of 0 we have no linepattern;
_getxdatainfo(entmarked,bool,false);
_getxdatainfo(entconelement,bool,false);
_getxdatainfo(pointindex,odint32,0);
_getxdatainfo(dictusrdataver,odint32,-1); // do not allow default value for the version -
_getxdatainfo(xrecordusrdataver,odint32,-1); // must be always written
_getxdatainfo(layerpassword,odstring,"");
_getxdatainfo(layerispsprotected,bool,false);
_getxdatainfo(groupentitiesowner,oddbsoftpointerid,oddbsoftpointerid::knull);
_getxdatainfo(blockcursorposition,odgepoint3d,odgepoint3d::korigin);
_getxdatainfo(xrecordusrdatatype,odint32,0);
_getxdatainfo(entplottable,bool,true);
_getxdatainfo(blockscopemode,odint32,0);
_getxdatainfo(blockscopegroupsxrec,oddbhandle,0);
_getxdatainfo(blockscopetypesxrec,oddbhandle,0);
_getxdatainfo(blockscopeseltoexclude,odstring,"");

_getxdatatypecode(odint32,odresbuf::kdxfxdinteger32);
_getxdatatypecode(bool,odresbuf::kdxfxdinteger16);
_getxdatatypecode(odint16,odresbuf::kdxfxdinteger16);
_getxdatatypecode(odstring,odresbuf::kdxfxdasciistring);
_getxdatatypecode(oddbsoftpointerid,odresbuf::kdxfsoftpointerid);
_getxdatatypecode(oddbhardpointerid,odresbuf::kdxfhardpointerid);
_getxdatatypecode(oddbsoftownershipid,odresbuf::kdxfsoftownershipid);
_getxdatatypecode(oddbhardownershipid,odresbuf::kdxfhardownershipid);
_getxdatatypecode(oddbhandle,odresbuf::kdxfxdhandle);
_getxdatatypecode(odgepoint3d,odresbuf::kdxfxdworldxcoord);
//_getxdatatypecode(dxfxdworldycoordtype,dxfxdworldycoordtype::value);
//_getxdatatypecode(dxfxdworldzcoordtype,dxfxdworldzcoordtype::value);
template <class t> struct xdatareader
{ _assertstatic(false,pleasespecialize); };
template <> struct xdatareader<odint32>
{ static odint32 read(odresbuf* p) { return p->getint32(); } };
template <> struct xdatareader<bool>
{ static bool read(odresbuf* p) { return p->getint16()!=0; } };
template <> struct xdatareader<odstring>
{ static odstring read(odresbuf* p) { return p->getstring(); } };
template <> struct xdatareader<odint16>
{ static odint16 read(odresbuf* p) { return p->getint16(); } };
template <> struct xdatareader<odgepoint3d>
{ static odgepoint3d read(odresbuf* p) { return p->getpoint3d(); } };
// fixme: isn't there more elegant way to pass the info to getobjectid()?
// set this before using xdata of type ...pointerid or ...ownershipid
oddbdatabase* g_pdb = 0;
template <> struct xdatareader<oddbhandle>
{ static oddbhandle read(odresbuf* p) { return p->gethandle(); } };
template <> struct xdatareader<oddbsoftpointerid>
{ static oddbsoftpointerid read(odresbuf* p) { return p->getobjectid(g_pdb); } };
template <> struct xdatareader<oddbhardpointerid>
{ static oddbhardpointerid read(odresbuf* p) { return p->getobjectid(g_pdb); } };
template <> struct xdatareader<oddbsoftownershipid>
{ static oddbsoftownershipid read(odresbuf* p) { return p->getobjectid(g_pdb); } };
template <> struct xdatareader<oddbhardownershipid>
{ static oddbhardownershipid read(odresbuf* p) { return p->getobjectid(g_pdb); } };
template <class t> struct xdatawriter
{ _assertstatic(false,pleasespecialize); };
template <> struct xdatawriter<odint32>
{ static void write(odresbufptr p, odint32 val) { p->setint32(val); } };
template <> struct xdatawriter<bool>
{ static void write(odresbufptr p, bool val) { p->setint16(val); } };
template <> struct xdatawriter<odstring>
{
static void write(odresbufptr p, const odstring& val) { p->setstring(val); }
static void write(odresbufptr p, const cachar* val) { p->setstring(val); }
};
template <> struct xdatawriter<odint16>
{ static void write(odresbufptr p, odint16 val) { p->setint16(val); } };
template <> struct xdatawriter<oddbsoftpointerid>
{ static void write(odresbuf* p, oddbsoftpointerid val) { p->setobjectid(val); } };
template <> struct xdatawriter<oddbhardpointerid>
{ static void write(odresbuf* p, oddbhardpointerid val) { p->setobjectid(val); } };
template <> struct xdatawriter<oddbsoftownershipid>
{ static void write(odresbuf* p, oddbsoftownershipid val) { p->setobjectid(val); } };
template <> struct xdatawriter<oddbhardownershipid>
{ static void write(odresbuf* p, oddbhardownershipid val) { p->setobjectid(val); } };
template <> struct xdatawriter<odgepoint3d>
{ static void write(odresbuf* p, const odgepoint3d& pt) { p->setpoint3d(pt); } };
template <> struct xdatawriter<oddbhandle>
{ static void write(odresbuf* p, const oddbhandle& handle) { return p->sethandle(handle); } };
// write the string of the reg app you want here;
// probably the name of your company
static const cachar* mainkeyname()
{ return "my_xdata_app"; }
// will return the default value if there is no record of the given type
template<int code>
struct accessxdata {
typedef typename getxdatainfo<code>::type type;
enum { typecode = getxdatatypecode<type>::result };

static type read(oddbobject* pobj)
{
_assertptr(pobj,sizeof(oddbobject));
_assert(pobj->isreadenabled());

odresbufptr pxdata = pobj->xdata(mainkeyname());
const type defaultvalue(getxdatainfo<code>::defaultvalue());
if (pxdata.isnull()) return defaultvalue;
//std::stringstream ss;
//dumpgroupcodes(pxdata,ss," ");
//_trace0(ss.str().c_str());

// has some xdata - so we search for the code
// skip the start of the chain
_assert(pxdata->getstring()==mainkeyname());
pxdata = pxdata->next();
_assert(pxdata.isnull()==false); // this should be the version of the xdata
if (pxdata.isnull()) return defaultvalue;
_assert(pxdata->restype()==listversioncode);

pxdata = pxdata->next(); // now start the list
for (; !pxdata.isnull(); pxdata=pxdata->next()) {
_assert(pxdata->restype()==xdatanamecode);
if (keymatch(pxdata)) {
_assert(pxdata->next()->restype()==typecode);
return xdatareader<type>::read(pxdata->next());
} else {
// not the one searched - so go to the next key;
pxdata = pxdata->next();
_assert(!pxdata.isnull());
if (pxdata.isnull()) return defaultvalue;
} //else
} //for

// not found ? so return the default value
return defaultvalue;
}

// if we choose to write the default value and a stored value is missing,
// then we just return, because the read() function will return this same value
// in case of missing record;
static void write(oddbobject* pobj, const type& val)
{
_assertptr(pobj,sizeof(oddbobject));
_assert(pobj->iswriteenabled());
bool bisdefaultvalue = (val == getxdatainfo<code>::defaultvalue());

odresbufptr pxdata = pobj->xdata(mainkeyname());
odresbufptr pwritepoint;
if (pxdata.isnull()) {
// must create new, but only if the value is not the default
if (bisdefaultvalue) return;
// then create the new value
pxdata = odresbuf::newrb(liststartcode);
xdatawriter<odstring>::write(pxdata,mainkeyname());
odresbufptr pxdataversion = odresbuf::newrb(listversioncode,(listversiontype)caxdatamanager::version);
pxdata->setnext(pxdataversion);
pwritepoint = createnewrecord(pxdataversion);
} else {
// check if there is some chain but our record is not there;
// so there is no need to write the default value;
if (!recordexists(pxdata) && bisdefaultvalue)
return;
//std::stringstream ss;
//dumpgroupcodes(pxdata,ss," ");
//_trace0("before getwritepoint:\n");
//_trace0(ss.str().c_str());

// some xdata, so get place at them
pwritepoint = getwritepoint(pxdata);
} //else
//{std::stringstream ss; ss << "before xdatawriter<type>::write(pwritepoint,val):\n";
//dumpgroupcodes(pwritepoint,ss," ");
//_trace0(ss.str().c_str());}
//
//{std::stringstream ss; ss<<"current xdata is:\n";
//dumpgroupcodes(pxdata,ss," ");
//_trace0(ss.str().c_str());}
_assert(pwritepoint.isnull()==false);
xdatawriter<type>::write(pwritepoint,val);
//{std::stringstream ss;
//dumpgroupcodes(pxdata,ss," ");
//_trace0("xdata to be written:\n");
//_trace0(ss.str().c_str());}
_assert(pxdata.isnull()==false);
pobj->setxdata(pxdata);
}
private:
// code for storing the xdata name
enum { xdatanamecode = odresbuf::kdxfxdinteger16 };
enum { liststartcode = odresbuf::kdxfregappname };
enum { listversioncode = odresbuf::kdxfxdinteger16 };

typedef odint16 xdatanametype;
typedef odint16 listversiontype;

static bool keymatch(odresbuf* p)
{
_assertptr(p,sizeof(odresbuf));
return (p->restype()==xdatanamecode) &&
(xdatareader<xdatanametype>::read(p) == code);
}
static bool recordexists(odresbufptr pxdata)
{
for (odresbufptr p=pxdata; !p.isnull(); p=p->next()) {
if (keymatch(p)) {
return true;
} //if
} //for
return false;
}
// returns a place where to set a xdata, for the given xdata list;
// if there is no such place, it will be created at the end of the list;
static odresbufptr getwritepoint(odresbufptr pxdata)
{
_assert(pxdata.isnull()==false);

// first is written the caddie application - we skip it
_assert(pxdata->restype()==liststartcode);
pxdata = pxdata->next();

_assert(pxdata.isnull()==false);

// now comes the version saved in the res buf
_assert(pxdata->restype()==listversioncode);
_assert(pxdata->getint16()<=caxdatamanager::version);
pxdata = pxdata->next(); // skip the version also

odresbufptr plast;
for (odresbufptr p=pxdata; !p.isnull(); p=p->next()) {
if (keymatch(p)) {
// the next is the insert point
_assert(p->next().isnull()==false);
return p->next();
} //if
p = p->next(); // skip the value written
_assert(p.isnull()==false);
plast = p; // save the last
} //for

// if we are here, then none was found = we must create a
// new one at the bottom of the list
_assert(plast.isnull()==false);
return createnewrecord(plast);
}

static odresbufptr createnewrecord(odresbufptr pxdata)
{
// assert for being the last one in the chain
_assert(pxdata->next().isnull());

// first write the key, and then item for the value
odresbufptr pkey( odresbuf::newrb(xdatanamecode,(xdatanametype)code) );
//_trace("code written = %i\n",code);
pxdata->setnext(pkey);
pkey->setnext(odresbuf::newrb(typecode));
return pkey->next();
}
static void dumpgroupcodes(odresbuf* xiter, std::stringstream& os, odstring pad)
{
for (; xiter != 0; xiter = xiter->next())
{
int code = xiter->restype();
os << pad.c_str() << getodresbufcode(code) << "(code:" << code <<")" << ", ";
switch (oddxfcode::_gettype(code))
{
case oddxfcode::name:
case oddxfcode::string:
os << xiter->getstring().c_str();
break;
case oddxfcode::bool:
os << xiter->getbool();
break;
case oddxfcode::integer8:
os << xiter->getint8();
break;
case oddxfcode::integer16:
os << xiter->getint16();
break;
case oddxfcode::integer32:
os << xiter->getint32();
break;
case oddxfcode::double:
os << xiter->getdouble();
break;
case oddxfcode::angle:
os << xiter->getdouble();
break;
case oddxfcode:oint:
{
odgepoint3d p = xiter->getpoint3d();
os << p.x << ", " << p.y << ", " << p.z;
}
break;
case oddxfcode::binarychunk:
os << "<binary data>";
break;
case oddxfcode::handle:
case oddxfcode::layername:
//os << xiter->gethandle();
os << xiter->getstring().c_str();
break;
case oddxfcode:bjectid:
case oddxfcode::softpointerid:
case oddxfcode::hardpointerid:
case oddxfcode::softownershipid:
case oddxfcode::hardownershipid:
{
oddbhandle h = xiter->gethandle();
os << h.ascii();
break;
}
case oddxfcode::unknown:
default:
os << "unknown";
break;
}
os << "\n";//std(endl);
}
}

};
well..., an example usage:
imagine you want to create a new value identified by some known integer key.
1) you must add this integer key in the xdatanames enum
enum xdatanames
{
xdatanames_dummyfirst,
...
mynewdata,
xdatanames_dummylast
};
be sure to add it in the end of the enum.
2) then you must describe the type of the new data.
use the macro getxdatainfo. also provide a default value.
_getxdatainfo(mynewdata,odint32,-1);
3) now you have access to the value:
oddbobject pobj;
odint32 nvalue = accessxdata<mynewdata>::read(pobj);
if there is no value written there, the default value will be returned - -1 in our case.
or:
accessxdata<mynewdata>::write(pobj,45);
if we type accessxdata<mynewdata>::write(pobj,-1); no data will be written, because this is the default value.
the good in this manager is that it takes care whether the xdata fields are empty or not. you should not care of the order of the data written into the xdata list. just define an integer key and access the data.
you can have data of type odint32, odstring, bool, odgepoint3d, and oddbhandle. usually they are enough.
regards
chudomir
well, you may group all the xdata provided by your application or your company in one reg app value.
and the readers of the xdata may just search through the linked list and search first for the value "datum" of you reg app "ajtruckle" and then they will "know" that the next element after the value "datum" will be the value of your attribute datum.
by the way, if you use the xdata manager i've just provided, you'll have an automated way in c++ to access the xdata. however the "key" there will not be string "datum", but an integer number - element of an enum list. this enum may be given to the lisp programmers and they will know what values to search about. this is because the xdata mananager writes the couples of odresbuf values inside the xdata list - first is the integer from the enum and then is the value.
regards
chudomir
thanks for code and clarification
ok. i am not using the template you provided, but i can get a simple code snippet to work that adds primitive types to a single application, similar to what was posted above.
now, what if i want to add two different application lists to the xdata on my entity? it seems that if i add a second application name, code 1001 or oddb::kdxfregappname, then is just gets added as a string under the first app name. so how do i add both?
thanks,
ryan
hopefully chudo can answer your question.
maybe you have to set what the current reg app is or something?
my needs were very simple. so i can't help you. sorry.
andrew
code 1001 with a string always indicates beginning of xdata associated with application with name "string".
code:
1001
app1
1000
string data of app1
1001
app2
1000
string data of app2
sergey slezkin
can you please point out some sample code, ryan? thanks
best regards
chudomir
sorry for the trouble. i was doing the writing correctly, but it was my verification method that was faulty. it all works great now. thanks for the help.
ryan
yang686526离线中   回复时引用此帖
GDT自动化论坛(仅游客可见)