View Javadoc
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 }