forked from RConsortium/S7
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconstructor.R
122 lines (102 loc) · 3.67 KB
/
constructor.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
new_constructor <- function(parent, properties,
envir = asNamespace("S7"), package = NULL) {
properties <- as_properties(properties)
arg_info <- constructor_args(parent, properties, envir, package)
self_args <- as_names(names(arg_info$self), named = TRUE)
if (identical(parent, S7_object) || (is_class(parent) && parent@abstract)) {
new_object_call <-
if (has_S7_symbols(envir, "new_object", "S7_object")) {
bquote(new_object(S7_object(), ..(self_args)), splice = TRUE)
} else {
bquote(S7::new_object(S7::S7_object(), ..(self_args)), splice = TRUE)
}
return(new_function(
args = arg_info$self,
body = as.call(c(quote(`{`),
# Force all promises here so that any errors are signaled from
# the constructor() call instead of the new_object() call.
unname(self_args),
new_object_call
)),
env = envir
))
}
if (is_class(parent)) {
parent_name <- parent@name
parent_fun <- parent
args <- modify_list(arg_info$parent, arg_info$self)
} else if (is_base_class(parent)) {
parent_name <- parent$constructor_name
parent_fun <- parent$constructor
args <- modify_list(arg_info$parent, arg_info$self)
} else if (is_S3_class(parent)) {
parent_name <- paste0("new_", parent$class[[1]])
parent_fun <- parent$constructor
args <- formals(parent$constructor)
args[names(arg_info$self)] <- arg_info$self
} else {
# user facing error in S7_class()
stop("Unsupported `parent` type", call. = FALSE)
}
# ensure default value for `...` is empty
if ("..." %in% names(args)) {
args[names(args) == "..."] <- list(quote(expr = ))
}
parent_args <- as_names(names(arg_info$parent), named = TRUE)
names(parent_args)[names(parent_args) == "..."] <- ""
parent_call <- new_call(parent_name, parent_args)
body <- new_call(
if (has_S7_symbols(envir, "new_object")) "new_object" else c("S7", "new_object"),
c(parent_call, self_args)
)
env <- new.env(parent = envir)
env[[parent_name]] <- parent_fun
new_function(args, body, env)
}
constructor_args <- function(parent, properties = list(),
envir = asNamespace("S7"), package = NULL) {
parent_args <- formals(class_constructor(parent))
# Remove read-only properties
properties <- properties[!vlapply(properties, prop_is_read_only)]
self_arg_nms <- names2(properties)
if (is_class(parent) && !parent@abstract) {
# Remove any parent properties; can't use parent_args() since the constructor
# might automatically set some properties.
self_arg_nms <- setdiff(self_arg_nms, names2(parent@properties))
}
self_args <- as.pairlist(lapply(
setNames(, self_arg_nms),
function(name) prop_default(properties[[name]], envir, package))
)
list(parent = parent_args,
self = self_args)
}
# helpers -----------------------------------------------------------------
is_property_dynamic <- function(x) is.function(x$getter)
missing_args <- function(names) {
lapply(setNames(, names), function(i) quote(class_missing))
}
new_call <- function(call, args) {
if (is.character(call)) {
call <- switch(length(call),
as.name(call),
as.call(c(quote(`::`), lapply(call, as.name))))
}
as.call(c(list(call), args))
}
as_names <- function(x, named = FALSE) {
if (named) {
names(x) <- x
}
lapply(x, as.name)
}
has_S7_symbols <- function(env, ...) {
env <- topenv(env)
if (identical(env, asNamespace("S7")))
return (TRUE)
if (!isNamespace(env))
return (FALSE)
imports <- getNamespaceImports(env)[["S7"]]
symbols <- c(...) %||% getNamespaceExports("S7")
all(symbols %in% imports)
}