blob: 4c6bdefe08590a888af668bee5a0675a0454b166 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.autoupdate.services;
import java.io.File;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.Util;
import org.openide.modules.ModuleInfo;
import org.openide.modules.Dependency;
import org.openide.modules.SpecificationVersion;
class DependencyChecker extends Object {
private static final Logger err = Logger.getLogger(DependencyChecker.class.getName ());
public static Set<Dependency> findBrokenDependencies (Set<Dependency> deps, Collection<ModuleInfo> modules) {
Set<Dependency> res = new HashSet<Dependency> ();
for (Dependency dep : deps) {
err.log(Level.FINE, "Dependency[" + dep.getType () + "]: " + dep);
switch (dep.getType ()) {
case (Dependency.TYPE_REQUIRES) :
case (Dependency.TYPE_NEEDS) :
if (findModuleMatchesDependencyRequires (dep, modules).isEmpty ()) {
// bad, report missing module
res.add (dep);
} else {
// ok
}
break;
case (Dependency.TYPE_RECOMMENDS) :
break;
case (Dependency.TYPE_MODULE) :
if (matchDependencyModule (dep, modules) != null) {
// ok
} else {
// bad, report missing module
res.add (dep);
}
break;
case (Dependency.TYPE_JAVA) :
if (! matchDependencyJava (dep)) {
err.log(Level.FINE, "The Java platform version " + dep +
" or higher was requested but only " + Dependency.JAVA_SPEC + " is running.");
res.add (dep);
}
break;
case (Dependency.TYPE_PACKAGE) :
if (! matchPackageDependency (dep)) {
err.log(Level.FINE, "The package " + dep +
" was requested but it is not in current ClassPath.");
res.add (dep);
}
break;
default:
//assert false : "Unknown type of Dependency, was " + dep.getType ();
err.log(Level.FINE, "Uncovered Dependency " + dep);
}
}
return res;
}
public static Set<Dependency> findBrokenDependenciesTransitive (ModuleInfo info, Collection<ModuleInfo> modules, Set<ModuleInfo> seen) {
if (seen.contains (info)) {
return Collections.emptySet ();
}
seen.add (info);
Set<Dependency> res = new HashSet<Dependency> ();
for (Dependency dep : filterTypeRecommends (info.getDependencies ())) {
err.log(Level.FINE, "Dependency[" + dep.getType () + "]: " + dep);
Collection<ModuleInfo> providers = null;
switch (dep.getType ()) {
case (Dependency.TYPE_REQUIRES) :
case (Dependency.TYPE_NEEDS) :
case (Dependency.TYPE_RECOMMENDS) :
providers = findModuleMatchesDependencyRequires (dep, modules);
if (providers.size () > 0) {
for (ModuleInfo m : providers) {
res.addAll (findBrokenDependenciesTransitive (m, modules, seen));
}
} else {
// bad, report missing module
res.add (dep);
}
break;
case (Dependency.TYPE_MODULE) :
ModuleInfo m = matchDependencyModule (dep, modules);
if (m != null) {
res.addAll (findBrokenDependenciesTransitive (m, modules, seen));
} else {
// bad, report missing module
res.add (dep);
}
break;
case (Dependency.TYPE_JAVA) :
if (! matchDependencyJava (dep)) {
err.log(Level.FINE, "The Java platform version " + dep +
" or higher was requested but only " + Dependency.JAVA_SPEC + " is running.");
res.add (dep);
}
break;
case (Dependency.TYPE_PACKAGE) :
if (! matchPackageDependency (dep)) {
err.log(Level.FINE, "The package " + dep +
" was requested but it is not in current ClassPath.");
res.add (dep);
}
break;
default:
//assert false : "Unknown type of Dependency, was " + dep.getType ();
err.log(Level.FINE, "Uncovered Dependency " + dep);
}
}
return res;
}
private static Set<Dependency> filterTypeRecommends (Collection<Dependency> deps) {
Set<Dependency> res = new HashSet<Dependency> ();
for (Dependency dep : deps) {
if (Dependency.TYPE_RECOMMENDS != dep.getType ()) {
res.add (dep);
}
}
return res;
}
static Collection<ModuleInfo> findModuleMatchesDependencyRequires (Dependency dep, Collection<ModuleInfo> modules) {
UpdateManagerImpl mgr = UpdateManagerImpl.getInstance ();
Set<ModuleInfo> providers = new HashSet<ModuleInfo> ();
providers.addAll (mgr.getAvailableProviders (dep.getName ()));
providers.addAll (mgr.getInstalledProviders (dep.getName ()));
Set<ModuleInfo> res = new HashSet<ModuleInfo> (providers);
for (ModuleInfo mi : providers) {
for (ModuleInfo input : modules) {
if (mi.getCodeName ().equals (input.getCodeName ())) {
res.add (mi);
}
}
}
return res;
}
private static ModuleInfo matchDependencyModule (Dependency dep, Collection<ModuleInfo> modules) {
for (ModuleInfo module : modules) {
if (checkDependencyModule (dep, module)) {
return module;
}
}
return null;
}
public static boolean matchDependencyJava (Dependency dep) {
if (dep.getName ().equals (Dependency.JAVA_NAME) && Dependency.COMPARE_SPEC == dep.getComparison ()) {
return Dependency.JAVA_SPEC.compareTo (new SpecificationVersion (dep.getVersion ())) >= 0;
}
// All other usages unlikely
return true;
}
public static boolean matchPackageDependency (Dependency dep) {
if (dep.getName().equals("javafx.application[Application]")) {
File javaHome = new File(System.getProperty("java.home"));
return
new File(new File(javaHome, "lib"), "jfxrt.jar").exists() ||
new File(new File(new File(javaHome, "lib"), "ext"), "jfxrt.jar").exists();
} else {
return Util.checkPackageDependency (dep, Util.class.getClassLoader());
}
}
static boolean checkDependencyModuleAllowEqual (Dependency dep, ModuleInfo module) {
return checkDependencyModule (dep, module, true);
}
static boolean checkDependencyModule (Dependency dep, ModuleInfo module) {
return checkDependencyModule (dep, module, false);
}
private static boolean checkDependencyModule (Dependency dep, ModuleInfo module, boolean allowEqual) {
boolean ok = false;
if (dep.getName ().equals (module.getCodeNameBase ()) || dep.getName ().equals (module.getCodeName ())) {
if (dep.getComparison () == Dependency.COMPARE_ANY) {
ok = true;
} else if (dep.getComparison () == Dependency.COMPARE_SPEC) {
if (module.getSpecificationVersion () == null) {
ok = false;
} else if (new SpecificationVersion (dep.getVersion ()).compareTo (module.getSpecificationVersion ()) > 0) {
ok = false;
} else if (allowEqual && new SpecificationVersion (dep.getVersion ()).compareTo (module.getSpecificationVersion ()) == 0) {
ok = true;
} else {
ok = true;
}
} else {
// COMPARE_IMPL
if (module.getImplementationVersion () == null) {
ok = false;
} else if (! module.getImplementationVersion ().equals (dep.getVersion ())) {
ok = false;
} else if (dep.getName ().indexOf ('/') == -1 || module.getCodeName().indexOf('/') !=-1) { // NOI18N
//COMPARE_IMPL with implicit release version specified - see Issue #177737
if(dep.getName ().equals (module.getCodeName ())) {
//release version specified in both dependency and module codename, and the same - since different release versions are handled separately
ok = true;
} else {
ok = false;
}
} else {
ok = true;
}
}
} else {
int dash = dep.getName ().indexOf ('-'); // NOI18N
if (dash != -1) {
// Ranged major release version, cf. #19714.
int slash = dep.getName ().indexOf ('/'); // NOI18N
String cnb = dep.getName ().substring (0, slash);
int relMin = Integer.parseInt (dep.getName ().substring (slash + 1, dash));
int relMax = Integer.parseInt (dep.getName ().substring (dash + 1));
if (cnb.equals (module.getCodeNameBase ()) &&
relMin <= module.getCodeNameRelease () &&
relMax >= module.getCodeNameRelease ()) {
if (dep.getComparison () == Dependency.COMPARE_ANY) {
ok = true;
} else {
// COMPARE_SPEC; COMPARE_IMPL not allowed here
if (module.getCodeNameRelease () > relMin) {
// Great, skip the spec version.
ok = true;
} else {
// As usual.
if (module.getSpecificationVersion () == null) {
ok = false;
} else if (new SpecificationVersion (dep.getVersion ()).compareTo (module.getSpecificationVersion ()) > 0) {
ok = false;
} else if (allowEqual && new SpecificationVersion (dep.getVersion ()).compareTo (module.getSpecificationVersion ()) > 0) {
ok = true;
} else {
ok = true;
}
}
}
}
} else if (dep.getName ().indexOf ('/') != -1) { // NOI18N
// MAJOR RELEASE
String cnb = dep.getName ().substring (0, dep.getName ().indexOf ('/')); // NOI18N
if (cnb.equals (module.getCodeNameBase ())) {
err.log (Level.FINE, "Unmatched major versions. Dependency " + dep + " doesn't match with module " + module);
ok = false;
}
}
}
return ok;
}
}