1 /*
2 * Copyright (C) 2002-2003,2017-2023 Dipl.-Inform. Kai Hofmann. All rights reserved!
3 */
4 package de.powerstat.phplib.templateengine.intern;
5
6
7 import java.util.ArrayList;
8 import java.util.Collections;
9 import java.util.List;
10 import java.util.Map;
11 import java.util.Map.Entry;
12 import java.util.Objects;
13 import java.util.Set;
14 import java.util.TreeSet;
15 import java.util.concurrent.ConcurrentHashMap;
16 import java.util.regex.Pattern;
17
18 import org.apache.logging.log4j.LogManager;
19 import org.apache.logging.log4j.Logger;
20
21
22 /**
23 * Template variable manager.
24 */
25 public final class VariableManager
26 {
27 /**
28 * Logger.
29 */
30 private static final Logger LOGGER = LogManager.getLogger(VariableManager.class);
31
32 /**
33 * Template matcher regexp pattern.
34 */
35 private static final Pattern TEMPLATE_MATCHER_REGEXP = Pattern.compile("\\{([^{^}\n\r\t :]+)\\}"); //$NON-NLS-1$
36
37 /**
38 * Block matcher regexp.
39 */
40 private static final Pattern BLOCK_MATCHER_REGEXP = Pattern.compile("\\{([^ \\t\\r\\n}]+)\\}"); //$NON-NLS-1$
41
42 /**
43 * Temporary variables map.
44 */
45 private final Map<String, String> vars = new ConcurrentHashMap<>();
46
47
48 /**
49 * Copy constructor.
50 *
51 * @param vManager Variable manager to copy from
52 * @throws NullPointerException If vManager is null
53 */
54 public VariableManager(final VariableManager vManager)
55 {
56 super();
57 Objects.requireNonNull(vManager, "vManager"); //$NON-NLS-1$
58 for (final Map.Entry<String, String> entry : vManager.vars.entrySet())
59 {
60 this.vars.put(entry.getKey(), entry.getValue());
61 }
62 }
63
64
65 /**
66 * Default constructor.
67 */
68 public VariableManager()
69 {
70 super();
71 }
72
73
74 /**
75 * Exists variable.
76 *
77 * @param varname Variable name
78 * @return true: Variable exists; false otherwise
79 */
80 public boolean existsVar(final String varname)
81 {
82 return this.vars.containsKey(varname);
83 }
84
85
86 /**
87 * Get template variable value.
88 *
89 * @param varname Template variable name
90 * @return Template variables value
91 * @throws NullPointerException If varname is null
92 * @throws IllegalArgumentException If varname is empty
93 */
94 public String getVar(final String varname)
95 {
96 final String value = this.vars.get(varname);
97 return (value == null) ? "" : value; //$NON-NLS-1$
98 }
99
100
101 /**
102 * Set template variables value.
103 *
104 * @param varname Template variable name
105 * @param value Template variable value, could be null
106 * @throws NullPointerException If varname is null
107 * @throws IllegalArgumentException If varname is empty
108 */
109 public void setVar(final String varname, final String value)
110 {
111 // if (!value.matches("^.+$"))
112 this.vars.put(varname, (value == null) ? "" : value); //$NON-NLS-1$
113 }
114
115
116 /**
117 * Unset template variable.
118 *
119 * @param varname Template variable name
120 * @throws NullPointerException If varname is null
121 * @throws IllegalArgumentException If varname is empty
122 */
123 @SuppressWarnings("java:S1120")
124 public void unsetVar(final String varname)
125 {
126 /* String value = */ this.vars.remove(varname);
127 }
128
129
130 /**
131 * Get list with all undefined template variables.
132 *
133 * @param varname Variable to parse for undefined variables
134 * @return List with undefined template variables names
135 * @throws NullPointerException If varname is null
136 * @throws IllegalArgumentException If varname is empty
137 */
138 public List<String> getUndefined(final String varname)
139 {
140 // TODO asserts
141 final var matcher = VariableManager.BLOCK_MATCHER_REGEXP.matcher(getVar(varname));
142 boolean result = matcher.find();
143 final List<String> undefvars = new ArrayList<>();
144 while (result)
145 {
146 final String vname = matcher.group(1);
147 if (!this.vars.containsKey(vname) && !undefvars.contains(vname))
148 {
149 undefvars.add(vname);
150 }
151 result = matcher.find();
152 }
153 return Collections.unmodifiableList(undefvars);
154 }
155
156
157 /* *
158 * Replace variables old version.
159 *
160 * @param block Template block
161 * @return Template/Block with replaced variables
162 */
163 /*
164 private String replaceVarsOld(String block)
165 {
166 assert (block != null) && !block.isEmpty();
167 // loop over all known variables an replace them
168 if (!this.vars.isEmpty())
169 {
170 final Set<Entry<String, String>> tempVarsSet = this.vars.entrySet();
171 for (Entry<String, String> mapEntry : tempVarsSet)
172 {
173 // convert into regexp (special char filter)
174 block = Pattern.compile("\\{" + mapEntry.getKey() + "\\}").matcher(block).replaceAll(mapEntry.getValue()); //$NON-NLS-1$ //$NON-NLS-2$
175 }
176 }
177 return block;
178 }
179 */
180
181
182 /**
183 * Replace variables new version.
184 *
185 * @param block Template block
186 * @return Template/Block with replaced variables
187 */
188 private String replaceVarsNew(final String block)
189 {
190 // assert (block != null) && !block.isEmpty();
191 // assert block.matches("^.+$")
192 // Get variable names to replace from varname
193 final var matcherTemplate = VariableManager.TEMPLATE_MATCHER_REGEXP.matcher(block);
194 final Set<String> varsSetTemplate = new TreeSet<>();
195 while (matcherTemplate.find())
196 {
197 final var varnameTemplate = block.substring(matcherTemplate.start() + 1, matcherTemplate.end() - 1);
198 if (this.vars.containsKey(varnameTemplate))
199 {
200 varsSetTemplate.add(varnameTemplate);
201 }
202 }
203 String resBlock = block;
204 for (final String varName : varsSetTemplate)
205 {
206 resBlock = resBlock.replaceAll("\\{" + varName + "\\}", getVar(varName)); //$NON-NLS-1$ //$NON-NLS-2$
207 }
208 return resBlock;
209 }
210
211
212 /**
213 * Substitute variable with its content.
214 *
215 * @param varname Variable name
216 * @return Replaced variable content or empty string
217 * @throws NullPointerException If varname is null
218 * @throws IllegalArgumentException If varname is empty
219 */
220 public String subst(final String varname)
221 {
222 // return replaceVarsOld(getVar(varname));
223 return replaceVarsNew(getVar(varname));
224 }
225
226
227 /**
228 * Parse a variable and replace all variables within it by their content.
229 *
230 * @param target Target for parsing operation
231 * @param varname Parse the content of this variable
232 * @param append true for appending blocks to target, otherwise false for replacing targets content
233 * @return Variables content after parsing
234 * @throws NullPointerException If target or varname is null
235 * @throws IllegalArgumentException If target or varname is empty
236 */
237 @SuppressWarnings("java:S2301")
238 public String parse(final String target, final String varname, final boolean append)
239 {
240 LOGGER.debug("varname: {}", varname);
241 final String str = subst(varname);
242 LOGGER.debug("str: {}", str);
243 setVar(target, (append ? getVar(target) : "") + str); //$NON-NLS-1$
244 return str;
245 }
246
247
248 /**
249 * Get list of all template variables.
250 *
251 * @return Array with names of template variables
252 */
253 public List<String> getVars()
254 {
255 if (this.vars.isEmpty())
256 {
257 return Collections.emptyList();
258 }
259 final List<String> result = new ArrayList<>(this.vars.size());
260 for (final Entry<String, String> entry : this.vars.entrySet())
261 {
262 result.add(entry.getKey()); // entry.getValue();
263 }
264 return Collections.unmodifiableList(result);
265 }
266
267
268 /**
269 * Returns the string representation of this VariableManager.
270 *
271 * The exact details of this representation are unspecified and subject to change, but the following may be regarded as typical:
272 *
273 * " VariableManager[vars=[name, ...]]"
274 *
275 * @return String representation of this VariableManager.
276 * @see java.lang.Object#toString()
277 */
278 @Override
279 public String toString()
280 {
281 return new StringBuilder().append("VariableManager[vars=").append(getVars()).append(']').toString(); //$NON-NLS-1$
282 }
283
284
285 /**
286 * Calculate hash code.
287 *
288 * @return Hash
289 * @see java.lang.Object#hashCode()
290 */
291 @Override
292 public int hashCode()
293 {
294 return Objects.hash(this.vars);
295 }
296
297
298 /**
299 * Is equal with another object.
300 *
301 * @param obj Object
302 * @return true when equal, false otherwise
303 * @see java.lang.Object#equals(java.lang.Object)
304 */
305 @Override
306 public boolean equals(final Object obj)
307 {
308 if (this == obj)
309 {
310 return true;
311 }
312 if (!(obj instanceof VariableManager))
313 {
314 return false;
315 }
316 final VariableManager other = (VariableManager)obj;
317 return this.vars.equals(other.vars);
318 }
319
320 }