1 /* $NoKeywords: $ */
2 /*
3 //
4 // Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved.
5 // OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
6 // McNeel & Associates.
7 //
8 // THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
9 // ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
10 // MERCHANTABILITY ARE HEREBY DISCLAIMED.
11 //
12 // For complete openNURBS copyright information see <http://www.opennurbs.org>.
13 //
14 ////////////////////////////////////////////////////////////////
15 */
16
17 #include "opennurbs.h"
18
19 ON_VIRTUAL_OBJECT_IMPLEMENT(ON_UserData,ON_Object,"850324A7-050E-11d4-BFFA-0010830122F0");
20
ON_UserData()21 ON_UserData::ON_UserData()
22 : m_userdata_uuid(ON_nil_uuid),
23 m_application_uuid(ON_nil_uuid),
24 m_userdata_copycount(0),
25 m_userdata_xform(1),
26 m_userdata_owner(0),
27 m_userdata_next(0)
28 {}
29
ON_UserData(const ON_UserData & src)30 ON_UserData::ON_UserData(const ON_UserData& src)
31 : ON_Object(src),
32 m_userdata_uuid(src.m_userdata_uuid),
33 m_application_uuid(src.m_application_uuid),
34 m_userdata_copycount(src.m_userdata_copycount),
35 m_userdata_xform(src.m_userdata_xform),
36 m_userdata_owner(0), // do not copy owner
37 m_userdata_next(0) // do not copy next
38 {
39 if ( m_userdata_copycount)
40 {
41 m_userdata_copycount++;
42 if ( !m_userdata_copycount )
43 m_userdata_copycount = 1;
44 }
45 }
46
47 //virtual
Archive() const48 ON_BOOL32 ON_UserData::Archive() const
49 {
50 // If you want your userdata to be saved, you must override
51 // ON_UserData::Archive() and have it return true.
52 return false;
53 }
54
55 //virtual
Transform(const ON_Xform & x)56 ON_BOOL32 ON_UserData::Transform(const ON_Xform& x )
57 {
58 m_userdata_xform = x*m_userdata_xform;
59 return true;
60 }
61
operator =(const ON_UserData & src)62 ON_UserData& ON_UserData::operator=(const ON_UserData& src)
63 {
64 // 16 January 2004 Dale Lear
65 // Do not copy the m_userdata_uuid, m_application_uuid,
66 // m_userdata_owner, or m_userdata_next values.
67 // The m_userdata_uuid and m_application_uuid are
68 // set when the class is constructed and should not be
69 // changed. The m_userdata_owner and m_userdata_next
70 // values are set when the user data is attached
71 // to a parent object.
72 if ( this != &src )
73 {
74 ON_Object::operator=(src);
75 m_userdata_copycount = src.m_userdata_copycount;
76 m_userdata_xform = src.m_userdata_xform;
77 if ( 0 != m_userdata_copycount )
78 {
79 m_userdata_copycount++;
80 if ( !m_userdata_copycount )
81 m_userdata_copycount = 1;
82 }
83 }
84
85 return *this;
86 }
87
~ON_UserData()88 ON_UserData::~ON_UserData()
89 {
90 ON_Object* owner = m_userdata_owner;
91 if ( owner ) {
92 // remove this piece of user data from owner->m_userdata_list
93 ON_UserData* prev = 0;
94 ON_UserData* p;
95 for ( p = owner->m_userdata_list; p; prev = p, p = p->m_userdata_next ) {
96 if ( p == this ) {
97 if ( prev ) {
98 prev->m_userdata_next = p->m_userdata_next;
99 }
100 else {
101 owner->m_userdata_list = p->m_userdata_next;
102 }
103 p->m_userdata_next = 0;
104 p->m_userdata_owner = 0;
105 break;
106 }
107 }
108 }
109 }
110
Dump(ON_TextLog & text_log) const111 void ON_UserData::Dump( ON_TextLog& text_log ) const
112 {
113 text_log.Print("User Data:\n");
114 text_log.PushIndent();
115
116 // print class name and class uuid
117 ON_Object::Dump(text_log);
118
119 // developer's user data description
120 ON_wString description;
121 const_cast<ON_UserData*>(this)->GetDescription(description);
122 if ( description.IsEmpty() )
123 description = L"none";
124 const wchar_t* ws = description;
125 text_log.Print("user data description: %ls\n",ws);
126 text_log.Print("user data uuid: ");
127 text_log.Print(m_userdata_uuid);
128 text_log.Print("\n");
129 text_log.Print("user data copy count: %d\n",this->m_userdata_copycount);
130
131 // archive setting
132 text_log.Print("user data saved in 3dm archive: %s\n",Archive() ? "yes" : "no");
133
134 text_log.PopIndent();
135 }
136
SizeOf() const137 unsigned int ON_UserData::SizeOf() const
138 {
139 unsigned int sz = ON_Object::SizeOf();
140 sz += (sizeof(*this) - sizeof(ON_Object));
141 return sz;
142 }
143
IsValid(ON_TextLog * text_log) const144 ON_BOOL32 ON_UserData::IsValid( ON_TextLog* text_log ) const
145 {
146 if ( 0 == ON_UuidCompare( &m_userdata_uuid, &ON_nil_uuid ) )
147 {
148 if ( 0 != text_log )
149 {
150 text_log->Print("invalid userdata - m_userdata_uuid = nil\n");
151 }
152 return false;
153 }
154
155 if ( 0 == ON_UuidCompare( m_userdata_uuid, ON_UserData::ClassId()->Uuid() ) )
156 {
157 if ( 0 != text_log )
158 {
159 text_log->Print("invalid userdata - m_userdata_uuid in use. Use guidgen to get a unique id.\n");
160 }
161 return false;
162 }
163
164 if ( Archive() && 0 == ON_UuidCompare( ClassId()->Uuid(), ON_UserData::ClassId()->Uuid() ) )
165 {
166 // 8 January 2004 Dale Lear:
167 // I added this test to help developers remember to use
168 // the ON_DECLARE_OBJECT/ON_IMPLEMENT_OBJECT macros when
169 // they create user data that gets archived.
170 if ( 0 != text_log )
171 {
172 text_log->Print("invalid userdata - classes derived from ON_UserData that get saved in 3dm archives must have a class id and name defined by ON_OBJECT_DECLARE/ON_OBJECT_IMPLEMENT.\n");
173 }
174 return false;
175 }
176
177 return true;
178 }
179
Owner() const180 ON_Object* ON_UserData::Owner() const
181 {
182 return m_userdata_owner;
183 }
184
Next() const185 ON_UserData* ON_UserData::Next() const
186 {
187 return m_userdata_next;
188 }
189
UserDataClassUuid() const190 ON_UUID ON_UserData::UserDataClassUuid() const
191 {
192 const ON_ClassId* cid = ClassId();
193 return ( cid == &ON_UnknownUserData::m_ON_UnknownUserData_class_id )
194 ? ((ON_UnknownUserData*)this)->m_unknownclass_uuid
195 : cid->Uuid();
196 }
197
IsUnknownUserData() const198 ON_BOOL32 ON_UserData::IsUnknownUserData() const
199 {
200 return (ClassId() == &ON_UnknownUserData::m_ON_UnknownUserData_class_id)?true:false;
201 }
202
GetDescription(ON_wString & description)203 ON_BOOL32 ON_UserData::GetDescription( ON_wString& description )
204 {
205 return true;
206 }
207
208
209 ON_OBJECT_IMPLEMENT(ON_UnknownUserData,ON_UserData,"850324A8-050E-11d4-BFFA-0010830122F0");
210
ON_UnknownUserData()211 ON_UnknownUserData::ON_UnknownUserData()
212 : m_unknownclass_uuid(ON_nil_uuid)
213 , m_sizeof_buffer(0)
214 , m_buffer(0)
215 , m_3dm_version(0)
216 , m_3dm_opennurbs_version(0)
217 {}
218
ON_UnknownUserData(const ON_UnknownUserData & src)219 ON_UnknownUserData::ON_UnknownUserData(const ON_UnknownUserData& src)
220 : ON_UserData(src)
221 , m_unknownclass_uuid(ON_nil_uuid)
222 , m_sizeof_buffer(0)
223 , m_buffer(0)
224 , m_3dm_version(0)
225 , m_3dm_opennurbs_version(0)
226 {
227 if ( m_userdata_copycount > 0 && src.m_sizeof_buffer > 0 && src.m_buffer )
228 {
229 // For most kinds of user data except ON_UnknownUserData,
230 // m_userdata_uuid is set by the constructor and should not
231 // be copied from src (which may be a derived class). However,
232 // for ON_UnknownUserData, the value of m_userdata_uuid is
233 // varies because it is set by the missing userdata class.
234 // So it has to be copied here.
235 m_userdata_uuid = src.m_userdata_uuid;
236
237 m_unknownclass_uuid = src.m_unknownclass_uuid;
238 m_sizeof_buffer = src.m_sizeof_buffer;
239 m_buffer = onmemdup( src.m_buffer, src.m_sizeof_buffer);
240 m_3dm_version = src.m_3dm_version;
241 m_3dm_opennurbs_version = src.m_3dm_opennurbs_version;
242 }
243 }
244
operator =(const ON_UnknownUserData & src)245 ON_UnknownUserData& ON_UnknownUserData::operator=(const ON_UnknownUserData& src)
246 {
247 if ( this != &src )
248 {
249 m_sizeof_buffer = 0;
250 if ( 0 != m_buffer )
251 {
252 onfree(m_buffer);
253 m_buffer = 0;
254 }
255
256 // ON_UserData::operator= handles setting m_userdata_copycount and
257 // m_userdata_xform.
258 ON_UserData::operator=(src);
259
260 // For most kinds of user data except ON_UnknownUserData,
261 // m_userdata_uuid and m_application_uuid are set by the
262 // constructor and should not be altered by an operator=.
263 // However, for ON_UnknownUserData, the value of m_userdata_uuid
264 // and m_application_uuid vary because they are set by the
265 // missing userdata class. So they have to be copied here.
266 m_userdata_uuid = src.m_userdata_uuid;
267 m_application_uuid = src.m_application_uuid; // fix added 26 January 2010
268
269 if ( m_userdata_copycount > 0 && src.m_sizeof_buffer > 0 && src.m_buffer )
270 {
271 m_unknownclass_uuid = src.m_unknownclass_uuid;
272 m_sizeof_buffer = src.m_sizeof_buffer;
273 m_buffer = onmemdup( src.m_buffer, src.m_sizeof_buffer);
274 m_3dm_version = src.m_3dm_version;
275 m_3dm_opennurbs_version = src.m_3dm_opennurbs_version;
276 }
277 else
278 {
279 // The unknown user data is not supposed to copy
280 m_userdata_uuid = ON_nil_uuid;
281 m_unknownclass_uuid = ON_nil_uuid;
282 m_sizeof_buffer = 0;
283 m_buffer = 0;
284 m_3dm_version = 0;
285 m_3dm_opennurbs_version = 0;
286 }
287 }
288 return *this;
289 }
290
~ON_UnknownUserData()291 ON_UnknownUserData::~ON_UnknownUserData()
292 {
293 if ( m_buffer )
294 onfree(m_buffer);
295 }
296
SizeOf() const297 unsigned int ON_UnknownUserData::SizeOf() const
298 {
299 return ON_UserData::SizeOf()
300 + (sizeof(ON_UnknownUserData)-sizeof(ON_UserData))
301 + m_sizeof_buffer;
302 }
303
GetDescription(ON_wString & s)304 ON_BOOL32 ON_UnknownUserData::GetDescription( ON_wString& s )
305 {
306 s = "Unknown user data. (Definition of class was not available when object was read.)";
307 return true;
308 }
309
IsValid(ON_TextLog * text_log) const310 ON_BOOL32 ON_UnknownUserData::IsValid( ON_TextLog* text_log ) const
311 {
312 ON_BOOL32 rc = ON_UserData::IsValid(text_log);
313
314 // valid unknown user data must have something in it
315 if (rc)
316 rc = (m_sizeof_buffer>0);
317 if (rc)
318 rc = (m_buffer != NULL);
319
320 // the unknown class uuid cannot be nil
321 if (rc)
322 rc = ON_UuidCompare( &m_unknownclass_uuid, &ON_nil_uuid );
323
324 // the unknown class uuid cannot be the ON_UnknownUserData class uuid
325 if (rc) {
326 ON_UUID ON_UnknownUserData_classuuid = ON_UnknownUserData::m_ON_UnknownUserData_class_id.Uuid();
327 rc = ON_UuidCompare( &m_unknownclass_uuid, &ON_UnknownUserData_classuuid );
328 }
329 return rc?true:false;
330 }
331
Dump(ON_TextLog & dump) const332 void ON_UnknownUserData::Dump( ON_TextLog& dump ) const
333 {
334 ON_UserData::Dump(dump);
335 dump.PushIndent();
336 dump.Print( "unknown class uuid: ");
337 dump.Print( m_unknownclass_uuid );
338 dump.Print( "\n");
339 dump.Print( "Data size in 3dm archive: %d bytes\n",m_sizeof_buffer);
340 dump.PopIndent();
341 }
342
Archive() const343 ON_BOOL32 ON_UnknownUserData::Archive() const
344 {
345 // 22 January 2004 Dale Lear
346 // Since unknown userdata is only created when
347 // an archive is read, it has to be saved.
348 return true;
349 }
350
Write(ON_BinaryArchive & file) const351 ON_BOOL32 ON_UnknownUserData::Write( ON_BinaryArchive& file ) const
352 {
353 return file.WriteByte(m_sizeof_buffer,m_buffer);
354 }
355
Read(ON_BinaryArchive & file)356 ON_BOOL32 ON_UnknownUserData::Read( ON_BinaryArchive& file )
357 {
358 m_buffer = onrealloc( m_buffer, m_sizeof_buffer );
359 m_3dm_version = file.Archive3dmVersion();
360 return file.ReadByte(m_sizeof_buffer,m_buffer);
361 }
362
363
364 class ON_UnknownUserDataArchive : public ON_BinaryArchive
365 {
366 // This class is used to define an ON_BinaryArchive that can be used
367 // in ON_UnknownUserData::Convert() to initialize a ON_UserData class
368 // from a memory buffer.
369 public:
370 ON_UnknownUserDataArchive( const ON_UnknownUserData& );
371 ~ON_UnknownUserDataArchive();
372
373 // ON_BinaryArchive overrides
374 size_t CurrentPosition( // current offset (in bytes) into archive ( like ftell() )
375 ) const;
376 bool SeekFromCurrentPosition( // seek from current position ( like fseek( ,SEEK_CUR) )
377 int // byte offset ( >= -CurrentPostion() )
378 );
379 bool SeekFromStart( // seek from current position ( like fseek( ,SEEK_SET) )
380 size_t // byte offset ( >= 0 )
381 );
382 bool AtEnd() const; // true if at end of file
383
384 protected:
385 size_t Read( size_t, void* ); // return actual number of bytes read (like fread())
386 size_t Write( size_t, const void* );
387 bool Flush();
388
389 private:
390 ON_UnknownUserDataArchive();
391
392 size_t m_sizeof_buffer;
393 const unsigned char* m_buffer;
394 size_t m_buffer_position;
395 };
396
ON_UnknownUserDataArchive(const ON_UnknownUserData & ud)397 ON_UnknownUserDataArchive::ON_UnknownUserDataArchive( const ON_UnknownUserData& ud ) : ON_BinaryArchive( ON::read3dm )
398 {
399 SetArchive3dmVersion(ud.m_3dm_version);
400 m_sizeof_buffer = ud.m_sizeof_buffer;
401 m_buffer = (const unsigned char*)ud.m_buffer;
402 m_buffer_position = 0;
403 }
404
~ON_UnknownUserDataArchive()405 ON_UnknownUserDataArchive::~ON_UnknownUserDataArchive()
406 {
407 }
408
CurrentPosition() const409 size_t ON_UnknownUserDataArchive::CurrentPosition() const
410 {
411 return m_buffer_position;
412 }
413
SeekFromCurrentPosition(int offset)414 bool ON_UnknownUserDataArchive::SeekFromCurrentPosition( int offset )
415 {
416 bool rc = false;
417 if ( offset >= 0 || m_buffer_position >= ((size_t)(-offset)) )
418 {
419 size_t newpos = m_buffer_position + offset;
420 if ( newpos < m_sizeof_buffer )
421 {
422 m_buffer_position = newpos;
423 rc = true;
424 }
425 }
426 return rc;
427 }
428
SeekFromStart(size_t offset)429 bool ON_UnknownUserDataArchive::SeekFromStart( size_t offset )
430 {
431 bool rc = false;
432 if ( offset < m_sizeof_buffer )
433 {
434 if ( offset > 0 )
435 m_buffer_position = offset;
436 else
437 m_buffer_position = 0;
438 rc = true;
439 }
440 return rc;
441 }
442
AtEnd() const443 bool ON_UnknownUserDataArchive::AtEnd() const
444 {
445 return (m_buffer_position >= m_sizeof_buffer) ? true : false;
446 }
447
Read(size_t count,void * buffer)448 size_t ON_UnknownUserDataArchive::Read( size_t count, void* buffer )
449 {
450 size_t maxcount = 0;
451
452 if ( m_sizeof_buffer > m_buffer_position )
453 {
454 maxcount = m_sizeof_buffer - m_buffer_position;
455 }
456
457 if ( count > maxcount )
458 {
459 count = maxcount;
460 }
461
462 if ( count > 0 )
463 {
464 memcpy( buffer, m_buffer+m_buffer_position, count );
465 m_buffer_position += count;
466 }
467
468 return count;
469 }
470
Write(size_t,const void *)471 size_t ON_UnknownUserDataArchive::Write( size_t, const void* )
472 {
473 // ON_UnknownUserDataArchive does not support Write() and Flush()
474 return 0;
475 }
476
Flush()477 bool ON_UnknownUserDataArchive::Flush()
478 {
479 // ON_UnknownUserDataArchive does not support Write() and Flush()
480 return false;
481 }
482
Convert() const483 ON_UserData* ON_UnknownUserData::Convert() const
484 {
485 ON_UserData* ud = NULL;
486 if ( IsValid() ) {
487 const ON_ClassId* pID = ON_ClassId::ClassId( m_unknownclass_uuid );
488 // if pID is NULL, it means the definiton of the unknown user data
489 // is still not available
490 if ( pID ) {
491 // The class definition has been dynamically loaded since the
492 // user data was read from an archive. Use the class's Read()
493 // to convert the buffer into a valid class
494 ON_Object* pObject = pID->Create();
495 if ( pObject ) {
496 ud = ON_UserData::Cast(pObject);
497 if ( !ud )
498 delete pObject;
499 else
500 {
501 // use class's Read() function to initialize class members from buffer
502 ON_UnknownUserDataArchive file(*this);
503 // copy values that would be set by reading the base class
504 ud->m_userdata_copycount = m_userdata_copycount;
505 ud->m_userdata_xform = m_userdata_xform;
506 ud->Read(file);
507 }
508 }
509 }
510 }
511 return ud;
512 }
513
MoveUserDataFrom(const ON_Object & source_object)514 bool ON_UserDataHolder::MoveUserDataFrom( const ON_Object& source_object )
515 {
516 PurgeUserData();
517 MoveUserData(*const_cast<ON_Object*>(&source_object));
518 return (0 != FirstUserData());
519 }
520
MoveUserDataTo(const ON_Object & source_object,bool bAppend)521 bool ON_UserDataHolder::MoveUserDataTo( const ON_Object& source_object, bool bAppend )
522 {
523 if ( !bAppend )
524 {
525 const_cast<ON_Object*>(&source_object)->PurgeUserData();
526 }
527 const_cast<ON_Object*>(&source_object)->MoveUserData(*this);
528 PurgeUserData();
529 return (0 != source_object.FirstUserData());
530 }
531
IsValid(ON_TextLog * text_log) const532 ON_BOOL32 ON_UserDataHolder::IsValid( ON_TextLog* text_log ) const
533 {
534 return true;
535 }
536
537
538 /////////////////////////////////////////////////////////////////
539 //
540 // Built in tool for attaching arbitrary string userdata
541 // to any opennurbs object. The savvy user will probably
542 // use XML in the string.
543
544
ON_UserString()545 ON_UserString::ON_UserString()
546 {
547 }
548
~ON_UserString()549 ON_UserString::~ON_UserString()
550 {
551 }
552
553
Write(ON_BinaryArchive & archive) const554 bool ON_UserString::Write(ON_BinaryArchive& archive) const
555 {
556 bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
557 if (!rc)
558 return false;
559
560 for(;;)
561 {
562 rc = archive.WriteString(m_key);
563 if (!rc) break;
564 rc = archive.WriteString(m_string_value);
565 if (!rc) break;
566
567 break;
568 }
569
570 if ( !archive.EndWrite3dmChunk() )
571 rc = false;
572
573 return rc;
574 }
575
Read(ON_BinaryArchive & archive)576 bool ON_UserString::Read(ON_BinaryArchive& archive)
577 {
578 m_key.Empty();
579 m_string_value.Empty();
580
581 int major_version = 0;
582 int minor_version = 0;
583 bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
584 if (!rc)
585 return false;
586
587 for(;;)
588 {
589 rc = ( 1 == major_version );
590 if (!rc) break;
591 rc = archive.ReadString(m_key);
592 if (!rc) break;
593 rc = archive.ReadString(m_string_value);
594 if (!rc) break;
595
596 break;
597 }
598
599 if ( !archive.EndRead3dmChunk() )
600 rc = false;
601
602 return rc;
603 }
604
Dump(ON_TextLog & text_log) const605 void ON_UserString::Dump(ON_TextLog& text_log) const
606 {
607 const wchar_t* ws = m_key;
608 if ( !ws )
609 ws = L"";
610 text_log.Print("Key: %ls\n", ws);
611
612 ws = m_string_value;
613 if ( !ws )
614 ws = L"";
615 text_log.Print("Value: %ls\n",ws);
616 }
617
618 ON_OBJECT_IMPLEMENT(ON_UserStringList,ON_UserData,"CE28DE29-F4C5-4faa-A50A-C3A6849B6329");
619
ON_UserStringList()620 ON_UserStringList::ON_UserStringList()
621 {
622 m_userdata_uuid = ON_UserStringList::m_ON_UserStringList_class_id.Uuid();
623 m_application_uuid = ON_opennurbs4_id; // opennurbs.dll reads/writes this userdata
624 // The id must be the version 4 id because
625 // V5 SaveAs V4 needs to work.
626 m_userdata_copycount = 1;
627 }
628
~ON_UserStringList()629 ON_UserStringList::~ON_UserStringList()
630 {
631 }
632
GetDescription(ON_wString & description)633 ON_BOOL32 ON_UserStringList::GetDescription( ON_wString& description )
634 {
635 description.Format("User text (%d entries)",m_e.Count());
636 return true;
637 }
638
Archive() const639 ON_BOOL32 ON_UserStringList::Archive() const
640 {
641 return true;
642 }
643
SizeOf() const644 unsigned int ON_UserStringList::SizeOf() const
645 {
646 unsigned int sz = ON_UserData::SizeOf();
647 sz += sizeof(*this) - sizeof(ON_UserData);
648 sz += m_e.SizeOfArray();
649 int i = m_e.Count();
650 while (i--)
651 sz += m_e[i].m_string_value.Length()*sizeof(wchar_t);
652 return sz;
653 }
654
655
DataCRC(ON__UINT32 current_remainder) const656 ON__UINT32 ON_UserStringList::DataCRC(ON__UINT32 current_remainder) const
657 {
658 int count = m_e.Count();
659 for ( int i = 0; i < count; i++ )
660 {
661 current_remainder = m_e[i].m_key.DataCRC(current_remainder);
662 current_remainder = m_e[i].m_string_value.DataCRC(current_remainder);
663 }
664 return current_remainder;
665 }
666
667
Dump(ON_TextLog & text_log) const668 void ON_UserStringList::Dump( ON_TextLog& text_log ) const
669 {
670 int i, count = m_e.Count();
671 text_log.Print("%d entries\n",count);
672 text_log.PushIndent();
673 for ( i = 0; i < count; i++ )
674 {
675 m_e[i].Dump(text_log);
676 }
677 text_log.PopIndent();
678 }
679
Write(ON_BinaryArchive & archive) const680 ON_BOOL32 ON_UserStringList::Write(ON_BinaryArchive& archive) const
681 {
682 bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
683 if ( !rc )
684 return false;
685
686 for(;;)
687 {
688 int count = m_e.Count();
689 rc = archive.WriteInt(count);
690 if(!rc) break;
691
692 for ( int i = 0; i < count && rc; i++ )
693 {
694 rc = m_e[i].Write(archive);
695 }
696 if (!rc) break;
697
698 break;
699 }
700
701 if ( !archive.EndWrite3dmChunk() )
702 rc = false;
703
704 return rc;
705 }
706
Read(ON_BinaryArchive & archive)707 ON_BOOL32 ON_UserStringList::Read(ON_BinaryArchive& archive)
708 {
709 int major_version = 0;
710 int minor_version = 0;
711 bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
712 if ( !rc )
713 return false;
714
715 for(;;)
716 {
717 rc = (1 == major_version);
718 if (!rc) break;
719
720 int count = 0;
721 rc = archive.ReadInt(&count);
722 if(!rc) break;
723
724 for ( int i = 0; i < count; i++ )
725 {
726 rc = m_e.AppendNew().Read(archive);
727 if ( !rc)
728 {
729 m_e.Remove();
730 break;
731 }
732 }
733 if (!rc) break;
734
735 break;
736 }
737
738 if ( !archive.EndRead3dmChunk() )
739 rc = false;
740
741 return rc;
742 }
743
744
SetUserString(const wchar_t * key,const wchar_t * string_value)745 bool ON_UserStringList::SetUserString( const wchar_t* key, const wchar_t* string_value )
746 {
747 if ( !key || !key[0] )
748 return false;
749
750 int i, count = m_e.Count();
751 for (i = 0; i < count; i++ )
752 {
753 if ( !m_e[i].m_key.CompareNoCase(key) )
754 {
755 if ( string_value && string_value[0] )
756 {
757 m_e[i].m_string_value = string_value;
758 }
759 else
760 {
761 m_e.Remove(i);
762 }
763 m_userdata_copycount++;
764 return true;
765 }
766 }
767
768 if ( string_value && string_value[0] )
769 {
770 ON_UserString& e = m_e.AppendNew();
771 e.m_key = key;
772 e.m_string_value = string_value;
773 m_userdata_copycount++;
774 return true;
775 }
776
777 return false;
778 }
779
GetUserString(const wchar_t * key,ON_wString & string_value) const780 bool ON_UserStringList::GetUserString( const wchar_t* key, ON_wString& string_value ) const
781 {
782 if ( key && key[0] )
783 {
784 int i, count = m_e.Count();
785 for (i = 0; i < count; i++ )
786 {
787 if ( !m_e[i].m_key.CompareNoCase(key) )
788 {
789 string_value = m_e[i].m_string_value;
790 return true;
791 }
792 }
793 }
794
795 string_value.Empty();
796 return false;
797 }
798
cmp_hash_2dex_ij(const void * a,const void * b)799 static int cmp_hash_2dex_ij(const void* a, const void* b)
800 {
801 const int* ai = (const int*)a;
802 const int* bi = (const int*)b;
803 // 26 January 2012 Dale Lear
804 // Part of the fix for http://dev.mcneel.com/bugtrack/?q=97693
805 //
806 // The "i" values are actually 32 bit hashes of a string
807 // and are often large. The sign of (ai[0] - bi[0]) cannot
808 // be used to compare ai[0] and bi[0] because integer
809 // overflow occures with values that are large.
810 //
811 ////// NO!
812 //////if ( 0 == (rc = ai[0] - bi[0]) )
813 ////// rc = ai[1] - bi[1];
814 //////return rc;
815
816 if ( ai[0] < bi[0] )
817 return -1;
818 if ( ai[0] > bi[0] )
819 return 1;
820 if ( ai[1] < bi[1] )
821 return -1;
822 if ( ai[1] > bi[1] )
823 return 1;
824 return 0;
825 }
826
SetUserStrings(int count,const ON_UserString * us,bool bReplace)827 int ON_UserStringList::SetUserStrings( int count, const ON_UserString* us, bool bReplace )
828 {
829 int added_count = 0;
830 int i;
831
832 if ( count <= 0 || 0 == us )
833 return 0;
834
835 if ( 1 == count )
836 {
837 // skip the hash table hoo haa
838 if ( us[0].m_key.IsEmpty() )
839 return 0;
840 for ( i = 0; i < m_e.Count(); i++ )
841 {
842 if ( m_e[i].m_key.CompareNoCase(us[0].m_key ) )
843 continue;
844 if ( bReplace )
845 {
846 if ( us[0].m_string_value.IsEmpty() )
847 m_e.Remove(i);
848 else
849 m_e[i] = us[0];
850 added_count++;
851 }
852 break;
853 }
854 return added_count;
855 }
856
857 size_t k0, k1;
858 int count0 = m_e.Count();
859 size_t count0_plus_count = (size_t)(count0 + count);
860 ON_2dex* hash = (ON_2dex*)onmalloc( (count0_plus_count + count)*sizeof(hash[0]) );
861 ON_2dex* hash1 = hash + (count0_plus_count);
862 const ON_2dex* h;
863 int deleted_count = 0;
864
865 for ( i = 0; i < count0; i++ )
866 {
867 hash[i].i = (int)m_e[i].m_key.DataCRCLower(0);
868 hash[i].j = i;
869 }
870
871 for ( i = 0; i < count; i++ )
872 {
873 hash1[i].i = (int)us[i].m_key.DataCRCLower(0);
874 hash1[i].j = i;
875 hash[i+count0].i = hash1[i].i;
876 hash[i+count0].j = hash1[i].j+count0;
877 }
878 ON_qsort(hash,count0_plus_count,sizeof(hash[0]),cmp_hash_2dex_ij);
879
880 m_e.Reserve(count0+count);
881 for ( i = 0; i < count; i++)
882 {
883 if ( us[i].m_key.IsEmpty() )
884 continue;
885
886 // Set k0, k1 so that hash[k0]....,hash[k1-1] are
887 // the hash[] entries keys with the same hash code
888 // as us[i].m_key.
889 h = ON_BinarySearch2dexArray(hash1[i].i,hash,count0_plus_count);
890 if ( 0 == h )
891 {
892 ON_ERROR("There is a bug in this function.");
893 continue;
894 }
895 k0 = h-hash;
896 while ( k0 > 0 && h[-1].i == h[0].i )
897 {
898 // set h = first element in hash[] with this hash code.
899 k0--;
900 h--;
901 }
902 for (k1 = k0+1; k1 < count0_plus_count; k1++ )
903 {
904 if ( hash[k1].i != hash[k0].i )
905 break;
906 if ( hash[k1].j > i+count0 )
907 break;
908 }
909
910 if ( hash[k0].j >= count0 )
911 {
912 // There are no entries in m_e[] with key matching hash,
913 // so us[i].m_key is not present in m_e.
914 if ( !us[i].m_string_value.IsEmpty() )
915 {
916 hash[k0].j = count0++;
917 m_e.Append(us[i]);
918 added_count++;
919 }
920 continue;
921 }
922
923 for (/* empty init*/; k0 < k1; k0++ )
924 {
925 if ( hash[k0].j < count0 )
926 {
927 if ( m_e[hash[k0].j].m_key.CompareNoCase(us[i].m_key) )
928 continue; // different keys with same hash
929 if ( bReplace )
930 {
931 m_e[hash[k0].j] = us[i];
932 added_count++;
933 if ( us[i].m_string_value.IsEmpty() )
934 deleted_count++;
935 }
936 break;
937 }
938 }
939
940 if ( k0 >= k1 )
941 {
942 // hash is unique up to this point, so us[i].m_key is unique,
943 // so we add it if it is valid.
944 if ( !us[i].m_string_value.IsEmpty() )
945 {
946 hash[k0].j = count0++;
947 m_e.Append(us[i]);
948 added_count++;
949 }
950 }
951 }
952
953 onfree(hash);
954
955 // remove deleted items.
956 i = m_e.Count();
957 while ( i-- > 0 && deleted_count > 0 )
958 {
959 if ( m_e[i].m_string_value.IsEmpty() )
960 {
961 m_e.Remove(i);
962 deleted_count--;
963 }
964 }
965
966 return added_count;
967 }
968
969
970
971
972
SetUserString(const wchar_t * key,const wchar_t * string_value)973 bool ON_Object::SetUserString( const wchar_t* key, const wchar_t* string_value )
974 {
975 ON_UserStringList* us = ON_UserStringList::Cast(GetUserData(ON_UserStringList::m_ON_UserStringList_class_id.Uuid()));
976 bool b = false;
977 if ( !us )
978 {
979 us = new ON_UserStringList();
980 if ( !AttachUserData(us) )
981 {
982 delete us;
983 us = 0;
984 }
985 else
986 {
987 b = true;
988 }
989 }
990
991 if ( us )
992 {
993 if ( us->SetUserString(key,string_value) )
994 {
995 if ( b && 2 == us->m_userdata_copycount )
996 {
997 // user data is brand new - roll back the
998 // m_userdata_copycount++ that happens in
999 // SetUserString().
1000 us->m_userdata_copycount = 1;
1001 }
1002 b = true;
1003 }
1004 else if ( b )
1005 {
1006 // user data was new-ed up and has nothing in it
1007 // because the input was bogus.
1008 delete us;
1009 us = 0;
1010 b = false;
1011 }
1012 }
1013 return b;
1014 }
1015
1016
SetUserStrings(int count,const ON_UserString * user_strings,bool bReplace)1017 int ON_Object::SetUserStrings( int count, const ON_UserString* user_strings, bool bReplace )
1018 {
1019 if ( 0 == count || 0 == user_strings )
1020 return 0;
1021
1022 int add_count = 0;
1023 int del_count = 0;
1024 for ( int i = 0; i < count; i++ )
1025 {
1026 if ( user_strings[i].m_key.IsEmpty() )
1027 continue;
1028 if ( user_strings[i].m_string_value.IsEmpty() )
1029 del_count++;
1030 else
1031 add_count++;
1032 }
1033 if ( 0 == add_count && 0 == del_count )
1034 return 0;
1035
1036 ON_UserStringList* us = ON_UserStringList::Cast(GetUserData(ON_UserStringList::m_ON_UserStringList_class_id.Uuid()));
1037 if ( !us && add_count > 0)
1038 {
1039 us = new ON_UserStringList();
1040 if ( !AttachUserData(us) )
1041 {
1042 delete us;
1043 us = 0;
1044 }
1045 }
1046
1047 return us ? us->SetUserStrings(count,user_strings,bReplace ) : 0;
1048 }
1049
1050
GetUserString(const wchar_t * key,ON_wString & string_value) const1051 bool ON_Object::GetUserString( const wchar_t* key, ON_wString& string_value ) const
1052 {
1053 string_value.Empty();
1054 const ON_UserStringList* us = ON_UserStringList::Cast(GetUserData(ON_UserStringList::m_ON_UserStringList_class_id.Uuid()));
1055 return us ? us->GetUserString(key,string_value) : false;
1056 }
1057
UserStringCount() const1058 int ON_Object::UserStringCount() const
1059 {
1060 const ON_UserStringList* us = ON_UserStringList::Cast(GetUserData(ON_UserStringList::m_ON_UserStringList_class_id.Uuid()));
1061 return ( 0 != us )
1062 ? us->m_e.Count()
1063 : 0;
1064 }
1065
1066
GetUserStrings(ON_ClassArray<ON_UserString> & user_strings) const1067 int ON_Object::GetUserStrings(
1068 ON_ClassArray<ON_UserString>& user_strings
1069 ) const
1070 {
1071 const int count0 = user_strings.Count();
1072 const ON_UserStringList* us = ON_UserStringList::Cast(GetUserData(ON_UserStringList::m_ON_UserStringList_class_id.Uuid()));
1073 if ( us )
1074 user_strings.Append(us->m_e.Count(),us->m_e.Array());
1075
1076 return user_strings.Count() - count0;
1077 }
1078
GetUserStringKeys(ON_ClassArray<ON_wString> & user_string_keys) const1079 int ON_Object::GetUserStringKeys(
1080 ON_ClassArray<ON_wString>& user_string_keys
1081 ) const
1082 {
1083 const int count0 = user_string_keys.Count();
1084 const ON_UserStringList* us = ON_UserStringList::Cast(GetUserData(ON_UserStringList::m_ON_UserStringList_class_id.Uuid()));
1085 if ( us )
1086 {
1087 user_string_keys.Reserve( count0 + us->m_e.Count() );
1088 for (int i = 0; i < us->m_e.Count(); i++ )
1089 {
1090 user_string_keys.Append(us->m_e[i].m_key);
1091 }
1092 }
1093
1094 return user_string_keys.Count() - count0;
1095 }
1096
1097 ON_OBJECT_IMPLEMENT(ON_DocumentUserStringList,ON_Object,"06F3218E-F5EC-4f6c-B74C-14583F0ED7BC");
1098
ON_DocumentUserStringList()1099 ON_DocumentUserStringList::ON_DocumentUserStringList()
1100 {
1101 }
1102
~ON_DocumentUserStringList()1103 ON_DocumentUserStringList::~ON_DocumentUserStringList()
1104 {
1105 }
1106
IsValid(ON_TextLog * text_log) const1107 ON_BOOL32 ON_DocumentUserStringList::IsValid( ON_TextLog* text_log ) const
1108 {
1109 return true;
1110 }
1111
Dump(ON_TextLog &) const1112 void ON_DocumentUserStringList::Dump( ON_TextLog& ) const
1113 {
1114 }
1115
DataCRC(ON__UINT32 current_remainder) const1116 ON__UINT32 ON_DocumentUserStringList::DataCRC(ON__UINT32 current_remainder) const
1117 {
1118 const ON_UserStringList* us = ON_UserStringList::Cast(GetUserData(ON_UserStringList::m_ON_UserStringList_class_id.Uuid()));
1119 if ( us )
1120 current_remainder = us->DataCRC(current_remainder);
1121 return current_remainder;
1122 }
1123
Write(ON_BinaryArchive & binary_archive) const1124 ON_BOOL32 ON_DocumentUserStringList::Write(ON_BinaryArchive& binary_archive) const
1125 {
1126 // The key/value pairs are saved as ON_UserStringList user data.
1127
1128 // A single char with value 1 is written to permit adding additional
1129 // information later. In that case change the 1 to a 2, uncomment the
1130 // begin block code, and the IO will still work.
1131 //
1132 unsigned char c = 1;
1133 bool rc = binary_archive.WriteChar(c);
1134
1135 ////rc = binary_archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
1136 ////if (!rc)
1137 //// return false;
1138 ////for(;;)
1139 ////{
1140 //// rc = binary_archive.Write...();
1141 //// if (!rc) break;
1142
1143 //// break;
1144 ////}
1145 ////if ( !binary_archive.EndWrite3dmChunk() )
1146 //// rc = false;
1147
1148 return rc;
1149 }
1150
Read(ON_BinaryArchive & binary_archive)1151 ON_BOOL32 ON_DocumentUserStringList::Read(ON_BinaryArchive& binary_archive)
1152 {
1153 // The key/value pairs are saved as ON_UserStringList user data.
1154
1155 unsigned char c = 0;
1156 bool rc = binary_archive.ReadChar(&c);
1157 if ( !rc || c < 1 || c > 2 )
1158 return false;
1159
1160 if ( 2 == c )
1161 {
1162 // The code in this if(2==c) will not be used unless
1163 // the 1 in Write is changed to a 2. This code is here
1164 // so old versions of Rhino will be able to skip over
1165 // any new information that is added at a later date.
1166 int major_version = 0;
1167 int minor_version = 0;
1168 rc = binary_archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
1169 if (!rc)
1170 return false;
1171 for(;;)
1172 {
1173 rc = (1 == major_version);
1174 if (!rc) break;
1175
1176 ////rc = binary_archive.Read(...);
1177 ////if (!rc) break;
1178
1179 break;
1180 }
1181 if (!binary_archive.EndRead3dmChunk() )
1182 rc = false;
1183 }
1184
1185 return rc;
1186 }
1187