Coverage Report - org.codehaus.plexus.metadata.gleaner.QDoxComponentGleaner
 
Classes in this File Line Coverage Branch Coverage Complexity
QDoxComponentGleaner
60 %
83/138
38 %
31/80
7,143
 
 1  
 package org.codehaus.plexus.metadata.gleaner;
 2  
 
 3  
 /*
 4  
  * The MIT License
 5  
  *
 6  
  * Copyright (c) 2004, The Codehaus
 7  
  *
 8  
  * Permission is hereby granted, free of charge, to any person obtaining a copy of
 9  
  * this software and associated documentation files (the "Software"), to deal in
 10  
  * the Software without restriction, including without limitation the rights to
 11  
  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 12  
  * of the Software, and to permit persons to whom the Software is furnished to do
 13  
  * so, subject to the following conditions:
 14  
  *
 15  
  * The above copyright notice and this permission notice shall be included in all
 16  
  * copies or substantial portions of the Software.
 17  
  *
 18  
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 19  
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 20  
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 21  
  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 22  
  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 23  
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 24  
  * SOFTWARE.
 25  
  */
 26  
 
 27  
 import java.util.*;
 28  
 
 29  
 import org.codehaus.plexus.component.repository.ComponentDescriptor;
 30  
 import org.codehaus.plexus.component.repository.ComponentRequirement;
 31  
 import org.codehaus.plexus.component.repository.ComponentRequirementList;
 32  
 import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
 33  
 import org.codehaus.plexus.logging.LogEnabled;
 34  
 import org.codehaus.plexus.personality.plexus.lifecycle.phase.*;
 35  
 import org.codehaus.plexus.util.StringUtils;
 36  
 
 37  
 import com.thoughtworks.qdox.JavaProjectBuilder;
 38  
 import com.thoughtworks.qdox.model.DocletTag;
 39  
 import com.thoughtworks.qdox.model.JavaClass;
 40  
 import com.thoughtworks.qdox.model.JavaField;
 41  
 
 42  
 /**
 43  
  * A source component gleaner which uses QDox to discover Javadoc annotations.
 44  
  * 
 45  
  * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
 46  
  * @version $Id$
 47  
  */
 48  4
 public class QDoxComponentGleaner
 49  
     extends ComponentGleanerSupport
 50  
     implements SourceComponentGleaner
 51  
 {
 52  
     public static final String PLEXUS_COMPONENT_TAG = "plexus.component";
 53  
 
 54  
     public static final String PLEXUS_REQUIREMENT_TAG = "plexus.requirement";
 55  
 
 56  
     public static final String PLEXUS_CONFIGURATION_TAG = "plexus.configuration";
 57  
 
 58  
     public static final String PLEXUS_VERSION_PARAMETER = "version";
 59  
 
 60  
     public static final String PLEXUS_ROLE_PARAMETER = "role";
 61  
 
 62  
     public static final String PLEXUS_ROLE_HINT_PARAMETER = "role-hint";
 63  
 
 64  
     public static final String PLEXUS_ROLE_HINT_LIST_PARAMETER = "role-hints";
 65  
 
 66  
     public static final String PLEXUS_ALIAS_PARAMETER = "alias";
 67  
 
 68  
     public static final String PLEXUS_DEFAULT_VALUE_PARAMETER = "default-value";
 69  
 
 70  
     public static final String PLEXUS_LIFECYCLE_HANDLER_PARAMETER = "lifecycle-handler";
 71  
 
 72  
     public static final String PLEXUS_INSTANTIATION_STARTEGY_PARAMETER = "instantiation-strategy";
 73  
 
 74  
     public static final String PLEXUS_OPTIONAL_PARAMETER = "optional";
 75  
 
 76  
     public static final String PLEXUS_DEFAULT_HINT = "default";
 77  
 
 78  
     // ----------------------------------------------------------------------
 79  
     // ComponentGleaner Implementation
 80  
     // ----------------------------------------------------------------------
 81  
 
 82  
     public ComponentDescriptor<?> glean( JavaProjectBuilder classCache, JavaClass javaClass )
 83  
         throws ComponentGleanerException
 84  
     {
 85  4
         DocletTag tag = javaClass.getTagByName( PLEXUS_COMPONENT_TAG );
 86  
 
 87  4
         if ( tag == null )
 88  
         {
 89  3
             return null;
 90  
         }
 91  
 
 92  1
         Map<String, String> parameters = tag.getNamedParameterMap();
 93  
 
 94  
         // ----------------------------------------------------------------------
 95  
         //
 96  
         // ----------------------------------------------------------------------
 97  
 
 98  1
         String fqn = javaClass.getFullyQualifiedName();
 99  
 
 100  
         //log.debug( "Creating descriptor for component: {}", fqn );
 101  
 
 102  1
         ComponentDescriptor<?> componentDescriptor = new ComponentDescriptor<Object>();
 103  
 
 104  1
         componentDescriptor.setImplementation( fqn );
 105  
 
 106  
         // ----------------------------------------------------------------------
 107  
         // Role
 108  
         // ----------------------------------------------------------------------
 109  
 
 110  1
         String role = getParameter( parameters, PLEXUS_ROLE_PARAMETER );
 111  
 
 112  1
         if ( role == null )
 113  
         {
 114  0
             role = findRole( javaClass );
 115  
 
 116  0
             if ( role == null )
 117  
             {
 118  
                 /*
 119  
                 log.warn( "Could not figure out a role for the component '" + fqn + "'. " +
 120  
                     "Please specify a role with a parameter '" + PLEXUS_ROLE_PARAMETER + "' " + "on the @" +
 121  
                     PLEXUS_COMPONENT_TAG + " tag." );
 122  
                     */
 123  
 
 124  0
                 return null;
 125  
             }
 126  
         }
 127  
 
 128  1
         componentDescriptor.setRole( role );
 129  
 
 130  
         // ----------------------------------------------------------------------
 131  
         // Role hint
 132  
         // ----------------------------------------------------------------------
 133  
 
 134  1
         String roleHint = getParameter( parameters, PLEXUS_ROLE_HINT_PARAMETER );
 135  
 
 136  1
         if ( roleHint != null )
 137  
         {
 138  
             // getLogger().debug( " Role hint: " + roleHint );
 139  
         }
 140  
 
 141  1
         componentDescriptor.setRoleHint( roleHint );
 142  
 
 143  
         // ----------------------------------------------------------------------
 144  
         // Version
 145  
         // ----------------------------------------------------------------------
 146  
 
 147  1
         String version = getParameter( parameters, PLEXUS_VERSION_PARAMETER );
 148  
 
 149  1
         componentDescriptor.setVersion( version );
 150  
 
 151  
         // ----------------------------------------------------------------------
 152  
         // Lifecycle handler
 153  
         // ----------------------------------------------------------------------
 154  
 
 155  1
         String lifecycleHandler = getParameter( parameters, PLEXUS_LIFECYCLE_HANDLER_PARAMETER );
 156  
 
 157  1
         componentDescriptor.setLifecycleHandler( lifecycleHandler );
 158  
 
 159  
         // ----------------------------------------------------------------------
 160  
         // Lifecycle handler
 161  
         // ----------------------------------------------------------------------
 162  
 
 163  1
         String instatiationStrategy = getParameter( parameters, PLEXUS_INSTANTIATION_STARTEGY_PARAMETER );
 164  
 
 165  1
         componentDescriptor.setInstantiationStrategy( instatiationStrategy );
 166  
 
 167  
         // ----------------------------------------------------------------------
 168  
         // Alias
 169  
         // ----------------------------------------------------------------------
 170  
 
 171  1
         componentDescriptor.setAlias( getParameter( parameters, PLEXUS_ALIAS_PARAMETER ) );
 172  
 
 173  
         // ----------------------------------------------------------------------
 174  
         //
 175  
         // ----------------------------------------------------------------------
 176  
 
 177  1
         findExtraParameters( PLEXUS_COMPONENT_TAG, parameters );
 178  
 
 179  
         // ----------------------------------------------------------------------
 180  
         // Requirements
 181  
         // ----------------------------------------------------------------------
 182  
 
 183  1
         findRequirements( classCache, componentDescriptor, javaClass );
 184  
 
 185  
         // ----------------------------------------------------------------------
 186  
         // Description
 187  
         // ----------------------------------------------------------------------
 188  
 
 189  1
         String comment = javaClass.getComment();
 190  
 
 191  1
         if ( comment != null )
 192  
         {
 193  1
             int i = comment.indexOf( '.' );
 194  
 
 195  1
             if ( i > 0 )
 196  
             {
 197  0
                 comment = comment.substring( 0, i + 1 ); // include the dot
 198  
             }
 199  
         }
 200  
 
 201  1
         componentDescriptor.setDescription( comment );
 202  
 
 203  
         // ----------------------------------------------------------------------
 204  
         // Configuration
 205  
         // ----------------------------------------------------------------------
 206  
 
 207  1
         XmlPlexusConfiguration configuration = new XmlPlexusConfiguration( "configuration" );
 208  
 
 209  1
         findConfiguration( configuration, javaClass );
 210  
 
 211  1
         componentDescriptor.setConfiguration( configuration );
 212  
 
 213  1
         return componentDescriptor;
 214  
     }
 215  
 
 216  
     // ----------------------------------------------------------------------
 217  
     //
 218  
     // ----------------------------------------------------------------------
 219  
 
 220  1
     private final static List<String> IGNORED_INTERFACES = Collections.unmodifiableList( Arrays.asList( new String[]{
 221  
         LogEnabled.class.getName(),
 222  
         Initializable.class.getName(),
 223  
         Configurable.class.getName(),
 224  
         Contextualizable.class.getName(),
 225  
         Disposable.class.getName(),
 226  
         Startable.class.getName(),
 227  
     } ) );
 228  
 
 229  
     private static String getPackage( JavaClass javaClass )
 230  
     {
 231  0
         if ( javaClass.getPackage() != null )
 232  
         {
 233  0
             return javaClass.getPackage().getName();
 234  
         }
 235  
         else
 236  
         {
 237  0
             return "";
 238  
         }
 239  
     }
 240  
 
 241  
     private String findRole( JavaClass javaClass )
 242  
     {
 243  
         // ----------------------------------------------------------------------
 244  
         // Remove any Plexus specific interfaces from the calculation
 245  
         // ----------------------------------------------------------------------
 246  
 
 247  0
         List<JavaClass> interfaces = new ArrayList<JavaClass>(  javaClass.getImplementedInterfaces() );
 248  
 
 249  0
         for ( Iterator<JavaClass> it = interfaces.iterator(); it.hasNext(); )
 250  
         {
 251  0
             JavaClass ifc = it.next();
 252  
 
 253  0
             if ( IGNORED_INTERFACES.contains( ifc.getFullyQualifiedName() ) )
 254  
             {
 255  0
                 it.remove();
 256  
             }
 257  0
         }
 258  
 
 259  
         // ----------------------------------------------------------------------
 260  
         // For each implemented interface, check to see if it's a candiate
 261  
         // interface
 262  
         // ----------------------------------------------------------------------
 263  
 
 264  0
         String role = null;
 265  
 
 266  0
         String className = javaClass.getName();
 267  
 
 268  0
         for (JavaClass ifc : interfaces) {
 269  0
             String fqn = ifc.getFullyQualifiedName();
 270  
 
 271  0
             String pkg = getPackage(ifc);
 272  
 
 273  0
             if (pkg == null) {
 274  0
                 int index = fqn.lastIndexOf('.');
 275  
 
 276  0
                 if (index == -1) {
 277  
                     // -----------------------------------------------------------------------
 278  
                     // This is a special case which will happen in two cases:
 279  
                     // 1) The component is in the default/root package
 280  
                     // 2) The interface is in another build, typically in an -api package
 281  
                     // while the code beeing gleaned in in the -impl build.
 282  
                     //
 283  
                     // Since it's most likely in another package than in the default package
 284  
                     // prepend the gleaned class' package
 285  
                     // -----------------------------------------------------------------------
 286  
 
 287  0
                     pkg = getPackage(javaClass);
 288  
 
 289  0
                     fqn = pkg + "." + fqn;
 290  
                 } else {
 291  0
                     pkg = fqn.substring(0, index);
 292  
                 }
 293  
             }
 294  
 
 295  0
             if (fqn == null) {
 296  0
                 fqn = ifc.getName();
 297  
             }
 298  
 
 299  0
             String name = fqn.substring(pkg.length() + 1);
 300  
 
 301  0
             if (className.endsWith(name)) {
 302  0
                 if (role != null) {
 303  
                     /*
 304  
                     log.warn( "Found several possible roles for component " + "'" +
 305  
                         javaClass.getFullyQualifiedName() + "', " + "will use '" + role + "', found: " + ifc.getName() + "." );
 306  
                         */
 307  
                 }
 308  
 
 309  0
                 role = fqn;
 310  
             }
 311  0
         }
 312  
 
 313  0
         if ( role == null )
 314  
         {
 315  0
             JavaClass superClass = javaClass.getSuperJavaClass();
 316  
 
 317  0
             if ( superClass != null )
 318  
             {
 319  0
                 role = findRole( superClass );
 320  
             }
 321  
         }
 322  
 
 323  0
         return role;
 324  
     }
 325  
 
 326  
     private void findRequirements( JavaProjectBuilder classCache, ComponentDescriptor<?> componentDescriptor,
 327  
                                    JavaClass javaClass )
 328  
     {
 329  2
         List<JavaField> fields = javaClass.getFields();
 330  
 
 331  
         // ----------------------------------------------------------------------
 332  
         // Search the super class for requirements
 333  
         // ----------------------------------------------------------------------
 334  
 
 335  2
         if ( javaClass.getSuperJavaClass() != null )
 336  
         {
 337  1
             findRequirements( classCache, componentDescriptor, javaClass.getSuperJavaClass() );
 338  
         }
 339  
 
 340  
         // ----------------------------------------------------------------------
 341  
         // Search the current class for requirements
 342  
         // ----------------------------------------------------------------------
 343  
 
 344  2
         for (JavaField field : fields) {
 345  2
             DocletTag tag = field.getTagByName(PLEXUS_REQUIREMENT_TAG);
 346  
 
 347  2
             if (tag == null) {
 348  1
                 continue;
 349  
             }
 350  
 
 351  1
             Map<String, String> parameters = new HashMap<String, String>(tag.getNamedParameterMap());
 352  
 
 353  
             // ----------------------------------------------------------------------
 354  
             // Role
 355  
             // ----------------------------------------------------------------------
 356  
 
 357  1
             String requirementClass = field.getType().getFullyQualifiedName();
 358  
 
 359  1
             boolean isMap = requirementClass.equals(Map.class.getName()) ||
 360  
                     requirementClass.equals(Collection.class.getName());
 361  
 
 362  
             try {
 363  1
                 isMap = isMap || Collection.class.isAssignableFrom(Class.forName(requirementClass));
 364  0
             } catch (ClassNotFoundException e) {
 365  
                 // ignore the assignable Collection test, though this should never happen
 366  1
             }
 367  
 
 368  1
             boolean isList = requirementClass.equals(List.class.getName());
 369  
 
 370  
             ComponentRequirement cr;
 371  
 
 372  1
             String hint = getParameter(parameters, PLEXUS_ROLE_HINT_PARAMETER);
 373  
 
 374  1
             if (isMap || isList) {
 375  0
                 cr = new ComponentRequirementList();
 376  
 
 377  0
                 String hintList = getParameter(parameters, PLEXUS_ROLE_HINT_LIST_PARAMETER);
 378  
 
 379  0
                 if (hintList != null) {
 380  0
                     String[] hintArr = hintList.split(",");
 381  
 
 382  0
                     ((ComponentRequirementList) cr).setRoleHints(Arrays.asList(hintArr));
 383  
                 }
 384  0
             } else {
 385  1
                 cr = new ComponentRequirement();
 386  
 
 387  1
                 cr.setRoleHint(hint);
 388  
             }
 389  
 
 390  1
             String role = getParameter(parameters, PLEXUS_ROLE_PARAMETER);
 391  
 
 392  1
             if (role == null) {
 393  1
                 cr.setRole(requirementClass);
 394  
             } else {
 395  0
                 cr.setRole(role);
 396  
             }
 397  
 
 398  1
             String optional = getParameter(parameters, PLEXUS_OPTIONAL_PARAMETER);
 399  
 
 400  1
             cr.setOptional(Boolean.parseBoolean(optional));
 401  
 
 402  1
             cr.setFieldName(field.getName());
 403  
 
 404  1
             if (isMap || isList) {
 405  0
                 if (hint != null) {
 406  
                     /*
 407  
                     log.warn( "Field: '" + field.getName() + "': A role hint cannot be specified if the " +
 408  
                         "field is a java.util.Map or a java.util.List" );
 409  
                         */
 410  
 
 411  0
                     continue;
 412  
                 }
 413  
 
 414  0
                 if (role == null) {
 415  
                     /*
 416  
                     log.warn( "Field: '" + field.getName() + "': A java.util.Map or java.util.List " +
 417  
                         "requirement has to specify a '" + PLEXUS_ROLE_PARAMETER + "' parameter on " + "the @" +
 418  
                         PLEXUS_REQUIREMENT_TAG + " tag so Plexus can know which components to " +
 419  
                         "put in the map or list." );
 420  
                         */
 421  
 
 422  0
                     continue;
 423  
                 }
 424  
 
 425  0
                 JavaClass roleClass = classCache.getClassByName(role);
 426  
 
 427  0
                 if (role.indexOf('.') == -1 && StringUtils.isEmpty(getPackage(roleClass))) {
 428  0
                     role = getPackage(javaClass) + "." + roleClass.getName();
 429  
                 }
 430  
 
 431  0
                 cr.setRole(role);
 432  
 
 433  0
                 findExtraParameters(PLEXUS_REQUIREMENT_TAG, parameters);
 434  
             }
 435  
 
 436  
             // ----------------------------------------------------------------------
 437  
             //
 438  
             // ----------------------------------------------------------------------
 439  
 
 440  1
             componentDescriptor.addRequirement(cr);
 441  1
         }
 442  2
     }
 443  
 
 444  
     private void findConfiguration( XmlPlexusConfiguration configuration, JavaClass javaClass )
 445  
         throws ComponentGleanerException
 446  
     {
 447  2
         List<JavaField> fields = javaClass.getFields();
 448  
 
 449  
         // ----------------------------------------------------------------------
 450  
         // Search the super class for configurable fields.
 451  
         // ----------------------------------------------------------------------
 452  
 
 453  2
         if ( javaClass.getSuperJavaClass() != null )
 454  
         {
 455  1
             findConfiguration( configuration, javaClass.getSuperJavaClass() );
 456  
         }
 457  
 
 458  
         // ----------------------------------------------------------------------
 459  
         // Search the current class for configurable fields.
 460  
         // ----------------------------------------------------------------------
 461  
 
 462  2
         for (JavaField field : fields) {
 463  2
             DocletTag tag = field.getTagByName(PLEXUS_CONFIGURATION_TAG);
 464  
 
 465  2
             if (tag == null) {
 466  1
                 continue;
 467  
             }
 468  
 
 469  1
             Map<String, String> parameters = new HashMap<String, String>(tag.getNamedParameterMap());
 470  
 
 471  
             /* don't use the getParameter helper as we like empty strings */
 472  1
             String defaultValue = parameters.remove(PLEXUS_DEFAULT_VALUE_PARAMETER);
 473  
 
 474  1
             if (defaultValue == null) {
 475  
                 /*
 476  
                 log.warn( "Component: " + javaClass.getName() + ", field name: '" + field.getName() + "': " +
 477  
                     "Currently configurable fields will not be written to the descriptor " +
 478  
                     "without a default value." );*/
 479  
 
 480  0
                 continue;
 481  
             }
 482  
 
 483  1
             String name = deHump(field.getName());
 484  
 
 485  
             XmlPlexusConfiguration c;
 486  
 
 487  1
             c = new XmlPlexusConfiguration(name);
 488  
 
 489  1
             c.setValue(defaultValue);
 490  
 
 491  
             //log.debug( " Configuration: {}={}", name, defaultValue );
 492  
 
 493  1
             configuration.addChild(c);
 494  
 
 495  1
             findExtraParameters(PLEXUS_CONFIGURATION_TAG, parameters);
 496  1
         }
 497  2
     }
 498  
 
 499  
     // ----------------------------------------------------------------------
 500  
     //
 501  
     // ----------------------------------------------------------------------
 502  
 
 503  
     private void findExtraParameters( String tagName, Map<String, String> parameters )
 504  
     {
 505  2
         for (String s : parameters.keySet()) {
 506  
             //log.warn( "Extra parameter on the '" + tagName + "' tag: '" + s + "'." );
 507  0
         }
 508  2
     }
 509  
 
 510  
     private String getParameter( Map<String, String> parameters, String parameter )
 511  
     {
 512  9
         String value = parameters.remove( parameter );
 513  
 
 514  9
         if ( StringUtils.isEmpty( value ) )
 515  
         {
 516  7
             return null;
 517  
         }
 518  
 
 519  2
         return value;
 520  
     }
 521  
 }