1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.juneau.svl;
18
19 import static org.apache.juneau.commons.utils.AssertionUtils.*;
20 import static org.apache.juneau.commons.utils.ThrowableUtils.*;
21
22 import java.io.*;
23
24 /**
25 * Abstract superclass of all Simple Var Language variables.
26 *
27 * <p>
28 * Vars are used to convert simple variables of the form <js>"$varName{varKey}"</js> into something else by the
29 * {@link VarResolver} class.
30 *
31 * <p>
32 * Subclasses must implement one of the following two methods:
33 * <ul class='javatree'>
34 * <li class='jm'>{@link #resolve(VarResolverSession,String)} - For simple vars.
35 * <li class='jm'>{@link #resolveTo(VarResolverSession,Writer,String)} - For streamed vars.
36 * </ul>
37 *
38 * <p>
39 * Subclasses MUST implement a no-arg constructor so that class names can be passed to the
40 * {@link VarResolver.Builder#vars(Class...)} method.
41 * <br><b>They must also be thread safe!</b>
42 *
43 * <p>
44 * Two direct abstract subclasses are provided to differentiated between simple and streamed vars:
45 * <ul class='javatree'>
46 * <li class='jac'>{@link SimpleVar}
47 * <li class='jac'>{@link StreamedVar}
48 * </ul>
49 *
50 * <h5 class='section'>See Also:</h5><ul>
51 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/SimpleVariableLanguageBasics">Simple Variable Language Basics</a>
52
53 * </ul>
54 */
55 public abstract class Var {
56
57 private final String name;
58 final boolean streamed;
59
60 /**
61 * Constructor.
62 *
63 * @param name The name of this variable.
64 * @param streamed
65 * Whether this variable is 'streamed', meaning the {@link #resolveTo(VarResolverSession, Writer, String)} method
66 * is implemented.
67 * If <jk>false</jk>, then the {@link #resolve(VarResolverSession, String)} method is implemented.
68 */
69 public Var(String name, boolean streamed) {
70 assertArgNotNull("name", name);
71 this.name = name;
72 this.streamed = streamed;
73
74 for (var i = 0; i < name.length(); i++) {
75 // Need to make sure only ASCII characters are used.
76 var c = name.charAt(i);
77 if (c < 'A' || c > 'z' || (c > 'Z' && c < 'a'))
78 throw illegalArg("Invalid var name. Must consist of only uppercase and lowercase ASCII letters.");
79 }
80 }
81
82 /**
83 * The interface that needs to be implemented for subclasses of {@link SimpleVar}.
84 *
85 * @param session The session object used for a single instance of a var resolution.
86 * @param arg The inside argument of the variable.
87 * @return The resolved value.
88 * @throws Exception Any exception can be thrown.
89 */
90 public abstract String resolve(VarResolverSession session, String arg) throws Exception;
91
92 /**
93 * The interface that needs to be implemented for subclasses of {@link StreamedVar}.
94 *
95 * @param session The session object used for a single instance of a var resolution.
96 * @param w The writer to send the resolved value to.
97 * @param arg The inside argument of the variable.
98 * @throws Exception Any exception can be thrown.
99 */
100 public abstract void resolveTo(VarResolverSession session, Writer w, String arg) throws Exception;
101
102 /**
103 * Returns whether nested variables are supported by this variable.
104 *
105 * <p>
106 * For example, in <js>"$X{$Y{xxx}}"</js>, $Y is a nested variable that will be resolved if this method returns
107 * <jk>true</jk>.
108 *
109 * <p>
110 * The default implementation of this method always returns <jk>true</jk>.
111 * Subclasses can override this method to override the default behavior.
112 *
113 * @return <jk>true</jk> if nested variables are supported by this variable.
114 */
115 protected boolean allowNested() {
116 return true;
117 }
118
119 /**
120 * Returns whether variables in the resolved contents of this variable should also be resolved.
121 *
122 * <p>
123 * For example, if <js>"$X{xxx}"</js> resolves to <js>"$Y{xxx}"</js>, then the $Y variable will be recursively
124 * resolved if this method returns <jk>true</jk>.
125 *
126 * <p>
127 * The default implementation of this method always returns <jk>true</jk>.
128 * <br>Subclasses can override this method to override the default behavior.
129 *
130 * <div class='warn'>
131 * As a general rule, variables that resolve user-entered data should not be recursively resolved as this may
132 * cause a security hole.
133 * </div>
134 *
135 * @return <jk>true</jk> if resolved variables should be recursively resolved.
136 */
137 protected boolean allowRecurse() {
138 return true;
139 }
140
141 /**
142 * Returns <jk>true</jk> if this variable can be resolved in the specified session.
143 *
144 * <p>
145 * For example, some variable cannot resolve unless specific context or session objects are available.
146 *
147 * @param session The current session.
148 * @return <jk>true</jk> if this variable can be resolved in the specified session.
149 */
150 protected boolean canResolve(VarResolverSession session) {
151 return true;
152 }
153
154 /**
155 * The method called from {@link VarResolver}.
156 *
157 * <p>
158 * Can be overridden to intercept the request and do special handling.
159 * <br>Default implementation simply calls resolve(String).
160 *
161 * @param session The session object used for a single instance of a string resolution.
162 * @param arg The inside argument of the variable.
163 * @return The resolved value.
164 * @throws Exception Any exception can be thrown.
165 */
166 protected String doResolve(VarResolverSession session, String arg) throws Exception {
167 return resolve(session, arg);
168 }
169
170 /**
171 * Return the name of this variable.
172 *
173 * <p>
174 * For example, the system property variable returns <js>"S"</js> since the format of the variable is
175 * <js>"$S{system.property}"</js>.
176 *
177 * @return The name of this variable.
178 */
179 protected String getName() { return name; }
180 }