Bug: 159253728

Clone this repo:
  1. 953cb3b Merge sc-mainline-prod by Xin Li · 2 years, 11 months ago aml_tz4_332714010 aml_tz5_341510010 android13-d2-release android13-d3-s1-release android13-d4-release android13-d4-s1-release android13-d4-s2-release android13-dev android13-frc-adbd-release android13-frc-art-release android13-frc-cellbroadcast-release android13-frc-conscrypt-release android13-frc-documentsui-release android13-frc-extservices-release android13-frc-ipsec-release android13-frc-media-release android13-frc-media-swcodec-release android13-frc-networking-release android13-frc-neuralnetworks-release android13-frc-odp-release android13-frc-os-statsd-release android13-frc-permission-release android13-frc-resolv-release android13-frc-scheduling-release android13-mainline-adservices-release android13-mainline-appsearch-release android13-mainline-go-adbd-release android13-mainline-go-adservices-release android13-mainline-go-appsearch-release android13-mainline-go-art-release android13-mainline-go-cellbroadcast-release android13-mainline-go-conscrypt-release android13-mainline-go-documentsui-release android13-mainline-go-extservices-release android13-mainline-go-ipsec-release android13-mainline-go-media-release android13-mainline-go-media-swcodec-release android13-mainline-go-mediaprovider-release android13-mainline-go-networking-release android13-mainline-go-neuralnetworks-release android13-mainline-go-odp-release android13-mainline-go-os-statsd-release android13-mainline-go-permission-release android13-mainline-go-resolv-release android13-mainline-go-scheduling-release android13-mainline-go-sdkext-release android13-mainline-go-tethering-release android13-mainline-go-tzdata4-release android13-mainline-go-uwb-release android13-mainline-go-wifi-release android13-mainline-tzdata4-release android13-mainline-uwb-release android13-qpr1-release android13-qpr1-s1-release android13-qpr1-s2-release android13-qpr1-s3-release android13-qpr1-s4-release android13-qpr1-s5-release android13-qpr1-s6-release android13-qpr1-s7-release android13-qpr1-s8-release android13-qpr2-b-s1-release android13-qpr2-release android13-qpr2-s1-release android13-qpr2-s10-release android13-qpr2-s11-release android13-qpr2-s12-release android13-qpr2-s2-release android13-qpr2-s3-release android13-qpr2-s5-release android13-qpr2-s6-release android13-qpr2-s7-release android13-qpr2-s8-release android13-qpr2-s9-release android13-qpr3-c-s1-release android13-qpr3-c-s10-release android13-qpr3-c-s11-release android13-qpr3-c-s12-release android13-qpr3-c-s2-release android13-qpr3-c-s3-release android13-qpr3-c-s4-release android13-qpr3-c-s5-release android13-qpr3-c-s6-release android13-qpr3-c-s7-release android13-qpr3-c-s8-release android13-qpr3-release android13-qpr3-s1-release android13-qpr3-s10-release android13-qpr3-s11-release android13-qpr3-s12-release android13-qpr3-s13-release android13-qpr3-s14-release android13-qpr3-s2-release android13-qpr3-s3-release android13-qpr3-s4-release android13-qpr3-s5-release android13-qpr3-s6-release android13-qpr3-s7-release android13-qpr3-s8-release android13-qpr3-s9-release android14-d1-release android14-d1-s1-release android14-d1-s2-release android14-d1-s3-release android14-d1-s4-release android14-d1-s5-release android14-d1-s6-release android14-d1-s7-release android14-dev android14-gsi android14-mainline-adservices-release android14-mainline-appsearch-release android14-mainline-healthfitness-release android14-mainline-uwb-release android14-platform-release android14-qpr1-release android14-qpr1-s2-release android14-qpr2-release android14-qpr2-s1-release android14-qpr2-s2-release android14-qpr2-s3-release android14-release android14-s1-release android14-s2-release android14-security-release android14-tests-release main main-16k main-16k-with-phones master aml_ads_331131000 aml_ads_331418080 aml_ads_331511020 aml_ads_331611190 aml_ads_331710270 aml_ads_331814200 aml_ads_331920180 aml_ads_340915050 aml_ads_341027030 aml_ads_341131050 aml_ads_341316030 aml_ads_341413000 aml_ads_341517040 aml_ase_331011020 aml_ase_331112000 aml_ase_331311020 aml_ase_340913000 aml_ase_341113000 aml_ase_341310010 aml_ase_341410000 aml_ase_341510000 aml_cfg_341510000 aml_go_adb_330913000 aml_go_ads_330913000 aml_go_ads_330915000 aml_go_ads_330915100 aml_go_art_330913000 aml_go_ase_330913000 aml_go_cbr_330912000 aml_go_con_330913000 aml_go_doc_330912000 aml_go_ext_330912000 aml_go_ips_330911000 aml_go_med_330913000 aml_go_mpr_330912000 aml_go_net_330913000 aml_go_neu_330912000 aml_go_odp_330912000 aml_go_odp_330913000 aml_go_per_330912000 aml_go_res_330912000 aml_go_sch_330911000 aml_go_sdk_330810000 aml_go_sta_330911000 aml_go_swc_330913000 aml_go_tet_330914010 aml_go_tz4_330912000 aml_go_uwb_330912000 aml_go_wif_330911000 aml_hef_341114030 aml_hef_341311010 aml_hef_341415040 aml_hef_341512030 aml_rkp_341012000 aml_rkp_341015010 aml_rkp_341114000 aml_rkp_341311000 aml_rkp_341510000 aml_tz4_331012000 aml_tz4_331012040 aml_tz4_331012050 aml_tz4_331314010 aml_tz4_331314020 aml_tz4_331314030 aml_tz4_331910000 aml_tz4_332714010 aml_tz4_332714050 aml_tz5_341510010 aml_tz5_341510050 aml_uwb_330810010 aml_uwb_331015040 aml_uwb_331115000 aml_uwb_331310030 aml_uwb_331410010 aml_uwb_331611010 aml_uwb_331613010 aml_uwb_331820070 aml_uwb_331910010 aml_uwb_341011000 aml_uwb_341111010 aml_uwb_341310030 aml_uwb_341310300 aml_uwb_341511050 android-13.0.0_r16 android-13.0.0_r17 android-13.0.0_r18 android-13.0.0_r19 android-13.0.0_r20 android-13.0.0_r21 android-13.0.0_r22 android-13.0.0_r23 android-13.0.0_r24 android-13.0.0_r27 android-13.0.0_r28 android-13.0.0_r29 android-13.0.0_r30 android-13.0.0_r32 android-13.0.0_r33 android-13.0.0_r34 android-13.0.0_r35 android-13.0.0_r36 android-13.0.0_r37 android-13.0.0_r38 android-13.0.0_r39 android-13.0.0_r40 android-13.0.0_r41 android-13.0.0_r42 android-13.0.0_r43 android-13.0.0_r44 android-13.0.0_r45 android-13.0.0_r46 android-13.0.0_r47 android-13.0.0_r48 android-13.0.0_r49 android-13.0.0_r50 android-13.0.0_r51 android-13.0.0_r52 android-13.0.0_r53 android-13.0.0_r54 android-13.0.0_r55 android-13.0.0_r56 android-13.0.0_r57 android-13.0.0_r58 android-13.0.0_r59 android-13.0.0_r60 android-13.0.0_r61 android-13.0.0_r62 android-13.0.0_r63 android-13.0.0_r64 android-13.0.0_r65 android-13.0.0_r66 android-13.0.0_r67 android-13.0.0_r68 android-13.0.0_r69 android-13.0.0_r70 android-13.0.0_r71 android-13.0.0_r72 android-13.0.0_r73 android-13.0.0_r74 android-13.0.0_r75 android-13.0.0_r76 android-13.0.0_r77 android-13.0.0_r78 android-13.0.0_r79 android-13.0.0_r80 android-13.0.0_r81 android-13.0.0_r82 android-13.0.0_r83 android-14.0.0_r1 android-14.0.0_r10 android-14.0.0_r11 android-14.0.0_r12 android-14.0.0_r13 android-14.0.0_r14 android-14.0.0_r15 android-14.0.0_r16 android-14.0.0_r17 android-14.0.0_r18 android-14.0.0_r19 android-14.0.0_r2 android-14.0.0_r20 android-14.0.0_r21 android-14.0.0_r22 android-14.0.0_r23 android-14.0.0_r24 android-14.0.0_r25 android-14.0.0_r26 android-14.0.0_r27 android-14.0.0_r28 android-14.0.0_r29 android-14.0.0_r3 android-14.0.0_r30 android-14.0.0_r31 android-14.0.0_r32 android-14.0.0_r33 android-14.0.0_r4 android-14.0.0_r5 android-14.0.0_r6 android-14.0.0_r7 android-14.0.0_r8 android-14.0.0_r9 android-cts-14.0_r1 android-cts-14.0_r2 android-cts-14.0_r3 android-platform-14.0.0_r1 android-platform-14.0.0_r2 android-platform-14.0.0_r3 android-platform-14.0.0_r4 android-platform-14.0.0_r5 android-platform-14.0.0_r6 android-security-14.0.0_r1 android-security-14.0.0_r2 android-security-14.0.0_r3 android-security-14.0.0_r4 android-security-14.0.0_r5 android-security-14.0.0_r6 android-security-14.0.0_r7 android-u-beta-1-gpl android-vts-14.0_r1 android-vts-14.0_r2 android-vts-14.0_r3 frc_340818110 frc_340818170 frc_340819010 frc_340819020 frc_340819030 frc_340819190 frc_340819220 frc_340819280 frc_340821000 t_frc_adb_330444000 t_frc_art_330443060 t_frc_ase_330444010 t_frc_cbr_330443000 t_frc_con_330443020 t_frc_doc_330443000 t_frc_doc_330443060 t_frc_doc_330543000 t_frc_ext_330443000 t_frc_ips_330443010 t_frc_med_330443030 t_frc_net_330443000 t_frc_neu_330443000 t_frc_neu_330443030 t_frc_odp_330442000 t_frc_odp_330442040 t_frc_per_330444010 t_frc_res_330443000 t_frc_sch_330443010 t_frc_sch_330443040 t_frc_sta_330443010 t_frc_swc_330443010 t_frc_swc_330443040 t_frc_tz4_330443010
  2. 57a68fb [LSC] Add LOCAL_LICENSE_KINDS to external/escapevelocity am: 29fd1ff90c am: d8363f3b50 am: de5924ce12 am: 53a3376464 by Bob Badour · 3 years, 2 months ago android12-mainline-art-release android12-mainline-networkstack-release aml_tz3_311312010 android-mainline-12.0.0_r100 android-mainline-12.0.0_r113 android-mainline-12.0.0_r115 android-mainline-12.0.0_r122 android-mainline-12.0.0_r19 android-mainline-12.0.0_r22 android-mainline-12.0.0_r39 android-mainline-12.0.0_r42 android-mainline-12.0.0_r49 android-mainline-12.0.0_r5 android-mainline-12.0.0_r56 android-mainline-12.0.0_r59 android-mainline-12.0.0_r63 android-mainline-12.0.0_r69 android-mainline-12.0.0_r70 android-mainline-12.0.0_r77 android-mainline-12.0.0_r98 android-mainline-12.0.0_r99
  3. 4db3961 [LSC] Add LOCAL_LICENSE_KINDS to external/escapevelocity am: 29fd1ff90c am: d8363f3b50 am: de5924ce12 am: 53a3376464 by Bob Badour · 3 years, 2 months ago
  4. 53a3376 [LSC] Add LOCAL_LICENSE_KINDS to external/escapevelocity am: 29fd1ff90c am: d8363f3b50 am: de5924ce12 by Bob Badour · 3 years, 2 months ago android-s-qpr3-beta-1 android-s-v2-beta-3 android-s-v2-preview-1 android-t-preview-1 android12--mainline-release android12-dev android12-qpr1-d-release android12-qpr1-d-s1-release android12-qpr1-d-s2-release android12-qpr1-d-s3-release android12-qpr1-release android12-qpr3-release android12-qpr3-s1-release android12-qpr3-s2-release android12-qpr3-s3-release android12-qpr3-s4-release android12-qpr3-s5-release android12-qpr3-s6-release android12-qpr3-s7-release android12L-d2-release android12L-d2-s1-release android12L-d2-s2-release android12L-d2-s3-release android12L-d2-s4-release android12L-d2-s5-release android12L-d2-s6-release android12L-d2-s7-release android12L-d2-s8-release android12L-dev android-12.0.0_r16 android-12.0.0_r18 android-12.0.0_r19 android-12.0.0_r20 android-12.0.0_r21 android-12.0.0_r26 android-12.0.0_r27 android-12.0.0_r28 android-12.0.0_r29 android-12.0.0_r32 android-12.1.0_r10 android-12.1.0_r11 android-12.1.0_r12 android-12.1.0_r13 android-12.1.0_r14 android-12.1.0_r15 android-12.1.0_r16 android-12.1.0_r17 android-12.1.0_r18 android-12.1.0_r19 android-12.1.0_r20 android-12.1.0_r21 android-12.1.0_r22 android-12.1.0_r23 android-12.1.0_r24 android-12.1.0_r25 android-12.1.0_r26 android-12.1.0_r7 android-12.1.0_r8 android-12.1.0_r9 android-mainline-12.0.0_r36 android-mainline-12.0.0_r4 android-s-qpr3-beta-1 android-s-v2-beta-2 android-s-v2-beta-3 android-s-v2-preview-1 android-s-v2-preview-2 android-t-beta-3 android-t-preview-1 android-t-preview-2
  5. de5924c [LSC] Add LOCAL_LICENSE_KINDS to external/escapevelocity am: 29fd1ff90c am: d8363f3b50 by Bob Badour · 3 years, 2 months ago

EscapeVelocity summary

EscapeVelocity is a templating engine that can be used from Java. It is a reimplementation of a subset of functionality from Apache Velocity.

This is not an official Google product.

For a fuller explanation of Velocity's functioning, see its User Guide

If EscapeVelocity successfully produces a result from a template evaluation, that result should be the exact same string that Velocity produces. If not, that is a bug.

EscapeVelocity has no facilities for HTML escaping and it is not appropriate for producing HTML output that might include portions of untrusted input.

Motivation

Velocity has a convenient templating language. It is easy to read, and it has widespread support from tools such as editors and coding websites. However, using Velocity can prove difficult. Its use to generate Java code in the AutoValue annotation processor required many workarounds. The way it dynamically loads classes as part of its standard operation makes it hard to shade it, which in the case of AutoValue led to interference if Velocity was used elsewhere in a project.

EscapeVelocity has a simple API that does not involve any class-loading or other sources of problems. It and its dependencies can be shaded with no difficulty.

Loading a template

The entry point for EscapeVelocity is the Template class. To obtain an instance, use Template.from(Reader). If a template is stored in a file, that file conventionally has the suffix .vm (for Velocity Macros). But since the argument is a Reader, you can also load a template directly from a Java string, using StringReader.

Here's how you might make a Template instance from a template file that is packaged as a resource in the same package as the calling class:

InputStream in = getClass().getResourceAsStream("foo.vm");
if (in == null) {
  throw new IllegalArgumentException("Could not find resource foo.vm");
}
Reader reader = new BufferedReader(new InputStreamReader(in));
Template template = Template.parseFrom(reader);

Expanding a template

Once you have a Template object, you can use it to produce a string where the variables in the template are given the values you provide. You can do this any number of times, specifying the same or different values each time.

Suppose you have this template:

The $language word for $original is $translated.

You might write this code:

Map<String, String> vars = new HashMap<>();
vars.put("language", "French");
vars.put("original", "toe");
vars.put("translated", "orteil");
String result = template.evaluate(vars);

The result string would then be: The French word for toe is orteil.

Comments

The characters ## introduce a comment. Characters from ## up to and including the following newline are omitted from the template. This template has comments:

Line 1 ## with a comment
Line 2

It is the same as this template:

Line 1 Line 2

References

EscapeVelocity supports most of the reference types described in the Velocity User Guide

Variables

A variable has an ASCII name that starts with a letter (a-z or A-Z) and where any other characters are also letters or digits or hyphens (-) or underscores (_). A variable reference can be written as $foo or as ${foo}. The value of a variable can be of any Java type. If the value v of variable foo is not a String then the result of $foo in a template will be String.valueOf(v). Variables must be defined before they are referenced; otherwise an EvaluationException will be thrown.

Variable names are case-sensitive: $foo is not the same variable as $Foo or $FOO.

Initially the values of variables come from the Map that is passed to Template.evaluate. Those values can be changed, and new ones defined, using the #set directive in the template:

#set ($foo = "bar")

Setting a variable affects later references to it in the template, but has no effect on the Map that was passed in or on later template evaluations.

Properties

If a reference looks like $purchase.Total then the value of the $purchase variable must be a Java object that has a public method getTotal() or gettotal(), or a method called isTotal() or istotal() that returns boolean. The result of $purchase.Total is then the result of calling that method on the $purchase object.

If you want to have a period (.) after a variable reference without it being a property reference, you can use braces like this: ${purchase}.Total. If, after a property reference, you have a further period, you can put braces around the reference like this: ${purchase.Total}.nonProperty.

Methods

If a reference looks like $purchase.addItem("scones", 23) then the value of the $purchase variable must be a Java object that has a public method addItem with two parameters that match the given values. Unlike Velocity, EscapeVelocity requires that there be exactly one such method. It is OK if there are other addItem methods provided they are not compatible with the arguments provided.

Properties are in fact a special case of methods: instead of writing $purchase.Total you could write $purchase.getTotal(). Braces can be used to make the method invocation explicit (${purchase.getTotal()}) or to prevent method invocation (${purchase}.getTotal()).

Indexing

If a reference looks like $indexme[$i] then the value of the $indexme variable must be a Java object that has a public get method that takes one argument that is compatible with the index. For example, $indexme might be a List and $i might be an integer. Then the reference would be the result of List.get(int) for that list and that integer. Or, $indexme might be a Map, and the reference would be the result of Map.get(Object) for the object $i. In general, $indexme[$i] is equivalent to $indexme.get($i).

Unlike Velocity, EscapeVelocity does not allow $indexme to be a Java array.

Undefined references

If a variable has not been given a value, either by being in the initial Map argument or by being set in the template, then referencing it will provoke an EvaluationException. There is a special case for #if: if you write #if ($var) then it is allowed for $var not to be defined, and it is treated as false.

Setting properties and indexes: not supported

Unlke Velocity, EscapeVelocity does not allow #set assignments with properties or indexes:

#set ($data.User = "jon")        ## Allowed in Velocity but not in EscapeVelocity
#set ($map["apple"] = "orange")  ## Allowed in Velocity but not in EscapeVelocity

Expressions

In certain contexts, such as the #set directive we have just seen or certain other directives, EscapeVelocity can evaluate expressions. An expression can be any of these:

  • A reference, of the kind we have just seen. The value is the value of the reference.
  • A string literal enclosed in double quotes, like "this". A string literal must appear on one line. EscapeVelocity does not support the characters $ or \\ in a string literal.
  • An integer literal such as 23 or -100. EscapeVelocity does not support floating-point literals.
  • A Boolean literal, true or false.
  • Simpler expressions joined together with operators that have the same meaning as in Java: !, ==, !=, <, <=, >, >=, &&, ||, +, -, *, /, %. The operators have the same precedence as in Java.
  • A simpler expression in parentheses, for example (2 + 3).

Velocity supports string literals with single quotes, like 'this' and also references within strings, like "a $reference in a string", but EscapeVelocity does not.

Directives

A directive is introduced by a # character followed by a word. We have already seen the #set directive, which sets the value of a variable. The other directives are listed below.

Directives can be spelled with or without braces, so #set or #{set}.

#if/#elseif/#else

The #if directive selects parts of the template according as a condition is true or false. The simplest case looks like this:

#if ($condition) yes #end

This evaluates to the string yes if the variable $condition is defined and has a true value, and to the empty string otherwise. It is allowed for $condition not to be defined in this case, and then it is treated as false.

The expression in #if (here $condition) is considered true if its value is not null and not equal to the Boolean value false.

An #if directive can also have an #else part, for example:

#if ($condition) yes #else no #end

This evaluates to the string yes if the condition is true or the string no if it is not.

An #if directive can have any number of #elseif parts. For example:

#if ($i == 0) zero #elseif ($i == 1) one #elseif ($i == 2) two #else many #end

#foreach

The #foreach directive repeats a part of the template once for each value in a list.

#foreach ($product in $allProducts)
  ${product}!
#end

This will produce one line for each value in the $allProducts variable. The value of $allProducts can be a Java Iterable, such as a List or Set; or it can be an object array; or it can be a Java Map. When it is a Map the #foreach directive loops over every value in the Map.

If $allProducts is a List containing the strings oranges and lemons then the result of the #foreach would be this:


oranges! lemons!

When the #foreach completes, the loop variable ($product in the example) goes back to whatever value it had before, or to being undefined if it was undefined before.

Within the #foreach, a special variable $foreach is defined, such that you can write $foreach.hasNext, which will be true if there are more values after this one or false if this is the last value. For example:

#foreach ($product in $allProducts)${product}#if ($foreach.hasNext), #end#end

This would produce the output oranges, lemons for the list above. (The example is scrunched up to avoid introducing extraneous spaces, as described in the section on spaces below.)

Velocity gives the $foreach variable other properties (index and count) but EscapeVelocity does not.

Macros

A macro is a part of the template that can be reused in more than one place, potentially with different parameters each time. In the simplest case, a macro has no arguments:

#macro (hello) bonjour #end

Then the macro can be referenced by writing #hello() and the result will be the string bonjour inserted at that point.

Macros can also have parameters:

#macro (greet $hello $world) $hello, $world! #end

Then #greet("bonjour", "monde") would produce bonjour, monde!. The comma is optional, so you could also write #greet("bonjour" "monde").

When a macro completes, the parameters ($hello and $world in the example) go back to whatever values they had before, or to being undefined if they were undefined before.

All macro definitions take effect before the template is evaluated, so you can use a macro at a point in the template that is before the point where it is defined. This also means that you can't define a macro conditionally:

## This doesn't work!
#if ($language == "French")
#macro (hello) bonjour #end
#else
#macro (hello) hello #end
#end

There is no particular reason to define the same macro more than once, but if you do it is the first definition that is retained. In the #if example just above, the bonjour version will always be used.

Macros can make templates hard to understand. You may prefer to put the logic in a Java method rather than a macro, and call the method from the template using $methods.doSomething("foo") or whatever.

Block quoting

If you have text that should be treated verbatim, you can enclose it in #[[...]]#. The text represented by ... will be copied into the output. # and $ characters will have no effect in that text.

#[[ This is not a #directive, and this is not a $variable. ]]#

Including other templates

If you want to include a template from another file, you can use the #parse directive. This can be useful if you have macros that are shared between templates, for example.

#set ($foo = "bar")
#parse("macros.vm")
#mymacro($foo) ## #mymacro defined in macros.vm

For this to work, you will need to tell EscapeVelocity how to find “resources” such as macro.vm in the example. You might use something like this:

ResourceOpener resourceOpener = resourceName -> {
  InputStream inputStream = getClass().getResource(resourceName);
  if (inputStream == null) {
    throw new IOException("Unknown resource: " + resourceName);
  }
  return new BufferedReader(InputStreamReader(inputStream, StandardCharsets.UTF_8));
};
Template template = Template.parseFrom("foo.vm", resourceOpener);

In this case, the resourceOpener is used to find the main template foo.vm, as well as any templates it may reference in #parse directives.

Spaces

For the most part, spaces and newlines in the template are preserved exactly in the output. To avoid unwanted newlines, you may end up using ## comments. In the #foreach example above we had this:

#foreach ($product in $allProducts)${product}#if ($foreach.hasNext), #end#end

That was to avoid introducing unwanted spaces and newlines. A more readable way to achieve the same result is this:

#foreach ($product in $allProducts)##
${product}##
#if ($foreach.hasNext), #end##
#end

Spaces are ignored between the # of a directive and the ) that closes it, so there is no trace in the output of the spaces in #foreach ($product in $allProducts) or #if ($foreach.hasNext). Spaces are also ignored inside references, such as $indexme[ $i ] or $callme( $i , $j ).

If you are concerned about the detailed formatting of the text from the template, you may want to post-process it. For example, if it is Java code, you could use a formatter such as google-java-format. Then you shouldn't have to worry about extraneous spaces.