jdk/make/modules/tools/src/com/sun/classanalyzer/Module.java
changeset 8642 a6cccd458bef
parent 8641 fbf4a969ccba
parent 8608 8e043a4d3cf6
child 8643 def8e16dd237
equal deleted inserted replaced
8641:fbf4a969ccba 8642:a6cccd458bef
     1 /*
       
     2  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  *
       
    23  */
       
    24 package com.sun.classanalyzer;
       
    25 
       
    26 import com.sun.classanalyzer.AnnotatedDependency.OptionalDependency;
       
    27 import java.io.IOException;
       
    28 import java.io.PrintWriter;
       
    29 import java.util.ArrayDeque;
       
    30 import java.util.Collection;
       
    31 import java.util.Collections;
       
    32 import java.util.Deque;
       
    33 import java.util.HashSet;
       
    34 import java.util.LinkedHashMap;
       
    35 import java.util.Map;
       
    36 import java.util.Set;
       
    37 import java.util.TreeMap;
       
    38 import java.util.TreeSet;
       
    39 
       
    40 /**
       
    41  *
       
    42  * @author Mandy Chung
       
    43  */
       
    44 public class Module implements Comparable<Module> {
       
    45 
       
    46     private static Map<String, Module> modules = new LinkedHashMap<String, Module>();
       
    47 
       
    48     public static Module addModule(ModuleConfig config) {
       
    49         String name = config.module;
       
    50         if (modules.containsKey(name)) {
       
    51             throw new RuntimeException("module \"" + name + "\" already exists");
       
    52         }
       
    53 
       
    54         Module m = new Module(config);
       
    55         modules.put(name, m);
       
    56         return m;
       
    57     }
       
    58 
       
    59     public static Module findModule(String name) {
       
    60         return modules.get(name);
       
    61     }
       
    62 
       
    63     static Collection<Module> getAllModules() {
       
    64         return Collections.unmodifiableCollection(modules.values());
       
    65     }
       
    66     private final String name;
       
    67     private final ModuleConfig config;
       
    68     private final Set<Klass> classes;
       
    69     private final Set<ResourceFile> resources;
       
    70     private final Set<Reference> unresolved;
       
    71     private final Set<Dependency> dependents;
       
    72     private final Map<String, PackageInfo> packages;
       
    73     private final Set<Module> members;
       
    74     private Module group;
       
    75     private boolean isBaseModule;
       
    76 
       
    77     private Module(ModuleConfig config) {
       
    78         this.name = config.module;
       
    79         this.isBaseModule = config.isBase;
       
    80         this.classes = new TreeSet<Klass>();
       
    81         this.resources = new TreeSet<ResourceFile>();
       
    82         this.config = config;
       
    83         this.unresolved = new HashSet<Reference>();
       
    84         this.dependents = new TreeSet<Dependency>();
       
    85         this.packages = new TreeMap<String, PackageInfo>();
       
    86         this.members = new TreeSet<Module>();
       
    87         this.group = this; // initialize to itself
       
    88     }
       
    89 
       
    90     String name() {
       
    91         return name;
       
    92     }
       
    93 
       
    94     Module group() {
       
    95         return group;
       
    96     }
       
    97 
       
    98     boolean isBase() {
       
    99         return isBaseModule;
       
   100     }
       
   101 
       
   102     Set<Module> members() {
       
   103         return members;
       
   104     }
       
   105 
       
   106     boolean contains(Klass k) {
       
   107         return k != null && classes.contains(k);
       
   108     }
       
   109 
       
   110     boolean isEmpty() {
       
   111         return classes.isEmpty() && resources.isEmpty();
       
   112     }
       
   113 
       
   114     /**
       
   115      * Returns an Iterable of Dependency, only one for each dependent
       
   116      * module of the strongest dependency (i.e.
       
   117      * hard static > hard dynamic > optional static > optional dynamic
       
   118      */
       
   119     Iterable<Dependency> dependents() {
       
   120         Map<Module, Dependency> deps = new LinkedHashMap<Module, Dependency>();
       
   121         for (Dependency dep : dependents) {
       
   122             Dependency d = deps.get(dep.module);
       
   123             if (d == null || dep.compareTo(d) > 0) {
       
   124                 deps.put(dep.module, dep);
       
   125             }
       
   126         }
       
   127         return deps.values();
       
   128     }
       
   129 
       
   130     @Override
       
   131     public int compareTo(Module o) {
       
   132         if (o == null) {
       
   133             return -1;
       
   134         }
       
   135         return name.compareTo(o.name);
       
   136     }
       
   137 
       
   138     @Override
       
   139     public String toString() {
       
   140         return name;
       
   141     }
       
   142 
       
   143     void addKlass(Klass k) {
       
   144         classes.add(k);
       
   145         k.setModule(this);
       
   146 
       
   147         // update package statistics
       
   148         String pkg = k.getPackageName();
       
   149         PackageInfo pkginfo = packages.get(pkg);
       
   150         if (pkginfo == null) {
       
   151             pkginfo = new PackageInfo(pkg);
       
   152             packages.put(pkg, pkginfo);
       
   153         }
       
   154         if (k.exists()) {
       
   155             // only count the class that is parsed
       
   156             pkginfo.add(k.getFileSize());
       
   157         }
       
   158     }
       
   159 
       
   160     void addResource(ResourceFile res) {
       
   161         resources.add(res);
       
   162         res.setModule(this);
       
   163     }
       
   164 
       
   165     void processRootsAndReferences() {
       
   166         // start with the root set
       
   167         Deque<Klass> pending = new ArrayDeque<Klass>();
       
   168         for (Klass k : Klass.getAllClasses()) {
       
   169             if (k.getModule() != null) {
       
   170                 continue;
       
   171             }
       
   172             String classname = k.getClassName();
       
   173             if (config.matchesRoot(classname) && !config.isExcluded(classname)) {
       
   174                 addKlass(k);
       
   175                 pending.add(k);
       
   176             }
       
   177         }
       
   178 
       
   179         // follow all references
       
   180         Klass k;
       
   181         while ((k = pending.poll()) != null) {
       
   182             if (!classes.contains(k)) {
       
   183                 addKlass(k);
       
   184             }
       
   185             for (Klass other : k.getReferencedClasses()) {
       
   186                 Module otherModule = other.getModule();
       
   187                 if (otherModule != null && otherModule != this) {
       
   188                     // this module is dependent on otherModule
       
   189                     addDependency(k, other);
       
   190                     continue;
       
   191                 }
       
   192 
       
   193                 if (!classes.contains(other)) {
       
   194                     if (config.isExcluded(other.getClassName())) {
       
   195                         // reference to an excluded class
       
   196                         unresolved.add(new Reference(k, other));
       
   197                     } else {
       
   198                         pending.add(other);
       
   199                     }
       
   200                 }
       
   201             }
       
   202         }
       
   203 
       
   204         // add other matching classes that don't require dependency analysis
       
   205         for (Klass c : Klass.getAllClasses()) {
       
   206             if (c.getModule() == null) {
       
   207                 String classname = c.getClassName();
       
   208                 if (config.matchesIncludes(classname) && !config.isExcluded(classname)) {
       
   209                     addKlass(c);
       
   210                     // dependencies
       
   211                     for (Klass other : c.getReferencedClasses()) {
       
   212                         Module otherModule = other.getModule();
       
   213                         if (otherModule == null) {
       
   214                             unresolved.add(new Reference(c, other));
       
   215                         } else {
       
   216                             if (otherModule != this) {
       
   217                                 // this module is dependent on otherModule
       
   218                                 addDependency(c, other);
       
   219                             }
       
   220                         }
       
   221                     }
       
   222                 }
       
   223             }
       
   224         }
       
   225 
       
   226 
       
   227         // add other matching classes that don't require dependency analysis
       
   228         for (ResourceFile res : ResourceFile.getAllResources()) {
       
   229             if (res.getModule() == null) {
       
   230                 String name = res.getName();
       
   231                 if (config.matchesIncludes(name) && !config.isExcluded(name)) {
       
   232                     addResource(res);
       
   233                 }
       
   234             }
       
   235         }
       
   236     }
       
   237 
       
   238     void addDependency(Klass from, Klass to) {
       
   239         Dependency dep = new Dependency(from, to);
       
   240         dependents.add(dep);
       
   241     }
       
   242 
       
   243     void fixupDependencies() {
       
   244         // update dependencies for classes that were allocated to modules after
       
   245         // this module was processed.
       
   246         for (Reference ref : unresolved) {
       
   247             Module m = ref.referree().getModule();
       
   248             if (m == null || m != this) {
       
   249                 addDependency(ref.referrer, ref.referree);
       
   250             }
       
   251         }
       
   252 
       
   253         fixupAnnotatedDependencies();
       
   254     }
       
   255 
       
   256     private void fixupAnnotatedDependencies() {
       
   257         // add dependencies that this klass may depend on due to the AnnotatedDependency
       
   258         dependents.addAll(AnnotatedDependency.getDependencies(this));
       
   259     }
       
   260 
       
   261     boolean isModuleDependence(Klass k) {
       
   262         Module m = k.getModule();
       
   263         return m == null || (!classes.contains(k) && !m.isBase());
       
   264     }
       
   265 
       
   266     Module getModuleDependence(Klass k) {
       
   267         if (isModuleDependence(k)) {
       
   268             Module m = k.getModule();
       
   269             if (group == this && m != null) {
       
   270                 // top-level module
       
   271                 return m.group;
       
   272             } else {
       
   273                 return m;
       
   274             }
       
   275         }
       
   276         return null;
       
   277     }
       
   278 
       
   279     <P> void visit(Set<Module> visited, Visitor<P> visitor, P p) {
       
   280         if (!visited.contains(this)) {
       
   281             visited.add(this);
       
   282             visitor.preVisit(this, p);
       
   283             for (Module m : members) {
       
   284                 m.visit(visited, visitor, p);
       
   285                 visitor.postVisit(this, m, p);
       
   286             }
       
   287         } else {
       
   288             throw new RuntimeException("Cycle detected: module " + this.name);
       
   289         }
       
   290     }
       
   291 
       
   292     void addMember(Module m) {
       
   293         // merge class list
       
   294         for (Klass k : m.classes) {
       
   295             classes.add(k);
       
   296         }
       
   297 
       
   298         // merge resource list
       
   299         for (ResourceFile res : m.resources) {
       
   300             resources.add(res);
       
   301         }
       
   302 
       
   303         // merge the package statistics
       
   304         for (PackageInfo pinfo : m.getPackageInfos()) {
       
   305             String packageName = pinfo.pkgName;
       
   306             PackageInfo pkginfo = packages.get(packageName);
       
   307             if (pkginfo == null) {
       
   308                 pkginfo = new PackageInfo(packageName);
       
   309                 packages.put(packageName, pkginfo);
       
   310             }
       
   311             pkginfo.add(pinfo);
       
   312         }
       
   313     }
       
   314 
       
   315     static void buildModuleMembers() {
       
   316         // set up module member relationship
       
   317         for (Module m : modules.values()) {
       
   318             m.group = m; // initialize to itself
       
   319             for (String name : m.config.members()) {
       
   320                 Module member = modules.get(name);
       
   321                 if (member == null) {
       
   322                     throw new RuntimeException("module \"" + name + "\" doesn't exist");
       
   323                 }
       
   324                 m.members.add(member);
       
   325             }
       
   326         }
       
   327 
       
   328         // set up the top-level module
       
   329         Visitor<Module> groupSetter = new Visitor<Module>() {
       
   330 
       
   331             public void preVisit(Module m, Module p) {
       
   332                 m.group = p;
       
   333                 if (p.isBaseModule) {
       
   334                     // all members are also base
       
   335                     m.isBaseModule = true;
       
   336                 }
       
   337             }
       
   338 
       
   339             public void postVisit(Module m, Module child, Module p) {
       
   340                 // nop - breadth-first search
       
   341             }
       
   342         };
       
   343 
       
   344         // propagate the top-level module to all its members
       
   345         for (Module p : modules.values()) {
       
   346             for (Module m : p.members) {
       
   347                 if (m.group == m) {
       
   348                     m.visit(new TreeSet<Module>(), groupSetter, p);
       
   349                 }
       
   350             }
       
   351         }
       
   352 
       
   353         Visitor<Module> mergeClassList = new Visitor<Module>() {
       
   354 
       
   355             public void preVisit(Module m, Module p) {
       
   356                 // nop - depth-first search
       
   357             }
       
   358 
       
   359             public void postVisit(Module m, Module child, Module p) {
       
   360                 m.addMember(child);
       
   361             }
       
   362         };
       
   363 
       
   364         Set<Module> visited = new TreeSet<Module>();
       
   365         for (Module m : modules.values()) {
       
   366             if (m.group() == m) {
       
   367                 if (m.members().size() > 0) {
       
   368                     // merge class list from all its members
       
   369                     m.visit(visited, mergeClassList, m);
       
   370                 }
       
   371 
       
   372                 // clear the dependencies before fixup
       
   373                 m.dependents.clear();
       
   374 
       
   375                 // fixup dependencies
       
   376                 for (Klass k : m.classes) {
       
   377                     for (Klass other : k.getReferencedClasses()) {
       
   378                         if (m.isModuleDependence(other)) {
       
   379                             // this module is dependent on otherModule
       
   380                             m.addDependency(k, other);
       
   381                         }
       
   382                     }
       
   383                 }
       
   384 
       
   385                 // add dependencies that this klass may depend on due to the AnnotatedDependency
       
   386                 m.fixupAnnotatedDependencies();
       
   387             }
       
   388         }
       
   389     }
       
   390 
       
   391     class PackageInfo implements Comparable {
       
   392 
       
   393         final String pkgName;
       
   394         int count;
       
   395         long filesize;
       
   396 
       
   397         PackageInfo(String name) {
       
   398             this.pkgName = name;
       
   399             this.count = 0;
       
   400             this.filesize = 0;
       
   401         }
       
   402 
       
   403         void add(PackageInfo pkg) {
       
   404             this.count += pkg.count;
       
   405             this.filesize += pkg.filesize;
       
   406         }
       
   407 
       
   408         void add(long size) {
       
   409             count++;
       
   410             filesize += size;
       
   411 
       
   412         }
       
   413 
       
   414         @Override
       
   415         public int compareTo(Object o) {
       
   416             return pkgName.compareTo(((PackageInfo) o).pkgName);
       
   417         }
       
   418     }
       
   419 
       
   420     Set<PackageInfo> getPackageInfos() {
       
   421         return new TreeSet<PackageInfo>(packages.values());
       
   422     }
       
   423 
       
   424     void printSummaryTo(String output) throws IOException {
       
   425         PrintWriter writer = new PrintWriter(output);
       
   426         try {
       
   427             long total = 0L;
       
   428             int count = 0;
       
   429             writer.format("%10s\t%10s\t%s\n", "Bytes", "Classes", "Package name");
       
   430             for (String pkg : packages.keySet()) {
       
   431                 PackageInfo info = packages.get(pkg);
       
   432                 if (info.count > 0) {
       
   433                     writer.format("%10d\t%10d\t%s\n", info.filesize, info.count, pkg);
       
   434                     total += info.filesize;
       
   435                     count += info.count;
       
   436                 }
       
   437             }
       
   438 
       
   439             writer.format("\nTotal: %d bytes (uncompressed) %d classes\n", total, count);
       
   440         } finally {
       
   441             writer.close();
       
   442         }
       
   443 
       
   444     }
       
   445 
       
   446     void printClassListTo(String output) throws IOException {
       
   447         // no file created if the module doesn't have any class nor resource
       
   448         if (isEmpty()) {
       
   449             return;
       
   450         }
       
   451 
       
   452         PrintWriter writer = new PrintWriter(output);
       
   453         try {
       
   454             for (Klass c : classes) {
       
   455                 if (c.exists()) {
       
   456                     writer.format("%s\n", c.getClassFilePathname());
       
   457                 } else {
       
   458                     trace("%s in module %s missing\n", c, this);
       
   459                 }
       
   460             }
       
   461 
       
   462         } finally {
       
   463             writer.close();
       
   464         }
       
   465     }
       
   466 
       
   467     void printResourceListTo(String output) throws IOException {
       
   468         // no file created if the module doesn't have any resource file
       
   469         if (resources.isEmpty()) {
       
   470             return;
       
   471         }
       
   472 
       
   473         PrintWriter writer = new PrintWriter(output);
       
   474         try {
       
   475             for (ResourceFile res : resources) {
       
   476                 writer.format("%s\n", res.getPathname());
       
   477             }
       
   478         } finally {
       
   479             writer.close();
       
   480         }
       
   481     }
       
   482 
       
   483     void printDependenciesTo(String output, boolean showDynamic) throws IOException {
       
   484         // no file created if the module doesn't have any class
       
   485         if (isEmpty()) {
       
   486             return;
       
   487         }
       
   488 
       
   489         PrintWriter writer = new PrintWriter(output);
       
   490         try {
       
   491             // classes that this klass may depend on due to the AnnotatedDependency
       
   492             Map<Reference, Set<AnnotatedDependency>> annotatedDeps = AnnotatedDependency.getReferences(this);
       
   493 
       
   494             for (Klass klass : classes) {
       
   495                 Set<Klass> references = klass.getReferencedClasses();
       
   496                 for (Klass other : references) {
       
   497                     String classname = klass.getClassName();
       
   498                     boolean optional = OptionalDependency.isOptional(klass, other);
       
   499                     if (optional) {
       
   500                         classname = "[optional] " + classname;
       
   501                     }
       
   502 
       
   503                     Module m = getModuleDependence(other);
       
   504                     if (m != null || other.getModule() == null) {
       
   505                         writer.format("%-40s -> %s (%s)", classname, other, m);
       
   506                         Reference ref = new Reference(klass, other);
       
   507                         if (annotatedDeps.containsKey(ref)) {
       
   508                             for (AnnotatedDependency ad : annotatedDeps.get(ref)) {
       
   509                                 writer.format(" %s", ad.getTag());
       
   510                             }
       
   511                             // printed; so remove the dependency from the annotated deps list
       
   512                             annotatedDeps.remove(ref);
       
   513                         }
       
   514                         writer.format("\n");
       
   515                     }
       
   516                 }
       
   517             }
       
   518 
       
   519 
       
   520             // print remaining dependencies specified in AnnotatedDependency list
       
   521             if (annotatedDeps.size() > 0) {
       
   522                 for (Map.Entry<Reference, Set<AnnotatedDependency>> entry : annotatedDeps.entrySet()) {
       
   523                     Reference ref = entry.getKey();
       
   524                     Module m = getModuleDependence(ref.referree);
       
   525                     if (m != null || ref.referree.getModule() == null) {
       
   526                         String classname = ref.referrer.getClassName();
       
   527                         boolean optional = true;
       
   528                         boolean dynamic = true;
       
   529                         String tag = "";
       
   530                         for (AnnotatedDependency ad : entry.getValue()) {
       
   531                             if (optional && !ad.isOptional()) {
       
   532                                 optional = false;
       
   533                                 tag = ad.getTag();
       
   534                             }
       
   535                             if (!ad.isDynamic()) {
       
   536                                 dynamic = false;
       
   537                             }
       
   538                         }
       
   539                         if (!showDynamic && optional && dynamic) {
       
   540                             continue;
       
   541                         }
       
   542                         if (optional) {
       
   543                             if (dynamic) {
       
   544                                 classname = "[dynamic] " + classname;
       
   545                             } else {
       
   546                                 classname = "[optional] " + classname;
       
   547                             }
       
   548                         }
       
   549                         writer.format("%-40s -> %s (%s) %s%n", classname, ref.referree, m, tag);
       
   550                     }
       
   551                 }
       
   552             }
       
   553 
       
   554         } finally {
       
   555             writer.close();
       
   556         }
       
   557     }
       
   558 
       
   559     static class Dependency implements Comparable<Dependency> {
       
   560 
       
   561         final Module module;
       
   562         final boolean optional;
       
   563         final boolean dynamic;
       
   564 
       
   565         Dependency(Klass from, Klass to) {
       
   566             // static dependency
       
   567             this.module = to.getModule() != null ? to.getModule().group() : null;
       
   568             this.optional = OptionalDependency.isOptional(from, to);
       
   569             this.dynamic = false;
       
   570         }
       
   571 
       
   572         Dependency(Module m, boolean optional, boolean dynamic) {
       
   573             this.module = m != null ? m.group() : null;
       
   574             this.optional = optional;
       
   575             this.dynamic = dynamic;
       
   576         }
       
   577 
       
   578         @Override
       
   579         public boolean equals(Object obj) {
       
   580             if (!(obj instanceof Dependency)) {
       
   581                 return false;
       
   582             }
       
   583             if (this == obj) {
       
   584                 return true;
       
   585             }
       
   586 
       
   587             Dependency d = (Dependency) obj;
       
   588             if (this.module != d.module) {
       
   589                 return false;
       
   590             } else {
       
   591                 return this.optional == d.optional && this.dynamic == d.dynamic;
       
   592             }
       
   593         }
       
   594 
       
   595         @Override
       
   596         public int hashCode() {
       
   597             int hash = 3;
       
   598             hash = 19 * hash + (this.module != null ? this.module.hashCode() : 0);
       
   599             hash = 19 * hash + (this.optional ? 1 : 0);
       
   600             hash = 19 * hash + (this.dynamic ? 1 : 0);
       
   601             return hash;
       
   602         }
       
   603 
       
   604         @Override
       
   605         public int compareTo(Dependency d) {
       
   606             if (this.equals(d)) {
       
   607                 return 0;
       
   608             }
       
   609 
       
   610             // Hard static > hard dynamic > optional static > optional dynamic
       
   611             if (this.module == d.module) {
       
   612                 if (this.optional == d.optional) {
       
   613                     return this.dynamic ? -1 : 1;
       
   614                 } else {
       
   615                     return this.optional ? -1 : 1;
       
   616                 }
       
   617             } else if (this.module != null && d.module != null) {
       
   618                 return (this.module.compareTo(d.module));
       
   619             } else {
       
   620                 return (this.module == null) ? -1 : 1;
       
   621             }
       
   622         }
       
   623 
       
   624         @Override
       
   625         public String toString() {
       
   626             String s = module.name();
       
   627             if (dynamic && optional) {
       
   628                 s += " (dynamic)";
       
   629             } else if (optional) {
       
   630                 s += " (optional)";
       
   631             }
       
   632             return s;
       
   633         }
       
   634     }
       
   635 
       
   636     static class Reference implements Comparable<Reference> {
       
   637 
       
   638         private final Klass referrer, referree;
       
   639 
       
   640         Reference(Klass referrer, Klass referree) {
       
   641             this.referrer = referrer;
       
   642             this.referree = referree;
       
   643         }
       
   644 
       
   645         Klass referrer() {
       
   646             return referrer;
       
   647         }
       
   648 
       
   649         Klass referree() {
       
   650             return referree;
       
   651         }
       
   652 
       
   653         @Override
       
   654         public int hashCode() {
       
   655             return referrer.hashCode() ^ referree.hashCode();
       
   656         }
       
   657 
       
   658         @Override
       
   659         public boolean equals(Object obj) {
       
   660             if (!(obj instanceof Reference)) {
       
   661                 return false;
       
   662             }
       
   663             if (this == obj) {
       
   664                 return true;
       
   665             }
       
   666 
       
   667             Reference r = (Reference) obj;
       
   668             return (this.referrer.equals(r.referrer) &&
       
   669                     this.referree.equals(r.referree));
       
   670         }
       
   671 
       
   672         @Override
       
   673         public int compareTo(Reference r) {
       
   674             int ret = referrer.compareTo(r.referrer);
       
   675             if (ret == 0) {
       
   676                 ret = referree.compareTo(r.referree);
       
   677             }
       
   678             return ret;
       
   679         }
       
   680     }
       
   681 
       
   682     interface Visitor<P> {
       
   683 
       
   684         public void preVisit(Module m, P param);
       
   685 
       
   686         public void postVisit(Module m, Module child, P param);
       
   687     }
       
   688     private static boolean traceOn = System.getProperty("classanalyzer.debug") != null;
       
   689 
       
   690     private static void trace(String format, Object... params) {
       
   691         System.err.format(format, params);
       
   692     }
       
   693 }