-
Notifications
You must be signed in to change notification settings - Fork 25
/
TileDBObject.R
219 lines (195 loc) · 7.39 KB
/
TileDBObject.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
#' TileDB Object Base Class
#'
#' @description
#' Base class to implement shared functionality across the TileDBArray and
#' TileDBGroup classes. (lifecycle: maturing)
#' @export
TileDBObject <- R6::R6Class(
classname = "TileDBObject",
public = list(
#' @description Create a new TileDB object. (lifecycle: maturing)
#' @param uri URI for the TileDB object
#' @param platform_config Optional platform configuration
#' @param tiledbsoma_ctx Optional SOMATileDBContext
#' @param tiledb_timestamp Optional Datetime (POSIXct) with TileDB timestamp
#' @param internal_use_only Character value to signal this is a 'permitted' call,
#' as `new()` is considered internal and should not be called directly.
initialize = function(uri, platform_config = NULL, tiledbsoma_ctx = NULL,
tiledb_timestamp = NULL, internal_use_only = NULL) {
if (is.null(internal_use_only) || internal_use_only != "allowed_use") {
stop(paste("Use of the new() method is for internal use only. Consider using a",
"factory method as e.g. 'SOMADataFrameOpen()'."), call. = FALSE)
}
if (missing(uri)) stop("Must specify a `uri`", call. = FALSE)
private$tiledb_uri <- TileDBURI$new(uri)
# Set platform config
platform_config <- platform_config %||% PlatformConfig$new()
if (!inherits(platform_config, 'PlatformConfig')) {
stop("'platform_config' must be a PlatformConfig object", call. = FALSE)
}
private$.tiledb_platform_config <- platform_config
# Set context
tiledbsoma_ctx <- tiledbsoma_ctx %||% SOMATileDBContext$new()
if (!inherits(x = tiledbsoma_ctx, what = 'SOMATileDBContext')) {
stop("'tiledbsoma_ctx' must be a SOMATileDBContext object", call. = FALSE)
}
private$.tiledbsoma_ctx <- tiledbsoma_ctx
private$.tiledb_ctx <- self$tiledbsoma_ctx$context()
if (!is.null(tiledb_timestamp)) {
stopifnot("'tiledb_timestamp' must be a POSIXct datetime object" = inherits(tiledb_timestamp, "POSIXct"))
private$tiledb_timestamp <- tiledb_timestamp
}
spdl::debug("[TileDBObject] initialize {} with '{}'", self$class(), self$uri)
},
#' @description Print the name of the R6 class.
class = function() {
class(self)[1]
},
# The create/open/close are necessarily specific to TileDBArray/TileDBGroup.
# This is a bit of re-use at the TileDBObject level.
#' @description Determine if the object is open for reading or writing
#'
#' @return \code{TRUE} if the object is open, otherwise \code{FALSE}
#'
is_open = function() {
return(self$mode() != 'CLOSED')
},
# TODO: make this an active
#' @description Get the mode of the object
#'
#' @return If the object is closed, returns \dQuote{\code{CLOSED}};
#' otherwise returns the mode (eg. \dQuote{\code{READ}}) of the object
#'
mode = function() {
if (is.null(private$.mode)) {
"CLOSED"
} else {
private$.mode
}
},
#' @description Close and reopen the TileDB object in a new mode
#'
#' @param mode New mode to open the object in; choose from:
#' \itemize{
#' \item \dQuote{\code{READ}}
#' \item \dQuote{\code{WRITE}}
#' }
#'
#' @return Invisibly returns \code{self} opened in \code{mode}
#'
reopen = function(mode) {
mode <- match.arg(mode, choices = c('READ', 'WRITE'))
self$close()
private$tiledb_timestamp <- NULL
self$open(mode, internal_use_only = 'allowed_use')
return(invisible(self))
},
#' @description Print-friendly representation of the object.
print = function() {
cat(glue::glue("<{self$class()}>"), sep = "\n")
cat(" uri:", self$uri, "\n")
},
#' @description Check if the object exists. (lifecycle: maturing)
#' @return `TRUE`` if the object exists, `FALSE` otherwise.
exists = function() {
if (self$class() == "TileDBObject") {
expected_type <- c("ARRAY", "GROUP")
} else if (inherits(self, "TileDBArray")) {
expected_type <- "ARRAY"
} else if (inherits(self, "TileDBGroup")) {
expected_type <- "GROUP"
} else {
stop("Unknown object type", call. = FALSE)
}
get_tiledb_object_type(self$uri, ctx = soma_context()) %in% expected_type
}
),
active = list(
#' @field platform_config Platform configuration
platform_config = function(value) {
if (!missing(x = value)) {
stop("'platform_config' is a read-only field", call. = FALSE)
}
return(private$.tiledb_platform_config)
},
#' @field tiledbsoma_ctx SOMATileDBContext
tiledbsoma_ctx = function(value) {
if (!missing(x = value)) {
stop("'tiledbsoma_ctx' is a read-only field", call. = FALSE)
}
return(private$.tiledbsoma_ctx)
},
#' @field uri
#' The URI of the TileDB object.
uri = function(value) {
if (missing(value)) return(private$tiledb_uri$uri)
stop(sprintf("'%s' is a read-only field.", "uri"), call. = FALSE)
}
),
private = list(
# Pro tip: in R6 we can't set these to anything other than NULL here, even if we want to. If
# you want them defaulted to anything other than NULL, leave them NULL here and set the defaults
# in the constructor.
# Set by TileDBArray and TileDBGroup, as stateful handles have incompatible semantics
# we can't completely abstract here in this parent class
#
# Semantics:
# * "READ" when opened for read
# * "WRITE" when opened for write
# * NULL when never opened, or when closed.
# * In particular, an is-open predicate can be reliably implemented by
# checking if .mode is non-null.
.mode = NULL,
# @description Contains TileDBURI object
tiledb_uri = NULL,
# Internal platform config
.tiledb_platform_config = NULL,
# Opener-supplied POSIXct timestamp, if any. TileDBArray and TileDBGroup are each responsible
# for making this effective, since the methods differ slightly.
tiledb_timestamp = NULL,
# Internal context
.tiledbsoma_ctx = NULL,
.tiledb_ctx = NULL,
.read_only_error = function(field) {
stop("Field ", sQuote(field), " is read-only", call. = FALSE)
},
is_open_for_read = function() {
# Pro-tip: it's not enough to check $private.mode != "READ", since logical(0) isn't
# the same as FALSE
if (is.null(private$.mode)) {
FALSE
} else if (private$.mode != "READ") {
FALSE
} else {
TRUE
}
},
is_open_for_write = function() {
if (is.null(private$.mode)) {
FALSE
} else if (private$.mode != "WRITE") {
FALSE
} else {
TRUE
}
},
# Per the spec, invoking user-level read requires open for read mode.
check_open_for_read = function() {
if (!private$is_open_for_read()) {
stop(paste("Item must be open for read:", self$uri))
}
},
# Per the spec, invoking user-level write requires open for write mode.
check_open_for_write = function() {
if (!private$is_open_for_write()) {
stop(paste("Item must be open for write.", self$uri))
}
},
# Per the spec, invoking user-level get-metadata requires open for read mode or write mode.
check_open_for_read_or_write = function() {
if (!self$is_open()) {
stop(paste("Item must be open for read or write.", self$uri))
}
}
)
)