001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.maven.tools.plugin.generator;
020
021import java.io.File;
022import java.io.IOException;
023import java.io.OutputStreamWriter;
024import java.io.Writer;
025import java.net.URI;
026import java.util.LinkedHashMap;
027import java.util.LinkedHashSet;
028import java.util.List;
029import java.util.Map;
030import java.util.Set;
031
032import org.apache.maven.plugin.descriptor.MojoDescriptor;
033import org.apache.maven.plugin.descriptor.Parameter;
034import org.apache.maven.plugin.descriptor.PluginDescriptor;
035import org.apache.maven.plugin.descriptor.Requirement;
036import org.apache.maven.project.MavenProject;
037import org.apache.maven.tools.plugin.ExtendedMojoDescriptor;
038import org.apache.maven.tools.plugin.PluginDescriptorHelper;
039import org.apache.maven.tools.plugin.PluginToolsRequest;
040import org.apache.maven.tools.plugin.javadoc.JavadocLinkGenerator;
041import org.apache.maven.tools.plugin.util.PluginUtils;
042import org.codehaus.plexus.util.StringUtils;
043import org.codehaus.plexus.util.io.CachingOutputStream;
044import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
045import org.codehaus.plexus.util.xml.XMLWriter;
046import org.slf4j.Logger;
047import org.slf4j.LoggerFactory;
048
049import static java.nio.charset.StandardCharsets.UTF_8;
050
051/**
052 * Serializes
053 * <ol>
054 * <li>a standard <a href="/ref/current/maven-plugin-api/plugin.html">Maven Plugin Descriptor XML file</a></li>
055 * <li>a descriptor containing a limited set of elements for {@link PluginHelpGenerator}</li>
056 * <li>an enhanced descriptor containing HTML values for some elements (instead of plain text as for the other two)
057 * for {@code org.apache.maven.plugin.plugin.report.GoalRenderer}</li>
058 * </ol>
059 * from a given in-memory descriptor. The in-memory descriptor acting as source is supposed to contain XHTML values
060 * for description elements.
061 */
062public class PluginDescriptorFilesGenerator implements Generator {
063    private static final Logger LOG = LoggerFactory.getLogger(PluginDescriptorFilesGenerator.class);
064
065    /**
066     * The type of the plugin descriptor file
067     */
068    enum DescriptorType {
069        STANDARD,
070        LIMITED_FOR_HELP_MOJO,
071        XHTML
072    }
073
074    @Override
075    public void execute(File destinationDirectory, PluginToolsRequest request) throws GeneratorException {
076        try {
077            // write standard plugin.xml descriptor
078            File f = new File(destinationDirectory, "plugin.xml");
079            writeDescriptor(f, request, DescriptorType.STANDARD);
080
081            // write plugin-help.xml help-descriptor (containing only a limited set of attributes)
082            MavenProject mavenProject = request.getProject();
083            f = new File(destinationDirectory, PluginHelpGenerator.getPluginHelpPath(mavenProject));
084            writeDescriptor(f, request, DescriptorType.LIMITED_FOR_HELP_MOJO);
085
086            // write enhanced plugin-enhanced.xml descriptor (containing some XHTML values)
087            f = getEnhancedDescriptorFilePath(mavenProject);
088            writeDescriptor(f, request, DescriptorType.XHTML);
089        } catch (IOException e) {
090            throw new GeneratorException(e.getMessage(), e);
091        }
092    }
093
094    public static File getEnhancedDescriptorFilePath(MavenProject project) {
095        return new File(project.getBuild().getDirectory(), "plugin-enhanced.xml");
096    }
097
098    private String getVersion() {
099        Package p = this.getClass().getPackage();
100        String version = (p == null) ? null : p.getSpecificationVersion();
101        return (version == null) ? "SNAPSHOT" : version;
102    }
103
104    public void writeDescriptor(File destinationFile, PluginToolsRequest request, DescriptorType type)
105            throws IOException {
106        PluginDescriptor pluginDescriptor = request.getPluginDescriptor();
107
108        if (!destinationFile.getParentFile().exists()) {
109            destinationFile.getParentFile().mkdirs();
110        }
111
112        String apiVersion = request.getPluginDescriptor().getRequiredMavenVersion();
113        boolean isV4 = apiVersion != null && apiVersion.startsWith("4.");
114
115        try (Writer writer = new OutputStreamWriter(new CachingOutputStream(destinationFile), UTF_8)) {
116            XMLWriter w = new PrettyPrintXMLWriter(writer, UTF_8.name(), null);
117
118            final String additionalInfo;
119            switch (type) {
120                case LIMITED_FOR_HELP_MOJO:
121                    additionalInfo = " (for help mojo with limited elements)";
122                    break;
123                case XHTML:
124                    additionalInfo = " (enhanced XHTML version (used for plugin:report))";
125                    break;
126                default:
127                    additionalInfo = "";
128                    break;
129            }
130            w.writeMarkup("\n<!-- Generated by maven-plugin-tools " + getVersion() + additionalInfo + "-->\n\n");
131
132            w.startElement("plugin");
133            if (isV4) {
134                w.addAttribute("xmlns", "http://maven.apache.org/PLUGIN/2.0.0");
135                w.addAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
136                w.addAttribute(
137                        "xsi:location",
138                        "http://maven.apache.org/PLUGIN/2.0.0 https://maven.apache.org/xsd/plugin-2.0.0.xsd");
139            }
140
141            GeneratorUtils.element(w, "name", pluginDescriptor.getName());
142
143            GeneratorUtils.element(w, "description", pluginDescriptor.getDescription());
144
145            GeneratorUtils.element(w, "groupId", pluginDescriptor.getGroupId());
146
147            GeneratorUtils.element(w, "artifactId", pluginDescriptor.getArtifactId());
148
149            GeneratorUtils.element(w, "version", pluginDescriptor.getVersion());
150
151            GeneratorUtils.element(w, "goalPrefix", pluginDescriptor.getGoalPrefix());
152
153            if (type != DescriptorType.LIMITED_FOR_HELP_MOJO) {
154                GeneratorUtils.element(w, "isolatedRealm", String.valueOf(pluginDescriptor.isIsolatedRealm()));
155
156                GeneratorUtils.element(
157                        w, "inheritedByDefault", String.valueOf(pluginDescriptor.isInheritedByDefault()));
158
159                if (StringUtils.isNotBlank(PluginDescriptorHelper.getRequiredJavaVersion(pluginDescriptor))) {
160                    GeneratorUtils.element(
161                            w, "requiredJavaVersion", PluginDescriptorHelper.getRequiredJavaVersion(pluginDescriptor));
162                }
163                if (StringUtils.isNotBlank(pluginDescriptor.getRequiredMavenVersion())) {
164                    GeneratorUtils.element(w, "requiredMavenVersion", pluginDescriptor.getRequiredMavenVersion());
165                }
166            }
167
168            w.startElement("mojos");
169
170            final JavadocLinkGenerator javadocLinkGenerator;
171            if (request.getInternalJavadocBaseUrl() != null
172                    || (request.getExternalJavadocBaseUrls() != null
173                            && !request.getExternalJavadocBaseUrls().isEmpty())) {
174                javadocLinkGenerator = new JavadocLinkGenerator(
175                        request.getInternalJavadocBaseUrl(),
176                        request.getInternalJavadocVersion(),
177                        request.getExternalJavadocBaseUrls(),
178                        request.getSettings());
179            } else {
180                javadocLinkGenerator = null;
181            }
182            if (pluginDescriptor.getMojos() != null) {
183                List<MojoDescriptor> descriptors = pluginDescriptor.getMojos();
184
185                PluginUtils.sortMojos(descriptors);
186
187                for (MojoDescriptor descriptor : descriptors) {
188                    processMojoDescriptor(descriptor, w, type, javadocLinkGenerator, isV4);
189                }
190            }
191
192            w.endElement();
193
194            if (!isV4 && type != DescriptorType.LIMITED_FOR_HELP_MOJO) {
195                GeneratorUtils.writeDependencies(w, pluginDescriptor);
196            }
197
198            w.endElement();
199
200            writer.flush();
201        }
202    }
203
204    /**
205     * @param type
206     * @param containsXhtmlValue
207     * @param text
208     * @return the normalized text value (i.e. potentially converted to XHTML)
209     */
210    private static String getTextValue(DescriptorType type, boolean containsXhtmlValue, String text) {
211        final String xhtmlText;
212        if (!containsXhtmlValue) // text comes from legacy extractor
213        {
214            xhtmlText = GeneratorUtils.makeHtmlValid(text);
215        } else {
216            xhtmlText = text;
217        }
218        if (type != DescriptorType.XHTML) {
219            return new HtmlToPlainTextConverter().convert(text);
220        } else {
221            return xhtmlText;
222        }
223    }
224
225    @SuppressWarnings("deprecation")
226    protected void processMojoDescriptor(
227            MojoDescriptor mojoDescriptor,
228            XMLWriter w,
229            DescriptorType type,
230            JavadocLinkGenerator javadocLinkGenerator,
231            boolean isV4) {
232        boolean containsXhtmlTextValues = mojoDescriptor instanceof ExtendedMojoDescriptor
233                && ((ExtendedMojoDescriptor) mojoDescriptor).containsXhtmlTextValues();
234
235        w.startElement("mojo");
236
237        // ----------------------------------------------------------------------
238        //
239        // ----------------------------------------------------------------------
240
241        w.startElement("goal");
242        w.writeText(mojoDescriptor.getGoal());
243        w.endElement();
244
245        // ----------------------------------------------------------------------
246        //
247        // ----------------------------------------------------------------------
248
249        String description = mojoDescriptor.getDescription();
250
251        if (description != null && !description.isEmpty()) {
252            w.startElement("description");
253            w.writeText(getTextValue(type, containsXhtmlTextValues, mojoDescriptor.getDescription()));
254            w.endElement();
255        }
256
257        // ----------------------------------------------------------------------
258        //
259        // ----------------------------------------------------------------------
260
261        if (StringUtils.isNotEmpty(mojoDescriptor.getDependencyResolutionRequired())) {
262            GeneratorUtils.element(
263                    w,
264                    isV4 ? "dependencyResolution" : "requiresDependencyResolution",
265                    mojoDescriptor.getDependencyResolutionRequired());
266        }
267
268        // ----------------------------------------------------------------------
269        //
270        // ----------------------------------------------------------------------
271
272        GeneratorUtils.element(
273                w,
274                isV4 ? "directInvocationOnly" : "requiresDirectInvocation",
275                String.valueOf(mojoDescriptor.isDirectInvocationOnly()));
276
277        // ----------------------------------------------------------------------
278        //
279        // ----------------------------------------------------------------------
280
281        GeneratorUtils.element(
282                w, isV4 ? "projectRequired" : "requiresProject", String.valueOf(mojoDescriptor.isProjectRequired()));
283
284        // ----------------------------------------------------------------------
285        //
286        // ----------------------------------------------------------------------
287
288        if (!isV4) {
289            GeneratorUtils.element(w, "requiresReports", String.valueOf(mojoDescriptor.isRequiresReports()));
290        }
291
292        // ----------------------------------------------------------------------
293        //
294        // ----------------------------------------------------------------------
295
296        GeneratorUtils.element(w, "aggregator", String.valueOf(mojoDescriptor.isAggregator()));
297
298        // ----------------------------------------------------------------------
299        //
300        // ----------------------------------------------------------------------
301
302        GeneratorUtils.element(
303                w, isV4 ? "onlineRequired" : "requiresOnline", String.valueOf(mojoDescriptor.isOnlineRequired()));
304
305        // ----------------------------------------------------------------------
306        //
307        // ----------------------------------------------------------------------
308
309        GeneratorUtils.element(w, "inheritedByDefault", String.valueOf(mojoDescriptor.isInheritedByDefault()));
310
311        // ----------------------------------------------------------------------
312        //
313        // ----------------------------------------------------------------------
314
315        if (StringUtils.isNotEmpty(mojoDescriptor.getPhase())) {
316            GeneratorUtils.element(w, "phase", mojoDescriptor.getPhase());
317        }
318
319        // ----------------------------------------------------------------------
320        //
321        // ----------------------------------------------------------------------
322
323        if (StringUtils.isNotEmpty(mojoDescriptor.getExecutePhase())) {
324            GeneratorUtils.element(w, "executePhase", mojoDescriptor.getExecutePhase());
325        }
326
327        if (StringUtils.isNotEmpty(mojoDescriptor.getExecuteGoal())) {
328            GeneratorUtils.element(w, "executeGoal", mojoDescriptor.getExecuteGoal());
329        }
330
331        if (StringUtils.isNotEmpty(mojoDescriptor.getExecuteLifecycle())) {
332            GeneratorUtils.element(w, "executeLifecycle", mojoDescriptor.getExecuteLifecycle());
333        }
334
335        // ----------------------------------------------------------------------
336        // After links (v4 API only)
337        // ----------------------------------------------------------------------
338
339        if (isV4 && mojoDescriptor instanceof ExtendedMojoDescriptor) {
340            List<ExtendedMojoDescriptor.AfterLink> afterLinks =
341                    ((ExtendedMojoDescriptor) mojoDescriptor).getAfterLinks();
342            if (!afterLinks.isEmpty()) {
343                w.startElement("afterLinks");
344                for (ExtendedMojoDescriptor.AfterLink afterLink : afterLinks) {
345                    w.startElement("afterLink");
346                    if (afterLink.getPhase() != null) {
347                        GeneratorUtils.element(w, "phase", afterLink.getPhase());
348                    }
349                    if (afterLink.getType() != null) {
350                        GeneratorUtils.element(w, "type", afterLink.getType());
351                    }
352                    if (afterLink.getScope() != null) {
353                        GeneratorUtils.element(w, "scope", afterLink.getScope());
354                    }
355                    w.endElement(); // afterLink
356                }
357                w.endElement(); // afterLinks
358            }
359        }
360
361        // ----------------------------------------------------------------------
362        //
363        // ----------------------------------------------------------------------
364
365        w.startElement("implementation");
366        w.writeText(mojoDescriptor.getImplementation());
367        w.endElement();
368
369        // ----------------------------------------------------------------------
370        //
371        // ----------------------------------------------------------------------
372
373        w.startElement("language");
374        w.writeText(mojoDescriptor.getLanguage());
375        w.endElement();
376
377        // ----------------------------------------------------------------------
378        //
379        // ----------------------------------------------------------------------
380
381        if (StringUtils.isNotEmpty(mojoDescriptor.getComponentConfigurator())) {
382            w.startElement("configurator");
383            w.writeText(mojoDescriptor.getComponentConfigurator());
384            w.endElement();
385        }
386
387        // ----------------------------------------------------------------------
388        //
389        // ----------------------------------------------------------------------
390
391        if (!isV4 && StringUtils.isNotEmpty(mojoDescriptor.getComponentComposer())) {
392            w.startElement("composer");
393            w.writeText(mojoDescriptor.getComponentComposer());
394            w.endElement();
395        }
396
397        // ----------------------------------------------------------------------
398        //
399        // ----------------------------------------------------------------------
400
401        if (!isV4) {
402            w.startElement("instantiationStrategy");
403            w.writeText(mojoDescriptor.getInstantiationStrategy());
404            w.endElement();
405        }
406
407        // ----------------------------------------------------------------------
408        // Strategy for handling repeated reference to mojo in
409        // the calculated (decorated, resolved) execution stack
410        // ----------------------------------------------------------------------
411
412        if (!isV4) {
413            w.startElement("executionStrategy");
414            w.writeText(mojoDescriptor.getExecutionStrategy());
415            w.endElement();
416        }
417
418        // ----------------------------------------------------------------------
419        //
420        // ----------------------------------------------------------------------
421
422        if (mojoDescriptor.getSince() != null) {
423            w.startElement("since");
424
425            if (StringUtils.isEmpty(mojoDescriptor.getSince())) {
426                w.writeText("No version given");
427            } else {
428                w.writeText(mojoDescriptor.getSince());
429            }
430
431            w.endElement();
432        }
433
434        // ----------------------------------------------------------------------
435        //
436        // ----------------------------------------------------------------------
437
438        if (mojoDescriptor.getDeprecated() != null) {
439            w.startElement("deprecated");
440
441            if (StringUtils.isEmpty(mojoDescriptor.getDeprecated())) {
442                w.writeText("No reason given");
443            } else {
444                w.writeText(getTextValue(type, containsXhtmlTextValues, mojoDescriptor.getDeprecated()));
445            }
446
447            w.endElement();
448        }
449
450        // ----------------------------------------------------------------------
451        // Extended (3.0) descriptor
452        // ----------------------------------------------------------------------
453
454        if (mojoDescriptor instanceof ExtendedMojoDescriptor) {
455            ExtendedMojoDescriptor extendedMojoDescriptor = (ExtendedMojoDescriptor) mojoDescriptor;
456            if (extendedMojoDescriptor.getDependencyCollectionRequired() != null) {
457                GeneratorUtils.element(
458                        w,
459                        isV4 ? "dependencyCollection" : "requiresDependencyCollection",
460                        extendedMojoDescriptor.getDependencyCollectionRequired());
461            }
462            if (!isV4) {
463                GeneratorUtils.element(w, "threadSafe", String.valueOf(extendedMojoDescriptor.isThreadSafe()));
464            }
465        }
466
467        // ----------------------------------------------------------------------
468        // Parameters
469        // ----------------------------------------------------------------------
470
471        List<Parameter> parameters = mojoDescriptor.getParameters();
472
473        w.startElement("parameters");
474
475        Map<String, Requirement> requirements = new LinkedHashMap<>();
476
477        Set<Parameter> configuration = new LinkedHashSet<>();
478
479        if (parameters != null) {
480            if (type == DescriptorType.LIMITED_FOR_HELP_MOJO) {
481                PluginUtils.sortMojoParameters(parameters);
482            }
483
484            for (Parameter parameter : parameters) {
485                String expression = getExpression(parameter);
486
487                if ((expression != null && !expression.isEmpty()) && expression.startsWith("${component.")) {
488                    // treat it as a component...a requirement, in other words.
489
490                    // remove "component." plus expression delimiters
491                    String role = expression.substring("${component.".length(), expression.length() - 1);
492
493                    String roleHint = null;
494
495                    int posRoleHintSeparator = role.indexOf('#');
496                    if (posRoleHintSeparator > 0) {
497                        roleHint = role.substring(posRoleHintSeparator + 1);
498
499                        role = role.substring(0, posRoleHintSeparator);
500                    }
501
502                    // TODO: remove deprecated expression
503                    requirements.put(parameter.getName(), new Requirement(role, roleHint));
504                } else if (parameter.getRequirement() != null) {
505                    requirements.put(parameter.getName(), parameter.getRequirement());
506                }
507                // don't show readonly parameters in help
508                else if (type != DescriptorType.LIMITED_FOR_HELP_MOJO || parameter.isEditable()) {
509                    // treat it as a normal parameter.
510
511                    w.startElement("parameter");
512
513                    GeneratorUtils.element(w, "name", parameter.getName());
514
515                    if (parameter.getAlias() != null) {
516                        GeneratorUtils.element(w, "alias", parameter.getAlias());
517                    }
518
519                    writeParameterType(w, type, javadocLinkGenerator, parameter, mojoDescriptor.getGoal());
520
521                    if (parameter.getSince() != null) {
522                        w.startElement("since");
523
524                        if (StringUtils.isEmpty(parameter.getSince())) {
525                            w.writeText("No version given");
526                        } else {
527                            w.writeText(parameter.getSince());
528                        }
529
530                        w.endElement();
531                    }
532
533                    if (parameter.getDeprecated() != null) {
534                        if (StringUtils.isEmpty(parameter.getDeprecated())) {
535                            GeneratorUtils.element(w, "deprecated", "No reason given");
536                        } else {
537                            GeneratorUtils.element(
538                                    w,
539                                    "deprecated",
540                                    getTextValue(type, containsXhtmlTextValues, parameter.getDeprecated()));
541                        }
542                    }
543
544                    if (!isV4 && parameter.getImplementation() != null) {
545                        GeneratorUtils.element(w, "implementation", parameter.getImplementation());
546                    }
547
548                    GeneratorUtils.element(w, "required", Boolean.toString(parameter.isRequired()));
549
550                    GeneratorUtils.element(w, "editable", Boolean.toString(parameter.isEditable()));
551
552                    GeneratorUtils.element(
553                            w, "description", getTextValue(type, containsXhtmlTextValues, parameter.getDescription()));
554
555                    if (isV4) {
556                        if (StringUtils.isNotEmpty(parameter.getExpression())) {
557                            GeneratorUtils.element(w, "expression", parameter.getExpression());
558                        }
559                        if (StringUtils.isNotEmpty(parameter.getDefaultValue())) {
560                            GeneratorUtils.element(w, "defaultValue", parameter.getDefaultValue());
561                        }
562                    } else {
563                        if (StringUtils.isNotEmpty(parameter.getDefaultValue())
564                                || StringUtils.isNotEmpty(parameter.getExpression())) {
565                            configuration.add(parameter);
566                        }
567                    }
568
569                    w.endElement();
570                }
571            }
572        }
573
574        w.endElement();
575
576        // ----------------------------------------------------------------------
577        // Configuration
578        // ----------------------------------------------------------------------
579
580        if (!configuration.isEmpty()) {
581            w.startElement("configuration");
582
583            for (Parameter parameter : configuration) {
584                if (type == DescriptorType.LIMITED_FOR_HELP_MOJO && !parameter.isEditable()) {
585                    // don't show readonly parameters in help
586                    continue;
587                }
588
589                w.startElement(parameter.getName());
590
591                // strip type by parameter type (generics) information
592                String parameterType = StringUtils.chomp(parameter.getType(), "<");
593                if (parameterType != null && !parameterType.isEmpty()) {
594                    w.addAttribute("implementation", parameterType);
595                }
596
597                if (parameter.getDefaultValue() != null) {
598                    w.addAttribute("default-value", parameter.getDefaultValue());
599                }
600
601                if (StringUtils.isNotEmpty(parameter.getExpression())) {
602                    w.writeText(parameter.getExpression());
603                }
604
605                w.endElement();
606            }
607
608            w.endElement();
609        }
610
611        // ----------------------------------------------------------------------
612        // Requirements
613        // ----------------------------------------------------------------------
614
615        if (!requirements.isEmpty() && type != DescriptorType.LIMITED_FOR_HELP_MOJO) {
616            w.startElement("requirements");
617
618            for (Map.Entry<String, Requirement> entry : requirements.entrySet()) {
619                String key = entry.getKey();
620                Requirement requirement = entry.getValue();
621
622                w.startElement("requirement");
623
624                GeneratorUtils.element(w, "role", requirement.getRole());
625
626                if (StringUtils.isNotEmpty(requirement.getRoleHint())) {
627                    GeneratorUtils.element(w, "role-hint", requirement.getRoleHint());
628                }
629
630                GeneratorUtils.element(w, "field-name", key);
631
632                w.endElement();
633            }
634
635            w.endElement();
636        }
637
638        w.endElement();
639    }
640
641    /**
642     * Writes parameter type information and potentially also the related javadoc URL.
643     *
644     * @param w
645     * @param type
646     * @param javadocLinkGenerator
647     * @param parameter
648     * @param goal
649     */
650    protected void writeParameterType(
651            XMLWriter w,
652            DescriptorType type,
653            JavadocLinkGenerator javadocLinkGenerator,
654            Parameter parameter,
655            String goal) {
656        String parameterType = parameter.getType();
657
658        if (type == DescriptorType.STANDARD) {
659            // strip type by parameter type (generics) information for standard plugin descriptor
660            parameterType = StringUtils.chomp(parameterType, "<");
661        }
662        GeneratorUtils.element(w, "type", parameterType);
663
664        if (type == DescriptorType.XHTML && javadocLinkGenerator != null) {
665            // skip primitives which never has javadoc
666            if (parameter.getType().indexOf('.') == -1) {
667                LOG.debug("Javadoc URLs are not available for primitive types like {}", parameter.getType());
668            } else {
669                try {
670                    URI javadocUrl = getJavadocUrlForType(javadocLinkGenerator, parameterType);
671                    GeneratorUtils.element(w, "typeJavadocUrl", javadocUrl.toString());
672                } catch (IllegalArgumentException e) {
673                    LOG.warn(
674                            "Could not get javadoc URL for type {} of parameter {} from goal {}: {}",
675                            parameter.getType(),
676                            parameter.getName(),
677                            goal,
678                            e.getMessage());
679                }
680            }
681        }
682    }
683
684    private static String extractBinaryNameForJavadoc(String type) {
685        final String binaryName;
686        int startOfParameterType = type.indexOf("<");
687        if (startOfParameterType != -1) {
688            // parse parameter type
689            String mainType = type.substring(0, startOfParameterType);
690
691            // some heuristics here
692            String[] parameterTypes = type.substring(startOfParameterType + 1, type.lastIndexOf(">"))
693                    .split(",\\s*");
694            switch (parameterTypes.length) {
695                case 1: // if only one parameter type, assume collection, first parameter type is most interesting
696                    binaryName = extractBinaryNameForJavadoc(parameterTypes[0]);
697                    break;
698                case 2: // if two parameter types assume map, second parameter type is most interesting
699                    binaryName = extractBinaryNameForJavadoc(parameterTypes[1]);
700                    break;
701                default:
702                    // all other cases link to main type
703                    binaryName = mainType;
704            }
705        } else {
706            binaryName = type;
707        }
708        return binaryName;
709    }
710
711    static URI getJavadocUrlForType(JavadocLinkGenerator javadocLinkGenerator, String type) {
712        return javadocLinkGenerator.createLink(extractBinaryNameForJavadoc(type));
713    }
714
715    /**
716     * Get the expression value, eventually surrounding it with <code>${ }</code>.
717     *
718     * @param parameter the parameter
719     * @return the expression value
720     */
721    private String getExpression(Parameter parameter) {
722        String expression = parameter.getExpression();
723        if (StringUtils.isNotBlank(expression) && !expression.contains("${")) {
724            expression = "${" + expression.trim() + "}";
725            parameter.setExpression(expression);
726        }
727        return expression;
728    }
729}