Coverage Report - pt.digitalis.dif.codegen.util.ClassEnhancementContext
 
Classes in this File Line Coverage Branch Coverage Complexity
ClassEnhancementContext
0%
0/127
0%
0/29
2,353
ClassEnhancementContext$1
0%
0/1
N/A
2,353
 
 1  0
 /**
 2  
  * 2007, Digitalis Informatica. All rights reserved. Distribuicao e Gestao de Informatica, Lda. Estrada de Paco de Arcos
 3  
  * num.9 - Piso -1 2780-666 Paco de Arcos Telefone: (351) 21 4408990 Fax: (351) 21 4408999 http://www.digitalis.pt
 4  
  */
 5  
 
 6  
 package pt.digitalis.dif.codegen.util;
 7  
 
 8  
 import java.util.HashMap;
 9  
 import java.util.Map;
 10  
 
 11  
 import javassist.CannotCompileException;
 12  
 import pt.digitalis.dif.codegen.CGAncillaries;
 13  
 import pt.digitalis.dif.codegen.templates.ApplicationCGTemplate;
 14  
 import pt.digitalis.dif.codegen.templates.ProviderCGTemplate;
 15  
 import pt.digitalis.dif.codegen.templates.ServiceCGTemplate;
 16  
 import pt.digitalis.dif.codegen.templates.StageCGTemplate;
 17  
 import pt.digitalis.dif.codegen.templates.StageInstanceCGTemplate;
 18  
 import pt.digitalis.dif.dem.Entity;
 19  
 import pt.digitalis.dif.exception.codegen.DIFCodeGenerationException;
 20  
 import pt.digitalis.dif.utils.ObjectFormatter;
 21  
 import pt.digitalis.utils.CodeGenUtil4Javassist;
 22  
 import pt.digitalis.utils.bytecode.exceptions.CodeGenerationException;
 23  
 import pt.digitalis.utils.bytecode.holders.ClassHolder;
 24  
 import pt.digitalis.utils.inspection.exception.ResourceNotFoundException;
 25  
 
 26  
 /**
 27  
  * Holds the information needed to enhance a set of classes.
 28  
  * 
 29  
  * @author Rodrigo Gon�alves <a href="mailto:rgoncalves@digitalis.pt">rgoncalves@digitalis.pt</a><br/>
 30  
  * @created 2007/12/10
 31  
  */
 32  0
 public class ClassEnhancementContext {
 33  
 
 34  
     /** The initial map capacity. */
 35  
     final static private int INITIAL_CAPACITY = 2;
 36  
 
 37  
     /** The initial load factor. */
 38  
     final static private float LOAD_FACTOR = 1.0f;
 39  
 
 40  
     /** Holds the enhancements to do on the target classes. */
 41  
     /*
 42  
      * Implementation Note: this hash-map is optimised for the situation we're having here. At most, we will have two
 43  
      * objects to enhance (Stage). By forcing a load factor of 1.0 we're guaranteeing that no rehash (hence no memory
 44  
      * footprint or processing time elapsing) will occur. An initial capacity of 2 and a load factor of 0.75 (default
 45  
      * value) would yield a rehashing to size 4 after the first insertion (HashMap source code defines the rehashing
 46  
      * threshold as: threshold = (int) (initialCapacity * loadFactor);). With the above values we will have a threshold
 47  
      * of exactly 2, and rehashing will occur only when (++size > threshold), hence when the third element is added.
 48  
      */
 49  0
     private Map<String, ClassEnhancements> classEnhancements = new HashMap<String, ClassEnhancements>(INITIAL_CAPACITY,
 50  0
             LOAD_FACTOR);
 51  
 
 52  
     /** The class ID for the entity to be returned has the entity class */
 53  
     private String entityClassID;
 54  
 
 55  
     /** */
 56  
     private Entity entityType;
 57  
 
 58  
     /** The original annotated POJO. */
 59  
     private ClassHolder theOriginalClass;
 60  
 
 61  
     /**
 62  
      * Builds a class enhancement context from a class holder.
 63  
      * 
 64  
      * @param classHolder
 65  
      *            the class holder for the class to enhance
 66  
      * @throws ResourceNotFoundException
 67  
      *             if the class annotations can't be read
 68  
      * @throws CodeGenerationException
 69  
      *             if the passed class holder isn't a valid DEM entity or the class holder's target semantics is
 70  
      *             incorrect
 71  
      */
 72  0
     public ClassEnhancementContext(ClassHolder classHolder) throws ResourceNotFoundException, CodeGenerationException
 73  
     {
 74  
 
 75  
         // Store original annotated POJO
 76  0
         this.theOriginalClass = classHolder;
 77  
 
 78  
         // Infer entity type
 79  0
         this.entityType = Entity.getEntityTypeFromClass(this.theOriginalClass);
 80  
 
 81  
         // If the entity is not a valid DEM entity throw an exception
 82  0
         if (this.entityType == null)
 83  0
             throw new CodeGenerationException(this.theOriginalClass.getFQName()
 84  0
                     + " is not a valid DEM entity and thus cannot be enhanced! Aborting...");
 85  
 
 86  
         // Set enhancements accordingly to entity type
 87  
 
 88  
         // For stages there will be two enriched classes, the proxy and the instances
 89  0
         if (entityType.equals(Entity.STAGE))
 90  
         {
 91  0
             this.prepareStageEnhancement();
 92  
         }
 93  
         // For all other entities there will be just one enriched class
 94  
         else
 95  
         {
 96  0
             this.prepareNonStageEnhancement();
 97  
         }
 98  0
     }
 99  
 
 100  
     /**
 101  
      * Builds a class enhancement context from a class ID.
 102  
      * 
 103  
      * @param originalClassID
 104  
      *            the original annotated class
 105  
      * @throws ResourceNotFoundException
 106  
      *             if the class with the given id can't be found
 107  
      * @throws CodeGenerationException
 108  
      *             if the code can't be compiled
 109  
      */
 110  
     public ClassEnhancementContext(String originalClassID) throws ResourceNotFoundException, CodeGenerationException
 111  
     {
 112  0
         this(new ClassHolder(originalClassID));
 113  0
     }
 114  
 
 115  
     /**
 116  
      * Adds an enhancement to a given class method.
 117  
      * 
 118  
      * @param className
 119  
      *            the name of the class to add the enhancement to
 120  
      * @param enhancement
 121  
      *            the enhancement to add
 122  
      * @throws DIFCodeGenerationException
 123  
      */
 124  
     public void addEnhancement(String className, ClassMethodEnhancement enhancement) throws DIFCodeGenerationException
 125  
     {
 126  
         try
 127  
         {
 128  0
             this.classEnhancements.get(className).addEnhancement(enhancement);
 129  
         }
 130  0
         catch (Exception e)
 131  
         {
 132  0
             DIFCodeGenerationException codeGenException = new DIFCodeGenerationException(
 133  0
                     "Error adding enhancement to class", e);
 134  0
             codeGenException.addToExceptionContext("Original Class Name", theOriginalClass.getFQName());
 135  0
             codeGenException.addToExceptionContext("Entity ID", entityClassID);
 136  0
             codeGenException.addToExceptionContext("Entity Type", entityType);
 137  0
             codeGenException.addToExceptionContext("Class Name", className);
 138  0
             codeGenException.addToExceptionContext("Enhancement", enhancement);
 139  
 
 140  0
             throw codeGenException;
 141  
         }
 142  0
     }
 143  
 
 144  
     /**
 145  
      * Adds an enhancement to a given class method. This method should be used for non-stage classes only since it does
 146  
      * provide means for specifying what class is to be enhanced
 147  
      * 
 148  
      * @param methodName
 149  
      *            the name of the method to add the enhancement to
 150  
      * @param enhancement
 151  
      *            the enhancement to add
 152  
      * @throws DIFCodeGenerationException
 153  
      */
 154  
     public void addEnhancement(String methodName, String enhancement) throws DIFCodeGenerationException
 155  
     {
 156  0
         if (entityType == Entity.STAGE)
 157  0
             addEnhancement(CGAncillaries.STAGE_PROXY_ID, methodName, enhancement);
 158  
         else
 159  0
             addEnhancement(CGAncillaries.NON_STAGE_ENRICHED_CLASS_ID, methodName, enhancement);
 160  0
     }
 161  
 
 162  
     /**
 163  
      * Adds an enhancement to a given class method.
 164  
      * 
 165  
      * @param className
 166  
      *            the name of the class to add the enhancement to
 167  
      * @param methodName
 168  
      *            the name of the method to add the enhancement to
 169  
      * @param enhancement
 170  
      *            the enhancement to add
 171  
      * @throws DIFCodeGenerationException
 172  
      */
 173  
     public void addEnhancement(String className, String methodName, String enhancement)
 174  
             throws DIFCodeGenerationException
 175  
     {
 176  
         try
 177  
         {
 178  0
             this.classEnhancements.get(className).addSource(methodName, enhancement);
 179  
         }
 180  0
         catch (Exception e)
 181  
         {
 182  0
             DIFCodeGenerationException codeGenException = new DIFCodeGenerationException(
 183  0
                     "Error adding enhancement to class", e);
 184  0
             codeGenException.addToExceptionContext("Original Class Name", theOriginalClass.getFQName());
 185  0
             codeGenException.addToExceptionContext("Entity ID", entityClassID);
 186  0
             codeGenException.addToExceptionContext("Entity Type", entityType);
 187  0
             codeGenException.addToExceptionContext("Class Name", className);
 188  0
             codeGenException.addToExceptionContext("Method Name", methodName);
 189  0
             codeGenException.addToExceptionContext("Enhancement", enhancement);
 190  
 
 191  0
             throw codeGenException;
 192  
         }
 193  0
     }
 194  
 
 195  
     /**
 196  
      * Adds an enhancement to a given class method.
 197  
      * 
 198  
      * @param className
 199  
      *            the name of the class to add the enhancement to
 200  
      * @param methodName
 201  
      *            the name of the method to add the enhancement to
 202  
      * @param terminator
 203  
      *            the finalize code for the method
 204  
      * @throws DIFCodeGenerationException
 205  
      */
 206  
     public void addEnhancementTerminatorCode(String className, String methodName, String terminator)
 207  
             throws DIFCodeGenerationException
 208  
     {
 209  
         try
 210  
         {
 211  0
             this.classEnhancements.get(className).setTerminator(methodName, terminator);
 212  
         }
 213  0
         catch (Exception e)
 214  
         {
 215  0
             DIFCodeGenerationException codeGenException = new DIFCodeGenerationException(
 216  0
                     "Error adding enhancement to class", e);
 217  0
             codeGenException.addToExceptionContext("Original Class Name", theOriginalClass.getFQName());
 218  0
             codeGenException.addToExceptionContext("Entity ID", entityClassID);
 219  0
             codeGenException.addToExceptionContext("Entity Type", entityType);
 220  0
             codeGenException.addToExceptionContext("Class Name", className);
 221  0
             codeGenException.addToExceptionContext("Method Name", methodName);
 222  0
             codeGenException.addToExceptionContext("Terminator", terminator);
 223  
 
 224  0
             throw codeGenException;
 225  
         }
 226  0
     }
 227  
 
 228  
     /**
 229  
      * Commits all the enhancements to a class file and returns the class holder object.
 230  
      * 
 231  
      * @throws ResourceNotFoundException
 232  
      *             if the class can't be found
 233  
      * @throws CodeGenerationException
 234  
      *             if the code can't be compiled
 235  
      */
 236  
     public void commitEnhancements() throws ResourceNotFoundException, CodeGenerationException
 237  
     {
 238  0
         for (String className: this.classEnhancements.keySet())
 239  
         {
 240  
             try
 241  
             {
 242  0
                 this.classEnhancements.get(className).commitEnhancements();
 243  
             }
 244  0
             catch (Exception e)
 245  
             {
 246  0
                 DIFCodeGenerationException codeGenException = new DIFCodeGenerationException(
 247  0
                         "Error adding enhancement to class", e);
 248  0
                 codeGenException.addToExceptionContext("Original Class Name", theOriginalClass.getFQName());
 249  0
                 codeGenException.addToExceptionContext("Entity ID", entityClassID);
 250  0
                 codeGenException.addToExceptionContext("Entity Type", entityType);
 251  0
                 codeGenException.addToExceptionContext("Class Name", className);
 252  
             }
 253  
         }
 254  0
     }
 255  
 
 256  
     /**
 257  
      * Inspects the enhancement map to see of the given method has been inserted for enhancement
 258  
      * 
 259  
      * @param methodName
 260  
      *            the name of the method to add the enhancement to
 261  
      * @return T if the method is present
 262  
      */
 263  
     public boolean containsEnhancement(String methodName)
 264  
     {
 265  0
         if (entityType == Entity.STAGE)
 266  0
             return containsEnhancement(CGAncillaries.STAGE_PROXY_ID, methodName);
 267  
         else
 268  0
             return containsEnhancement(CGAncillaries.NON_STAGE_ENRICHED_CLASS_ID, methodName);
 269  
     }
 270  
 
 271  
     /**
 272  
      * Inspects the enhancement map to see of the given method has been inserted for enhancement
 273  
      * 
 274  
      * @param className
 275  
      *            the name of the class to add the enhancement to
 276  
      * @param methodName
 277  
      *            the name of the method to add the enhancement to
 278  
      * @return T if the method is present
 279  
      */
 280  
     public boolean containsEnhancement(String className, String methodName)
 281  
     {
 282  0
         return this.classEnhancements.get(className).getMethodEnhancements().containsKey(methodName);
 283  
     }
 284  
 
 285  
     /**
 286  
      * Returns the class enhancements map.
 287  
      * 
 288  
      * @return the class enhancements map.
 289  
      */
 290  
     public Map<String, ClassEnhancements> getClassEnhancements()
 291  
     {
 292  0
         return this.classEnhancements;
 293  
     }
 294  
 
 295  
     /**
 296  
      * Return the enriched entity class holder.
 297  
      * 
 298  
      * @return the class holder
 299  
      */
 300  
     public ClassHolder getEntityClass()
 301  
     {
 302  0
         return classEnhancements.get(entityClassID).getClassObject();
 303  
     }
 304  
 
 305  
     /**
 306  
      * Return the original POJO class holder.
 307  
      * 
 308  
      * @return the class holder
 309  
      */
 310  
     public ClassHolder getOriginalClassObject()
 311  
     {
 312  0
         return this.theOriginalClass;
 313  
     }
 314  
 
 315  
     /**
 316  
      * Returns the original class name.
 317  
      * 
 318  
      * @return the original class name
 319  
      */
 320  
     public String getOriginalClassName()
 321  
     {
 322  0
         return this.theOriginalClass.getFQName();
 323  
     }
 324  
 
 325  
     /**
 326  
      * Prepare the class enhancement context for a non-stage entity.
 327  
      * 
 328  
      * @throws CodeGenerationException
 329  
      *             if the Entity is invalid
 330  
      * @throws ResourceNotFoundException
 331  
      *             if the template class can't be found
 332  
      */
 333  
     final private void prepareNonStageEnhancement() throws CodeGenerationException, ResourceNotFoundException
 334  
     {
 335  
         // Create entity class holder
 336  0
         this.entityClassID = CGAncillaries.NON_STAGE_ENRICHED_CLASS_ID;
 337  0
         ClassHolder enrichedClassHolder = CodeGenUtil4Javassist.createNewClassHolder(this.theOriginalClass.getFQName()
 338  0
                 + CGAncillaries.NON_STAGE_ENRICHED_CLASS_ID);
 339  
         try
 340  
         {
 341  
             // Extend stage instance from original stage
 342  0
             enrichedClassHolder.setSuperClass(this.theOriginalClass.getFQName());
 343  
         }
 344  0
         catch (CannotCompileException cannotCompileException)
 345  
         {
 346  0
             throw new CodeGenerationException("Class " + this.theOriginalClass.getFQName()
 347  0
                     + " already extends from another class! Unable to compile...", cannotCompileException);
 348  
         }
 349  
 
 350  
         // Copy methods from the appropriate template
 351  0
         switch (entityType)
 352  
         {
 353  
             case VALIDATOR:
 354  
                 // No ValidatorCGTemplate class defined in this release
 355  
                 // enrichedClassHolder.copyAllFromClass(new ClassHolder(ValidatorCGTemplate.class.getCanonicalName()));
 356  0
                 break;
 357  
             case PROVIDER:
 358  0
                 enrichedClassHolder.copyAllFromClass(new ClassHolder(ProviderCGTemplate.class.getCanonicalName()));
 359  0
                 break;
 360  
             case APPLICATION:
 361  0
                 enrichedClassHolder.copyAllFromClass(new ClassHolder(ApplicationCGTemplate.class.getCanonicalName()));
 362  0
                 break;
 363  
             case SERVICE:
 364  0
                 enrichedClassHolder.copyAllFromClass(new ClassHolder(ServiceCGTemplate.class.getCanonicalName()));
 365  0
                 break;
 366  
             default:
 367  0
                 throw new CodeGenerationException("Invalid entity type: " + this.theOriginalClass.getFQName()
 368  0
                         + "!! Aborting...");
 369  
         }
 370  
 
 371  
         // Create class enhancements object
 372  0
         ClassEnhancements enrichedClassEnhancements = new ClassEnhancements(enrichedClassHolder);
 373  
 
 374  
         // Add class to class enhancement maps
 375  0
         classEnhancements.put(CGAncillaries.NON_STAGE_ENRICHED_CLASS_ID, enrichedClassEnhancements);
 376  0
     }
 377  
 
 378  
     /**
 379  
      * Prepare the class enhancement context for a stage entity.
 380  
      * 
 381  
      * @throws ResourceNotFoundException
 382  
      *             if any class to copy or to extend from can't be found
 383  
      * @throws CodeGenerationException
 384  
      *             if the original stage POJO already inherits from another class
 385  
      */
 386  
     final private void prepareStageEnhancement() throws CodeGenerationException, ResourceNotFoundException
 387  
     {
 388  
         // Create proxy class holder
 389  0
         this.entityClassID = CGAncillaries.STAGE_PROXY_ID;
 390  0
         ClassHolder stageProxyClassHolder = CodeGenUtil4Javassist.createNewClassHolder(this.theOriginalClass
 391  0
                 .getFQName() + CGAncillaries.STAGE_PROXY_ID);
 392  
         // Copy StageCGTemplate
 393  0
         stageProxyClassHolder.copyAllFromClass(new ClassHolder(StageCGTemplate.class.getCanonicalName()));
 394  
         // Create instance class holder
 395  0
         ClassEnhancements stageProxyClassEnhancements = new ClassEnhancements(stageProxyClassHolder);
 396  
 
 397  
         // Create stage instance class holder
 398  0
         ClassHolder stageInstanceClassHolder = CodeGenUtil4Javassist.createNewClassHolder(this.theOriginalClass
 399  0
                 .getFQName() + CGAncillaries.STAGE_INSTANCE_ID);
 400  
 
 401  
         try
 402  
         {
 403  
             // Extend stage instance from original stage
 404  0
             stageInstanceClassHolder.setSuperClass(this.theOriginalClass.getFQName());
 405  
         }
 406  0
         catch (CannotCompileException cannotCompileException)
 407  
         {
 408  0
             throw new CodeGenerationException("Class " + this.theOriginalClass.getFQName()
 409  0
                     + " already extends from another class! Unable to compile...", cannotCompileException);
 410  
         }
 411  
         // Copy StageInstanceCGTemplate
 412  0
         stageInstanceClassHolder.copyAllFromClass(new ClassHolder(StageInstanceCGTemplate.class.getCanonicalName()));
 413  
 
 414  
         // Create class enhancements object
 415  0
         ClassEnhancements stageInstanceClassEnhancements = new ClassEnhancements(stageInstanceClassHolder);
 416  
 
 417  
         // Stage proxys have list builder methods. They will be incremental.
 418  0
         stageProxyClassEnhancements.registerMethodAsIncremental(CGAncillaries.STAGE_INJECTED_VIEWS_BUILDER);
 419  0
         stageProxyClassEnhancements.registerMethodAsIncremental(CGAncillaries.STAGE_INJECTED_ERRORVIEWS_BUILDER);
 420  0
         stageProxyClassEnhancements.registerMethodAsIncremental(CGAncillaries.STAGE_INJECTED_STAGES_BUILDER);
 421  0
         stageProxyClassEnhancements.registerMethodAsIncremental(CGAncillaries.STAGE_INJECTED_ERRORSTAGES_BUILDER);
 422  0
         stageProxyClassEnhancements.registerMethodAsIncremental(CGAncillaries.STAGE_EVENT_HANDLERS_BUILDER);
 423  
 
 424  
         // Stage instances will have initialization and finalization methods. All will be treated as incremental
 425  
         // methods, as such they must be registered.
 426  0
         stageInstanceClassEnhancements
 427  0
                 .registerMethodAsIncremental(CGAncillaries.STAGE_INJECTED_ATTRIBUTES_INIT_METHOD_NAME);
 428  0
         stageInstanceClassEnhancements.registerMethodAsIncremental(CGAncillaries.STAGE_POSTPROCESSING_METHOD_NAME);
 429  0
         stageInstanceClassEnhancements.registerMethodAsIncremental(CGAncillaries.CALL_EXEC_ONEVENT_METHOD);
 430  0
         stageInstanceClassEnhancements.registerMethodAsIncremental(CGAncillaries.CALL_EVENT_METHOD);
 431  
 
 432  
         // Add classes to class enhancement maps
 433  0
         classEnhancements.put(CGAncillaries.STAGE_PROXY_ID, stageProxyClassEnhancements);
 434  0
         classEnhancements.put(CGAncillaries.STAGE_INSTANCE_ID, stageInstanceClassEnhancements);
 435  0
     }
 436  
 
 437  
     /**
 438  
      * Adds an enhancement to a given class method.
 439  
      * 
 440  
      * @param className
 441  
      *            the name of the class to add the enhancement to
 442  
      * @param methodName
 443  
      *            the name of the method to add the enhancement to
 444  
      * @param enhancement
 445  
      *            the enhancement to add
 446  
      */
 447  
     public void setEnhancements(String className, String methodName, String enhancement)
 448  
     {
 449  0
         this.classEnhancements.get(className).addSource(methodName, enhancement);
 450  0
     }
 451  
 
 452  
     /**
 453  
      * @see java.lang.Object#toString()
 454  
      */
 455  
     @Override
 456  
     public String toString()
 457  
     {
 458  0
         ObjectFormatter formatter = new ObjectFormatter();
 459  
 
 460  0
         formatter.addItem("Original Class Name", this.getOriginalClassName());
 461  0
         formatter.addItem("Entity Class Type", this.entityType);
 462  0
         formatter.addItem("Entity Class Name", this.entityClassID);
 463  0
         formatter.addItem("Classes", this.classEnhancements);
 464  
 
 465  0
         return formatter.getFormatedObject();
 466  
     }
 467  
 }