001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans;
043:
044: import java.io.File;
045: import java.io.IOException;
046: import java.util.ArrayList;
047: import java.util.Arrays;
048: import java.util.Collections;
049: import java.util.HashSet;
050: import java.util.List;
051: import java.util.Set;
052: import java.util.StringTokenizer;
053: import java.util.jar.Attributes;
054: import java.util.jar.Manifest;
055: import java.util.logging.Level;
056: import org.openide.modules.Dependency;
057: import org.openide.modules.ModuleInfo;
058: import org.openide.modules.SpecificationVersion;
059: import org.openide.util.Exceptions;
060: import org.openide.util.NbBundle;
061: import org.openide.util.Union2;
062:
063: /** Object representing one module, possibly installed.
064: * Responsible for opening of module JAR file; reading
065: * manifest; parsing basic information such as dependencies;
066: * and creating a classloader for use by the installer.
067: * Methods not defined in ModuleInfo must be called from within
068: * the module manager's read mutex as a rule.
069: * @author Jesse Glick
070: * @since 2.1 the class was made public abstract
071: */
072: public abstract class Module extends ModuleInfo {
073:
074: public static final String PROP_RELOADABLE = "reloadable"; // NOI18N
075: public static final String PROP_CLASS_LOADER = "classLoader"; // NOI18N
076: public static final String PROP_MANIFEST = "manifest"; // NOI18N
077: public static final String PROP_VALID = "valid"; // NOI18N
078: public static final String PROP_PROBLEMS = "problems"; // NOI18N
079:
080: /** manager which owns this module */
081: protected final ModuleManager mgr;
082: /** event logging (should not be much here) */
083: protected final Events events;
084: /** associated history object
085: * @see ModuleHistory
086: */
087: private final Object history;
088: /** true if currently enabled; manipulated by ModuleManager */
089: private boolean enabled;
090: /** whether it is supposed to be automatically loaded when required */
091: private final boolean autoload;
092: /** */
093: protected boolean reloadable;
094: /** if true, this module is eagerly turned on whenever it can be */
095: private final boolean eager;
096: /** code name base (no slash) */
097: private String codeNameBase;
098: /** code name release, or -1 if undefined */
099: private int codeNameRelease;
100: /** full code name */
101: private String codeName;
102: /** provided tokens */
103: private String[] provides;
104: /** set of dependencies parsed from manifest */
105: private Dependency[] dependenciesA;
106: /** specification version parsed from manifest, or null */
107: private SpecificationVersion specVers;
108: /** currently active module classloader */
109: protected ClassLoader classloader = null;
110: /** public packages, may be null */
111: private PackageExport[] publicPackages;
112: /** Set<String> of CNBs of friend modules or null */
113: private Set/*<String>*/friendNames;
114:
115: /** Use ModuleManager.create as a factory. */
116: protected Module(ModuleManager mgr, Events ev, Object history,
117: boolean reloadable, boolean autoload, boolean eager)
118: throws IOException {
119: if (autoload && eager)
120: throw new IllegalArgumentException(
121: "A module may not be both autoload and eager"); // NOI18N
122: this .mgr = mgr;
123: this .events = ev;
124: this .history = history;
125: this .reloadable = reloadable;
126: this .autoload = autoload;
127: this .eager = eager;
128: enabled = false;
129: }
130:
131: /** Create a special-purpose "fixed" JAR. */
132: protected Module(ModuleManager mgr, Events ev, Object history,
133: ClassLoader classloader) throws InvalidException {
134: this (mgr, ev, history, classloader, false, false);
135: }
136:
137: /**
138: * Create a special-purpose "fixed" JAR which may nonetheless be marked eager or autoload.
139: * @since 2.7
140: */
141: protected Module(ModuleManager mgr, Events ev, Object history,
142: ClassLoader classloader, boolean autoload, boolean eager)
143: throws InvalidException {
144: if (autoload && eager)
145: throw new IllegalArgumentException(
146: "A module may not be both autoload and eager"); // NOI18N
147: this .mgr = mgr;
148: this .events = ev;
149: this .history = history;
150: this .classloader = classloader;
151: reloadable = false;
152: this .autoload = autoload;
153: this .eager = eager;
154: enabled = false;
155: }
156:
157: /** Get the associated module manager. */
158: public ModuleManager getManager() {
159: return mgr;
160: }
161:
162: public boolean isEnabled() {
163: return enabled;
164: }
165:
166: // Access from ModuleManager:
167: void setEnabled(boolean enabled) {
168: /* #13647: actually can happen if loading of bootstrap modules is rolled back: */
169: if (isFixed() && !enabled)
170: throw new IllegalStateException(
171: "Cannot disable a fixed module: " + this ); // NOI18N
172: this .enabled = enabled;
173: }
174:
175: /** Normally a module once created and managed is valid
176: * (that is, either installed or not, but at least managed).
177: * If it is deleted any remaining references to it become
178: * invalid.
179: */
180: public boolean isValid() {
181: return mgr.get(getCodeNameBase()) == this ;
182: }
183:
184: /** Is this module automatically loaded?
185: * If so, no information about its state is kept
186: * permanently beyond the existence of its JAR file;
187: * it is enabled when some real module needs it to be,
188: * and disabled when this is no longer the case.
189: * @see <a href="http://www.netbeans.org/issues/show_bug.cgi?id=9779">#9779</a>
190: */
191: public boolean isAutoload() {
192: return autoload;
193: }
194:
195: /** Is this module eagerly enabled?
196: * If so, no information about its state is kept permanently.
197: * It is turned on whenever it can be, i.e. whenever it meets all of
198: * its dependencies. This may be used to implement "bridge" modules with
199: * simple functionality that just depend on two normal modules.
200: * A module may not be simultaneously eager and autoload.
201: * @see <a href="http://www.netbeans.org/issues/show_bug.cgi?id=17501">#17501</a>
202: * @since org.netbeans.core/1 1.3
203: */
204: public boolean isEager() {
205: return eager;
206: }
207:
208: /** Get an associated arbitrary attribute.
209: * Right now, simply provides the main attributes of the manifest.
210: * In the future some of these could be suppressed (if only of dangerous
211: * interest, e.g. Class-Path) or enhanced with other information available
212: * from the core (if needed).
213: */
214: public Object getAttribute(String attr) {
215: return getManifest().getMainAttributes().getValue(attr);
216: }
217:
218: public String getCodeName() {
219: return codeName;
220: }
221:
222: public String getCodeNameBase() {
223: return codeNameBase;
224: }
225:
226: public int getCodeNameRelease() {
227: return codeNameRelease;
228: }
229:
230: public String[] getProvides() {
231: return provides;
232: }
233:
234: /** Test whether the module provides a given token or not.
235: * @since JST-PENDING again used from NbProblemDisplayer
236: */
237: public final boolean provides(String token) {
238: for (int i = 0; i < provides.length; i++) {
239: if (provides[i].equals(token)) {
240: return true;
241: }
242: }
243: return false;
244: }
245:
246: public Set<Dependency> getDependencies() {
247: return new HashSet<Dependency>(Arrays.asList(dependenciesA));
248: }
249:
250: // Faster to loop over:
251: // @since JST-PENDING called from NbInstaller
252: public final Dependency[] getDependenciesArray() {
253: return dependenciesA;
254: }
255:
256: public SpecificationVersion getSpecificationVersion() {
257: return specVers;
258: }
259:
260: public boolean owns(Class clazz) {
261: ClassLoader cl = clazz.getClassLoader();
262: if (cl instanceof Util.ModuleProvider) {
263: return ((Util.ModuleProvider) cl).getModule() == this ;
264: }
265: return false;
266:
267: }
268:
269: /** Get all packages exported by this module to other modules.
270: * @return a list (possibly empty) of exported packages, or null to export everything
271: * @since org.netbeans.core/1 > 1.4
272: * @see "#19621"
273: */
274: public PackageExport[] getPublicPackages() {
275: return publicPackages;
276: }
277:
278: /** Checks whether we use friends attribute and if so, then
279: * whether the name of module is listed there.
280: */
281: boolean isDeclaredAsFriend(Module module) {
282: if (friendNames == null) {
283: return true;
284: }
285: return friendNames.contains(module.getCodeNameBase());
286: }
287:
288: /** Parse information from the current manifest.
289: * Includes code name, specification version, and dependencies.
290: * If anything is in an invalid format, throws an exception with
291: * some kind of description of the problem.
292: */
293: protected void parseManifest() throws InvalidException {
294: Attributes attr = getManifest().getMainAttributes();
295: // Code name
296: codeName = attr.getValue("OpenIDE-Module"); // NOI18N
297: if (codeName == null) {
298: InvalidException e = new InvalidException(
299: "Not a module: no OpenIDE-Module tag in manifest of "
300: + /* #17629: important! */this ); // NOI18N
301: // #29393: plausible user mistake, deal with it politely.
302: Exceptions.attachLocalizedMessage(e, NbBundle.getMessage(
303: Module.class, "EXC_not_a_module", this .toString()));
304: throw e;
305: }
306: try {
307: // This has the side effect of checking syntax:
308: if (codeName.indexOf(',') != -1) {
309: throw new InvalidException(
310: "Illegal code name syntax parsing OpenIDE-Module: "
311: + codeName); // NOI18N
312: }
313: Dependency.create(Dependency.TYPE_MODULE, codeName);
314: Object[] cnParse = Util.parseCodeName(codeName);
315: codeNameBase = (String) cnParse[0];
316: codeNameRelease = (cnParse[1] != null) ? ((Integer) cnParse[1])
317: .intValue()
318: : -1;
319: if (cnParse[2] != null)
320: throw new NumberFormatException(codeName);
321: // Spec vers
322: String specVersS = attr
323: .getValue("OpenIDE-Module-Specification-Version"); // NOI18N
324: if (specVersS != null) {
325: try {
326: specVers = new SpecificationVersion(specVersS);
327: } catch (NumberFormatException nfe) {
328: throw (InvalidException) new InvalidException(
329: "While parsing OpenIDE-Module-Specification-Version: "
330: + nfe.toString()).initCause(nfe); // NOI18N
331: }
332: } else {
333: specVers = null;
334: }
335: // Token provides
336: String providesS = attr.getValue("OpenIDE-Module-Provides"); // NOI18N
337: if (providesS == null) {
338: provides = new String[] {};
339: } else {
340: StringTokenizer tok = new StringTokenizer(providesS,
341: ", "); // NOI18N
342: provides = new String[tok.countTokens()];
343: for (int i = 0; i < provides.length; i++) {
344: String provide = tok.nextToken();
345: if (provide.indexOf(',') != -1) {
346: throw new InvalidException(
347: "Illegal code name syntax parsing OpenIDE-Module-Provides: "
348: + provide); // NOI18N
349: }
350: Dependency.create(Dependency.TYPE_MODULE, provide);
351: if (provide.lastIndexOf('/') != -1)
352: throw new IllegalArgumentException(
353: "Illegal OpenIDE-Module-Provides: "
354: + provide); // NOI18N
355: provides[i] = provide;
356: }
357: if (new HashSet<String>(Arrays.asList(provides)).size() < provides.length) {
358: throw new IllegalArgumentException(
359: "Duplicate entries in OpenIDE-Module-Provides: "
360: + providesS); // NOI18N
361: }
362: }
363: String[] additionalProvides = mgr.refineProvides(this );
364: if (additionalProvides != null) {
365: if (provides == null) {
366: provides = additionalProvides;
367: } else {
368: ArrayList<String> l = new ArrayList<String>();
369: l.addAll(Arrays.asList(provides));
370: l.addAll(Arrays.asList(additionalProvides));
371: provides = l.toArray(provides);
372: }
373: }
374:
375: // Exports
376: String exportsS = attr
377: .getValue("OpenIDE-Module-Public-Packages"); // NOI18N
378: if (exportsS != null) {
379: if (exportsS.trim().equals("-")) { // NOI18N
380: publicPackages = new PackageExport[0];
381: } else {
382: StringTokenizer tok = new StringTokenizer(exportsS,
383: ", "); // NOI18N
384: List<PackageExport> exports = new ArrayList<PackageExport>(
385: Math.max(tok.countTokens(), 1));
386: while (tok.hasMoreTokens()) {
387: String piece = tok.nextToken();
388: if (piece.endsWith(".*")) { // NOI18N
389: String pkg = piece.substring(0, piece
390: .length() - 2);
391: Dependency.create(Dependency.TYPE_MODULE,
392: pkg);
393: if (pkg.lastIndexOf('/') != -1)
394: throw new IllegalArgumentException(
395: "Illegal OpenIDE-Module-Public-Packages: "
396: + exportsS); // NOI18N
397: exports.add(new PackageExport(pkg.replace(
398: '.', '/') + '/', false));
399: } else if (piece.endsWith(".**")) { // NOI18N
400: String pkg = piece.substring(0, piece
401: .length() - 3);
402: Dependency.create(Dependency.TYPE_MODULE,
403: pkg);
404: if (pkg.lastIndexOf('/') != -1)
405: throw new IllegalArgumentException(
406: "Illegal OpenIDE-Module-Public-Packages: "
407: + exportsS); // NOI18N
408: exports.add(new PackageExport(pkg.replace(
409: '.', '/') + '/', true));
410: } else {
411: throw new IllegalArgumentException(
412: "Illegal OpenIDE-Module-Public-Packages: "
413: + exportsS); // NOI18N
414: }
415: }
416: if (exports.isEmpty())
417: throw new IllegalArgumentException(
418: "Illegal OpenIDE-Module-Public-Packages: "
419: + exportsS); // NOI18N
420: publicPackages = exports
421: .toArray(new PackageExport[exports.size()]);
422: }
423: } else {
424: // XXX new link?
425: Util.err
426: .warning("module "
427: + codeNameBase
428: + " does not declare OpenIDE-Module-Public-Packages in its manifest, so all packages are considered public by default: http://www.netbeans.org/download/dev/javadoc/OpenAPIs/org/openide/doc-files/upgrade.html#3.4-public-packages");
429: publicPackages = null;
430: }
431:
432: {
433: // friends
434: String friends = attr
435: .getValue("OpenIDE-Module-Friends"); // NOI18N
436: if (friends != null) {
437: StringTokenizer tok = new StringTokenizer(friends,
438: ", "); // NOI18N
439: HashSet<String> set = new HashSet<String>();
440: while (tok.hasMoreTokens()) {
441: String piece = tok.nextToken();
442: if (piece.indexOf('/') != -1) {
443: throw new IllegalArgumentException(
444: "May specify only module code name bases in OpenIDE-Module-Friends, not major release versions: "
445: + piece); // NOI18N
446: }
447: // Indirect way of checking syntax:
448: Dependency
449: .create(Dependency.TYPE_MODULE, piece);
450: // OK, add it.
451: set.add(piece);
452: }
453: if (set.isEmpty()) {
454: throw new IllegalArgumentException(
455: "Empty OpenIDE-Module-Friends: "
456: + friends); // NOI18N
457: }
458: if (publicPackages == null
459: || publicPackages.length == 0) {
460: throw new IllegalArgumentException(
461: "No use specifying OpenIDE-Module-Friends without any public packages: "
462: + friends); // NOI18N
463: }
464: this .friendNames = set;
465: }
466: }
467:
468: // Dependencies
469: Set<Dependency> dependencies = new HashSet<Dependency>(20);
470: // First convert IDE/1 -> org.openide/1, so we never have to deal with
471: // "IDE deps" internally:
472: @SuppressWarnings("deprecation")
473: Set<Dependency> openideDeps = Dependency.create(
474: Dependency.TYPE_IDE,
475: attr.getValue("OpenIDE-Module-IDE-Dependencies")); // NOI18N
476: if (!openideDeps.isEmpty()) {
477: // If empty, leave it that way; NbInstaller will add it anyway.
478: Dependency d = openideDeps.iterator().next();
479: String name = d.getName();
480: if (!name.startsWith("IDE/"))
481: throw new IllegalStateException("Weird IDE dep: "
482: + name); // NOI18N
483: dependencies.addAll(Dependency.create(
484: Dependency.TYPE_MODULE, "org.openide/"
485: + name.substring(4) + " > "
486: + d.getVersion())); // NOI18N
487: if (dependencies.size() != 1)
488: throw new IllegalStateException(
489: "Should be singleton: " + dependencies); // NOI18N
490:
491: Util.err
492: .warning("the module "
493: + codeNameBase
494: + " uses OpenIDE-Module-IDE-Dependencies which is deprecated. See http://openide.netbeans.org/proposals/arch/modularize.html"); // NOI18N
495: }
496: dependencies.addAll(Dependency.create(Dependency.TYPE_JAVA,
497: attr.getValue("OpenIDE-Module-Java-Dependencies"))); // NOI18N
498: dependencies
499: .addAll(Dependency
500: .create(
501: Dependency.TYPE_MODULE,
502: attr
503: .getValue("OpenIDE-Module-Module-Dependencies"))); // NOI18N
504: String pkgdeps = attr
505: .getValue("OpenIDE-Module-Package-Dependencies"); // NOI18N
506: if (pkgdeps != null) {
507: // XXX: Util.err.log(ErrorManager.WARNING, "Warning: module " + codeNameBase + " uses the OpenIDE-Module-Package-Dependencies manifest attribute, which is now deprecated: XXX URL TBD");
508: dependencies.addAll(Dependency.create(
509: Dependency.TYPE_PACKAGE, pkgdeps)); // NOI18N
510: }
511: dependencies.addAll(Dependency.create(
512: Dependency.TYPE_REQUIRES, attr
513: .getValue("OpenIDE-Module-Requires"))); // NOI18N
514: dependencies.addAll(Dependency.create(
515: Dependency.TYPE_NEEDS, attr
516: .getValue("OpenIDE-Module-Needs"))); // NOI18N
517: dependencies.addAll(Dependency.create(
518: Dependency.TYPE_RECOMMENDS, attr
519: .getValue("OpenIDE-Module-Recommends"))); // NOI18N
520: // Permit the concrete installer to make some changes:
521: mgr.refineDependencies(this , dependencies);
522: dependenciesA = dependencies
523: .toArray(new Dependency[dependencies.size()]);
524: } catch (IllegalArgumentException iae) {
525: throw (InvalidException) new InvalidException(
526: "While parsing " + codeName
527: + " a dependency attribute: "
528: + iae.toString()).initCause(iae); // NOI18N
529: }
530: }
531:
532: /** Get all JARs loaded by this module.
533: * Includes the module itself, any locale variants of the module,
534: * any extensions specified with Class-Path, any locale variants
535: * of those extensions.
536: * The list will be in classpath order (patches first).
537: * Currently the temp JAR is provided in the case of test modules, to prevent
538: * sporadic ZIP file exceptions when background threads (like Java parsing) tries
539: * to open libraries found in the library path.
540: * JARs already present in the classpath are <em>not</em> listed.
541: * @return a list of JARs
542: */
543: public abstract List<File> getAllJars();
544:
545: /** Is this module supposed to be easily reloadable?
546: * If so, it is suitable for testing inside the IDE.
547: * Controls whether a copy of the JAR file is made before
548: * passing it to the classloader, which can affect locking
549: * and refreshing of the JAR.
550: */
551: public boolean isReloadable() {
552: return reloadable;
553: }
554:
555: /** Set whether this module is supposed to be reloadable.
556: * Has no immediate effect, only impacts what happens the
557: * next time it is enabled (after having been disabled if
558: * necessary).
559: * Must be called from within a write mutex.
560: * @param r whether the module should be considered reloadable
561: */
562: public abstract void setReloadable(boolean r);
563:
564: /** Reload this module. Access from ModuleManager.
565: * If an exception is thrown, the module is considered
566: * to be in an invalid state.
567: * @since JST-PENDING: needed from ModuleSystem
568: */
569: public abstract void reload() throws IOException;
570:
571: // impl of ModuleInfo method
572: public ClassLoader getClassLoader() throws IllegalArgumentException {
573: if (!enabled) {
574: throw new IllegalArgumentException("Not enabled: "
575: + codeNameBase); // NOI18N
576: }
577: assert classloader != null : "Should have had a non-null loader for "
578: + this ;
579: return classloader;
580: }
581:
582: // Access from ModuleManager:
583: /** Turn on the classloader. Passed a list of parent modules to use.
584: * The parents should already have had their classloaders initialized.
585: */
586: protected abstract void classLoaderUp(Set<Module> parents)
587: throws IOException;
588:
589: /** Turn off the classloader and release all resources. */
590: protected abstract void classLoaderDown();
591:
592: /** Should be called after turning off the classloader of one or more modules & GC'ing. */
593: protected abstract void cleanup();
594:
595: /** Notify the module that it is being deleted. */
596: protected abstract void destroy();
597:
598: /**
599: * Fixed modules are treated differently.
600: * @see FixedModule
601: */
602: public abstract boolean isFixed();
603:
604: /** Get the JAR this module is packaged in.
605: * May be null for modules installed specially, e.g.
606: * automatically from the classpath.
607: * @see #isFixed
608: */
609: public File getJarFile() {
610: return null;
611: }
612:
613: /** Get the JAR manifest.
614: * Should never be null, even if disabled.
615: * Might change if a module is reloaded.
616: * It is not guaranteed that change events will be fired
617: * for changes in this property.
618: */
619: public abstract Manifest getManifest();
620:
621: /**
622: * Release memory storage for the JAR manifest, if applicable.
623: */
624: public void releaseManifest() {
625: }
626:
627: /** Get a set of {@link org.openide.modules.Dependency} objects representing missed dependencies.
628: * This module is examined to see
629: * why it would not be installable.
630: * If it is enabled, there are no problems.
631: * If it is in fact installable (possibly only
632: * by also enabling some other managed modules which are currently disabled), and
633: * all of its non-module dependencies are met, the returned set will be empty.
634: * Otherwise it will contain a list of reasons why this module cannot be installed:
635: * non-module dependencies which are not met; and module dependencies on modules
636: * which either do not exist in the managed set, or are the wrong version,
637: * or themselves cannot be installed
638: * for some reason or another (which may be separately examined).
639: * Note that in the (illegal) situation of two or more modules forming a cyclic
640: * dependency cycle, none of them will be installable, and the missing dependencies
641: * for each will be stated as the dependencies on the others. Again other modules
642: * dependent on modules in the cycle will list failed dependencies on the cyclic modules.
643: * Missing package dependencies are not guaranteed to be reported unless an install
644: * of the module has already been attempted, and failed due to them.
645: * The set may also contain {@link InvalidException}s representing known failures
646: * of the module to be installed, e.g. due to classloader problems, missing runtime
647: * resources, or failed ad-hoc dependencies. Again these are not guaranteed to be
648: * reported unless an install has already been attempted and failed due to them.
649: */
650: public Set<Object> getProblems() { // cannot use Union2<Dependency,InvalidException> without being binary-incompatible
651: if (!isValid())
652: throw new IllegalStateException("Not valid: " + this ); // NOI18N
653: if (isEnabled())
654: return Collections.emptySet();
655: Set<Object> problems = new HashSet<Object>();
656: for (Union2<Dependency, InvalidException> problem : mgr
657: .missingDependencies(this )) {
658: if (problem.hasFirst()) {
659: problems.add(problem.first());
660: } else {
661: problems.add(problem.second());
662: }
663: }
664: return problems;
665: }
666:
667: // Access from ChangeFirer:
668: final void firePropertyChange0(String prop, Object old, Object nue) {
669: if (Util.err.isLoggable(Level.FINE)) {
670: Util.err.fine("Module.propertyChange: " + this + " " + prop
671: + ": " + old + " -> " + nue);
672: }
673: firePropertyChange(prop, old, nue);
674: }
675:
676: /** Get the history object representing what has happened to this module before.
677: * @see ModuleHistory
678: */
679: public final Object getHistory() {
680: return history;
681: }
682:
683: /** String representation for debugging. */
684: public String toString() {
685: String s = "Module:" + getCodeNameBase(); // NOI18N
686: if (!isValid())
687: s += "[invalid]"; // NOI18N
688: return s;
689: }
690:
691: /** Struct representing a package exported from a module.
692: * @since org.netbeans.core/1 > 1.4
693: * @see Module#getPublicPackages
694: */
695: public static final class PackageExport {
696: /** Package to export, in the form <samp>org/netbeans/modules/foo/</samp>. */
697: public final String pkg;
698: /** If true, export subpackages also. */
699: public final boolean recursive;
700:
701: /** Create a package export struct with the named parameters. */
702: public PackageExport(String pkg, boolean recursive) {
703: this .pkg = pkg;
704: this .recursive = recursive;
705: }
706:
707: public String toString() {
708: return "PackageExport[" + pkg + (recursive ? "**/" : "")
709: + "]"; // NOI18N
710: }
711: }
712:
713: }
|