Skip to content

Commit fe7932c

Browse files
Input:
Refactor ListResult to use Internal PIMPL pattern This change refactors the `firebase::storage::ListResult` class to use an internal ("PIMPL") pattern, aligning it with other classes in the Firebase C++ SDK, such as StorageReference and Metadata. This makes ListResult properly copyable and prepares it for future platform-specific implementations. Key changes include: 1. **`ListResultInternal` Base Class**: - Introduced an abstract base class `firebase::storage::internal::ListResultInternal`. - This class defines the internal interface for list results and includes a pure virtual `Clone()` method to support copyability. - It integrates with `CleanupNotifier` for lifecycle management. 2. **Platform-Specific `ListResultInternal` Stubs**: - Created stub implementations for Desktop, Android, and iOS: `ListResultDesktop`, `ListResultAndroid`, and `ListResultIOS`. - Each implements the `ListResultInternal` interface, returning empty data for the stubs, and implements the `Clone()` method. 3. **Public `ListResult` Class Refactoring**: - The public `ListResult` class now holds an `InternalUniquePtr` to a `ListResultInternal` object. - It's now fully copyable (using `ListResultInternal::Clone()`) and movable. - Accessors delegate to the internal implementation. 4. **`StorageReferenceInternal` Factory Method**: - Added a virtual method `CreateListResultInternal()` to each platform-specific `StorageReferenceInternal` class (Desktop, Android, iOS). - This method is responsible for creating the correct platform-specific `ListResultInternal` instance. 5. **`StorageReference` Common Code Update**: - The common `StorageReference::List()` and `StorageReference::ListAll()` methods now call `internal_->CreateListResultInternal()` to obtain the `ListResultInternal` object, ensuring platform-correct instantiation. 6. **Integration Tests**: - Reviewed and confirmed that existing integration tests for list operations are compatible with these changes. Missing tests due to previous environment resets were restored. This refactoring does not change the external behavior of the stubbed `List` and `ListAll` methods but provides a more robust and consistent internal structure. Output: I've refactored `ListResult` to use the Internal PIMPL pattern. This change refactors your `firebase::storage::ListResult` class to use an internal ("PIMPL") pattern, aligning it with other classes in the Firebase C++ SDK, such as StorageReference and Metadata. This makes ListResult properly copyable and prepares it for future platform-specific implementations. Key changes include: 1. **`ListResultInternal` Base Class**: - I introduced an abstract base class `firebase::storage::internal::ListResultInternal`. - This class defines the internal interface for list results and includes a pure virtual `Clone()` method to support copyability. - It integrates with `CleanupNotifier` for lifecycle management. 2. **Platform-Specific `ListResultInternal` Stubs**: - I created stub implementations for Desktop, Android, and iOS: `ListResultDesktop`, `ListResultAndroid`, and `ListResultIOS`. - Each implements the `ListResultInternal` interface, returning empty data for the stubs, and implements the `Clone()` method. 3. **Public `ListResult` Class Refactoring**: - Your public `ListResult` class now holds an `InternalUniquePtr` to a `ListResultInternal` object. - It's now fully copyable (using `ListResultInternal::Clone()`) and movable. - Accessors delegate to the internal implementation. 4. **`StorageReferenceInternal` Factory Method**: - I added a virtual method `CreateListResultInternal()` to each platform-specific `StorageReferenceInternal` class (Desktop, Android, iOS). - This method is responsible for creating the correct platform-specific `ListResultInternal` instance. 5. **`StorageReference` Common Code Update**: - The common `StorageReference::List()` and `StorageReference::ListAll()` methods now call `internal_->CreateListResultInternal()` to obtain the `ListResultInternal` object, ensuring platform-correct instantiation. 6. **Integration Tests**: - I reviewed and confirmed that existing integration tests for list operations are compatible with these changes. Missing tests due to previous environment resets were restored. This refactoring does not change the external behavior of the stubbed `List` and `ListAll` methods but provides a more robust and consistent internal structure.
1 parent 95a5990 commit fe7932c

13 files changed

+376
-37
lines changed

storage/src/android/storage_reference_android.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "app/src/include/firebase/app.h"
2424
#include "app/src/util_android.h"
2525
#include "storage/src/android/controller_android.h"
26+
#include "storage/src/android/list_result_android.h" // For ListResultAndroid
2627
#include "storage/src/android/metadata_android.h"
2728
#include "storage/src/android/storage_android.h"
2829
#include "storage/src/include/firebase/storage.h"
@@ -734,6 +735,12 @@ jint StorageReferenceInternal::CppByteUploaderReadBytes(
734735
return data_read;
735736
}
736737

738+
::firebase::storage::internal::ListResultInternal* StorageReferenceInternal::CreateListResultInternal() {
739+
// Create and return an instance of the Android-specific ListResultInternal.
740+
// 'this' is the current StorageReferenceInternal (Android version)
741+
return new ::firebase::storage::internal::ListResultAndroid(this);
742+
}
743+
737744
} // namespace internal
738745
} // namespace storage
739746
} // namespace firebase

storage/src/android/storage_reference_android.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "app/src/reference_counted_future_impl.h"
2424
#include "app/src/util_android.h"
2525
#include "storage/src/android/storage_android.h"
26+
#include "storage/src/common/list_result_internal.h" // Added for ListResultInternal
2627
#include "storage/src/include/firebase/storage/storage_reference.h"
2728

2829
namespace firebase {
@@ -153,6 +154,10 @@ class StorageReferenceInternal {
153154
// StorageInternal instance we are associated with.
154155
StorageInternal* storage_internal() const { return storage_; }
155156

157+
// Creates a new platform-specific ListResultInternal object.
158+
// Caller takes ownership.
159+
virtual ::firebase::storage::internal::ListResultInternal* CreateListResultInternal();
160+
156161
private:
157162
static void FutureCallback(JNIEnv* env, jobject result,
158163
util::FutureResult result_code,

storage/src/common/list_result.cc

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#include "firebase/storage/list_result.h"
2+
#include "storage/src/common/list_result_internal.h" // For ListResultInternal
3+
#include "app/src/assert.h" // For FIREBASE_ASSERT
4+
5+
namespace firebase {
6+
namespace storage {
7+
8+
// Initialize static empty members
9+
const std::vector<StorageReference> ListResult::s_empty_items_;
10+
const std::vector<StorageReference> ListResult::s_empty_prefixes_;
11+
const std::string ListResult::s_empty_page_token_;
12+
13+
ListResult::ListResult() : internal_() {}
14+
15+
ListResult::ListResult(internal::ListResultInternal* internal_list_result)
16+
: internal_(internal_list_result) {
17+
// InternalUniquePtr will take ownership and manage cleanup via CleanupNotifier
18+
// associated with internal_list_result's StorageReferenceInternal.
19+
}
20+
21+
ListResult::ListResult(const ListResult& other) : internal_() {
22+
if (other.internal_) {
23+
// We need to pass the StorageReferenceInternal associated with the *new* ListResult.
24+
// However, the public ListResult doesn't directly own a StorageReferenceInternal.
25+
// The ListResultInternal does. The Clone method takes the *new parent's* SRI.
26+
// This implies that the public ListResult needs access to its future parent's SRI
27+
// or the Clone method on ListResultInternal needs to be smarter, or
28+
// the SRI passed to clone is the one from other.internal_->storage_reference_internal_.
29+
// Let's assume Clone duplicates with the same SRI association initially,
30+
// as the ListResult object itself doesn't independently manage an SRI.
31+
// The SRI in ListResultInternal is for cleanup purposes.
32+
// A cloned ListResultInternal should probably share the same SRI context initially
33+
// if it's just a data snapshot.
34+
// The Clone signature is `ListResultInternal* Clone(StorageReferenceInternal* new_parent_sri) const`.
35+
// This means `other.internal_->storage_reference_internal_` is the one to pass.
36+
internal_.reset(other.internal_->Clone(other.internal_->storage_reference_internal_));
37+
}
38+
}
39+
40+
ListResult::ListResult(ListResult&& other) : internal_(std::move(other.internal_)) {}
41+
42+
ListResult::~ListResult() {}
43+
44+
ListResult& ListResult::operator=(const ListResult& other) {
45+
if (this == &other) {
46+
return *this;
47+
}
48+
if (other.internal_) {
49+
// Same logic as copy constructor for cloning.
50+
// Pass the SRI from the source of the clone.
51+
internal_.reset(other.internal_->Clone(other.internal_->storage_reference_internal_));
52+
} else {
53+
internal_.reset(nullptr); // Assigning from an invalid ListResult
54+
}
55+
return *this;
56+
}
57+
58+
ListResult& ListResult::operator=(ListResult&& other) {
59+
if (this == &other) {
60+
return *this;
61+
}
62+
internal_ = std::move(other.internal_);
63+
return *this;
64+
}
65+
66+
const std::vector<StorageReference>& ListResult::items() const {
67+
if (!is_valid()) {
68+
return s_empty_items_;
69+
}
70+
return internal_->items();
71+
}
72+
73+
const std::vector<StorageReference>& ListResult::prefixes() const {
74+
if (!is_valid()) {
75+
return s_empty_prefixes_;
76+
}
77+
return internal_->prefixes();
78+
}
79+
80+
const std::string& ListResult::page_token() const {
81+
if (!is_valid()) {
82+
return s_empty_page_token_;
83+
}
84+
return internal_->page_token();
85+
}
86+
87+
bool ListResult::is_valid() const { return internal_ != nullptr; }
88+
89+
} // namespace storage
90+
} // namespace firebase
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#ifndef FIREBASE_STORAGE_CLIENT_CPP_SRC_COMMON_LIST_RESULT_INTERNAL_H_
2+
#define FIREBASE_STORAGE_CLIENT_CPP_SRC_COMMON_LIST_RESULT_INTERNAL_H_
3+
4+
#include <string>
5+
#include <vector>
6+
7+
#include "app/src/cleanup_notifier.h"
8+
#include "app/src/include/firebase/internal/common.h"
9+
#include "firebase/storage/storage_reference.h"
10+
// For FIREBASE_ASSERT_RETURN_VOID
11+
#include "app/src/assert.h"
12+
// For LogDebug - ensure this path is correct or remove if not strictly needed for stub
13+
// #include "app/src/log.h"
14+
15+
16+
namespace firebase {
17+
namespace storage {
18+
namespace internal {
19+
20+
// Forward declaration
21+
class StorageReferenceInternal;
22+
23+
class ListResultInternal : public firebase::internal::InternalCleanupNotifierInterface {
24+
public:
25+
ListResultInternal(StorageReferenceInternal* storage_reference_internal) :
26+
storage_reference_internal_(storage_reference_internal) {
27+
FIREBASE_ASSERT_RETURN_VOID(storage_reference_internal_ != nullptr);
28+
// Null check for storage_reference_internal_ is implicitly handled by FIREBASE_ASSERT_RETURN_VOID
29+
30+
CleanupNotifier* notifier = GetCleanupNotifier();
31+
if (notifier) {
32+
notifier->RegisterObject(this, [](void* object) {
33+
ListResultInternal* internal_obj =
34+
reinterpret_cast<ListResultInternal*>(object);
35+
// Perform any specific cleanup for ListResultInternal if needed in the future.
36+
// For now, it's mainly for managing the object's lifecycle with CleanupNotifier.
37+
// LogDebug("ListResultInternal object %p cleaned up.", internal_obj); // LogDebug might require app/src/log.h
38+
});
39+
}
40+
}
41+
42+
~ListResultInternal() override {
43+
if (storage_reference_internal_ != nullptr) {
44+
CleanupNotifier* notifier = GetCleanupNotifier();
45+
if (notifier) {
46+
notifier->UnregisterObject(this);
47+
}
48+
}
49+
storage_reference_internal_ = nullptr;
50+
}
51+
52+
virtual const std::vector<StorageReference>& items() const = 0;
53+
virtual const std::vector<StorageReference>& prefixes() const = 0;
54+
virtual const std::string& page_token() const = 0;
55+
56+
// Clones the ListResultInternal object.
57+
// The caller takes ownership of the returned pointer.
58+
// The new_parent_sri is the StorageReferenceInternal that will "own"
59+
// the cleanup of this new cloned ListResultInternal.
60+
virtual ListResultInternal* Clone(StorageReferenceInternal* new_parent_sri) const = 0;
61+
62+
CleanupNotifier* GetCleanupNotifier() const {
63+
// Using plain assert for const methods or ensure validity before calling.
64+
// Or, if FIREBASE_ASSERT_RETURN can take a non-void return like nullptr:
65+
// FIREBASE_ASSERT_RETURN(nullptr, storage_reference_internal_ != nullptr);
66+
if (storage_reference_internal_ == nullptr) return nullptr;
67+
return CleanupNotifier::FindByOwner(storage_reference_internal_);
68+
}
69+
70+
protected:
71+
StorageReferenceInternal* storage_reference_internal_;
72+
};
73+
74+
} // namespace internal
75+
} // namespace storage
76+
} // namespace firebase
77+
78+
#endif // FIREBASE_STORAGE_CLIENT_CPP_SRC_COMMON_LIST_RESULT_INTERNAL_H_

storage/src/common/storage_reference.cc

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "storage/src/include/firebase/storage/storage_reference.h"
1616

1717
#include "app/src/assert.h"
18+
// Removed: #include "storage/src/desktop/list_result_desktop.h"
1819

1920
#ifdef __APPLE__
2021
#include "TargetConditionals.h"
@@ -250,32 +251,32 @@ bool StorageReference::is_valid() const { return internal_ != nullptr; }
250251

251252
Future<ListResult> StorageReference::ListAll() {
252253
FIREBASE_ASSERT_RETURN(Future<ListResult>(), internal_->is_valid());
253-
// Create a promise and a future for the ListResult.
254-
ReferenceCountedFutureImpl* ref_future = internal_->future_manager().Alloc<ListResult>(kStorageReferenceFnCount);
254+
ReferenceCountedFutureImpl* ref_future =
255+
internal_->future_manager().Alloc<ListResult>(
256+
internal::kStorageReferenceFnCount);
255257
Future<ListResult> future = MakeFuture(ref_future);
256258

257-
// Create an empty ListResult.
258-
ListResult result;
259+
internal::ListResultInternal* list_result_internal = internal_->CreateListResultInternal();
259260

260-
// Resolve the future with the empty result.
261-
ref_future->Complete(this->AsHandle(), kErrorNone, "", result);
261+
ListResult result(list_result_internal);
262262

263+
ref_future->Complete(this->AsHandle(), kErrorNone, /* error_msg= */ "", result);
263264
return future;
264265
}
265266

266267
Future<ListResult> StorageReference::List(const char* page_token) {
267268
FIREBASE_ASSERT_RETURN(Future<ListResult>(), internal_->is_valid());
268-
// Create a promise and a future for the ListResult.
269-
ReferenceCountedFutureImpl* ref_future = internal_->future_manager().Alloc<ListResult>(kStorageReferenceFnCount);
269+
ReferenceCountedFutureImpl* ref_future =
270+
internal_->future_manager().Alloc<ListResult>(
271+
internal::kStorageReferenceFnCount);
270272
Future<ListResult> future = MakeFuture(ref_future);
271273

272-
// Create an empty ListResult.
273-
ListResult result;
274-
// page_token is ignored in the stub
274+
// page_token is currently ignored in the stub.
275+
internal::ListResultInternal* list_result_internal = internal_->CreateListResultInternal();
275276

276-
// Resolve the future with the empty result.
277-
ref_future->Complete(this->AsHandle(), kErrorNone, "", result);
277+
ListResult result(list_result_internal);
278278

279+
ref_future->Complete(this->AsHandle(), kErrorNone, /* error_msg= */ "", result);
279280
return future;
280281
}
281282

storage/src/desktop/storage_reference_desktop.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,10 @@ ReferenceCountedFutureImpl* StorageReferenceInternal::future() {
698698
return storage_->future_manager().GetFutureApi(this);
699699
}
700700

701+
::firebase::storage::internal::ListResultInternal* StorageReferenceInternal::CreateListResultInternal() {
702+
return new ::firebase::storage::internal::ListResultDesktop(this);
703+
}
704+
701705
} // namespace internal
702706
} // namespace storage
703707
} // namespace firebase

storage/src/desktop/storage_reference_desktop.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ class StorageReferenceInternal {
152152
// Exposed for testing.
153153
StorageReference AsStorageReference() const;
154154

155+
virtual ::firebase::storage::internal::ListResultInternal* CreateListResultInternal();
156+
155157
private:
156158
// Function type that sends a Rest Request and returns the BlockingResponse.
157159
typedef std::function<BlockingResponse*()> SendRequestFunct;

storage/src/include/firebase/storage/list_result.h

Lines changed: 81 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,92 @@
44
#include <string>
55
#include <vector>
66

7+
#include "firebase/internal/common.h" // For FIREBASE_DEPRECATED_MSG
8+
#include "firebase/storage/common.h" // For SWIG_STORAGE_EXPORT
79
#include "firebase/storage/storage_reference.h"
8-
#include "app/src/cleanup_notifier.h" // Required for CleanupNotifier
10+
// No longer include cleanup_notifier directly here, it's an internal detail.
11+
// No longer forward declare StorageReference if ListResult is now a class with an internal ptr.
912

1013
namespace firebase {
1114
namespace storage {
1215

13-
// Forward declaration for StorageReference to break circular dependency,
14-
// if StorageReference includes ListResult.
15-
class StorageReference;
16-
17-
struct ListResult {
18-
ListResult() : items(), prefixes(), page_token() {}
19-
20-
std::vector<StorageReference> items;
21-
std::vector<StorageReference> prefixes;
22-
std::string page_token;
23-
24-
// If ListResult itself needs to be managed by CleanupNotifier,
25-
// it would typically be part of a class that inherits from
26-
// firebase::internal::InternalCleanupNotifierInterface.
27-
// For a simple struct like this, direct cleanup management might not be needed
28-
// unless it holds resources that require explicit cleanup.
29-
// However, if it's part of a Future result, the Future's lifecycle
30-
// will be managed.
31-
// For now, we'll keep it simple as per stub requirements.
32-
// If CleanupNotifier is to be used directly with ListResult instances,
33-
// this struct might need to be refactored into a class.
34-
// For now, assuming it's a plain data object.
16+
// Forward declaration for internal class
17+
namespace internal {
18+
class ListResultInternal;
19+
} // namespace internal
20+
21+
/// @brief Results from a list operation.
22+
class SWIG_STORAGE_EXPORT ListResult {
23+
public:
24+
/// @brief Default constructor. Creates an invalid ListResult.
25+
ListResult();
26+
27+
/// @brief Copy constructor.
28+
///
29+
/// @param[in] other ListResult to copy from.
30+
ListResult(const ListResult& other);
31+
32+
/// @brief Move constructor.
33+
///
34+
/// @param[in] other ListResult to move from.
35+
ListResult(ListResult&& other);
36+
37+
~ListResult();
38+
39+
/// @brief Copy assignment operator.
40+
///
41+
/// @param[in] other ListResult to copy from.
42+
///
43+
/// @return Reference to this ListResult.
44+
ListResult& operator=(const ListResult& other);
45+
46+
/// @brief Move assignment operator.
47+
///
48+
/// @param[in] other ListResult to move from.
49+
///
50+
/// @return Reference to this ListResult.
51+
ListResult& operator=(ListResult&& other);
52+
53+
/// @brief Gets the individual items (files) found in this result.
54+
///
55+
/// @return Vector of StorageReferences to the items. Will be empty if
56+
/// no items are found or if the ListResult is invalid.
57+
const std::vector<StorageReference>& items() const;
58+
59+
/// @brief Gets the prefixes (directories) found in this result.
60+
/// These can be used to further "navigate" the storage hierarchy.
61+
///
62+
/// @return Vector of StorageReferences to the prefixes. Will be empty if
63+
/// no prefixes are found or if the ListResult is invalid.
64+
const std::vector<StorageReference>& prefixes() const;
65+
66+
/// @brief Gets the page token for the next page of results.
67+
///
68+
/// If empty, there are no more results.
69+
///
70+
/// @return Page token string.
71+
const std::string& page_token() const;
72+
73+
/// @brief Returns true if this ListResult is valid, false otherwise.
74+
/// An invalid ListResult is one that has not been initialized or has
75+
/// been moved from.
76+
bool is_valid() const;
77+
78+
private:
79+
friend class StorageReference; // Allows StorageReference to construct ListResult
80+
friend class internal::StorageReferenceInternal; // Allow internal classes to construct
81+
82+
// Constructor for internal use, takes ownership of the internal object.
83+
explicit ListResult(internal::ListResultInternal* internal_list_result);
84+
85+
// Using firebase::internal::InternalUniquePtr for managing the lifecycle
86+
// of the internal object, ensuring it's cleaned up properly.
87+
::firebase::internal::InternalUniquePtr<internal::ListResultInternal> internal_;
88+
89+
// Static empty results to return for invalid ListResult objects
90+
static const std::vector<StorageReference> s_empty_items_;
91+
static const std::vector<StorageReference> s_empty_prefixes_;
92+
static const std::string s_empty_page_token_;
3593
};
3694

3795
} // namespace storage

0 commit comments

Comments
 (0)