-
-
Notifications
You must be signed in to change notification settings - Fork 518
/
AbstractScriptMacro.java
303 lines (268 loc) · 10.8 KB
/
AbstractScriptMacro.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.rendering.macro.script;
import java.io.StringReader;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.commons.lang3.StringUtils;
import org.xwiki.context.Execution;
import org.xwiki.observation.ObservationManager;
import org.xwiki.rendering.block.Block;
import org.xwiki.rendering.macro.AbstractSignableMacro;
import org.xwiki.rendering.macro.MacroContentParser;
import org.xwiki.rendering.macro.MacroExecutionException;
import org.xwiki.rendering.macro.descriptor.ContentDescriptor;
import org.xwiki.rendering.parser.ParseException;
import org.xwiki.rendering.parser.Parser;
import org.xwiki.rendering.transformation.MacroTransformationContext;
import org.xwiki.rendering.util.ParserUtils;
import org.xwiki.script.event.ScriptEvaluatedEvent;
import org.xwiki.script.event.ScriptEvaluatingEvent;
/**
* Base Class for script evaluation macros.
* <p>
* It is not obvious to see how macro execution works just from looking at the code. A lot of checking and
* initialization is done in listeners to the {@link org.xwiki.script.event.ScriptEvaluatingEvent} and
* {@link org.xwiki.script.event.ScriptEvaluatedEvent}. E.g. the check for programming rights for JSR223 scripts, check
* for nested script macros and selecting the right class loader is done there.
* </p>
*
* @param <P> the type of macro parameters bean.
* @version $Id$
* @since 1.7M3
*/
public abstract class AbstractScriptMacro<P extends ScriptMacroParameters> extends AbstractSignableMacro<P> implements
ScriptMacro
{
/**
* The default description of the script macro content.
*/
protected static final String CONTENT_DESCRIPTION = "the script to execute";
/**
* Used to find if the current document's author has programming rights.
*
* @deprecated since 2.5M1 (not used any more)
*/
@Inject
@Deprecated
protected org.xwiki.bridge.DocumentAccessBridge documentAccessBridge;
/**
* Used by subclasses.
*/
@Inject
protected Execution execution;
/**
* Used to parse the result of the script execution into a XDOM object when the macro is configured by the user to
* not interpret wiki syntax.
*/
@Inject
@Named("plain/1.0")
private Parser plainTextParser;
/**
* The parser used to parse box content and box title parameter.
*/
@Inject
private MacroContentParser contentParser;
/**
* Observation manager used to sent evaluation events.
*/
@Inject
private ObservationManager observation;
/**
* Utility to remove the top level paragraph.
*/
private ParserUtils parserUtils = new ParserUtils();
/**
* @param macroName the name of the macro (eg "groovy")
*/
public AbstractScriptMacro(String macroName)
{
super(macroName, null, ScriptMacroParameters.class);
setDefaultCategories(Set.of(DEFAULT_CATEGORY_DEVELOPMENT));
}
/**
* @param macroName the name of the macro (eg "groovy")
* @param macroDescription the text description of the macro.
*/
public AbstractScriptMacro(String macroName, String macroDescription)
{
super(macroName, macroDescription, ScriptMacroParameters.class);
setDefaultCategories(Set.of(DEFAULT_CATEGORY_DEVELOPMENT));
}
/**
* @param macroName the name of the macro (eg "groovy")
* @param macroDescription the text description of the macro.
* @param contentDescriptor the description of the macro content.
*/
public AbstractScriptMacro(String macroName, String macroDescription, ContentDescriptor contentDescriptor)
{
super(macroName, macroDescription, contentDescriptor, ScriptMacroParameters.class);
setDefaultCategories(Set.of(DEFAULT_CATEGORY_DEVELOPMENT));
}
/**
* @param macroName the name of the macro (eg "groovy")
* @param macroDescription the text description of the macro.
* @param parametersBeanClass class of the parameters bean for this macro.
*/
public AbstractScriptMacro(String macroName, String macroDescription,
Class< ? extends ScriptMacroParameters> parametersBeanClass)
{
super(macroName, macroDescription, parametersBeanClass);
setDefaultCategories(Set.of(DEFAULT_CATEGORY_DEVELOPMENT));
}
/**
* @param macroName the name of the macro (eg "groovy")
* @param macroDescription the text description of the macro.
* @param contentDescriptor the description of the macro content.
* @param parametersBeanClass class of the parameters bean for this macro.
*/
public AbstractScriptMacro(String macroName, String macroDescription, ContentDescriptor contentDescriptor,
Class< ? extends ScriptMacroParameters> parametersBeanClass)
{
super(macroName, macroDescription, contentDescriptor, parametersBeanClass);
setDefaultCategories(Set.of(DEFAULT_CATEGORY_DEVELOPMENT));
}
@Override
public List<Block> execute(P parameters, String content, MacroTransformationContext context)
throws MacroExecutionException
{
List<Block> result = Collections.emptyList();
if (StringUtils.isNotEmpty(content)) {
try {
// send evaluation starts event
ScriptEvaluatingEvent event = new ScriptEvaluatingEvent(getDescriptor().getId().getId());
this.observation.notify(event, context, parameters);
if (event.isCanceled()) {
throw new MacroExecutionException(event.getReason());
}
// 2) Run script engine on macro block content
List<Block> blocks = evaluateBlock(parameters, content, context);
if (parameters.isOutput()) {
result = blocks;
}
} finally {
// send evaluation finished event
this.observation.notify(new ScriptEvaluatedEvent(getDescriptor().getId().getId()), context, parameters);
}
}
return result;
}
/**
* Convert script result as a {@link Block} list.
*
* @param content the script result to parse.
* @param parameters the macro parameters.
* @param context the context of the macro transformation.
* @return the {@link Block}s.
* @throws MacroExecutionException Failed to find source parser.
* @since 2.1M1
*/
protected List<Block> parseScriptResult(String content, P parameters, MacroTransformationContext context)
throws MacroExecutionException
{
List<Block> result;
if (parameters.isWiki()) {
result = parseSourceSyntax(content, context);
} else {
try {
result = this.plainTextParser.parse(new StringReader(content)).getChildren();
} catch (ParseException e) {
// This shouldn't happen since the parser cannot throw an exception since the source is a memory
// String.
throw new MacroExecutionException("Failed to parse link label as plain text", e);
}
}
// 3) If in inline mode remove any top level paragraph
if (context.isInline()) {
this.parserUtils.convertToInline(result);
}
return result;
}
/**
* Execute provided script.
*
* @param parameters the macro parameters.
* @param content the script to execute.
* @param context the context of the macro transformation.
* @return the result of script execution.
* @throws MacroExecutionException failed to evaluate provided content.
* @deprecated since 2.4M2 use {@link #evaluateString(ScriptMacroParameters, String, MacroTransformationContext)}
* instead
*/
@Deprecated
protected String evaluate(P parameters, String content, MacroTransformationContext context)
throws MacroExecutionException
{
return "";
}
/**
* Execute provided script and return {@link String} based result.
*
* @param parameters the macro parameters.
* @param content the script to execute.
* @param context the context of the macro transformation.
* @return the result of script execution.
* @throws MacroExecutionException failed to evaluate provided content.
* @since 2.4M2
*/
protected String evaluateString(P parameters, String content, MacroTransformationContext context)
throws MacroExecutionException
{
// Call old method for retro-compatibility
return evaluate(parameters, content, context);
}
/**
* Execute provided script and return {@link Block} based result.
*
* @param parameters the macro parameters.
* @param content the script to execute.
* @param context the context of the macro transformation.
* @return the result of script execution.
* @throws MacroExecutionException failed to evaluate provided content.
* @since 2.4M2
*/
protected List<Block> evaluateBlock(P parameters, String content, MacroTransformationContext context)
throws MacroExecutionException
{
String scriptResult = evaluateString(parameters, content, context);
List<Block> result = Collections.emptyList();
if (parameters.isOutput()) {
// Run the wiki syntax parser on the script-rendered content
result = parseScriptResult(scriptResult, parameters, context);
}
return result;
}
/**
* Parse provided content with the parser of the current wiki syntax.
*
* @param content the content to parse.
* @param context the context of the macro transformation.
* @return the result of the parsing.
* @throws MacroExecutionException failed to parse content
*/
protected List<Block> parseSourceSyntax(String content, MacroTransformationContext context)
throws MacroExecutionException
{
return this.contentParser.parse(content, context, false, false).getChildren();
}
}