1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <cinttypes>
17 
18 #include <core/ecs/intf_entity_manager.h>
19 #include <core/log.h>
20 #include <core/property/intf_property_api.h>
21 
22 #include "ecs_serializer/ecs_clone_util.h"
23 #include "ecs_serializer/intf_entity_collection.h"
24 
25 using namespace BASE_NS;
26 using namespace CORE_NS;
27 
28 ECS_SERIALIZER_BEGIN_NAMESPACE()
29 
30 class EntityCollection : public IEntityCollection, private IEntityCollection::IListener {
31 public:
32     using Ptr = BASE_NS::unique_ptr<EntityCollection, Deleter>;
33 
34     EntityCollection(CORE_NS::IEcs& ecs, BASE_NS::string_view uri, BASE_NS::string_view contextUri);
35 
36     void AddListener(IEntityCollection::IListener& listener) override;
37     void RemoveListener(IEntityCollection::IListener& listener) override;
38 
39     //
40     // From IEntityCollection
41     //
42     CORE_NS::IEcs& GetEcs() const override;
43     BASE_NS::string GetUri() const override;
44     BASE_NS::string GetContextUri() const override;
45 
46     BASE_NS::string GetSrc() const override;
47     void SetSrc(BASE_NS::string_view src) override;
48 
49     BASE_NS::string GetType() const override;
50     void SetType(BASE_NS::string_view type) override;
51 
52     size_t GetEntityCount() const override;
53     CORE_NS::EntityReference GetEntity(size_t collectionIndex) const override;
54     CORE_NS::EntityReference GetEntity(BASE_NS::string_view localContextId) const override;
55     BASE_NS::array_view<const CORE_NS::EntityReference> GetEntities() const override;
56     void AddEntity(CORE_NS::EntityReference entitity) override;
57     void AddEntities(BASE_NS::array_view<const CORE_NS::EntityReference> entities) override;
58     bool RemoveEntity(CORE_NS::EntityReference entitity) override;
59     void RemoveEntities(BASE_NS::array_view<const CORE_NS::EntityReference> entities) override;
60     void SetId(BASE_NS::string_view id, CORE_NS::EntityReference entity) override;
61     BASE_NS::string_view GetId(CORE_NS::Entity entity) const override;
62 
63     size_t GetSubCollectionCount() const override;
64     IEntityCollection* GetSubCollection(size_t index) override;
65     const IEntityCollection* GetSubCollection(size_t index) const override;
66     int32_t GetSubCollectionIndex(BASE_NS::string_view uri) const override;
67     int32_t GetSubCollectionIndexByRoot(CORE_NS::Entity entity) const override;
68     IEntityCollection& AddSubCollection(BASE_NS::string_view uri, BASE_NS::string_view contextUri) override;
69     IEntityCollection& AddSubCollectionClone(IEntityCollection& collection, BASE_NS::string_view uri) override;
70     void RemoveSubCollection(size_t index) override;
71 
72     size_t GetEntityCountRecursive(bool includeDestroyed) const override;
73     void GetEntitiesRecursive(
74         bool includeDestroyed, BASE_NS::vector<CORE_NS::EntityReference>& entitiesOut) const override;
75 
76     bool Contains(CORE_NS::Entity entity) const override;
77     bool IsExternal(CORE_NS::Entity entity) const override;
78     bool isSubCollectionRoot(CORE_NS::Entity entity) const override;
79     CORE_NS::EntityReference GetReference(CORE_NS::Entity entity) const override;
80 
81     void SetActive(bool active) override;
82     bool IsActive() const override;
83 
84     void MarkDestroyed(bool destroyed) override;
85     bool IsMarkedDestroyed() const override;
86 
87     void MarkModified(bool modified) override;
88     void MarkModified(bool modified, bool recursive) override;
89     bool IsMarkedModified() const override;
90 
91     void Clear() override;
92 
93     void CopyContents(IEntityCollection& srcCollection) override;
94 
95     BASE_NS::vector<CORE_NS::EntityReference> CopyContentsWithSerialization(IEntityCollection& srcCollection) override;
96     BASE_NS::vector<CORE_NS::EntityReference> CopyContentsWithSerialization(
97         IEntityCollection& srcCollection, BASE_NS::array_view<const CORE_NS::EntityReference> entities) override;
98 
99     bool MarkComponentSerialized(CORE_NS::Entity entity, BASE_NS::Uid component, bool serialize) override;
100     bool MarkAllPropertiesSerialized(CORE_NS::Entity entity, BASE_NS::Uid component) override;
101     bool MarkPropertySerialized(
102         CORE_NS::Entity entity, BASE_NS::Uid component, BASE_NS::string_view propertyPath, bool serialize) override;
103     bool IsPropertySerialized(
104         CORE_NS::Entity entity, BASE_NS::Uid component, BASE_NS::string_view propertyPath) override;
105     const PropertyList* GetSerializedProperties(CORE_NS::Entity entity, BASE_NS::Uid component) const override;
106 
107 protected:
108     void Destroy() override;
109     ~EntityCollection() override;
110 
111 private:
112     // Prevent copying.
113     EntityCollection(const EntityCollection&) = delete;
114     EntityCollection& operator=(const EntityCollection&) = delete;
115 
116     // From IEntityCollection::IListener
117     void ModifiedChanged(IEntityCollection& entityCollection, bool modified) override;
118 
119     void DoGetEntitiesRecursive(bool includeDestroyed, BASE_NS::vector<CORE_NS::EntityReference>& entitiesOut) const;
120 
121     void ClonePrivate(EntityCollection& dst) const;
122     void DoCloneRecursive(EntityCollection& dst) const;
123 
124     // Components to be serialized in an entity.
125     using ComponentMap = BASE_NS::unordered_map<BASE_NS::Uid, PropertyList>;
126     BASE_NS::unordered_map<CORE_NS::Entity, ComponentMap> serializationInfo_;
127 
128     CORE_NS::IEcs& ecs_;
129     BASE_NS::string uri_;
130     BASE_NS::string contextUri_;
131 
132     BASE_NS::string src_ {};
133     BASE_NS::string type_ {};
134 
135     BASE_NS::unordered_map<BASE_NS::string, CORE_NS::EntityReference> namedEntities_;
136     BASE_NS::vector<CORE_NS::EntityReference> entities_;
137 
138     BASE_NS::vector<EntityCollection::Ptr> collections_;
139 
140     bool isActive_ { true };
141     bool isMarkedDestroyed_ { false };
142     bool isMarkedModified_ { false };
143 
144     BASE_NS::vector<IEntityCollection::IListener*> listeners_;
145 };
146 
EntityCollection(IEcs& ecs, string_view uri, string_view contextUri)147 EntityCollection::EntityCollection(IEcs& ecs, string_view uri, string_view contextUri)
148     : ecs_(ecs), uri_(uri), contextUri_(contextUri)
149 {}
150 
AddListener(IEntityCollection::IListener& listener)151 void EntityCollection::AddListener(IEntityCollection::IListener& listener)
152 {
153     BASE_ASSERT(&listener);
154     listeners_.emplace_back(&listener);
155 }
156 
RemoveListener(IEntityCollection::IListener& listener)157 void EntityCollection::RemoveListener(IEntityCollection::IListener& listener)
158 {
159     BASE_ASSERT(&listener);
160     for (size_t i = 0; i < listeners_.size(); ++i) {
161         if (&listener == listeners_[i]) {
162             listeners_.erase(listeners_.begin() + static_cast<int64_t>(i));
163             return;
164         }
165     }
166 
167     // trying to remove a non-existent listener.
168     BASE_ASSERT(true);
169 }
170 
GetEcs() const171 IEcs& EntityCollection::GetEcs() const
172 {
173     return ecs_;
174 }
175 
GetUri() const176 string EntityCollection::GetUri() const
177 {
178     return uri_;
179 }
180 
GetContextUri() const181 string EntityCollection::GetContextUri() const
182 {
183     return contextUri_;
184 }
185 
GetSrc() const186 string EntityCollection::GetSrc() const
187 {
188     return src_;
189 }
190 
SetSrc(string_view src)191 void EntityCollection::SetSrc(string_view src)
192 {
193     src_ = src;
194     MarkModified(true);
195 }
196 
GetType() const197 string EntityCollection::GetType() const
198 {
199     return type_;
200 }
201 
SetType(string_view type)202 void EntityCollection::SetType(string_view type)
203 {
204     type_ = type;
205     MarkModified(true);
206 }
207 
GetEntityCount() const208 size_t EntityCollection::GetEntityCount() const
209 {
210     return entities_.size();
211 }
212 
GetEntity(size_t collectionIndex) const213 EntityReference EntityCollection::GetEntity(size_t collectionIndex) const
214 {
215     BASE_ASSERT(collectionIndex < entities_.size());
216     if (collectionIndex >= entities_.size()) {
217         return EntityReference {};
218     }
219     return entities_[collectionIndex];
220 }
221 
GetEntity(string_view localContextId) const222 EntityReference EntityCollection::GetEntity(string_view localContextId) const
223 {
224     const auto it = namedEntities_.find(localContextId);
225     if (it != namedEntities_.end()) {
226         return it->second;
227     }
228     return EntityReference {};
229 }
230 
GetEntities() const231 array_view<const EntityReference> EntityCollection::GetEntities() const
232 {
233     return entities_;
234 }
235 
AddEntity(EntityReference entity)236 void EntityCollection::AddEntity(EntityReference entity)
237 {
238     AddEntities({ &entity, 1 });
239 }
240 
AddEntities(array_view<const EntityReference> entities)241 void EntityCollection::AddEntities(array_view<const EntityReference> entities)
242 {
243     entities_.reserve(entities_.size() + entities.size());
244     for (auto entity : entities) {
245         if (entity != Entity {}) {
246             entities_.emplace_back(entity);
247         } else {
248             CORE_LOG_W("Trying to add empty entity to a collection '%s'", uri_.c_str());
249         }
250     }
251     MarkModified(true);
252 }
253 
RemoveEntity(EntityReference entity)254 bool EntityCollection::RemoveEntity(EntityReference entity)
255 {
256     for (size_t i = 0; i < entities_.size(); ++i) {
257         if (entities_[i] == entity) {
258             entities_.erase(entities_.begin() + static_cast<int64_t>(i));
259 
260             // Also remove any related id mappings.
261             for (auto it = namedEntities_.begin(); it != namedEntities_.end(); ++it) {
262                 if (it->second == entity) {
263                     namedEntities_.erase(it);
264                     break;
265                 }
266             }
267 
268             // need to remember that and the information about deletion needs to be serialized. Maybe check if
269             // (src_.empty()). However this also needs to work with undo
270 
271             MarkModified(true);
272             return true;
273         }
274     }
275 
276     // Not found. Check the sub-collections.
277     for (auto& collection : collections_) {
278         BASE_ASSERT(collection);
279         if (collection->RemoveEntity(entity)) {
280             MarkModified(true);
281             return true;
282         }
283     }
284 
285     return false;
286 }
287 
RemoveEntities(array_view<const EntityReference> entities)288 void EntityCollection::RemoveEntities(array_view<const EntityReference> entities)
289 {
290     for (auto entity : entities) {
291         RemoveEntity(entity);
292     }
293 }
294 
SetId(string_view id, EntityReference entity)295 void EntityCollection::SetId(string_view id, EntityReference entity)
296 {
297 #if 0
298     CORE_LOG_V("EntityCollection '%s': Set entity id: %" PRIx64 " = '%s'", src_.c_str(), entity.operator Entity().id, string(id).c_str());
299 #endif
300     namedEntities_[id] = entity;
301 }
GetId(Entity entity) const302 string_view EntityCollection::GetId(Entity entity) const
303 {
304     for (auto& it : namedEntities_) {
305         if (it.second == entity) {
306             return it.first;
307         }
308     }
309     return {};
310 }
311 
GetSubCollectionCount() const312 size_t EntityCollection::GetSubCollectionCount() const
313 {
314     return collections_.size();
315 }
316 
GetSubCollection(size_t index)317 IEntityCollection* EntityCollection::GetSubCollection(size_t index)
318 {
319     if (index >= collections_.size()) {
320         return nullptr;
321     }
322     return collections_.at(index).get();
323 }
324 
GetSubCollection(size_t index) const325 const IEntityCollection* EntityCollection::GetSubCollection(size_t index) const
326 {
327     if (index >= collections_.size()) {
328         return nullptr;
329     }
330     return collections_.at(index).get();
331 }
332 
GetSubCollectionIndex(string_view uri) const333 int32_t EntityCollection::GetSubCollectionIndex(string_view uri) const
334 {
335     for (size_t i = 0; i < collections_.size(); ++i) {
336         BASE_ASSERT(collections_[i]);
337         if (collections_[i]->GetUri() == uri) {
338             return static_cast<int32_t>(i);
339         }
340     }
341     return -1;
342 }
343 
GetSubCollectionIndexByRoot(Entity entity) const344 int32_t EntityCollection::GetSubCollectionIndexByRoot(Entity entity) const
345 {
346     if (entity != Entity {}) {
347         for (size_t i = 0; i < collections_.size(); ++i) {
348             BASE_ASSERT(collections_[i]);
349             if (collections_[i]->GetEntity("/") == entity) {
350                 return static_cast<int32_t>(i);
351             }
352         }
353     }
354     return -1;
355 }
356 
AddSubCollection(string_view uri, string_view contextUri)357 IEntityCollection& EntityCollection::AddSubCollection(string_view uri, string_view contextUri)
358 {
359     collections_.emplace_back(EntityCollection::Ptr { new EntityCollection(ecs_, uri, contextUri) });
360 
361     // listen to changes in subcollection
362     collections_.back()->AddListener(*this);
363 
364     MarkModified(true);
365     return *collections_.back();
366 }
367 
AddSubCollectionClone(IEntityCollection& collection, string_view uri)368 IEntityCollection& EntityCollection::AddSubCollectionClone(IEntityCollection& collection, string_view uri)
369 {
370     collections_.emplace_back(EntityCollection::Ptr { new EntityCollection(ecs_, uri, collection.GetContextUri()) });
371     auto& ec = *collections_.back();
372     static_cast<EntityCollection&>(collection).ClonePrivate(ec);
373 
374     // listen to changes in subcollection
375     ec.AddListener(*this);
376 
377     MarkModified(true);
378     return ec;
379 }
380 
RemoveSubCollection(size_t index)381 void EntityCollection::RemoveSubCollection(size_t index)
382 {
383     BASE_ASSERT(index < collections_.size());
384     if (index < collections_.size()) {
385         // stop listening to changes in subcollection
386         auto& ec = collections_.at(index);
387         ec->RemoveListener(*this);
388 
389         collections_.erase(collections_.begin() + static_cast<int64_t>(index));
390         MarkModified(true);
391     }
392 }
393 
394 size_t EntityCollection::GetEntityCountRecursive(bool includeDestroyed) const
395 {
396     if (!includeDestroyed && IsMarkedDestroyed()) {
397         return 0;
398     }
399 
400     auto size = entities_.size();
401     for (const auto& collection : collections_) {
402         BASE_ASSERT(collection);
403         size += collection->GetEntityCountRecursive(includeDestroyed);
404     }
405     return size;
406 }
407 
408 void EntityCollection::GetEntitiesRecursive(bool includeDestroyed, vector<EntityReference>& entitiesOut) const
409 {
410     // NOTE: Cloning depends on ordering of entitiesOut.
411     entitiesOut.reserve(entitiesOut.size() + GetEntityCountRecursive(includeDestroyed));
412     DoGetEntitiesRecursive(includeDestroyed, entitiesOut);
413 }
414 
415 void EntityCollection::DoGetEntitiesRecursive(bool includeDestroyed, vector<EntityReference>& entitiesOut) const
416 {
417     if (!includeDestroyed && IsMarkedDestroyed()) {
418         return;
419     }
420 
421     entitiesOut.insert(entitiesOut.end(), entities_.begin(), entities_.end());
422     for (const auto& collection : collections_) {
423         BASE_ASSERT(collection);
424         collection->DoGetEntitiesRecursive(includeDestroyed, entitiesOut);
425     }
426 }
427 
428 bool EntityCollection::Contains(Entity entity) const
429 {
430     for (auto it : entities_) {
431         if (it == entity) {
432             return true;
433         }
434     }
435     for (const auto& collection : collections_) {
436         if (!collection->IsMarkedDestroyed()) {
437             if (collection->Contains(entity)) {
438                 return true;
439             }
440         }
441     }
442     return false;
443 }
444 
445 bool EntityCollection::IsExternal(Entity entity) const
446 {
447     for (auto it : entities_) {
448         if (it == entity) {
449             return false;
450         }
451     }
452     return true;
453 }
454 
455 bool EntityCollection::isSubCollectionRoot(Entity entity) const
456 {
457     if (entity == Entity {}) {
458         return false;
459     }
460 
461     for (const auto& collection : collections_) {
462         if (collection->GetEntity("/") == entity) {
463             return true;
464         }
465     }
466 
467     return false;
468 }
469 
470 CORE_NS::EntityReference EntityCollection::GetReference(CORE_NS::Entity entity) const
471 {
472     if (Contains(entity)) {
473         auto ref = GetEcs().GetEntityManager().GetReferenceCounted(entity);
474 
475         // Check that this entity was reference counted already (it should be part of the collection).
476         CORE_ASSERT(ref.GetRefCount() > 1);
477 
478         return ref;
479     }
480     return {};
481 }
482 
483 void EntityCollection::SetActive(bool active)
484 {
485     isActive_ = active;
486     const bool effectivelyActive = isActive_ && !isMarkedDestroyed_;
487 
488     auto& em = ecs_.GetEntityManager();
489     for (auto& entity : entities_) {
490         em.SetActive(entity, effectivelyActive);
491     }
492 
493     for (auto& collection : collections_) {
494         BASE_ASSERT(collection);
495         collection->SetActive(active);
496     }
497 }
498 
499 bool EntityCollection::IsActive() const
500 {
501     return isActive_;
502 }
503 
504 void EntityCollection::MarkDestroyed(bool destroyed)
505 {
506     MarkModified(true);
507     isMarkedDestroyed_ = destroyed;
508     const bool effectivelyActive = isActive_ && !isMarkedDestroyed_;
509 
510     // Change the active state of entities without changing the active state of the collection itself.
511     auto& em = ecs_.GetEntityManager();
512     for (auto& entity : entities_) {
513         em.SetActive(entity, effectivelyActive);
514     }
515 
516     for (auto& collection : collections_) {
517         BASE_ASSERT(collection);
518         collection->MarkDestroyed(destroyed);
519     }
520 }
521 
522 bool EntityCollection::IsMarkedDestroyed() const
523 {
524     return isMarkedDestroyed_;
525 }
526 
527 void EntityCollection::MarkModified(bool modified)
528 {
529     if (isMarkedModified_ != modified) {
530         if (uri_ == "project://assets/prefabs/boxes.prefab") {
531             CORE_LOG_D("Modified uri=%s, src=%s, active=%i, address=%p, modified=%i", uri_.c_str(), src_.c_str(),
532                 isActive_, (void*)this, modified);
533         }
534 
535         isMarkedModified_ = modified;
536         for (auto* l : listeners_) {
537             l->ModifiedChanged(*this, modified);
538         }
539     }
540 }
541 
542 void EntityCollection::MarkModified(bool modified, bool recursive)
543 {
544     if (recursive && !collections_.empty()) {
545         for (auto& c : collections_) {
546             c->MarkModified(modified, true);
547         }
548     }
549     MarkModified(modified);
550 }
551 
552 bool EntityCollection::IsMarkedModified() const
553 {
554     return isMarkedModified_;
555 }
556 
557 void EntityCollection::Clear()
558 {
559     serializationInfo_.clear();
560     namedEntities_.clear();
561     entities_.clear();
562     collections_.clear();
563     MarkModified(true);
564 }
565 
566 void EntityCollection::CopyContents(IEntityCollection& srcCollection)
567 {
568     static_cast<EntityCollection&>(srcCollection).ClonePrivate(*this);
569     MarkModified(true);
570 }
571 
572 namespace {
573 void CopyFrom(IEntityCollection& srcCollection, array_view<const EntityReference> srcEntities,
574     IEntityCollection& dstCollection)
575 {
576     BASE_NS::unordered_map<IEntityCollection*, bool> copiedCollections;
577 
578     for (auto& srcEntity : srcEntities) {
579         auto index = srcCollection.GetSubCollectionIndexByRoot(srcEntity);
580         if (index >= 0) {
581             auto* col = srcCollection.GetSubCollection(static_cast<size_t>(index));
582             auto& copy = dstCollection.AddSubCollection(col->GetUri(), col->GetContextUri());
583             copy.SetSrc(col->GetSrc());
584             vector<EntityReference> allEntities;
585             col->GetEntitiesRecursive(false, allEntities);
586             CopyFrom(*col, allEntities, copy, oldToNew, clonedOut, true);
587             copiedCollections[col] = true;
588         } else if (!srcCollection.IsExternal(srcEntity)) {
589             auto dstEntity = CloneEntityReference(srcCollection.GetEcs(), srcEntity, dstCollection.GetEcs());
590             clonedOut.emplace_back(dstEntity);
591             oldToNew[srcEntity] = dstEntity;
592             dstCollection.AddEntity(dstEntity);
593             auto id = srcCollection.GetId(srcEntity);
594             if (!id.empty()) {
595                 dstCollection.SetId(id, dstEntity);
596             }
597         }
598     }
599 
600     if (copyCollections) {
601         auto subCollectionCount = srcCollection.GetSubCollectionCount();
602         for (size_t subIndex = 0; subIndex < subCollectionCount; subIndex++) {
603             auto* subCollection = srcCollection.GetSubCollection(subIndex);
604             if (!copiedCollections.contains(subCollection)) {
605                 auto& copy = dstCollection.AddSubCollection(subCollection->GetUri(), subCollection->GetContextUri());
606                 copy.SetSrc(subCollection->GetSrc());
607                 vector<EntityReference> allEntities;
608                 subCollection->GetEntitiesRecursive(false, allEntities);
609                 CopyFrom(*subCollection, allEntities, copy, oldToNew, clonedOut, false);
610             }
611         }
612     }
613 }
614 
615 CORE_NS::Entity GetOldEntity(unordered_map<Entity, Entity>& oldToNew, CORE_NS::Entity newEntity)
616 {
617     for (auto& e : oldToNew) {
618         if (e.second == newEntity) {
619             return e.first;
620         }
621     }
622     return {};
623 }
624 
625 void CopySerializationInfo(IEntityCollection& srcCollection, IEntityCollection& dstCollection,
626     unordered_map<Entity, Entity>& oldToNew, vector<EntityReference>& entitiesOut)
627 {
628     // Copy all serialization info for copied entites from their original entities
629     // Note that the root collections contain all the serialization info.
630     auto srcEntity = GetOldEntity(oldToNew, entityOut);
631     if (CORE_NS::EntityUtil::IsValid(srcEntity)) {
632         auto* properties = srcCollection.GetSerializedProperties(srcEntity, cm->GetUid());
633         if (properties) {
634             dstCollection.MarkComponentSerialized(entityOut, cm->GetUid(), true);
635             for (auto property : *properties) {
636                 dstCollection.MarkPropertySerialized(entityOut, cm->GetUid(), property, true);
637             }
638         }
639     }
640 }
641 } // namespace
642 
643 vector<EntityReference> EntityCollection::CopyContentsWithSerialization(
644     IEntityCollection& srcCollection, array_view<const EntityReference> entities)
645 {
646     unordered_map<Entity, Entity> oldToNew;
647 
648     vector<EntityReference> entitiesOut;
649     entitiesOut.reserve(entities.size());
650     CopyFrom(srcCollection, entities, *this, oldToNew, entitiesOut, false);
651     CopySerializationInfo(srcCollection, *this, oldToNew, entitiesOut);
652 
653     for (auto& entity : entitiesOut) {
654         RewriteEntityReferences(GetEcs(), entity, oldToNew);
655     }
656     return entitiesOut;
657 }
658 
659 vector<EntityReference> EntityCollection::CopyContentsWithSerialization(IEntityCollection& srcCollection)
660 {
661     vector<EntityReference> allEntities;
662     srcCollection.GetEntitiesRecursive(false, allEntities);
663     return CopyContentsWithSerialization(srcCollection, allEntities);
664 }
665 
666 void EntityCollection::ClonePrivate(EntityCollection& dst) const
667 {
668     // Clone all collections recursively.
669     DoCloneRecursive(dst);
670 
671     //
672     // Remap entity properties that are pointing to the src entities to point to cloned ones.
673     //
674     unordered_map<Entity, Entity> oldToNew;
675 
676     vector<EntityReference> sourceEntities;
677     GetEntitiesRecursive(false, sourceEntities);
678     vector<EntityReference> clonedEntities;
679     dst.GetEntitiesRecursive(false, clonedEntities);
680 
681     // NOTE: Assuming the order in GetEntitiesRecursive is consistent.
682     BASE_ASSERT(sourceEntities.size() == clonedEntities.size());
683     const auto entityCount = sourceEntities.size();
684     for (size_t i = 0; i < entityCount; ++i) {
685         oldToNew[sourceEntities[i]] = clonedEntities[i];
686     }
687     for (auto& entity : clonedEntities) {
688         RewriteEntityReferences(dst.GetEcs(), entity, oldToNew);
689     }
690 }
691 
692 void EntityCollection::DoCloneRecursive(EntityCollection& dst) const
693 {
694     // Clone entities.
695     dst.entities_ = CloneEntityReferences(ecs_, { entities_.data(), entities_.size() }, dst.GetEcs());
696 
697     // Create id mapping but reference cloned entities instead of the original
698     BASE_ASSERT(entities_.size() == dst.entities_.size());
699     dst.namedEntities_.clear();
700     dst.namedEntities_.reserve(namedEntities_.size());
701     const auto entityCount = entities_.size();
702     for (const auto& it : namedEntities_) {
703         for (size_t i = 0; i < entityCount; ++i) {
704             if (it.second == entities_[i]) {
705                 dst.SetId(it.first, dst.entities_[i]);
706                 break;
707             }
708         }
709     }
710 
711     // Recurse.
712     dst.collections_.reserve(collections_.size());
713     for (auto& collection : collections_) {
714         BASE_ASSERT(collection);
715 
716         if (!collection->IsMarkedDestroyed()) {
717             dst.collections_.emplace_back(EntityCollection::Ptr {
718                 new EntityCollection(dst.GetEcs(), collection->GetUri(), collection->GetContextUri()) });
719             auto& clonedChild = *dst.collections_.back();
720             collection->DoCloneRecursive(clonedChild);
721         }
722     }
723 }
724 
725 namespace {
726 
727 bool SetPropertyDefined(EntityCollection::PropertyList& pl, string_view propertyPath)
728 {
729     for (const auto& prop : pl) {
730         if (prop == propertyPath) {
731             // Already marked as a property defined by this node.
732             return false;
733         }
734 
735         // check if the property we are trying to set is a sub-property of an already defined property
736         auto len1 = prop.length();
737         auto len2 = propertyPath.length();
738         if (len2 > len1) {
739             auto view1 = prop.substr(0, len1);
740             auto view2 = propertyPath.substr(0, len1);
741             if (view1 == view2) {
742                 // already defined in a higher level, so no need to define this sub-property
743                 return false;
744             }
745         }
746     }
747     pl.push_back(string(propertyPath));
748     return true;
749 }
750 bool SetPropertyUndefined(EntityCollection::PropertyList& pl, string_view propertyPath)
751 {
752     for (size_t i = 0; i < pl.size(); ++i) {
753         if (pl[i] == propertyPath) {
754             pl.erase(pl.begin() + static_cast<int64_t>(i));
755             return true;
756         }
757     }
758     return false;
759 }
760 
761 } // namespace
762 
763 bool EntityCollection::MarkComponentSerialized(Entity entity, Uid component, bool serialize)
764 {
765     bool changed = false;
766     const auto entityInfo = serializationInfo_.find(entity);
767     const bool entityFound = (entityInfo != serializationInfo_.end());
768     if (serialize) {
769         if (!entityFound) {
770             serializationInfo_[entity][component] = {};
771             changed = true;
772         } else {
773             const auto componentInfo = entityInfo->second.find(component);
774             const bool componentFound = (componentInfo != entityInfo->second.end());
775             if (!componentFound) {
776                 entityInfo->second[component] = {};
777                 changed = true;
778             }
779         }
780     } else {
781         if (entityFound) {
782             entityInfo->second.erase(component);
783             changed = true;
784         }
785     }
786 
787     if (changed) {
788         MarkModified(true);
789     }
790 
791     return changed;
792 }
793 
794 bool EntityCollection::MarkAllPropertiesSerialized(Entity entity, Uid component)
795 {
796     bool changed = false;
797 
798     auto cm = GetEcs().GetComponentManager(component);
799     if (!cm) {
800         CORE_LOG_W("Set modified: Unrecognized component");
801         return false;
802     }
803 
804     auto info = serializationInfo_.find(entity);
805     if (info == serializationInfo_.end()) {
806         serializationInfo_[entity] = {};
807         info = serializationInfo_.find(entity);
808         changed = true;
809     }
810 
811     const auto& propertyApi = cm->GetPropertyApi();
812     const auto propertyCount = propertyApi.PropertyCount();
813     for (size_t i = 0; i < propertyCount; ++i) {
814         auto* property = propertyApi.MetaData(i);
815         changed = changed | SetPropertyDefined(info->second[component], property->name);
816     }
817 
818     if (changed) {
819         MarkModified(true);
820     }
821 
822     return changed;
823 }
824 
825 bool EntityCollection::MarkPropertySerialized(Entity entity, Uid component, string_view propertyPath, bool serialize)
826 {
827     bool changed = false;
828 
829     auto* cm = GetEcs().GetComponentManager(component);
830     if (cm) {
831         auto info = serializationInfo_.find(entity);
832         if (serialize) {
833             if (info == serializationInfo_.end()) {
834                 serializationInfo_[entity][component] = {};
835                 info = serializationInfo_.find(entity);
836                 changed = true;
837             }
838             changed = changed | SetPropertyDefined(info->second[component], propertyPath);
839         } else {
840             if (info != serializationInfo_.end()) {
841                 changed = changed | SetPropertyUndefined(info->second[component], propertyPath);
842             }
843         }
844     }
845 
846     if (changed) {
847         MarkModified(true);
848     }
849 
850     return changed;
851 }
852 
853 const IEntityCollection::PropertyList* EntityCollection::GetSerializedProperties(Entity entity, Uid component) const
854 {
855     const auto info = serializationInfo_.find(entity);
856     if (info != serializationInfo_.end()) {
857         const auto props = info->second.find(component);
858         if (props != info->second.end()) {
859             return &props->second;
860         }
861     }
862     return nullptr;
863 }
864 
865 bool EntityCollection::IsPropertySerialized(Entity entity, Uid component, string_view propertyPath)
866 {
867     auto info = serializationInfo_.find(entity);
868     if (info == serializationInfo_.end()) {
869         return false;
870     } else {
871         const auto props = info->second.find(component);
872         if (props == info->second.end()) {
873             return false;
874         } else {
875             for (auto& prop : props->second) {
876                 if (prop == propertyPath) {
877                     return true;
878                 }
879             }
880         }
881     }
882     return false;
883 }
884 
885 void EntityCollection::Destroy()
886 {
887     delete this;
888 }
889 
890 EntityCollection::~EntityCollection()
891 {
892     Clear();
893 }
894 
895 void EntityCollection::ModifiedChanged(IEntityCollection& entityCollection, bool modified)
896 {
897     CORE_UNUSED(entityCollection);
898 
899     // subcollection changed, propagate modified status
900     if (modified) {
901         MarkModified(modified);
902     }
903 }
904 
905 IEntityCollection::Ptr CreateEntityCollection(IEcs& ecs, string_view uri, string_view contextUri)
906 {
907     return EntityCollection::Ptr { new EntityCollection(ecs, uri, contextUri) };
908 }
909 
910 ECS_SERIALIZER_END_NAMESPACE()
911