Skip to content
Francis Galiegue edited this page May 25, 2013 · 15 revisions

Introduction

URI template is defined by RFC 6570. Unfortunately, the text of the RFC is quite complex to follow, and it takes quite some time to make sense of the different sections and proposed algorithm.

The algorithm used by this implementation is based on the reference algorithm but differs in a fundamental way, so as to make it more simple, therefore easier to implement, and ultimately less bug prone. This implementation is heavily tested and, as far as the author can tell, all you can throw at it will be rendered correctly.

This page sets as a goal to fully explain the algorithm. But before plunging into the core of the matter, a number of definitions must be written so that the algorithm itself be easily understood.

Definitions

Preliminary note

These definitions are an adaptation of the contents of the RFC; some of these definitions are not in the RFC while other definitions in the RFC are ignored. In particular, levels defined by the RFC (from 1 to 4) have been deemed counterproductive, and you will find no mention of them. If anything, these levels are unusable in any sane implementation, and they are not even referred to in the reference algorithm.

Variable value

A variable value can be either of a string, a list or a map. Note that the RFC refers to maps (ie, key/value pairs) as associative arrays; this text, however, uses maps. All list elements or map keys/values are strings. Examples of variable values are (note: JSON representations):

  • "foobar" (string),
  • [ "foo", "bar" ] (list),
  • { "key1": "foo", "key2": "bar" } (map).

Any individual string (a whole string value, a list element, a map key or value) can be empty; similarly, there can also be empty lists and empty maps.

Variable specifier (or varspec for short)

A varspec is an association of a variable name with an optional modifier. Modifers can be of two kinds: the explode modifier (*) or a prefix modifier (:n, where n is an integer between 0 and 10000).

Note: unlike the sample RFC algorithm, this implementation does not allow both modifiers to exist at the same time (which does not make sense anyway).

Examples of varspecs are:

  • foobar:12 (variable name foobar, with prefix modifier, length 12);
  • foobar* (variable name foobar, with explode modifier).

Expression type

The expression type is determined by the first character found in a template expression (see below). Even though the algorithm is not driven by expression types, they contain crucial information to be accounted for when expanding:

  • the prefix string: what string should be in front of any non empty expansion list;
  • the joining character: if the expansion is non empty, what character should be used to join the expansion list elements;
  • the raw character set: what characters in a value should not be subjected to percent-encoding expansion;
  • the empty value substitution: in some situations, when a given value is empty, this string should be used as a replacement;
  • named versus non named: for some expression types, the variable name should be used in order to produce the expanded string.

For those of you who have read the RFC in its entirety, you will recognize the contents of the table in the appenfix; this algorithm makes use of this information, but uses it differently.

Template expression

A template expression is defined by a sequence of the beginning character of a template ({), followed by an optional expression type character (none means basic expression type), followed by one or more varspecs separated by commas, followed by the end character of a template (}).

Examples: {var,list,map}; {#foo:3,list*}; and so on.

Expansion data: variable names and values

When faced with expanding an actual template, an implementation will have to deal with two things: the URI template itself, and a map pairing variable names and values. The latter is the expansion data.

You will note that there is no previous knowledge as to:

  • where a variable name appears in any template expression,
  • how this variable name is affected by modifiers (ie, a varspec).

Template expression expansion algorithm

Inputs

The template expression itself is split between the expression type and varspec list (in order). The other input is the expansion data. The corresponding Java types are:

  • expression type: ExpressionType;
  • list of varspecs: List<VariableSpec>;
  • expansion data: Map<String, VariableValue>.

Expansion

The expansion process consists of building a list of expansion tokens (which are strings).

The algorithm walks through the list of varspecs and proceeds as follows:

  • if no variable value is present for this variable name, continue to the next varspec;
  • build the expansion token list for that varspec, value and expression type;
  • add this expansion token list to the global expansion token list.

After having walked through all varspecs, build the result string:

  • if the expansion token list is empty, this is the empty string;
  • otherwise, this is a string consisting of the expression type prefix, followed by the expansion token elements joined with the expression type separator.

Note that for one set of (varspec, value, expression type), the resulting expansion token list may be empty.

Dealing with empty values

This section covers what happens when an empty value is encountered. Note that an empty value and an undefined value are different!

Empty string values

Let us have a variable name foo, which matching value is the empty string "". Expanding this to a token list then depends on two things:

  • whether the expression type is named;
  • if it is named, the empty value substitution string.

If the expression type is not named, the expansion is a single element list consisting of the empty string: [ "" ]. If the expression type is named, the expansion is still a single element list where the element consists of the variable name followed by the empty value substitution string. For example:

  • query parameter expansion (?, empty substitution string "="): [ "foo=" ];
  • query parameter continuation expansion (&, empty substitution string "="): [ "foo=" ];
  • path-style parameter extension (;, empty substitution string ""): [ "foo" ].