View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.tools.plugin.generator;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.io.OutputStreamWriter;
24  import java.io.Writer;
25  import java.net.URI;
26  import java.util.LinkedHashMap;
27  import java.util.LinkedHashSet;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Set;
31  
32  import org.apache.maven.plugin.descriptor.MojoDescriptor;
33  import org.apache.maven.plugin.descriptor.Parameter;
34  import org.apache.maven.plugin.descriptor.PluginDescriptor;
35  import org.apache.maven.plugin.descriptor.Requirement;
36  import org.apache.maven.project.MavenProject;
37  import org.apache.maven.tools.plugin.ExtendedMojoDescriptor;
38  import org.apache.maven.tools.plugin.ExtendedPluginDescriptor;
39  import org.apache.maven.tools.plugin.PluginToolsRequest;
40  import org.apache.maven.tools.plugin.javadoc.JavadocLinkGenerator;
41  import org.apache.maven.tools.plugin.util.PluginUtils;
42  import org.codehaus.plexus.util.StringUtils;
43  import org.codehaus.plexus.util.io.CachingOutputStream;
44  import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
45  import org.codehaus.plexus.util.xml.XMLWriter;
46  import org.slf4j.Logger;
47  import org.slf4j.LoggerFactory;
48  
49  import static java.nio.charset.StandardCharsets.UTF_8;
50  
51  /**
52   * Serializes
53   * <ol>
54   * <li>a standard <a href="/ref/current/maven-plugin-api/plugin.html">Maven Plugin Descriptor XML file</a></li>
55   * <li>a descriptor containing a limited set of elements for {@link PluginHelpGenerator}</li>
56   * <li>an enhanced descriptor containing HTML values for some elements (instead of plain text as for the other two)
57   * for {@code org.apache.maven.plugin.plugin.report.GoalRenderer}</li>
58   * </ol>
59   * from a given in-memory descriptor. The in-memory descriptor acting as source is supposed to contain XHTML values
60   * for description elements.
61   *
62   */
63  public class PluginDescriptorFilesGenerator implements Generator {
64      private static final Logger LOG = LoggerFactory.getLogger(PluginDescriptorFilesGenerator.class);
65  
66      /**
67       * The type of the plugin descriptor file
68       */
69      enum DescriptorType {
70          STANDARD,
71          LIMITED_FOR_HELP_MOJO,
72          XHTML
73      }
74  
75      @Override
76      public void execute(File destinationDirectory, PluginToolsRequest request) throws GeneratorException {
77          try {
78              // write standard plugin.xml descriptor
79              File f = new File(destinationDirectory, "plugin.xml");
80              writeDescriptor(f, request, DescriptorType.STANDARD);
81  
82              // write plugin-help.xml help-descriptor (containing only a limited set of attributes)
83              MavenProject mavenProject = request.getProject();
84              f = new File(destinationDirectory, PluginHelpGenerator.getPluginHelpPath(mavenProject));
85              writeDescriptor(f, request, DescriptorType.LIMITED_FOR_HELP_MOJO);
86  
87              // write enhanced plugin-enhanced.xml descriptor (containing some XHTML values)
88              f = getEnhancedDescriptorFilePath(mavenProject);
89              writeDescriptor(f, request, DescriptorType.XHTML);
90          } catch (IOException e) {
91              throw new GeneratorException(e.getMessage(), e);
92          }
93      }
94  
95      public static File getEnhancedDescriptorFilePath(MavenProject project) {
96          return new File(project.getBuild().getDirectory(), "plugin-enhanced.xml");
97      }
98  
99      private String getVersion() {
100         Package p = this.getClass().getPackage();
101         String version = (p == null) ? null : p.getSpecificationVersion();
102         return (version == null) ? "SNAPSHOT" : version;
103     }
104 
105     public void writeDescriptor(File destinationFile, PluginToolsRequest request, DescriptorType type)
106             throws IOException {
107         PluginDescriptor pluginDescriptor = request.getPluginDescriptor();
108 
109         if (!destinationFile.getParentFile().exists()) {
110             destinationFile.getParentFile().mkdirs();
111         }
112 
113         try (Writer writer = new OutputStreamWriter(new CachingOutputStream(destinationFile), UTF_8)) {
114             XMLWriter w = new PrettyPrintXMLWriter(writer, UTF_8.name(), null);
115 
116             final String additionalInfo;
117             switch (type) {
118                 case LIMITED_FOR_HELP_MOJO:
119                     additionalInfo = " (for help mojo with limited elements)";
120                     break;
121                 case XHTML:
122                     additionalInfo = " (enhanced XHTML version (used for plugin:report))";
123                     break;
124                 default:
125                     additionalInfo = "";
126                     break;
127             }
128             w.writeMarkup("\n<!-- Generated by maven-plugin-tools " + getVersion() + additionalInfo + "-->\n\n");
129 
130             w.startElement("plugin");
131 
132             GeneratorUtils.element(w, "name", pluginDescriptor.getName());
133 
134             GeneratorUtils.element(w, "description", pluginDescriptor.getDescription());
135 
136             GeneratorUtils.element(w, "groupId", pluginDescriptor.getGroupId());
137 
138             GeneratorUtils.element(w, "artifactId", pluginDescriptor.getArtifactId());
139 
140             GeneratorUtils.element(w, "version", pluginDescriptor.getVersion());
141 
142             GeneratorUtils.element(w, "goalPrefix", pluginDescriptor.getGoalPrefix());
143 
144             if (type != DescriptorType.LIMITED_FOR_HELP_MOJO) {
145                 GeneratorUtils.element(w, "isolatedRealm", String.valueOf(pluginDescriptor.isIsolatedRealm()));
146 
147                 GeneratorUtils.element(
148                         w, "inheritedByDefault", String.valueOf(pluginDescriptor.isInheritedByDefault()));
149 
150                 if (pluginDescriptor instanceof ExtendedPluginDescriptor) {
151                     ExtendedPluginDescriptor extPluginDescriptor = (ExtendedPluginDescriptor) pluginDescriptor;
152                     if (StringUtils.isNotBlank(extPluginDescriptor.getRequiredJavaVersion())) {
153                         GeneratorUtils.element(w, "requiredJavaVersion", extPluginDescriptor.getRequiredJavaVersion());
154                     }
155                 }
156                 if (StringUtils.isNotBlank(pluginDescriptor.getRequiredMavenVersion())) {
157                     GeneratorUtils.element(w, "requiredMavenVersion", pluginDescriptor.getRequiredMavenVersion());
158                 }
159             }
160 
161             w.startElement("mojos");
162 
163             final JavadocLinkGenerator javadocLinkGenerator;
164             if (request.getInternalJavadocBaseUrl() != null
165                     || (request.getExternalJavadocBaseUrls() != null
166                             && !request.getExternalJavadocBaseUrls().isEmpty())) {
167                 javadocLinkGenerator = new JavadocLinkGenerator(
168                         request.getInternalJavadocBaseUrl(),
169                         request.getInternalJavadocVersion(),
170                         request.getExternalJavadocBaseUrls(),
171                         request.getSettings());
172             } else {
173                 javadocLinkGenerator = null;
174             }
175             if (pluginDescriptor.getMojos() != null) {
176                 List<MojoDescriptor> descriptors = pluginDescriptor.getMojos();
177 
178                 PluginUtils.sortMojos(descriptors);
179 
180                 for (MojoDescriptor descriptor : descriptors) {
181                     processMojoDescriptor(descriptor, w, type, javadocLinkGenerator);
182                 }
183             }
184 
185             w.endElement();
186 
187             if (type != DescriptorType.LIMITED_FOR_HELP_MOJO) {
188                 GeneratorUtils.writeDependencies(w, pluginDescriptor);
189             }
190 
191             w.endElement();
192 
193             writer.flush();
194         }
195     }
196 
197     /**
198      *
199      * @param type
200      * @param containsXhtmlValue
201      * @param text
202      * @return the normalized text value (i.e. potentially converted to XHTML)
203      */
204     private static String getTextValue(DescriptorType type, boolean containsXhtmlValue, String text) {
205         final String xhtmlText;
206         if (!containsXhtmlValue) // text comes from legacy extractor
207         {
208             xhtmlText = GeneratorUtils.makeHtmlValid(text);
209         } else {
210             xhtmlText = text;
211         }
212         if (type != DescriptorType.XHTML) {
213             return new HtmlToPlainTextConverter().convert(text);
214         } else {
215             return xhtmlText;
216         }
217     }
218 
219     @SuppressWarnings("deprecation")
220     protected void processMojoDescriptor(
221             MojoDescriptor mojoDescriptor,
222             XMLWriter w,
223             DescriptorType type,
224             JavadocLinkGenerator javadocLinkGenerator) {
225         boolean containsXhtmlTextValues = mojoDescriptor instanceof ExtendedMojoDescriptor
226                 && ((ExtendedMojoDescriptor) mojoDescriptor).containsXhtmlTextValues();
227 
228         w.startElement("mojo");
229 
230         // ----------------------------------------------------------------------
231         //
232         // ----------------------------------------------------------------------
233 
234         w.startElement("goal");
235         w.writeText(mojoDescriptor.getGoal());
236         w.endElement();
237 
238         // ----------------------------------------------------------------------
239         //
240         // ----------------------------------------------------------------------
241 
242         String description = mojoDescriptor.getDescription();
243 
244         if (description != null && !description.isEmpty()) {
245             w.startElement("description");
246             w.writeText(getTextValue(type, containsXhtmlTextValues, mojoDescriptor.getDescription()));
247             w.endElement();
248         }
249 
250         // ----------------------------------------------------------------------
251         //
252         // ----------------------------------------------------------------------
253 
254         if (StringUtils.isNotEmpty(mojoDescriptor.isDependencyResolutionRequired())) {
255             GeneratorUtils.element(w, "requiresDependencyResolution", mojoDescriptor.isDependencyResolutionRequired());
256         }
257 
258         // ----------------------------------------------------------------------
259         //
260         // ----------------------------------------------------------------------
261 
262         GeneratorUtils.element(w, "requiresDirectInvocation", String.valueOf(mojoDescriptor.isDirectInvocationOnly()));
263 
264         // ----------------------------------------------------------------------
265         //
266         // ----------------------------------------------------------------------
267 
268         GeneratorUtils.element(w, "requiresProject", String.valueOf(mojoDescriptor.isProjectRequired()));
269 
270         // ----------------------------------------------------------------------
271         //
272         // ----------------------------------------------------------------------
273 
274         GeneratorUtils.element(w, "requiresReports", String.valueOf(mojoDescriptor.isRequiresReports()));
275 
276         // ----------------------------------------------------------------------
277         //
278         // ----------------------------------------------------------------------
279 
280         GeneratorUtils.element(w, "aggregator", String.valueOf(mojoDescriptor.isAggregator()));
281 
282         // ----------------------------------------------------------------------
283         //
284         // ----------------------------------------------------------------------
285 
286         GeneratorUtils.element(w, "requiresOnline", String.valueOf(mojoDescriptor.isOnlineRequired()));
287 
288         // ----------------------------------------------------------------------
289         //
290         // ----------------------------------------------------------------------
291 
292         GeneratorUtils.element(w, "inheritedByDefault", String.valueOf(mojoDescriptor.isInheritedByDefault()));
293 
294         // ----------------------------------------------------------------------
295         //
296         // ----------------------------------------------------------------------
297 
298         if (StringUtils.isNotEmpty(mojoDescriptor.getPhase())) {
299             GeneratorUtils.element(w, "phase", mojoDescriptor.getPhase());
300         }
301 
302         // ----------------------------------------------------------------------
303         //
304         // ----------------------------------------------------------------------
305 
306         if (StringUtils.isNotEmpty(mojoDescriptor.getExecutePhase())) {
307             GeneratorUtils.element(w, "executePhase", mojoDescriptor.getExecutePhase());
308         }
309 
310         if (StringUtils.isNotEmpty(mojoDescriptor.getExecuteGoal())) {
311             GeneratorUtils.element(w, "executeGoal", mojoDescriptor.getExecuteGoal());
312         }
313 
314         if (StringUtils.isNotEmpty(mojoDescriptor.getExecuteLifecycle())) {
315             GeneratorUtils.element(w, "executeLifecycle", mojoDescriptor.getExecuteLifecycle());
316         }
317 
318         // ----------------------------------------------------------------------
319         //
320         // ----------------------------------------------------------------------
321 
322         w.startElement("implementation");
323         w.writeText(mojoDescriptor.getImplementation());
324         w.endElement();
325 
326         // ----------------------------------------------------------------------
327         //
328         // ----------------------------------------------------------------------
329 
330         w.startElement("language");
331         w.writeText(mojoDescriptor.getLanguage());
332         w.endElement();
333 
334         // ----------------------------------------------------------------------
335         //
336         // ----------------------------------------------------------------------
337 
338         if (StringUtils.isNotEmpty(mojoDescriptor.getComponentConfigurator())) {
339             w.startElement("configurator");
340             w.writeText(mojoDescriptor.getComponentConfigurator());
341             w.endElement();
342         }
343 
344         // ----------------------------------------------------------------------
345         //
346         // ----------------------------------------------------------------------
347 
348         if (StringUtils.isNotEmpty(mojoDescriptor.getComponentComposer())) {
349             w.startElement("composer");
350             w.writeText(mojoDescriptor.getComponentComposer());
351             w.endElement();
352         }
353 
354         // ----------------------------------------------------------------------
355         //
356         // ----------------------------------------------------------------------
357 
358         w.startElement("instantiationStrategy");
359         w.writeText(mojoDescriptor.getInstantiationStrategy());
360         w.endElement();
361 
362         // ----------------------------------------------------------------------
363         // Strategy for handling repeated reference to mojo in
364         // the calculated (decorated, resolved) execution stack
365         // ----------------------------------------------------------------------
366         w.startElement("executionStrategy");
367         w.writeText(mojoDescriptor.getExecutionStrategy());
368         w.endElement();
369 
370         // ----------------------------------------------------------------------
371         //
372         // ----------------------------------------------------------------------
373 
374         if (mojoDescriptor.getSince() != null) {
375             w.startElement("since");
376 
377             if (StringUtils.isEmpty(mojoDescriptor.getSince())) {
378                 w.writeText("No version given");
379             } else {
380                 w.writeText(mojoDescriptor.getSince());
381             }
382 
383             w.endElement();
384         }
385 
386         // ----------------------------------------------------------------------
387         //
388         // ----------------------------------------------------------------------
389 
390         if (mojoDescriptor.getDeprecated() != null) {
391             w.startElement("deprecated");
392 
393             if (StringUtils.isEmpty(mojoDescriptor.getDeprecated())) {
394                 w.writeText("No reason given");
395             } else {
396                 w.writeText(getTextValue(type, containsXhtmlTextValues, mojoDescriptor.getDeprecated()));
397             }
398 
399             w.endElement();
400         }
401 
402         // ----------------------------------------------------------------------
403         // Extended (3.0) descriptor
404         // ----------------------------------------------------------------------
405 
406         if (mojoDescriptor instanceof ExtendedMojoDescriptor) {
407             ExtendedMojoDescriptor extendedMojoDescriptor = (ExtendedMojoDescriptor) mojoDescriptor;
408             if (extendedMojoDescriptor.getDependencyCollectionRequired() != null) {
409                 GeneratorUtils.element(
410                         w, "requiresDependencyCollection", extendedMojoDescriptor.getDependencyCollectionRequired());
411             }
412 
413             GeneratorUtils.element(w, "threadSafe", String.valueOf(extendedMojoDescriptor.isThreadSafe()));
414 
415             boolean v4Api = extendedMojoDescriptor.isV4Api();
416             if (v4Api) {
417                 GeneratorUtils.element(w, "v4Api", String.valueOf(v4Api));
418             }
419         }
420 
421         // ----------------------------------------------------------------------
422         // Parameters
423         // ----------------------------------------------------------------------
424 
425         List<Parameter> parameters = mojoDescriptor.getParameters();
426 
427         w.startElement("parameters");
428 
429         Map<String, Requirement> requirements = new LinkedHashMap<>();
430 
431         Set<Parameter> configuration = new LinkedHashSet<>();
432 
433         if (parameters != null) {
434             if (type == DescriptorType.LIMITED_FOR_HELP_MOJO) {
435                 PluginUtils.sortMojoParameters(parameters);
436             }
437 
438             for (Parameter parameter : parameters) {
439                 String expression = getExpression(parameter);
440 
441                 if ((expression != null && !expression.isEmpty()) && expression.startsWith("${component.")) {
442                     // treat it as a component...a requirement, in other words.
443 
444                     // remove "component." plus expression delimiters
445                     String role = expression.substring("${component.".length(), expression.length() - 1);
446 
447                     String roleHint = null;
448 
449                     int posRoleHintSeparator = role.indexOf('#');
450                     if (posRoleHintSeparator > 0) {
451                         roleHint = role.substring(posRoleHintSeparator + 1);
452 
453                         role = role.substring(0, posRoleHintSeparator);
454                     }
455 
456                     // TODO: remove deprecated expression
457                     requirements.put(parameter.getName(), new Requirement(role, roleHint));
458                 } else if (parameter.getRequirement() != null) {
459                     requirements.put(parameter.getName(), parameter.getRequirement());
460                 }
461                 // don't show readonly parameters in help
462                 else if (type != DescriptorType.LIMITED_FOR_HELP_MOJO || parameter.isEditable()) {
463                     // treat it as a normal parameter.
464 
465                     w.startElement("parameter");
466 
467                     GeneratorUtils.element(w, "name", parameter.getName());
468 
469                     if (parameter.getAlias() != null) {
470                         GeneratorUtils.element(w, "alias", parameter.getAlias());
471                     }
472 
473                     writeParameterType(w, type, javadocLinkGenerator, parameter, mojoDescriptor.getGoal());
474 
475                     if (parameter.getSince() != null) {
476                         w.startElement("since");
477 
478                         if (StringUtils.isEmpty(parameter.getSince())) {
479                             w.writeText("No version given");
480                         } else {
481                             w.writeText(parameter.getSince());
482                         }
483 
484                         w.endElement();
485                     }
486 
487                     if (parameter.getDeprecated() != null) {
488                         if (StringUtils.isEmpty(parameter.getDeprecated())) {
489                             GeneratorUtils.element(w, "deprecated", "No reason given");
490                         } else {
491                             GeneratorUtils.element(
492                                     w,
493                                     "deprecated",
494                                     getTextValue(type, containsXhtmlTextValues, parameter.getDeprecated()));
495                         }
496                     }
497 
498                     if (parameter.getImplementation() != null) {
499                         GeneratorUtils.element(w, "implementation", parameter.getImplementation());
500                     }
501 
502                     GeneratorUtils.element(w, "required", Boolean.toString(parameter.isRequired()));
503 
504                     GeneratorUtils.element(w, "editable", Boolean.toString(parameter.isEditable()));
505 
506                     GeneratorUtils.element(
507                             w, "description", getTextValue(type, containsXhtmlTextValues, parameter.getDescription()));
508 
509                     if (StringUtils.isNotEmpty(parameter.getDefaultValue())
510                             || StringUtils.isNotEmpty(parameter.getExpression())) {
511                         configuration.add(parameter);
512                     }
513 
514                     w.endElement();
515                 }
516             }
517         }
518 
519         w.endElement();
520 
521         // ----------------------------------------------------------------------
522         // Configuration
523         // ----------------------------------------------------------------------
524 
525         if (!configuration.isEmpty()) {
526             w.startElement("configuration");
527 
528             for (Parameter parameter : configuration) {
529                 if (type == DescriptorType.LIMITED_FOR_HELP_MOJO && !parameter.isEditable()) {
530                     // don't show readonly parameters in help
531                     continue;
532                 }
533 
534                 w.startElement(parameter.getName());
535 
536                 // strip type by parameter type (generics) information
537                 String parameterType = StringUtils.chomp(parameter.getType(), "<");
538                 if (parameterType != null && !parameterType.isEmpty()) {
539                     w.addAttribute("implementation", parameterType);
540                 }
541 
542                 if (parameter.getDefaultValue() != null) {
543                     w.addAttribute("default-value", parameter.getDefaultValue());
544                 }
545 
546                 if (StringUtils.isNotEmpty(parameter.getExpression())) {
547                     w.writeText(parameter.getExpression());
548                 }
549 
550                 w.endElement();
551             }
552 
553             w.endElement();
554         }
555 
556         // ----------------------------------------------------------------------
557         // Requirements
558         // ----------------------------------------------------------------------
559 
560         if (!requirements.isEmpty() && type != DescriptorType.LIMITED_FOR_HELP_MOJO) {
561             w.startElement("requirements");
562 
563             for (Map.Entry<String, Requirement> entry : requirements.entrySet()) {
564                 String key = entry.getKey();
565                 Requirement requirement = entry.getValue();
566 
567                 w.startElement("requirement");
568 
569                 GeneratorUtils.element(w, "role", requirement.getRole());
570 
571                 if (StringUtils.isNotEmpty(requirement.getRoleHint())) {
572                     GeneratorUtils.element(w, "role-hint", requirement.getRoleHint());
573                 }
574 
575                 GeneratorUtils.element(w, "field-name", key);
576 
577                 w.endElement();
578             }
579 
580             w.endElement();
581         }
582 
583         w.endElement();
584     }
585 
586     /**
587      * Writes parameter type information and potentially also the related javadoc URL.
588      * @param w
589      * @param type
590      * @param javadocLinkGenerator
591      * @param parameter
592      * @param goal
593      */
594     protected void writeParameterType(
595             XMLWriter w,
596             DescriptorType type,
597             JavadocLinkGenerator javadocLinkGenerator,
598             Parameter parameter,
599             String goal) {
600         String parameterType = parameter.getType();
601 
602         if (type == DescriptorType.STANDARD) {
603             // strip type by parameter type (generics) information for standard plugin descriptor
604             parameterType = StringUtils.chomp(parameterType, "<");
605         }
606         GeneratorUtils.element(w, "type", parameterType);
607 
608         if (type == DescriptorType.XHTML && javadocLinkGenerator != null) {
609             // skip primitives which never has javadoc
610             if (parameter.getType().indexOf('.') == -1) {
611                 LOG.debug("Javadoc URLs are not available for primitive types like {}", parameter.getType());
612             } else {
613                 try {
614                     URI javadocUrl = getJavadocUrlForType(javadocLinkGenerator, parameterType);
615                     GeneratorUtils.element(w, "typeJavadocUrl", javadocUrl.toString());
616                 } catch (IllegalArgumentException e) {
617                     LOG.warn(
618                             "Could not get javadoc URL for type {} of parameter {} from goal {}: {}",
619                             parameter.getType(),
620                             parameter.getName(),
621                             goal,
622                             e.getMessage());
623                 }
624             }
625         }
626     }
627 
628     static URI getJavadocUrlForType(JavadocLinkGenerator javadocLinkGenerator, String type) {
629         final String binaryName;
630         int startOfParameterType = type.indexOf("<");
631         if (startOfParameterType != -1) {
632             // parse parameter type
633             String mainType = type.substring(0, startOfParameterType);
634 
635             // some heuristics here
636             String[] parameterTypes = type.substring(startOfParameterType + 1, type.lastIndexOf(">"))
637                     .split(",\\s*");
638             switch (parameterTypes.length) {
639                 case 1: // if only one parameter type, assume collection, first parameter type is most interesting
640                     binaryName = parameterTypes[0];
641                     break;
642                 case 2: // if two parameter types assume map, second parameter type is most interesting
643                     binaryName = parameterTypes[1];
644                     break;
645                 default:
646                     // all other cases link to main type
647                     binaryName = mainType;
648             }
649         } else {
650             binaryName = type;
651         }
652         return javadocLinkGenerator.createLink(binaryName);
653     }
654 
655     /**
656      * Get the expression value, eventually surrounding it with <code>${ }</code>.
657      *
658      * @param parameter the parameter
659      * @return the expression value
660      */
661     private String getExpression(Parameter parameter) {
662         String expression = parameter.getExpression();
663         if (StringUtils.isNotBlank(expression) && !expression.contains("${")) {
664             expression = "${" + expression.trim() + "}";
665             parameter.setExpression(expression);
666         }
667         return expression;
668     }
669 }