blob: d832e56f52ce2aa6446daa5c1d2690655f2e59c6 [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.core.startup;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.netbeans.InvalidException;
import org.netbeans.Module;
import org.openide.modules.Dependency;
import org.openide.modules.SpecificationVersion;
import org.openide.util.BaseUtilities;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
import org.openide.util.TopologicalSortException;
// XXX public for reflection from contrib/modulemanager; should be changed to friend dep!
/**
* Utility class to provide localized messages explaining problems
* that modules had during attempted installation.
* Used by both {@link org.netbeans.core.startup.NbEvents} and autoupdate's ModuleBean.
* @author Jesse Glick
* @see "#16636"
*/
public final class NbProblemDisplayer {
private NbProblemDisplayer() {}
/**
* Provide a localized explanation of some installation problem.
* Problem may be either an InvalidException or a Dependency.
* Structure of message can assume that the module failing its
* dependencies is already being displayed, and concentrate
* on the problem.
* @param m the module which cannot be installed
* @param problem either an {@link InvalidException} or {@link Dependency} as returned from {@link Module#getProblems}
* @return an explanation of the problem in the most human-friendly format available
*/
// XXX only exists for reflective calls
public static String messageForProblem(Module m, Object problem) {
return messageForProblem(m, problem, true);
}
/**
* @param localized true to use display names, false to use code name bases
*/
static String messageForProblem(Module m, Object problem, boolean localized) {
if (problem instanceof InvalidException) {
String loc = Exceptions.findLocalizedMessage((InvalidException) problem);
return loc != null ? loc : problem.toString();
} else {
Dependency dep = (Dependency)problem;
switch (dep.getType()) {
case Dependency.TYPE_MODULE:
String polite = (String)m.getLocalizedAttribute("OpenIDE-Module-Module-Dependency-Message"); // NOI18N
if (polite != null) {
return polite;
} else {
String name = dep.getName();
// Find code name base:
int idx = name.lastIndexOf('/');
if (idx != -1) {
name = name.substring(0, idx);
}
Module other = m.getManager().get(name);
if (other != null && other.getCodeName().equals(dep.getName())) {
switch (dep.getComparison()) {
case Dependency.COMPARE_ANY:
// Just disabled (probably had its own problems).
return NbBundle.getMessage(NbProblemDisplayer.class, "MSG_problem_other_disabled", label(other, localized));
case Dependency.COMPARE_IMPL:
String requestedI = dep.getVersion();
String actualI = (other.getImplementationVersion() != null) ?
other.getImplementationVersion() :
NbBundle.getMessage(NbProblemDisplayer.class, "LBL_no_impl_version");
if (requestedI.equals(actualI)) {
// Just disabled (probably had its own problems).
return NbBundle.getMessage(NbProblemDisplayer.class, "MSG_problem_other_disabled", label(other, localized));
} else {
// Wrong version.
return NbBundle.getMessage(NbProblemDisplayer.class, "MSG_problem_other_wrong_version", label(other, localized), requestedI, actualI);
}
case Dependency.COMPARE_SPEC:
SpecificationVersion requestedS = new SpecificationVersion(dep.getVersion());
SpecificationVersion actualS = (other.getSpecificationVersion() != null) ?
other.getSpecificationVersion() :
new SpecificationVersion("0"); // NOI18N
if (actualS.compareTo(requestedS) >= 0) {
// Just disabled (probably had its own problems).
return NbBundle.getMessage(NbProblemDisplayer.class, "MSG_problem_other_disabled", label(other, localized));
} else {
// Too old.
return NbBundle.getMessage(NbProblemDisplayer.class, "MSG_problem_other_too_old", label(other, localized), requestedS, actualS);
}
default:
throw new IllegalStateException();
}
} else {
// Keep the release version info in this case.
// XXX would be nice to have a special message for mismatched major release
// version - i.e. other != null
return NbBundle.getMessage(NbProblemDisplayer.class, "MSG_problem_other_needed_not_found", dep.getName());
}
}
case Dependency.TYPE_REQUIRES:
case Dependency.TYPE_NEEDS:
polite = (String)m.getLocalizedAttribute("OpenIDE-Module-Requires-Message"); // NOI18N
if (polite != null) {
return polite;
} else {
for (Module other : m.getManager().getModules()) {
if (other.provides(dep.getName())) {
return NbBundle.getMessage(NbProblemDisplayer.class, "MSG_problem_require_disabled", dep.getName());
}
}
return NbBundle.getMessage(NbProblemDisplayer.class, "MSG_problem_require_not_found", dep.getName());
}
case Dependency.TYPE_PACKAGE:
polite = (String)m.getLocalizedAttribute("OpenIDE-Module-Package-Dependency-Message"); // NOI18N
if (polite != null) {
return polite;
} else {
String name = dep.getName();
// Find package name or qualified name of probe class:
int idx = name.lastIndexOf('[');
if (idx == 0) {
// Probed class. [javax.television.Antenna]
return NbBundle.getMessage(NbProblemDisplayer.class, "MSG_problem_class_not_loaded", name.substring(1, name.length() - 1));
} else if (idx != -1) {
// Package plus sample class. javax.television[Antenna]
return NbBundle.getMessage(NbProblemDisplayer.class, "MSG_problem_package_not_loaded_or_old", name.substring(0, idx));
} else {
return NbBundle.getMessage(NbProblemDisplayer.class, "MSG_problem_package_not_loaded_or_old", name);
}
}
case Dependency.TYPE_JAVA:
// XXX would OpenIDE-Module-Java-Dependency-Message be useful?
if (dep.getName().equals(Dependency.JAVA_NAME) && dep.getComparison() == Dependency.COMPARE_SPEC) {
return NbBundle.getMessage(NbProblemDisplayer.class, "MSG_problem_java_too_old", dep.getVersion(), Dependency.JAVA_SPEC);
} else {
// All other usages unlikely, don't bother making pretty.
return dep.toString();
}
default:
throw new IllegalArgumentException(dep.toString());
}
}
}
private static String label(Module m, boolean localized) {
if (localized) {
return m.getDisplayName();
} else {
return m.getCodeNameBase();
}
}
static void problemMessagesForModules(final Appendable writeTo, Collection<? extends Module> modules, final boolean justRootCause) {
try {
HashSet<String> names = new HashSet<String>();
for (Module m : modules) {
names.add(m.getCodeName());
}
HashSet<String> dependentModules = new HashSet<String>();
class Report {
final Module m;
final List<Object> problems = new ArrayList<Object>();
Report(Module m) {
this.m = m;
}
void write() throws IOException {
for (Object problem : problems) {
writeTo.append("\n\t").append(label(m, justRootCause) + " - " + NbProblemDisplayer.messageForProblem(m, problem, justRootCause));
}
}
}
Map<Module,Report> reports = new HashMap<Module,Report>();
Map<Module,Collection<Module>> edges = new HashMap<Module,Collection<Module>>();
for (Module m : modules) {
Set<Object> problems = m.getProblems();
if (problems.isEmpty()) {
throw new IllegalStateException("Module " + m + " could not be installed but had no problems"); // NOI18N
}
Report r = new Report(m);
for (Object problem : problems) {
if (problem instanceof Dependency && justRootCause) {
Dependency d = (Dependency) problem;
if (d.getType() == Dependency.TYPE_MODULE && names.contains(d.getName())) {
dependentModules.add(m.getCodeName());
continue;
}
}
r.problems.add(problem);
}
reports.put(m, r);
edges.put(m, m.getManager().getModuleInterdependencies(m, true, false, false));
}
try {
for (Module m : BaseUtilities.topologicalSort(edges.keySet(), edges)) {
reports.get(m).write();
}
} catch (TopologicalSortException x) {
for (Report r : reports.values()) {
r.write();
}
}
if (!dependentModules.isEmpty()) {
writeTo.append("\n\t").append(NbBundle.getMessage(NbProblemDisplayer.class, "MSG_also_dep_modules", dependentModules.size()));
}
} catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
}