This document is for a version of USD that is under development. See this page for the current release.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
listEditor.h
1//
2// Copyright 2016 Pixar
3//
4// Licensed under the terms set forth in the LICENSE.txt file available at
5// https://openusd.org/license.
6//
7#ifndef PXR_USD_SDF_LIST_EDITOR_H
8#define PXR_USD_SDF_LIST_EDITOR_H
9
10#include "pxr/pxr.h"
11#include "pxr/base/tf/token.h"
12#include "pxr/usd/sdf/allowed.h"
14#include "pxr/usd/sdf/listOp.h"
15#include "pxr/usd/sdf/path.h"
16#include "pxr/usd/sdf/schema.h"
17#include "pxr/usd/sdf/spec.h"
18
19#include <functional>
20#include <optional>
21
22PXR_NAMESPACE_OPEN_SCOPE
23
24SDF_DECLARE_HANDLES(SdfLayer);
25SDF_DECLARE_HANDLES(SdfSpec);
26
32template <class TypePolicy>
33class Sdf_ListEditor
34{
35 Sdf_ListEditor(const Sdf_ListEditor&) = delete;
36 Sdf_ListEditor& operator=(const Sdf_ListEditor&) = delete;
37private:
38 typedef Sdf_ListEditor<TypePolicy> This;
39
40public:
41 typedef typename TypePolicy::value_type value_type;
42 typedef std::vector<value_type> value_vector_type;
43
44 virtual ~Sdf_ListEditor() = default;
45
46 SdfLayerHandle GetLayer() const
47 {
48 return _owner ? _owner->GetLayer() : SdfLayerHandle();
49 }
50
51 SdfPath GetPath() const
52 {
53 return _owner ? _owner->GetPath() : SdfPath();
54 }
55
56 bool IsValid() const
57 {
58 return !IsExpired();
59 }
60
61 bool IsExpired() const
62 {
63 return !_owner;
64 }
65
66 bool HasKeys() const
67 {
68 if (IsExplicit()) {
69 return true;
70 }
71 else if (IsOrderedOnly()) {
72 return !_GetOperations(SdfListOpTypeOrdered).empty();
73 }
74 else {
75 return (!_GetOperations(SdfListOpTypeAdded).empty() ||
76 !_GetOperations(SdfListOpTypePrepended).empty() ||
77 !_GetOperations(SdfListOpTypeAppended).empty() ||
78 !_GetOperations(SdfListOpTypeDeleted).empty() ||
79 !_GetOperations(SdfListOpTypeOrdered).empty());
80 }
81 }
82
83 virtual bool IsExplicit() const = 0;
84 virtual bool IsOrderedOnly() const = 0;
85
86 virtual SdfAllowed PermissionToEdit(SdfListOpType op) const
87 {
88 if (!_owner) {
89 return SdfAllowed("List editor is expired");
90 }
91
92 if (!_owner->PermissionToEdit()) {
93 return SdfAllowed("Permission denied");
94 }
95
96 return true;
97 }
98
99 virtual bool CopyEdits(const Sdf_ListEditor& rhs) = 0;
100 virtual bool ClearEdits() = 0;
101 virtual bool ClearEditsAndMakeExplicit() = 0;
102
103 typedef std::function<
104 std::optional<value_type>(const value_type&)
105 >
106 ModifyCallback;
107
114 virtual void ModifyItemEdits(const ModifyCallback& cb) = 0;
115
116 typedef std::function<
117 std::optional<value_type>(SdfListOpType, const value_type&)
118 >
119 ApplyCallback;
120
128 virtual void ApplyEditsToList(
129 value_vector_type* vec,
130 const ApplyCallback& cb = ApplyCallback()) = 0;
131
133 size_t GetSize(SdfListOpType op) const
134 {
135 return _GetOperations(op).size();
136 }
137
139 value_type Get(SdfListOpType op, size_t i) const
140 {
141 return _GetOperations(op)[i];
142 }
143
145 value_vector_type GetVector(SdfListOpType op) const
146 {
147 return _GetOperations(op);
148 }
149
152 size_t Count(SdfListOpType op, const value_type& val) const
153 {
154 const value_vector_type& ops = _GetOperations(op);
155 return std::count(ops.begin(), ops.end(), _typePolicy.Canonicalize(val));
156 }
157
160 size_t Find(SdfListOpType op, const value_type& val) const
161 {
162 const value_vector_type& vec = _GetOperations(op);
163 typename value_vector_type::const_iterator findIt =
164 std::find(vec.begin(), vec.end(), _typePolicy.Canonicalize(val));
165 if (findIt != vec.end()) {
166 return std::distance(vec.begin(), findIt);
167 }
168
169 return size_t(-1);
170 }
171
174 virtual bool ReplaceEdits(
175 SdfListOpType op, size_t index, size_t n,
176 const value_vector_type& elems) = 0;
177
179 virtual void ApplyList(SdfListOpType op, const Sdf_ListEditor& rhs) = 0;
180
181protected:
182 Sdf_ListEditor()
183 {
184 }
185
186 Sdf_ListEditor(const SdfSpecHandle& owner, const TfToken& field,
187 const TypePolicy& typePolicy)
188 : _owner(owner),
189 _field(field),
190 _typePolicy(typePolicy)
191 {
192 }
193
194 const SdfSpecHandle& _GetOwner() const
195 {
196 return _owner;
197 }
198
199 const TfToken& _GetField() const
200 {
201 return _field;
202 }
203
204 const TypePolicy& _GetTypePolicy() const
205 {
206 return _typePolicy;
207 }
208
209 virtual bool _ValidateEdit(SdfListOpType op,
210 const value_vector_type& oldValues,
211 const value_vector_type& newValues) const
212 {
213 // Disallow duplicate items from being stored in the new list
214 // editor values. This is O(n^2), but we expect the number of elements
215 // stored to be small enough that this won't matter.
216 //
217 // XXX:
218 // We assume that duplicate data items are never allowed to be
219 // authored. For full generality, this information ought to come from
220 // the layer schema.
221
222 // We also assume that the `oldValues` are already valid and do not
223 // contain duplicates. With this assumption, we can accelerate the
224 // common case of appending new items at the end and skip over a common
225 // prefix of oldValues and newValues. Then we can only check for dupes
226 // in the tail of newValues.
227
228 typename value_vector_type::const_iterator
229 oldValuesTail = oldValues.begin(),
230 newValuesTail = newValues.begin();
231 auto oldEnd = oldValues.end(), newEnd = newValues.end();
232 while (oldValuesTail != oldEnd && newValuesTail != newEnd &&
233 *oldValuesTail == *newValuesTail) {
234 ++oldValuesTail, ++newValuesTail;
235 }
236
237 for (auto i = newValuesTail; i != newEnd; ++i) {
238 // Have to check unmatched new items for dupes.
239 for (auto j = newValues.begin(); j != i; ++j) {
240 if (*i == *j) {
241 TF_CODING_ERROR("Duplicate item '%s' not allowed for "
242 "field '%s' on <%s>",
243 TfStringify(*i).c_str(),
244 _field.GetText(),
245 this->GetPath().GetText());
246 return false;
247 }
248 }
249 }
250
251 // Ensure that all new values are valid for this field.
252 const SdfSchema::FieldDefinition* fieldDef =
253 _owner->GetSchema().GetFieldDefinition(_field);
254 if (!fieldDef) {
255 TF_CODING_ERROR("No field definition for field '%s'",
256 _field.GetText());
257 }
258 else {
259 for (auto i = newValuesTail; i != newEnd; ++i) {
260 if (SdfAllowed isValid = fieldDef->IsValidListValue(*i)) { }
261 else {
262 TF_CODING_ERROR("%s", isValid.GetWhyNot().c_str());
263 return false;
264 }
265 }
266 }
267
268 return true;
269 }
270
271 virtual void _OnEdit(SdfListOpType op,
272 const value_vector_type& oldValues,
273 const value_vector_type& newValues) const
274 {
275 }
276
277 virtual const value_vector_type& _GetOperations(SdfListOpType op) const = 0;
278
279private:
280 SdfSpecHandle _owner;
281 TfToken _field;
282 TypePolicy _typePolicy;
283
284};
285
286template <class TypePolicy>
287std::ostream&
288operator<<(std::ostream& s, const Sdf_ListEditor<TypePolicy>& x)
289{
290 struct Util {
291 typedef typename Sdf_ListEditor<TypePolicy>::value_vector_type
292 value_vector_type;
293
294 static void _Write(std::ostream& s, const value_vector_type& v)
295 {
296 s << '[';
297 for (size_t i = 0, n = v.size(); i < n; ++i) {
298 if (i != 0) {
299 s << ", ";
300 }
301 s << v[i];
302 }
303 s << ']';
304 }
305 };
306
307 if (!x.IsValid()) {
308 return s;
309 }
310 else if (x.IsExplicit()) {
311 Util::_Write(s, x.GetVector(SdfListOpTypeExplicit));
312 return s;
313 }
314 else {
315 s << "{ ";
316 if (!x.IsOrderedOnly()) {
317 s << "'added': ";
318 Util::_Write(s, x.GetVector(SdfListOpTypeAdded));
319 s << "'prepended': ";
320 Util::_Write(s, x.GetVector(SdfListOpTypePrepended));
321 s << "'appended': ";
322 Util::_Write(s, x.GetVector(SdfListOpTypeAppended));
323 s << ", 'deleted': ";
324 Util::_Write(s, x.GetVector(SdfListOpTypeDeleted));
325 s << ", ";
326 }
327 s << "'ordered': ";
328 Util::_Write(s, x.GetVector(SdfListOpTypeOrdered));
329 return s << " }";
330 }
331}
332
333PXR_NAMESPACE_CLOSE_SCOPE
334
335#endif // PXR_USD_SDF_LIST_EDITOR_H
Indicates if an operation is allowed and, if not, why not.
Definition: allowed.h:29
A scene description container that can combine with other such containers to form simple component as...
Definition: layer.h:84
A path value used to locate objects in layers or scenegraphs.
Definition: path.h:274
Class defining various attributes for a field.
Definition: schema.h:56
Base class for all Sdf spec classes.
Definition: spec.h:33
Token for efficient comparison, assignment, and hashing of known strings.
Definition: token.h:71
GF_API std::ostream & operator<<(std::ostream &, const GfBBox3d &)
Output a GfBBox3d using the format [(range) matrix zeroArea].
#define TF_CODING_ERROR(fmt, args)
Issue an internal programming error, but continue execution.
Definition: diagnostic.h:68
std::string TfStringify(const T &v)
Convert an arbitrary type into a string.
Definition: stringUtils.h:555
TfToken class for efficient string referencing and hashing, plus conversions to and from stl string c...