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 package com.sun.classanalyzer; 24 25 import java.util.*; 26 27 /** 28 * Information about a module. ModuleInfo.toString() returns 29 * a string representation of the module-info.java source file. 30 * 31 * @author Mandy Chung 32 */ 33 public class ModuleInfo { 34 35 private final Module module; 36 private final String version; 37 private final Set<PackageInfo> packages; 38 private final Set<Dependence> requires; 39 private final Set<Module> permits; 40 41 ModuleInfo(Module m, String version, 42 Collection<PackageInfo> packages, 43 Collection<Dependence> reqs, 44 Collection<Module> permits) { 45 this.module = m; 46 this.version = version; 47 this.packages = new TreeSet<PackageInfo>(packages); 48 this.permits = new TreeSet<Module>(permits); 49 this.requires = new TreeSet<Dependence>(); 50 // filter non-top level module 51 for (Dependence d : reqs) { 52 if (d.getModule().isTopLevel()) { 53 requires.add(d); 54 } 55 } 56 } 57 58 public Module getModule() { 59 return module; 60 } 61 62 /** 63 * This module's identifier 64 */ 65 public String name() { 66 return module.name(); 67 } 68 69 public String id() { 70 return module.name() + " @ " + version; 71 } 72 73 public Set<PackageInfo> packages() { 74 return Collections.unmodifiableSet(packages); 75 } 76 77 /** 78 * The dependences of this module 79 */ 80 public Set<Dependence> requires() { 81 return Collections.unmodifiableSet(requires); 82 } 83 84 /** 85 * The modules that are permitted to require this module 86 */ 87 public Set<Module> permits() { 88 return Collections.unmodifiableSet(permits); 89 } 90 91 public void addPermit(Module m) { 92 permits.add(m); 93 } 94 126 127 private synchronized Set<String> reexports() { 128 if (reexports != null) { 129 return reexports; 130 } 131 132 final Module m = module; 133 Set<Module> deps = dependences(new Dependence.Filter() { 134 135 @Override 136 public boolean accept(Dependence d) { 137 // filter itself 138 return d.isPublic(); 139 } 140 }); 141 142 reexports = new TreeSet<String>(); 143 for (Module dm : deps) { 144 if (dm != module) { 145 // exports all local packages 146 for (PackageInfo p : dm.getModuleInfo().packages) { 147 if (PackageInfo.isExportedPackage(p.pkgName)) { 148 reexports.add(p.pkgName + ".*"); 149 } 150 } 151 reexports.addAll(dm.getModuleInfo().reexports()); 152 } 153 } 154 return reexports; 155 } 156 private static final String INDENT = " "; 157 158 /** 159 * Returns a string representation of module-info.java for 160 * this module. 161 */ 162 @Override 163 public String toString() { 164 StringBuilder sb = new StringBuilder(); 165 sb.append(String.format("module %s {%n", id())); 166 167 for (Dependence d : requires()) { 168 String mods = ""; 169 for (Dependence.Modifier mod : d.mods) { 170 if (mod != Dependence.Modifier.PUBLIC) { 171 mods += mod.toString() + " "; 172 } 173 } 174 sb.append(String.format("%srequires %s%s;%n", INDENT, 175 mods, 176 d.getModule().getModuleInfo().id())); 177 } 178 179 String permits = INDENT + "permits "; 180 int i = 0; 181 for (Module pm : permits()) { 182 if (i > 0) { 183 permits += ", "; 184 if ((i % 5) == 0) { 185 permits += "\n" + INDENT + " "; // "permits" 186 } 187 } 188 permits += pm.name(); 189 i++; 190 } 191 192 if (permits().size() > 0) { 193 sb.append(permits).append(";\n"); 194 } 195 if (module.mainClass() != null) { 196 sb.append(String.format("%sclass %s;%n", INDENT, mainClass())); 197 } 198 199 Set<Module> modules = dependences(new Dependence.Filter() { 200 201 @Override 202 public boolean accept(Dependence d) { 203 // filter itself 204 return d.isPublic(); 205 } 206 }); 207 208 // explicit exports in the given config file 209 Set<String> cexports = new TreeSet<String>(); 210 for (Module m : modules) { 211 cexports.addAll(m.config().exports()); 212 } 213 214 if (cexports.size() > 0) { 215 sb.append("\n" + INDENT + "// explicit exports\n"); 216 for (String e : cexports) { 217 sb.append(String.format("%sexport %s;%n", INDENT, e)); 218 } 219 } 220 221 // exports all local packages 222 Set<String> pkgs = new TreeSet<String>(); 223 for (PackageInfo pi : packages) { 224 String p = pi.pkgName; 225 if (module.exportAllPackages() || PackageInfo.isExportedPackage(p)) 226 pkgs.add(p); 227 } 228 229 if (pkgs.size() > 0) { 230 sb.append(String.format("%n%s// exports %s packages%n", INDENT, 231 module.exportAllPackages() ? "all local" : "supported")); 232 for (String p : pkgs) { 233 sb.append(String.format("%sexport %s.*;%n", INDENT, p)); 234 } 235 } 236 237 // reexports 238 if (reexports().size() > 0) { 239 Set<String> rexports = new TreeSet<String>(); 240 if (modules.size() == 2) { 241 // special case? 242 rexports.addAll(reexports()); 243 } else { 244 for (String e : reexports()) { 245 int j = e.indexOf('.'); 246 rexports.add(e.substring(0, j) + ".**"); 247 } 248 } 249 sb.append("\n" + INDENT + "// reexports\n"); 250 for (String p : rexports) { 251 sb.append(String.format("%sexport %s;%n", INDENT, p)); 252 } 253 } 254 255 sb.append("}\n"); 256 return sb.toString(); 257 } 258 259 static class Dependence implements Comparable<Dependence> { 260 261 static enum Modifier { 262 263 PUBLIC("public"), 264 OPTIONAL("optional"), 265 LOCAL("local"); 266 private final String name; 267 268 Modifier(String n) { 269 this.name = n; 270 } 271 272 @Override 273 public String toString() { 274 return name; 275 } 276 } 277 private final String id; 278 private EnumSet<Modifier> mods; 279 private Module sm = null; 280 281 public Dependence(Module sm) { 282 this(sm, false); 283 } 284 285 public Dependence(Module sm, boolean optional) { 286 this(sm, modifier(optional)); 287 } 288 289 public Dependence(Klass from, Klass to, boolean optional) { 290 this(to.getModule(), modifier(optional)); 291 } 292 293 public Dependence(Module sm, EnumSet<Modifier> mods) { 294 this.sm = sm.group(); 295 this.id = this.sm.name(); 296 this.mods = mods; 297 } 298 299 public Dependence(String name, boolean optional) { 300 this(name, optional, false, false); 301 } 302 303 public Dependence(String name, boolean optional, boolean reexport, boolean local) { 304 Set<Modifier> ms = new TreeSet<Modifier>(); 305 if (optional) { 306 ms.add(Modifier.OPTIONAL); 307 } 308 if (reexport) { 309 ms.add(Modifier.PUBLIC); 310 } 311 if (local) { 312 ms.add(Modifier.LOCAL); 313 } 314 this.id = name; 315 this.mods = ms.isEmpty() 316 ? EnumSet.noneOf(Modifier.class) 317 : EnumSet.copyOf(ms); 318 } 319 320 private static EnumSet<Modifier> modifier(boolean optional) { 321 return optional ? EnumSet.of(Modifier.OPTIONAL) 322 : EnumSet.noneOf(Modifier.class); 323 } 324 325 synchronized Module getModule() { 326 if (sm == null) { 327 Module m = Module.findModule(id); 328 if (m == null) { 329 throw new RuntimeException("Module " + id + " doesn't exist"); 330 } 331 sm = m.group(); 332 } 333 return sm; 334 } 335 336 public boolean isOptional() { 337 return mods.contains(Modifier.OPTIONAL); 338 } 339 340 public boolean isLocal() { 341 return mods.contains(Modifier.LOCAL); 342 } 343 344 public boolean isPublic() { 345 return mods.contains(Modifier.PUBLIC); 346 } 347 348 public void addModifier(Modifier e) { 349 mods.add(e); 350 } 351 352 public void update(Dependence d) { 353 // static dependence overrides the optional | 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 package com.sun.classanalyzer; 24 25 import java.util.*; 26 27 /** 28 * Information about a module. ModuleInfo.toString() returns 29 * a string representation of the module-info.java source file. 30 * 31 * @author Mandy Chung 32 */ 33 public class ModuleInfo { 34 35 private final Module module; 36 private final Set<Dependence> requires; 37 private final Set<Module> permits; 38 39 ModuleInfo(Module m, 40 Collection<Dependence> reqs, 41 Collection<Module> permits) { 42 this.module = m; 43 this.permits = new TreeSet<Module>(permits); 44 this.requires = new TreeSet<Dependence>(); 45 // filter non-top level module 46 for (Dependence d : reqs) { 47 if (d.getModule().isTopLevel()) { 48 requires.add(d); 49 } 50 } 51 } 52 53 public Module getModule() { 54 return module; 55 } 56 57 /** 58 * This module's identifier 59 */ 60 public String name() { 61 return module.name(); 62 } 63 64 public String id() { 65 return module.name() + " @ " + module.version(); 66 } 67 68 /** 69 * The dependences of this module 70 */ 71 public Set<Dependence> requires() { 72 return Collections.unmodifiableSet(requires); 73 } 74 75 /** 76 * The modules that are permitted to require this module 77 */ 78 public Set<Module> permits() { 79 return Collections.unmodifiableSet(permits); 80 } 81 82 public void addPermit(Module m) { 83 permits.add(m); 84 } 85 117 118 private synchronized Set<String> reexports() { 119 if (reexports != null) { 120 return reexports; 121 } 122 123 final Module m = module; 124 Set<Module> deps = dependences(new Dependence.Filter() { 125 126 @Override 127 public boolean accept(Dependence d) { 128 // filter itself 129 return d.isPublic(); 130 } 131 }); 132 133 reexports = new TreeSet<String>(); 134 for (Module dm : deps) { 135 if (dm != module) { 136 // exports all local packages 137 for (PackageInfo p : dm.packages()) { 138 if (PackageInfo.isExportedPackage(p.pkgName)) { 139 reexports.add(p.pkgName + ".*"); 140 } 141 } 142 reexports.addAll(dm.getModuleInfo().reexports()); 143 } 144 } 145 return reexports; 146 } 147 148 // a system property to use "requires public" to reexport 149 // instead of the "exports" statement 150 private static final boolean requiresPublic = 151 Boolean.getBoolean("classanalyzer.requiresPublic"); 152 private static final String INDENT = " "; 153 154 155 /** 156 * Returns a string representation of module-info.java for 157 * this module. 158 */ 159 @Override 160 public String toString() { 161 StringBuilder sb = new StringBuilder(); 162 sb.append(String.format("module %s {%n", id())); 163 164 for (Dependence d : requires()) { 165 String mods = ""; 166 for (Dependence.Modifier mod : d.mods) { 167 if (requiresPublic || mod != Dependence.Modifier.PUBLIC) { 168 mods += mod.toString() + " "; 169 } 170 } 171 sb.append(String.format("%srequires %s%s;%n", INDENT, 172 mods, 173 d.getModule().getModuleInfo().id())); 174 } 175 176 String permits = INDENT + "permits "; 177 int i = 0; 178 for (Module pm : permits()) { 179 if (i > 0) { 180 permits += ", "; 181 if ((i % 5) == 0) { 182 permits += "\n" + INDENT + " "; // "permits" 183 } 184 } 185 permits += pm.name(); 186 i++; 187 } 188 189 if (permits().size() > 0) { 190 sb.append(permits).append(";\n"); 191 } 192 if (module.mainClass() != null) { 193 sb.append(String.format("%sclass %s;%n", INDENT, mainClass())); 194 } 195 196 if (!requiresPublic) 197 printExports(sb); 198 199 sb.append("}\n"); 200 return sb.toString(); 201 } 202 203 private void printExports(StringBuilder sb) { 204 Set<Module> modules = dependences(new Dependence.Filter() { 205 206 @Override 207 public boolean accept(Dependence d) { 208 // filter itself 209 return d.isPublic(); 210 } 211 }); 212 213 // explicit exports in the given config file 214 Set<String> cexports = new TreeSet<String>(); 215 for (Module m : modules) { 216 cexports.addAll(m.config().exports()); 217 } 218 219 if (cexports.size() > 0) { 220 sb.append("\n" + INDENT + "// explicit exports\n"); 221 for (String e : cexports) { 222 sb.append(String.format("%sexport %s;%n", INDENT, e)); 223 } 224 } 225 226 // exports all local packages 227 Set<String> pkgs = new TreeSet<String>(); 228 for (PackageInfo pi : module.packages()) { 229 String p = pi.pkgName; 230 if (module.exportAllPackages() || PackageInfo.isExportedPackage(p)) 231 pkgs.add(p); 232 } 233 234 if (pkgs.size() > 0) { 235 sb.append(String.format("%n%s// exports %s packages%n", INDENT, 236 module.exportAllPackages() ? "all local" : "supported")); 237 for (String p : pkgs) { 238 sb.append(String.format("%sexport %s.*;%n", INDENT, p)); 239 } 240 } 241 242 // reexports 243 if (reexports().size() > 0) { 244 Set<String> rexports = new TreeSet<String>(); 245 if (modules.size() == 2) { 246 // special case? 247 rexports.addAll(reexports()); 248 } else { 249 for (String e : reexports()) { 250 int j = e.indexOf('.'); 251 rexports.add(e.substring(0, j) + ".**"); 252 } 253 } 254 sb.append("\n" + INDENT + "// reexports\n"); 255 for (String p : rexports) { 256 sb.append(String.format("%sexport %s;%n", INDENT, p)); 257 } 258 } 259 } 260 261 static class Dependence implements Comparable<Dependence> { 262 263 static enum Modifier { 264 265 PUBLIC("public"), 266 OPTIONAL("optional"), 267 LOCAL("local"); 268 private final String name; 269 270 Modifier(String n) { 271 this.name = n; 272 } 273 274 @Override 275 public String toString() { 276 return name; 277 } 278 } 279 final String id; 280 private EnumSet<Modifier> mods; 281 private Module dm = null; 282 283 public Dependence(Module dm) { 284 this(dm, false); 285 } 286 287 public Dependence(Module dm, boolean optional) { 288 this(dm, modifier(optional)); 289 } 290 291 public Dependence(Module dm, EnumSet<Modifier> mods) { 292 this.dm = dm.group(); 293 this.id = dm.name(); 294 this.mods = mods; 295 } 296 297 public Dependence(String name, boolean optional) { 298 this(name, optional, false, false); 299 } 300 301 public Dependence(String name, boolean optional, boolean reexport, boolean local) { 302 Set<Modifier> ms = new TreeSet<Modifier>(); 303 if (optional) { 304 ms.add(Modifier.OPTIONAL); 305 } 306 if (reexport) { 307 ms.add(Modifier.PUBLIC); 308 } 309 if (local) { 310 ms.add(Modifier.LOCAL); 311 } 312 this.id = name; 313 this.mods = ms.isEmpty() 314 ? EnumSet.noneOf(Modifier.class) 315 : EnumSet.copyOf(ms); 316 } 317 318 private static EnumSet<Modifier> modifier(boolean optional) { 319 return optional ? EnumSet.of(Modifier.OPTIONAL) 320 : EnumSet.noneOf(Modifier.class); 321 } 322 323 void setModule(Module m) { 324 assert dm == null && m != null; 325 dm = m.group(); 326 } 327 328 Module getModule() { 329 return dm; 330 } 331 332 public boolean isOptional() { 333 return mods.contains(Modifier.OPTIONAL); 334 } 335 336 public boolean isLocal() { 337 return mods.contains(Modifier.LOCAL); 338 } 339 340 public boolean isPublic() { 341 return mods.contains(Modifier.PUBLIC); 342 } 343 344 public void addModifier(Modifier e) { 345 mods.add(e); 346 } 347 348 public void update(Dependence d) { 349 // static dependence overrides the optional |