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  /// Saves the attribute map to a JSON writer, in the most current data
511  /// format
512  bool save(UT_JSONWriter& writer,
513  PDG_AttributeSaveType save_type,
514  bool is_patch,
515  bool include_bound) const;
516 
517  /// Saves the attribute map to a UT_Options, in the most current data
518  /// format
519  void save(UT_OptionsHolder& dict,
520  PDG_AttributeSaveType save_type,
521  bool is_patch,
522  bool include_bound) const;
523 
524  /// Saves a single attribute holder to a JSON writer, in the most current
525  /// data format.
526  bool save(UT_JSONWriter& writer,
527  const PDG_AttributeHolder& holder,
528  const UT_StringHolder& name) const;
529 
530  /// Saves a single attribute holder to a UT_Options, in the most current
531  /// data format.
532  void save(UT_OptionsHolder& dict,
533  const PDG_AttributeHolder& holder) const;
534 
535  /// Loads a V1 data format json object
536  bool loadV1(PDG_AttributeCollision strategy,
537  const UT_JSONValue& value);
538 
539  /// Loads a V2 data format json object
540  bool loadV2(PDG_AttributeSaveType save_type,
541  PDG_AttributeCollision strategy,
542  const UT_JSONValue& value);
543 
544  /// Loads a V2 data format dictionary
545  bool loadV2(PDG_AttributeSaveType save_type,
546  PDG_AttributeCollision strategy,
547  const UT_OptionsHolder& dict);
548 
549  /// Returns the attribute owner
550  const PDG_AttributeOwner* owner() const
551  { return myOwner; }
552 
553  /// Returns a read-only handle to the standard input file attribute
555  inputFileRO(AttribTable table=eTableCount) const
556  {
557  return refRO<PDG_AttributeFile>(theInputFileName, table);
558  }
559 
560  /// Returns a read/write handle to the standard input file attribute
562  inputFileRW(CreateWhen create=eCreateNever,
563  AttribTable table=eTableCount)
564  {
565  return refRWC<PDG_AttributeFile>(
566  theInputFileName,
567  create,
569  table,
570  true,
571  theInputFileFlags);
572  }
573 
574  /// Returns a read-only handle to the standard output file attribute
576  outputFileRO(AttribTable table=eTableCount) const
577  {
578  return refRO<PDG_AttributeFile>(theOutputFileName, table);
579  }
580 
581  /// Returns a read-write handle to the standard output file attribute
583  outputFileRW(CreateWhen create=eCreateNever,
584  AttribTable table=eTableCount)
585  {
586  return refRWC<PDG_AttributeFile>(
587  theOutputFileName,
588  create,
590  table,
591  true,
592  theOutputFileFlags);
593  }
594 
595  /// Returns a read-only handle to the standard added file attribute
597  addedFileRO(AttribTable table=eTableCount) const
598  {
599  return refRO<PDG_AttributeFile>(theAddedFileName, table);
600  }
601 
602  /// Returns a read/write handle to the standard input file attribute
604  addedFileRW(CreateWhen create=eCreateNever,
605  AttribTable table=eTableCount)
606  {
607  return refRWC<PDG_AttributeFile>(
608  theAddedFileName,
609  create,
611  table,
612  true,
613  theAddedFileFlags);
614  }
615 
616  /// Returns a read-only handle to the standard command string file
617  /// attribute
619  commandStringRO(AttribTable table=eTableCount) const
620  {
621  return refRO<PDG_AttributeString>(theCommandStringName, table);
622  }
623 
624  /// Returns a read/write handle to the standard input file attribute
626  commandStringRW(CreateWhen create=eCreateNever,
627  AttribTable table=eTableCount)
628  {
629  return refRWC<PDG_AttributeString>(
630  theCommandStringName,
631  create,
633  table,
634  true,
635  theCommandStringFlags);
636  }
637 
638  /// Returns a read-only handle to the standard work item label
639  /// attribute
641  labelRO(AttribTable table=eTableCount) const
642  {
643  return refRO<PDG_AttributeString>(theLabelName, table);
644  }
645 
646  /// Returns a read/write handle to the standard work item label attribute
648  labelRW(CreateWhen create=eCreateNever,
649  AttribTable table=eTableCount)
650  {
651  return refRWC<PDG_AttributeString>(
652  theLabelName,
653  create,
655  table,
656  true,
657  theLabelFlags);
658  }
659 
660  /// Returns a read-only handle to the standard work item custom state
661  /// attribute
663  stateRO(AttribTable table=eTableCount) const
664  {
665  return refRO<PDG_AttributeString>(theCustomStateName, table);
666  }
667 
668  /// Returns a read/write handle to the standard work item custom state
669  /// attribute
671  stateRW(CreateWhen create=eCreateNever,
672  AttribTable table=eTableCount)
673  {
674  return refRWC<PDG_AttributeString>(
675  theCustomStateName,
676  create,
678  table,
679  true,
680  theCustomStateFlags);
681  }
682 
683  /// Returns a read-only handle to the standard work item cook progress
684  /// attribute
686  cookPercentRO(AttribTable table=eTableCount) const
687  {
688  return refRO<PDG_AttributeFloat>(theCookPercentName, table);
689  }
690 
691  /// Returns a read/write handle to the standard work item custom state
692  /// attribute
694  cookPercentRW(CreateWhen create=eCreateNever,
695  AttribTable table=eTableCount)
696  {
697  return refRWC<PDG_AttributeFloat>(
698  theCookPercentName,
699  create,
701  table,
702  true,
703  theCustomStateFlags);
704  }
705 
706  /// Returns a read-only, const attribute handle. Will be invalid if the
707  /// specified attribute does not exist or is of the wrong type.
708  template <typename Attribute=PDG_AttributeData>
711  AttribTable table=eTableCount) const
712  {
715 
716  if (table == eTableCount)
717  table = isRuntime(local_ctx) ? eTableDynamic : eTableStatic;
718 
719  const PDG_AttributeHolder* holder =
720  attribHolderR<Attribute>(error, name, table, true);
721 
722  if (!holder)
723  return PDG_ROAttributeRef<Attribute>(nullptr, name, myOwner, error);
724  return PDG_ROAttributeRef<Attribute>(holder, name, myOwner);
725  }
726 
727  /// Returns a read/write, non-const attribute handle that permits both
728  /// flag and data manipulation. Does not create the attribute, but will
729  /// upgrade it from a parent map/lower table if necessary. Note that this
730  /// method is intionally distinct from the version below since it uses a
731  /// codepath that doesn't require a concrete Attribute type in order to
732  /// create an attribute reference.
733  template <typename Attribute>
736  {
738  if (!canWrite(local_ctx))
739  {
740  return PDG_RWAttributeRef<Attribute>(nullptr, name, myOwner,
742  }
743 
744  AttribTable table = isStatic(local_ctx) ? eTableStatic : eTableDynamic;
745 
746  UT_StringHolder valid;
747  PDG_AttributeData::makeValidName(valid, name, false);
748 
750  PDG_AttributeHolder* holder = attribHolderW<Attribute>(
751  error, valid, table);
752 
753  if (holder)
754  {
755  myWasModified = true;
756  return PDG_RWAttributeRef<Attribute>(holder, valid, myOwner);
757  }
758 
759  return PDG_RWAttributeRef<Attribute>(nullptr, name, myOwner, error);
760  }
761 
762  /// Returns a read/write, non-const attribute handle that permits both
763  /// flag and data manipulation. Optionally creates the attribute if it
764  /// does not exist. Will be invalid if the specified attribute does not
765  /// exist, is of the wrong type, or is marked as read-only.
766  template <typename Attribute>
769  bool create,
770  PDG_AttributeOverwrite overwrite =
772  uint16 flags=0)
773  {
774  return refRWC<Attribute>(
775  name,
776  (create ? eCreateAlways : eCreateNever),
777  overwrite,
778  eTableCount,
779  false,
780  flags);
781  }
782 
783  /// Same as above, but with greater control on the creation flags.
784  template <typename Attribute>
787  CreateWhen create=eCreateNever,
788  PDG_AttributeOverwrite overwrite =
790  AttribTable table=eTableCount,
791  bool allow_internal_names=false,
792  uint16 flags=0)
793  {
795  if (!canWrite(local_ctx))
796  {
797  return PDG_RWAttributeRef<Attribute>(nullptr, name, myOwner,
799  }
800 
801  UT_StringHolder valid;
802  PDG_AttributeData::makeValidName(valid, name, allow_internal_names);
803 
804  if (table == eTableCount)
805  table = isStatic(local_ctx) ? eTableStatic : eTableDynamic;
806 
808  PDG_AttributeHolder* holder = attribHolderC<Attribute>(error, valid,
809  create, overwrite, true, table, flags);
810 
811  if (holder)
812  {
813  myWasModified = true;
814  return PDG_RWAttributeRef<Attribute>(holder, valid, myOwner);
815  }
816 
817  return PDG_RWAttributeRef<Attribute>(nullptr, name, myOwner, error);
818  }
819 
820  /// Copies the specified attribute into an attribute on this map
821  template <typename Attribute>
824  bool overwrite_existing,
825  bool deep_copy)
826  {
828  if (!canWrite(local_ctx))
830 
831  UT_StringHolder valid;
833 
835  isStatic(local_ctx) ? eTableStatic : eTableDynamic;
836  PDG_AttributeOverwrite overwrite = overwrite_existing ?
838 
840  PDG_AttributeHolder* holder = attribHolderC<Attribute>(error, valid,
841  eCreateAlways, overwrite, false, table, 0);
842  if (!holder)
843  return error;
844 
845  const PDG_AttributeHolder* ref_holder = ref.holder();
846  if (holder->isOwner())
847  {
848  holder->attribute<Attribute>()->copy(
849  ref_holder->template attribute<Attribute>());
850  }
851  else
852  {
853  holder->reset(*ref_holder, false, true);
854 
855  if (deep_copy)
856  holder->own();
857  }
858 
859  myWasModified = true;
861  }
862 
863  /// Renames the specififed attribute
865  const UT_StringHolder& dst_name,
866  bool overwrite)
867  {
868  // Attributes cannot be renamed if we don't have write access
870  if (!canWrite(local_ctx))
872 
874  isStatic(local_ctx) ? eTableStatic : eTableDynamic;
875 
876  PDG_AttributeHolder* holder = nullptr;
877  AttribTable src_table = eTableCount;
878 
879  // Find the attribute in the desired table, or lower table if it does
880  // not exist
881  for (int i = table; i >= 0; i--)
882  {
883  auto&& attrib = myAttributes[i].find(src_name);
884  if (attrib != myAttributes[i].end())
885  {
886  holder = attrib->second.get();
887  src_table = static_cast<AttribTable>(i);
888  break;
889  }
890  }
891 
892  // If the attribute only exists in a lower table, return an error since
893  // we cannot manipulate that table.
894  if (src_table < table)
896 
897  // If there's no holder, there's nothing to rename
898  if (!holder)
900 
901  // Disallow renaming of write protected attributes
902  if (holder->hasFlag(PDG_AttributeFlag::eReadOnlyFlag))
904 
905  // Disallow renaming of internal attributes
906  if (holder->hasFlag(PDG_AttributeFlag::eInternalFlag))
908 
909  // Create a valid destination name
910  UT_StringHolder valid;
911  PDG_AttributeData::makeValidName(valid, dst_name);
912 
913  // Check for an existing attribute with the dest name
914  auto&& iter = myAttributes[table].find(valid);
915  if (iter != myAttributes[table].end())
916  {
917  // Internal attributes can never be the destination of a rename
918  if (iter->second->hasFlag(PDG_AttributeFlag::eInternalFlag))
920  // Return early if the caller doesn't want to overwrite
921  else if (!overwrite)
923 
924  // Erase the existing attribute
925  myAttributes[table].erase(iter);
926  }
927 
928  // Move the attribute into the map entry with the new name
929  HolderPtr holder_copy =
930  UTmakeUnique<PDG_AttributeHolder>(std::move(*holder));
931  myAttributes[table].emplace(valid, std::move(holder_copy));
932  myAttributes[table].erase(src_name);
933 
934  myWasModified = true;
936  }
937 
938  /// Utility method that looks up a value from an attribute, with a given
939  /// index, and an optional flag filter
940  template <typename Attribute>
941  bool value(typename Attribute::Data& value,
942  const UT_StringHolder& name,
943  int index,
944  uint16 flags = 0) const
945  {
946  auto&& attribute = refRO<Attribute>(name);
947  if (!attribute || !attribute.hasFlags(flags))
948  return false;
950  return false;
951 
952  return attribute->value(value, index);
953  }
954 
955  /// Utility method that looks up an array value from an attribute, with an
956  /// optional flag filter
957  template <typename Attribute>
958  bool value(typename Attribute::Array& value,
959  const UT_StringHolder& name,
960  uint16 flags = 0) const
961  {
962  auto&& attribute = refRO<Attribute>(name);
963  if (!attribute || !attribute.hasFlags(flags))
964  return false;
966  return false;
967 
968  value = attribute->values();
969  return true;
970  }
971 
972  /// Utility method that sets an attribute value at a specified index, and
973  /// creates it if it does not already exist
974  template <typename Attribute>
976  const typename Attribute::Data& value,
977  int index,
978  bool create,
979  PDG_AttributeOverwrite overwrite =
981  {
982  auto&& attribute = refRW<Attribute>(name, create, overwrite);
983  if (!attribute)
984  return attribute.error();
985  attribute->setValue(value, index);
987  }
988 
989  /// Utility method that sets an attribute array value, and creates it if
990  /// it does not already exist
991  template <typename Attribute>
993  const typename Attribute::Array& values,
994  bool create,
995  PDG_AttributeOverwrite overwrite =
997  {
998  auto&& attribute = refRW<Attribute>(name, create, overwrite);
999  if (!attribute)
1000  return attribute.error();
1001  attribute->setValue(values);
1003  }
1004 
1005  /// Utility method that clears all values from an attribute, and creates
1006  /// it if it does not already exist
1007  template <typename Attribute>
1009  bool create,
1010  PDG_AttributeOverwrite overwrite =
1012  {
1013  auto&& attribute = refRW<Attribute>(name, create, overwrite);
1014  if (!attribute)
1015  return attribute.error();
1016  attribute->clear();
1018  }
1019 
1020  /// Queries all values of the specified attribute type and returns them as
1021  /// a map.
1022  template <typename Attribute>
1023  bool values(typename Attribute::Map& value_map,
1024  uint16 flags = 0) const
1025  {
1026  values<Attribute>(eTableStatic, value_map, flags);
1028  if (isRuntime(local_ctx))
1029  values<Attribute>(eTableDynamic, value_map, flags);
1030  return true;
1031  }
1032 
1033  /// Queries a scalar value by name/index and stores it as a PDGT_Value
1034  bool value(PDGT_Value& value,
1035  const UT_StringHolder& name,
1036  int index,
1037  uint16 flags = 0) const;
1038 
1039  /// Queries all values as PDGT_Values
1040  bool values(PDGT_Value::Map& value_map,
1041  uint16 flags = 0) const;
1042 
1043 
1044  /// Returns true if attribute has the the specified flag set, else false
1045  bool hasFlag(const UT_StringHolder& name,
1046  PDG_AttributeFlag flag) const;
1047 
1048  /// Sets or clears a flag on the specified attribute
1049  PDG_AttributeError setFlag(const UT_StringHolder& name,
1050  PDG_AttributeFlag flag,
1051  bool set);
1052 
1053  /// Sets or clears a flag on all attributes
1054  bool setFlagAll(PDG_AttributeFlag flag,
1055  bool set);
1056 
1057 
1058  /// Begins tracking attribute changes
1059  void startChangeTracking();
1060 
1061  /// Ends tracking changes, returning true if a change made was
1062  ChangeResult endChangeTracking(bool check_all);
1063 
1064  /// Computes the hash value for attrib values, errors can occur if python
1065  /// objects hash() raises an exception.
1066  bool hash(SYS_HashType& hash_val,
1067  AttribTable table,
1068  bool include_internal,
1069  bool include_unowned,
1070  UT_WorkBuffer& errors) const;
1071 
1072  /// Computes the hash value for the specific attribute values specified in
1073  /// the set of names. This is the same as the function above, but it
1074  /// only applies to the indicated subset of attribs.
1075  bool hash(SYS_HashType& hash_val,
1076  const UT_ArrayStringSet& filter,
1077  AttribTable table,
1078  bool include_internal,
1079  bool include_unowned,
1080  UT_WorkBuffer& errors) const;
1081 
1082 private:
1083  /// Managed attribute holder pointer
1084  using HolderPtr = UT_UniquePtr<PDG_AttributeHolder>;
1085 
1086  /// String map to attribute holder - the actual storage type of this
1087  /// structure
1088  using Map = UT_ArrayStringMap<HolderPtr>;
1089 
1090 private:
1091  /// Returns the attribute lock for this attribute map
1092  UT_RWLock& attributeLock() const;
1093 
1094  /// Returns the global attribute lock for this map
1095  UT_RWLock* globalLock() const;
1096 
1097  /// Called when the map is unlocked
1098  void onUnlock() const;
1099 
1100  /// Internal logic for hashing attributes
1101  bool hashAttributes(
1102  SYS_HashType& hash_val,
1103  const UT_ArrayStringSet& filter,
1104  AttribTable table,
1105  bool include_internal,
1106  bool include_unowned,
1107  UT_WorkBuffer& errors) const;
1108 
1109  /// Returns the sorted set of attribute names for a save operation
1110  void saveNames(
1111  UT_SortedStringSet& names,
1112  PDG_AttributeSaveType save_type) const;
1113 
1114  /// Returns the attribute holder for the specified name and save options,
1115  /// or nullptr if no valid holder meets the save settings
1116  const PDG_AttributeHolder*
1117  saveHolder(
1118  const UT_StringHolder& name,
1119  PDG_AttributeSaveType save_type,
1120  AttribTable table,
1121  bool is_patch,
1122  bool include_bound) const;
1123 
1124  /// Returns a reference to the shared, static invalid attribute reg
1125  static PDG_AttributeHolder&
1126  invalidHolder();
1127 
1128  /// Returns true if the attribute owenr that owns this attribute map can
1129  /// create RW attribute references
1130  bool canWrite(const PDG_EvaluationContext* ctx) const;
1131 
1132  /// Returns true if called during a runtime/cook operation, instead of a
1133  /// generation operation
1134  bool isRuntime(const PDG_EvaluationContext* ctx) const;
1135 
1136  /// Returns true if called during work item generation
1137  bool isStatic(const PDG_EvaluationContext* ctx) const;
1138 
1139  /// Returns true if the specified attribute exists in the table
1140  bool hasAttribute(const UT_StringHolder& name,
1141  AttribTable table) const
1142  {
1143  if (myParentMap && myParentMap->hasAttribute(name, table))
1144  return true;
1145  return (myAttributes[table].find(name) != myAttributes[table].end());
1146  }
1147 
1148  /// Merges attributes from the source table in another map to dst
1149  /// table in this map.
1150  bool mergeMap(
1151  PDG_AttributeMerge& attrib_merge,
1152  UT_StringSet& exclusion_set,
1153  const PDG_AttributeMap& other,
1154  AttribTable src_table,
1155  AttribTable dst_table,
1156  bool flatten_dynamic);
1157 
1158  /// Merges an attribute from a different map into the dst table in this
1159  /// map.
1160  bool mergeAttribute(
1161  PDG_AttributeMerge& attrib_merge,
1162  UT_StringSet& exclusion_set,
1163  const PDG_AttributeHolder& other_holder,
1164  const UT_StringHolder& name,
1165  AttribTable dst_table);
1166 
1167  /// Loads an attribute from the json value, with the specified name, type
1168  /// and flags
1169  bool loadAttribute(AttribTable table,
1170  PDG_AttributeCollision strategy,
1172  const UT_StringHolder& name,
1173  uint16 flags,
1174  bool is_own,
1175  bool is_concat,
1176  const UT_JSONValue& data);
1177 
1178  /// Loads an attribute from the dict, with the specified name, type
1179  /// and flags
1180  bool loadAttribute(AttribTable table,
1181  PDG_AttributeCollision strategy,
1183  const UT_StringHolder& name,
1184  uint16 flags,
1185  bool is_own,
1186  bool is_concat,
1187  const UT_OptionsHolder& dict);
1188 
1189  /// Loads an old-style attribute entry from json
1190  bool loadV1Map(PDG_AttributeCollision strategy,
1192  const UT_JSONValue& data);
1193 
1194  /// Saves the values from the specified map into an instace of a generic
1195  /// data map
1196  bool values(AttribTable table,
1197  PDGT_Value::Map& value_map,
1198  uint16 flags) const
1199  {
1200  if (myParentMap)
1201  myParentMap->values(table, value_map, flags);
1202 
1203  for (auto&& attrib_pair : myAttributes[table])
1204  {
1205  const PDG_AttributeHolder& holder = *attrib_pair.second;
1206  if (!holder.hasFlags(flags))
1207  continue;
1209  continue;
1210  if (!attribToValue(value_map[attrib_pair.first], 0, holder))
1211  return false;
1212  }
1213 
1214  return true;
1215  }
1216 
1217  /// Saves the values from the specified map into an instance of the map
1218  /// data type
1219  template <typename Attribute>
1220  void values(AttribTable table,
1221  typename Attribute::Map& value_map,
1222  uint16 flags) const
1223  {
1224  if (myParentMap)
1225  myParentMap->values<Attribute>(table, value_map, flags);
1226 
1227  for (auto&& attrib_pair : myAttributes[table])
1228  {
1229  const PDG_AttributeHolder& holder = *attrib_pair.second;
1230  if (!holder.typeMatch<Attribute>())
1231  {
1232  value_map.erase(attrib_pair.first);
1233  continue;
1234  }
1235 
1236  if (!holder.hasFlags(flags))
1237  continue;
1239  continue;
1240 
1241  const Attribute* attribute = holder.attribute<Attribute>();
1242  if (attribute)
1243  value_map[attrib_pair.first] = attribute->values();
1244  }
1245  }
1246 
1247  /// Returns the size of an attribute
1248  static int attribSize(const PDG_AttributeHolder& src)
1249  {
1250  switch (src.type())
1251  {
1253  return src.attribute<PDG_AttributeInteger>()->size();
1255  return src.attribute<PDG_AttributeFloat>()->size();
1257  return src.attribute<PDG_AttributeString>()->size();
1259  return src.attribute<PDG_AttributeFile>()->size();
1261  return src.attribute<PDG_AttributeDictionary>()->size();
1263  return src.attribute<PDG_AttributePyObject>()->size();
1265  return src.attribute<PDG_AttributeGeometry>()->size();
1266  default:
1267  return -1;
1268  }
1269  }
1270 
1271  /// Concatenates an attribute
1272  static bool attribConcat(PDG_AttributeHolder& dst,
1273  const PDG_AttributeHolder& src)
1274  {
1275  if (src.type() != dst.type())
1276  return false;
1277 
1278  switch (dst.type())
1279  {
1281  return dst.concat<PDG_AttributeInteger>(src);
1283  return dst.concat<PDG_AttributeFloat>(src);
1285  return dst.concat<PDG_AttributeString>(src);
1287  return dst.concat<PDG_AttributeFile>(src);
1289  return dst.concat<PDG_AttributeDictionary>(src);
1290  default:
1291  return false;
1292  }
1293 
1294  return true;
1295  }
1296 
1297  /// Updates the size of an attribute
1298  static bool attribAdjustSize(PDG_AttributeHolder& attrib,
1299  int offset)
1300  {
1301  switch (attrib.type())
1302  {
1304  return attrib.adjustSize<PDG_AttributeInteger>(offset);
1306  return attrib.adjustSize<PDG_AttributeFloat>(offset);
1308  return attrib.adjustSize<PDG_AttributeString>(offset);
1310  return attrib.adjustSize<PDG_AttributeFile>(offset);
1312  return attrib.adjustSize<PDG_AttributeDictionary>(offset);
1313  default:
1314  return false;
1315  }
1316 
1317  return true;
1318  }
1319 
1320  /// Clears an attribute
1321  static void attribClear(PDG_AttributeHolder& attrib)
1322  {
1323  switch (attrib.type())
1324  {
1326  attrib.clear<PDG_AttributeInteger>();
1327  break;
1329  attrib.clear<PDG_AttributeFloat>();
1330  break;
1332  attrib.clear<PDG_AttributeString>();
1333  break;
1335  attrib.clear<PDG_AttributeFile>();
1336  break;
1338  attrib.clear<PDG_AttributeDictionary>();
1339  break;
1340  default:
1341  break;
1342  }
1343  }
1344 
1345  /// Saves the name of the specified attribute to the PDGT_Value variant.
1346  static bool attribToValue(PDGT_Value& value,
1347  int index,
1348  const PDG_AttributeHolder& holder)
1349  {
1350  switch (holder.type())
1351  {
1353  return attribToValue<PDG_AttributeFloat>(value, index, holder);
1355  return attribToValue<PDG_AttributeInteger>(value, index, holder);
1357  return attribToValue<PDG_AttributeString>(value, index, holder);
1359  {
1360  typename PDG_AttributeFile::Data temp;
1361  if (holder.attribute<PDG_AttributeFile>()->value(temp, index))
1362  value.setValue(temp.local_data());
1363  else
1364  value.setValue("");
1365  return true;
1366  }
1368  {
1370  if (holder.attribute<PDG_AttributeDictionary>()->desc(
1371  buffer, index))
1372  {
1373  value.setValue(buffer.buffer());
1374  }
1375  else
1376  value.setValue("");
1377  return true;
1378  }
1380  {
1381  UT_StringHolder str;
1382  if (holder.attribute<PDG_AttributePyObject>()->str(str))
1383  value.setValue(str);
1384  else
1385  value.setValue("");
1386  return true;
1387  }
1389  {
1390  UT_StringHolder str;
1391  if (holder.attribute<PDG_AttributeGeometry>()->desc(str))
1392  value.setValue(str);
1393  else
1394  value.setValue("");
1395  return true;
1396 
1397  }
1398  default:
1399  return false;
1400  }
1401 
1402  return false;
1403  }
1404 
1405  /// Helper method for storing an attribute value to a PDGT_Value variant.
1406  template <typename Attribute>
1407  static bool attribToValue(PDGT_Value& value,
1408  int index,
1409  const PDG_AttributeHolder& holder)
1410  {
1411  typename Attribute::Data temp;
1412  if (holder.attribute<Attribute>()->value(temp, index))
1413  value.setValue(temp);
1414  else
1415  value.setValue(typename Attribute::Data());
1416 
1417  return true;
1418  }
1419 
1420  /// Returns a const attribute holder an existing static or dynamic
1421  /// attribute. Uses the local context to determine if dynamic access
1422  /// is permitted.
1423  template <typename Attribute>
1424  const PDG_AttributeHolder*
1425  attribHolderR(const UT_StringHolder& name) const
1426  {
1429  AttribTable table = isRuntime(local_ctx) ? eTableDynamic : eTableStatic;
1430  return attribHolderR<Attribute>(error, name, table, true);
1431  }
1432 
1433  /// Returns an const attribute holder to an existing static or dynamic
1434  /// attribute. Does not create the attribute if it does not exist.
1435  template <typename Attribute>
1436  const PDG_AttributeHolder*
1437  attribHolderR(PDG_AttributeError& error,
1438  const UT_StringHolder& name,
1439  AttribTable table,
1440  bool check_parent) const
1441  {
1442  const PDG_AttributeHolder* holder = nullptr;
1443  for (int i = table; i >= 0; i--)
1444  {
1445  auto&& attrib = myAttributes[i].find(name);
1446  if (attrib != myAttributes[i].end())
1447  {
1448  holder = attrib->second.get();
1449  break;
1450  }
1451  }
1452 
1453  if (!holder)
1454  {
1455  if (myParentMap && check_parent)
1456  {
1457  return myParentMap->attribHolderR<Attribute>(
1458  error, name, table, true);
1459  }
1460 
1462  return nullptr;
1463  }
1464 
1465  if (!holder->typeMatch<Attribute>())
1466  {
1468  return nullptr;
1469  }
1470 
1471  error = PDG_AttributeError::eNone;
1472  return holder;
1473  }
1474 
1475  /// Returns an attribute holder for an existing or new attribute, either
1476  /// static or dynamic. If the attribute is static but we need a dynamic
1477  /// one, the existing static attribute is copied to the dynamic map.
1478  template <typename Attribute>
1479  PDG_AttributeHolder* attribHolderW(PDG_AttributeError& error,
1480  const UT_StringHolder& name,
1481  AttribTable table)
1482  {
1483  PDG_AttributeHolder* holder = nullptr;
1484  AttribTable src_table = eTableCount;
1485 
1486  // Check for the attribute in the desired table, and work downwards if
1487  // its not found
1488  for (int i = table; i >= 0; i--)
1489  {
1490  auto&& attrib = myAttributes[i].find(name);
1491  if (attrib != myAttributes[i].end())
1492  {
1493  holder = attrib->second.get();
1494  src_table = static_cast<AttribTable>(i);
1495  break;
1496  }
1497  }
1498 
1499  // If the attribute doesn't exist anywhere, check the parent map first
1500  // before returning an error.
1501  if (!holder)
1502  {
1503  // If a parent map is set, check for the attribute on the parent
1504  if (myParentMap)
1505  {
1506  const PDG_AttributeHolder* parent =
1507  attribHolderR<Attribute>(error, name, table, true);
1508 
1509  // We never create an attribute on the parent map, but if one
1510  // does exist make a copy of it and store it on this map.
1511  if (parent)
1512  {
1513  HolderPtr new_holder = UTmakeUnique<PDG_AttributeHolder>();
1514  new_holder->reset(*parent, parent->isOwner(), true);
1515  auto&& insert = myAttributes[table].emplace(
1516  name, std::move(new_holder));
1517  error = PDG_AttributeError::eNone;
1518  return insert.first->second.get();
1519  }
1520  }
1521 
1523  return nullptr;
1524  }
1525 
1526  // Read only attributes cannot be accessed for writing in any case
1527  if (holder->hasFlag(PDG_AttributeFlag::eReadOnlyFlag))
1528  {
1530  return nullptr;
1531  }
1532 
1533  // If the attribute exists in any of the tables but is the wrong type,
1534  // this is always an error.
1535  if (!holder->typeMatch<Attribute>())
1536  {
1538  return nullptr;
1539  }
1540 
1541  // If the attribute was found in a lower table than the one that was
1542  // requested, only create the attribute if the creation mode is
1543  // Always or Upgrade.
1544  if (src_table < table)
1545  {
1546  HolderPtr holder_copy = UTmakeUnique<PDG_AttributeHolder>();
1547  holder_copy->reset(*holder, holder->isOwner(), true);
1548 
1549  auto&& insert = myAttributes[table].emplace(
1550  name, std::move(holder_copy));
1551  error = PDG_AttributeError::eNone;
1552  return insert.first->second.get();
1553  }
1554 
1555  error = PDG_AttributeError::eNone;
1556  return holder;
1557  }
1558 
1559  /// Returns an attribute holder for an existing or new attribute, either
1560  /// static or dynamic. If the attribute is static but we need a dynamic
1561  /// one, the existing static attribute is copied to the dynamic map. This
1562  /// method will also create the attribute if needed, or if the type does
1563  /// not match, depending on the creation/overwrite options.
1564  template <typename Attribute>
1565  PDG_AttributeHolder* attribHolderC(PDG_AttributeError& error,
1566  const UT_StringHolder& name,
1567  CreateWhen create,
1568  PDG_AttributeOverwrite overwrite,
1569  bool own,
1570  AttribTable table,
1571  uint16 flags)
1572  {
1573  PDG_AttributeHolder* holder = nullptr;
1574  AttribTable src_table = eTableCount;
1575 
1576  // Check for the attribute in the desired table, and work downwards if
1577  // its not found
1578  for (int i = table; i >= 0; i--)
1579  {
1580  auto&& attrib = myAttributes[i].find(name);
1581  if (attrib != myAttributes[i].end())
1582  {
1583  holder = attrib->second.get();
1584  src_table = static_cast<AttribTable>(i);
1585  break;
1586  }
1587  }
1588 
1589  // If the attribute doesn't exist anywhere, only create it when the
1590  // create mode is Always.
1591  if (!holder)
1592  {
1593  // If a parent map is set, check for the attribute on the parent
1594  if (myParentMap)
1595  {
1596  const PDG_AttributeHolder* parent =
1597  attribHolderR<Attribute>(error, name, table, true);
1598 
1599  // We never create an attribute on the parent map, but if one
1600  // does exist and we were request to upgrade if possible, make
1601  // a copy of it and store it on this map.
1602  if (parent)
1603  {
1604  if (create == eCreateNever)
1605  {
1607  return nullptr;
1608  }
1609 
1610  HolderPtr new_holder = UTmakeUnique<PDG_AttributeHolder>();
1611  new_holder->reset(*parent, parent->isOwner(), true);
1612 
1613  auto&& insert = myAttributes[table].emplace(
1614  name, std::move(new_holder));
1615  error = PDG_AttributeError::eNone;
1616  return insert.first->second.get();
1617  }
1618  }
1619 
1620  if (create != eCreateAlways)
1621  {
1623  return nullptr;
1624  }
1625 
1626  HolderPtr holder = UTmakeUnique<PDG_AttributeHolder>(
1627  own ? new Attribute() : nullptr,
1628  Attribute::TypeEnum,
1629  flags,
1630  own,
1631  false);
1632 
1633  auto&& insert = myAttributes[table].emplace(
1634  name, std::move(holder));
1635  error = PDG_AttributeError::eNone;
1636  return insert.first->second.get();
1637  }
1638 
1639  // If overwrite is off, we don't return back a holder
1640  if (overwrite == PDG_AttributeOverwrite::eNever)
1641  {
1643  return nullptr;
1644  }
1645 
1646  // Read only attributes cannot be accessed for writing in any case
1647  if (holder->hasFlag(PDG_AttributeFlag::eReadOnlyFlag))
1648  {
1650  return nullptr;
1651  }
1652 
1653  // If the attribute exists in any of the tables but is the wrong type,
1654  // this is always an error.
1655  bool type_match = holder->typeMatch<Attribute>();
1656  if (!type_match && (overwrite != PDG_AttributeOverwrite::eAlways))
1657  {
1659  return nullptr;
1660  }
1661 
1662  // If the attribute was found in a lower table than the one that was
1663  // requested, only create the attribute if the creation mode is
1664  // Always or Upgrade.
1665  if (src_table < table)
1666  {
1667  if (create == eCreateNever)
1668  {
1670  return nullptr;
1671  }
1672 
1673  HolderPtr holder_copy = UTmakeUnique<PDG_AttributeHolder>();
1674  if (!type_match)
1675  {
1676  holder_copy->reset(
1677  new Attribute(),
1678  Attribute::TypeEnum,
1679  flags,
1680  own,
1681  false,
1682  false);
1683  }
1684  else
1685  holder_copy->reset(*holder, holder->isOwner(), true);
1686 
1687  auto&& insert = myAttributes[table].emplace(
1688  name, std::move(holder_copy));
1689  error = PDG_AttributeError::eNone;
1690  return insert.first->second.get();
1691  }
1692 
1693  error = PDG_AttributeError::eNone;
1694  if (!type_match)
1695  {
1696  holder->reset(
1697  new Attribute(), Attribute::TypeEnum, flags, own, false, false);
1698  }
1699 
1700  return holder;
1701  }
1702 
1703 private:
1704  Map myAttributes[eTableCount];
1705  UT_UniquePtr<Map[]> myStaleAttributes;
1706  const PDG_AttributeOwner* myOwner;
1707  PDG_AttributeMap* myParentMap;
1708  mutable UT_RWLock myAttributeLock;
1709 
1710  bool myHasRuntimeAttribs;
1711  mutable bool myWasModified;
1712 };
1713 
1714 #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
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.
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
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:7172
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.
OIIO_FORCEINLINE vbool4 insert(const vbool4 &a, bool val)
Helper: substitute val for a[i].
Definition: simd.h:3436
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.
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)
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
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.
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.
Definition: core.h:1131
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
type
Definition: core.h:1059
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
An array of UT_OptionsHolder values.
static const UT_StringHolder theCommandStringName
Name of the built-in command attribute.
Definition: format.h:895
The entire attribute map is read only.
FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr &out) -> bool
Definition: core.h:2089
PDG_AttributeError copyFrom(const UT_StringHolder &name, const PDG_ROAttributeRef< Attribute > &ref, bool overwrite_existing, bool deep_copy)
Copies the specified attribute into an attribute on this map.
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition: glcorearb.h:1297
GLenum src
Definition: glcorearb.h:1793