Coverage Report - org.codehaus.plexus.metadata.gleaner.AnnotationComponentGleaner
 
Classes in this File Line Coverage Branch Coverage Complexity
AnnotationComponentGleaner
82 %
82/100
60 %
35/58
6,333
 
 1  
 /*
 2  
  * Copyright (C) 2007 the original author or authors.
 3  
  *
 4  
  * Licensed under the Apache License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  *     http://www.apache.org/licenses/LICENSE-2.0
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 
 17  
 package org.codehaus.plexus.metadata.gleaner;
 18  
 
 19  
 import java.io.IOException;
 20  
 import java.io.InputStream;
 21  
 import java.lang.reflect.Modifier;
 22  
 import java.net.URL;
 23  
 import java.util.ArrayList;
 24  
 import java.util.Arrays;
 25  
 import java.util.Enumeration;
 26  
 import java.util.List;
 27  
 
 28  
 import org.codehaus.plexus.component.annotations.Component;
 29  
 import org.codehaus.plexus.component.annotations.Configuration;
 30  
 import org.codehaus.plexus.component.annotations.Requirement;
 31  
 import org.codehaus.plexus.component.repository.ComponentDescriptor;
 32  
 import org.codehaus.plexus.component.repository.ComponentRequirement;
 33  
 import org.codehaus.plexus.component.repository.ComponentRequirementList;
 34  
 import org.codehaus.plexus.configuration.PlexusConfiguration;
 35  
 import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
 36  
 import org.codehaus.plexus.metadata.ann.AnnClass;
 37  
 import org.codehaus.plexus.metadata.ann.AnnField;
 38  
 import org.codehaus.plexus.metadata.ann.AnnReader;
 39  
 import org.codehaus.plexus.util.IOUtil;
 40  
 
 41  
 /**
 42  
  * A class component gleaner which inspects each type for <tt>org.codehaus.plexus.component.annotations.*</tt> annotations
 43  
  * and when found translates them into a {@link ComponentDescriptor}.
 44  
  *
 45  
  * @version $Id$
 46  
  */
 47  2
 public class AnnotationComponentGleaner
 48  
     extends ComponentGleanerSupport
 49  
     implements ClassComponentGleaner
 50  
 {
 51  1
     private static final String OBJECT_SLASHED_NAME = Object.class.getName().replace('.', '/');
 52  
 
 53  
     public ComponentDescriptor<?> glean(String className, ClassLoader cl) throws ComponentGleanerException 
 54  
     {
 55  1
         assert className != null;
 56  1
         assert cl != null;
 57  
 
 58  1
         AnnClass annClass = readClass(className.replace('.', '/'), cl);
 59  
         
 60  
         // Skip abstract classes
 61  1
         if (Modifier.isAbstract(annClass.getAccess())) {
 62  0
             return null;
 63  
         }
 64  
         
 65  
         
 66  1
         Component anno = annClass.getAnnotation(Component.class);
 67  
 
 68  1
         if (anno == null) {
 69  0
             return null;
 70  
         }
 71  
 
 72  1
         ComponentDescriptor<?> component = new ComponentDescriptor<Object>();
 73  
         
 74  1
         component.setRole(anno.role().getName());
 75  
 
 76  1
         component.setRoleHint(filterEmptyAsNull(anno.hint()));
 77  
 
 78  1
         component.setImplementation(className);
 79  
 
 80  1
         component.setVersion(filterEmptyAsNull(anno.version()));
 81  
 
 82  1
         component.setComponentType(filterEmptyAsNull(anno.type()));
 83  
 
 84  1
         component.setInstantiationStrategy(filterEmptyAsNull(anno.instantiationStrategy()));
 85  
 
 86  1
         component.setLifecycleHandler(filterEmptyAsNull(anno.lifecycleHandler()));
 87  
 
 88  1
         component.setComponentProfile(filterEmptyAsNull(anno.profile()));
 89  
 
 90  1
         component.setComponentComposer(filterEmptyAsNull(anno.composer()));
 91  
 
 92  1
         component.setComponentConfigurator(filterEmptyAsNull(anno.configurator()));
 93  
 
 94  1
         component.setComponentFactory(filterEmptyAsNull(anno.factory()));
 95  
 
 96  1
         component.setDescription(filterEmptyAsNull(anno.description()));
 97  
 
 98  1
         component.setAlias(filterEmptyAsNull(anno.alias()));
 99  
 
 100  1
         component.setIsolatedRealm(anno.isolatedRealm());
 101  
 
 102  1
         for (AnnClass c : getClasses(annClass, cl)) {
 103  1
             for (AnnField field : c.getFields().values()) {
 104  2
                 ComponentRequirement requirement = findRequirement(field, c, cl);
 105  
 
 106  2
                 if (requirement != null) {
 107  2
                     component.addRequirement(requirement);
 108  
                 }
 109  
 
 110  2
                 PlexusConfiguration config = findConfiguration(field, c, cl);
 111  
 
 112  2
                 if (config != null) {
 113  1
                     addChildConfiguration(component, config);
 114  
                 }
 115  2
             }
 116  
 
 117  
             //
 118  
             // TODO: Inspect methods?
 119  
             //
 120  1
         }
 121  
 
 122  1
         return component;
 123  
     }
 124  
 
 125  
     private AnnClass readClass(String className, ClassLoader cl) throws ComponentGleanerException 
 126  
     {
 127  1
             InputStream is = null;
 128  
             
 129  
             try 
 130  
             {
 131  
                     // only read annotation from project classes (not jars)
 132  1
                     Enumeration<URL> en = cl.getResources( className + ".class" );
 133  1
                     while ( en.hasMoreElements() ) {
 134  1
                                 URL url = en.nextElement();
 135  1
                                 if( url.toString().startsWith( "file:" ) ) 
 136  
                                 {
 137  1
                                         is = url.openStream();
 138  1
                                         return AnnReader.read( is, cl );
 139  
                                 }        
 140  0
                         }
 141  0
                     throw new ComponentGleanerException("Can't find class " + className);
 142  
         } 
 143  0
         catch (IOException ex) 
 144  
         {
 145  0
                 throw new ComponentGleanerException("Can't read class " + className, ex);
 146  
         }
 147  
         finally
 148  
         {
 149  1
                 IOUtil.close(is);
 150  
         }
 151  
     }
 152  
 
 153  
     private AnnClass readClass2(String className, ClassLoader cl) throws ComponentGleanerException 
 154  
     {
 155  0
         InputStream is = null;
 156  
         try 
 157  
         {
 158  0
           is = cl.getResourceAsStream(className + ".class");
 159  0
           return AnnReader.read(is, cl);
 160  
         } 
 161  0
         catch (IOException ex) 
 162  
         {
 163  0
           throw new ComponentGleanerException("Can't read class " + className, ex);
 164  
         }
 165  
         finally
 166  
         {
 167  0
           IOUtil.close(is);
 168  
         }
 169  
     }
 170  
     
 171  
     /**
 172  
      * Returns a list of all of the classes which the given type inherits from.
 173  
      */
 174  
     private List<AnnClass> getClasses(AnnClass annClass, ClassLoader cl) throws ComponentGleanerException {
 175  1
         assert annClass != null;
 176  
 
 177  1
         List<AnnClass> classes = new ArrayList<AnnClass>();
 178  
 
 179  1
         while(annClass!=null) {
 180  1
             classes.add(annClass);
 181  1
             String superName = annClass.getSuperName();
 182  1
             if(superName!=null && !superName.equals(OBJECT_SLASHED_NAME)) {
 183  0
               annClass = readClass2(superName, cl);
 184  
             } else {
 185  
               break;
 186  
             }
 187  
 
 188  
             //
 189  
             // TODO: See if we need to include interfaces here too?
 190  
             //
 191  0
         }
 192  
 
 193  1
         return classes;
 194  
     }
 195  
 
 196  
     private ComponentRequirement findRequirement(final AnnField field, AnnClass annClass, ClassLoader cl) 
 197  
         throws ComponentGleanerException 
 198  
     {
 199  2
         assert field != null;
 200  
 
 201  2
         Requirement anno = field.getAnnotation(Requirement.class);
 202  
         
 203  2
         if (anno == null) {
 204  0
             return null;
 205  
         }
 206  
 
 207  2
         String fieldType = field.getType();
 208  
         
 209  
         // TODO implement type resolution without loading classes
 210  
         Class<?> type;
 211  
         try {
 212  2
           type = Class.forName(fieldType, false, cl);
 213  0
         } catch (ClassNotFoundException ex) {
 214  
           // TODO Auto-generated catch block
 215  0
           throw new ComponentGleanerException("Can't load class " + fieldType);
 216  2
         }
 217  
 
 218  
         ComponentRequirement requirement;
 219  
 
 220  2
         if (isRequirementListType(type)) {
 221  1
             requirement = new ComponentRequirementList();
 222  
 
 223  1
             String[] hints = anno.hints();
 224  
 
 225  1
             if (hints != null && hints.length > 0) {
 226  1
                 ((ComponentRequirementList)requirement).setRoleHints(Arrays.asList(hints));
 227  
             }
 228  
 
 229  
             //
 230  
             // TODO: See if we can glean any type details out of any generic information from the map or collection
 231  
             //
 232  1
         }
 233  
         else {
 234  1
             requirement = new ComponentRequirement();
 235  
 
 236  1
             requirement.setRoleHint(filterEmptyAsNull(anno.hint()));
 237  
         }
 238  
 
 239  
         // TODO need to read default annotation values 
 240  
         // if (anno.role()==null || anno.role().isAssignableFrom(Object.class)) {
 241  2
         if (anno.role().isAssignableFrom(Object.class)) {
 242  1
             requirement.setRole(type.getName());
 243  
         }
 244  
         else {
 245  1
             requirement.setRole(anno.role().getName());
 246  
         }
 247  
 
 248  2
         requirement.setFieldName(field.getName());
 249  
 
 250  2
         requirement.setFieldMappingType(type.getName());
 251  
 
 252  2
         requirement.setOptional( anno.optional() );
 253  
 
 254  2
         return requirement;
 255  
     }
 256  
 
 257  
     private PlexusConfiguration findConfiguration(AnnField field, AnnClass c, ClassLoader cl) {
 258  2
         assert field != null;
 259  
 
 260  2
         Configuration anno = field.getAnnotation(Configuration.class);
 261  
 
 262  2
         if (anno == null) {
 263  1
             return null;
 264  
         }
 265  
 
 266  1
         String name = filterEmptyAsNull(anno.name());
 267  1
         if (name == null) {
 268  0
             name = field.getName();
 269  
         }
 270  1
         name = deHump(name);
 271  
         
 272  1
         XmlPlexusConfiguration config = new XmlPlexusConfiguration(name);
 273  
 
 274  1
         String value = filterEmptyAsNull(anno.value());
 275  1
         if (value != null) {
 276  1
             config.setValue(value);
 277  
         }
 278  
 
 279  1
         return config;
 280  
     }
 281  
 }