[med-svn] [r-cran-registry] 01/02: Imported Upstream version 0.3-1
Alba Crespi
albac-guest at moszumanska.debian.org
Sun Apr 10 21:21:19 UTC 2016
This is an automated email from the git hooks/post-receive script.
albac-guest pushed a commit to branch master
in repository r-cran-registry.
commit 485baacccadf9333d6ef768180f59192ae3d8b13
Author: Alba Crespi <crespialba+debian at gmail.com>
Date: Sun Apr 10 21:42:22 2016 +0100
Imported Upstream version 0.3-1
---
DESCRIPTION | 14 +
MD5 | 21 +
NAMESPACE | 13 +
R/accessors.R | 22 +
R/converters.R | 25 +
R/matchfuns.R | 20 +
R/printsummary.R | 32 ++
R/registry.R | 501 +++++++++++++++++
R/utils.R | 2 +
build/vignette.rds | Bin 0 -> 193 bytes
demo/00Index | 2 +
demo/registry.R | 67 +++
inst/doc/registry.R | 207 +++++++
inst/doc/registry.Rnw | 313 +++++++++++
inst/doc/registry.pdf | Bin 0 -> 152573 bytes
man/matchfuns.Rd | 65 +++
man/registry.Rd | 53 ++
man/regobj.Rd | 146 +++++
tests/registry.R | 2 +
vignettes/abbrvnat.bst | 1432 ++++++++++++++++++++++++++++++++++++++++++++++++
vignettes/registry.Rnw | 313 +++++++++++
vignettes/registry.bib | 33 ++
22 files changed, 3283 insertions(+)
diff --git a/DESCRIPTION b/DESCRIPTION
new file mode 100755
index 0000000..2f3e5a3
--- /dev/null
+++ b/DESCRIPTION
@@ -0,0 +1,14 @@
+Package: registry
+Type: Package
+Title: Infrastructure for R Package Registries
+Version: 0.3
+Author: David Meyer
+Maintainer: David Meyer <David.Meyer at R-project.org>
+Description: Provides a generic infrastructure for creating and using registries.
+Depends: R (>= 2.6.0)
+Imports: utils
+License: GPL-2
+NeedsCompilation: no
+Packaged: 2015-07-07 16:27:02 UTC; david
+Repository: CRAN
+Date/Publication: 2015-07-08 17:11:04
diff --git a/MD5 b/MD5
new file mode 100644
index 0000000..fd237b7
--- /dev/null
+++ b/MD5
@@ -0,0 +1,21 @@
+2615feeba9ddba58a04e6678f0476ae5 *DESCRIPTION
+5d602abc58c4875294dc11f72158774d *NAMESPACE
+b3074b376232f3c1fd80571ba446996f *R/accessors.R
+bb342556b9b96cfa5283fce894700b0d *R/converters.R
+bc616cbb85bd434ba34d927e64f630be *R/matchfuns.R
+880b67c5a7ad7d534dbafe347219223b *R/printsummary.R
+8b1af8764497f1347e5ff3754692652e *R/registry.R
+21bb06f605604d73919a8d3cb816f44c *R/utils.R
+d906ffbab1a29b3f48d69c99bb6fed45 *build/vignette.rds
+8eb52e1e2fe74ae94a8aa3113511b94f *demo/00Index
+aef06553ca79a1d87d69ae93ea9ffcd8 *demo/registry.R
+7d44eb1b5a93ff5de790427a3b8c2301 *inst/doc/registry.R
+03889df16f04b2e9156693baf5e9e694 *inst/doc/registry.Rnw
+f15f4b44a36b80e44016450e74d2ae81 *inst/doc/registry.pdf
+d75af01cf06a4c33ade70aea44df055d *man/matchfuns.Rd
+3ba25d05aadad18ae969da1ed416f2b6 *man/registry.Rd
+5648e6108e71120477b77b0150624873 *man/regobj.Rd
+2c77c272d1d8f1c5bb781bb9ec6edf44 *tests/registry.R
+ee1b5ad6f2633c9d108cc96775448248 *vignettes/abbrvnat.bst
+03889df16f04b2e9156693baf5e9e694 *vignettes/registry.Rnw
+a2cad786f79804c8744a9e008c1651c0 *vignettes/registry.bib
diff --git a/NAMESPACE b/NAMESPACE
new file mode 100755
index 0000000..b32ead7
--- /dev/null
+++ b/NAMESPACE
@@ -0,0 +1,13 @@
+importFrom("utils", "formatUL")
+
+export("registry")
+exportPattern("match_.*")
+
+S3method("[", "registry")
+S3method("[[", "registry")
+S3method("length", "registry")
+S3method("as.data.frame", "registry")
+S3method("print", "registry")
+S3method("summary", "registry")
+S3method("print", "registry_field")
+S3method("print", "registry_entry")
diff --git a/R/accessors.R b/R/accessors.R
new file mode 100755
index 0000000..b1cbd51
--- /dev/null
+++ b/R/accessors.R
@@ -0,0 +1,22 @@
+"[[.registry" <-
+function(x, ...)
+{
+ if (missing(..1))
+ x$get_entry()
+ else
+ x$get_entry(...)
+}
+
+"[.registry" <-
+function(x, ...)
+{
+ if (missing(..1))
+ x$get_entries()
+ else
+ x$get_entries(...)
+}
+
+length.registry <-
+function(x)
+ x$n_of_entries()
+
diff --git a/R/converters.R b/R/converters.R
new file mode 100755
index 0000000..e4d422c
--- /dev/null
+++ b/R/converters.R
@@ -0,0 +1,25 @@
+as.data.frame.registry <-
+function(x, ...)
+{
+ .one_line <- function(entry) {
+ entry <- lapply(.labels(entry), function(i) i[[1]])
+ data.frame(unclass(entry), ...)
+ }
+
+ ret <- do.call(rbind, lapply(x$get_entries(), .one_line))
+ row.names(ret) <- NULL
+ ret
+}
+
+.labels <-
+function(x)
+{
+ ## transform function entries
+ x[sapply(x, inherits, "function")] <- "<<function>>"
+
+ ## transform objects
+ obj <- sapply(x, is.object)
+ x[obj] <- paste("<<", sapply(x, class)[obj], ">>", sep = "")
+
+ x
+}
diff --git a/R/matchfuns.R b/R/matchfuns.R
new file mode 100755
index 0000000..699cf3f
--- /dev/null
+++ b/R/matchfuns.R
@@ -0,0 +1,20 @@
+## user-specifiable lookup-functions
+match_ignorecase <-
+function(lookup, entry, ...)
+ tolower(lookup) %in% tolower(entry)
+
+match_exact <-
+function(lookup, entry, ...)
+ lookup %in% entry
+
+match_partial <-
+function(lookup, entry, ...)
+ !is.na(pmatch(lookup, entry, ...))
+
+match_partial_ignorecase <-
+function(lookup, entry, ...)
+ !is.na(pmatch(tolower(lookup), tolower(entry), ...))
+
+match_regexp <-
+function(lookup, entry, ...)
+ length(grep(lookup, entry, ...)) > 0
diff --git a/R/printsummary.R b/R/printsummary.R
new file mode 100755
index 0000000..5d50683
--- /dev/null
+++ b/R/printsummary.R
@@ -0,0 +1,32 @@
+print.registry <-
+function(x, ...)
+{
+ l <- x$n_of_entries()
+ if (l < 1)
+ writeLines(gettext("An object of class 'registry' with no entry."))
+ else if (l == 1)
+ writeLines(gettext("An object of class 'registry' with one entry."))
+ else
+ writeLines(gettextf("An object of class 'registry' with %d entries.", l))
+ invisible(x)
+}
+
+print.registry_field <-
+function(x, ...) ## remove index function
+{
+ writeLines(formatUL(x[-7], label = names(x[-7]), ...))
+ invisible(x)
+}
+
+print.registry_entry <-
+function(x, ...)
+{
+ x <- lapply(.labels(x), paste, collapse = ", ")
+ writeLines(formatUL(x, label = names(x)))
+ invisible(x)
+}
+
+summary.registry <-
+function(object, ...)
+ as.data.frame(object, ...)
+
diff --git a/R/registry.R b/R/registry.R
new file mode 100755
index 0000000..0fb9d34
--- /dev/null
+++ b/R/registry.R
@@ -0,0 +1,501 @@
+###################################
+### generic registry infrastructure
+
+### IDEA: use lexical scope with nested functions to create an
+### S3-"object" that exposes the data structure only through accessor functions.
+
+## creating function
+registry <-
+ function(entry_class = NULL, registry_class = NULL,
+ validity_FUN = NULL, stop_if_missing = FALSE)
+{
+### ATTRIBUTES
+ ## repository
+ DATA <- META <- list()
+
+ ## permissions
+ PERMISSIONS <- c(set_entries = TRUE, modify_entries = TRUE,
+ delete_entries = TRUE, set_fields = TRUE)
+ SEALED_FIELDS <- SEALED_ENTRIES <- character(0)
+
+
+### METHODS (PRIVATE)
+ ## helper functions
+ .field_exists <-
+ function(name)
+ name %in% .get_field_names()
+
+ .make_field <-
+ function(type = NA, alternatives = NA, default = NA,
+ is_mandatory = FALSE, is_modifiable = TRUE, is_key = FALSE,
+ validity_FUN = NULL, index_FUN = match_ignorecase,
+ index_FUN_args = NULL
+ )
+ structure(list(type = type,
+ alternatives = alternatives,
+ default = default,
+ is_mandatory = is_mandatory,
+ is_modifiable = is_modifiable,
+ is_key = is_key,
+ validity_FUN = validity_FUN,
+ index_FUN = index_FUN,
+ index_FUN_args = index_FUN_args
+ ),
+ class = "registry_field")
+
+
+ .make_entry <- function(l)
+ {
+ ## get index fields
+ index_fields <- .get_index_fields()
+
+ ## sort
+ l <- l[c(index_fields, setdiff(.get_field_names(), index_fields))]
+
+ ## return object (possibly inheriting from entry_class)
+ structure(l, class = c(entry_class, "registry_entry"))
+ }
+
+ .get_index_fields <-
+ function()
+ names(META)[sapply(META, function(i) i$is_key)]
+
+ .get_mandatory_fields <-
+ function()
+ names(META)[sapply(META, function(i) i$is_mandatory)]
+
+ .get_field_defaults <-
+ function()
+ lapply(META, function(i) i$default)
+
+ .get_entry_indices <-
+ function(key, stop_missing = stop_if_missing, FUN = NULL, ARGS = NULL) {
+ ## get index fields
+ index_fields <- .get_index_fields()
+ l <- length(key)
+ n <- names(key)
+ if (l != length(index_fields))
+ index_fields <- if (is.null(n))
+ index_fields[1:l]
+ else
+ intersect(n, index_fields)
+
+ ## returns the index of the first match
+ index <- seq_along(DATA)
+ for (index_field in seq_along(index_fields)) {
+ if (length(index) < 1)
+ break
+
+ if (is.null(FUN)) {
+ FUN <- .get_field(index_fields[index_field])$index_FUN
+ ARGS <- .get_field(index_fields[index_field])$index_FUN_args
+ }
+ index <-
+ index[sapply(DATA[index],
+ function(i) do.call(FUN,
+ c(list(key[index_field],
+ i[[ index_fields[index_field] ]]),
+ ARGS))
+ )]
+ }
+
+ ## nothing found
+ if (length(index) < 1) {
+ if (stop_missing)
+ stop("Entry not in registry.", call. = FALSE)
+ else
+ return(NULL)
+ }
+
+ ## return
+ index
+ }
+
+ .get_first_entry_index <-
+ function(key, stop_missing = stop_if_missing)
+ .get_entry_indices(key, stop_missing)[1]
+
+ .check_value <-
+ function(field_name, field, value)
+ {
+ ## Note we do not check NA entries because this may by set automatically
+ if (is.object(value) || !is.function(value) && !is.na(value[1])) {
+ ## check class / list of alternatives, if any
+ if (!any(is.na(field$type))) {
+ ## check class
+ if (!inherits(value, field$type)) {
+ stop(paste("Field", dQuote(field_name),
+ "does not inherit from:",
+ paste(field$type, collapse = ", ")),
+ call. = FALSE)
+ }
+
+ ## check list of alternatives
+ if (!any(is.na(field$alternatives))) {
+ if(!any(unlist(lapply(field$alternatives,
+ identical, value))))
+ stop(paste("Possible values for", dQuote(field_name),
+ "are:", paste(field$alternatives,
+ collapse = ", ")),
+ call. = FALSE)
+ }
+ }
+
+ ## apply validity function, if any
+ if (!is.null(field$validity_FUN))
+ do.call(field$validity_FUN, list(value))
+ }
+ }
+
+ .check_for_unknown_fields <-
+ function(n)
+ {
+ ## check for fields not in repository
+ missing <- !.field_exists(n)
+ if (any(missing))
+ stop(paste("Field(s) not in repository:",
+ paste(n[missing], collapse = ", ")), call. = FALSE)
+ }
+
+### METHODS (PUBLIC)
+ ## field accessors
+ .get_field <-
+ function(name)
+ {
+ if (!.field_exists(name))
+ stop(paste("Field", dQuote(name), "not in registry."),
+ call. = FALSE)
+
+ META[[name]]
+ }
+
+ .get_fields <-
+ function()
+ META
+
+ .get_field_names <-
+ function()
+ names(META)
+
+ .set_field <-
+ function(name,
+ type = NA, alternatives = NA, default = NA,
+ is_mandatory = NULL, is_modifiable = TRUE, is_key = FALSE,
+ validity_FUN = NULL, index_FUN = match_ignorecase, ...
+ )
+ {
+ ## check permissions
+ if (!PERMISSIONS["set_fields"])
+ stop("Setting of fields not allowed.", call. = FALSE)
+
+ ## check for double entries
+ if (.field_exists(name))
+ stop(paste("Field", dQuote(name), "already in registry."),
+ call. = FALSE)
+
+ ## possibly, infer type from argment
+ if (!is.na(type) && !(is.character(type)))
+ type <- class(type)
+
+ ## check mandatory fields
+ if (is.null(is_mandatory))
+ is_mandatory <- is_key
+ if (is_mandatory && !is.na(default))
+ stop("Mandatory fields should have no default.", call. = FALSE)
+ if (is_key && !is_mandatory)
+ stop("Key fields must be mandatory.", call. = FALSE)
+
+ ## create field entry
+ field <- .make_field(type = type,
+ alternatives = alternatives,
+ default = default,
+ is_mandatory = is_mandatory,
+ is_modifiable = is_modifiable,
+ is_key = is_key,
+ validity_FUN = validity_FUN,
+ index_FUN = index_FUN,
+ index_FUN_args = list(...)
+ )
+
+ ## check validity of default
+ .check_value("default", field, default)
+
+ ## check validity of alternatives
+ if (!any(is.na(alternatives)))
+ for (i in alternatives)
+ .check_value("alternatives", field, i)
+
+ ## add field to meta data
+ META <<- c(META, list(field))
+ names(META)[length(META)] <<- name
+
+ ## add (missing) fields to data entries
+ DATA <<- lapply(DATA, `[[<-`, name, default)
+ }
+
+ .has_entry <-
+ function(key)
+ length(.get_entry_indices(key)) > 0
+
+ .n_of_entries <-
+ function()
+ length(DATA)
+
+ ## entry accessors
+ .set_entry <-
+ function(...)
+ {
+ ## check permissions
+ if (!PERMISSIONS["set_entries"])
+ stop("Setting of entries not allowed.", call. = FALSE)
+
+ ## parameter handling
+ l <- list(...)
+ n <- names(l)
+ index_fields <- .get_index_fields()
+ fields <- .get_field_names()
+
+ if (is.null(n)) {
+ if (length(l) == length(fields)) {
+ n <- fields
+ names(l) <- n
+ } else
+ stop("Need either named arguments, or complete entry.")
+ }
+
+
+ .check_for_unknown_fields(n)
+
+ ## check if there is at least one index field
+ if (length(index_fields) < 1)
+ stop("Need at least one index field before adding entries.",
+ call. = FALSE)
+
+ ## check for mandatory fields
+ mandatory_fields <- .get_mandatory_fields()
+ missing_mandatory_fields <- !mandatory_fields %in% n
+ if (any(missing_mandatory_fields))
+ stop(paste("The following fields are mandatory, but missing:",
+ paste(mandatory_fields[missing_mandatory_fields],
+ collapse = ", ")), call. = FALSE)
+
+ ## check for double entries
+ if (length(.get_entry_indices(l[index_fields],
+ stop_missing = FALSE,
+ FUN = match_exact)) > 0)
+ stop(paste("Entry already in registry."), call. = FALSE)
+
+ ## check defaults and set values, if needed
+ field_defaults <- .get_field_defaults()
+ default_fields <- names(field_defaults)
+ missing_fields <- setdiff(default_fields, n)
+ l[missing_fields] <- field_defaults[missing_fields]
+
+ ## check field types, and apply field check function, if any.
+ for (f in n)
+ .check_value(f, .get_field(f), l[[f]])
+
+
+ ## apply entry check function
+ if (!is.null(validity_FUN))
+ do.call(validity_FUN, list(l))
+
+ ## add entry
+ entry <- list(.make_entry(l))
+ names(entry) <-
+ paste(lapply(entry[[1]][seq_along(index_fields)], `[[`, 1),
+ collapse = "_")
+ DATA <<- c(DATA, entry)
+ }
+
+ .get_entries <-
+ function(...)
+ {
+ key <- list(...)
+ if (length(key) > 0) {
+ ind <- .get_entry_indices(key)
+ if (length(ind) > 0)
+ DATA[ind]
+ else
+ NULL
+ } else DATA
+ }
+
+ .grep_entries <-
+ function(pattern, ...)
+ {
+ pattern_in_entry <-
+ function(x) any(sapply(x, function(i) is.character(i)
+ && length(grep(pattern, i, ...) > 0)))
+ ind <- sapply(DATA, pattern_in_entry)
+ if (any(ind))
+ DATA[ind]
+ else
+ NULL
+ }
+
+ .get_first_entry <-
+ function(...)
+ .get_entries(...)[[1]]
+
+ .get_entry_names <-
+ function()
+ {
+ if (length(DATA) < 1)
+ character(0)
+ else
+ names(DATA)
+ }
+
+ .delete_entry <-
+ function(...)
+ {
+ key <- list(...)
+
+ ## check permissions
+ if (!PERMISSIONS["delete_entries"])
+ stop("Deletion of entries not allowed.", call. = FALSE)
+
+ ## fetch entry index (this also checks if the entry exists)
+ entry_index <- .get_entry_indices(key)
+
+ ## check if it is unique
+ if (length(entry_index) != 1)
+ stop("Key specification must be unique.", call. = FALSE)
+
+ ## check sealed entries
+ if (entry_index %in% SEALED_ENTRIES)
+ stop(paste("Deletion of entry not allowed."), call. = FALSE)
+
+ ## delete it
+ DATA[entry_index] <<- NULL
+ }
+
+
+ .modify_entry <-
+ function(...)
+ {
+ ## check permissions
+ if (!PERMISSIONS["modify_entries"])
+ stop("Modifying of entries not allowed.", call. = FALSE)
+
+ ## parameter handling
+ l <- list(...)
+ n <- names(l)
+
+ .check_for_unknown_fields(n)
+
+ ## determine entry key
+ key <- l[.get_index_fields()]
+
+ ## fetch entry index (this also checks if the entry exists)
+ entry_index <- .get_entry_indices(key)
+
+ ## check if it is unique
+ if (length(entry_index) != 1)
+ stop("Key specification must be unique!", call. = FALSE)
+
+ ## fetch entry
+ entry <- DATA[[entry_index]]
+
+ for(field in setdiff(n, .get_index_fields())) {
+ ## check if field is modifiable
+ field_entry <- .get_field(field)
+ if (!field_entry$is_modifiable)
+ stop(paste("Field", dQuote(field), "is not modifiable."),
+ call. = FALSE)
+
+ ## check if entry and field are sealed
+ if ((entry_index %in% SEALED_ENTRIES) &&
+ (field %in% SEALED_FIELDS))
+ stop(paste("Modification of field", dQuote(field),
+ "in this entry not allowed."), call. = FALSE)
+
+ ## check new value
+ value <- l[[field]]
+ .check_value(field, field_entry, value)
+
+ ## modify entry locally
+ entry[[field]] <- value
+ }
+
+ ## apply entry check function
+ if (!is.null(validity_FUN))
+ do.call(validity_FUN, list(entry))
+
+ ## modify entry in registry
+ DATA[entry_index] <<- list(entry)
+ }
+
+ ## get all entries for one field
+ .get_field_entries <-
+ function(field, unlist = TRUE)
+ {
+ if (!.field_exists(field))
+ stop(paste("Field", dQuote(field), "not in registry."),
+ call. = FALSE)
+
+ ret <- lapply(DATA, `[[`, field)
+ if (unlist)
+ unlist(ret)
+ else
+ ret
+ }
+
+ ## permission getters/setters
+ .get_permissions <-
+ function()
+ PERMISSIONS
+
+ .restrict_permissions <-
+ function(set_entries = TRUE, modify_entries = TRUE,
+ delete_entries = TRUE, set_fields = TRUE)
+ {
+ PERMISSIONS["set_entries"] <<-
+ PERMISSIONS["set_entries"] && set_entries
+ PERMISSIONS["modify_entries"] <<-
+ PERMISSIONS["modify_entries"] && modify_entries
+ PERMISSIONS["delete_entries"] <<-
+ PERMISSIONS["delete_entries"] && delete_entries
+ PERMISSIONS["set_fields"] <<-
+ PERMISSIONS["set_fields"] && set_fields
+ }
+
+ .seal_entries <-
+ function()
+ {
+ SEALED_ENTRIES <<- seq_along(DATA)
+ SEALED_FIELDS <<- .get_field_names()
+ }
+
+ .get_sealed_field_names <-
+ function()
+ SEALED_FIELDS
+
+### CONSTRUCTOR
+ ## return class
+ structure(list(get_field = .FUNCall(.get_field),
+ get_fields = .FUNCall(.get_fields),
+ get_field_names = .FUNCall(.get_field_names),
+ set_field = .FUNCall(.set_field),
+
+ has_entry = .FUNCall(.has_entry),
+ get_entry = .FUNCall(.get_first_entry),
+ get_entries = .FUNCall(.get_entries),
+ get_entry_names = .FUNCall(.get_entry_names),
+ grep_entries = .FUNCall(.grep_entries),
+ set_entry = .FUNCall(.set_entry),
+ modify_entry = .FUNCall(.modify_entry),
+ delete_entry = .FUNCall(.delete_entry),
+ n_of_entries = .FUNCall(.n_of_entries),
+
+ get_field_entries = .FUNCall(.get_field_entries),
+
+ get_permissions = .FUNCall(.get_permissions),
+ restrict_permissions = .FUNCall(.restrict_permissions),
+ seal_entries = .FUNCall(.seal_entries),
+ get_sealed_field_names = .FUNCall(.get_sealed_field_names)
+ ),
+ class = c(registry_class, "registry"))
+}
+
diff --git a/R/utils.R b/R/utils.R
new file mode 100755
index 0000000..603d8ee
--- /dev/null
+++ b/R/utils.R
@@ -0,0 +1,2 @@
+## This should be in base R!
+.FUNCall <- function(f) function(...) f(...)
diff --git a/build/vignette.rds b/build/vignette.rds
new file mode 100644
index 0000000..e4c1648
Binary files /dev/null and b/build/vignette.rds differ
diff --git a/demo/00Index b/demo/00Index
new file mode 100755
index 0000000..55c7719
--- /dev/null
+++ b/demo/00Index
@@ -0,0 +1,2 @@
+registry Demo for the registry package
+
diff --git a/demo/registry.R b/demo/registry.R
new file mode 100755
index 0000000..c4d2b9f
--- /dev/null
+++ b/demo/registry.R
@@ -0,0 +1,67 @@
+##########################
+### registry test instances
+
+library(registry)
+
+.my_check_fun <- function(x) if (x$Z == 999 && x$New2 == 999) stop("No evil allowed!")
+
+## create registry
+R <- registry(entry_class = "simple.list",
+ validity_FUN = .my_check_fun)
+R
+
+## set fields
+R$set_field("names", type = "character", is_key = TRUE)
+R$set_field("X", type = TRUE, is_mandatory = TRUE)
+R$set_field("Y", type = "character")
+R$set_field("Z", default = 123)
+R$get_fields()
+
+## add entries
+R$set_entry(names = c("test", "bar"), X = TRUE, Y = "bla")
+R$set_entry(names = "test2", X = FALSE, Y = "foo", Z = 99)
+R$set_entry(names = "test3", X = FALSE, Y = "bar", Z = "chars")
+R$get_entry("test")
+R[["test2"]]
+R[["test3"]]
+R[["bar"]]
+
+## add new field
+R$set_field("New")
+R$get_field("New")
+
+## change entries
+R$modify_entry(names = "test", New = 123)
+R$modify_entry(names = "test2", New = "test")
+
+## field check function (checks for strict positive values)
+R$set_field("New2", type = "numeric", validity_FUN = function(x) stopifnot(x > 0))
+R$set_entry(names = "test5", X = TRUE, New2 = 2)
+
+## add field with fixed alternatives
+R$set_field("New3", type = "character", alternatives = c("A", "B"))
+R$get_field("New")
+R$set_entry(names = "test6", X = TRUE, New3 = "A")
+
+## print/summary = as.data.frame
+R
+summary(R)
+
+## seal entries
+R$seal_entries()
+R$set_field("New4")
+R$set_entry(names = "test7", X = TRUE, Y = "bla")
+R$delete_entry("test7")
+R$modify_entry(names = "test", New4 = "test")
+
+## error cases:
+TRY <- function(...) stopifnot(inherits(try(..., silent = TRUE), "try-error"))
+TRY(R$set_field("bla", type = "character", default = 123))
+TRY(R$set_entry("err1", Y = "bla"))
+TRY(R$set_entry("err2", X = "bla"))
+TRY(R$set_entry("err3", X = TRUE, New2 = -2))
+TRY(R$set_entry("err4", X = TRUE, Z = 999, New2 = 999))
+TRY(R$set_entry("err5", X = TRUE, New3 = "C"))
+TRY(R$modify_entry("Bla", "New", 123))
+TRY(R$modify_entry("X", "Bla", 123))
+TRY(R$modify_entry("test","X",TRUE))
diff --git a/inst/doc/registry.R b/inst/doc/registry.R
new file mode 100644
index 0000000..a2d7424
--- /dev/null
+++ b/inst/doc/registry.R
@@ -0,0 +1,207 @@
+### R code from vignette source 'registry.Rnw'
+### Encoding: UTF-8
+
+###################################################
+### code chunk number 1: registry.Rnw:56-58
+###################################################
+options(width = 80)
+library("registry")
+
+
+###################################################
+### code chunk number 2: registry.Rnw:122-125
+###################################################
+library(registry)
+R <- registry()
+print(R)
+
+
+###################################################
+### code chunk number 3: registry.Rnw:141-142
+###################################################
+checkAge <- function(x) stopifnot(is.na(x) || x > 0 && x < 100)
+
+
+###################################################
+### code chunk number 4: registry.Rnw:147-148
+###################################################
+checkPhone <- function(x) stopifnot(!is.na(x$mobile) || !is.na(x$home))
+
+
+###################################################
+### code chunk number 5: registry.Rnw:153-155
+###################################################
+R <- registry(registry_class = "Addressbook", entry_class = "Address",
+ validity_FUN = checkPhone)
+
+
+###################################################
+### code chunk number 6: registry.Rnw:158-164
+###################################################
+print.Addressbook <-
+function(x, ...) {
+ writeLines(sprintf("An address book with %i entries.\n", length(x)))
+ invisible(x)
+}
+print(R)
+
+
+###################################################
+### code chunk number 7: registry.Rnw:170-172
+###################################################
+R$set_field("last", type = "character", is_key = TRUE, index_FUN = match_partial_ignorecase)
+R$set_field("first", type = "character", is_key = TRUE, index_FUN = match_partial_ignorecase)
+
+
+###################################################
+### code chunk number 8: registry.Rnw:175-176
+###################################################
+R$set_field("address", type = "character")
+
+
+###################################################
+### code chunk number 9: registry.Rnw:181-183
+###################################################
+R$set_field("mobile", type = "character")
+R$set_field("home", type = "character")
+
+
+###################################################
+### code chunk number 10: registry.Rnw:187-188
+###################################################
+R$set_field("age", type = "integer", validity_FUN = checkAge)
+
+
+###################################################
+### code chunk number 11: registry.Rnw:192-195
+###################################################
+R$set_field("type", type = "character",
+ alternatives = c("Business", "Private"),
+ default = "Business")
+
+
+###################################################
+### code chunk number 12: registry.Rnw:198-199
+###################################################
+R$get_field("type")
+
+
+###################################################
+### code chunk number 13: registry.Rnw:206-210
+###################################################
+R$set_entry(last = "Smith", first = "Mary", address = "Vienna",
+ home = "734 43 34", type = "Private", age = 44L)
+R$set_entry(last = "Smith", first = "Peter", address = "New York",
+ mobile = "878 78 87")
+
+
+###################################################
+### code chunk number 14: registry.Rnw:213-215
+###################################################
+R$set_entry("Myers", "John", "Washington", "52 32 34", "898 89 99",
+ 33L, "Business")
+
+
+###################################################
+### code chunk number 15: registry.Rnw:218-223
+###################################################
+TRY <- function(expr) tryCatch(expr, error = print)
+TRY(R$set_entry(last = "Smith", first = "Mary"))
+TRY(R$set_entry(last = "Miller", first = "Henry"))
+TRY(R$set_entry(last = "Miller", first = "Henry", age = 12.5))
+TRY(R$set_entry(last = "Miller", first = "Henry", age = 999L))
+
+
+###################################################
+### code chunk number 16: registry.Rnw:226-227
+###################################################
+R$get_entry(last = "Smith", first = "mar")
+
+
+###################################################
+### code chunk number 17: registry.Rnw:231-234
+###################################################
+print.Address <- function(x) with(x,
+ writeLines(sprintf("%s %s, %s; home: %s, mobile: %s; age: %i (%s)", first, last, address, home, mobile, age, type)))
+R$get_entry(last = "Smith", first = "mar")
+
+
+###################################################
+### code chunk number 18: registry.Rnw:241-242
+###################################################
+R[["Myers"]]
+
+
+###################################################
+### code chunk number 19: registry.Rnw:246-248
+###################################################
+R$set_entry(last = "Frears", first = c("Joe", "Jonathan"),
+ address = "Washington", home = "721 42 34")
+
+
+###################################################
+### code chunk number 20: registry.Rnw:251-252
+###################################################
+identical(R[["Frears", "Jonathan"]], R[["Frears", "Joe"]])
+
+
+###################################################
+### code chunk number 21: registry.Rnw:258-259
+###################################################
+R$get_entries("Smith")
+
+
+###################################################
+### code chunk number 22: registry.Rnw:262-263
+###################################################
+R$grep_entries("Priv")
+
+
+###################################################
+### code chunk number 23: registry.Rnw:266-268 (eval = FALSE)
+###################################################
+## R$get_entries()
+## R[]
+
+
+###################################################
+### code chunk number 24: registry.Rnw:271-272
+###################################################
+summary(R)
+
+
+###################################################
+### code chunk number 25: registry.Rnw:276-279
+###################################################
+R[["Smith", "Peter"]]
+R$modify_entry(last = "Smith", first = "Peter", age = 22L)
+R[["Smith", "Peter"]]
+
+
+###################################################
+### code chunk number 26: registry.Rnw:282-284
+###################################################
+R$delete_entry(last = "Smith", first = "Peter")
+R[["Smith", "Peter"]]
+
+
+###################################################
+### code chunk number 27: registry.Rnw:294-299
+###################################################
+R$seal_entries()
+TRY(R$delete_entry("Smith", "Mary"))
+R$set_entry(last = "Slater", first = "Christian", address = "Boston",
+ mobile = "766 23 88")
+R[["Slater"]]
+
+
+###################################################
+### code chunk number 28: registry.Rnw:302-307
+###################################################
+R$get_permissions()
+R$restrict_permissions(delete_entries = FALSE)
+TRY(R$delete_entry("Slater"))
+R$modify_entry(last = "Slater", first = "Christian", age = 44L)
+R[["Slater"]]
+
+
diff --git a/inst/doc/registry.Rnw b/inst/doc/registry.Rnw
new file mode 100755
index 0000000..aa04475
--- /dev/null
+++ b/inst/doc/registry.Rnw
@@ -0,0 +1,313 @@
+\documentclass[a4paper]{article}
+\usepackage[round,longnamesfirst]{natbib}
+\usepackage{graphicx,keyval,thumbpdf,a4wide,makeidx,color,colordvi}
+\usepackage{amsfonts,hyperref}
+\usepackage[utf8]{inputenc}
+\DeclareUnicodeCharacter{201C}{"}
+\DeclareUnicodeCharacter{201D}{"}
+
+\newcommand\R{\textsf{R}}
+\newcommand{\pkg}[1]{{\normalfont\fontseries{b}\selectfont #1}}
+\newcommand{\sQuote}[1]{`{#1}'}
+\newcommand{\dQuote}[1]{``{#1}''}
+\newcommand{\file}[1]{\sQuote{\textsf{#1}}}
+\newcommand{\data}[1]{\texttt{#1}}
+\newcommand{\var}[1]{\textit{#1}}
+\newcommand{\class}[1]{\textsf{#1}}
+\newcommand{\proglang}[1]{\textsf{#1}}
+%% \code without `-' ligatures
+\def\nohyphenation{\hyphenchar\font=-1 \aftergroup\restorehyphenation}
+\def\restorehyphenation{\hyphenchar\font=`-}
+{\catcode`\-=\active%
+ \global\def\code{\bgroup%
+ \catcode`\-=\active \let-\codedash%
+ \Rd at code}}
+\def\codedash{-\discretionary{}{}{}}
+\def\Rd at code#1{\texttt{\nohyphenation#1}\egroup}
+\newcommand{\codefun}[1]{\code{#1()}}
+\newcommand{\codefunind}[1]{\codefun{#1}\index{\texttt{#1}}}
+\newcommand{\codeind}[1]{\code{#1}\index{\texttt{#1}}}
+
+\SweaveOpts{strip.white=true}
+
+\definecolor{Blue}{rgb}{0,0,0.8}
+\definecolor{Red}{rgb}{0.7,0,0}
+
+\date{2009-02-17}
+\title{A Generic Registry Infrastructure for \R}
+\author{David Meyer}
+%\VignetteIndexEntry{Registry}
+%\VignetteDepends{registry}
+%\VignetteKeywords{registry}
+%\VignettePackage{registry}
+
+\makeindex{}
+
+\sloppy{}
+
+\begin{document}
+\maketitle
+
+% \begin{abstract}
+% This document introduces a generic registry infrastructure for \R,
+% provided by the \pkg{registry} package.
+% \end{abstract}
+
+<<echo=FALSE>>=
+options(width = 80)
+library("registry")
+@ %
+
+\section{Introduction}
+\label{sec:introduction}
+
+More and more, \R~packages are offering dynamic functionality,
+allowing users to extend a \dQuote{repository} of initial features or
+data. For example, the \pkg{proxy} package \citep{registry:meyer+buchta:2008}
+provides an enhanced
+\codefun{dist} function for computing dissimilarity matrices,
+allowing to choose among several proximity
+measures stored in a registry. Each entry is composed of a small
+workhorse function and some meta data including, e.g., a character vector
+of aliases, literature references, the formula in plain text,
+a function to coerce
+between similarity and distance, and a type categorization
+(binary, metric, etc.). Users can add new proximity measures to the
+registry at run-time and immediately use them without recreating the
+package, specifying one of the aliases defined in the meta data.
+Similarly, the \pkg{relations} \citep{registry:hornik+meyer:2008}
+and \pkg{CLUE} \citep{registry:hornik:2005,registry:hornik:2007}
+packages use simple
+registries internally to link some meta data to available functions,
+used by the high-level consensus ranking and cluster ensemble
+functions, respectively.
+
+Such a registry, whether exposed to the user or not, is conceptually a
+small in-memory data base where entries with a common field structure are
+stored and retrieved and whose fields can be of mixed type.
+At first sight, a data frame seems to be the
+data structure of choice for an appropriate implementation.
+Unfortunately, data frames are inconvenient to use
+with factors, functions, or other recursive types such as lists
+due to automatic coercions taking place behind the scenes. In fact, a
+simpler, record-like structure such as a list with named components
+(\dQuote{fields}) appears more practical. Also,
+features known from \dQuote{real} data bases such as compound keys,
+validity checking of new entries, and use of access rights are not
+available by default and need to be \dQuote{reinvented} every time
+they are needed.
+
+The \pkg{registry} package provides a simple mechanism for defining
+and manipulating user-extensible registry objects. A typical
+use case in the context of an \R~package could include the following steps:
+
+\begin{enumerate}
+\item Create one or more registry objects inside the package's namespace.
+\item Insert entries to the registry.
+\item Possibly, \dQuote{seal} the entries and set access rights.
+\item Possibly, export the registry object to the user level.
+\item Browse and retrieve entries from the registry.
+\end{enumerate}
+
+In the following, we explain these steps in more detail:
+first, how a registry can be set up; second, how entries
+can be added, modified and retrieved; and third, how a registry can be
+sealed and restricted through the definition of access rights.
+
+\section{Creating Registries}
+
+A registry basically is a container (implemented in \R~as an
+environment), along with some access functions. A new object of class
+\code{registry} can simply be created using the \codefun{registry} function:
+<<>>=
+library(registry)
+R <- registry()
+print(R)
+@
+Optional parameters include the specification of an (additional) class
+for the created registry object and the individual entries,
+as well as the specification of some validity function checking new
+entries to be added to the registry.
+
+In the following, we will use the example of a simple address book,
+whose entries include first and last name, address, age, home/cell
+phone number, and a business/private classification.
+Last and first name build the search key. Age is an
+optional integer in the range of 1 and
+99. Additionally, at least one phone number should be added to the registry.
+
+We start by creating two simple validity functions. The first one, to
+be specified at field level later on, checks a given age:
+<<>>=
+checkAge <- function(x) stopifnot(is.na(x) || x > 0 && x < 100)
+@
+The second one, specified at registry level,
+checks whether a given registry entry (list of named components)
+contains at least one phone number:
+<<>>=
+checkPhone <- function(x) stopifnot(!is.na(x$mobile) || !is.na(x$home))
+@
+Next, we create a registry of class \code{Addressbook} (inheriting
+from \code{registry}), containing entries of class \code{Address} and
+using the above validity function.
+<<>>=
+R <- registry(registry_class = "Addressbook", entry_class = "Address",
+ validity_FUN = checkPhone)
+@
+The additional class for the registry allows, e.g., user-defined printing:
+<<>>=
+print.Addressbook <-
+function(x, ...) {
+ writeLines(sprintf("An address book with %i entries.\n", length(x)))
+ invisible(x)
+}
+print(R)
+@
+
+At this stage, we are ready to set up the field information. First and last
+names are mandatory character fields, uniquely identifying an entry
+(key fields). Lookups should work with partial completion, ignoring case:
+<<>>=
+R$set_field("last", type = "character", is_key = TRUE, index_FUN = match_partial_ignorecase)
+R$set_field("first", type = "character", is_key = TRUE, index_FUN = match_partial_ignorecase)
+@
+The address is also character, but optional:
+<<>>=
+R$set_field("address", type = "character")
+@
+At least one phone number (character) is required. This can be
+achieved by making them optional, and using the validity
+function specified at the registry level to check whether one of them is empty:
+<<>>=
+R$set_field("mobile", type = "character")
+R$set_field("home", type = "character")
+@
+The age field is an optional integer with a defined range, checked by
+the field-level validity function:
+<<>>=
+R$set_field("age", type = "integer", validity_FUN = checkAge)
+@
+Finally, the business/private category is defined by specifying the
+possible alternatives (\code{Business} is set as default):
+<<>>=
+R$set_field("type", type = "character",
+ alternatives = c("Business", "Private"),
+ default = "Business")
+@
+The setup for a field can be retrieved using \codefun{get\_field}:
+<<>>=
+R$get_field("type")
+@
+\codefun{get\_fields} returns the complete list.
+
+\section{Using Registries}
+
+We now can start adding entries to the registry:
+<<>>=
+R$set_entry(last = "Smith", first = "Mary", address = "Vienna",
+ home = "734 43 34", type = "Private", age = 44L)
+R$set_entry(last = "Smith", first = "Peter", address = "New York",
+ mobile = "878 78 87")
+@
+If all field values are specified, the field names can be omitted:
+<<>>=
+R$set_entry("Myers", "John", "Washington", "52 32 34", "898 89 99",
+ 33L, "Business")
+@
+Duplicate or invalid entries are not accepted:
+<<>>=
+TRY <- function(expr) tryCatch(expr, error = print)
+TRY(R$set_entry(last = "Smith", first = "Mary"))
+TRY(R$set_entry(last = "Miller", first = "Henry"))
+TRY(R$set_entry(last = "Miller", first = "Henry", age = 12.5))
+TRY(R$set_entry(last = "Miller", first = "Henry", age = 999L))
+@
+A single entry can be retrieved using \codefun{get\_entry}:
+<<>>=
+R$get_entry(last = "Smith", first = "mar")
+@
+Since returned entries inherit from \code{Address}, we can provide a
+user-defined print method:
+<<>>=
+print.Address <- function(x) with(x,
+ writeLines(sprintf("%s %s, %s; home: %s, mobile: %s; age: %i (%s)", first, last, address, home, mobile, age, type)))
+R$get_entry(last = "Smith", first = "mar")
+@
+Note that even though
+the first name of Mary Smith is incompletely specified and in
+lower case, the lookup is still successful
+because of the partial matching indexing function. The \code{[[} operator
+can be used as an alternative to \codefun{get\_entry}:
+<<>>=
+R[["Myers"]]
+@
+For Myers, the last name uniquely identifies the entry, so
+the first name can be omitted. Key values can have alternative values:
+<<>>=
+R$set_entry(last = "Frears", first = c("Joe", "Jonathan"),
+ address = "Washington", home = "721 42 34")
+@
+Either of them can be used for retrieval:
+<<>>=
+identical(R[["Frears", "Jonathan"]], R[["Frears", "Joe"]])
+@
+Unsuccessful lookups result in a
+return of \code{NULL}. Multiple entries can be retrieved
+using the \codefun{get\_entries} accessing function. They are returned
+in a list whose component names are generated from the key values:
+<<>>=
+R$get_entries("Smith")
+@
+Full-text search in all information is provided by \codefun{grep\_entries}:
+<<>>=
+R$grep_entries("Priv")
+@
+A list of all entries can be obtained using either of:
+<<eval=FALSE>>=
+R$get_entries()
+R[]
+@
+The summary method for registry objects returns a data frame:
+<<>>=
+summary(R)
+@
+Entries can also be modified using \codefun{modify\_entry}, specifying
+key and new field values:
+<<>>=
+R[["Smith", "Peter"]]
+R$modify_entry(last = "Smith", first = "Peter", age = 22L)
+R[["Smith", "Peter"]]
+@
+Finally, entries can be removed using \codefun{delete\_entry}:
+<<>>=
+R$delete_entry(last = "Smith", first = "Peter")
+R[["Smith", "Peter"]]
+@
+
+\section{Sealing Registries and Setting Access Rights}
+
+Occasionally, developers might want to protect a registry that ships
+with some package to prevent accidental deletions or
+alterations. For this, \pkg{registry} offers two mechanisms: first, a
+registry object can be \dQuote{sealed} to prevent modifications of
+\emph{existing} data:
+<<>>=
+R$seal_entries()
+TRY(R$delete_entry("Smith", "Mary"))
+R$set_entry(last = "Slater", first = "Christian", address = "Boston",
+ mobile = "766 23 88")
+R[["Slater"]]
+@
+Second, the access permissions for registries can be restricted:
+<<>>=
+R$get_permissions()
+R$restrict_permissions(delete_entries = FALSE)
+TRY(R$delete_entry("Slater"))
+R$modify_entry(last = "Slater", first = "Christian", age = 44L)
+R[["Slater"]]
+@
+
+\bibliographystyle{abbrvnat}
+\bibliography{registry}
+
+\end{document}
diff --git a/inst/doc/registry.pdf b/inst/doc/registry.pdf
new file mode 100755
index 0000000..66a2958
Binary files /dev/null and b/inst/doc/registry.pdf differ
diff --git a/man/matchfuns.Rd b/man/matchfuns.Rd
new file mode 100755
index 0000000..6b33566
--- /dev/null
+++ b/man/matchfuns.Rd
@@ -0,0 +1,65 @@
+\name{matchfuns}
+\alias{matchfuns}
+\alias{match_ignorecase}
+\alias{match_exact}
+\alias{match_partial}
+\alias{match_partial_ignorecase}
+\alias{match_regexp}
+\title{Matching functions}
+\description{
+ Functions used for lookups of search keys.
+}
+\usage{
+match_ignorecase(lookup, entry, \dots)
+match_exact(lookup, entry, \dots)
+match_partial(lookup, entry, \dots)
+match_partial_ignorecase(lookup, entry, \dots)
+match_regexp(lookup, entry, \dots)
+}
+\arguments{
+ \item{lookup}{Search value to look up (for some key field).}
+ \item{entry}{Vector of key values where \code{lookup} is sought.}
+ \item{\dots}{For \code{match_ignorecase} and \code{match_exact}:
+ currently not used. For \code{match_partial} and
+ \code{match_partial_ignorecase}: additional arguments passed to
+ \code{\link[base]{pmatch}}. For \code{match_regexp}: additional
+ arguments passed to \code{\link[base]{grep}}.}
+}
+\details{
+ These are matching functions to be specified for key fields,
+ controlling how search values are looked up in the registry.
+}
+\author{David Meyer \email{David.Meyer at R-project.org}}
+
+\seealso{\code{\link{regobj}}}
+
+\examples{
+## use exact matching
+R <- registry(stop_if_missing = FALSE)
+R$set_field("Key", type = "character", is_key = TRUE, index_FUN = match_exact)
+R$set_field("Value", type = "numeric")
+R$set_entry("the key", 1)
+
+R[["the key"]]
+R[["the"]]
+
+## use partial matching
+R <- registry()
+R$set_field("Key", type = "character", is_key = TRUE, index_FUN = match_partial)
+R$set_field("Value", type = "numeric")
+R$set_entry("the key", 1)
+
+R[["the"]]
+
+## use regular expressions
+R <- registry()
+R$set_field("Key", type = "character", is_key = TRUE, index_FUN = match_regexp)
+R$set_field("Value", type = "numeric")
+R$set_entry("the key", 1)
+R$set_entry("key", 2)
+
+R[["k.*"]]
+R["k.*"]
+
+}
+\keyword{data}
\ No newline at end of file
diff --git a/man/registry.Rd b/man/registry.Rd
new file mode 100755
index 0000000..6145b6d
--- /dev/null
+++ b/man/registry.Rd
@@ -0,0 +1,53 @@
+\name{registry}
+\alias{registry}
+\title{Registry creator}
+\description{
+ Function to create a registry object.
+}
+\usage{
+registry(entry_class = NULL, registry_class = NULL,
+ validity_FUN = NULL, stop_if_missing = FALSE)
+}
+
+\arguments{
+ \item{entry_class}{character string indicating a class the
+ returned registry object will additionally inherit from
+ (optional). Used for dispatching user-specified print and summary methods.}
+ \item{registry_class}{character string indicating a class the
+ registry entries will additionally inherit from (optional).
+ Used for dispatching user-specified print and summary methods.}
+ \item{validity_FUN}{a function accepting a new registry entry as argument for
+ checking its validity and possibly aborting with an error
+ message. The entry will be provided by the calling function as a
+ list with named components (fields).}
+ \item{stop_if_missing}{logical indicating whether the registry lookup
+ functions should abort or just return \code{NULL} in case of no
+ match.}
+}
+\details{
+ This is a generating function that will return a registry object whose
+ components are accessor functions for the contained data. These are
+ documented separately (\code{\link{regobj}}).
+}
+\author{David Meyer \email{David.Meyer at R-project.org}}
+
+\seealso{\code{\link{regobj}}}
+
+\examples{
+R <- registry()
+
+R$set_field("X", type = TRUE)
+R$set_field("Y", type = "character")
+R$set_field("index", type = "character", is_key = TRUE,
+ index_FUN = match_partial_ignorecase)
+R$set_field("index2", type = "integer", is_key = TRUE)
+
+R$set_entry(X = TRUE, Y = "bla", index = "test", index2 = 1L)
+R$set_entry(X = FALSE, Y = "foo", index = c("test", "bar"), index2 = 2L)
+
+R$get_entries("test")
+R[["test", 1]]
+R["test"]
+R[["test"]]
+}
+\keyword{data}
\ No newline at end of file
diff --git a/man/regobj.Rd b/man/regobj.Rd
new file mode 100755
index 0000000..fd2cbb3
--- /dev/null
+++ b/man/regobj.Rd
@@ -0,0 +1,146 @@
+\name{regobj}
+\alias{regobj}
+\alias{summary.registry}
+\alias{print.registry}
+\alias{[.registry}
+\alias{[[.registry}
+
+%- Also NEED an '\alias' for EACH other topic documented here.
+\title{Registry object}
+\description{
+ Registry object.
+}
+\usage{
+\special{regobj$get_field(name)}
+\special{regobj$get_fields()}
+\special{regobj$get_field_names()}
+\special{regobj$set_field(name,
+ type = NA, alternatives = NA, default = NA,
+ is_mandatory = FALSE, is_modifiable = TRUE,
+ is_key = FALSE, validity_FUN = NULL,
+ index_FUN = match_ignorecase, \dots)}
+
+\special{regobj$has_entry(key)}
+\special{regobj$get_entry(\dots)}
+\special{regobj$get_entries(\dots)}
+\special{regobj$grep_entries(pattern, \dots)}
+\special{regobj$get_entry_names()}
+\special{regobj$set_entry(\dots)}
+\special{regobj$modify_entry(\dots)}
+\special{regobj$delete_entry(\dots)}
+\special{regobj$n_of_entries(name)}
+\special{regobj$get_field_entries(field, unlist = TRUE)}
+
+\special{regobj$get_permissions()}
+\special{regobj$restrict_permissions(set_entries = TRUE,
+ modify_entries = TRUE, delete_entries = TRUE, set_fields = TRUE)}
+\special{regobj$seal_entries()}
+\special{regobj$get_sealed_field_names()}
+
+
+\method{print}{registry}(x, \dots)
+\method{summary}{registry}(object, \dots)
+
+\method{[[}{registry}(x, \dots)
+\method{[}{registry}(x, \dots)
+}
+%- maybe also 'usage' for other objects documented here.
+\arguments{
+ \item{name}{character string representing the name of an
+ entry (case-insensitive).}
+ \item{pattern}{regular expression to be matched to all fields of class
+ \code{"character"} in all entries.}
+ \item{type}{character vector specifying accepted classes
+ for this field. If \code{NA} (default), any class will be accepted.
+ If \code{type} is not a character vector, the
+ class will be inferred from the argument given.}
+ \item{alternatives}{vector of alternatives accepted for this field.}
+ \item{default}{optional default value for the field.}
+ \item{is_mandatory}{logical specifying whether new entries are required
+ to have a value for this field.}
+ \item{is_modifiable}{logical specifying whether entries can be changed
+ with respect to that field.}
+ \item{is_key}{logical indicating whether the field is (part of) an
+ index.}
+ \item{validity_FUN}{optional function or character string with the name of a
+ function that checks the validity of a field entry. Such a function
+ gets the value to be investigated as argument, and should stop with an
+ error message if the value is not correct.}
+ \item{index_FUN}{vectorized predicate function matching an
+ index value to a vector (of existing field entries). See \link{matchfuns}.}
+ \item{x, object}{a registry object.}
+ \item{\dots}{for \code{regobj$set_entry} and \code{regobj$modify_entry}:
+ named list of fields to be modified in or added to the registry,
+ including the index field(s) (see details).
+ For \code{grep_entries}: additional parameters passed to
+ \code{\link[base]{grep}}.
+ For \code{set_field}: additional parameters passed to the specified
+ match function.
+ For \code{get_entry}, \code{get_entries}
+ and the indexing functions: key values for the entry (entries)
+ to be looked up.}
+}
+\details{
+ \code{regobj} represents a registry object returned by
+ \code{\link{registry}} whose elements can be processed using
+ the following accessor functions:
+
+ \code{get_field_names()} returns a character
+ vector with all field names. \code{get_field()} returns the information
+ for a specific field as a list with components named as described
+ above. \code{get_fields()} returns a list with all field
+ entries. \code{set_field()} is used to create new fields in the
+ repository (the default value will be set in all
+ entries).
+
+ \code{get_entry_names()} returns a character vector with (the first
+ alias of) all entries. \code{entry_exists()} is a predicate checking
+ if an entry with the specified alias exists in the
+ registry. \code{get_entry()} returns the first specified entry
+ if at least one exists (and, by
+ default, gives an error if it does not). \code{get_entries()} is used to
+ query more than one entry matching the index
+ (named argument list) exactly. \code{grep_entries()} returns those entries
+ where the regular expression in \code{pattern} matches \emph{any}
+ character field in an entry. By default, all values are
+ returned. \code{delete_entry} removes an existing entry from the
+ registry.
+
+ \code{set_entry}, \code{delete_entry} and \code{modify_entry}
+ require a named list
+ of arguments used as field entries.
+ At least the index fields are required.
+
+ \code{set_entry}
+ will check for all other mandatory fields. If specified in the field
+ meta data, each field entry and the entry as a whole is checked for
+ validity. Note that it is possible to specify a vector of values for
+ an index field, treated as alternative keys for this entry.
+
+ It is possible to \emph{restrict} permissions (for setting, getting, deleting
+ and modifying entries) using \code{restrict_permissions}. Further, a
+ user can \emph{seal} the current registry state (fields, entries) so
+ that \emph{existing} structure and information becomes
+ immutable. Additional fields and entries can be changed according to the
+ permissions set. Permissions and sealing are useful for exported
+ registry objects to control the users' capabilities of
+ modifying/extending them.
+}
+\author{David Meyer \email{David.Meyer at R-project.org}}
+
+\seealso{\code{\link{dist}}, \link{matchfuns}}
+\examples{
+regobj <- registry()
+regobj$set_field("X", type = TRUE)
+regobj$set_field("Y", type = "character")
+regobj$set_field("index", type = "character", is_key = TRUE,
+ index_FUN = match_partial_ignorecase)
+regobj$set_field("index2", type = "integer", is_key = TRUE)
+regobj$set_entry(X = TRUE, Y = "bla", index = "test", index2 = 1L)
+regobj$set_entry(X = FALSE, Y = "foo", index = c("test", "bar"), index2 = 2L)
+regobj$get_entries("test")
+regobj[["test", 1]]
+regobj["test"]
+regobj[["test"]]
+}
+\keyword{data}% __ONLY ONE__ keyword per line
diff --git a/tests/registry.R b/tests/registry.R
new file mode 100755
index 0000000..265f3f5
--- /dev/null
+++ b/tests/registry.R
@@ -0,0 +1,2 @@
+library(registry)
+demo("registry")
diff --git a/vignettes/abbrvnat.bst b/vignettes/abbrvnat.bst
new file mode 100755
index 0000000..2068b0f
--- /dev/null
+++ b/vignettes/abbrvnat.bst
@@ -0,0 +1,1432 @@
+%% File: `abbrvnat.bst'
+%% A modification of `abbrv.bst' for use with natbib package
+%%
+%% Copyright 1993-2005 Patrick W Daly
+%% Max-Planck-Institut f\"ur Sonnensystemforschung
+%% Max-Planck-Str. 2
+%% D-37191 Katlenburg-Lindau
+%% Germany
+%% E-mail: daly at mps.mpg.de
+%%
+%% This program can be redistributed and/or modified under the terms
+%% of the LaTeX Project Public License Distributed from CTAN
+%% archives in directory macros/latex/base/lppl.txt; either
+%% version 1 of the License, or any later version.
+%%
+ % Version and source file information:
+ % \ProvidesFile{natbst.mbs}[2005/01/07 1.8 (PWD)]
+ %
+ % BibTeX `plainnat' family
+ % version 0.99b for BibTeX versions 0.99a or later,
+ % for LaTeX versions 2.09 and 2e.
+ %
+ % For use with the `natbib.sty' package; emulates the corresponding
+ % member of the `plain' family, but with author-year citations.
+ %
+ % With version 6.0 of `natbib.sty', it may also be used for numerical
+ % citations, while retaining the commands \citeauthor, \citefullauthor,
+ % and \citeyear to print the corresponding information.
+ %
+ % For version 7.0 of `natbib.sty', the KEY field replaces missing
+ % authors/editors, and the date is left blank in \bibitem.
+ %
+ % Includes field EID for the sequence/citation number of electronic journals
+ % which is used instead of page numbers.
+ %
+ % Includes fields ISBN and ISSN.
+ %
+ % Includes field URL for Internet addresses.
+ %
+ % Includes field DOI for Digital Object Idenfifiers.
+ %
+ % Works best with the url.sty package of Donald Arseneau.
+ %
+ % Works with identical authors and year are further sorted by
+ % citation key, to preserve any natural sequence.
+ %
+ENTRY
+ { address
+ author
+ booktitle
+ chapter
+ doi
+ eid
+ edition
+ editor
+ howpublished
+ institution
+ isbn
+ issn
+ journal
+ key
+ month
+ note
+ number
+ organization
+ pages
+ publisher
+ school
+ series
+ title
+ type
+ url
+ volume
+ year
+ }
+ {}
+ { label extra.label sort.label short.list }
+
+INTEGERS { output.state before.all mid.sentence after.sentence after.block }
+
+FUNCTION {init.state.consts}
+{ #0 'before.all :=
+ #1 'mid.sentence :=
+ #2 'after.sentence :=
+ #3 'after.block :=
+}
+
+STRINGS { s t }
+
+FUNCTION {output.nonnull}
+{ 's :=
+ output.state mid.sentence =
+ { ", " * write$ }
+ { output.state after.block =
+ { add.period$ write$
+ newline$
+ "\newblock " write$
+ }
+ { output.state before.all =
+ 'write$
+ { add.period$ " " * write$ }
+ if$
+ }
+ if$
+ mid.sentence 'output.state :=
+ }
+ if$
+ s
+}
+
+FUNCTION {output}
+{ duplicate$ empty$
+ 'pop$
+ 'output.nonnull
+ if$
+}
+
+FUNCTION {output.check}
+{ 't :=
+ duplicate$ empty$
+ { pop$ "empty " t * " in " * cite$ * warning$ }
+ 'output.nonnull
+ if$
+}
+
+FUNCTION {fin.entry}
+{ add.period$
+ write$
+ newline$
+}
+
+FUNCTION {new.block}
+{ output.state before.all =
+ 'skip$
+ { after.block 'output.state := }
+ if$
+}
+
+FUNCTION {new.sentence}
+{ output.state after.block =
+ 'skip$
+ { output.state before.all =
+ 'skip$
+ { after.sentence 'output.state := }
+ if$
+ }
+ if$
+}
+
+FUNCTION {not}
+{ { #0 }
+ { #1 }
+ if$
+}
+
+FUNCTION {and}
+{ 'skip$
+ { pop$ #0 }
+ if$
+}
+
+FUNCTION {or}
+{ { pop$ #1 }
+ 'skip$
+ if$
+}
+
+FUNCTION {new.block.checka}
+{ empty$
+ 'skip$
+ 'new.block
+ if$
+}
+
+FUNCTION {new.block.checkb}
+{ empty$
+ swap$ empty$
+ and
+ 'skip$
+ 'new.block
+ if$
+}
+
+FUNCTION {new.sentence.checka}
+{ empty$
+ 'skip$
+ 'new.sentence
+ if$
+}
+
+FUNCTION {new.sentence.checkb}
+{ empty$
+ swap$ empty$
+ and
+ 'skip$
+ 'new.sentence
+ if$
+}
+
+FUNCTION {field.or.null}
+{ duplicate$ empty$
+ { pop$ "" }
+ 'skip$
+ if$
+}
+
+FUNCTION {emphasize}
+{ duplicate$ empty$
+ { pop$ "" }
+ { "\emph{" swap$ * "}" * }
+ if$
+}
+
+INTEGERS { nameptr namesleft numnames }
+
+FUNCTION {format.names}
+{ 's :=
+ #1 'nameptr :=
+ s num.names$ 'numnames :=
+ numnames 'namesleft :=
+ { namesleft #0 > }
+ { s nameptr "{f.~}{vv~}{ll}{, jj}" format.name$ 't :=
+ nameptr #1 >
+ { namesleft #1 >
+ { ", " * t * }
+ { numnames #2 >
+ { "," * }
+ 'skip$
+ if$
+ t "others" =
+ { " et~al." * }
+ { " and " * t * }
+ if$
+ }
+ if$
+ }
+ 't
+ if$
+ nameptr #1 + 'nameptr :=
+ namesleft #1 - 'namesleft :=
+ }
+ while$
+}
+
+FUNCTION {format.key}
+{ empty$
+ { key field.or.null }
+ { "" }
+ if$
+}
+
+FUNCTION {format.authors}
+{ author empty$
+ { "" }
+ { author format.names }
+ if$
+}
+
+FUNCTION {format.editors}
+{ editor empty$
+ { "" }
+ { editor format.names
+ editor num.names$ #1 >
+ { ", editors" * }
+ { ", editor" * }
+ if$
+ }
+ if$
+}
+
+FUNCTION {format.isbn}
+{ isbn empty$
+ { "" }
+ { new.block "ISBN " isbn * }
+ if$
+}
+
+FUNCTION {format.issn}
+{ issn empty$
+ { "" }
+ { new.block "ISSN " issn * }
+ if$
+}
+
+FUNCTION {format.url}
+{ url empty$
+ { "" }
+ { new.block "URL \url{" url * "}" * }
+ if$
+}
+
+FUNCTION {format.doi}
+{ doi empty$
+ { "" }
+ { new.block "\doi{" doi * "}" * }
+ if$
+}
+
+FUNCTION {format.title}
+{ title empty$
+ { "" }
+ { title "t" change.case$ }
+ if$
+}
+
+FUNCTION {format.full.names}
+{'s :=
+ #1 'nameptr :=
+ s num.names$ 'numnames :=
+ numnames 'namesleft :=
+ { namesleft #0 > }
+ { s nameptr
+ "{vv~}{ll}" format.name$ 't :=
+ nameptr #1 >
+ {
+ namesleft #1 >
+ { ", " * t * }
+ {
+ numnames #2 >
+ { "," * }
+ 'skip$
+ if$
+ t "others" =
+ { " et~al." * }
+ { " and " * t * }
+ if$
+ }
+ if$
+ }
+ 't
+ if$
+ nameptr #1 + 'nameptr :=
+ namesleft #1 - 'namesleft :=
+ }
+ while$
+}
+
+FUNCTION {author.editor.full}
+{ author empty$
+ { editor empty$
+ { "" }
+ { editor format.full.names }
+ if$
+ }
+ { author format.full.names }
+ if$
+}
+
+FUNCTION {author.full}
+{ author empty$
+ { "" }
+ { author format.full.names }
+ if$
+}
+
+FUNCTION {editor.full}
+{ editor empty$
+ { "" }
+ { editor format.full.names }
+ if$
+}
+
+FUNCTION {make.full.names}
+{ type$ "book" =
+ type$ "inbook" =
+ or
+ 'author.editor.full
+ { type$ "proceedings" =
+ 'editor.full
+ 'author.full
+ if$
+ }
+ if$
+}
+
+FUNCTION {output.bibitem}
+{ newline$
+ "\bibitem[" write$
+ label write$
+ ")" make.full.names duplicate$ short.list =
+ { pop$ }
+ { * }
+ if$
+ "]{" * write$
+ cite$ write$
+ "}" write$
+ newline$
+ ""
+ before.all 'output.state :=
+}
+
+FUNCTION {n.dashify}
+{ 't :=
+ ""
+ { t empty$ not }
+ { t #1 #1 substring$ "-" =
+ { t #1 #2 substring$ "--" = not
+ { "--" *
+ t #2 global.max$ substring$ 't :=
+ }
+ { { t #1 #1 substring$ "-" = }
+ { "-" *
+ t #2 global.max$ substring$ 't :=
+ }
+ while$
+ }
+ if$
+ }
+ { t #1 #1 substring$ *
+ t #2 global.max$ substring$ 't :=
+ }
+ if$
+ }
+ while$
+}
+
+FUNCTION {format.date}
+{ year duplicate$ empty$
+ { "empty year in " cite$ * warning$
+ pop$ "" }
+ 'skip$
+ if$
+ month empty$
+ 'skip$
+ { month
+ " " * swap$ *
+ }
+ if$
+ extra.label *
+}
+
+FUNCTION {format.btitle}
+{ title emphasize
+}
+
+FUNCTION {tie.or.space.connect}
+{ duplicate$ text.length$ #3 <
+ { "~" }
+ { " " }
+ if$
+ swap$ * *
+}
+
+FUNCTION {either.or.check}
+{ empty$
+ 'pop$
+ { "can't use both " swap$ * " fields in " * cite$ * warning$ }
+ if$
+}
+
+FUNCTION {format.bvolume}
+{ volume empty$
+ { "" }
+ { "volume" volume tie.or.space.connect
+ series empty$
+ 'skip$
+ { " of " * series emphasize * }
+ if$
+ "volume and number" number either.or.check
+ }
+ if$
+}
+
+FUNCTION {format.number.series}
+{ volume empty$
+ { number empty$
+ { series field.or.null }
+ { output.state mid.sentence =
+ { "number" }
+ { "Number" }
+ if$
+ number tie.or.space.connect
+ series empty$
+ { "there's a number but no series in " cite$ * warning$ }
+ { " in " * series * }
+ if$
+ }
+ if$
+ }
+ { "" }
+ if$
+}
+
+FUNCTION {format.edition}
+{ edition empty$
+ { "" }
+ { output.state mid.sentence =
+ { edition "l" change.case$ " edition" * }
+ { edition "t" change.case$ " edition" * }
+ if$
+ }
+ if$
+}
+
+INTEGERS { multiresult }
+
+FUNCTION {multi.page.check}
+{ 't :=
+ #0 'multiresult :=
+ { multiresult not
+ t empty$ not
+ and
+ }
+ { t #1 #1 substring$
+ duplicate$ "-" =
+ swap$ duplicate$ "," =
+ swap$ "+" =
+ or or
+ { #1 'multiresult := }
+ { t #2 global.max$ substring$ 't := }
+ if$
+ }
+ while$
+ multiresult
+}
+
+FUNCTION {format.pages}
+{ pages empty$
+ { "" }
+ { pages multi.page.check
+ { "pages" pages n.dashify tie.or.space.connect }
+ { "page" pages tie.or.space.connect }
+ if$
+ }
+ if$
+}
+
+FUNCTION {format.eid}
+{ eid empty$
+ { "" }
+ { "art." eid tie.or.space.connect }
+ if$
+}
+
+FUNCTION {format.vol.num.pages}
+{ volume field.or.null
+ number empty$
+ 'skip$
+ { "\penalty0 (" number * ")" * *
+ volume empty$
+ { "there's a number but no volume in " cite$ * warning$ }
+ 'skip$
+ if$
+ }
+ if$
+ pages empty$
+ 'skip$
+ { duplicate$ empty$
+ { pop$ format.pages }
+ { ":\penalty0 " * pages n.dashify * }
+ if$
+ }
+ if$
+}
+
+FUNCTION {format.vol.num.eid}
+{ volume field.or.null
+ number empty$
+ 'skip$
+ { "\penalty0 (" number * ")" * *
+ volume empty$
+ { "there's a number but no volume in " cite$ * warning$ }
+ 'skip$
+ if$
+ }
+ if$
+ eid empty$
+ 'skip$
+ { duplicate$ empty$
+ { pop$ format.eid }
+ { ":\penalty0 " * eid * }
+ if$
+ }
+ if$
+}
+
+FUNCTION {format.chapter.pages}
+{ chapter empty$
+ 'format.pages
+ { type empty$
+ { "chapter" }
+ { type "l" change.case$ }
+ if$
+ chapter tie.or.space.connect
+ pages empty$
+ 'skip$
+ { ", " * format.pages * }
+ if$
+ }
+ if$
+}
+
+FUNCTION {format.in.ed.booktitle}
+{ booktitle empty$
+ { "" }
+ { editor empty$
+ { "In " booktitle emphasize * }
+ { "In " format.editors * ", " * booktitle emphasize * }
+ if$
+ }
+ if$
+}
+
+FUNCTION {empty.misc.check}
+{ author empty$ title empty$ howpublished empty$
+ month empty$ year empty$ note empty$
+ and and and and and
+ key empty$ not and
+ { "all relevant fields are empty in " cite$ * warning$ }
+ 'skip$
+ if$
+}
+
+FUNCTION {format.thesis.type}
+{ type empty$
+ 'skip$
+ { pop$
+ type "t" change.case$
+ }
+ if$
+}
+
+FUNCTION {format.tr.number}
+{ type empty$
+ { "Technical Report" }
+ 'type
+ if$
+ number empty$
+ { "t" change.case$ }
+ { number tie.or.space.connect }
+ if$
+}
+
+FUNCTION {format.article.crossref}
+{ key empty$
+ { journal empty$
+ { "need key or journal for " cite$ * " to crossref " * crossref *
+ warning$
+ ""
+ }
+ { "In \emph{" journal * "}" * }
+ if$
+ }
+ { "In " key * }
+ if$
+ " \citep{" * crossref * "}" *
+}
+
+FUNCTION {format.book.crossref}
+{ volume empty$
+ { "empty volume in " cite$ * "'s crossref of " * crossref * warning$
+ "In "
+ }
+ { "Volume" volume tie.or.space.connect
+ " of " *
+ }
+ if$
+ editor empty$
+ editor field.or.null author field.or.null =
+ or
+ { key empty$
+ { series empty$
+ { "need editor, key, or series for " cite$ * " to crossref " *
+ crossref * warning$
+ "" *
+ }
+ { "\emph{" * series * "}" * }
+ if$
+ }
+ { key * }
+ if$
+ }
+ 'skip$
+ if$
+ ", \citet{" * crossref * "}" *
+}
+
+FUNCTION {format.incoll.inproc.crossref}
+{ editor empty$
+ editor field.or.null author field.or.null =
+ or
+ { key empty$
+ { booktitle empty$
+ { "need editor, key, or booktitle for " cite$ * " to crossref " *
+ crossref * warning$
+ ""
+ }
+ { "In \emph{" booktitle * "}" * }
+ if$
+ }
+ { "In " key * }
+ if$
+ }
+ { "In " }
+ if$
+ " \citet{" * crossref * "}" *
+}
+
+FUNCTION {article}
+{ output.bibitem
+ format.authors "author" output.check
+ author format.key output
+ new.block
+ format.title "title" output.check
+ new.block
+ crossref missing$
+ { journal emphasize "journal" output.check
+ eid empty$
+ { format.vol.num.pages output }
+ { format.vol.num.eid output }
+ if$
+ format.date "year" output.check
+ }
+ { format.article.crossref output.nonnull
+ eid empty$
+ { format.pages output }
+ { format.eid output }
+ if$
+ }
+ if$
+ format.issn output
+ format.doi output
+ format.url output
+ new.block
+ note output
+ fin.entry
+}
+
+FUNCTION {book}
+{ output.bibitem
+ author empty$
+ { format.editors "author and editor" output.check
+ editor format.key output
+ }
+ { format.authors output.nonnull
+ crossref missing$
+ { "author and editor" editor either.or.check }
+ 'skip$
+ if$
+ }
+ if$
+ new.block
+ format.btitle "title" output.check
+ crossref missing$
+ { format.bvolume output
+ new.block
+ format.number.series output
+ new.sentence
+ publisher "publisher" output.check
+ address output
+ }
+ { new.block
+ format.book.crossref output.nonnull
+ }
+ if$
+ format.edition output
+ format.date "year" output.check
+ format.isbn output
+ format.doi output
+ format.url output
+ new.block
+ note output
+ fin.entry
+}
+
+FUNCTION {booklet}
+{ output.bibitem
+ format.authors output
+ author format.key output
+ new.block
+ format.title "title" output.check
+ howpublished address new.block.checkb
+ howpublished output
+ address output
+ format.date output
+ format.isbn output
+ format.doi output
+ format.url output
+ new.block
+ note output
+ fin.entry
+}
+
+FUNCTION {inbook}
+{ output.bibitem
+ author empty$
+ { format.editors "author and editor" output.check
+ editor format.key output
+ }
+ { format.authors output.nonnull
+ crossref missing$
+ { "author and editor" editor either.or.check }
+ 'skip$
+ if$
+ }
+ if$
+ new.block
+ format.btitle "title" output.check
+ crossref missing$
+ { format.bvolume output
+ format.chapter.pages "chapter and pages" output.check
+ new.block
+ format.number.series output
+ new.sentence
+ publisher "publisher" output.check
+ address output
+ }
+ { format.chapter.pages "chapter and pages" output.check
+ new.block
+ format.book.crossref output.nonnull
+ }
+ if$
+ format.edition output
+ format.date "year" output.check
+ format.isbn output
+ format.doi output
+ format.url output
+ new.block
+ note output
+ fin.entry
+}
+
+FUNCTION {incollection}
+{ output.bibitem
+ format.authors "author" output.check
+ author format.key output
+ new.block
+ format.title "title" output.check
+ new.block
+ crossref missing$
+ { format.in.ed.booktitle "booktitle" output.check
+ format.bvolume output
+ format.number.series output
+ format.chapter.pages output
+ new.sentence
+ publisher "publisher" output.check
+ address output
+ format.edition output
+ format.date "year" output.check
+ }
+ { format.incoll.inproc.crossref output.nonnull
+ format.chapter.pages output
+ }
+ if$
+ format.isbn output
+ format.doi output
+ format.url output
+ new.block
+ note output
+ fin.entry
+}
+
+FUNCTION {inproceedings}
+{ output.bibitem
+ format.authors "author" output.check
+ author format.key output
+ new.block
+ format.title "title" output.check
+ new.block
+ crossref missing$
+ { format.in.ed.booktitle "booktitle" output.check
+ format.bvolume output
+ format.number.series output
+ format.pages output
+ address empty$
+ { organization publisher new.sentence.checkb
+ organization output
+ publisher output
+ format.date "year" output.check
+ }
+ { address output.nonnull
+ format.date "year" output.check
+ new.sentence
+ organization output
+ publisher output
+ }
+ if$
+ }
+ { format.incoll.inproc.crossref output.nonnull
+ format.pages output
+ }
+ if$
+ format.isbn output
+ format.doi output
+ format.url output
+ new.block
+ note output
+ fin.entry
+}
+
+FUNCTION {conference} { inproceedings }
+
+FUNCTION {manual}
+{ output.bibitem
+ format.authors output
+ author format.key output
+ new.block
+ format.btitle "title" output.check
+ organization address new.block.checkb
+ organization output
+ address output
+ format.edition output
+ format.date output
+ format.url output
+ new.block
+ note output
+ fin.entry
+}
+
+FUNCTION {mastersthesis}
+{ output.bibitem
+ format.authors "author" output.check
+ author format.key output
+ new.block
+ format.title "title" output.check
+ new.block
+ "Master's thesis" format.thesis.type output.nonnull
+ school "school" output.check
+ address output
+ format.date "year" output.check
+ format.url output
+ new.block
+ note output
+ fin.entry
+}
+
+FUNCTION {misc}
+{ output.bibitem
+ format.authors output
+ author format.key output
+ title howpublished new.block.checkb
+ format.title output
+ howpublished new.block.checka
+ howpublished output
+ format.date output
+ format.issn output
+ format.url output
+ new.block
+ note output
+ fin.entry
+ empty.misc.check
+}
+
+FUNCTION {phdthesis}
+{ output.bibitem
+ format.authors "author" output.check
+ author format.key output
+ new.block
+ format.btitle "title" output.check
+ new.block
+ "PhD thesis" format.thesis.type output.nonnull
+ school "school" output.check
+ address output
+ format.date "year" output.check
+ format.url output
+ new.block
+ note output
+ fin.entry
+}
+
+FUNCTION {proceedings}
+{ output.bibitem
+ format.editors output
+ editor format.key output
+ new.block
+ format.btitle "title" output.check
+ format.bvolume output
+ format.number.series output
+ address output
+ format.date "year" output.check
+ new.sentence
+ organization output
+ publisher output
+ format.isbn output
+ format.doi output
+ format.url output
+ new.block
+ note output
+ fin.entry
+}
+
+FUNCTION {techreport}
+{ output.bibitem
+ format.authors "author" output.check
+ author format.key output
+ new.block
+ format.title "title" output.check
+ new.block
+ format.tr.number output.nonnull
+ institution "institution" output.check
+ address output
+ format.date "year" output.check
+ format.url output
+ new.block
+ note output
+ fin.entry
+}
+
+FUNCTION {unpublished}
+{ output.bibitem
+ format.authors "author" output.check
+ author format.key output
+ new.block
+ format.title "title" output.check
+ format.url output
+ new.block
+ note "note" output.check
+ format.date output
+ fin.entry
+}
+
+FUNCTION {default.type} { misc }
+
+
+MACRO {jan} {"Jan."}
+
+MACRO {feb} {"Feb."}
+
+MACRO {mar} {"Mar."}
+
+MACRO {apr} {"Apr."}
+
+MACRO {may} {"May"}
+
+MACRO {jun} {"June"}
+
+MACRO {jul} {"July"}
+
+MACRO {aug} {"Aug."}
+
+MACRO {sep} {"Sept."}
+
+MACRO {oct} {"Oct."}
+
+MACRO {nov} {"Nov."}
+
+MACRO {dec} {"Dec."}
+
+
+
+MACRO {acmcs} {"ACM Comput. Surv."}
+
+MACRO {acta} {"Acta Inf."}
+
+MACRO {cacm} {"Commun. ACM"}
+
+MACRO {ibmjrd} {"IBM J. Res. Dev."}
+
+MACRO {ibmsj} {"IBM Syst.~J."}
+
+MACRO {ieeese} {"IEEE Trans. Softw. Eng."}
+
+MACRO {ieeetc} {"IEEE Trans. Comput."}
+
+MACRO {ieeetcad}
+ {"IEEE Trans. Comput.-Aided Design Integrated Circuits"}
+
+MACRO {ipl} {"Inf. Process. Lett."}
+
+MACRO {jacm} {"J.~ACM"}
+
+MACRO {jcss} {"J.~Comput. Syst. Sci."}
+
+MACRO {scp} {"Sci. Comput. Programming"}
+
+MACRO {sicomp} {"SIAM J. Comput."}
+
+MACRO {tocs} {"ACM Trans. Comput. Syst."}
+
+MACRO {tods} {"ACM Trans. Database Syst."}
+
+MACRO {tog} {"ACM Trans. Gr."}
+
+MACRO {toms} {"ACM Trans. Math. Softw."}
+
+MACRO {toois} {"ACM Trans. Office Inf. Syst."}
+
+MACRO {toplas} {"ACM Trans. Prog. Lang. Syst."}
+
+MACRO {tcs} {"Theoretical Comput. Sci."}
+
+
+READ
+
+FUNCTION {sortify}
+{ purify$
+ "l" change.case$
+}
+
+INTEGERS { len }
+
+FUNCTION {chop.word}
+{ 's :=
+ 'len :=
+ s #1 len substring$ =
+ { s len #1 + global.max$ substring$ }
+ 's
+ if$
+}
+
+FUNCTION {format.lab.names}
+{ 's :=
+ s #1 "{vv~}{ll}" format.name$
+ s num.names$ duplicate$
+ #2 >
+ { pop$ " et~al." * }
+ { #2 <
+ 'skip$
+ { s #2 "{ff }{vv }{ll}{ jj}" format.name$ "others" =
+ { " et~al." * }
+ { " and " * s #2 "{vv~}{ll}" format.name$ * }
+ if$
+ }
+ if$
+ }
+ if$
+}
+
+FUNCTION {author.key.label}
+{ author empty$
+ { key empty$
+ { cite$ #1 #3 substring$ }
+ 'key
+ if$
+ }
+ { author format.lab.names }
+ if$
+}
+
+FUNCTION {author.editor.key.label}
+{ author empty$
+ { editor empty$
+ { key empty$
+ { cite$ #1 #3 substring$ }
+ 'key
+ if$
+ }
+ { editor format.lab.names }
+ if$
+ }
+ { author format.lab.names }
+ if$
+}
+
+FUNCTION {author.key.organization.label}
+{ author empty$
+ { key empty$
+ { organization empty$
+ { cite$ #1 #3 substring$ }
+ { "The " #4 organization chop.word #3 text.prefix$ }
+ if$
+ }
+ 'key
+ if$
+ }
+ { author format.lab.names }
+ if$
+}
+
+FUNCTION {editor.key.organization.label}
+{ editor empty$
+ { key empty$
+ { organization empty$
+ { cite$ #1 #3 substring$ }
+ { "The " #4 organization chop.word #3 text.prefix$ }
+ if$
+ }
+ 'key
+ if$
+ }
+ { editor format.lab.names }
+ if$
+}
+
+FUNCTION {calc.short.authors}
+{ type$ "book" =
+ type$ "inbook" =
+ or
+ 'author.editor.key.label
+ { type$ "proceedings" =
+ 'editor.key.organization.label
+ { type$ "manual" =
+ 'author.key.organization.label
+ 'author.key.label
+ if$
+ }
+ if$
+ }
+ if$
+ 'short.list :=
+}
+
+FUNCTION {calc.label}
+{ calc.short.authors
+ short.list
+ "("
+ *
+ year duplicate$ empty$
+ short.list key field.or.null = or
+ { pop$ "" }
+ 'skip$
+ if$
+ *
+ 'label :=
+}
+
+FUNCTION {sort.format.names}
+{ 's :=
+ #1 'nameptr :=
+ ""
+ s num.names$ 'numnames :=
+ numnames 'namesleft :=
+ { namesleft #0 > }
+ {
+ s nameptr "{vv{ } }{ll{ }}{ f{ }}{ jj{ }}" format.name$ 't :=
+ nameptr #1 >
+ {
+ " " *
+ namesleft #1 = t "others" = and
+ { "zzzzz" * }
+ { numnames #2 > nameptr #2 = and
+ { "zz" * year field.or.null * " " * }
+ 'skip$
+ if$
+ t sortify *
+ }
+ if$
+ }
+ { t sortify * }
+ if$
+ nameptr #1 + 'nameptr :=
+ namesleft #1 - 'namesleft :=
+ }
+ while$
+}
+
+FUNCTION {sort.format.title}
+{ 't :=
+ "A " #2
+ "An " #3
+ "The " #4 t chop.word
+ chop.word
+ chop.word
+ sortify
+ #1 global.max$ substring$
+}
+
+FUNCTION {author.sort}
+{ author empty$
+ { key empty$
+ { "to sort, need author or key in " cite$ * warning$
+ ""
+ }
+ { key sortify }
+ if$
+ }
+ { author sort.format.names }
+ if$
+}
+
+FUNCTION {author.editor.sort}
+{ author empty$
+ { editor empty$
+ { key empty$
+ { "to sort, need author, editor, or key in " cite$ * warning$
+ ""
+ }
+ { key sortify }
+ if$
+ }
+ { editor sort.format.names }
+ if$
+ }
+ { author sort.format.names }
+ if$
+}
+
+FUNCTION {author.organization.sort}
+{ author empty$
+ { organization empty$
+ { key empty$
+ { "to sort, need author, organization, or key in " cite$ * warning$
+ ""
+ }
+ { key sortify }
+ if$
+ }
+ { "The " #4 organization chop.word sortify }
+ if$
+ }
+ { author sort.format.names }
+ if$
+}
+
+FUNCTION {editor.organization.sort}
+{ editor empty$
+ { organization empty$
+ { key empty$
+ { "to sort, need editor, organization, or key in " cite$ * warning$
+ ""
+ }
+ { key sortify }
+ if$
+ }
+ { "The " #4 organization chop.word sortify }
+ if$
+ }
+ { editor sort.format.names }
+ if$
+}
+
+
+FUNCTION {presort}
+{ calc.label
+ label sortify
+ " "
+ *
+ type$ "book" =
+ type$ "inbook" =
+ or
+ 'author.editor.sort
+ { type$ "proceedings" =
+ 'editor.organization.sort
+ { type$ "manual" =
+ 'author.organization.sort
+ 'author.sort
+ if$
+ }
+ if$
+ }
+ if$
+ " "
+ *
+ cite$
+ *
+ #1 entry.max$ substring$
+ 'sort.label :=
+ sort.label *
+ #1 entry.max$ substring$
+ 'sort.key$ :=
+}
+
+ITERATE {presort}
+
+SORT
+
+STRINGS { longest.label last.label next.extra }
+
+INTEGERS { longest.label.width last.extra.num number.label }
+
+FUNCTION {initialize.longest.label}
+{ "" 'longest.label :=
+ #0 int.to.chr$ 'last.label :=
+ "" 'next.extra :=
+ #0 'longest.label.width :=
+ #0 'last.extra.num :=
+ #0 'number.label :=
+}
+
+FUNCTION {forward.pass}
+{ last.label label =
+ { last.extra.num #1 + 'last.extra.num :=
+ last.extra.num int.to.chr$ 'extra.label :=
+ }
+ { "a" chr.to.int$ 'last.extra.num :=
+ "" 'extra.label :=
+ label 'last.label :=
+ }
+ if$
+ number.label #1 + 'number.label :=
+}
+
+FUNCTION {reverse.pass}
+{ next.extra "b" =
+ { "a" 'extra.label := }
+ 'skip$
+ if$
+ extra.label 'next.extra :=
+ extra.label
+ duplicate$ empty$
+ 'skip$
+ { "{\natexlab{" swap$ * "}}" * }
+ if$
+ 'extra.label :=
+ label extra.label * 'label :=
+}
+
+EXECUTE {initialize.longest.label}
+
+ITERATE {forward.pass}
+
+REVERSE {reverse.pass}
+
+FUNCTION {bib.sort.order}
+{ sort.label 'sort.key$ :=
+}
+
+ITERATE {bib.sort.order}
+
+SORT
+
+FUNCTION {begin.bib}
+{ preamble$ empty$
+ 'skip$
+ { preamble$ write$ newline$ }
+ if$
+ "\begin{thebibliography}{" number.label int.to.str$ * "}" *
+ write$ newline$
+ "\providecommand{\natexlab}[1]{#1}"
+ write$ newline$
+ "\providecommand{\url}[1]{\texttt{#1}}"
+ write$ newline$
+ "\expandafter\ifx\csname urlstyle\endcsname\relax"
+ write$ newline$
+ " \providecommand{\doi}[1]{doi: #1}\else"
+ write$ newline$
+ " \providecommand{\doi}{doi: \begingroup \urlstyle{rm}\Url}\fi"
+ write$ newline$
+}
+
+EXECUTE {begin.bib}
+
+EXECUTE {init.state.consts}
+
+ITERATE {call.type$}
+
+FUNCTION {end.bib}
+{ newline$
+ "\end{thebibliography}" write$ newline$
+}
+
+EXECUTE {end.bib}
diff --git a/vignettes/registry.Rnw b/vignettes/registry.Rnw
new file mode 100755
index 0000000..aa04475
--- /dev/null
+++ b/vignettes/registry.Rnw
@@ -0,0 +1,313 @@
+\documentclass[a4paper]{article}
+\usepackage[round,longnamesfirst]{natbib}
+\usepackage{graphicx,keyval,thumbpdf,a4wide,makeidx,color,colordvi}
+\usepackage{amsfonts,hyperref}
+\usepackage[utf8]{inputenc}
+\DeclareUnicodeCharacter{201C}{"}
+\DeclareUnicodeCharacter{201D}{"}
+
+\newcommand\R{\textsf{R}}
+\newcommand{\pkg}[1]{{\normalfont\fontseries{b}\selectfont #1}}
+\newcommand{\sQuote}[1]{`{#1}'}
+\newcommand{\dQuote}[1]{``{#1}''}
+\newcommand{\file}[1]{\sQuote{\textsf{#1}}}
+\newcommand{\data}[1]{\texttt{#1}}
+\newcommand{\var}[1]{\textit{#1}}
+\newcommand{\class}[1]{\textsf{#1}}
+\newcommand{\proglang}[1]{\textsf{#1}}
+%% \code without `-' ligatures
+\def\nohyphenation{\hyphenchar\font=-1 \aftergroup\restorehyphenation}
+\def\restorehyphenation{\hyphenchar\font=`-}
+{\catcode`\-=\active%
+ \global\def\code{\bgroup%
+ \catcode`\-=\active \let-\codedash%
+ \Rd at code}}
+\def\codedash{-\discretionary{}{}{}}
+\def\Rd at code#1{\texttt{\nohyphenation#1}\egroup}
+\newcommand{\codefun}[1]{\code{#1()}}
+\newcommand{\codefunind}[1]{\codefun{#1}\index{\texttt{#1}}}
+\newcommand{\codeind}[1]{\code{#1}\index{\texttt{#1}}}
+
+\SweaveOpts{strip.white=true}
+
+\definecolor{Blue}{rgb}{0,0,0.8}
+\definecolor{Red}{rgb}{0.7,0,0}
+
+\date{2009-02-17}
+\title{A Generic Registry Infrastructure for \R}
+\author{David Meyer}
+%\VignetteIndexEntry{Registry}
+%\VignetteDepends{registry}
+%\VignetteKeywords{registry}
+%\VignettePackage{registry}
+
+\makeindex{}
+
+\sloppy{}
+
+\begin{document}
+\maketitle
+
+% \begin{abstract}
+% This document introduces a generic registry infrastructure for \R,
+% provided by the \pkg{registry} package.
+% \end{abstract}
+
+<<echo=FALSE>>=
+options(width = 80)
+library("registry")
+@ %
+
+\section{Introduction}
+\label{sec:introduction}
+
+More and more, \R~packages are offering dynamic functionality,
+allowing users to extend a \dQuote{repository} of initial features or
+data. For example, the \pkg{proxy} package \citep{registry:meyer+buchta:2008}
+provides an enhanced
+\codefun{dist} function for computing dissimilarity matrices,
+allowing to choose among several proximity
+measures stored in a registry. Each entry is composed of a small
+workhorse function and some meta data including, e.g., a character vector
+of aliases, literature references, the formula in plain text,
+a function to coerce
+between similarity and distance, and a type categorization
+(binary, metric, etc.). Users can add new proximity measures to the
+registry at run-time and immediately use them without recreating the
+package, specifying one of the aliases defined in the meta data.
+Similarly, the \pkg{relations} \citep{registry:hornik+meyer:2008}
+and \pkg{CLUE} \citep{registry:hornik:2005,registry:hornik:2007}
+packages use simple
+registries internally to link some meta data to available functions,
+used by the high-level consensus ranking and cluster ensemble
+functions, respectively.
+
+Such a registry, whether exposed to the user or not, is conceptually a
+small in-memory data base where entries with a common field structure are
+stored and retrieved and whose fields can be of mixed type.
+At first sight, a data frame seems to be the
+data structure of choice for an appropriate implementation.
+Unfortunately, data frames are inconvenient to use
+with factors, functions, or other recursive types such as lists
+due to automatic coercions taking place behind the scenes. In fact, a
+simpler, record-like structure such as a list with named components
+(\dQuote{fields}) appears more practical. Also,
+features known from \dQuote{real} data bases such as compound keys,
+validity checking of new entries, and use of access rights are not
+available by default and need to be \dQuote{reinvented} every time
+they are needed.
+
+The \pkg{registry} package provides a simple mechanism for defining
+and manipulating user-extensible registry objects. A typical
+use case in the context of an \R~package could include the following steps:
+
+\begin{enumerate}
+\item Create one or more registry objects inside the package's namespace.
+\item Insert entries to the registry.
+\item Possibly, \dQuote{seal} the entries and set access rights.
+\item Possibly, export the registry object to the user level.
+\item Browse and retrieve entries from the registry.
+\end{enumerate}
+
+In the following, we explain these steps in more detail:
+first, how a registry can be set up; second, how entries
+can be added, modified and retrieved; and third, how a registry can be
+sealed and restricted through the definition of access rights.
+
+\section{Creating Registries}
+
+A registry basically is a container (implemented in \R~as an
+environment), along with some access functions. A new object of class
+\code{registry} can simply be created using the \codefun{registry} function:
+<<>>=
+library(registry)
+R <- registry()
+print(R)
+@
+Optional parameters include the specification of an (additional) class
+for the created registry object and the individual entries,
+as well as the specification of some validity function checking new
+entries to be added to the registry.
+
+In the following, we will use the example of a simple address book,
+whose entries include first and last name, address, age, home/cell
+phone number, and a business/private classification.
+Last and first name build the search key. Age is an
+optional integer in the range of 1 and
+99. Additionally, at least one phone number should be added to the registry.
+
+We start by creating two simple validity functions. The first one, to
+be specified at field level later on, checks a given age:
+<<>>=
+checkAge <- function(x) stopifnot(is.na(x) || x > 0 && x < 100)
+@
+The second one, specified at registry level,
+checks whether a given registry entry (list of named components)
+contains at least one phone number:
+<<>>=
+checkPhone <- function(x) stopifnot(!is.na(x$mobile) || !is.na(x$home))
+@
+Next, we create a registry of class \code{Addressbook} (inheriting
+from \code{registry}), containing entries of class \code{Address} and
+using the above validity function.
+<<>>=
+R <- registry(registry_class = "Addressbook", entry_class = "Address",
+ validity_FUN = checkPhone)
+@
+The additional class for the registry allows, e.g., user-defined printing:
+<<>>=
+print.Addressbook <-
+function(x, ...) {
+ writeLines(sprintf("An address book with %i entries.\n", length(x)))
+ invisible(x)
+}
+print(R)
+@
+
+At this stage, we are ready to set up the field information. First and last
+names are mandatory character fields, uniquely identifying an entry
+(key fields). Lookups should work with partial completion, ignoring case:
+<<>>=
+R$set_field("last", type = "character", is_key = TRUE, index_FUN = match_partial_ignorecase)
+R$set_field("first", type = "character", is_key = TRUE, index_FUN = match_partial_ignorecase)
+@
+The address is also character, but optional:
+<<>>=
+R$set_field("address", type = "character")
+@
+At least one phone number (character) is required. This can be
+achieved by making them optional, and using the validity
+function specified at the registry level to check whether one of them is empty:
+<<>>=
+R$set_field("mobile", type = "character")
+R$set_field("home", type = "character")
+@
+The age field is an optional integer with a defined range, checked by
+the field-level validity function:
+<<>>=
+R$set_field("age", type = "integer", validity_FUN = checkAge)
+@
+Finally, the business/private category is defined by specifying the
+possible alternatives (\code{Business} is set as default):
+<<>>=
+R$set_field("type", type = "character",
+ alternatives = c("Business", "Private"),
+ default = "Business")
+@
+The setup for a field can be retrieved using \codefun{get\_field}:
+<<>>=
+R$get_field("type")
+@
+\codefun{get\_fields} returns the complete list.
+
+\section{Using Registries}
+
+We now can start adding entries to the registry:
+<<>>=
+R$set_entry(last = "Smith", first = "Mary", address = "Vienna",
+ home = "734 43 34", type = "Private", age = 44L)
+R$set_entry(last = "Smith", first = "Peter", address = "New York",
+ mobile = "878 78 87")
+@
+If all field values are specified, the field names can be omitted:
+<<>>=
+R$set_entry("Myers", "John", "Washington", "52 32 34", "898 89 99",
+ 33L, "Business")
+@
+Duplicate or invalid entries are not accepted:
+<<>>=
+TRY <- function(expr) tryCatch(expr, error = print)
+TRY(R$set_entry(last = "Smith", first = "Mary"))
+TRY(R$set_entry(last = "Miller", first = "Henry"))
+TRY(R$set_entry(last = "Miller", first = "Henry", age = 12.5))
+TRY(R$set_entry(last = "Miller", first = "Henry", age = 999L))
+@
+A single entry can be retrieved using \codefun{get\_entry}:
+<<>>=
+R$get_entry(last = "Smith", first = "mar")
+@
+Since returned entries inherit from \code{Address}, we can provide a
+user-defined print method:
+<<>>=
+print.Address <- function(x) with(x,
+ writeLines(sprintf("%s %s, %s; home: %s, mobile: %s; age: %i (%s)", first, last, address, home, mobile, age, type)))
+R$get_entry(last = "Smith", first = "mar")
+@
+Note that even though
+the first name of Mary Smith is incompletely specified and in
+lower case, the lookup is still successful
+because of the partial matching indexing function. The \code{[[} operator
+can be used as an alternative to \codefun{get\_entry}:
+<<>>=
+R[["Myers"]]
+@
+For Myers, the last name uniquely identifies the entry, so
+the first name can be omitted. Key values can have alternative values:
+<<>>=
+R$set_entry(last = "Frears", first = c("Joe", "Jonathan"),
+ address = "Washington", home = "721 42 34")
+@
+Either of them can be used for retrieval:
+<<>>=
+identical(R[["Frears", "Jonathan"]], R[["Frears", "Joe"]])
+@
+Unsuccessful lookups result in a
+return of \code{NULL}. Multiple entries can be retrieved
+using the \codefun{get\_entries} accessing function. They are returned
+in a list whose component names are generated from the key values:
+<<>>=
+R$get_entries("Smith")
+@
+Full-text search in all information is provided by \codefun{grep\_entries}:
+<<>>=
+R$grep_entries("Priv")
+@
+A list of all entries can be obtained using either of:
+<<eval=FALSE>>=
+R$get_entries()
+R[]
+@
+The summary method for registry objects returns a data frame:
+<<>>=
+summary(R)
+@
+Entries can also be modified using \codefun{modify\_entry}, specifying
+key and new field values:
+<<>>=
+R[["Smith", "Peter"]]
+R$modify_entry(last = "Smith", first = "Peter", age = 22L)
+R[["Smith", "Peter"]]
+@
+Finally, entries can be removed using \codefun{delete\_entry}:
+<<>>=
+R$delete_entry(last = "Smith", first = "Peter")
+R[["Smith", "Peter"]]
+@
+
+\section{Sealing Registries and Setting Access Rights}
+
+Occasionally, developers might want to protect a registry that ships
+with some package to prevent accidental deletions or
+alterations. For this, \pkg{registry} offers two mechanisms: first, a
+registry object can be \dQuote{sealed} to prevent modifications of
+\emph{existing} data:
+<<>>=
+R$seal_entries()
+TRY(R$delete_entry("Smith", "Mary"))
+R$set_entry(last = "Slater", first = "Christian", address = "Boston",
+ mobile = "766 23 88")
+R[["Slater"]]
+@
+Second, the access permissions for registries can be restricted:
+<<>>=
+R$get_permissions()
+R$restrict_permissions(delete_entries = FALSE)
+TRY(R$delete_entry("Slater"))
+R$modify_entry(last = "Slater", first = "Christian", age = 44L)
+R[["Slater"]]
+@
+
+\bibliographystyle{abbrvnat}
+\bibliography{registry}
+
+\end{document}
diff --git a/vignettes/registry.bib b/vignettes/registry.bib
new file mode 100755
index 0000000..03d2f0a
--- /dev/null
+++ b/vignettes/registry.bib
@@ -0,0 +1,33 @@
+ at Manual{registry:meyer+buchta:2008,
+ title = {proxy: Distance and Similarity Measures},
+ author = {David Meyer and Christian Buchta},
+ year = 2008,
+ note = {R package version 0.4-1}
+}
+
+ at Manual{registry:hornik:2007,
+ title = {clue: Cluster ensembles},
+ author = {Kurt Hornik},
+ year = 2007,
+ note = {R package version 0.3-20.},
+ url = {http://CRAN.R-project.org/}
+}
+
+ at Article{registry:hornik:2005,
+ title = {A {CLUE} for {CLUster Ensembles}},
+ author = {Kurt Hornik},
+ year = 2005,
+ journal = {Journal of Statistical Software},
+ volume = 14,
+ number = 12,
+ url = {http://www.jstatsoft.org/v14/i12/},
+ month = {September}
+}
+
+ at Manual{registry:hornik+meyer:2008,
+ title = {relations: Data Structures and Algorithms for
+ Relations},
+ author = {Kurt Hornik and David Meyer},
+ year = 2008,
+ note = {R package version 0.5}
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/r-cran-registry.git
More information about the debian-med-commit
mailing list