~ubuntu-branches/ubuntu/lucid/libjcommon-java/lucid

« back to all changes in this revision

Viewing changes to source/org/jfree/base/modules/PackageManager.java

  • Committer: Bazaar Package Importer
  • Author(s): Wolfgang Baer
  • Date: 2006-02-09 15:58:13 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20060209155813-fzi9zwh2rzedbllq
Tags: 1.0.0-1
* New stable upstream release (closes: #328574)
* Move to main - build with kaffe
* Use cdbs build system - added cdbs build-dependency
* Move package to pkg-java-maintainers for comaintenance, 
  added Christian Bayle and myself as uploaders
* Removed unneeded README.Debian
* Added README.Debian-source how the upstream tarball was cleaned
* Move big documentation in an own -doc package
* Register javadoc api with doc-base
* Standards-Version 3.6.2 (no changes)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* ========================================================================
 
2
 * JCommon : a free general purpose class library for the Java(tm) platform
 
3
 * ========================================================================
 
4
 *
 
5
 * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
 
6
 * 
 
7
 * Project Info:  http://www.jfree.org/jcommon/index.html
 
8
 *
 
9
 * This library is free software; you can redistribute it and/or modify it 
 
10
 * under the terms of the GNU Lesser General Public License as published by 
 
11
 * the Free Software Foundation; either version 2.1 of the License, or 
 
12
 * (at your option) any later version.
 
13
 *
 
14
 * This library is distributed in the hope that it will be useful, but 
 
15
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 
16
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
 
17
 * License for more details.
 
18
 *
 
19
 * You should have received a copy of the GNU Lesser General Public
 
20
 * License along with this library; if not, write to the Free Software
 
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
 
22
 * USA.  
 
23
 *
 
24
 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
 
25
 * in the United States and other countries.]
 
26
 * 
 
27
 * -------------------
 
28
 * PackageManager.java
 
29
 * -------------------
 
30
 * (C)opyright 2003, 2004, by Thomas Morgner and Contributors.
 
31
 *
 
32
 * Original Author:  Thomas Morgner;
 
33
 * Contributor(s):   David Gilbert (for Object Refinery Limited);
 
34
 *
 
35
 * $Id: PackageManager.java,v 1.8 2005/11/03 09:55:27 mungady Exp $
 
36
 *
 
37
 * Changes
 
38
 * -------
 
39
 * 26-Jun-2003 : Initial version
 
40
 * 07-Jun-2004 : Added JCommon header (DG);
 
41
 * 
 
42
 */
 
43
 
 
44
package org.jfree.base.modules;
 
45
 
 
46
import java.io.PrintStream;
 
47
import java.util.ArrayList;
 
48
import java.util.Arrays;
 
49
import java.util.HashMap;
 
50
import java.util.Iterator;
 
51
 
 
52
import org.jfree.base.AbstractBoot;
 
53
import org.jfree.base.config.HierarchicalConfiguration;
 
54
import org.jfree.base.config.PropertyFileConfiguration;
 
55
import org.jfree.base.log.PadMessage;
 
56
import org.jfree.util.Configuration;
 
57
import org.jfree.util.Log;
 
58
import org.jfree.util.ObjectUtilities;
 
59
 
 
60
/**
 
61
 * The PackageManager is used to load and configure the modules of JFreeReport.
 
62
 * Modules are used to extend the basic capabilities of JFreeReport by providing
 
63
 * a simple plugin-interface.
 
64
 * <p/>
 
65
 * Modules provide a simple capability to remove unneeded functionality from the
 
66
 * JFreeReport system and to reduce the overall code size. The modularisation provides
 
67
 * a very strict way of removing unnecessary dependencies beween the various packages.
 
68
 * <p/>
 
69
 * The package manager can be used to add new modules to the system or to check
 
70
 * the existence and state of installed modules.
 
71
 *
 
72
 * @author Thomas Morgner
 
73
 */
 
74
public final class PackageManager {
 
75
    /**
 
76
     * The PackageConfiguration handles the module level configuration.
 
77
     *
 
78
     * @author Thomas Morgner
 
79
     */
 
80
    public static class PackageConfiguration extends PropertyFileConfiguration {
 
81
        /**
 
82
         * DefaultConstructor. Creates a new package configuration.
 
83
         */
 
84
        public PackageConfiguration() {
 
85
            // nothing required
 
86
        }
 
87
 
 
88
        /**
 
89
         * The new configuartion will be inserted into the list of report configuration,
 
90
         * so that this configuration has the given report configuration instance as parent.
 
91
         *
 
92
         * @param config the new report configuration.
 
93
         */
 
94
        public void insertConfiguration(final HierarchicalConfiguration config) {
 
95
            super.insertConfiguration(config);
 
96
        }
 
97
    }
 
98
 
 
99
 
 
100
    /**
 
101
     * An internal constant declaring that the specified module was already loaded.
 
102
     */
 
103
    private static final int RETURN_MODULE_LOADED = 0;
 
104
    /**
 
105
     * An internal constant declaring that the specified module is not known.
 
106
     */
 
107
    private static final int RETURN_MODULE_UNKNOWN = 1;
 
108
    /**
 
109
     * An internal constant declaring that the specified module produced an error while loading.
 
110
     */
 
111
    private static final int RETURN_MODULE_ERROR = 2;
 
112
 
 
113
 
 
114
    /**
 
115
     * The module configuration instance that should be used to store module
 
116
     * properties. This separates the user defined properties from the implementation
 
117
     * defined properties.
 
118
     */
 
119
    private final PackageConfiguration packageConfiguration;
 
120
 
 
121
    /**
 
122
     * A list of all defined modules.
 
123
     */
 
124
    private final ArrayList modules;
 
125
    /**
 
126
     * A list of module name definitions.
 
127
     */
 
128
    private final ArrayList initSections;
 
129
 
 
130
    /** The boot implementation for which the modules are managed. */
 
131
    private AbstractBoot booter;
 
132
 
 
133
    /** The instances of all modules for all booters. */
 
134
    private static HashMap instances;
 
135
 
 
136
    /**
 
137
     * Creates a package manager instance.
 
138
     *
 
139
     * @param booter the booter.
 
140
     * @return A package manager.
 
141
     */
 
142
    public static PackageManager createInstance(final AbstractBoot booter) {
 
143
        PackageManager manager;
 
144
        if (instances == null) {
 
145
            instances = new HashMap();
 
146
            manager = new PackageManager(booter);
 
147
            instances.put(booter, manager);
 
148
            return manager;
 
149
        }
 
150
        manager = (PackageManager) instances.get(booter);
 
151
        if (manager == null) {
 
152
            manager = new PackageManager(booter);
 
153
            instances.put(booter, manager);
 
154
        }
 
155
        return manager;
 
156
    }
 
157
 
 
158
    /**
 
159
     * Creates a new package manager.
 
160
     *
 
161
     * @param booter the booter (<code>null</code> not permitted).
 
162
     */
 
163
    private PackageManager(final AbstractBoot booter) {
 
164
        if (booter == null) {
 
165
            throw new NullPointerException();
 
166
        }
 
167
        this.booter = booter;
 
168
        this.packageConfiguration = new PackageConfiguration();
 
169
        this.modules = new ArrayList();
 
170
        this.initSections = new ArrayList();
 
171
    }
 
172
 
 
173
    /**
 
174
     * Checks, whether a certain module is available.
 
175
     *
 
176
     * @param moduleDescription the module description of the desired module.
 
177
     * @return true, if the module is available and the version of the module
 
178
     *         is compatible, false otherwise.
 
179
     */
 
180
    public boolean isModuleAvailable(final ModuleInfo moduleDescription) {
 
181
        final PackageState[] packageStates =
 
182
            (PackageState[]) this.modules.toArray(new PackageState[this.modules.size()]);
 
183
        for (int i = 0; i < packageStates.length; i++) {
 
184
            final PackageState state = packageStates[i];
 
185
            if (state.getModule().getModuleClass().equals(moduleDescription.getModuleClass())) {
 
186
                return (state.getState() == PackageState.STATE_INITIALIZED);
 
187
            }
 
188
        }
 
189
        return false;
 
190
    }
 
191
 
 
192
    /**
 
193
     * Loads all modules mentioned in the report configuration starting with
 
194
     * the given prefix. This method is used during the boot process of
 
195
     * JFreeReport. You should never need to call this method directly.
 
196
     *
 
197
     * @param modulePrefix the module prefix.
 
198
     */
 
199
    public void load(final String modulePrefix) {
 
200
        if (this.initSections.contains(modulePrefix)) {
 
201
            return;
 
202
        }
 
203
        this.initSections.add(modulePrefix);
 
204
 
 
205
        final Configuration config = this.booter.getGlobalConfig();
 
206
        final Iterator it = config.findPropertyKeys(modulePrefix);
 
207
        while (it.hasNext()) {
 
208
            final String key = (String) it.next();
 
209
            if (key.endsWith(".Module")) {
 
210
                addModule(config.getConfigProperty(key));
 
211
            }
 
212
        }
 
213
        Log.debug("Loaded a total of " + this.modules.size() + " modules under prefix: " + modulePrefix);
 
214
    }
 
215
 
 
216
    /**
 
217
     * Initializes all previously uninitialized modules. Once a module is initialized,
 
218
     * it is not re-initialized a second time.
 
219
     */
 
220
    public synchronized void initializeModules() {
 
221
        // sort by subsystems and dependency
 
222
        PackageSorter.sort(this.modules);
 
223
 
 
224
        for (int i = 0; i < this.modules.size(); i++) {
 
225
            final PackageState mod = (PackageState) this.modules.get(i);
 
226
            if (mod.configure(this.booter)) {
 
227
                Log.debug(new Log.SimpleMessage("Conf: ",
 
228
                    new PadMessage(mod.getModule().getModuleClass(), 70),
 
229
                    " [", mod.getModule().getSubSystem(), "]"));
 
230
            }
 
231
        }
 
232
 
 
233
        for (int i = 0; i < this.modules.size(); i++) {
 
234
            final PackageState mod = (PackageState) this.modules.get(i);
 
235
            if (mod.initialize(this.booter)) {
 
236
                Log.debug(new Log.SimpleMessage("Init: ",
 
237
                    new PadMessage(mod.getModule().getModuleClass(), 70),
 
238
                    " [", mod.getModule().getSubSystem(), "]"));
 
239
            }
 
240
        }
 
241
    }
 
242
 
 
243
    /**
 
244
     * Adds a module to the package manager.
 
245
     * Once all modules are added, you have to call initializeModules()
 
246
     * to configure and initialize the new modules.
 
247
     *
 
248
     * @param modClass the module class
 
249
     */
 
250
    public synchronized void addModule(final String modClass) {
 
251
        final ArrayList loadModules = new ArrayList();
 
252
        final ModuleInfo modInfo = new DefaultModuleInfo
 
253
            (modClass, null, null, null);
 
254
        if (loadModule(modInfo, new ArrayList(), loadModules, false)) {
 
255
            for (int i = 0; i < loadModules.size(); i++) {
 
256
                final Module mod = (Module) loadModules.get(i);
 
257
                this.modules.add(new PackageState(mod));
 
258
            }
 
259
        }
 
260
    }
 
261
 
 
262
    /**
 
263
     * Checks, whether the given module is already loaded in either the given
 
264
     * tempModules list or the global package registry. If tmpModules is null,
 
265
     * only the previously installed modules are checked.
 
266
     *
 
267
     * @param tempModules a list of previously loaded modules.
 
268
     * @param module      the module specification that is checked.
 
269
     * @return true, if the module is already loaded, false otherwise.
 
270
     */
 
271
    private int containsModule(final ArrayList tempModules, final ModuleInfo module) {
 
272
        if (tempModules != null) {
 
273
            final ModuleInfo[] mods = (ModuleInfo[])
 
274
                tempModules.toArray(new ModuleInfo[tempModules.size()]);
 
275
            for (int i = 0; i < mods.length; i++) {
 
276
                if (mods[i].getModuleClass().equals(module.getModuleClass())) {
 
277
                    return RETURN_MODULE_LOADED;
 
278
                }
 
279
            }
 
280
        }
 
281
 
 
282
        final PackageState[] packageStates =
 
283
            (PackageState[]) this.modules.toArray(new PackageState[this.modules.size()]);
 
284
        for (int i = 0; i < packageStates.length; i++) {
 
285
            if (packageStates[i].getModule().getModuleClass().equals(module.getModuleClass())) {
 
286
                if (packageStates[i].getState() == PackageState.STATE_ERROR) {
 
287
                    return RETURN_MODULE_ERROR;
 
288
                }
 
289
                else {
 
290
                    return RETURN_MODULE_LOADED;
 
291
                }
 
292
            }
 
293
        }
 
294
        return RETURN_MODULE_UNKNOWN;
 
295
    }
 
296
 
 
297
    /**
 
298
     * A utility method that collects all failed modules. Such an module caused
 
299
     * an error while being loaded, and is now cached in case it is referenced
 
300
     * elsewhere.
 
301
     *
 
302
     * @param state the failed module.
 
303
     */
 
304
    private void dropFailedModule(final PackageState state) {
 
305
        if (this.modules.contains(state) == false) {
 
306
            this.modules.add(state);
 
307
        }
 
308
    }
 
309
 
 
310
    /**
 
311
     * Tries to load a given module and all dependent modules. If the dependency check
 
312
     * fails for that module (or for one of the dependent modules), the loaded modules
 
313
     * are discarded and no action is taken.
 
314
     *
 
315
     * @param moduleInfo        the module info of the module that should be loaded.
 
316
     * @param incompleteModules a list of incompletly loaded modules. This are module
 
317
     *                          specifications which depend on the current module and wait for the module to
 
318
     *                          be completly loaded.
 
319
     * @param modules           the list of previously loaded modules for this module.
 
320
     * @param fatal             a flag that states, whether the failure of loading a module should
 
321
     *                          be considered an error. Root-modules load errors are never fatal, as we try
 
322
     *                          to load all known modules, regardless whether they are active or not.
 
323
     * @return true, if the module was loaded successfully, false otherwise.
 
324
     */
 
325
    private boolean loadModule(final ModuleInfo moduleInfo, final ArrayList incompleteModules,
 
326
                               final ArrayList modules, final boolean fatal) {
 
327
        try {
 
328
 
 
329
            final Class c = ObjectUtilities.getClassLoader(getClass()).loadClass(moduleInfo.getModuleClass());
 
330
            final Module module = (Module) c.newInstance();
 
331
 
 
332
            if (acceptVersion(moduleInfo, module) == false) {
 
333
                // module conflict!
 
334
                Log.warn("Module " + module.getName() + ": required version: " 
 
335
                        + moduleInfo + ", but found Version: \n" + module);
 
336
                final PackageState state = new PackageState(module, PackageState.STATE_ERROR);
 
337
                dropFailedModule(state);
 
338
                return false;
 
339
            }
 
340
 
 
341
            final int moduleContained = containsModule(modules, module);
 
342
            if (moduleContained == RETURN_MODULE_ERROR) {
 
343
                // the module caused harm before ...
 
344
                Log.debug("Indicated failure for module: " + module.getModuleClass());
 
345
                final PackageState state = new PackageState(module, PackageState.STATE_ERROR);
 
346
                dropFailedModule(state);
 
347
                return false;
 
348
            }
 
349
            else if (moduleContained == RETURN_MODULE_UNKNOWN) {
 
350
                if (incompleteModules.contains(module)) {
 
351
                    // we assume that loading will continue ...
 
352
                    Log.error(new Log.SimpleMessage
 
353
                        ("Circular module reference: This module definition is invalid: ",
 
354
                            module.getClass()));
 
355
                    final PackageState state = new PackageState(module, PackageState.STATE_ERROR);
 
356
                    dropFailedModule(state);
 
357
                    return false;
 
358
                }
 
359
                incompleteModules.add(module);
 
360
                final ModuleInfo[] required = module.getRequiredModules();
 
361
                for (int i = 0; i < required.length; i++) {
 
362
                    if (loadModule(required[i], incompleteModules, modules, true) == false) {
 
363
                        Log.debug("Indicated failure for module: " + module.getModuleClass());
 
364
                        final PackageState state = new PackageState(module, PackageState.STATE_ERROR);
 
365
                        dropFailedModule(state);
 
366
                        return false;
 
367
                    }
 
368
                }
 
369
 
 
370
                final ModuleInfo[] optional = module.getOptionalModules();
 
371
                for (int i = 0; i < optional.length; i++) {
 
372
                    if (loadModule(optional[i], incompleteModules, modules, true) == false) {
 
373
                        Log.debug(new Log.SimpleMessage("Optional module: ",
 
374
                            optional[i].getModuleClass(), " was not loaded."));
 
375
                    }
 
376
                }
 
377
                // maybe a dependent module defined the same base module ...
 
378
                if (containsModule(modules, module) == RETURN_MODULE_UNKNOWN) {
 
379
                    modules.add(module);
 
380
                }
 
381
                incompleteModules.remove(module);
 
382
            }
 
383
            return true;
 
384
        }
 
385
        catch (ClassNotFoundException cnfe) {
 
386
            if (fatal) {
 
387
                Log.warn(new Log.SimpleMessage
 
388
                    ("Unresolved dependency for package: ", moduleInfo.getModuleClass()));
 
389
            }
 
390
            Log.debug(new Log.SimpleMessage("ClassNotFound: ", cnfe.getMessage()));
 
391
            return false;
 
392
        }
 
393
        catch (Exception e) {
 
394
            Log.warn(new Log.SimpleMessage("Exception while loading module: ", moduleInfo), e);
 
395
            return false;
 
396
        }
 
397
    }
 
398
 
 
399
    /**
 
400
     * Checks, whether the given module meets the requirements defined in the module
 
401
     * information.
 
402
     *
 
403
     * @param moduleRequirement the required module specification.
 
404
     * @param module            the module that should be checked against the specification.
 
405
     * @return true, if the module meets the given specifications, false otherwise.
 
406
     */
 
407
    private boolean acceptVersion(final ModuleInfo moduleRequirement, final Module module) {
 
408
        if (moduleRequirement.getMajorVersion() == null) {
 
409
            return true;
 
410
        }
 
411
        if (module.getMajorVersion() == null) {
 
412
            Log.warn("Module " + module.getName() + " does not define a major version.");
 
413
        }
 
414
        else {
 
415
            final int compare = acceptVersion(moduleRequirement.getMajorVersion(),
 
416
                module.getMajorVersion());
 
417
            if (compare > 0) {
 
418
                return false;
 
419
            }
 
420
            else if (compare < 0) {
 
421
                return true;
 
422
            }
 
423
        }
 
424
 
 
425
        if (moduleRequirement.getMinorVersion() == null) {
 
426
            return true;
 
427
        }
 
428
        if (module.getMinorVersion() == null) {
 
429
            Log.warn("Module " + module.getName() + " does not define a minor version.");
 
430
        }
 
431
        else {
 
432
            final int compare = acceptVersion(moduleRequirement.getMinorVersion(),
 
433
                module.getMinorVersion());
 
434
            if (compare > 0) {
 
435
                return false;
 
436
            }
 
437
            else if (compare < 0) {
 
438
                return true;
 
439
            }
 
440
        }
 
441
 
 
442
        if (moduleRequirement.getPatchLevel() == null) {
 
443
            return true;
 
444
        }
 
445
        if (module.getPatchLevel() == null) {
 
446
            Log.debug("Module " + module.getName() + " does not define a patch level.");
 
447
        }
 
448
        else {
 
449
            if (acceptVersion(moduleRequirement.getPatchLevel(),
 
450
                module.getPatchLevel()) > 0) {
 
451
                Log.debug("Did not accept patchlevel: " 
 
452
                        + moduleRequirement.getPatchLevel() + " - " 
 
453
                        + module.getPatchLevel());
 
454
                return false;
 
455
            }
 
456
        }
 
457
        return true;
 
458
 
 
459
    }
 
460
 
 
461
    /**
 
462
     * Compare the version strings. If the strings have a different length,
 
463
     * the shorter string is padded with spaces to make them compareable.
 
464
     *
 
465
     * @param modVer    the version string of the module
 
466
     * @param depModVer the version string of the dependent or optional module
 
467
     * @return 0, if the dependent module version is equal tothe module's required
 
468
     *         version, a negative number if the dependent module is newer or a positive
 
469
     *         number if the dependent module is older and does not fit.
 
470
     */
 
471
    private int acceptVersion(final String modVer, final String depModVer) {
 
472
        final int mLength = Math.max(modVer.length(), depModVer.length());
 
473
        final char[] modVerArray;
 
474
        final char[] depVerArray;
 
475
        if (modVer.length() > depModVer.length()) {
 
476
            modVerArray = modVer.toCharArray();
 
477
            depVerArray = new char[mLength];
 
478
            final int delta = modVer.length() - depModVer.length();
 
479
            Arrays.fill(depVerArray, 0, delta, ' ');
 
480
            System.arraycopy(depVerArray, delta, depModVer.toCharArray(), 0, depModVer.length());
 
481
        }
 
482
        else if (modVer.length() < depModVer.length()) {
 
483
            depVerArray = depModVer.toCharArray();
 
484
            modVerArray = new char[mLength];
 
485
            final char[] b1 = new char[mLength];
 
486
            final int delta = depModVer.length() - modVer.length();
 
487
            Arrays.fill(b1, 0, delta, ' ');
 
488
            System.arraycopy(b1, delta, modVer.toCharArray(), 0, modVer.length());
 
489
        }
 
490
        else {
 
491
            depVerArray = depModVer.toCharArray();
 
492
            modVerArray = modVer.toCharArray();
 
493
        }
 
494
        return new String(modVerArray).compareTo(new String(depVerArray));
 
495
    }
 
496
 
 
497
    /**
 
498
     * Returns the default package configuration. Private report configuration
 
499
     * instances may be inserted here. These inserted configuration can never override
 
500
     * the settings from this package configuration.
 
501
     *
 
502
     * @return the package configuration.
 
503
     */
 
504
    public PackageConfiguration getPackageConfiguration() {
 
505
        return this.packageConfiguration;
 
506
    }
 
507
 
 
508
    /**
 
509
     * Returns an array of the currently active modules. The module definition
 
510
     * returned contain all known modules, including buggy and unconfigured
 
511
     * instances.
 
512
     *
 
513
     * @return the modules.
 
514
     */
 
515
    public Module[] getAllModules() {
 
516
        final Module[] mods = new Module[this.modules.size()];
 
517
        for (int i = 0; i < this.modules.size(); i++) {
 
518
            final PackageState state = (PackageState) this.modules.get(i);
 
519
            mods[i] = state.getModule();
 
520
        }
 
521
        return mods;
 
522
    }
 
523
 
 
524
    /**
 
525
     * Returns all active modules. This array does only contain modules
 
526
     * which were successfully configured and initialized.
 
527
     *
 
528
     * @return the list of all active modules.
 
529
     */
 
530
    public Module[] getActiveModules() {
 
531
        final ArrayList mods = new ArrayList();
 
532
        for (int i = 0; i < this.modules.size(); i++) {
 
533
            final PackageState state = (PackageState) this.modules.get(i);
 
534
            if (state.getState() == PackageState.STATE_INITIALIZED) {
 
535
                mods.add(state.getModule());
 
536
            }
 
537
        }
 
538
        return (Module[]) mods.toArray(new Module[mods.size()]);
 
539
    }
 
540
 
 
541
    /**
 
542
     * Prints the modules that are used.
 
543
     *
 
544
     * @param p the print stream.
 
545
     */
 
546
    public void printUsedModules(final PrintStream p) {
 
547
        final Module[] allMods = getAllModules();
 
548
        final ArrayList activeModules = new ArrayList();
 
549
        final ArrayList failedModules = new ArrayList();
 
550
 
 
551
        for (int i = 0; i < allMods.length; i++) {
 
552
            if (isModuleAvailable(allMods[i])) {
 
553
                activeModules.add(allMods[i]);
 
554
            }
 
555
            else {
 
556
                failedModules.add(allMods[i]);
 
557
            }
 
558
        }
 
559
 
 
560
        p.print("Active modules: ");
 
561
        p.println(activeModules.size());
 
562
        p.println("----------------------------------------------------------");
 
563
        for (int i = 0; i < activeModules.size(); i++) {
 
564
            final Module mod = (Module) activeModules.get(i);
 
565
            p.print(new PadMessage(mod.getModuleClass(), 70));
 
566
            p.print(" [");
 
567
            p.print(mod.getSubSystem());
 
568
            p.println("]");
 
569
            p.print("  Version: ");
 
570
            p.print(mod.getMajorVersion());
 
571
            p.print("-");
 
572
            p.print(mod.getMinorVersion());
 
573
            p.print("-");
 
574
            p.print(mod.getPatchLevel());
 
575
            p.print(" Producer: ");
 
576
            p.println(mod.getProducer());
 
577
            p.print("  Description: ");
 
578
            p.println(mod.getDescription());
 
579
        }
 
580
    }
 
581
}