1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
107
108
109 copyStructure();
110
111
112
113 applyWorkingContext();
114 }
115
116
117
118
119 private void copyStructure() throws LocationException {
120
121
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
133
134 moduleCache = new HashMap();
135
136
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 (!manifestBaseDirectory.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 (!manifestTempDirectory.exists()) {
218 manifestTempDirectory.mkdir();
219 }
220 return manifestTempDirectory;
221 }
222
223 /***
224 * Gets a manifests' name (the <name>-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 <version>-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><version></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, 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
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
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 Manifest) {
362 if (getName().equals(((Manifest) 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
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
414
415
416 Set moduleDependencies = null;
417 moduleDependencies = ((BaseModule) module).getDependencies();
418
419
420
421
422 for (Iterator j = moduleDependencies.iterator(); j.hasNext();) {
423 ModuleDependency moduleDependency = (ModuleDependency) j.next();
424 if (moduleDependency.isModuleDependency()) {
425
426
427
428 if (interDependencies.containsKey(moduleDependency.getModule())) {
429
430
431
432 Collection col = (Collection) interDependencies.get(moduleDependency.getModule());
433 col.add(module);
434 } else {
435
436
437 Collection col = new HashSet();
438 col.add(module);
439
440 interDependencies.put(moduleDependency.getModule(), col);
441 }
442 }
443 }
444 } else {
445
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
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
484
485
486
487
488
489 } catch(Exception e) {
490
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
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 ReleaseManifest) {
545 return Module.STATIC;
546 } else {
547 return Module.DYNAMIC;
548 }
549 }
550
551 FilenameFilter filter = new FilenameFilter() {
552 public boolean accept(File dir, String name) {
553 if ((name != null) && name.matches(".WORKING|.STATIC|.DYNAMIC")) {
554 return true;
555 } else {
556 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
572
573 if (".WORKING".equals(stateFiles[0])) {
574 return Module.WORKING;
575 } else {
576
577
578
579 if (this instanceof ReleaseManifest) {
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 }