boolean (DifferentiateContext context, Difference.Specifier<JvmModule, JvmModule.Diff> modulesDiff, Utils future, Utils present) { if (modulesDiff.unchanged()) { return true; } for (JvmModule addedModule : modulesDiff.added()) { // after module has been added, the whole target should be rebuilt // because necessary 'require' directives may be missing from the newly added module-info file affectModule(context, future, addedModule); } for (JvmModule removedModule : modulesDiff.removed()) { affectDependentModules(context, present, removedModule, true, null); } for (Difference.Change<JvmModule, JvmModule.Diff> change : modulesDiff.changed()) { JvmModule changedModule = change.getPast(); JvmModule.Diff diff = change.getDiff(); boolean affectSelf = false; boolean affectDeps = false; Set<String> constraintPackageNames = new SmartHashSet<>(); if (diff.versionChanged()) { String version = changedModule.getVersion(); String moduleName = changedModule.getName(); affectDependentModules( context, present, changedModule, false, mod -> mod instanceof JvmModule && !Iterators.isEmpty(Iterators.filter(((JvmModule)mod).getRequires(), req -> Objects.equals(moduleName, req.getName()) && Objects.equals(version, req.getVersion()))) ); } Difference.Specifier<ModuleRequires, ModuleRequires.Diff> requiresDiff = diff.requires(); for (ModuleRequires removedRequires : requiresDiff.removed()) { affectSelf = true; if (removedRequires.isTransitive()) { affectDeps = true; break; } } for (Difference.Change<ModuleRequires, ModuleRequires.Diff> rChange : requiresDiff.changed()) { affectSelf |= rChange.getDiff().versionChanged(); if (rChange.getDiff().becameNonTransitive()) { affectDeps = true; // we could have created more precise constraint here: analyze if required module (recursively) // has only qualified exports that include given module's name. But this seems to be excessive since // in most cases module's exports are unqualified, so that any other module can access the exported API. } } Difference.Specifier<ModulePackage, ModulePackage.Diff> exportsDiff = diff.exports(); if (!affectDeps) { if (!Iterators.isEmpty(exportsDiff.removed())) { affectDeps = true; if (Iterators.isEmpty(Iterators.filter(exportsDiff.removed(), modPackage -> !modPackage.isQualified()))) { // all removed exports are qualified Iterators.collect(Iterators.flat(Iterators.map(exportsDiff.removed(), modPackage -> modPackage.getModules())), constraintPackageNames); } } } if (!affectDeps || !constraintPackageNames.isEmpty()) { for (Difference.Change<ModulePackage, ModulePackage.Diff> exportChange : exportsDiff.changed()) { Iterable<String> removedModuleNames = exportChange.getDiff().targetModules().removed(); affectDeps |= !Iterators.isEmpty(removedModuleNames); if (affectDeps) { Iterators.collect(removedModuleNames, constraintPackageNames); } } } if (affectSelf) { affectModule(context, present, changedModule); } if (affectDeps) { affectDependentModules( context, present, changedModule, true, constraintPackageNames.isEmpty()? null : node -> node instanceof JvmModule && constraintPackageNames.contains(((JvmModule)node).getName()) ); } } return true; }