blob: a42ccd3c9ee691f0ef72106aa6c29fbbdd517906 [file] [log] [blame]
# File src/library/methods/R/SClasses.R
# Part of the R package, https://www.R-project.org
#
# Copyright (C) 1995-2016 The R Core Team
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# A copy of the GNU General Public License is available at
# https://www.R-project.org/Licenses/
setClass <-
## Define Class to be an S4 class.
function(Class, representation = list(), prototype = NULL,
contains = character(), validity = NULL, access = list(),
where = topenv(parent.frame()), version = .newExternalptr(),
sealed = FALSE, package = getPackageName(where),
S3methods = FALSE, slots)
{
oldDef <- getClassDef(Class, where)
if(is(oldDef, "classRepresentation") && oldDef@sealed)
stop(gettextf("%s has a sealed class definition and cannot be redefined",
dQuote(Class)),
domain = NA)
if(!missing(slots)) {
## The modern version consistent with reference classes
## Arguments slots= and contains= are used, representation must not be
if(!missing(representation))
stop("Argument \"representation\" cannot be used if argument \"slots\" is supplied")
properties <- inferProperties(slots, "slot")
classDef <- makeClassRepresentation(Class, properties, contains, prototype, package,
validity, access, version, sealed, where = where)
}
else if(is(representation, "classRepresentation")) {
## supplied a class definition object
classDef <- representation
if(!(missing(prototype) && missing(contains) && missing(validity) && missing(access)
&& missing(version) && missing(package)))
stop("only arguments 'Class' and 'where' can be supplied when argument 'representation' is a 'classRepresentation' object")
if(length(classDef@package) == 0L)
classDef@package <- package # the default
}
else {
## catch the special case of a single class name as the representation
if(is.character(representation) && length(representation) == 1L &&
is.null(names(representation)))
representation <- list(representation)
slots <- nzchar(allNames(representation))
superClasses <- c(as.character(representation[!slots]), contains)
properties <- representation[slots]
classDef <- makeClassRepresentation(Class, properties, superClasses, prototype, package,
validity, access, version, sealed, where = where)
}
superClasses <- names(classDef@contains)
classDef <- completeClassDefinition(Class, classDef, where, doExtends = FALSE)
## uncache an old definition for this package, if one is cached
.uncacheClass(Class, classDef)
if(length(superClasses) > 0L) {
sealed <- classDef@sealed
classDef@sealed <- FALSE # to allow setIs to work anyway; will be reset later
assignClassDef(Class, classDef, where)
badContains <- character()
### FIXME: need to iterate over contains, not superclass to get
### package for getClassDef()
for(class2 in superClasses) {
if(is(try(setIs(Class, class2, classDef = classDef, where = where)), "try-error"))
badContains <- c(badContains, class2)
else { # update class definition
classDef <- getClassDef(Class, where = where)
if(is.null(classDef))
stop(sprintf("internal error: definition of class %s not properly assigned",
dQuote(Class)),
domain = NA)
}
}
if(length(badContains)) {
msg <- paste(.dQ(badContains), collapse = ", ")
if(is(try(removeClass(Class, where)), "try-error"))
stop(gettextf("error in contained classes (%s) for class %s and unable to remove definition from %s",
msg, dQuote(Class),
sQuote(getPackageName(where))),
domain = NA)
if(is.null(oldDef))
stop(gettextf("error in contained classes (%s) for class %s; class definition removed from %s",
msg, dQuote(Class),
sQuote(getPackageName(where))),
domain = NA)
else if(is(try(setClass(Class, oldDef, where=where)), "try-error"))
stop(gettextf("error in contained classes (%s) for class %s and unable to restore previous definition from %s",
msg, dQuote(Class),
sQuote(getPackageName(where))),
domain = NA)
else
stop(gettextf("error in contained classes (%s) for class %s; previous definition restored to %s",
msg, dQuote(Class),
sQuote(getPackageName(where))),
domain = NA)
}
if(length(attr(classDef@contains, "conflicts")) > 0)
.reportSuperclassConflicts(Class, classDef@contains, where)
.checkRequiredGenerics(Class, classDef, where)
if(sealed) {
classDef@sealed <- TRUE
}
}
if(S3methods)
classDef <- .setS3MethodsOn(classDef)
assignClassDef(Class, classDef, where)
invisible(classGeneratorFunction(classDef, where))
}
representation <-
## Representation of a class; that is,
## a list of named slots and unnamed classes to be included in a class
## definition.
function(...)
{
value <- list(...)
## unlike the S-Plus function, this does not form the class representation,
## since set SClass works separately with the slots and extends arguments.
anames <- allNames(value)
for(i in seq_along(value)) {
ei <- value[[i]]
if(!is.character(ei) || length(ei) != 1L)
stop(gettextf("element %d of the representation was not a single character string", i), domain = NA)
}
includes <- as.character(value[!nzchar(anames)])
if(anyDuplicated(includes))
stop(gettextf("duplicate class names among superclasses: %s",
paste(.dQ(includes[duplicated(includes)]),
collapse = ", ")),
domain = NA)
slots <- anames[nzchar(anames)]
if(anyDuplicated(slots)) {
dslots <- slots[duplicated(slots)]
stop(sprintf(ngettext(length(dslots),
"duplicated slot name: %s",
"duplicated slot names: %s"),
paste(sQuote(dslots), collapse="")),
domain = NA)
}
value
}
### the version called prototype is the external interface. But functions with argument
### named prototype in R cannot call the prototype function (until there is a methods namespace
### to allow methods::prototype(...)
prototype <- function(...)
.prototype(...)
.prototype <- function(...) {
props <- list(...)
names <- allNames(props)
data <- !nzchar(names)
dataPart <- any(data)
if(dataPart) {
if(sum(data) > 1)
stop("only one data object (unnamed argument to prototype) allowed")
obj <- unclass(props[[seq_along(data)[data] ]])
props <- props[!data]
names <- names[!data]
}
else
obj <- defaultPrototype()
for(i in seq_along(names))
slot(obj, names[[i]], FALSE) <- props[[i]]
new("classPrototypeDef", object = obj, slots = names, dataPart = dataPart)
}
makeClassRepresentation <-
## Set the Class Definition.
## The formal definition of the class is set according to the arguments.
##
## Users should call setClass instead of this function.
function(name, slots = list(), superClasses = character(), prototype = NULL,
package, validity = NULL, access = list(), version = .newExternalptr(),
sealed = FALSE, virtual = NA, where)
{
if(any(superClasses %in% .AbnormalTypes))
superClasses <- .addAbnormalDataType(superClasses)
if(!is.null(prototype) || length(slots) || length(superClasses)) {
## collect information about slots, create prototype if needed
pp <- reconcilePropertiesAndPrototype(name, slots, prototype, superClasses, where)
slots <- pp$properties
prototype <- pp$prototype
}
contains <- list()
if(nzchar(package))
packageSlot(name) <- package
for(what in superClasses) {
whatClassDef <-
if(is(what, "classRepresentation"))
what
else if(is.null(packageSlot(what)))
getClass(what, where = where)
else
getClass(what)
what <- whatClassDef@className # includes package name as attribute
## Create the SClassExtension objects (will be simple, possibly dataPart).
## The slots are supplied explicitly, since `name' is currently an undefined class
contains[[what]] <- makeExtends(name, what, slots = slots,
classDef2 = whatClassDef, package = package)
}
validity <- .makeValidityMethod(name, validity)
if(is.na(virtual)) {
virtual <- testVirtual(slots, contains, prototype, where)
if(virtual && !is.na(match("VIRTUAL", superClasses)))
contains[["VIRTUAL"]] <- NULL
}
# new() must return an S4 object, except perhaps for basic classes
if(!is.null(prototype) && is.na(match(name, .BasicClasses)))
prototype <- .asS4(prototype)
if(".S3Class" %in% names(slots))
prototype <- .addS3Class(name, prototype, contains, where)
newClassRepresentation(className = name, slots = slots,
contains = contains,
prototype = prototype,
virtual = virtual,
validity = validity,
access = access,
package = package,
versionKey = version,
sealed = sealed)
}
getClassDef <-
## Get the definition of the class supplied as a string.
function(Class, where = topenv(parent.frame()), package = packageSlot(Class),
inherits = TRUE)
{
if(inherits) {
value <- .getClassesFromCache(Class)
if(is.list(value))
value <- .resolveClassList(value, where, package)
} else
value <- NULL
if(is.null(value)) {
cname <- classMetaName(if(length(Class) > 1L)
## S3 class; almost certainly has no packageSlot,
## but we'll continue anyway
Class[[1L]] else Class)
## a string with a package slot strongly implies the class definition
## should be in that package.
if(is.character(where)) {
package <- where
}
if(isTRUE(nzchar(package))) {
package <- .requirePackage(package)
}
if (is.environment(package)) {
value <- get0(cname, package, inherits = inherits)
}
if(is.null(value))
value <- get0(cname, where, inherits = inherits) # NULL if not existing
}
value
}
getClass <-
## Get the complete definition of the class supplied as a string,
## including all slots, etc. in classes that this class extends.
function(Class, .Force = FALSE,
where = .classEnv(Class, topenv(parent.frame()), FALSE))
{
value <- getClassDef(Class, where)
if(is.null(value)) {
if(!.Force)
stop(gettextf("%s is not a defined class",
dQuote(Class)),
domain = NA)
else
value <- makeClassRepresentation(Class, package = "base",
virtual = TRUE, where = where)
}
value
}
slot <-
## Get the value of the named slot. This function does exact, not partial, matching of names,
## and the name must be one of the slot names specified in the class's definition.
##
## Because slots are stored as attributes, the validity check is not 100% guaranteed,
## but should be OK if nobody has "cheated" (e.g., by setting other attributes directly).
function(object, name)
.Call(C_R_get_slot, object, name)
"slot<-" <-
## Set the value of the named slot. Must be one of the slots in the class's definition.
function(object, name, check = TRUE, value) {
if(check)
value <- checkSlotAssignment(object, name, value)
.Call(C_R_set_slot, object, name, value)
## currently --> R_do_slot_assign() in ../../../main/attrib.c
}
## ". - hidden" since one should typically rather use is(), extends() etc:
.hasSlot <- function(object, name)
.Call(C_R_hasSlot, object, name)
checkSlotAssignment <- function(obj, name, value)
{
cl <- class(obj)
ClassDef <- getClass(cl) # fails if cl not a defined class (!)
slotClass <- ClassDef@slots[[name]]
if(is.null(slotClass))
stop(gettextf("%s is not a slot in class %s",
sQuote(name), dQuote(cl)),
domain = NA)
valueClass <- class(value)
if(.identC(slotClass, valueClass))
return(value)
## check the value, but be careful to use the definition of the slot's class from
## the class environment of obj (change validObject too if a better way is found)
ok <- possibleExtends(valueClass, slotClass,
ClassDef2 = getClassDef(slotClass, where = .classEnv(ClassDef)))
if(isFALSE(ok))
stop(gettextf("assignment of an object of class %s is not valid for slot %s in an object of class %s; is(value, \"%s\") is not TRUE",
dQuote(valueClass), sQuote(name), dQuote(cl), slotClass),
domain = NA)
else if(isTRUE(ok))
value
else
as(value, slotClass, strict=FALSE, ext = ok)
}
## slightly simpler verison to be called from do_attrgets()
checkAtAssignment <- function(cl, name, valueClass)
{
ClassDef <- getClass(cl) # fails if cl not a defined class (!)
slotClass <- ClassDef@slots[[name]]
if(is.null(slotClass))
stop(gettextf("%s is not a slot in class %s",
sQuote(name), dQuote(cl)),
domain = NA)
if(.identC(slotClass, valueClass))
return(TRUE)
## check the value, but be careful to use the definition of the slot's class from
## the class environment of obj (change validObject too if a better way is found)
ok <- possibleExtends(valueClass, slotClass,
ClassDef2 = getClassDef(slotClass, where = .classEnv(ClassDef)))
if(isFALSE(ok))
stop(gettextf("assignment of an object of class %s is not valid for @%s in an object of class %s; is(value, \"%s\") is not TRUE",
dQuote(valueClass), sQuote(name), dQuote(cl), slotClass),
domain = NA)
TRUE
}
## Now a primitive in base
## "@<-" <-
## function(object, name, value) {
## arg <- substitute(name)
## if(is.name(arg))
## name <- as.character(arg)
## "slot<-"(object, name, TRUE, value)
## }
## The names of the class's slots. The argument is either the name
## of a class, or an object from the relevant class.
## NOTA BENE: .slotNames() shouldn't be needed,
## rather slotNames() should be changed (to work like .slotNames())!
slotNames <- function(x)
if(is(x, "classRepresentation")) names(x@slots) else .slotNames(x)
.slotNames <- function(x)
{
classDef <- getClassDef(
if(!isS4(x) && is.character(x) && length(x) == 1L) x else class(x))
if(is.null(classDef))
character()
else
names(classDef@slots)
}
removeClass <- function(Class, where = topenv(parent.frame())) {
if(missing(where)) {
classEnv <- .classEnv(Class, where, FALSE)
classWhere <- findClass(Class, where = classEnv)
if(length(classWhere) == 0L) {
warning(gettextf("class definition for %s not found (no action taken)",
dQuote(Class)),
domain = NA)
return(FALSE)
}
if(length(classWhere) > 1L)
warning(gettextf(
"class %s has multiple definitions visible; only the first removed",
dQuote(Class)),
domain = NA)
classWhere <- classWhere[[1L]]
}
else classWhere <- where
classDef <- getClassDef(Class, where=classWhere)
if(length(classDef@subclasses)) {
subclasses <- names(classDef@subclasses)
found <- vapply(subclasses, isClass, NA, where = where, USE.NAMES=TRUE)
for(what in subclasses[found])
.removeSuperClass(what, Class)
}
.removeSuperclassBackRefs(Class, classDef, classWhere)
.uncacheClass(Class, classDef)
.undefineMethod("initialize", Class, classWhere)
what <- classMetaName(Class)
rm(list=what, pos=classWhere)
TRUE
}
isClass <-
## Is this a formally defined class?
function(Class, formal=TRUE, where = topenv(parent.frame()))
## argument formal is for Splus compatibility & is ignored. (All classes that
## are defined must have a class definition object.)
!is.null(getClassDef(Class, where))
### TODO s/Class/._class/ -- in order to allow 'Class' as regular slot name
new <-
## Generate an object from the specified class.
##
## Note that the basic vector classes, `"numeric"', etc. are implicitly defined,
## so one can use `new' for these classes.
##
function(Class, ...)
{
ClassDef <- getClass(Class, where = topenv(parent.frame()))
value <- .Call(C_new_object, ClassDef)
initialize(value, ...)
}
getClasses <-
## The names of all the classes formally defined on `where'.
## If called with no argument, all the classes currently known in the session
## (which does not include classes that may be defined on one of the attached
## libraries, but have not yet been used in the session).
function(where = .externalCallerEnv(), inherits = missing(where))
{
pat <- paste0("^",classMetaName(""))
if(!is.environment(where)) ## e.g. for "package:stats4"
where <- as.environment(where)
if(inherits) {
evList <- .parentEnvList(where)
clNames <- character()
for(ev in evList)
clNames <- c(clNames, grep(pat, names(ev), value=TRUE))
clNames <- unique(clNames)
}
else
clNames <- grep(pat, names(where), value=TRUE)
## strip off the leading pattern (this implicitly assumes the characters
## in classMetaName("") are either "." or not metacharacters
substring(clNames, nchar(pat, "c"))
}
validObject <- function(object, test = FALSE, complete = FALSE)
{
Class <- class(object)
classDef <- getClassDef(Class)
where <- .classEnv(classDef)
anyStrings <- function(x) if(isTRUE(x)) character() else x
## perform, from bottom up, the default and any explicit validity tests
## First, validate the slots.
errors <- character()
slotTypes <- classDef@slots
slotNames <- names(slotTypes)
attrNames <- c(".Data", ".S3Class", names(attributes(object)))
if(any(is.na(match(slotNames, attrNames)))) {
badSlots <- is.na(match(slotNames, attrNames))
errors <-
c(errors,
paste("slots in class definition but not in object:",
paste0('"', slotNames[badSlots], '"', collapse = ", ")))
slotTypes <- slotTypes[!badSlots]
slotNames <- slotNames[!badSlots]
}
for(i in seq_along(slotTypes)) {
classi <- slotTypes[[i]]
classDefi <- getClassDef(classi, where = where)
if(is.null(classDefi)) {
errors <- c(errors,
paste0("undefined class for slot \"", slotNames[[i]],
"\" (\"", classi, "\")"))
next
}
namei <- slotNames[[i]]
sloti <- try(switch(namei,
## .S3Class for S3 objects (e.g., "factor")
.S3Class = S3Class(object),
slot(object, namei)
), silent = TRUE)
if(inherits(sloti, "try-error")) {
errors <- c(errors, sloti)
next
}
## note that the use of possibleExtends is shared with checkSlotAssignment(), in case a
## future revision improves on it!
ok <- possibleExtends(class(sloti), classi, ClassDef2 = classDefi)
if(isFALSE(ok)) {
errors <- c(errors,
paste0("invalid object for slot \"", slotNames[[i]],
"\" in class \"", Class,
"\": got class \"", class(sloti),
"\", should be or extend class \"", classi, "\""))
next
}
if(!complete)
next
errori <- anyStrings(Recall(sloti, TRUE, TRUE))
if(length(errori)) {
errori <- paste0("In slot \"", slotNames[[i]],
"\" of class \"", class(sloti), "\": ", errori)
errors <- c(errors, errori)
}
}
extends <- rev(classDef@contains)
for(i in seq_along(extends)) {
exti <- extends[[i]]
superClass <- exti@superClass
if(!exti@simple && !is(object, superClass))
next ## skip conditional relations that don't hold for this object
superDef <- getClassDef(superClass, package = packageSlot(exti))
if(is.null(superDef)) {
errors <- c(errors,
paste0("superclass \"", superClass,
"\" not defined in the environment of the object's class"))
break
}
validityMethod <- superDef@validity
if(is.function(validityMethod)) {
errors <- c(errors, anyStrings(validityMethod(as(object, superClass))))
if(length(errors))
break
}
}
validityMethod <- classDef@validity
if(length(errors) == 0L && is.function(validityMethod)) {
errors <- c(errors, anyStrings(validityMethod(object)))
}
if(length(errors)) {
if(test)
errors
else {
msg <- gettextf("invalid class %s object", dQuote(Class))
if(length(errors) > 1L)
stop(paste(paste0(msg, ":"),
paste(seq_along(errors), errors, sep=": "),
collapse = "\n"), domain = NA)
else stop(msg, ": ", errors, domain = NA)
}
}
else
TRUE
}
setValidity <- function(Class, method, where = topenv(parent.frame())) {
if(isClassDef(Class)) {
ClassDef <- Class
Class <- ClassDef@className
}
else {
ClassDef <- getClassDef(Class, where)
}
method <- .makeValidityMethod(Class, method)
if(is.null(method) ||
(is.function(method) && length(formalArgs(method)) == 1L))
ClassDef@validity <- method
else
stop("validity method must be NULL or a function of one argument")
## TO DO: check the where argument against the package of the class def.
assignClassDef(Class, ClassDef, where = where)
resetClass(Class, ClassDef, where = where)
}
getValidity <- function (ClassDef) {
## "needed" according to ../man/validObject.Rd
ClassDef@validity
}
resetClass <- function(Class, classDef, where) {
if(is(Class, "classRepresentation")) {
classDef <- Class
Class <- Class@className
if(missing(where))
where <- .classDefEnv(classDef)
}
else {
if(missing(where)) {
if(missing(classDef))
where <- findClass(Class, unique = "resetting the definition")[[1L]]
else
where <- .classDefEnv(classDef)
}
if(missing(classDef)) {
classDef <- getClassDef(Class, where)
if(is.null(classDef)) {
warning(gettextf("class %s not found on %s; 'resetClass' will have no effect",
dQuote(Class),
sQuote(getPackageName(where))),
domain = NA)
return(classDef)
}
}
else if(!is(classDef, "classRepresentation"))
stop(gettextf("argument 'classDef' must be a string or a class representation; got an object of class %s",
dQuote(class(classDef))),
domain = NA)
# package <- getPackageName(where)
}
if(classDef@sealed)
warning(gettextf("class %s is sealed; 'resetClass' will have no effect",
dQuote(Class)),
domain = NA)
else {
classDef <- .uncompleteClassDefinition(classDef)
classDef <- completeClassDefinition(Class, classDef, where)
assignClassDef(Class, classDef, where)
}
classDef
}
## the (default) initialization: becomes the default method when the function
## is made a generic by .InitMethodDefinitions
initialize <- function(.Object, ...) {
args <- list(...)
if(length(args)) {
Class <- class(.Object)
## the basic classes have fixed definitions
if(!is.na(match(Class, .BasicClasses)))
return(newBasic(Class, ...))
ClassDef <- getClass(Class)
## separate the slots, superclass objects
snames <- allNames(args)
which <- nzchar(snames)
elements <- args[which]
supers <- args[!which]
thisExtends <- names(ClassDef@contains)
slotDefs <- ClassDef@slots
dataPart <- slotDefs[[".Data"]]
if(is.null(dataPart)) dataPart <- "missing"
if(length(supers)) {
for(i in rev(seq_along(supers))) {
obj <- supers[[i]]
Classi <- class(obj)
if(length(Classi) > 1L)
Classi <- Classi[[1L]] #possible S3 inheritance
## test some cases that let information be copied into the
## object, ordered from more to less: all the slots in the
## first two cases, some in the 3rd, just the data part in 4th
if(.identC(Classi, Class))
.Object <- obj
else if(extends(Classi, Class))
.Object <- as(obj, Class, strict=FALSE)
else if(extends(Class, Classi))
as(.Object, Classi) <- obj
else if(extends(Classi, dataPart))
.Object@.Data <- obj
else {
## is there a class to which we can coerce obj
## that is then among the superclasses of Class?
extendsi <- extends(Classi)[-1L]
## look for the common extensions, choose the first
## one in the extensions of Class
which <- match(thisExtends, extendsi)
which <- seq_along(which)[!is.na(which)]
if(length(which)) {
Classi <- thisExtends[which[1L]]
### was: as(.Object, Classi) <- as(obj, Classi, strict = FALSE)
## but as<- does an as(....) to its value argument
as(.Object, Classi) <- obj
}
else
stop(gettextf("cannot use object of class %s in new(): class %s does not extend that class",
dQuote(Classi),
dQuote(Class)),
domain = NA)
}
}
}
if(length(elements)) {
snames <- names(elements)
if(anyDuplicated(snames))
stop(gettextf("duplicated slot names: %s",
paste(sQuote(snames[duplicated(snames)]),
collapse = ", ")), domain = NA)
which <- match(snames, names(slotDefs))
if(anyNA(which))
stop(sprintf(ngettext(sum(is.na(which)),
"invalid name for slot of class %s: %s",
"invalid names for slots of class %s: %s"),
dQuote(Class),
paste(snames[is.na(which)], collapse=", ")),
domain = NA)
firstTime <- TRUE
for(i in seq_along(snames)) {
slotName <- snames[[i]]
slotClass <- slotDefs[[slotName]]
slotClassDef <- getClassDef(slotClass,
package = packageSlot(slotClass))
slotVal <- elements[[i]]
## perform non-strict coercion, but leave the error messages for
## values not conforming to the slot definitions to validObject(),
## hence the check = FALSE argument in the slot assignment
if(!.identC(class(slotVal), slotClass)
&& !is.null(slotClassDef) ) {
valClass <- class(slotVal)
valClassDef <- getClassDef(valClass, package = ClassDef@package)
if(!identical(possibleExtends(valClass, slotClass,
valClassDef, slotClassDef), FALSE))
slotVal <- as(slotVal, slotClass, strict = FALSE)
}
if (firstTime) {
## force a copy of .Object
slot(.Object, slotName, check = FALSE) <- slotVal
firstTime <- FALSE
} else {
## XXX: do the assignment in-place
"slot<-"(.Object, slotName, check = FALSE, slotVal)
}
}
}
validObject(.Object)
}
.Object
}
findClass <- function(Class, where = topenv(parent.frame()), unique = "") {
if(is(Class, "classRepresentation")) {
pkg <- Class@package
classDef <- Class
Class <- Class@className
}
else {
pkg <- packageSlot(Class)
if(is.null(pkg))
pkg <- ""
classDef <- getClassDef(Class, where, pkg)
}
where <- if(missing(where) && nzchar(pkg)) .requirePackage(pkg) else as.environment(where)
what <- classMetaName(Class)
where <- .findAll(what, where)
if(length(where) > 1L && nzchar(pkg)) {
pkgs <- sapply(where, function(db)get(what, db)@package)
where <- where[match(pkg, pkgs, 0L)]
}
else
pkgs <- pkg
if(length(where) == 0L) {
if(is.null(classDef))
classDef <- getClassDef(Class) # but won't likely succeed over previous
if(nzchar(unique)) {
if(is(classDef, "classRepresentation"))
stop(gettextf("class %s is defined, with package %s, but no corresponding metadata object was found (not exported?)",
dQuote(Class),
sQuote(classDef@package)),
domain = NA)
else
stop(gettextf("no definition of %s to use for %s",
dQuote(Class),
unique),
domain = NA)
}
}
else if(length(where) > 1L) {
pkgs <- sapply(where, getPackageName, create = FALSE)
## not all environments need be packages (e.g., imports)
## We only try to eliminate duplicate package namespaces
where <- where[!(nzchar(pkgs) & duplicated(pkgs))]
if(length(where) > 1L)
if(nzchar(unique)) {
pkgs <- base::unique(pkgs)
where <- where[1L]
## problem: 'unique'x is text passed in, so do not translate
warning(sprintf(ngettext(length(pkgs),
"multiple definition of class %s visible (%s); using the definition\n in package %s for %s",
"multiple definitions of class %s visible (%s); using the definition\n in package %s for %s"),
dQuote(Class),
paste(sQuote(pkgs), collapse = ", "),
sQuote(pkgs[[1L]]),
unique),
domain = NA)
}
## else returns a list of >1 places, for the caller to sort out (e.g., .findOrCopyClass)
}
where
}
isSealedClass <- function(Class, where = topenv(parent.frame())) {
if(is.character(Class))
Class <- getClass(Class, TRUE, where)
if(!is(Class, "classRepresentation"))
FALSE
else
Class@sealed
}
sealClass <- function(Class, where = topenv(parent.frame())) {
if(missing(where))
where <- findClass(Class, unique = "sealing the class", where = where)
classDef <- getClassDef(Class, where)
if(!classDef@sealed) {
classDef@sealed <- TRUE
assignClassDef(Class, classDef, where)
}
invisible(classDef)
}
## see $RHOME/src/main/duplicate.c for the corresponding datatypes
## not copied by duplicate1
.AbnormalTypes <- c("environment", "name", "externalptr", "NULL")
.indirectAbnormalClasses <- paste0(".", .AbnormalTypes)
names(.indirectAbnormalClasses) <- .AbnormalTypes
## the types not supported by indirect classes (yet)
.AbnormalTypes <- c(.AbnormalTypes,
"special","builtin", "weakref", "bytecode")
.addAbnormalDataType <- function(classes) {
types <- match(classes, .AbnormalTypes, 0) > 0
type = classes[types]
if(length(type) == 0)
return(classes)
if(length(type) > 1)
stop(gettextf("class definition cannot extend more than one of these data types: %s",
paste0('"',type, '"', collapse = ", ")),
domain = NA)
class <- .indirectAbnormalClasses[type]
if(is.na(class))
stop(gettextf("abnormal type %s is not supported as a superclass of a class definition",
dQuote(type)),
domain = NA)
## this message USED TO BE PRINTED: reminds programmers that
## they will see an unexpected superclass
## message(gettextf('Defining type "%s" as a superclass via class "%s"',
## type, class), domain = NA)
c(class, classes[!types])
}
.checkRequiredGenerics <- function(Class, classDef, where) {}
..checkRequiredGenerics <- function(Class, classDef, where) {
## If any of the superclasses are in the .NeedPrimitiveMethods
## list, cache the corresponding generics now and also save their names in
## .requireCachedGenerics to be used when the environment
## where= is loaded.
supers <- names(classDef@contains)
allNeeded <- get(".NeedPrimitiveMethods", envir = .methodsNamespace)
specials <- names(allNeeded)
needed <- match(specials, supers, 0L) > 0L
if(any(needed)) {
generics <- unique(allNeeded[needed])
packages <- vapply(generics, function(g) {
def <- getGeneric(g)
pkg <- def@package # must be "methods" ?
cacheGenericsMetaData(g, def, TRUE, where, pkg)
pkg
}, character(1))
previous <- if(exists(".requireCachedGenerics", where, inherits = FALSE))
get(".requireCachedGenerics", where) else character()
packages <- c(attr(previous, "package"), packages)
gg <- c(previous, generics)
attr(gg, "package") <- packages
assign(".requireCachedGenerics", gg, where)
}
}
.setS3MethodsOn <- function(classDef) {
ext <- extends(classDef)
slots <- classDef@slots
if(is.na(match(".S3Class", names(slots)))) {
## add the slot if it's not there
slots$.S3Class <- getClass("oldClass")@slots$.S3Class
classDef@slots <- slots
}
## in any case give the prototype the full extends as .S3Class
proto <- classDef@prototype
if(is.null(proto)) # simple virtual class--unlikely but valid
proto <- defaultPrototype()
attr(proto, ".S3Class") <- ext
classDef@prototype <- proto
classDef
}
multipleClasses <- function(details = FALSE) {
classes <- as.list(.classTable, all.names=TRUE)
dups <- Filter(is.list, classes)
if(details) dups else names(dups)
}
className <- function(class, package) {
if(is(class, "character")) {
className <- as.character(class)
if(missing(package))
package <- packageSlot(class)
if(is.null(package)) {
if(exists(className, envir = .classTable, inherits = FALSE))
classDef <- get(className, envir = .classTable)
else {
classDef <- findClass(className, topenv(parent.frame()))
if(length(classDef) == 1)
classDef <- classDef[[1]]
}
## at this point, classDef is the definition if
## unique, otherwise a list of 0 or >1 definitions
if(is(classDef, "classRepresentation"))
package <- classDef@package
else if(length(classDef) > 1L) {
pkgs <- sapply(classDef, function(cl)cl@package)
warning(gettextf("multiple class definitions for %s from packages: %s; picking the first",
dQuote(className),
paste(sQuote(pkgs), collapse = ", ")),
domain = NA)
package <- pkgs[[1L]]
}
else
stop(gettextf("no package name supplied and no class definition found for %s",
dQuote(className)),
domain = NA)
}
}
else if(is(class, classDef)) {
className <- class@className
if(missing(package))
package <- class@package
}
new("className", .Data = className, package = package)
}
## bootstrap version before the class is defined
classGeneratorFunction <- function(classDef, env = topenv(parent.frame())) {
fun <- function(...)NULL
## put the class name with package attribute into new()
body(fun) <- substitute(new(CLASS, ...),
list(CLASS = classDef@className))
environment(fun) <- env
fun
}
.classGeneratorFunction <- function(classDef, env = topenv(parent.frame())) {
if(is(classDef, "classRepresentation")) {}
else if(is(classDef, "character")) {
if(is.null(packageSlot(classDef)))
classDef <- getClass(classDef, where = env)
else
classDef <- getClass(classDef)
}
else
stop("argument 'classDef' must be a class definition or the name of a class")
fun <- function(...)NULL
## put the class name with package attribute into new()
body(fun) <- substitute(new(CLASS, ...),
list(CLASS = classDef@className))
environment(fun) <- env
fun <- as(fun, "classGeneratorFunction")
fun@className <- classDef@className
fun@package <- classDef@package
fun
}
## grammar: 'what' is an adjective, so not plural ....
inferProperties <- function(props, what) {
.validPropNames <- function(propNames) {
n <- length(props)
if(!n)
return(character())
else if(is.null(propNames))
stop(gettextf("No %s names supplied", what),
domain = NA, call. = FALSE)
else if(!all(nzchar(propNames)))
stop(gettextf("All %s names must be nonempty in:\n(%s)", what,
paste(sQuote(propNames), collapse = ", ")),
domain = NA, call. = FALSE)
else if(any(duplicated(propNames))) # NB: not translatable because of plurals
stop(gettextf("All %s names must be distinct in:\n(%s)", what,
paste(sQuote(propNames), collapse = ", ")),
domain = NA, call. = FALSE)
propNames
}
if(is.character(props)) {
propNames <- names(props)
if(is.null(propNames)) {
propNames <- .validPropNames(props) # the text is the names
## treat as "ANY"
props <- as.list(rep("ANY", length(props)))
names(props) <- propNames
}
else {
.validPropNames(propNames)
props <- as.list(props)
}
}
else if(is.list(props)) {
if(length(props) > 0) # just validate them
.validPropNames(names(props))
}
else
stop(gettextf("argument %s must be a list or a character vector; got an object of class %s",
dQuote(what), dQuote(class(fields))),
domain = NA)
props
}