blob: eeac6bef49617af9e922dadf718bcb333f34edc3 [file] [log] [blame]
# File src/library/methods/R/NextMethod.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/
callNextMethod <- function(...) {
method <- nextMethod <- NULL
dotNextMethod <- as.name(".nextMethod")
## 2 environments are used here: callEnv, from which the .nextMethod call
## takes place; and methodEnv, the method environment used to find the next method
## Because of the .local mechanism used to allow variable argument lists
## in methods (see rematchDefinition) these may be different.
parent <- sys.parent(1)
methodFun <- maybeMethod <- sys.function(parent)
if(is(maybeMethod, "MethodDefinition")) {
callEnv <- methodEnv <- parent.frame(1)
mcall <- sys.call(parent)
dotsenv <- parent.frame(2)
i <- 1L
}
else {
callEnv <- parent.frame(1)
methodEnv <- parent.frame(2)
mcall <- sys.call(sys.parent(2))
dotsenv <- parent.frame(3)
maybeMethod <- sys.function(sys.parent(2))
i <- 2L
}
## set up the nextMethod object, load it
## into the calling environment, and cache it
if(!is.null(method <- methodEnv$.Method)) {
## call to standardGeneric(f)
nextMethod <- callEnv$.nextMethod
f <- methodEnv$.Generic
}
else if(identical(mcall[[1L]], dotNextMethod)) {
## a call from another callNextMethod()
nextMethodEnv <- parent.frame(i+1L)
nextMethod <- nextMethodEnv$.nextMethod
f <- nextMethodEnv$.Generic
}
else if (is(maybeMethod, "MethodDefinition")) {
f <- maybeMethod@generic
method <- maybeMethod
}
else {
## may be a method call for a primitive; not available as .Method
if (is.primitive(mcall[[1L]])) {
f <- .primname(mcall[[1L]])
} else {
f <- as.character(mcall[[1L]])
}
fdef <- genericForBasic(f)
## check that this could be a basic function with methods
if(is.null(fdef))
stop(gettextf("a call to callNextMethod() appears in a call to %s, but the call does not seem to come from either a generic function or another 'callNextMethod'",
sQuote(f)),
domain = NA)
f <- fdef@generic
method <- maybeMethod
}
if(is(method, "MethodDefinition")) {
if(is.null(nextMethod)) {
if(!is(method, "MethodWithNext")) {
method <- addNextMethod(method, f, envir=methodEnv)
## cache the method with the nextMethod included,
## so later calls will load this information.
cacheMethod(f, method@target, method, fdef = getGeneric(f), inherited = TRUE)
}
nextMethod <- method@nextMethod
assign(".nextMethod", nextMethod, envir = callEnv)
assign(".Generic", f, envir = callEnv)
}
}
else if(is.null(method)) {
if(is.null(nextMethod))
stop("call to 'callNextMethod' does not appear to be in a 'method' or 'callNextMethod' context")
## else, callNextMethod() from another callNextMethod
method <- nextMethod
if(!is(method, "MethodWithNext")) {
method <- addNextMethod(method, f, envir=methodEnv)
}
nextMethod <- method@nextMethod
## store the nextmethod in the previous nextmethod's
assign(".nextMethod", nextMethod, envir = callEnv)
assign(".Generic", f, envir = callEnv)
assign(".nextMethod", method, envir = nextMethodEnv)
assign(".Generic", f, envir = nextMethodEnv)
}
else
stop(gettextf("bad object found as method (class %s)",
dQuote(class(method))), domain = NA)
if (is.null(nextMethod))
stop("No next method available")
subsetCase <- !is.na(match(f, .BasicSubsetFunctions))
if(nargs()>0) {
call <- sys.call()
call[[1L]] <- as.name(".nextMethod")
eval(call, callEnv)
}
else {
if(subsetCase) {
## don't use match.call, because missing args will screw up for "[", etc.
call <- as.list(mcall)
## don't test with identical(), there may be a package attr.
if((f == "[") && length(names(call)>0))
call <- .doSubNextCall(call, method) # [ with a drop= arg.
else {
fnames <- c("", formalArgs(method))
i <- match("...",fnames)
if(is.na(i) || i > length(call))
length(fnames) <- length(call)
else {
i <- i-1L
length(fnames) <- i
fnames <- c(fnames, rep("", length(call) - i))
}
if (substring(f, nchar(f)-1L) == "<-")
fnames[length(fnames)] <- "value"
names(call) <- fnames
call <- as.call(call)
}
}
else
call <- match.call(methodFun, mcall, expand.dots = FALSE,
envir = dotsenv)
.Call(C_R_nextMethodCall, call, callEnv)
}
}
## Skeleton for the generic in ./MethodsListClass.R :
loadMethod <- function(method, fname, envir) method
.doSubNextCall <- function(call, method) {
idrop <- match("drop", names(call))
hasDrop <- !is.na(idrop)
if(hasDrop) {
drop <- call$drop
call <- call[-idrop]
}
fnames <- c("", formalArgs(method))
i <- match("...",fnames)
if(is.na(i) || i > length(call))
length(fnames) <- length(call)
else {
i <- i-1
length(fnames) <- i
cnames <- if (is.null(names(call)))
rep("", length(call) - i)
else utils::tail(names(call), -i)
fnames <- c(fnames, cnames)
}
names(call) <- fnames
if(hasDrop)
call$drop <- drop
as.call(call)
}