1 // 2 // Copyright 2016 Pixar 3 // 4 // Licensed under the Apache License, Version 2.0 (the "Apache License") 5 // with the following modification; you may not use this file except in 6 // compliance with the Apache License and the following modification to it: 7 // Section 6. Trademarks. is deleted and replaced with: 8 // 9 // 6. Trademarks. This License does not grant permission to use the trade 10 // names, trademarks, service marks, or product names of the Licensor 11 // and its affiliates, except as required to comply with Section 4(c) of 12 // the License and to reproduce the content of the NOTICE file. 13 // 14 // You may obtain a copy of the Apache License at 15 // 16 // http://www.apache.org/licenses/LICENSE-2.0 17 // 18 // Unless required by applicable law or agreed to in writing, software 19 // distributed under the Apache License with the above modification is 20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 21 // KIND, either express or implied. See the Apache License for the specific 22 // language governing permissions and limitations under the Apache License. 23 // 24 #ifndef PXR_USD_SDF_LIST_EDITOR_PROXY_H 25 #define PXR_USD_SDF_LIST_EDITOR_PROXY_H 26 27 /// \file sdf/listEditorProxy.h 28 29 #include "pxr/pxr.h" 30 #include "pxr/usd/sdf/listEditor.h" 31 #include "pxr/usd/sdf/listProxy.h" 32 #include "pxr/usd/sdf/changeBlock.h" 33 34 #include "pxr/base/vt/value.h" // for Vt_DefaultValueFactory 35 36 #include <boost/optional.hpp> 37 #include <boost/shared_ptr.hpp> 38 39 #include <functional> 40 41 PXR_NAMESPACE_OPEN_SCOPE 42 43 /// \class SdfListEditorProxy 44 /// 45 /// Represents a set of list editing operations. 46 /// 47 /// An SdfListEditorProxy allows consumers to specify a transformation to be 48 /// applied to a list via a set of list editing operations. Given a starting 49 /// ordered list, it can either replace the result with another ordered list 50 /// or apply a sequence of three operations: deleting keys, then adding keys 51 /// to the end (if they aren't already in the starting list), then reordering 52 /// keys. 53 /// 54 /// The type policy defines the value type that a particular proxy can operate 55 /// on. 56 template <class _TypePolicy> 57 class SdfListEditorProxy { 58 public: 59 typedef _TypePolicy TypePolicy; 60 typedef SdfListEditorProxy<TypePolicy> This; 61 typedef SdfListProxy<TypePolicy> ListProxy; 62 typedef typename TypePolicy::value_type value_type; 63 typedef std::vector<value_type> value_vector_type; 64 65 // ApplyEdits types. 66 typedef std::function<boost::optional<value_type> 67 (SdfListOpType, const value_type&)> ApplyCallback; 68 69 // ModifyEdits types. 70 typedef std::function<boost::optional<value_type> 71 (const value_type&)> ModifyCallback; 72 73 /// Creates a default proxy object. The object evaluates to \c false in a 74 /// boolean context and all operations on this object have no effect. SdfListEditorProxy()75 SdfListEditorProxy() 76 { 77 } 78 79 /// Creates a new proxy object backed by the supplied list editor. SdfListEditorProxy(const boost::shared_ptr<Sdf_ListEditor<TypePolicy>> & listEditor)80 explicit SdfListEditorProxy( 81 const boost::shared_ptr<Sdf_ListEditor<TypePolicy> >& listEditor) 82 : _listEditor(listEditor) 83 { 84 } 85 86 /// Returns true if the list editor is expired. IsExpired()87 bool IsExpired() const 88 { 89 if (!_listEditor) { 90 return false; 91 } 92 93 return _listEditor->IsExpired(); 94 } 95 96 /// Returns \c true if the editor has an explicit list, \c false if 97 /// it has list operations. IsExplicit()98 bool IsExplicit() const 99 { 100 return _Validate() ? _listEditor->IsExplicit() : true; 101 } 102 103 /// Returns \c true if the editor is not explicit and allows ordering 104 /// only. IsOrderedOnly()105 bool IsOrderedOnly() const 106 { 107 return _Validate() ? _listEditor->IsOrderedOnly() : false; 108 } 109 110 /// Returns \c true if the editor has an explicit list (even if it's 111 /// empty) or it has any added, prepended, appended, deleted, 112 /// or ordered keys. HasKeys()113 bool HasKeys() const 114 { 115 return _Validate() ? _listEditor->HasKeys() : true; 116 } 117 118 /// Apply the edits to \p vec. ApplyEditsToList(value_vector_type * vec)119 void ApplyEditsToList(value_vector_type* vec) const 120 { 121 if (_Validate()) { 122 _listEditor->ApplyEditsToList(vec, ApplyCallback()); 123 } 124 } 125 126 /// Apply the edits to \p vec. If \p callback is valid then it's called 127 /// for every key in the editor before applying it to \p vec. If the 128 /// returned key is invalid then the key will not be applied. Otherwise 129 /// the returned key is applied, allowing callbacks to perform key 130 /// translation. 131 template <class CB> ApplyEditsToList(value_vector_type * vec,CB callback)132 void ApplyEditsToList(value_vector_type* vec, CB callback) const 133 { 134 if (_Validate()) { 135 _listEditor->ApplyEditsToList(vec, ApplyCallback(callback)); 136 } 137 } 138 139 /// Copies the keys from \p other. This differs from assignment 140 /// because assignment just makes two list editors refer to the 141 /// same lists. 142 /// 143 /// Not all list editors support changing their mode. If the mode 144 /// can't be changed to the mode of \p other then this does nothing 145 /// and returns \c false, otherwise it returns \c true. CopyItems(const This & other)146 bool CopyItems(const This& other) 147 { 148 return _Validate() && other._Validate() ? 149 _listEditor->CopyEdits(*other._listEditor) : false; 150 } 151 152 /// Removes all keys and changes the editor to have list operations. 153 /// 154 /// Not all list editors support changing their mode. If the mode 155 /// can't be changed to the mode of \p other then this does nothing 156 /// and returns \c false, otherwise it returns \c true. ClearEdits()157 bool ClearEdits() 158 { 159 return _Validate() ? _listEditor->ClearEdits() : false; 160 } 161 162 /// Removes all keys and changes the editor to be explicit. 163 /// 164 /// Not all list editors support changing their mode. If the mode 165 /// can't be changed to the mode of \p other then this does nothing 166 /// and returns \c false, otherwise it returns \c true. ClearEditsAndMakeExplicit()167 bool ClearEditsAndMakeExplicit() 168 { 169 return _Validate() ? _listEditor->ClearEditsAndMakeExplicit() : false; 170 } 171 172 /// \p callback is called for every key. If the returned key is 173 /// invalid then the key is removed, otherwise it's replaced with the 174 /// returned key. 175 template <class CB> ModifyItemEdits(CB callback)176 void ModifyItemEdits(CB callback) 177 { 178 if (_Validate()) { 179 _listEditor->ModifyItemEdits(ModifyCallback(callback)); 180 } 181 } 182 183 /// Check if the given item is explicit, added, prepended, appended, 184 /// deleted, or ordered by this editor. If \p onlyAddOrExplicit is 185 /// \c true we only check the added or explicit items. 186 bool ContainsItemEdit(const value_type& item, 187 bool onlyAddOrExplicit = false) const 188 { 189 if (_Validate()) { 190 size_t i; 191 192 i = GetExplicitItems().Find(item); 193 if (i != size_t(-1)) { 194 return true; 195 } 196 197 i = GetAddedItems().Find(item); 198 if (i != size_t(-1)) { 199 return true; 200 } 201 202 i = GetPrependedItems().Find(item); 203 if (i != size_t(-1)) { 204 return true; 205 } 206 207 i = GetAppendedItems().Find(item); 208 if (i != size_t(-1)) { 209 return true; 210 } 211 212 if (!onlyAddOrExplicit) { 213 i = GetDeletedItems().Find(item); 214 if (i != size_t(-1)) { 215 return true; 216 } 217 218 i = GetOrderedItems().Find(item); 219 if (i != size_t(-1)) { 220 return true; 221 } 222 } 223 } 224 225 return false; 226 } 227 228 /// Remove all occurrences of the given item, regardless of whether 229 /// the item is explicit, added, prepended, appended, deleted, or ordered. RemoveItemEdits(const value_type & item)230 void RemoveItemEdits(const value_type& item) 231 { 232 if (_Validate()) { 233 SdfChangeBlock block; 234 235 GetExplicitItems().Remove(item); 236 GetAddedItems().Remove(item); 237 GetPrependedItems().Remove(item); 238 GetAppendedItems().Remove(item); 239 GetDeletedItems().Remove(item); 240 GetOrderedItems().Remove(item); 241 } 242 } 243 244 /// Replace all occurrences of the given item, regardless of 245 /// whether the item is explicit, added, prepended, appended, 246 /// deleted or ordered. ReplaceItemEdits(const value_type & oldItem,const value_type & newItem)247 void ReplaceItemEdits(const value_type& oldItem, const value_type& newItem) 248 { 249 if (_Validate()) { 250 SdfChangeBlock block; 251 252 GetExplicitItems().Replace(oldItem, newItem); 253 GetAddedItems().Replace(oldItem, newItem); 254 GetPrependedItems().Replace(oldItem, newItem); 255 GetAppendedItems().Replace(oldItem, newItem); 256 GetDeletedItems().Replace(oldItem, newItem); 257 GetOrderedItems().Replace(oldItem, newItem); 258 } 259 } 260 261 /// Returns the explicitly set items. GetExplicitItems()262 ListProxy GetExplicitItems() const 263 { 264 return ListProxy(_listEditor, SdfListOpTypeExplicit); 265 } 266 267 /// Returns the items added by this list editor GetAddedItems()268 ListProxy GetAddedItems() const 269 { 270 return ListProxy(_listEditor, SdfListOpTypeAdded); 271 } 272 273 /// Returns the items prepended by this list editor GetPrependedItems()274 ListProxy GetPrependedItems() const 275 { 276 return ListProxy(_listEditor, SdfListOpTypePrepended); 277 } 278 279 /// Returns the items appended by this list editor GetAppendedItems()280 ListProxy GetAppendedItems() const 281 { 282 return ListProxy(_listEditor, SdfListOpTypeAppended); 283 } 284 285 /// Returns the items deleted by this list editor GetDeletedItems()286 ListProxy GetDeletedItems() const 287 { 288 return ListProxy(_listEditor, SdfListOpTypeDeleted); 289 } 290 291 /// Returns the items reordered by this list editor GetOrderedItems()292 ListProxy GetOrderedItems() const 293 { 294 return ListProxy(_listEditor, SdfListOpTypeOrdered); 295 } 296 297 /// Returns the added or explicitly set items. GetAddedOrExplicitItems()298 value_vector_type GetAddedOrExplicitItems() const 299 { 300 value_vector_type result; 301 if (_Validate()) { 302 _listEditor->ApplyEditsToList(&result); 303 } 304 return result; 305 } 306 Add(const value_type & value)307 void Add(const value_type& value) 308 { 309 if (_Validate()) { 310 if (!_listEditor->IsOrderedOnly()) { 311 if (_listEditor->IsExplicit()) { 312 _AddOrReplace(SdfListOpTypeExplicit, value); 313 } 314 else { 315 GetDeletedItems().Remove(value); 316 _AddOrReplace(SdfListOpTypeAdded, value); 317 } 318 } 319 } 320 } 321 Prepend(const value_type & value)322 void Prepend(const value_type& value) 323 { 324 if (_Validate()) { 325 if (!_listEditor->IsOrderedOnly()) { 326 if (_listEditor->IsExplicit()) { 327 _Prepend(SdfListOpTypeExplicit, value); 328 } 329 else { 330 GetDeletedItems().Remove(value); 331 _Prepend(SdfListOpTypePrepended, value); 332 } 333 } 334 } 335 } 336 Append(const value_type & value)337 void Append(const value_type& value) 338 { 339 if (_Validate()) { 340 if (!_listEditor->IsOrderedOnly()) { 341 if (_listEditor->IsExplicit()) { 342 _Append(SdfListOpTypeExplicit, value); 343 } 344 else { 345 GetDeletedItems().Remove(value); 346 _Append(SdfListOpTypeAppended, value); 347 } 348 } 349 } 350 } 351 Remove(const value_type & value)352 void Remove(const value_type& value) 353 { 354 if (_Validate()) { 355 if (_listEditor->IsExplicit()) { 356 GetExplicitItems().Remove(value); 357 } 358 else if (!_listEditor->IsOrderedOnly()) { 359 GetAddedItems().Remove(value); 360 GetPrependedItems().Remove(value); 361 GetAppendedItems().Remove(value); 362 _AddIfMissing(SdfListOpTypeDeleted, value); 363 } 364 } 365 } 366 Erase(const value_type & value)367 void Erase(const value_type& value) 368 { 369 if (_Validate()) { 370 if (!_listEditor->IsOrderedOnly()) { 371 if (_listEditor->IsExplicit()) { 372 GetExplicitItems().Remove(value); 373 } 374 else { 375 GetAddedItems().Remove(value); 376 GetPrependedItems().Remove(value); 377 GetAppendedItems().Remove(value); 378 } 379 } 380 } 381 } 382 383 /// Explicit bool conversion operator. A ListEditorProxy object 384 /// converts to \c true iff the list editor is valid, converts to \c false 385 /// otherwise. 386 explicit operator bool() const 387 { 388 return _listEditor && _listEditor->IsValid(); 389 } 390 391 private: _Validate()392 bool _Validate() 393 { 394 if (!_listEditor) { 395 return false; 396 } 397 398 if (IsExpired()) { 399 TF_CODING_ERROR("Accessing expired list editor"); 400 return false; 401 } 402 return true; 403 } 404 _Validate()405 bool _Validate() const 406 { 407 if (!_listEditor) { 408 return false; 409 } 410 411 if (IsExpired()) { 412 TF_CODING_ERROR("Accessing expired list editor"); 413 return false; 414 } 415 return true; 416 } 417 _AddIfMissing(SdfListOpType op,const value_type & value)418 void _AddIfMissing(SdfListOpType op, const value_type& value) 419 { 420 ListProxy proxy(_listEditor, op); 421 size_t index = proxy.Find(value); 422 if (index == size_t(-1)) { 423 proxy.push_back(value); 424 } 425 } 426 _AddOrReplace(SdfListOpType op,const value_type & value)427 void _AddOrReplace(SdfListOpType op, const value_type& value) 428 { 429 ListProxy proxy(_listEditor, op); 430 size_t index = proxy.Find(value); 431 if (index == size_t(-1)) { 432 proxy.push_back(value); 433 } 434 else if (value != static_cast<value_type>(proxy[index])) { 435 proxy[index] = value; 436 } 437 } 438 _Prepend(SdfListOpType op,const value_type & value)439 void _Prepend(SdfListOpType op, const value_type& value) 440 { 441 ListProxy proxy(_listEditor, op); 442 size_t index = proxy.Find(value); 443 if (index != 0) { 444 if (index != size_t(-1)) { 445 proxy.Erase(index); 446 } 447 proxy.insert(proxy.begin(), value); 448 } 449 } 450 _Append(SdfListOpType op,const value_type & value)451 void _Append(SdfListOpType op, const value_type& value) 452 { 453 ListProxy proxy(_listEditor, op); 454 size_t index = proxy.Find(value); 455 if (proxy.empty() || (index != proxy.size()-1)) { 456 if (index != size_t(-1)) { 457 proxy.Erase(index); 458 } 459 proxy.push_back(value); 460 } 461 } 462 463 private: 464 boost::shared_ptr<Sdf_ListEditor<TypePolicy> > _listEditor; 465 466 friend class Sdf_ListEditorProxyAccess; 467 template <class T> friend class SdfPyWrapListEditorProxy; 468 }; 469 470 // Cannot get from a VtValue except as the correct type. 471 template <class TP> 472 struct Vt_DefaultValueFactory<SdfListEditorProxy<TP> > { 473 static Vt_DefaultValueHolder Invoke() = delete; 474 }; 475 476 PXR_NAMESPACE_CLOSE_SCOPE 477 478 #endif // PXR_USD_SDF_LIST_EDITOR_PROXY_H 479