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