# EnumValue is typically for individual values. # Bitwise can support a vector. # These are for values. The definition is separate. setClass("SymbolicConstant", representation = c(names = "character"), contains = "integer") setClass("EnumValue", contains = "SymbolicConstant", prototype = as.integer(NA)) setClass("BitwiseValue", contains = "SymbolicConstant", prototype = as.integer(NA)) ################################## # IGNORE # These 3 and the next classes look like versions of the same idea. if(FALSE) { setClass("SymbolicConstantsDefinition", representation(name = "character")) setClass("EnumValueDefinition", contains = c("EnumValue", "SymbolicConstantsDefinition")) setClass("BitwiseValueDefinition", contains = c("BitwiseValue", "SymbolicConstantsDefinition")) BitwiseValueDefinition = function(values, name = names(values), class = "BitwiseValueDefinition") { ans = new("BitwiseValueDefinition", wxStretchValues, name = "wxStretch") names(ans) = name ans } setMethod("[", "BitwiseValueDefinition", function(x, i, j, ..., drop = TRUE) { k = class(x[[1]]) ans = callNextMethod() BitwiseValue(ans, class = k) }) } # IGNORE ################################## setClass("EnumDef", representation(EnumName = "character"), contains = "integer") EnumDef = function(name, values, symbolicNames = names(values)) { values = as(values, "integer") ans = new("EnumDef", structure(values, names = symbolicNames), EnumName = name) names(ans) = symbolicNames ans } # General types that can be used in an S4 method signature in setMethod # to cover an enumeration or bitwise in its various forms of specification, # i.e. as a number, a string or as an actual EnumValue or BitwiseValue setClassUnion("EnumerationValue", c("numeric", "integer", "character", "EnumValue")) # Display with the name. setMethod("show", "SymbolicConstant", function(object) # avoid the bitlist coercion show(structure(as(unclass(object), "numeric"), names = names(object))) ) # display as a 1 row matrix with the name of the enum type as the row name. # Do we need this or can we have the generic SymbolicConstant. tmp = function(object) show(matrix(as(object, "integer"), 1, , dimnames = list(paste(object@EnumName, ":", sep = ""), names(object)))) #setMethod("show", "EnumValue", tmp) # do we want this. setMethod("show", "EnumDef", tmp) setMethod("show", "SymbolicConstant", function(object) show(matrix(as(object, "integer"), 1, , dimnames = list(class(object), names(object))))) setMethod("[", "EnumDef", function(x, i, j, ..., drop = TRUE) { vals = get(paste(x@EnumName, "Values", sep = "")) asEnumValue( unclass(x)[i], vals) }) makeSymbolicVariables = # # For element in the def, create a corresponding R variable with that name # which contains that value. # e.g. # c(a = 1, b = 2) # for a class MyEnum, we would end up with # variables named a and b with values 1 and 2 respectively # and each would be of class MyEnum. # # The target class must have been defined before this. # function(def, className = class(def), where = globalenv()) { invisible(sapply(names(def), function(i) { if(is(def, "BitwiseValue")) el = BitwiseValue(def[i], i, class = className) else el = def[i] assign(i, el, where) })) } cumBitOr = bitlist = function(...) { x = unlist(list(...)) if(length(x) == 1) return(x) ans = x[1] for(i in 2:length(x)) { ans = bitOr(ans, x[i]) # from package bitops } ans } asEnumValue = # # if fixCloseMatches is TRUE, we continue on if we can find a match # for all of the possible values that were specified slightly incorrectly. # function(val, values, class = values@EnumName, fromString = NA, fixCloseMatches = TRUE, prefix = character(), S3 = is.null(getClassDef(class))) { # handle multiple entries. if(length(val) > 1) { tmp = sapply(val, asEnumValue, values, class, fromString, fixCloseMatches, prefix, USE.NAMES = FALSE) # if we have multiple values and they relate to a BitwiseValue enumeration, # collapse them into a single value. # class was getClass(class) if(extends(class, "BitwiseValue")) #XXX augment for S3 return(bitlist(tmp)) if(S3) class(tmp) = c(class, "EnumValue") else tmp = as(tmp, class) return(tmp) } if(is.na(fromString)) fromString = is(val, "character") if(fromString) { i = pmatch(val, names(values)) # allowing pmatch, but should type it explicitly in code. # deal with lowercase matches for covenience if(is.na(i)) i = pmatch(val, tolower(names(values))) # and if still not there, remove the prefix. if(is.na(i) && length(prefix)) i = pmatch(val, tolower(gsub(paste("^", prefix, sep = ""), "", names(values)))) } else i = match(val, values) if(any(is.na(i))) { i = raiseEnumError(val, values, fromString, fixCloseMatches, index = i) } if(S3) { ans = structure(unclass(values)[i], names = names(values)[i]) class(ans) = c(class, "EnumValue") return(ans) } else { ans = new(class, unclass(values)[i]) names(ans) = names(values)[i] } ans } GenericEnumValue = function(name, val, class = "EnumValue", S3 = FALSE) { if(S3) { ans = structure(val, names = name, class = unique(c(class, "EnumValue"))) } else { ans = new(class, val) names(ans) = name } ans } raiseEnumError = function(val, values, fromString = is(fromString, "character"), fixCloseMatches = TRUE, index = match(val, if(fromString) names(values) else values)) { # see if we can find values that were close to the ones the user gave us incorrectly. if(fromString) { possibles = names(values)[m <- agrep(val[is.na(index)], names(values))] } else { possibles = values[ m <- agrep(as.character(val), as.character(values)) ] } if(length(possibles)) { txt = paste("\n\tPerhaps you meant", if(length(possibles) > 1) "one of", paste(possibles, collapse = ", ")) txt = paste("No such value(s) ", val[is.na(index)], " in ", paste(names(values), collapse = ", "), txt, sep = "") msg = list(message = txt, call = NULL, possibleValues = possibles, class = class) if(fixCloseMatches && all(!is.na(m))) { class(msg) = c("EnumCoercionWarning", "warning", "condition") warning(msg) index[is.na(index)] = m return(index) } else { class(msg) = c("EnumCoercionError", "error", "condition") stop(msg) } } else stop("No such value(s) ", val[is.na(index)], " in ", paste(names(values), collapse = ", ")) }