HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
PDG_AttributeMap.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * COMMENTS:
7  */
8 
9 #ifndef __PDG_ATTRIBUTE_MAP_H__
10 #define __PDG_ATTRIBUTE_MAP_H__
11 
12 #include "PDG_API.h"
13 
14 #include "PDG_AttributeData.h"
16 #include "PDG_AttributeFile.h"
17 #include "PDG_AttributeGeometry.h"
18 #include "PDG_AttributeHolder.h"
19 #include "PDG_AttributePrimitive.h"
20 #include "PDG_AttributePyObject.h"
21 #include "PDG_AttributeRef.h"
22 #include "PDG_AttributeTypes.h"
23 #include "PDG_EvaluationContext.h"
24 
25 #include <PDGT/PDGT_Value.h>
26 
27 #include <UT/UT_ArrayStringMap.h>
28 #include <UT/UT_ArrayStringSet.h>
29 #include <UT/UT_NonCopyable.h>
30 #include <UT/UT_RWLock.h>
31 #include <UT/UT_StringHolder.h>
32 #include <UT/UT_UniquePtr.h>
33 
34 class UT_JSONValue;
35 class UT_JSONWriter;
36 class UT_OptionsHolder;
37 
38 class PDG_AttributeMerge;
39 class PDG_AttributeOwner;
41 
42 /**
43  * Map for storing, accessing and persisting PDG attribute data.
44  * Attributes are stored in a map of string value to attribute holder, which
45  * maintains a weak/strong reference to an attribute. Writing to an attrib value
46  * results in a deep copy being performed on the specified attribute (i.e. copy
47  * on write). Attribute handles are given to callers in the form of a
48  * PDG_ROAttributeRef or PDG_RWAttributeRef, depending on whether the caller
49  * requested a read only or read/write handle.
50  *
51  * Attributes are owned by a PDG_AttributeOwner instance. In most cases this is
52  * a PDG_WorkItem, and the attributes are accessed/modified when the node
53  * creates or cooks the work item.
54  *
55  * The attribute map maintains a separate table for static and dynamic
56  * attributes. Static attributes are any attributes created during the work
57  * item generation step, and dynamic attributes are attributes created or
58  * modified at any other point in time. If a work item attempts to modify
59  * a static attribute while cooking, including adding output files, then a
60  * copy made is in the dynamic map. The modifications are peformed on that
61  * rather than on the original static attribute. This means that when a work
62  * item is dirtied, it can easily roll back to the exact same state it was in
63  * before cooking. When a work item is being regenerated, only static values
64  * are considered. If the item is dirty then all dynamic attributes are
65  * discarded, otherwise if the item is still valid then the dynamic values
66  * are left as-is.
67  *
68  * The various merge* methods should be used to merge attribute map data
69  * from a parent or dependency on the work item. Care should be taken to only
70  * merge static attributes during work item generation. Dynamic attributes
71  * can be merged at any point between generation and the work item being marked
72  * as cooked.
73  */
75 {
76 public:
77  /// Prefix for all built-in PDG attributes that can potentially appear on
78  /// work items. User-created attributes cannot begin with this prefix.
80 
81  /// Name of the built-in input file list attribute, which contains the
82  /// read only list of input files to a work item
84 
85  /// Name of the built-in output file list attribute, which contains the
86  /// list of all output files from the work item, including any outputs
87  /// copied from inputs.
89 
90  /// Name of the built-in added file list attribute, which contains files
91  /// explicitly added to the work item.
93 
94  /// Name of the built-in command attribute
96 
97  /// Name of the built-in human readable label attribute
99 
100  /// Name of the built-in custom state attribute
102 
103  /// Name of the built-in cook percent attribute
105 
106  /// Name of the built-in partition size attribute
108 
109 
110  /// Special token indicating all attributes, used when binding attributes
111  /// with PDG_AttributeInfo.
113 
114  /// Special token used to indicate a "pseudo" attribute dependency, such
115  /// as the work item's index or frame. These attribute dependencies only
116  /// exist because the values are accessible via expressions like a regular
117  /// attribute, although the value itself is not stored as an attribute.
119 
120 
121  /// Flags for the standard input file attribute
122  static const uint16 theInputFileFlags;
123 
124  /// Flags for the standard output file attribute
126 
127  /// Flags for the standard added file attribute
128  static const uint16 theAddedFileFlags;
129 
130  /// Flags for the standard command string attribute
132 
133  /// Flags for the standard label attribute
134  static const uint16 theLabelFlags;
135 
136  /// Flags for the standard custom state attribute
138 
139  /// Determines when to create an attribute, for refRW method calls that
140  /// can optionally create one if it does not yet exist.
142  {
143  /// Never create the attribute, even if its not found
145 
146  /// Always create the attribute if it does not exist
148 
149  /// Only create the attribute if its necessary to upgrade from
150  /// a static table entry to a dynamic table entry.
152  };
153 
154  /// Enumeration of possible attribute change results during regeneration
156  {
157  /// No changes occured
159 
160  /// Attribute values were changed or added
162 
163  /// At least one attribute was deleted
164  eChangeDestructive
165  };
166 
167  /// Enumeration of attribute tables
169  {
170  /// Static attribute table, attributes in this table are modified when
171  /// the work item is generated
173 
174  /// Dynamic attribute table, attributes in this table are modified
175  /// outside of the generation step (during the cook, precook, etc).
177 
178  /// Number of attribute tables
180  };
181 
182 public:
183  /// Scoped lock of an attribute map and the node that owns the work item
184  /// associated with the attribute map. This is used to serialize access
185  /// to work item attributes when running a node callback or adding results
186  /// from the scheduler.
187  ///
188  /// When a node callback runs, the node's read lock and work item's write
189  /// lock are acquired. When a full-node callback like onRegenerate is
190  /// run, the node's write lock is acquired, effectively locking all attribs
191  /// on all items in the node.
192  ///
193  /// Internally, PDG does not require the use of this lock. The cook logic
194  /// and dependency graph are structure in such a way that work items don't
195  /// need to lock in order to safely access themselves or their parent. This
196  /// lock and the scoped locks around various work item callbacks exist for
197  /// the benefit of external code that requires direct, random access to
198  /// PDG work items. PDG attribute events, for example, can safely read
199  /// attribute data without locking.
200  template <bool Read>
202  {
203  public:
204  /// We must always acquire the global lock first, then the attrib lock.
205  /// Other places in the code such as the onRegenerate callback
206  /// assume that to be the case, and doing so in the wrong order will
207  /// cause a deadlock. The global lock is only acquired as a read lock
208  /// here -- acquring a write lock to the global lock can be used to get
209  /// exclusive access to ALL attribute maps that use the same lock.
210  ScopedLock(const PDG_AttributeMap& attribute_map)
211  : myGlobalLock(attribute_map.globalLock())
212  , myAttribLock(attribute_map.attributeLock())
213  , myAttribMap(attribute_map)
214  {
215  if (myGlobalLock)
216  myGlobalLock->readLock();
217 
218  if (Read)
219  myAttribLock.readLock();
220  else
221  myAttribLock.writeLock();
222 
223  myAttribMap.myWasModified = false;
224  }
225 
227  {
228  if (Read)
229  myAttribLock.readUnlock();
230  else
231  {
232  myAttribMap.onUnlock();
233  myAttribLock.writeUnlock();
234  }
235 
236  if (myGlobalLock)
237  myGlobalLock->readUnlock();
238  }
239 
240  private:
241  UT_RWLock* myGlobalLock;
242  UT_RWLock& myAttribLock;
243  const PDG_AttributeMap& myAttribMap;
244  };
245 
246  /// Version of the above lock that only tries to lock, and sets a flag
247  /// to true or false based on whether the lock was acquired. This should
248  /// be used for UI updates that don't require access to a specific attrib
249  /// and are simply presenting current state, for example. It reduces the
250  /// amount of time the UI spends blocked on PDG structures.
252  {
253  public:
254  ScopedTryLock(const PDG_AttributeMap& attribute_map)
255  : myGlobalLock(attribute_map.globalLock())
256  , myAttribLock(attribute_map.attributeLock())
257  , myDidLockGlobal(false)
258  , myDidLockAttrib(false)
259  {
260  if (!myGlobalLock || myGlobalLock->tryReadLock())
261  {
262  myDidLockGlobal = true;
263  if (myAttribLock.tryReadLock())
264  myDidLockAttrib = true;
265  }
266  }
267 
269  {
270  if (myDidLockAttrib)
271  myAttribLock.readUnlock();
272 
273  if (myGlobalLock && myDidLockGlobal)
274  myGlobalLock->readUnlock();
275  }
276 
277  /// Returns true only if the lock was successful. This method should be
278  /// checked before performing any actions on this attribute map.
279  inline bool didLock() const
280  { return (myDidLockGlobal && myDidLockAttrib); }
281 
282  private:
283  UT_RWLock* myGlobalLock;
284  UT_RWLock& myAttribLock;
285  bool myDidLockGlobal;
286  bool myDidLockAttrib;
287  };
288 
289  /// Runs a functor on this map under the scoped read lock, so it's safe
290  /// for concurrent access on an arbitrary thread. This holds both the
291  /// global read lock and attribute map read lock, so it can always be
292  /// called in order to safely iterate over attribute values. This should
293  /// be used in, for example, an event handler callback in order to ensure
294  /// that the attributes are not also being written to by the owner or
295  /// a full-item list operation performed by another holder of the global
296  /// lock.
297  ///
298  /// If this map does not have an owner, or the owner does not have a
299  /// global lock, the functor will be called with only the local attribute
300  /// lock held.
301  template <typename Func>
302  bool lockedAccess(const Func& func) const
303  {
304  ScopedLock<true> auto_lock(*this);
305  func(*this);
306  return true;
307  }
308 
309  /// Same as above, but only try-locks. If the lock cannot be acquired the
310  /// functor is not run and the method returns false. If the functor is
311  /// run successfully this method returns true.
312  template <typename Func>
313  bool tryLockedAccess(const Func& func) const
314  {
315  ScopedTryLock auto_lock(*this);
316  if (!auto_lock.didLock())
317  return false;
318 
319  func(*this);
320  return true;
321  }
322 
323  /// Iterates over attributes in the map of the specified type, and invokes
324  /// the input function on each matching entry. This function operates on
325  /// the space of attribute names, and therefore will including parent map
326  /// attributes and dynamic attributes, if permitted by the active context.
327  ///
328  /// The supplied function can return true to halt iteration.
329  template <typename Attribute, typename Func>
330  void forEachRO(const Func& func) const
331  {
332  UT_ArrayStringSet names;
333  attributeNames(names, Attribute::TypeEnum);
334 
335  for (auto&& name : names)
336  {
337  auto&& ref = refRO<Attribute>(name);
338  if (ref && func(ref))
339  break;
340  }
341  }
342 
343  /// Iterates over attributes in the map that match the specified
344  /// pattern, and invokes the function on each matching entry.
345  /// Attributes are referenced generally, as PDG_AttributeData references.
346  ///
347  /// The supplied function can return true to halt iteration. This function
348  /// returns true if iteration was interrupted.
349  template <typename Func>
350  bool forEachRO(
352  const Func& func) const
353  {
354  UT_ArrayStringSet names;
355  if (!attributeMatch(names, pattern, false))
356  return false;
357 
358  for (auto&& name : names)
359  {
360  auto&& ref = refRO<PDG_AttributeData>(name);
361  if (ref && func(ref))
362  return true;
363  }
364 
365  return false;
366  }
367 
368  /// Iterates over attributes in the map that match the specified
369  /// pattern and type, and invokes the function on each matching entry.
370  /// Attributes are referenced generally, as PDG_AttributeData references.
371  ///
372  /// The supplied function can return true to halt iteration. This function
373  /// returns true if iteration was interrupted.
374  template <typename Attribute, typename Func>
375  bool forEachRO(
377  const Func& func) const
378  {
379  UT_ArrayStringSet names;
380  if (!attributeMatch(names, pattern, Attribute::TypeEnum, false))
381  return false;
382 
383  for (auto&& name : names)
384  {
385  auto&& ref = refRO<Attribute>(name);
386  if (ref && func(ref))
387  return true;
388  }
389 
390  return false;
391  }
392 
393 public:
394  explicit PDG_AttributeMap(
395  const PDG_AttributeOwner* owner,
396  bool has_runtime);
397  ~PDG_AttributeMap();
398 
399  /// Returns the memory usage of this attribute map
400  int64 getMemoryUsage(bool inclusive) const;
401 
402  /// Swaps the target attribute map contents with this one
403  void swap(PDG_AttributeMap& other);
404 
405  /// Freezes the attributes in the map, which invokes a deep copy on any
406  /// unowned attributes. This makes the map fully standalone, without any
407  /// shallow references to external attribute maps.
408  void freeze();
409 
410  /// Merges all attributes from another map into this one.
411  bool mergeFull(
412  PDG_AttributeMerge& attrib_merge,
413  const PDG_AttributeMap& other,
414  AttribTable dst_table=eTableStatic,
415  bool reset=true);
416 
417  /// Merges all attributes from another attribute owner into this one.
418  bool mergeFull(
419  PDG_AttributeMerge& attrib_merge,
420  const PDG_AttributeOwner* owner,
421  AttribTable dst_table=eTableStatic,
422  bool reset=true);
423 
424  /// Merges static attributes from another attribute owenr into this one.
425  bool mergeStatic(
426  PDG_AttributeMerge& attrib_merge,
427  const PDG_AttributeOwner* owner);
428 
429  /// Merges dynamic attributes from another attribute owenr into this one.
430  bool mergeDynamic(
431  PDG_AttributeMerge& attrib_merge,
432  const PDG_AttributeOwner* owner);
433 
434  /// Sets the parent map
435  void setParentMap(PDG_AttributeOwner* parent);
436 
437  /// Resolves any pending operations in the specified attribute merge map
438  void resolveMergeOps(PDG_AttributeMerge& attrib_merge);
439 
440  /// Erases any dynamic attributes, unowned attributes, or owned attributes
441  /// that were formed by concatenating input attributes. Returns true if
442  /// an attribute that was owned by this map is destroyed.
443  bool eraseDirty(bool include_unowned);
444 
445  /// Removes any stale attributes from the map from the deserialization
446  /// process
447  void eraseStale();
448 
449  /// Erases all attributes in this map
450  PDG_AttributeError eraseAll(bool include_internal);
451 
452  /// Erases all attributes of the specified Attribute type from the map
454 
455  /// Erases an attribute from the map by name
457 
458  /// Clears the contents of any owned concatenated attributes, but does
459  /// not remove them
460  void clearConcat(AttribTable table=eTableCount);
461 
462 
463  /// Returns the set of time dependent attributes for this map
464  bool timeDependentAttributes(
465  UT_ArrayStringSet& names) const;
466 
467  /// Checks for the existance of an attribute by name. Returns true if the
468  /// attribute is found, else false
469  bool hasAttribute(const UT_StringHolder& name) const;
470 
471  /// Returns the type of the attribute with the specified name
472  PDG_AttributeType attributeType(const UT_StringHolder& name) const;
473 
474  /// Returns the size of the attribute with the specified name
475  int attributeSize(const UT_StringHolder& name) const;
476 
477  /// Returns the set of attribute names in the map, for the specified
478  /// attribute type. Values are returned back as a string set.
479  void attributeNames(UT_ArrayStringSet& names,
480  PDG_AttributeType type) const;
481 
482  /// Returns the set of attribute names in the map, for the specified
483  /// attribute type. Values are returned back as an array, sorted by
484  /// attribute name.
485  void attributeNames(UT_StringArray& names,
486  PDG_AttributeType type) const;
487 
488  /// Returns the total number of attributes in the map, for the specified
489  /// atribute type.
490  int numAttributes(PDG_AttributeType type) const;
491 
492  /// Returns true if any attributes in this map match the specified pattern
493  bool attributeMatch(const PDG_AttributePattern& pattern,
494  bool include_internal) const;
495 
496  /// Returns the set of attribute names in the map that match the specified
497  /// attribute pattern. Returns true if anything matches, else false.
498  bool attributeMatch(UT_ArrayStringSet& names,
500  bool include_internal) const;
501 
502  /// Returns the set of attribute names in the map that match the specified
503  /// attribute pattern and attribute type. Returns true if anything matches,
504  /// else false.
505  bool attributeMatch(UT_ArrayStringSet& names,
508  bool include_internal) const;
509 
510  /// Returns the set of attribute names in the map that match the specified
511  /// attribute pattern and attribute type. Writes out the best compatible
512  /// type between all matched attributes, or Undefined if no compatible type
513  /// exists. Returns true if anything matches, else false.
514  bool attributeMatch(UT_ArrayStringSet& names,
515  PDG_AttributeType& compatible,
518  bool include_internal) const;
519 
520  /// Saves the attribute map to a JSON writer, in the most current data
521  /// format
522  bool save(UT_JSONWriter& writer,
523  PDG_AttributeSaveType save_type,
524  bool is_patch,
525  bool include_bound) const;
526 
527  /// Saves the attribute map to a UT_Options, in the most current data
528  /// format
529  void save(UT_OptionsHolder& dict,
530  PDG_AttributeSaveType save_type,
531  bool is_patch,
532  bool include_bound) const;
533 
534  /// Saves a single attribute holder to a JSON writer, in the most current
535  /// data format.
536  bool save(UT_JSONWriter& writer,
537  const PDG_AttributeHolder& holder,
538  const UT_StringHolder& name) const;
539 
540  /// Saves a single attribute holder to a UT_Options, in the most current
541  /// data format.
542  void save(UT_OptionsHolder& dict,
543  const PDG_AttributeHolder& holder) const;
544 
545  /// Loads a V1 data format json object
546  bool loadV1(PDG_AttributeCollision strategy,
547  const UT_JSONValue& value);
548 
549  /// Loads a V2 data format json object
550  bool loadV2(PDG_AttributeSaveType save_type,
551  PDG_AttributeCollision strategy,
552  const UT_JSONValue& value);
553 
554  /// Loads a V2 data format dictionary
555  bool loadV2(PDG_AttributeSaveType save_type,
556  PDG_AttributeCollision strategy,
557  const UT_OptionsHolder& dict);
558 
559  /// Returns the attribute owner
560  const PDG_AttributeOwner* owner() const
561  { return myOwner; }
562 
563  /// Returns a read-only handle to the standard input file attribute
565  inputFileRO(AttribTable table=eTableCount) const
566  {
567  return refRO<PDG_AttributeFile>(theInputFileName, table);
568  }
569 
570  /// Returns a read/write handle to the standard input file attribute
572  inputFileRW(CreateWhen create=eCreateNever,
573  AttribTable table=eTableCount)
574  {
575  return refRWC<PDG_AttributeFile>(
576  theInputFileName,
577  create,
579  table,
580  true,
581  theInputFileFlags);
582  }
583 
584  /// Returns a read-only handle to the standard output file attribute
586  outputFileRO(AttribTable table=eTableCount) const
587  {
588  return refRO<PDG_AttributeFile>(theOutputFileName, table);
589  }
590 
591  /// Returns a read-write handle to the standard output file attribute
593  outputFileRW(CreateWhen create=eCreateNever,
594  AttribTable table=eTableCount)
595  {
596  return refRWC<PDG_AttributeFile>(
597  theOutputFileName,
598  create,
600  table,
601  true,
602  theOutputFileFlags);
603  }
604 
605  /// Returns a read-only handle to the standard added file attribute
607  addedFileRO(AttribTable table=eTableCount) const
608  {
609  return refRO<PDG_AttributeFile>(theAddedFileName, table);
610  }
611 
612  /// Returns a read/write handle to the standard input file attribute
614  addedFileRW(CreateWhen create=eCreateNever,
615  AttribTable table=eTableCount)
616  {
617  return refRWC<PDG_AttributeFile>(
618  theAddedFileName,
619  create,
621  table,
622  true,
623  theAddedFileFlags);
624  }
625 
626  /// Returns a read-only handle to the standard command string file
627  /// attribute
629  commandStringRO(AttribTable table=eTableCount) const
630  {
631  return refRO<PDG_AttributeString>(theCommandStringName, table);
632  }
633 
634  /// Returns a read/write handle to the standard input file attribute
636  commandStringRW(CreateWhen create=eCreateNever,
637  AttribTable table=eTableCount)
638  {
639  return refRWC<PDG_AttributeString>(
640  theCommandStringName,
641  create,
643  table,
644  true,
645  theCommandStringFlags);
646  }
647 
648  /// Returns a read-only handle to the standard work item label
649  /// attribute
651  labelRO(AttribTable table=eTableCount) const
652  {
653  return refRO<PDG_AttributeString>(theLabelName, table);
654  }
655 
656  /// Returns a read/write handle to the standard work item label attribute
658  labelRW(CreateWhen create=eCreateNever,
659  AttribTable table=eTableCount)
660  {
661  return refRWC<PDG_AttributeString>(
662  theLabelName,
663  create,
665  table,
666  true,
667  theLabelFlags);
668  }
669 
670  /// Returns a read-only handle to the standard work item custom state
671  /// attribute
673  stateRO(AttribTable table=eTableCount) const
674  {
675  return refRO<PDG_AttributeString>(theCustomStateName, table);
676  }
677 
678  /// Returns a read/write handle to the standard work item custom state
679  /// attribute
681  stateRW(CreateWhen create=eCreateNever,
682  AttribTable table=eTableCount)
683  {
684  return refRWC<PDG_AttributeString>(
685  theCustomStateName,
686  create,
688  table,
689  true,
690  theCustomStateFlags);
691  }
692 
693  /// Returns a read-only handle to the standard work item cook progress
694  /// attribute
696  cookPercentRO(AttribTable table=eTableCount) const
697  {
698  return refRO<PDG_AttributeFloat>(theCookPercentName, table);
699  }
700 
701  /// Returns a read/write handle to the standard work item custom state
702  /// attribute
704  cookPercentRW(CreateWhen create=eCreateNever,
705  AttribTable table=eTableCount)
706  {
707  return refRWC<PDG_AttributeFloat>(
708  theCookPercentName,
709  create,
711  table,
712  true,
713  theCustomStateFlags);
714  }
715 
716  /// Returns a read-only, const attribute handle. Will be invalid if the
717  /// specified attribute does not exist or is of the wrong type.
718  template <typename Attribute=PDG_AttributeData>
721  AttribTable table=eTableCount) const
722  {
725 
726  if (table == eTableCount)
727  table = isRuntime(local_ctx) ? eTableDynamic : eTableStatic;
728 
729  const PDG_AttributeHolder* holder =
730  attribHolderR<Attribute>(error, name, table, true);
731 
732  if (!holder)
733  return PDG_ROAttributeRef<Attribute>(nullptr, name, myOwner, error);
734  return PDG_ROAttributeRef<Attribute>(holder, name, myOwner);
735  }
736 
737  /// Returns a read/write, non-const attribute handle that permits both
738  /// flag and data manipulation. Does not create the attribute, but will
739  /// upgrade it from a parent map/lower table if necessary. Note that this
740  /// method is intionally distinct from the version below since it uses a
741  /// codepath that doesn't require a concrete Attribute type in order to
742  /// create an attribute reference.
743  template <typename Attribute>
746  {
748  if (!canWrite(local_ctx))
749  {
750  return PDG_RWAttributeRef<Attribute>(nullptr, name, myOwner,
752  }
753 
754  AttribTable table = isStatic(local_ctx) ? eTableStatic : eTableDynamic;
755 
756  UT_StringHolder valid;
757  PDG_AttributeData::makeValidName(valid, name, false);
758 
760  PDG_AttributeHolder* holder = attribHolderW<Attribute>(
761  error, valid, table);
762 
763  if (holder)
764  {
765  myWasModified = true;
766  return PDG_RWAttributeRef<Attribute>(holder, valid, myOwner);
767  }
768 
769  return PDG_RWAttributeRef<Attribute>(nullptr, name, myOwner, error);
770  }
771 
772  /// Returns a read/write, non-const attribute handle that permits both
773  /// flag and data manipulation. Optionally creates the attribute if it
774  /// does not exist. Will be invalid if the specified attribute does not
775  /// exist, is of the wrong type, or is marked as read-only.
776  template <typename Attribute>
779  bool create,
780  PDG_AttributeOverwrite overwrite =
782  uint16 flags=0)
783  {
784  return refRWC<Attribute>(
785  name,
786  (create ? eCreateAlways : eCreateNever),
787  overwrite,
788  eTableCount,
789  false,
790  flags);
791  }
792 
793  /// Same as above, but with greater control on the creation flags.
794  template <typename Attribute>
797  CreateWhen create=eCreateNever,
798  PDG_AttributeOverwrite overwrite =
800  AttribTable table=eTableCount,
801  bool allow_internal_names=false,
802  uint16 flags=0)
803  {
805  if (!canWrite(local_ctx))
806  {
807  return PDG_RWAttributeRef<Attribute>(nullptr, name, myOwner,
809  }
810 
811  UT_StringHolder valid;
812  PDG_AttributeData::makeValidName(valid, name, allow_internal_names);
813 
814  if (table == eTableCount)
815  table = isStatic(local_ctx) ? eTableStatic : eTableDynamic;
816 
818  PDG_AttributeHolder* holder = attribHolderC<Attribute>(error, valid,
819  create, overwrite, true, table, flags);
820 
821  if (holder)
822  {
823  myWasModified = true;
824  return PDG_RWAttributeRef<Attribute>(holder, valid, myOwner);
825  }
826 
827  return PDG_RWAttributeRef<Attribute>(nullptr, name, myOwner, error);
828  }
829 
830  /// Copies the specified attribute from this map to attribute with the
831  /// new name
833  const UT_StringHolder& old_name,
834  PDG_AttributeCopyOp copy_op,
835  bool deep_copy)
836  {
837  PDG_AttributeType type = attributeType(old_name);
838  if (type == PDG_AttributeType::eUndefined)
840 
841  switch (type)
842  {
844  {
845  return copyFrom(
846  new_name,
847  refRO<PDG_AttributeInteger>(old_name),
848  copy_op,
849  deep_copy);
850  }
851 
853  {
854  return copyFrom(
855  new_name,
856  refRO<PDG_AttributeFloat>(old_name),
857  copy_op,
858  deep_copy);
859  }
860 
862  {
863  return copyFrom(
864  new_name,
865  refRO<PDG_AttributeString>(old_name),
866  copy_op,
867  deep_copy);
868  }
869 
871  {
872  return copyFrom(
873  new_name,
874  refRO<PDG_AttributeFile>(old_name),
875  copy_op,
876  deep_copy);
877  }
878 
880  {
881  return copyFrom(
882  new_name,
883  refRO<PDG_AttributeDictionary>(old_name),
884  copy_op,
885  deep_copy);
886  }
887 
889  {
890  return copyFrom(
891  new_name,
892  refRO<PDG_AttributePyObject>(old_name),
893  copy_op,
894  deep_copy);
895  }
896 
898  {
899  return copyFrom(
900  new_name,
901  refRO<PDG_AttributeGeometry>(old_name),
902  copy_op,
903  deep_copy);
904 
905  }
906 
907  default:
909  }
910  }
911 
912  /// Copies the specified attribute into an attribute on this map
913  template <typename Attribute>
916  PDG_AttributeCopyOp copy_op,
917  bool deep_copy)
918  {
920  if (!canWrite(local_ctx))
922 
923  UT_StringHolder valid;
925 
927  isStatic(local_ctx) ? eTableStatic : eTableDynamic;
928  PDG_AttributeOverwrite overwrite;
929 
930  if (copy_op == PDG_AttributeCopyOp::eNew)
931  overwrite = PDG_AttributeOverwrite::eNever;
932  else if (copy_op == PDG_AttributeCopyOp::eOverwriteType)
934  else
935  overwrite = PDG_AttributeOverwrite::eMatch;
936 
937  bool array_op =
938  (copy_op == PDG_AttributeCopyOp::eAppend) ||
939  (copy_op == PDG_AttributeCopyOp::ePrepend) ||
940  (copy_op == PDG_AttributeCopyOp::eUpdate);
941 
943  PDG_AttributeHolder* holder = attribHolderC<Attribute>(error, valid,
944  eCreateAlways, overwrite, false, table, 0);
945  if (!holder)
946  return error;
947 
948  if (!ref.isArray() && array_op)
950 
951  const PDG_AttributeHolder* ref_holder = ref.holder();
952  if (holder->isOwner())
953  {
954  holder->attribute<Attribute>()->copy(
955  ref_holder->template attribute<Attribute>(),
956  copy_op);
957  }
958  else if (holder->attribute() && array_op)
959  {
960  holder->own();
961  holder->attribute<Attribute>()->copy(
962  ref_holder->template attribute<Attribute>(),
963  copy_op);
964  }
965  else
966  {
967  holder->reset(*ref_holder, false, true);
968 
969  if (deep_copy)
970  holder->own();
971  }
972 
973  myWasModified = true;
975  }
976 
977  /// Renames the specififed attribute
979  const UT_StringHolder& dst_name,
980  bool overwrite)
981  {
982  // Attributes cannot be renamed if we don't have write access
984  if (!canWrite(local_ctx))
986 
988  isStatic(local_ctx) ? eTableStatic : eTableDynamic;
989 
990  PDG_AttributeHolder* holder = nullptr;
991  AttribTable src_table = eTableCount;
992 
993  // Find the attribute in the desired table, or lower table if it does
994  // not exist
995  for (int i = table; i >= 0; i--)
996  {
997  auto&& attrib = myAttributes[i].find(src_name);
998  if (attrib != myAttributes[i].end())
999  {
1000  holder = attrib->second.get();
1001  src_table = static_cast<AttribTable>(i);
1002  break;
1003  }
1004  }
1005 
1006  // If the attribute only exists in a lower table, return an error since
1007  // we cannot manipulate that table.
1008  if (src_table < table)
1010 
1011  // If there's no holder, there's nothing to rename
1012  if (!holder)
1014 
1015  // Disallow renaming of write protected attributes
1016  if (holder->hasFlag(PDG_AttributeFlag::eReadOnlyFlag))
1018 
1019  // Disallow renaming of internal attributes
1020  if (holder->hasFlag(PDG_AttributeFlag::eInternalFlag))
1022 
1023  // Create a valid destination name
1024  UT_StringHolder valid;
1025  PDG_AttributeData::makeValidName(valid, dst_name);
1026 
1027  // Check for an existing attribute with the dest name
1028  auto&& iter = myAttributes[table].find(valid);
1029  if (iter != myAttributes[table].end())
1030  {
1031  // Internal attributes can never be the destination of a rename
1032  if (iter->second->hasFlag(PDG_AttributeFlag::eInternalFlag))
1034  // Return early if the caller doesn't want to overwrite
1035  else if (!overwrite)
1037 
1038  // Erase the existing attribute
1039  myAttributes[table].erase(iter);
1040  }
1041 
1042  // Move the attribute into the map entry with the new name
1043  HolderPtr holder_copy =
1044  UTmakeUnique<PDG_AttributeHolder>(std::move(*holder));
1045  myAttributes[table].emplace(valid, std::move(holder_copy));
1046  myAttributes[table].erase(src_name);
1047 
1048  myWasModified = true;
1050  }
1051 
1052  /// Utility method that looks up a value from an attribute, with a given
1053  /// index, and an optional flag filter
1054  template <typename Attribute>
1055  bool value(typename Attribute::Data& value,
1056  const UT_StringHolder& name,
1057  int index,
1058  uint16 flags = 0) const
1059  {
1060  auto&& attribute = refRO<Attribute>(name);
1061  if (!attribute || !attribute.hasFlags(flags))
1062  return false;
1064  return false;
1065 
1066  return attribute->value(value, index);
1067  }
1068 
1069  /// Utility method that looks up an array value from an attribute, with an
1070  /// optional flag filter
1071  template <typename Attribute>
1072  bool value(typename Attribute::Array& value,
1073  const UT_StringHolder& name,
1074  uint16 flags = 0) const
1075  {
1076  auto&& attribute = refRO<Attribute>(name);
1077  if (!attribute || !attribute.hasFlags(flags))
1078  return false;
1080  return false;
1081 
1082  value = attribute->values();
1083  return true;
1084  }
1085 
1086  /// Utility method that sets an attribute value at a specified index, and
1087  /// creates it if it does not already exist
1088  template <typename Attribute>
1090  const typename Attribute::Data& value,
1091  int index,
1092  bool create,
1093  PDG_AttributeOverwrite overwrite =
1095  {
1096  auto&& attribute = refRW<Attribute>(name, create, overwrite);
1097  if (!attribute)
1098  return attribute.error();
1099  attribute->setValue(value, index);
1101  }
1102 
1103  /// Utility method that sets an attribute array value, and creates it if
1104  /// it does not already exist
1105  template <typename Attribute>
1107  const typename Attribute::Array& values,
1108  bool create,
1109  PDG_AttributeOverwrite overwrite =
1111  {
1112  auto&& attribute = refRW<Attribute>(name, create, overwrite);
1113  if (!attribute)
1114  return attribute.error();
1115  attribute->setValue(values);
1117  }
1118 
1119  /// Utility method that clears all values from an attribute, and creates
1120  /// it if it does not already exist
1121  template <typename Attribute>
1123  bool create,
1124  PDG_AttributeOverwrite overwrite =
1126  {
1127  auto&& attribute = refRW<Attribute>(name, create, overwrite);
1128  if (!attribute)
1129  return attribute.error();
1130  attribute->clear();
1132  }
1133 
1134  /// Queries all values of the specified attribute type and returns them as
1135  /// a map.
1136  template <typename Attribute>
1137  bool values(typename Attribute::Map& value_map,
1138  uint16 flags = 0) const
1139  {
1140  values<Attribute>(eTableStatic, value_map, flags);
1142  if (isRuntime(local_ctx))
1143  values<Attribute>(eTableDynamic, value_map, flags);
1144  return true;
1145  }
1146 
1147  /// Queries a scalar value by name/index and stores it as a PDGT_Value
1148  bool value(PDGT_Value& value,
1149  const UT_StringHolder& name,
1150  int index,
1151  uint16 flags = 0) const;
1152 
1153  /// Queries all values as PDGT_Values
1154  bool values(PDGT_Value::Map& value_map,
1155  uint16 flags = 0) const;
1156 
1157 
1158  /// Returns true if attribute has the the specified flag set, else false
1159  bool hasFlag(const UT_StringHolder& name,
1160  PDG_AttributeFlag flag) const;
1161 
1162  /// Sets or clears a flag on the specified attribute
1163  PDG_AttributeError setFlag(const UT_StringHolder& name,
1164  PDG_AttributeFlag flag,
1165  bool set);
1166 
1167  /// Sets or clears a flag on all attributes
1168  bool setFlagAll(PDG_AttributeFlag flag,
1169  bool set);
1170 
1171 
1172  /// Begins tracking attribute changes
1173  void startChangeTracking();
1174 
1175  /// Ends tracking changes, returning true if a change made was
1176  ChangeResult endChangeTracking(bool check_all);
1177 
1178  /// Computes the hash value for attrib values, errors can occur if python
1179  /// objects hash() raises an exception.
1180  bool hash(SYS_HashType& hash_val,
1181  AttribTable table,
1182  bool include_internal,
1183  bool include_unowned,
1184  UT_WorkBuffer& errors) const;
1185 
1186  /// Computes the hash value for the specific attribute values specified in
1187  /// the set of names. This is the same as the function above, but it
1188  /// only applies to the indicated subset of attribs.
1189  bool hash(SYS_HashType& hash_val,
1190  const UT_ArrayStringSet& filter,
1191  AttribTable table,
1192  bool include_internal,
1193  bool include_unowned,
1194  UT_WorkBuffer& errors) const;
1195 
1196 private:
1197  /// Managed attribute holder pointer
1198  using HolderPtr = UT_UniquePtr<PDG_AttributeHolder>;
1199 
1200  /// String map to attribute holder - the actual storage type of this
1201  /// structure
1202  using Map = UT_ArrayStringMap<HolderPtr>;
1203 
1204 private:
1205  /// Returns the attribute lock for this attribute map
1206  UT_RWLock& attributeLock() const;
1207 
1208  /// Returns the global attribute lock for this map
1209  UT_RWLock* globalLock() const;
1210 
1211  /// Called when the map is unlocked
1212  void onUnlock() const;
1213 
1214  /// Internal logic for hashing attributes
1215  bool hashAttributes(
1216  SYS_HashType& hash_val,
1217  const UT_ArrayStringSet& filter,
1218  AttribTable table,
1219  bool include_internal,
1220  bool include_unowned,
1221  UT_WorkBuffer& errors) const;
1222 
1223  /// Returns the sorted set of attribute names for a save operation
1224  void saveNames(
1225  UT_SortedStringSet& names,
1226  PDG_AttributeSaveType save_type) const;
1227 
1228  /// Returns the attribute holder for the specified name and save options,
1229  /// or nullptr if no valid holder meets the save settings
1230  const PDG_AttributeHolder*
1231  saveHolder(
1232  const UT_StringHolder& name,
1233  PDG_AttributeSaveType save_type,
1234  AttribTable table,
1235  bool is_patch,
1236  bool include_bound) const;
1237 
1238  /// Returns a reference to the shared, static invalid attribute reg
1239  static PDG_AttributeHolder&
1240  invalidHolder();
1241 
1242  /// Returns true if the attribute owenr that owns this attribute map can
1243  /// create RW attribute references
1244  bool canWrite(const PDG_EvaluationContext* ctx) const;
1245 
1246  /// Returns true if called during a runtime/cook operation, instead of a
1247  /// generation operation
1248  bool isRuntime(const PDG_EvaluationContext* ctx) const;
1249 
1250  /// Returns true if called during work item generation
1251  bool isStatic(const PDG_EvaluationContext* ctx) const;
1252 
1253  /// Returns true if the specified attribute exists in the table
1254  bool hasAttribute(const UT_StringHolder& name,
1255  AttribTable table) const
1256  {
1257  if (myParentMap && myParentMap->hasAttribute(name, table))
1258  return true;
1259  return (myAttributes[table].find(name) != myAttributes[table].end());
1260  }
1261 
1262  /// Merges attributes from the source table in another map to dst
1263  /// table in this map.
1264  bool mergeMap(
1265  PDG_AttributeMerge& attrib_merge,
1266  UT_StringSet& exclusion_set,
1267  const PDG_AttributeMap& other,
1268  AttribTable src_table,
1269  AttribTable dst_table,
1270  bool flatten_dynamic);
1271 
1272  /// Merges an attribute from a different map into the dst table in this
1273  /// map.
1274  bool mergeAttribute(
1275  PDG_AttributeMerge& attrib_merge,
1276  UT_StringSet& exclusion_set,
1277  const PDG_AttributeHolder& other_holder,
1278  const UT_StringHolder& name,
1279  AttribTable dst_table);
1280 
1281  /// Loads an attribute from the json value, with the specified name, type
1282  /// and flags
1283  bool loadAttribute(AttribTable table,
1284  PDG_AttributeCollision strategy,
1286  const UT_StringHolder& name,
1287  uint16 flags,
1288  bool is_own,
1289  bool is_concat,
1290  const UT_JSONValue& data);
1291 
1292  /// Loads an attribute from the dict, with the specified name, type
1293  /// and flags
1294  bool loadAttribute(AttribTable table,
1295  PDG_AttributeCollision strategy,
1297  const UT_StringHolder& name,
1298  uint16 flags,
1299  bool is_own,
1300  bool is_concat,
1301  const UT_OptionsHolder& dict);
1302 
1303  /// Loads an old-style attribute entry from json
1304  bool loadV1Map(PDG_AttributeCollision strategy,
1306  const UT_JSONValue& data);
1307 
1308  /// Saves the values from the specified map into an instace of a generic
1309  /// data map
1310  bool values(AttribTable table,
1311  PDGT_Value::Map& value_map,
1312  uint16 flags) const
1313  {
1314  if (myParentMap)
1315  myParentMap->values(table, value_map, flags);
1316 
1317  for (auto&& attrib_pair : myAttributes[table])
1318  {
1319  const PDG_AttributeHolder& holder = *attrib_pair.second;
1320  if (!holder.hasFlags(flags))
1321  continue;
1323  continue;
1324  if (!attribToValue(value_map[attrib_pair.first], 0, holder))
1325  return false;
1326  }
1327 
1328  return true;
1329  }
1330 
1331  /// Saves the values from the specified map into an instance of the map
1332  /// data type
1333  template <typename Attribute>
1334  void values(AttribTable table,
1335  typename Attribute::Map& value_map,
1336  uint16 flags) const
1337  {
1338  if (myParentMap)
1339  myParentMap->values<Attribute>(table, value_map, flags);
1340 
1341  for (auto&& attrib_pair : myAttributes[table])
1342  {
1343  const PDG_AttributeHolder& holder = *attrib_pair.second;
1344  if (!holder.typeMatch<Attribute>())
1345  {
1346  value_map.erase(attrib_pair.first);
1347  continue;
1348  }
1349 
1350  if (!holder.hasFlags(flags))
1351  continue;
1353  continue;
1354 
1355  const Attribute* attribute = holder.attribute<Attribute>();
1356  if (attribute)
1357  value_map[attrib_pair.first] = attribute->values();
1358  }
1359  }
1360 
1361  /// Returns the size of an attribute
1362  static int attribSize(const PDG_AttributeHolder& src)
1363  {
1364  switch (src.type())
1365  {
1367  return src.attribute<PDG_AttributeInteger>()->size();
1369  return src.attribute<PDG_AttributeFloat>()->size();
1371  return src.attribute<PDG_AttributeString>()->size();
1373  return src.attribute<PDG_AttributeFile>()->size();
1375  return src.attribute<PDG_AttributeDictionary>()->size();
1377  return src.attribute<PDG_AttributePyObject>()->size();
1379  return src.attribute<PDG_AttributeGeometry>()->size();
1380  default:
1381  return -1;
1382  }
1383  }
1384 
1385  /// Concatenates an attribute
1386  static bool attribConcat(PDG_AttributeHolder& dst,
1387  const PDG_AttributeHolder& src)
1388  {
1389  if (src.type() != dst.type())
1390  return false;
1391 
1392  switch (dst.type())
1393  {
1395  return dst.concat<PDG_AttributeInteger>(src);
1397  return dst.concat<PDG_AttributeFloat>(src);
1399  return dst.concat<PDG_AttributeString>(src);
1401  return dst.concat<PDG_AttributeFile>(src);
1403  return dst.concat<PDG_AttributeDictionary>(src);
1404  default:
1405  return false;
1406  }
1407 
1408  return true;
1409  }
1410 
1411  /// Updates the size of an attribute
1412  static bool attribAdjustSize(PDG_AttributeHolder& attrib,
1413  int offset)
1414  {
1415  switch (attrib.type())
1416  {
1418  return attrib.adjustSize<PDG_AttributeInteger>(offset);
1420  return attrib.adjustSize<PDG_AttributeFloat>(offset);
1422  return attrib.adjustSize<PDG_AttributeString>(offset);
1424  return attrib.adjustSize<PDG_AttributeFile>(offset);
1426  return attrib.adjustSize<PDG_AttributeDictionary>(offset);
1427  default:
1428  return false;
1429  }
1430 
1431  return true;
1432  }
1433 
1434  /// Clears an attribute
1435  static void attribClear(PDG_AttributeHolder& attrib)
1436  {
1437  switch (attrib.type())
1438  {
1440  attrib.clear<PDG_AttributeInteger>();
1441  break;
1443  attrib.clear<PDG_AttributeFloat>();
1444  break;
1446  attrib.clear<PDG_AttributeString>();
1447  break;
1449  attrib.clear<PDG_AttributeFile>();
1450  break;
1452  attrib.clear<PDG_AttributeDictionary>();
1453  break;
1454  default:
1455  break;
1456  }
1457  }
1458 
1459  /// Saves the name of the specified attribute to the PDGT_Value variant.
1460  static bool attribToValue(PDGT_Value& value,
1461  int index,
1462  const PDG_AttributeHolder& holder)
1463  {
1464  switch (holder.type())
1465  {
1467  return attribToValue<PDG_AttributeFloat>(value, index, holder);
1469  return attribToValue<PDG_AttributeInteger>(value, index, holder);
1471  return attribToValue<PDG_AttributeString>(value, index, holder);
1473  {
1474  typename PDG_AttributeFile::Data temp;
1475  if (holder.attribute<PDG_AttributeFile>()->value(temp, index))
1476  value.setValue(temp.local_data());
1477  else
1478  value.setValue("");
1479  return true;
1480  }
1482  {
1484  if (holder.attribute<PDG_AttributeDictionary>()->desc(
1485  buffer, index))
1486  {
1487  value.setValue(buffer.buffer());
1488  }
1489  else
1490  value.setValue("");
1491  return true;
1492  }
1494  {
1495  UT_StringHolder str;
1496  if (holder.attribute<PDG_AttributePyObject>()->str(str))
1497  value.setValue(str);
1498  else
1499  value.setValue("");
1500  return true;
1501  }
1503  {
1504  UT_StringHolder str;
1505  if (holder.attribute<PDG_AttributeGeometry>()->desc(str))
1506  value.setValue(str);
1507  else
1508  value.setValue("");
1509  return true;
1510 
1511  }
1512  default:
1513  return false;
1514  }
1515 
1516  return false;
1517  }
1518 
1519  /// Helper method for storing an attribute value to a PDGT_Value variant.
1520  template <typename Attribute>
1521  static bool attribToValue(PDGT_Value& value,
1522  int index,
1523  const PDG_AttributeHolder& holder)
1524  {
1525  typename Attribute::Data temp;
1526  if (holder.attribute<Attribute>()->value(temp, index))
1527  value.setValue(temp);
1528  else
1529  value.setValue(typename Attribute::Data());
1530 
1531  return true;
1532  }
1533 
1534  /// Returns a const attribute holder an existing static or dynamic
1535  /// attribute. Uses the local context to determine if dynamic access
1536  /// is permitted.
1537  template <typename Attribute>
1538  const PDG_AttributeHolder*
1539  attribHolderR(const UT_StringHolder& name) const
1540  {
1543  AttribTable table = isRuntime(local_ctx) ? eTableDynamic : eTableStatic;
1544  return attribHolderR<Attribute>(error, name, table, true);
1545  }
1546 
1547  /// Returns an const attribute holder to an existing static or dynamic
1548  /// attribute. Does not create the attribute if it does not exist.
1549  template <typename Attribute>
1550  const PDG_AttributeHolder*
1551  attribHolderR(PDG_AttributeError& error,
1552  const UT_StringHolder& name,
1553  AttribTable table,
1554  bool check_parent) const
1555  {
1556  const PDG_AttributeHolder* holder = nullptr;
1557  for (int i = table; i >= 0; i--)
1558  {
1559  auto&& attrib = myAttributes[i].find(name);
1560  if (attrib != myAttributes[i].end())
1561  {
1562  holder = attrib->second.get();
1563  break;
1564  }
1565  }
1566 
1567  if (!holder)
1568  {
1569  if (myParentMap && check_parent)
1570  {
1571  return myParentMap->attribHolderR<Attribute>(
1572  error, name, table, true);
1573  }
1574 
1576  return nullptr;
1577  }
1578 
1579  if (!holder->typeMatch<Attribute>())
1580  {
1582  return nullptr;
1583  }
1584 
1585  error = PDG_AttributeError::eNone;
1586  return holder;
1587  }
1588 
1589  /// Returns an attribute holder for an existing or new attribute, either
1590  /// static or dynamic. If the attribute is static but we need a dynamic
1591  /// one, the existing static attribute is copied to the dynamic map.
1592  template <typename Attribute>
1593  PDG_AttributeHolder* attribHolderW(PDG_AttributeError& error,
1594  const UT_StringHolder& name,
1595  AttribTable table)
1596  {
1597  PDG_AttributeHolder* holder = nullptr;
1598  AttribTable src_table = eTableCount;
1599 
1600  // Check for the attribute in the desired table, and work downwards if
1601  // its not found
1602  for (int i = table; i >= 0; i--)
1603  {
1604  auto&& attrib = myAttributes[i].find(name);
1605  if (attrib != myAttributes[i].end())
1606  {
1607  holder = attrib->second.get();
1608  src_table = static_cast<AttribTable>(i);
1609  break;
1610  }
1611  }
1612 
1613  // If the attribute doesn't exist anywhere, check the parent map first
1614  // before returning an error.
1615  if (!holder)
1616  {
1617  // If a parent map is set, check for the attribute on the parent
1618  if (myParentMap)
1619  {
1620  const PDG_AttributeHolder* parent =
1621  attribHolderR<Attribute>(error, name, table, true);
1622 
1623  // We never create an attribute on the parent map, but if one
1624  // does exist make a copy of it and store it on this map.
1625  if (parent)
1626  {
1627  HolderPtr new_holder = UTmakeUnique<PDG_AttributeHolder>();
1628  new_holder->reset(*parent, parent->isOwner(), true);
1629  auto&& insert = myAttributes[table].emplace(
1630  name, std::move(new_holder));
1631  error = PDG_AttributeError::eNone;
1632  return insert.first->second.get();
1633  }
1634  }
1635 
1637  return nullptr;
1638  }
1639 
1640  // Read only attributes cannot be accessed for writing in any case
1641  if (holder->hasFlag(PDG_AttributeFlag::eReadOnlyFlag))
1642  {
1644  return nullptr;
1645  }
1646 
1647  // If the attribute exists in any of the tables but is the wrong type,
1648  // this is always an error.
1649  if (!holder->typeMatch<Attribute>())
1650  {
1652  return nullptr;
1653  }
1654 
1655  // If the attribute was found in a lower table than the one that was
1656  // requested, only create the attribute if the creation mode is
1657  // Always or Upgrade.
1658  if (src_table < table)
1659  {
1660  HolderPtr holder_copy = UTmakeUnique<PDG_AttributeHolder>();
1661  holder_copy->reset(*holder, holder->isOwner(), true);
1662 
1663  auto&& insert = myAttributes[table].emplace(
1664  name, std::move(holder_copy));
1665  error = PDG_AttributeError::eNone;
1666  return insert.first->second.get();
1667  }
1668 
1669  error = PDG_AttributeError::eNone;
1670  return holder;
1671  }
1672 
1673  /// Returns an attribute holder for an existing or new attribute, either
1674  /// static or dynamic. If the attribute is static but we need a dynamic
1675  /// one, the existing static attribute is copied to the dynamic map. This
1676  /// method will also create the attribute if needed, or if the type does
1677  /// not match, depending on the creation/overwrite options.
1678  template <typename Attribute>
1679  PDG_AttributeHolder* attribHolderC(PDG_AttributeError& error,
1680  const UT_StringHolder& name,
1681  CreateWhen create,
1682  PDG_AttributeOverwrite overwrite,
1683  bool own,
1684  AttribTable table,
1685  uint16 flags)
1686  {
1687  PDG_AttributeHolder* holder = nullptr;
1688  AttribTable src_table = eTableCount;
1689 
1690  // Check for the attribute in the desired table, and work downwards if
1691  // its not found
1692  for (int i = table; i >= 0; i--)
1693  {
1694  auto&& attrib = myAttributes[i].find(name);
1695  if (attrib != myAttributes[i].end())
1696  {
1697  holder = attrib->second.get();
1698  src_table = static_cast<AttribTable>(i);
1699  break;
1700  }
1701  }
1702 
1703  // If the attribute doesn't exist anywhere, only create it when the
1704  // create mode is Always.
1705  if (!holder)
1706  {
1707  // If a parent map is set, check for the attribute on the parent
1708  if (myParentMap)
1709  {
1710  const PDG_AttributeHolder* parent =
1711  attribHolderR<Attribute>(error, name, table, true);
1712 
1713  // We never create an attribute on the parent map, but if one
1714  // does exist and we were request to upgrade if possible, make
1715  // a copy of it and store it on this map.
1716  if (parent)
1717  {
1718  if (create == eCreateNever)
1719  {
1721  return nullptr;
1722  }
1723 
1724  HolderPtr new_holder = UTmakeUnique<PDG_AttributeHolder>();
1725  new_holder->reset(*parent, parent->isOwner(), true);
1726 
1727  auto&& insert = myAttributes[table].emplace(
1728  name, std::move(new_holder));
1729  error = PDG_AttributeError::eNone;
1730  return insert.first->second.get();
1731  }
1732  }
1733 
1734  if (create != eCreateAlways)
1735  {
1737  return nullptr;
1738  }
1739 
1740  HolderPtr holder = UTmakeUnique<PDG_AttributeHolder>(
1741  own ? new Attribute() : nullptr,
1742  Attribute::TypeEnum,
1743  flags,
1744  own,
1745  false);
1746 
1747  auto&& insert = myAttributes[table].emplace(
1748  name, std::move(holder));
1749  error = PDG_AttributeError::eNone;
1750  return insert.first->second.get();
1751  }
1752 
1753  // If overwrite is off, we don't return back a holder
1754  if (overwrite == PDG_AttributeOverwrite::eNever)
1755  {
1757  return nullptr;
1758  }
1759 
1760  // Read only attributes cannot be accessed for writing in any case
1761  if (holder->hasFlag(PDG_AttributeFlag::eReadOnlyFlag))
1762  {
1764  return nullptr;
1765  }
1766 
1767  // If the attribute exists in any of the tables but is the wrong type,
1768  // this is always an error.
1769  bool type_match = holder->typeMatch<Attribute>();
1770  if (!type_match && (overwrite != PDG_AttributeOverwrite::eAlways))
1771  {
1773  return nullptr;
1774  }
1775 
1776  // If the attribute was found in a lower table than the one that was
1777  // requested, only create the attribute if the creation mode is
1778  // Always or Upgrade.
1779  if (src_table < table)
1780  {
1781  if (create == eCreateNever)
1782  {
1784  return nullptr;
1785  }
1786 
1787  HolderPtr holder_copy = UTmakeUnique<PDG_AttributeHolder>();
1788  if (!type_match)
1789  {
1790  holder_copy->reset(
1791  new Attribute(),
1792  Attribute::TypeEnum,
1793  flags,
1794  own,
1795  false,
1796  false);
1797  }
1798  else
1799  holder_copy->reset(*holder, holder->isOwner(), true);
1800 
1801  auto&& insert = myAttributes[table].emplace(
1802  name, std::move(holder_copy));
1803  error = PDG_AttributeError::eNone;
1804  return insert.first->second.get();
1805  }
1806 
1807  error = PDG_AttributeError::eNone;
1808  if (!type_match)
1809  {
1810  holder->reset(
1811  new Attribute(), Attribute::TypeEnum, flags, own, false, false);
1812  }
1813 
1814  return holder;
1815  }
1816 
1817 private:
1818  Map myAttributes[eTableCount];
1819  UT_UniquePtr<Map[]> myStaleAttributes;
1820  const PDG_AttributeOwner* myOwner;
1821  PDG_AttributeMap* myParentMap;
1822  mutable UT_RWLock myAttributeLock;
1823 
1824  bool myHasRuntimeAttribs;
1825  mutable bool myWasModified;
1826 };
1827 
1828 #endif
PDG_AttributeError
Only overwrite the attribute if the type matches.
GLbitfield flags
Definition: glcorearb.h:1596
PDG_RWAttributeRef< PDG_AttributeString > commandStringRW(CreateWhen create=eCreateNever, AttribTable table=eTableCount)
Returns a read/write handle to the standard input file attribute.
unsigned short uint16
Definition: SYS_Types.h:38
PDG_AttributeType type() const
Returns the attribute type.
bool desc(UT_StringHolder &str) const
bool hasFlag(PDG_AttributeFlag flag) const
Returns true if this attribute has the specified flag.
PDG_ROAttributeRef< PDG_AttributeString > labelRO(AttribTable table=eTableCount) const
bool adjustSize(int offset)
Sets the size of the attribute.
Never create the attribute, even if its not found.
static const UT_StringHolder theAddedFileName
Append to the existing attribute if it exists and matches.
static const uint16 theAddedFileFlags
Flags for the standard added file attribute.
OIIO_UTIL_API bool copy(string_view from, string_view to, std::string &err)
Never overwrite the attribute.
PDG_RWAttributeRef< Attribute > refRWC(const UT_StringHolder &name, CreateWhen create=eCreateNever, PDG_AttributeOverwrite overwrite=PDG_AttributeOverwrite::eMatch, AttribTable table=eTableCount, bool allow_internal_names=false, uint16 flags=0)
Same as above, but with greater control on the creation flags.
Attribute values were changed or added.
Undefined or uninitialized attribute type.
GLsizei const GLfloat * value
Definition: glcorearb.h:824
ScopedTryLock(const PDG_AttributeMap &attribute_map)
PDG_AttributeError clearValues(const UT_StringHolder &name, bool create, PDG_AttributeOverwrite overwrite=PDG_AttributeOverwrite::eMatch)
PDG_RWAttributeRef< PDG_AttributeFile > addedFileRW(CreateWhen create=eCreateNever, AttribTable table=eTableCount)
Returns a read/write handle to the standard input file attribute.
#define PDG_API
Definition: PDG_API.h:23
bool isArray() const
Returns true if the attribute is an array.
PDG_ROAttributeRef< PDG_AttributeString > stateRO(AttribTable table=eTableCount) const
static const UT_StringHolder thePseudoAttribName
SYS_FORCE_INLINE const char * buffer() const
void clear()
Clears the attribute.
void swap(T &lhs, T &rhs)
Definition: pugixml.cpp:7440
std::size_t SYS_HashType
Define the type for hash values.
Definition: SYS_Hash.h:19
PDG_RWAttributeRef< PDG_AttributeFile > outputFileRW(CreateWhen create=eCreateNever, AttribTable table=eTableCount)
Returns a read-write handle to the standard output file attribute.
static const uint16 theCustomStateFlags
Flags for the standard custom state attribute.
bool hasFlags(uint16 flags) const
Returns true if this attribute has all of the specified flags set.
bool values(typename Attribute::Map &value_map, uint16 flags=0) const
Class which writes ASCII or binary JSON streams.
Definition: UT_JSONWriter.h:37
PDG_ROAttributeRef< PDG_AttributeString > commandStringRO(AttribTable table=eTableCount) const
const PDG_AttributeOwner * owner() const
Returns the attribute owner.
FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr &out) -> bool
Definition: core.h:2138
OIIO_FORCEINLINE vbool4 insert(const vbool4 &a, bool val)
Helper: substitute val for a[i].
Definition: simd.h:3556
static const UT_StringHolder theBuiltinPrefix
GLuint buffer
Definition: glcorearb.h:660
bool value(typename Attribute::Data &value, const UT_StringHolder &name, int index, uint16 flags=0) const
static PDG_EvaluationContext * getContext()
Returns the thread-local PDG_EvaluationContext instance.
std::unique_ptr< T, Deleter > UT_UniquePtr
A smart pointer for unique ownership of dynamically allocated objects.
Definition: UT_UniquePtr.h:39
< returns > If no error
Definition: snippets.dox:2
PDG_ROAttributeRef< PDG_AttributeFile > addedFileRO(AttribTable table=eTableCount) const
Returns a read-only handle to the standard added file attribute.
PDG_AttributeError setValue(const UT_StringHolder &name, const typename Attribute::Data &value, int index, bool create, PDG_AttributeOverwrite overwrite=PDG_AttributeOverwrite::eMatch)
PDG_AttributeOverwrite
ScopedLock(const PDG_AttributeMap &attribute_map)
const UT_StringHolder & local_data() const
static const UT_StringHolder theInputFileName
A single PDG_ApplicationShim::Geometry instance.
GLint GLint GLsizei GLint GLenum GLenum type
Definition: glcorearb.h:108
No error was specified, i.e. the ref is valid.
GLintptr offset
Definition: glcorearb.h:665
AttribTable
Enumeration of attribute tables.
PDG_RWAttributeRef< PDG_AttributeString > labelRW(CreateWhen create=eCreateNever, AttribTable table=eTableCount)
Returns a read/write handle to the standard work item label attribute.
PDG_RWAttributeRef< PDG_AttributeFloat > cookPercentRW(CreateWhen create=eCreateNever, AttribTable table=eTableCount)
GLboolean reset
Definition: glad.h:5138
void forEachRO(const Func &func) const
GLint ref
Definition: glcorearb.h:124
PDG_ROAttributeRef< PDG_AttributeFile > outputFileRO(AttribTable table=eTableCount) const
Returns a read-only handle to the standard output file attribute.
OPENEXR_IMF_INTERNAL_NAMESPACE_HEADER_ENTER class IMF_EXPORT_TEMPLATE_TYPE Array
Definition: ImfForward.h:21
static const UT_StringHolder theAllAttribName
void setValue(const PDGT_Value &value)
constexpr auto set(type rhs) -> int
Definition: core.h:610
PDG_ROAttributeRef< PDG_AttributeFloat > cookPercentRO(AttribTable table=eTableCount) const
Always create the attribute if it does not exist.
GLuint GLuint end
Definition: glcorearb.h:475
PDG_AttributeError copyFrom(const UT_StringHolder &name, const PDG_ROAttributeRef< Attribute > &ref, PDG_AttributeCopyOp copy_op, bool deep_copy)
Copies the specified attribute into an attribute on this map.
bool value(typename Attribute::Array &value, const UT_StringHolder &name, uint16 flags=0) const
bool concat(const PDG_AttributeHolder &other)
Concats the specified attribute with the attribute in this holder.
The attribute is read-only and cannot be modified on this work item.
static const uint16 theCommandStringFlags
Flags for the standard command string attribute.
const PDG_AttributeHolder * holder() const
Returns the underlying attribute holder.
long long int64
Definition: SYS_Types.h:116
static const UT_StringHolder theLabelName
Name of the built-in human readable label attribute.
PDG_AttributeType
Enumeration of possible attribute types.
static const UT_StringHolder theCookPercentName
Name of the built-in cook percent attribute.
Number of attribute tables.
An array of UT_StringHolder values.
static const UT_StringHolder theOutputFileName
GLuint const GLchar * name
Definition: glcorearb.h:786
bool str(UT_StringHolder &str) const
GLushort pattern
Definition: glad.h:2583
PDG_AttributeFlag
Enumeration of extra attribute flags. Flags can be ORed together.
An array of fpreal values.
Prepend to the existing attribute if it exists and matches.
GLenum GLenum GLsizei void * table
Definition: glad.h:5129
PDG_AttributeSaveType
Enumeration of the different ways that attributes can be saved.
static const UT_StringHolder thePartitionSizeName
Name of the built-in partition size attribute.
static const UT_StringHolder theCustomStateName
Name of the built-in custom state attribute.
static const uint16 theLabelFlags
Flags for the standard label attribute.
bool tryLockedAccess(const Func &func) const
GLsizeiptr size
Definition: glcorearb.h:664
GLenum GLenum dst
Definition: glcorearb.h:1793
GLenum func
Definition: glcorearb.h:783
The requested attribute is static and was not upgraded to dynamic.
bool forEachRO(const PDG_AttributePattern &pattern, const Func &func) const
GLenum GLsizei GLsizei GLint * values
Definition: glcorearb.h:1602
PDG_RWAttributeRef< PDG_AttributeString > stateRW(CreateWhen create=eCreateNever, AttribTable table=eTableCount)
An array of PDG_File values, e.g. File info structs.
bool desc(UT_WorkBuffer &result, bool pretty_print=false) const
Writes a string representation of the dictionaries to the buffer.
PDG_RWAttributeRef< Attribute > refRW(const UT_StringHolder &name)
static const uint16 theInputFileFlags
Flags for the standard input file attribute.
static const uint16 theOutputFileFlags
Flags for the standard output file attribute.
bool value(DataType &data, int component) const
GLuint index
Definition: glcorearb.h:786
PDG_AttributeCollision
Class to store JSON objects as C++ objects.
Definition: UT_JSONValue.h:99
OIIO_API bool attribute(string_view name, TypeDesc type, const void *val)
bool lockedAccess(const Func &func) const
A single, opaque PyObject.
The requested attribute was not found - the ref is invalid.
An array of int values.
const Attribute * attribute() const
Const accessor for the own attribute - could be nullptr.
PDG_AttributeError setValue(const UT_StringHolder &name, const typename Attribute::Array &values, bool create, PDG_AttributeOverwrite overwrite=PDG_AttributeOverwrite::eMatch)
PDG_RWAttributeRef< PDG_AttributeFile > inputFileRW(CreateWhen create=eCreateNever, AttribTable table=eTableCount)
Returns a read/write handle to the standard input file attribute.
PDG_AttributeError rename(const UT_StringHolder &src_name, const UT_StringHolder &dst_name, bool overwrite)
Renames the specififed attribute.
PDG_ROAttributeRef< PDG_AttributeFile > inputFileRO(AttribTable table=eTableCount) const
Returns a read-only handle to the standard input file attribute.
ChangeResult
Enumeration of possible attribute change results during regeneration.
PDG_ROAttributeRef< Attribute > refRO(const UT_StringHolder &name, AttribTable table=eTableCount) const
static bool makeValidName(UT_StringHolder &name, const UT_StringHolder &str, bool allow_internal=false)
Reader/Writer mutex class.
Definition: UT_RWLock.h:48
class IMF_EXPORT_TYPE Attribute
Definition: ImfForward.h:28
PDG_RWAttributeRef< Attribute > refRW(const UT_StringHolder &name, bool create, PDG_AttributeOverwrite overwrite=PDG_AttributeOverwrite::eMatch, uint16 flags=0)
bool forEachRO(const PDG_AttributePattern &pattern, const Func &func) const
Only copy if the existing attribute does not exist.
An array of UT_OptionsHolder values.
static const UT_StringHolder theCommandStringName
Name of the built-in command attribute.
PDG_AttributeCopyOp
Enumeration of attribute copy operations.
Definition: format.h:1821
PDG_AttributeError copyFrom(const UT_StringHolder &new_name, const UT_StringHolder &old_name, PDG_AttributeCopyOp copy_op, bool deep_copy)
The entire attribute map is read only.
An unspecified error occured - the ref is invalid.
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition: glcorearb.h:1297
GLenum src
Definition: glcorearb.h:1793