Coverage report

  %line %branch
nl.toolforge.karma.core.manifest.AbstractManifest$2
0% 
0% 

 1  
 /*
 2  
 Karma core - Core of the Karma application
 3  
 Copyright (C) 2004  Toolforge <www.toolforge.nl>
 4  
 
 5  
 This library is free software; you can redistribute it and/or
 6  
 modify it under the terms of the GNU Lesser General Public
 7  
 License as published by the Free Software Foundation; either
 8  
 version 2.1 of the License, or (at your option) any later version.
 9  
 
 10  
 This library is distributed in the hope that it will be useful,
 11  
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
 Lesser General Public License for more details.
 14  
 
 15  
 You should have received a copy of the GNU Lesser General Public
 16  
 License along with this library; if not, write to the Free Software
 17  
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 18  
 */
 19  
 package nl.toolforge.karma.core.manifest;
 20  
 
 21  
 import nl.toolforge.karma.core.KarmaRuntimeException;
 22  
 import nl.toolforge.karma.core.boot.WorkingContext;
 23  
 import nl.toolforge.karma.core.location.LocationException;
 24  
 import nl.toolforge.karma.core.module.BaseModule;
 25  
 import nl.toolforge.karma.core.module.Module;
 26  
 import nl.toolforge.karma.core.module.ModuleDigester;
 27  
 import nl.toolforge.karma.core.module.ModuleFactory;
 28  
 import nl.toolforge.karma.core.scm.ModuleDependency;
 29  
 import nl.toolforge.karma.core.vc.cvsimpl.AdminHandler;
 30  
 import org.apache.commons.io.FileUtils;
 31  
 
 32  
 import java.io.File;
 33  
 import java.io.FilenameFilter;
 34  
 import java.io.IOException;
 35  
 import java.util.ArrayList;
 36  
 import java.util.Collection;
 37  
 import java.util.HashMap;
 38  
 import java.util.HashSet;
 39  
 import java.util.Hashtable;
 40  
 import java.util.Iterator;
 41  
 import java.util.Map;
 42  
 import java.util.Set;
 43  
 
 44  
 /**
 45  
  * <p>General stuff for a manifest.</p>
 46  
  *
 47  
  * <p>Check the <a href="package-summary.html">package documentation</a> for more information on the concepts behind
 48  
  * Karma.</p>
 49  
  *
 50  
  * @author D.A. Smedes
 51  
  * @version $Id: AbstractManifest.java,v 1.38 2004/11/10 23:53:09 asmedes Exp $
 52  
  */
 53  
 public abstract class AbstractManifest implements Manifest {
 54  
 
 55  
   private Collection childManifests = new ArrayList();
 56  
 
 57  
   private String name = null;
 58  
   private String version = null;
 59  
   private String description = null;
 60  
 
 61  
   private Map modules = null;
 62  
 
 63  
   private WorkingContext workingContext = null;
 64  
   private ManifestStructure manifestStructure = null;
 65  
 
 66  
   private File manifestBaseDirectory = null;
 67  
   private File manifestTempDirectory = null;
 68  
   private Map moduleCache = null;
 69  
 
 70  
   /**
 71  
    * Constructs a manifest instance; <code>name</code> is mandatory.
 72  
    */
 73  
   public AbstractManifest(WorkingContext workingContext, String name) throws ManifestException, LocationException {
 74  
 
 75  
     if ("".equals(name) || name == null) {
 76  
       throw new IllegalArgumentException("Manifest name cannot be empty or null.");
 77  
     }
 78  
     this.name = name;
 79  
 
 80  
     ManifestLoader loader = new ManifestLoader(workingContext);
 81  
     this.manifestStructure = loader.load(name);
 82  
 
 83  
     init();
 84  
   }
 85  
 
 86  
   /**
 87  
    * A manifest is created based on its <code>ManifestStructure</code>, which can be loaded by the
 88  
    * <code>ManifestLoader</code>. The <code>ManifestStructure</code> is the basis for the Manifest; a number of checks
 89  
    * are applied to it, including a linking of the manifest to the {@link WorkingContext}.
 90  
    *
 91  
    * @param workingContext The current working context.
 92  
    * @param structure The ManifestStructure, which is the basis for the manifest.
 93  
    */
 94  
   public AbstractManifest(WorkingContext workingContext, ManifestStructure structure) throws LocationException {
 95  
 
 96  
     this.workingContext = workingContext;
 97  
     this.manifestStructure = structure;
 98  
 
 99  
     this.name = structure.getName();
 100  
 
 101  
     init();
 102  
   }
 103  
 
 104  
   private void init() throws LocationException {
 105  
 
 106  
     // Recursively load all modules from the root manifest and all includes to have them available quickly as one
 107  
     // list.
 108  
     //
 109  
     copyStructure();
 110  
 
 111  
     // Apply the current working context to this manifest.
 112  
     //
 113  
     applyWorkingContext();
 114  
   }
 115  
 
 116  
   //
 117  
   //
 118  
   //
 119  
   private void copyStructure() throws LocationException {
 120  
 
 121  
     // Step 1
 122  
     //
 123  
     modules = new Hashtable();
 124  
 
 125  
     ModuleFactory moduleFactory = new ModuleFactory(workingContext);
 126  
 
 127  
     for (Iterator i = manifestStructure.getModules().iterator(); i.hasNext();) {
 128  
       Module module = moduleFactory.create((ModuleDigester) i.next(), Module.UNKNOWN);
 129  
       modules.put(module.getName(), module);
 130  
     }
 131  
 
 132  
     // Step 2
 133  
     //
 134  
     moduleCache = new HashMap();
 135  
 
 136  
     // If there is nothing in the cache, there is a chance that we have not yet
 137  
 
 138  
     ManifestFactory factory = new ManifestFactory();
 139  
 
 140  
     for (Iterator i = manifestStructure.getChilds().values().iterator(); i.hasNext();) {
 141  
 
 142  
       ManifestStructure childStructure = (ManifestStructure) i.next();
 143  
       Manifest manifest = factory.create(workingContext, childStructure);
 144  
 
 145  
       childManifests.add(manifest);
 146  
       moduleCache.putAll(manifest.getAllModules());
 147  
     }
 148  
     moduleCache.putAll(getModulesForManifest());
 149  
   }
 150  
 
 151  
   //
 152  
   //
 153  
   //
 154  
   private void applyWorkingContext() {
 155  
 
 156  
     manifestBaseDirectory = new File(workingContext.getProjectBaseDirectory(), getName());
 157  
     manifestTempDirectory = new File(getBaseDirectory(), "tmp");
 158  
 
 159  
     for (Iterator i = moduleCache.values().iterator(); i.hasNext();) {
 160  
 
 161  
       Module module = (Module) i.next();
 162  
       setModuleBaseDir(module);
 163  
       applyWorkingContext(workingContext, module);
 164  
       removeLocal(module);
 165  
     }
 166  
   }
 167  
 
 168  
   /**
 169  
    * A specific <code>Manifest</code> implementation may have to apply specific actions to modules per working context.
 170  
    * Each implementation should therefor implement this method and do what it has to do.
 171  
    *
 172  
    * @param context The current {@link WorkingContext}.
 173  
    * @param module The module to which <code>context</code> should be applied.
 174  
    */
 175  
   protected abstract void applyWorkingContext(WorkingContext context, Module module);
 176  
 
 177  
   public final File getBaseDirectory() {
 178  
 
 179  
     if (!manclass="keyword">ifestBaseDirectory.exists()) {
 180  
       manifestBaseDirectory.mkdir();
 181  
     }
 182  
     return manifestBaseDirectory;
 183  
   }
 184  
 
 185  
   public File getBuildBaseDirectory() {
 186  
 
 187  
     File f = new File(getBaseDirectory(), "build");
 188  
 
 189  
     if (!f.exists()) {
 190  
       f.mkdir();
 191  
     }
 192  
     return f;
 193  
   }
 194  
 
 195  
   public File getReportsBaseDirectory() {
 196  
 
 197  
     File f = new File(getBaseDirectory(), "reports");
 198  
 
 199  
     if (!f.exists()) {
 200  
       f.mkdir();
 201  
     }
 202  
     return f;
 203  
   }
 204  
 
 205  
   public File getModuleBaseDirectory() {
 206  
 
 207  
     File f = new File(getBaseDirectory(), "modules");
 208  
 
 209  
     if (!f.exists()) {
 210  
       f.mkdir();
 211  
     }
 212  
     return f;
 213  
   }
 214  
 
 215  
   public final File getTempDirectory() {
 216  
 
 217  
     if (!manclass="keyword">ifestTempDirectory.exists()) {
 218  
       manifestTempDirectory.mkdir();
 219  
     }
 220  
     return manifestTempDirectory;
 221  
   }
 222  
 
 223  
   /**
 224  
    * Gets a manifests' name (the &lt;name&gt;-attribute) from the manifest XML file.
 225  
    *
 226  
    * @return The manifests' name.
 227  
    */
 228  
   public final String getName() {
 229  
     return name;
 230  
   }
 231  
 
 232  
   public abstract String getType();
 233  
 
 234  
   /**
 235  
    * Gets a manifests' version (the &lt;version&gt;-attribute) from the manifest XML file.
 236  
    *
 237  
    * @return The manifests' version.
 238  
    */
 239  
   public final String getVersion() {
 240  
     return version;
 241  
   }
 242  
 
 243  
   /**
 244  
    * Sets the manifests' version. This method is called by
 245  
    * <a href="http://jakarta.apache.org/commons/digester">Digester</a> while parsing the manifest XML file.
 246  
    *
 247  
    * @param version The manifests' version (<code>&lt;version&gt;</code>-attribute); may be <code>null</code>.
 248  
    */
 249  
   public final void setVersion(String version) {
 250  
     this.version = version;
 251  
   }
 252  
 
 253  
   public final String getDescription() {
 254  
     return description;
 255  
   }
 256  
 
 257  
   public final void setDescription(String description) {
 258  
     this.description = description;
 259  
   }
 260  
 
 261  
   /**
 262  
    * Gets all modules defined in this manifest (excluding includedManifests).
 263  
    *
 264  
    * @see #getAllModules()
 265  
    *
 266  
    * @return A <code>Map</code> with {@link Module} instances.
 267  
    */
 268  
   public final Map getModulesForManifest() {
 269  
     return modules;
 270  
   }
 271  
 
 272  
   /**
 273  
    * Gets all modules defined in this manifest including all modules for all child manifests.
 274  
    *
 275  
    * @see #getModulesForManifest()
 276  
    *
 277  
    * @return A <code>Map</code> with {@link Module} instances.
 278  
    */
 279  
   public final Map getAllModules() {
 280  
     return moduleCache;
 281  
   }
 282  
 
 283  
   /**
 284  
    * Counts all modules for a manifest, also counting all modules of all included manifests. This method thus counts
 285  
    * all child manifests as well.
 286  
    *
 287  
    * @return The total number of modules in this manifest (inlcuding all included manifests).
 288  
    */
 289  
   public final int size() {
 290  
 
 291  
     int total = getModulesForManifest().size();
 292  
 
 293  
     for (Iterator i = childManifests.iterator(); i.hasNext();) {
 294  
       total += ((AbstractManifest) i.next()).size();
 295  
     }
 296  
 
 297  
     return total;
 298  
   }
 299  
 
 300  
 
 301  
   /**
 302  
    *
 303  
    * @param moduleName
 304  
    * @return
 305  
    * @throws ManifestException
 306  
    */
 307  
   public final Module getModule(String moduleName) throws ManifestException {
 308  
 
 309  
     Map allModules = getAllModules();
 310  
 
 311  
     if (allModules.containsKey(moduleName)) {
 312  
       return (Module) allModules.get(moduleName);
 313  
     } else {
 314  
       throw new ManifestException(ManifestException.MODULE_NOT_FOUND, class="keyword">new Object[]{moduleName});
 315  
     }
 316  
   }
 317  
 
 318  
   public final boolean isLocal() {
 319  
 
 320  
     for (Iterator i = getAllModules().values().iterator(); i.hasNext();) {
 321  
 
 322  
       Module m = (Module) i.next();
 323  
 
 324  
       // If we stumble upon a non local module, return false
 325  
       if (!isLocal(m)) {
 326  
         return false;
 327  
       }
 328  
     }
 329  
 
 330  
     return true;
 331  
   }
 332  
 
 333  
   public final boolean isLocal(Module module) {
 334  
     return module.getBaseDir().exists();
 335  
   }
 336  
 
 337  
   /**
 338  
    * Retrieves all included manifests.
 339  
    *
 340  
    * @return A <code>Collection</code> of <code>AbstractManifest</code> instances, or an empty collection if no included
 341  
    *   manifests are available.
 342  
    */
 343  
   public final Collection getIncludes() {
 344  
     return childManifests;
 345  
   }
 346  
 
 347  
   /**
 348  
    * Saves the manifest to disk, including all its included manifests.
 349  
    */
 350  
   public void save() throws ManifestException {
 351  
     // todo this requires a manifest to maintain a map of which module belongs to which manifest ...
 352  
   }
 353  
 
 354  
   /**
 355  
    * A manifest is equal to another manifest if their names are equal.
 356  
    *
 357  
    * @param o A <code>AbstractManifest</code> instance.
 358  
    */
 359  
   public final boolean equals(Object o) {
 360  
 
 361  
     if (o instanceof Manclass="keyword">ifest) {
 362  
       if (getName().equals(((Manclass="keyword">ifest) o).getName())) {
 363  
         return true;
 364  
       } else {
 365  
         return false;
 366  
       }
 367  
     } else {
 368  
       return false;
 369  
     }
 370  
   }
 371  
 
 372  
   public int hashCode() {
 373  
     return name.hashCode();
 374  
   }
 375  
 
 376  
   /**
 377  
    *
 378  
    *
 379  
    * @param module
 380  
    * @return Interdependencies for <code>module</code> or an empty <code>Collection</code>.
 381  
    */
 382  
   public final Collection getModuleInterdependencies(Module module) throws ManifestException {
 383  
 
 384  
     Collection deps = (Collection) getInterdependencies().get(module.getName());
 385  
 
 386  
     return (deps == null ? new HashSet() : deps);
 387  
   }
 388  
 
 389  
   /**
 390  
    * <p>Calculates interdepencies between modules in the manifest; interdependencies are inverse relationships
 391  
    * between a module and other modules (being <code>SourceModule</code> instances).
 392  
    *
 393  
    * <p>If a module <code>B</code> has a dependency on module <code>A</code>, then this method will return a map, with
 394  
    * a key <code>A</code> and its value a <code>Collection</code> of interdependencies (in this case, <code>B</code>).
 395  
    *
 396  
    * @return
 397  
    */
 398  
   public final Map getInterdependencies() throws ManifestException {
 399  
 
 400  
     Map interDependencies = new Hashtable();
 401  
 
 402  
     // Interdependencies can only be determined if the module has been checked out locally ...
 403  
     //
 404  
 
 405  
     Map allModules = getAllModules();
 406  
 
 407  
     for (Iterator i = allModules.keySet().iterator(); i.hasNext();) {
 408  
 
 409  
       Module module = (Module) allModules.get((String) i.next());
 410  
 
 411  
       if (isLocal(module)) {
 412  
 
 413  
         // Does the module have 'module'-deps ?
 414  
         //
 415  
 
 416  
         Set moduleDependencies = null;
 417  
         moduleDependencies = ((BaseModule) module).getDependencies();
 418  
 
 419  
         // Iterate over all dependencies. If it is a module dep, check if we already have an
 420  
         // entry in the interdep-collection; create one when necessary.
 421  
         //
 422  
         for (Iterator j = moduleDependencies.iterator(); j.hasNext();) {
 423  
           ModuleDependency moduleDependency = (ModuleDependency) j.next();
 424  
           if (moduleDependency.isModuleDependency()) {
 425  
 
 426  
             // Check if a key for the module dep already exists.
 427  
             //
 428  
             if (interDependencies.containsKey(moduleDependency.getModule())) {
 429  
 
 430  
               // If so, get the corresponding collection and add the module to the collection.
 431  
               //
 432  
               Collection col = (Collection) interDependencies.get(moduleDependency.getModule());
 433  
               col.add(module);
 434  
             } else {
 435  
               // For the dependency, no entry exists, so we create one.
 436  
               //
 437  
               Collection col = new HashSet();
 438  
               col.add(module);
 439  
               // todo TEST (!) if the mechanism works for 'duplicate' keys.
 440  
               interDependencies.put(moduleDependency.getModule(), col);
 441  
             }
 442  
           }
 443  
         }
 444  
       } else {
 445  
         // todo else what ???
 446  
       }
 447  
     }
 448  
 
 449  
     return interDependencies;
 450  
   }
 451  
 
 452  
   /**
 453  
    * <p>Checks is <code>module</code> should be removed locally. This can - e.g. - happen if the module was checked out
 454  
    * from a location elsewhere and the module with the same name but with a different location has been defined in the
 455  
    * manifest, before the module was cleaned locally.
 456  
    *
 457  
    * <p>This method only supports CVS.
 458  
    *
 459  
    * @param module
 460  
    * @return <code>true</code> if a local version has been removed or <code>false</code> if nothing has been removed.
 461  
    */
 462  
   private boolean removeLocal(Module module) {
 463  
 
 464  
     // todo this method is not abstract ! handles CVS only.
 465  
 
 466  
     AdminHandler handler = new AdminHandler(module);
 467  
     if (!handler.isEqualLocation()) {
 468  
       try {
 469  
         FileUtils.deleteDirectory(module.getBaseDir());
 470  
       } catch (IOException e) {
 471  
         return false;
 472  
       }
 473  
     }
 474  
 
 475  
     return true;
 476  
   }
 477  
 
 478  
   private void setModuleBaseDir(Module module) {
 479  
 
 480  
     try {
 481  
       module.setBaseDir(new File(getModuleBaseDirectory(), module.getName()));
 482  
 
 483  
 //      if (((VersionControlSystem)module.getLocation()).getModuleOffset() == null) {
 484  
 //        module.setBaseDir(new File(getModuleBaseDirectory(), module.getName()));
 485  
 //      } else {
 486  
 //        module.setBaseDir(new File(new File(getModuleBaseDirectory(), ((VersionControlSystem)module.getLocation()).getModuleOffset()), module.getName()));
 487  
 //      }
 488  
 //      module.setCheckoutDir(getModuleBaseDirectory());
 489  
     } catch(Exception e) {
 490  
       // Basically, if we can't do this, we have nothing ... really a RuntimeException
 491  
       //
 492  
       throw new KarmaRuntimeException("Could not set base directory for module " + module.getName());
 493  
     }
 494  
   }
 495  
 
 496  
   /**
 497  
    * Sets a modules' state when the module is locally available.
 498  
    *
 499  
    * @param module
 500  
    * @param state
 501  
    */
 502  
   public final void setState(Module module, Module.State state) {
 503  
     if (state == null) {
 504  
       throw new IllegalArgumentException("Parameter state cannot be null.");
 505  
     }
 506  
 
 507  
     if (!isLocal(module)) {
 508  
       return;
 509  
     }
 510  
 
 511  
     try {
 512  
 
 513  
       // Remove old state files ...
 514  
       //
 515  
       FilenameFilter filter = new FilenameFilter() {
 516  
         public boolean accept(File dir, String name) {
 517  
           if ((name != null) && ((".WORKING".equals(name)) || (".STATIC".equals(name)) || (".DYNAMIC".equals(name)))) {
 518  
             return true;
 519  
           } else {
 520  
             return false;
 521  
           }
 522  
         }
 523  
       };
 524  
 
 525  
       String[] stateFiles = module.getBaseDir().list(filter);
 526  
 
 527  
       if (stateFiles != null) {
 528  
         for (int i = 0; i < stateFiles.length; i++) {
 529  
           new File(module.getBaseDir(), stateFiles[i]).delete();
 530  
         }
 531  
       }
 532  
 
 533  
       File stateFile = new File(module.getBaseDir(), state.getHiddenFileName());
 534  
       stateFile.createNewFile();
 535  
 
 536  
     } catch (Exception e) {
 537  
       throw new KarmaRuntimeException(e);
 538  
     }
 539  
   }
 540  
 
 541  
   public final Module.State getState(Module module) {
 542  
 
 543  
     if (!isLocal(module)) {
 544  
       if (module.hasVersion() || this instanceof ReleaseManclass="keyword">ifest) {
 545  
         return Module.STATIC;
 546  
       } else {
 547  
         return Module.DYNAMIC;
 548  
       }
 549  
     }
 550  
 
 551  
     FilenameFilter filter = new FilenameFilter() {
 552  0
       public boolean accept(File dir, String name) {
 553  0
         if ((name != null) && name.matches(".WORKING|.STATIC|.DYNAMIC")) {
 554  0
           return true;
 555  
         } else {
 556  0
           return false;
 557  
         }
 558  
       }
 559  
     };
 560  
 
 561  
     String[] stateFiles = module.getBaseDir().list(filter);
 562  
 
 563  
     if ((stateFiles == null || stateFiles.length == 0)) {
 564  
       if (module.hasVersion()) {
 565  
         return Module.STATIC;
 566  
       } else {
 567  
         return Module.DYNAMIC;
 568  
       }
 569  
     } else {
 570  
 
 571  
       // We have state files, meaning that the module was local.
 572  
       //
 573  
       if (".WORKING".equals(stateFiles[0])) {
 574  
         return Module.WORKING;
 575  
       } else {
 576  
 
 577  
         // The module is not working.
 578  
         //
 579  
         if (this instanceof ReleaseManclass="keyword">ifest) {
 580  
           return Module.STATIC;
 581  
         } else {
 582  
           if (module.hasVersion()) {
 583  
             return Module.STATIC;
 584  
           }
 585  
         }
 586  
         return Module.DYNAMIC;
 587  
       }
 588  
     }
 589  
   }
 590  
 
 591  
   public final String toString() {
 592  
     return getName();
 593  
   }
 594  
 }

This report is generated by jcoverage, Maven and Maven JCoverage Plugin.