001package org.apache.maven.tools.plugin.extractor.javadoc; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.io.File; 023import java.net.MalformedURLException; 024import java.net.URL; 025import java.net.URLClassLoader; 026import java.util.ArrayList; 027import java.util.Collection; 028import java.util.List; 029import java.util.Map; 030import java.util.TreeMap; 031 032import org.apache.maven.artifact.Artifact; 033import org.apache.maven.plugin.descriptor.InvalidParameterException; 034import org.apache.maven.plugin.descriptor.InvalidPluginDescriptorException; 035import org.apache.maven.plugin.descriptor.MojoDescriptor; 036import org.apache.maven.plugin.descriptor.Parameter; 037import org.apache.maven.plugin.descriptor.Requirement; 038import org.apache.maven.project.MavenProject; 039import org.apache.maven.tools.plugin.ExtendedMojoDescriptor; 040import org.apache.maven.tools.plugin.PluginToolsRequest; 041import org.apache.maven.tools.plugin.extractor.ExtractionException; 042import org.apache.maven.tools.plugin.extractor.MojoDescriptorExtractor; 043import org.apache.maven.tools.plugin.util.PluginUtils; 044import org.codehaus.plexus.component.annotations.Component; 045import org.codehaus.plexus.logging.AbstractLogEnabled; 046import org.codehaus.plexus.util.StringUtils; 047 048import com.thoughtworks.qdox.JavaProjectBuilder; 049import com.thoughtworks.qdox.library.SortedClassLibraryBuilder; 050import com.thoughtworks.qdox.model.DocletTag; 051import com.thoughtworks.qdox.model.JavaClass; 052import com.thoughtworks.qdox.model.JavaField; 053import com.thoughtworks.qdox.model.JavaType; 054 055/** 056 * Extracts Mojo descriptors from <a href="http://java.sun.com/">Java</a> sources. 057 * <br/> 058 * For more information about the usage tag, have a look to: 059 * <a href="http://maven.apache.org/developers/mojo-api-specification.html"> 060 * http://maven.apache.org/developers/mojo-api-specification.html</a> 061 * 062 * @version $Id: JavaJavadocMojoDescriptorExtractor.html 1030109 2018-05-20 14:45:18Z hboutemy $ 063 * @see org.apache.maven.plugin.descriptor.MojoDescriptor 064 */ 065@Component( role = MojoDescriptorExtractor.class, hint = "java-javadoc" ) 066public class JavaJavadocMojoDescriptorExtractor 067 extends AbstractLogEnabled 068 implements MojoDescriptorExtractor, JavadocMojoAnnotation 069{ 070 /** 071 * @param parameter not null 072 * @param i positive number 073 * @throws InvalidParameterException if any 074 */ 075 protected void validateParameter( Parameter parameter, int i ) 076 throws InvalidParameterException 077 { 078 // TODO: remove when backward compatibility is no longer an issue. 079 String name = parameter.getName(); 080 081 if ( name == null ) 082 { 083 throw new InvalidParameterException( "name", i ); 084 } 085 086 // TODO: remove when backward compatibility is no longer an issue. 087 String type = parameter.getType(); 088 089 if ( type == null ) 090 { 091 throw new InvalidParameterException( "type", i ); 092 } 093 094 // TODO: remove when backward compatibility is no longer an issue. 095 String description = parameter.getDescription(); 096 097 if ( description == null ) 098 { 099 throw new InvalidParameterException( "description", i ); 100 } 101 } 102 103 // ---------------------------------------------------------------------- 104 // Mojo descriptor creation from @tags 105 // ---------------------------------------------------------------------- 106 107 /** 108 * @param javaClass not null 109 * @return a mojo descriptor 110 * @throws InvalidPluginDescriptorException if any 111 */ 112 protected MojoDescriptor createMojoDescriptor( JavaClass javaClass ) 113 throws InvalidPluginDescriptorException 114 { 115 ExtendedMojoDescriptor mojoDescriptor = new ExtendedMojoDescriptor(); 116 mojoDescriptor.setLanguage( "java" ); 117 mojoDescriptor.setImplementation( javaClass.getFullyQualifiedName() ); 118 mojoDescriptor.setDescription( javaClass.getComment() ); 119 120 // ---------------------------------------------------------------------- 121 // Mojo annotations in alphabetical order 122 // ---------------------------------------------------------------------- 123 124 // Aggregator flag 125 DocletTag aggregator = findInClassHierarchy( javaClass, JavadocMojoAnnotation.AGGREGATOR ); 126 if ( aggregator != null ) 127 { 128 mojoDescriptor.setAggregator( true ); 129 } 130 131 // Configurator hint 132 DocletTag configurator = findInClassHierarchy( javaClass, JavadocMojoAnnotation.CONFIGURATOR ); 133 if ( configurator != null ) 134 { 135 mojoDescriptor.setComponentConfigurator( configurator.getValue() ); 136 } 137 138 // Additional phase to execute first 139 DocletTag execute = findInClassHierarchy( javaClass, JavadocMojoAnnotation.EXECUTE ); 140 if ( execute != null ) 141 { 142 String executePhase = execute.getNamedParameter( JavadocMojoAnnotation.EXECUTE_PHASE ); 143 String executeGoal = execute.getNamedParameter( JavadocMojoAnnotation.EXECUTE_GOAL ); 144 145 if ( executePhase == null && executeGoal == null ) 146 { 147 throw new InvalidPluginDescriptorException( javaClass.getFullyQualifiedName() 148 + ": @execute tag requires either a 'phase' or 'goal' parameter" ); 149 } 150 else if ( executePhase != null && executeGoal != null ) 151 { 152 throw new InvalidPluginDescriptorException( javaClass.getFullyQualifiedName() 153 + ": @execute tag can have only one of a 'phase' or 'goal' parameter" ); 154 } 155 mojoDescriptor.setExecutePhase( executePhase ); 156 mojoDescriptor.setExecuteGoal( executeGoal ); 157 158 String lifecycle = execute.getNamedParameter( JavadocMojoAnnotation.EXECUTE_LIFECYCLE ); 159 if ( lifecycle != null ) 160 { 161 mojoDescriptor.setExecuteLifecycle( lifecycle ); 162 if ( mojoDescriptor.getExecuteGoal() != null ) 163 { 164 throw new InvalidPluginDescriptorException( javaClass.getFullyQualifiedName() 165 + ": @execute lifecycle requires a phase instead of a goal" ); 166 } 167 } 168 } 169 170 // Goal name 171 DocletTag goal = findInClassHierarchy( javaClass, JavadocMojoAnnotation.GOAL ); 172 if ( goal != null ) 173 { 174 mojoDescriptor.setGoal( goal.getValue() ); 175 } 176 177 // inheritByDefault flag 178 boolean value = 179 getBooleanTagValue( javaClass, JavadocMojoAnnotation.INHERIT_BY_DEFAULT, 180 mojoDescriptor.isInheritedByDefault() ); 181 mojoDescriptor.setInheritedByDefault( value ); 182 183 // instantiationStrategy 184 DocletTag tag = findInClassHierarchy( javaClass, JavadocMojoAnnotation.INSTANTIATION_STRATEGY ); 185 if ( tag != null ) 186 { 187 mojoDescriptor.setInstantiationStrategy( tag.getValue() ); 188 } 189 190 // executionStrategy (and deprecated @attainAlways) 191 tag = findInClassHierarchy( javaClass, JavadocMojoAnnotation.MULTI_EXECUTION_STRATEGY ); 192 if ( tag != null ) 193 { 194 getLogger().warn( "@" + JavadocMojoAnnotation.MULTI_EXECUTION_STRATEGY + " in " 195 + javaClass.getFullyQualifiedName() + " is deprecated: please use '@" 196 + JavadocMojoAnnotation.EXECUTION_STATEGY + " always' instead." ); 197 mojoDescriptor.setExecutionStrategy( MojoDescriptor.MULTI_PASS_EXEC_STRATEGY ); 198 } 199 else 200 { 201 mojoDescriptor.setExecutionStrategy( MojoDescriptor.SINGLE_PASS_EXEC_STRATEGY ); 202 } 203 tag = findInClassHierarchy( javaClass, JavadocMojoAnnotation.EXECUTION_STATEGY ); 204 if ( tag != null ) 205 { 206 mojoDescriptor.setExecutionStrategy( tag.getValue() ); 207 } 208 209 // Phase name 210 DocletTag phase = findInClassHierarchy( javaClass, JavadocMojoAnnotation.PHASE ); 211 if ( phase != null ) 212 { 213 mojoDescriptor.setPhase( phase.getValue() ); 214 } 215 216 // Dependency resolution flag 217 DocletTag requiresDependencyResolution = 218 findInClassHierarchy( javaClass, JavadocMojoAnnotation.REQUIRES_DEPENDENCY_RESOLUTION ); 219 if ( requiresDependencyResolution != null ) 220 { 221 String v = requiresDependencyResolution.getValue(); 222 223 if ( StringUtils.isEmpty( v ) ) 224 { 225 v = "runtime"; 226 } 227 228 mojoDescriptor.setDependencyResolutionRequired( v ); 229 } 230 231 // Dependency collection flag 232 DocletTag requiresDependencyCollection = 233 findInClassHierarchy( javaClass, JavadocMojoAnnotation.REQUIRES_DEPENDENCY_COLLECTION ); 234 if ( requiresDependencyCollection != null ) 235 { 236 String v = requiresDependencyCollection.getValue(); 237 238 if ( StringUtils.isEmpty( v ) ) 239 { 240 v = "runtime"; 241 } 242 243 mojoDescriptor.setDependencyCollectionRequired( v ); 244 } 245 246 // requiresDirectInvocation flag 247 value = 248 getBooleanTagValue( javaClass, JavadocMojoAnnotation.REQUIRES_DIRECT_INVOCATION, 249 mojoDescriptor.isDirectInvocationOnly() ); 250 mojoDescriptor.setDirectInvocationOnly( value ); 251 252 // Online flag 253 value = 254 getBooleanTagValue( javaClass, JavadocMojoAnnotation.REQUIRES_ONLINE, mojoDescriptor.isOnlineRequired() ); 255 mojoDescriptor.setOnlineRequired( value ); 256 257 // Project flag 258 value = 259 getBooleanTagValue( javaClass, JavadocMojoAnnotation.REQUIRES_PROJECT, mojoDescriptor.isProjectRequired() ); 260 mojoDescriptor.setProjectRequired( value ); 261 262 // requiresReports flag 263 value = 264 getBooleanTagValue( javaClass, JavadocMojoAnnotation.REQUIRES_REPORTS, mojoDescriptor.isRequiresReports() ); 265 mojoDescriptor.setRequiresReports( value ); 266 267 // ---------------------------------------------------------------------- 268 // Javadoc annotations in alphabetical order 269 // ---------------------------------------------------------------------- 270 271 // Deprecation hint 272 DocletTag deprecated = javaClass.getTagByName( JavadocMojoAnnotation.DEPRECATED ); 273 if ( deprecated != null ) 274 { 275 mojoDescriptor.setDeprecated( deprecated.getValue() ); 276 } 277 278 // What version it was introduced in 279 DocletTag since = findInClassHierarchy( javaClass, JavadocMojoAnnotation.SINCE ); 280 if ( since != null ) 281 { 282 mojoDescriptor.setSince( since.getValue() ); 283 } 284 285 // Thread-safe mojo 286 287 value = getBooleanTagValue( javaClass, JavadocMojoAnnotation.THREAD_SAFE, true, mojoDescriptor.isThreadSafe() ); 288 mojoDescriptor.setThreadSafe( value ); 289 290 extractParameters( mojoDescriptor, javaClass ); 291 292 return mojoDescriptor; 293 } 294 295 /** 296 * @param javaClass not null 297 * @param tagName not null 298 * @param defaultValue the wanted default value 299 * @return the boolean value of the given tagName 300 * @see #findInClassHierarchy(JavaClass, String) 301 */ 302 private static boolean getBooleanTagValue( JavaClass javaClass, String tagName, boolean defaultValue ) 303 { 304 DocletTag tag = findInClassHierarchy( javaClass, tagName ); 305 306 if ( tag != null ) 307 { 308 String value = tag.getValue(); 309 310 if ( StringUtils.isNotEmpty( value ) ) 311 { 312 defaultValue = Boolean.valueOf( value ).booleanValue(); 313 } 314 } 315 return defaultValue; 316 } 317 318 /** 319 * @param javaClass not null 320 * @param tagName not null 321 * @param defaultForTag The wanted default value when only the tagname is present 322 * @param defaultValue the wanted default value when the tag is not specified 323 * @return the boolean value of the given tagName 324 * @see #findInClassHierarchy(JavaClass, String) 325 */ 326 private static boolean getBooleanTagValue( JavaClass javaClass, String tagName, boolean defaultForTag, 327 boolean defaultValue ) 328 { 329 DocletTag tag = findInClassHierarchy( javaClass, tagName ); 330 331 if ( tag != null ) 332 { 333 String value = tag.getValue(); 334 335 if ( StringUtils.isNotEmpty( value ) ) 336 { 337 return Boolean.valueOf( value ).booleanValue(); 338 } 339 else 340 { 341 return defaultForTag; 342 } 343 } 344 return defaultValue; 345 } 346 347 /** 348 * @param javaClass not null 349 * @param tagName not null 350 * @return docletTag instance 351 */ 352 private static DocletTag findInClassHierarchy( JavaClass javaClass, String tagName ) 353 { 354 DocletTag tag = javaClass.getTagByName( tagName ); 355 356 if ( tag == null ) 357 { 358 JavaClass superClass = javaClass.getSuperJavaClass(); 359 360 if ( superClass != null ) 361 { 362 tag = findInClassHierarchy( superClass, tagName ); 363 } 364 } 365 366 return tag; 367 } 368 369 /** 370 * @param mojoDescriptor not null 371 * @param javaClass not null 372 * @throws InvalidPluginDescriptorException if any 373 */ 374 private void extractParameters( MojoDescriptor mojoDescriptor, JavaClass javaClass ) 375 throws InvalidPluginDescriptorException 376 { 377 // --------------------------------------------------------------------------------- 378 // We're resolving class-level, ancestor-class-field, local-class-field order here. 379 // --------------------------------------------------------------------------------- 380 381 Map<String, JavaField> rawParams = extractFieldParameterTags( javaClass ); 382 383 for ( Map.Entry<String, JavaField> entry : rawParams.entrySet() ) 384 { 385 JavaField field = entry.getValue(); 386 387 JavaType type = field.getType(); 388 389 Parameter pd = new Parameter(); 390 391 pd.setName( entry.getKey() ); 392 393 pd.setType( type.getFullyQualifiedName() ); 394 395 pd.setDescription( field.getComment() ); 396 397 DocletTag deprecationTag = field.getTagByName( JavadocMojoAnnotation.DEPRECATED ); 398 399 if ( deprecationTag != null ) 400 { 401 pd.setDeprecated( deprecationTag.getValue() ); 402 } 403 404 DocletTag sinceTag = field.getTagByName( JavadocMojoAnnotation.SINCE ); 405 if ( sinceTag != null ) 406 { 407 pd.setSince( sinceTag.getValue() ); 408 } 409 410 DocletTag componentTag = field.getTagByName( JavadocMojoAnnotation.COMPONENT ); 411 412 if ( componentTag != null ) 413 { 414 // Component tag 415 String role = componentTag.getNamedParameter( JavadocMojoAnnotation.COMPONENT_ROLE ); 416 417 if ( role == null ) 418 { 419 role = field.getType().toString(); 420 } 421 422 String roleHint = componentTag.getNamedParameter( JavadocMojoAnnotation.COMPONENT_ROLEHINT ); 423 424 if ( roleHint == null ) 425 { 426 // support alternate syntax for better compatibility with the Plexus CDC. 427 roleHint = componentTag.getNamedParameter( "role-hint" ); 428 } 429 430 // recognize Maven-injected objects as components annotations instead of parameters 431 // Note: the expressions we are looking for, i.e. "${project}", are in the values of the Map, 432 // so the lookup mechanism is different here than in maven-plugin-tools-annotations 433 boolean isDeprecated = PluginUtils.MAVEN_COMPONENTS.containsValue( role ); 434 435 if ( !isDeprecated ) 436 { 437 // normal component 438 pd.setRequirement( new Requirement( role, roleHint ) ); 439 } 440 else 441 { 442 // not a component but a Maven object to be transformed into an expression/property 443 getLogger().warn( "Deprecated @component Javadoc tag for '" + pd.getName() + "' field in " 444 + javaClass.getFullyQualifiedName() 445 + ": replace with @Parameter( defaultValue = \"" + role 446 + "\", readonly = true )" ); 447 pd.setDefaultValue( role ); 448 pd.setRequired( true ); 449 } 450 451 pd.setEditable( false ); 452 /* TODO: or better like this? Need @component fields be editable for the user? 453 pd.setEditable( field.getTagByName( READONLY ) == null ); 454 */ 455 } 456 else 457 { 458 // Parameter tag 459 DocletTag parameter = field.getTagByName( JavadocMojoAnnotation.PARAMETER ); 460 461 pd.setRequired( field.getTagByName( JavadocMojoAnnotation.REQUIRED ) != null ); 462 463 pd.setEditable( field.getTagByName( JavadocMojoAnnotation.READONLY ) == null ); 464 465 String name = parameter.getNamedParameter( JavadocMojoAnnotation.PARAMETER_NAME ); 466 467 if ( !StringUtils.isEmpty( name ) ) 468 { 469 pd.setName( name ); 470 } 471 472 String alias = parameter.getNamedParameter( JavadocMojoAnnotation.PARAMETER_ALIAS ); 473 474 if ( !StringUtils.isEmpty( alias ) ) 475 { 476 pd.setAlias( alias ); 477 } 478 479 String expression = parameter.getNamedParameter( JavadocMojoAnnotation.PARAMETER_EXPRESSION ); 480 String property = parameter.getNamedParameter( JavadocMojoAnnotation.PARAMETER_PROPERTY ); 481 482 if ( StringUtils.isNotEmpty( expression ) && StringUtils.isNotEmpty( property ) ) 483 { 484 getLogger().error( javaClass.getFullyQualifiedName() + "#" + field.getName() + ":" ); 485 getLogger().error( " Cannot use both:" ); 486 getLogger().error( " @parameter expression=\"${property}\"" ); 487 getLogger().error( " and" ); 488 getLogger().error( " @parameter property=\"property\"" ); 489 getLogger().error( " Second syntax is preferred." ); 490 throw new InvalidParameterException( javaClass.getFullyQualifiedName() + "#" + field.getName() 491 + ": cannot" + " use both @parameter expression and property", null ); 492 } 493 494 if ( StringUtils.isNotEmpty( expression ) ) 495 { 496 getLogger().warn( javaClass.getFullyQualifiedName() + "#" + field.getName() + ":" ); 497 getLogger().warn( " The syntax" ); 498 getLogger().warn( " @parameter expression=\"${property}\"" ); 499 getLogger().warn( " is deprecated, please use" ); 500 getLogger().warn( " @parameter property=\"property\"" ); 501 getLogger().warn( " instead." ); 502 503 } 504 else if ( StringUtils.isNotEmpty( property ) ) 505 { 506 expression = "${" + property + "}"; 507 } 508 509 pd.setExpression( expression ); 510 511 if ( StringUtils.isNotEmpty( expression ) && expression.startsWith( "${component." ) ) 512 { 513 getLogger().warn( javaClass.getFullyQualifiedName() + "#" + field.getName() + ":" ); 514 getLogger().warn( " The syntax" ); 515 getLogger().warn( " @parameter expression=\"${component.<role>#<roleHint>}\"" ); 516 getLogger().warn( " is deprecated, please use" ); 517 getLogger().warn( " @component role=\"<role>\" roleHint=\"<roleHint>\"" ); 518 getLogger().warn( " instead." ); 519 } 520 521 if ( "${reports}".equals( pd.getExpression() ) ) 522 { 523 mojoDescriptor.setRequiresReports( true ); 524 } 525 526 pd.setDefaultValue( parameter.getNamedParameter( JavadocMojoAnnotation.PARAMETER_DEFAULT_VALUE ) ); 527 528 pd.setImplementation( parameter.getNamedParameter( JavadocMojoAnnotation.PARAMETER_IMPLEMENTATION ) ); 529 } 530 531 mojoDescriptor.addParameter( pd ); 532 } 533 } 534 535 /** 536 * extract fields that are either parameters or components. 537 * 538 * @param javaClass not null 539 * @return map with Mojo parameters names as keys 540 */ 541 private Map<String, JavaField> extractFieldParameterTags( JavaClass javaClass ) 542 { 543 Map<String, JavaField> rawParams; 544 545 // we have to add the parent fields first, so that they will be overwritten by the local fields if 546 // that actually happens... 547 JavaClass superClass = javaClass.getSuperJavaClass(); 548 549 if ( superClass != null ) 550 { 551 rawParams = extractFieldParameterTags( superClass ); 552 } 553 else 554 { 555 rawParams = new TreeMap<String, JavaField>(); 556 } 557 558 for ( JavaField field : javaClass.getFields() ) 559 { 560 if ( field.getTagByName( JavadocMojoAnnotation.PARAMETER ) != null 561 || field.getTagByName( JavadocMojoAnnotation.COMPONENT ) != null ) 562 { 563 rawParams.put( field.getName(), field ); 564 } 565 } 566 return rawParams; 567 } 568 569 /** {@inheritDoc} */ 570 public List<MojoDescriptor> execute( PluginToolsRequest request ) 571 throws ExtractionException, InvalidPluginDescriptorException 572 { 573 Collection<JavaClass> javaClasses = discoverClasses( request ); 574 575 List<MojoDescriptor> descriptors = new ArrayList<MojoDescriptor>(); 576 577 for ( JavaClass javaClass : javaClasses ) 578 { 579 DocletTag tag = javaClass.getTagByName( GOAL ); 580 581 if ( tag != null ) 582 { 583 MojoDescriptor mojoDescriptor = createMojoDescriptor( javaClass ); 584 mojoDescriptor.setPluginDescriptor( request.getPluginDescriptor() ); 585 586 // Validate the descriptor as best we can before allowing it to be processed. 587 validate( mojoDescriptor ); 588 589 descriptors.add( mojoDescriptor ); 590 } 591 } 592 593 return descriptors; 594 } 595 596 /** 597 * @param request The plugin request. 598 * @return an array of java class 599 */ 600 @SuppressWarnings( "unchecked" ) 601 protected Collection<JavaClass> discoverClasses( final PluginToolsRequest request ) 602 { 603 JavaProjectBuilder builder = new JavaProjectBuilder( new SortedClassLibraryBuilder() ); 604 builder.setEncoding( request.getEncoding() ); 605 606 // Build isolated Classloader with only the artifacts of the project (none of this plugin) 607 List<URL> urls = new ArrayList<URL>( request.getDependencies().size() ); 608 for ( Artifact artifact : request.getDependencies() ) 609 { 610 try 611 { 612 urls.add( artifact.getFile().toURI().toURL() ); 613 } 614 catch ( MalformedURLException e ) 615 { 616 // noop 617 } 618 } 619 builder.addClassLoader( new URLClassLoader( urls.toArray( new URL[0] ), ClassLoader.getSystemClassLoader() ) ); 620 621 MavenProject project = request.getProject(); 622 623 for ( String source : (List<String>) project.getCompileSourceRoots() ) 624 { 625 builder.addSourceTree( new File( source ) ); 626 } 627 628 // TODO be more dynamic 629 File generatedPlugin = new File( project.getBasedir(), "target/generated-sources/plugin" ); 630 if ( !project.getCompileSourceRoots().contains( generatedPlugin.getAbsolutePath() ) ) 631 { 632 builder.addSourceTree( generatedPlugin ); 633 } 634 635 return builder.getClasses(); 636 } 637 638 /** 639 * @param mojoDescriptor not null 640 * @throws InvalidParameterException if any 641 */ 642 protected void validate( MojoDescriptor mojoDescriptor ) 643 throws InvalidParameterException 644 { 645 @SuppressWarnings( "unchecked" ) 646 List<Parameter> parameters = mojoDescriptor.getParameters(); 647 648 if ( parameters != null ) 649 { 650 for ( int j = 0; j < parameters.size(); j++ ) 651 { 652 validateParameter( parameters.get( j ), j ); 653 } 654 } 655 } 656}