blob: 9f39b5c815403c1fdaf2eb05f53eb1c19f06681f [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.java.project.ui;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.net.URI;
import java.net.URL;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.annotations.common.NullAllowed;
import org.netbeans.api.java.platform.JavaPlatform;
import org.netbeans.api.java.platform.JavaPlatformManager;
import org.netbeans.api.java.platform.Specification;
import org.netbeans.api.project.FileOwnerQuery;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ProjectManager;
import org.netbeans.api.project.ProjectUtils;
import org.netbeans.api.project.libraries.LibrariesCustomizer;
import org.netbeans.api.project.libraries.Library;
import org.netbeans.api.project.libraries.LibraryManager;
import org.netbeans.spi.project.libraries.support.LibrariesSupport;
import org.netbeans.spi.project.support.ant.AntProjectHelper;
import org.netbeans.spi.project.support.ant.EditableProperties;
import org.netbeans.spi.project.support.ant.PropertyEvaluator;
import org.netbeans.spi.project.support.ant.PropertyUtils;
import org.netbeans.spi.project.support.ant.ReferenceHelper;
import org.netbeans.spi.project.ui.ProjectProblemsProvider;
import org.openide.filesystems.FileObject;
import org.openide.util.NbBundle;
import org.openide.util.Parameters;
import static org.netbeans.modules.java.project.ui.Bundle.*;
import org.netbeans.spi.java.project.support.ProjectPlatform;
import org.netbeans.spi.java.project.support.ui.BrokenReferencesSupport;
import org.netbeans.spi.project.support.ant.ui.VariablesSupport;
import org.netbeans.spi.project.ui.ProjectProblemResolver;
import org.netbeans.spi.project.ui.ProjectProblemsProvider.Result;
import org.netbeans.spi.project.ui.support.ProjectChooser;
import org.netbeans.spi.project.ui.support.ProjectProblemsProviderSupport;
import org.openide.DialogDescriptor;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.awt.Mnemonics;
import org.openide.filesystems.FileAttributeEvent;
import org.openide.filesystems.FileChangeListener;
import org.openide.filesystems.FileEvent;
import org.openide.filesystems.FileRenameEvent;
import org.openide.filesystems.FileUtil;
import org.openide.modules.SpecificationVersion;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.Mutex;
import org.openide.util.RequestProcessor;
import org.openide.util.Utilities;
import org.openide.util.WeakListeners;
/**
*
* @author Tomas Zezula
*/
public class ProjectProblemsProviders {
static final String PLAT_PROP_ANT_NAME = "platform.ant.name"; //NOI18N
private static final Logger LOG = Logger.getLogger(ProjectProblemsProviders.class.getName());
private static final RequestProcessor RP = new RequestProcessor(ProjectProblemsProviders.class);
private ProjectProblemsProviders() {
throw new IllegalStateException(String.format("The %s cannot be instantiated.",this.getClass().getName())); //NOI18N
}
@NonNull
public static ProjectProblemsProvider createReferenceProblemProvider(
@NonNull final AntProjectHelper projectHelper,
@NonNull final ReferenceHelper referenceHelper,
@NonNull final PropertyEvaluator evaluator,
@NullAllowed final BrokenReferencesSupport.PlatformUpdatedCallBack callback,
@NonNull final String[] properties,
@NonNull final String[] platformProperties) {
final ReferenceProblemProviderImpl pp = new ReferenceProblemProviderImpl(
projectHelper,
evaluator,
referenceHelper,
callback,
properties,
platformProperties);
pp.attachListeners();
return pp;
}
@NonNull
public static ProjectProblemsProvider createPlatformVersionProblemProvider(
@NonNull final AntProjectHelper helper,
@NonNull final PropertyEvaluator evaluator,
@NullAllowed final BrokenReferencesSupport.PlatformUpdatedCallBack hook,
@NonNull final String platformType,
@NonNull final SpecificationVersion minimalVersion,
@NonNull final String platformProperty,
@NonNull final String... platformVersionProperties) {
final PlatformVersionProblemProviderImpl pp = new PlatformVersionProblemProviderImpl(
helper,
evaluator,
hook,
platformType,
minimalVersion,
platformProperty,
platformVersionProperties);
pp.attachListeners();
return pp;
}
@NonNull
public static ProjectProblemsProvider createProfileProblemProvider(
@NonNull final AntProjectHelper antProjectHelper,
@NonNull final ReferenceHelper refHelper,
@NonNull final PropertyEvaluator evaluator,
@NonNull final String profileProperty,
@NonNull final String... classPathProperties) {
return new ProfileProblemsProviderImpl(
antProjectHelper,
refHelper,
evaluator,
profileProperty,
classPathProperties);
}
@NonNull
static Future<ProjectProblemsProvider.Result> future(@NullAllowed final ProjectProblemsProvider.Result result) {
return new Done(result);
}
//<editor-fold defaultstate="collapsed" desc="Helper Methods & Types">
@NonNull
private static Set<? extends ProjectProblemsProvider.ProjectProblem> getReferenceProblems(
@NullAllowed final AntProjectHelper helper,
@NullAllowed final PropertyEvaluator evaluator,
@NullAllowed final ReferenceHelper refHelper,
@NonNull final String[] ps,
@NullAllowed final Collection<? super File> files,
final boolean abortAfterFirstProblem) {
Set<ProjectProblemsProvider.ProjectProblem> set = new LinkedHashSet<ProjectProblemsProvider.ProjectProblem>();
StringBuilder all = new StringBuilder();
// this call waits for list of libraries to be refreshhed
LibraryManager.getDefault().getLibraries();
if (helper == null || evaluator == null || refHelper == null) {
return set;
}
final Queue<FileResolver> fileReoslvers = new ArrayDeque<FileResolver>();
EditableProperties ep = helper.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
for (String p : ps) {
// evaluate given property and tokenize it
String prop = evaluator.getProperty(p);
if (prop == null) {
continue;
}
LOG.log(Level.FINE, "Evaluated {0}={1}", new Object[] {p, prop});
String[] vals = PropertyUtils.tokenizePath(prop);
// no check whether after evaluating there are still some
// references which could not be evaluated
for (String v : vals) {
// we are checking only: project reference, file reference, library reference
if (!(v.startsWith("${file.reference.") ||
v.startsWith("${project.") ||
(v.startsWith("${libs.") && v.endsWith(".classpath}")) ||
v.startsWith("${var."))) { // NOI18N
all.append(v);
continue;
}
if (v.startsWith("${project.")) { // NOI18N
// something in the form: "${project.<projID>}/dist/foo.jar"
String val = v.substring(2, v.indexOf('}')); // NOI18N
set.add(
ProjectProblemsProvider.ProjectProblem.createError(
getDisplayName(RefType.PROJECT, val),
getDescription(RefType.PROJECT, val),
new ProjectResolver(val, helper)));
} else {
final String val = v.substring(2, v.length() - 1);
final ProjectProblemsProvider.ProjectProblem problem;
if (v.startsWith("${file.reference")) { // NOI18N
final FileResolver fr = new FileResolver(val, helper, fileReoslvers);
fileReoslvers.offer(fr);
problem = ProjectProblemsProvider.ProjectProblem.createError(
getDisplayName(RefType.FILE, val),
getDescription(RefType.FILE, val),
fr);
} else if (v.startsWith("${var")) { // NOI18N
problem = ProjectProblemsProvider.ProjectProblem.createError(
getDisplayName(RefType.VARIABLE, v),
getDescription(RefType.VARIABLE, v),
new VariableResolver(RefType.VARIABLE, v));
} else {
// Since 8.1, "junit" library definition and junit-3.8.2 binaries
// were removed. Project problems are handled now from class
// org.netbeans.modules.junit.ant.ui.JUnitProjectOpenedHook.
if (val.equals("libs.junit.classpath")) {
continue;
}
problem = ProjectProblemsProvider.ProjectProblem.createError(
getDisplayName(RefType.LIBRARY, val),
getDescription(RefType.LIBRARY, val),
new LibraryResolver(RefType.LIBRARY, val, refHelper));
}
set.add(problem);
}
if (abortAfterFirstProblem) {
break;
}
}
if (set.size() > 0 && abortAfterFirstProblem) {
break;
}
// test that resolved variable based property points to an existing file
String path = ep.getProperty(p);
if (path != null) {
for (String v : PropertyUtils.tokenizePath(path)) {
if (v.startsWith("${file.reference.")) { //NOI18N
v = ep.getProperty(v.substring(2, v.length() - 1));
}
if (v != null && v.startsWith("${var.")) { //NOI18N
String value = evaluator.evaluate(v);
if (value.startsWith("${var.")) { // NOI18N
// this problem was already reported
continue;
}
File f = getFile(helper, evaluator, value);
if (files != null) {
files.add(f);
}
if (f.exists()) {
continue;
}
set.add(
ProjectProblemsProvider.ProjectProblem.createError(
getDisplayName(RefType.VARIABLE_CONTENT, v),
getDescription(RefType.VARIABLE_CONTENT, v),
new VariableResolver(RefType.VARIABLE_CONTENT, v)));
}
}
}
}
// Check also that all referenced project really exist and are reachable.
// If they are not report them as broken reference.
// XXX: there will be API in PropertyUtils for listing of Ant
// prop names in String. Consider using it here.
final Map<String, String> entries = evaluator.getProperties();
if (entries == null) {
throw new IllegalArgumentException("Properies mapping could not be computed (e.g. due to a circular definition). Evaluator: "+evaluator.toString()); //NOI18N
}
for (Map.Entry<String, String> entry : entries.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (key.startsWith("project.")) { // NOI18N
if ("project.license".equals(key)) { //NOI18N
continue;
}
File f = getFile(helper, evaluator, value);
if (files != null) {
files.add(f);
}
if (f.exists()) {
continue;
}
// Check that the value is really used by some property.
// If it is not then ignore such a project.
if (all.indexOf(value) == -1) {
continue;
}
set.add(
ProjectProblemsProvider.ProjectProblem.createError(
getDisplayName(RefType.PROJECT, key),
getDescription(RefType.PROJECT, key),
new ProjectResolver(key, helper)));
}
else if (key.startsWith("file.reference")) { //NOI18N
File f = getFile(helper, evaluator, value);
if (files != null) {
files.add(f);
}
String unevaluatedValue = ep.getProperty(key);
boolean alreadyChecked = unevaluatedValue != null ? unevaluatedValue.startsWith("${var.") : false; // NOI18N
if (f.exists() || all.indexOf(value) == -1 || alreadyChecked) { // NOI18N
continue;
}
final FileResolver fr = new FileResolver(key, helper, fileReoslvers);
fileReoslvers.offer(fr);
set.add(
ProjectProblemsProvider.ProjectProblem.createError(
getDisplayName(RefType.FILE, key),
getDescription(RefType.FILE, key),
fr));
}
}
//Check for libbraries with broken classpath content
Set<String> usedLibraries = new HashSet<String>();
Pattern libPattern = Pattern.compile("\\$\\{(libs\\.[-._a-zA-Z0-9]+\\.classpath)\\}"); //NOI18N
for (String p : ps) {
String propertyValue = ep.getProperty(p);
if (propertyValue != null) {
for (String v : PropertyUtils.tokenizePath(propertyValue)) {
Matcher m = libPattern.matcher(v);
if (m.matches()) {
usedLibraries.add (m.group(1));
}
}
}
}
for (String libraryRef : usedLibraries) {
String libraryName = libraryRef.substring(5,libraryRef.length()-10);
Library lib = refHelper.findLibrary(libraryName);
if (lib == null) {
// Since 8.1, "junit" library definition and junit-3.8.2 binaries
// were removed. Project problems are handled now from class
// org.netbeans.modules.junit.ant.ui.JUnitProjectOpenedHook.
if(libraryName.equals("junit")) {
continue;
}
// Should already have been caught before?
set.add(
ProjectProblemsProvider.ProjectProblem.createError(
getDisplayName(RefType.LIBRARY, libraryRef),
getDescription(RefType.LIBRARY, libraryRef),
new LibraryResolver(RefType.LIBRARY, libraryRef, refHelper)));
}
else {
//XXX: Should check all the volumes (sources, javadoc, ...)?
for (URI uri : lib.getURIContent("classpath")) { // NOI18N
URI uri2 = LibrariesSupport.getArchiveFile(uri);
if (uri2 == null) {
uri2 = uri;
}
FileObject fo = LibrariesSupport.resolveLibraryEntryFileObject(lib.getManager().getLocation(), uri2);
if (null == fo && !canResolveEvaluatedUri(helper.getStandardPropertyEvaluator(), lib.getManager().getLocation(), uri2)) {
set.add(
ProjectProblemsProvider.ProjectProblem.createError(
getDisplayName(RefType.LIBRARY_CONTENT, libraryRef),
getDescription(RefType.LIBRARY_CONTENT, libraryRef),
new LibraryResolver(RefType.LIBRARY_CONTENT, libraryRef, refHelper)));
break;
}
}
}
}
return set;
}
@NonNull
private static Set<ProjectProblemsProvider.ProjectProblem> getPlatformProblems(
@NullAllowed final PropertyEvaluator evaluator,
@NonNull final AntProjectHelper helper,
@NullAllowed final BrokenReferencesSupport.PlatformUpdatedCallBack callback,
@NonNull final String[] platformProperties,
boolean abortAfterFirstProblem) {
final Set<ProjectProblemsProvider.ProjectProblem> set = new LinkedHashSet<>();
if (evaluator == null) {
return set;
}
for (String pprop : platformProperties) {
String prop = evaluator.getProperty(pprop);
if (prop == null) {
continue;
}
if (!existPlatform(
FileOwnerQuery.getOwner(helper.getProjectDirectory()),
evaluator,
prop)) {
// XXX: the J2ME stores in project.properties also platform
// display name and so show this display name instead of just
// prop ID if available.
if (evaluator.getProperty(pprop + ".description") != null) { // NOI18N
prop = evaluator.getProperty(pprop + ".description"); // NOI18N
}
set.add(
ProjectProblemsProvider.ProjectProblem.createError(
getDisplayName(RefType.PLATFORM, prop),
getDescription(RefType.PLATFORM, prop),
new PlatformResolver(prop, pprop, null, evaluator, helper, callback)));
}
if (set.size() > 0 && abortAfterFirstProblem) {
break;
}
}
return set;
}
private static File getFile (AntProjectHelper helper, PropertyEvaluator evaluator, String name) {
if (helper != null) {
return new File(helper.resolvePath(name));
} else {
File f = new File(name);
if (!f.exists()) {
// perhaps the file is relative?
String basedir = evaluator.getProperty("basedir"); // NOI18N
assert basedir != null;
f = new File(new File(basedir), name);
}
return f;
}
}
/** Tests whether evaluated URI can be resolved. To support library entry
* like "${MAVEN_REPO}/struts/struts.jar".
*/
private static boolean canResolveEvaluatedUri(PropertyEvaluator eval, URL libBase, URI libUri) {
if (libUri.isAbsolute()) {
return false;
}
String path = LibrariesSupport.convertURIToFilePath(libUri);
String newPath = eval.evaluate(path);
if (newPath.equals(path)) {
return false;
}
URI newUri = LibrariesSupport.convertFilePathToURI(newPath);
return null != LibrariesSupport.resolveLibraryEntryFileObject(libBase, newUri);
}
private static boolean existPlatform(
@NullAllowed final Project prj,
@NonNull final PropertyEvaluator eval,
@NonNull final String platform) {
if (platform.equals("default_platform")) { // NOI18N
return true;
}
for (JavaPlatform plat : JavaPlatformManager.getDefault().getInstalledPlatforms()) {
// XXX: this should be defined as PROPERTY somewhere
if (platform.equals(plat.getProperties().get(PLAT_PROP_ANT_NAME))) {
return plat.isValid();
}
}
return prj == null ?
false :
ProjectPlatform.forProject(prj, eval, "j2se") != null; //NOI18N //Todo: custom platform type?
}
@NonNull
@NbBundle.Messages({
"LBL_BrokenLinksCustomizer_BrokenLibrary=\"{0}\" library could not be found",
"LBL_BrokenLinksCustomizer_BrokenDefinableLibrary=\"{0}\" library must be defined",
"LBL_BrokenLinksCustomizer_BrokenLibraryContent=\"{0}\" library has missing items",
"LBL_BrokenLinksCustomizer_BrokenProjectReference=\"{0}\" project could not be found",
"LBL_BrokenLinksCustomizer_BrokenFileReference=\"{0}\" file/folder could not be found",
"LBL_BrokenLinksCustomizer_BrokenVariable=\"{0}\" variable could not be found",
"LBL_BrokenLinksCustomizer_BrokenVariableContent=\"{0}\" variable based file/folder could not be found",
"LBL_BrokenLinksCustomizer_BrokenPlatform=\"{0}\" platform could not be found"
})
private static String getDisplayName(
@NonNull final RefType type,
@NonNull final String id) {
switch (type) {
case LIBRARY:
return LBL_BrokenLinksCustomizer_BrokenLibrary(getDisplayId(type, id));
case DEFINABLE_LIBRARY:
return LBL_BrokenLinksCustomizer_BrokenDefinableLibrary(getDisplayId(type, id));
case LIBRARY_CONTENT:
return LBL_BrokenLinksCustomizer_BrokenLibraryContent(getDisplayId(type, id));
case PROJECT:
return LBL_BrokenLinksCustomizer_BrokenProjectReference(getDisplayId(type, id));
case FILE:
return LBL_BrokenLinksCustomizer_BrokenFileReference(getDisplayId(type, id));
case PLATFORM:
return LBL_BrokenLinksCustomizer_BrokenPlatform(getDisplayId(type, id));
case VARIABLE:
return LBL_BrokenLinksCustomizer_BrokenVariable(getDisplayId(type, id));
case VARIABLE_CONTENT:
return LBL_BrokenLinksCustomizer_BrokenVariableContent(getDisplayId(type, id));
default:
assert false;
return id;
}
}
@NbBundle.Messages({
"LBL_BrokenLinksCustomizer_BrokenLibraryDesc=Problem: The project uses a class library called \"{0}\", but this class library was not found.\nSolution: Click Resolve to open the Library Manager and create a new class library called \"{0}\".",
"LBL_BrokenLinksCustomizer_BrokenDefinableLibraryDesc=Problem: The project uses a class library called \"{0}\", but this class library is not currently defined locally.\nSolution: Click Resolve to download or otherwise automatically define this library.",
"LBL_BrokenLinksCustomizer_BrokenLibraryContentDesc=Problem: The project uses the class library called \"{0}\" but the classpath items of this library are missing.\nSolution: Click Resolve to open the Library Manager and locate the missing classpath items of \"{0}\" library.",
"LBL_BrokenLinksCustomizer_BrokenProjectReferenceDesc=Problem: The project classpath includes a reference to the project called \"{0}\", but this project was not found.\nSolution: Click Resolve and locate the missing project.",
"LBL_BrokenLinksCustomizer_BrokenFileReferenceDesc=Problem: The project uses the file/folder called \"{0}\", but this file/folder was not found.\nSolution: Click Resolve and locate the missing file/folder.",
"LBL_BrokenLinksCustomizer_BrokenVariableReferenceDesc=Problem: The project uses the variable called \"{0}\", but this variable was not found.\nSolution: Click Resolve and setup this variable there.",
"LBL_BrokenLinksCustomizer_BrokenVariableContentDesc=Problem: The project uses the variable based file/folder \"{0}\", but this file/folder was not found.\nSolution: Click Resolve and update your variable to point to correct location.",
"LBL_BrokenLinksCustomizer_BrokenPlatformDesc=Problem: The project uses the Java Platform called \"{0}\", but this platform was not found.\nSolution: Click Resolve and create new platform called \"{0}\"."
})
private static String getDescription(
@NonNull final RefType type,
@NonNull final String id
) {
switch (type) {
case LIBRARY:
return LBL_BrokenLinksCustomizer_BrokenLibraryDesc(getDisplayId(type, id));
case DEFINABLE_LIBRARY:
return LBL_BrokenLinksCustomizer_BrokenDefinableLibraryDesc(getDisplayId(type, id));
case LIBRARY_CONTENT:
return LBL_BrokenLinksCustomizer_BrokenLibraryContentDesc(getDisplayId(type, id));
case PROJECT:
return LBL_BrokenLinksCustomizer_BrokenProjectReferenceDesc(getDisplayId(type, id));
case FILE:
return LBL_BrokenLinksCustomizer_BrokenFileReferenceDesc(getDisplayId(type, id));
case PLATFORM:
return LBL_BrokenLinksCustomizer_BrokenPlatformDesc(getDisplayId(type, id));
case VARIABLE:
return LBL_BrokenLinksCustomizer_BrokenVariableReferenceDesc(getDisplayId(type, id));
case VARIABLE_CONTENT:
return LBL_BrokenLinksCustomizer_BrokenVariableContent(getDisplayId(type, id));
default:
assert false;
return id;
}
}
private static String getDisplayId(
@NonNull final RefType type,
@NonNull final String id) {
switch (type) {
case LIBRARY:
case DEFINABLE_LIBRARY:
case LIBRARY_CONTENT:
// libs.<name>.classpath
return id.substring(5, id.length()-10);
case PROJECT:
// project.<name>
return id.substring(8);
case FILE:
// file.reference.<name>
return id.substring(15);
case PLATFORM:
return id;
case VARIABLE:
return id.substring(6, id.indexOf('}')); // NOI18N
case VARIABLE_CONTENT:
return id.substring(6, id.indexOf('}')) + id.substring(id.indexOf('}')+1); // NOI18N
default:
assert false;
return id;
}
}
private enum RefType {
PROJECT,
FILE,
PLATFORM,
LIBRARY,
DEFINABLE_LIBRARY,
LIBRARY_CONTENT,
VARIABLE,
VARIABLE_CONTENT,
}
//</editor-fold>
//<editor-fold defaultstate="collapsed" desc="Resolver implementations">
private static abstract class BaseResolver implements ProjectProblemResolver {
protected final RefType type;
protected final String id;
BaseResolver(
@NonNull final RefType type,
@NonNull final String id) {
Parameters.notNull("type", type); //NOI18N
Parameters.notNull("id", id); //NOI18N
this.type = type;
this.id = id;
}
@Override
public final int hashCode() {
int result = 17;
result = 31 * result + type.hashCode();
result = 31 * result + id.hashCode();
return result;
}
@Override
public final boolean equals(@NullAllowed final Object other) {
if (!(other instanceof BaseResolver)) {
return false;
}
final BaseResolver otherResolver = (BaseResolver) other;
return type.equals(otherResolver.type) &&
id.equals(otherResolver.id);
}
@Override
public String toString() {
return String.format(
"Resolver for %s %s", //NOI18N
type,
id);
}
}
private static class PlatformResolver extends BaseResolver {
private final String propertyName;
private final String platformType;
private final PropertyEvaluator eval;
private final AntProjectHelper helper;
private final BrokenReferencesSupport.PlatformUpdatedCallBack callback;
PlatformResolver(
@NonNull final String id,
@NonNull final String propertyName,
@NullAllowed final String platformType,
@NonNull final PropertyEvaluator eval,
@NonNull final AntProjectHelper helper,
@NullAllowed final BrokenReferencesSupport.PlatformUpdatedCallBack callback) {
super(RefType.PLATFORM, id);
Parameters.notNull("propertyName", propertyName); //NOI18N
Parameters.notNull("eval", eval); //NOI18N
Parameters.notNull("helper", helper); //NOI18N
this.propertyName = propertyName;
this.platformType = platformType;
this.eval = eval;
this.helper = helper;
this.callback = callback;
}
@Override
@NonNull
@NbBundle.Messages({
"TXT_FixBrokenPlatform=Resolve Broken Platform",
"LBL_OK=&OK"
})
public Future<ProjectProblemsProvider.Result> resolve() {
final JButton ok = new JButton();
Mnemonics.setLocalizedText(ok, LBL_OK());
final FixPlatform fixPlatform = new FixPlatform(
propertyName,
id,
platformType,
eval,
helper,
callback,
ok);
final DialogDescriptor dd = new DialogDescriptor(
fixPlatform,
TXT_FixBrokenPlatform(),
true,
new Object[] {
ok,
DialogDescriptor.CANCEL_OPTION
},
ok,
DialogDescriptor.DEFAULT_ALIGN,
null,
null);
if (DialogDisplayer.getDefault().notify(dd) == ok) {
return fixPlatform.resolve();
} else {
return new Done(ProjectProblemsProvider.Result.create(ProjectProblemsProvider.Status.UNRESOLVED));
}
}
}
private static class LibraryResolver extends BaseResolver {
private final Callable<Library> definer;
private final Reference<ReferenceHelper> refHelper;
LibraryResolver(
@NonNull RefType type,
@NonNull final String id,
@NonNull final ReferenceHelper refHelper) {
this(translate(type, id),id, refHelper);
}
private LibraryResolver(
@NonNull final Object[] typeDefiner/*todo: replace by Pair*/,
@NonNull final String id,
@NonNull final ReferenceHelper refHelper) {
super((RefType)typeDefiner[0],id);
this.definer = (Callable<Library>)typeDefiner[1];
this.refHelper = new WeakReference<ReferenceHelper>(refHelper);
}
@Override
@NonNull
public Future<ProjectProblemsProvider.Result> resolve() {
if (type == RefType.DEFINABLE_LIBRARY) {
return resolveByDefiner();
} else {
return new Done(ProjectProblemsProvider.Result.create(resolveByLibraryManager()));
}
}
private ProjectProblemsProvider.Status resolveByLibraryManager() {
final LibraryManager lm = getProjectLibraryManager();
if (lm == null) {
//Closed and freed project
return ProjectProblemsProvider.Status.UNRESOLVED;
}
LibrariesCustomizer.showCustomizer(null,lm);
return ProjectProblemsProvider.Status.RESOLVED;
}
private Future<ProjectProblemsProvider.Result> resolveByDefiner() {
assert definer != null;
final RunnableFuture<ProjectProblemsProvider.Result> future =
new FutureTask<ProjectProblemsProvider.Result>(
new Callable<ProjectProblemsProvider.Result>() {
@Override
public ProjectProblemsProvider.Result call() throws Exception {
ProjectProblemsProvider.Status result = ProjectProblemsProvider.Status.UNRESOLVED;
try {
Library lib = definer.call();
LOG.log(Level.FINE, "found {0}", lib); //NOI18N
result = ProjectProblemsProvider.Status.RESOLVED;
} catch (Exception x) {
LOG.log(Level.INFO, null, x);
result = resolveByLibraryManager();
}
return ProjectProblemsProvider.Result.create(result);
}
});
RP.post(future);
return future;
}
@CheckForNull
private LibraryManager getProjectLibraryManager() {
final ReferenceHelper rh = refHelper.get();
return rh == null ?
null:
rh.getProjectLibraryManager() != null ?
rh.getProjectLibraryManager():
LibraryManager.getDefault();
}
@NonNull
private static Object[] translate(
@NonNull RefType original,
@NonNull String id) {
Callable<Library> _definer = null;
if (original == RefType.LIBRARY) {
final String name = id.substring(5, id.length() - 10);
for (BrokenReferencesSupport.LibraryDefiner ld : Lookup.getDefault().lookupAll(BrokenReferencesSupport.LibraryDefiner.class)) {
_definer = ld.missingLibrary(name);
if (_definer != null) {
return new Object[] {RefType.DEFINABLE_LIBRARY, _definer};
}
}
}
return new Object[] {original, null};
}
}
private static class VariableResolver extends BaseResolver {
VariableResolver(@NonNull final RefType type, @NonNull final String id) {
super(type, id);
}
@Override
@NonNull
public Future<ProjectProblemsProvider.Result> resolve() {
VariablesSupport.showVariablesCustomizer();
return new Done(ProjectProblemsProvider.Result.create(ProjectProblemsProvider.Status.RESOLVED));
}
}
private static abstract class ReferenceResolver extends BaseResolver {
static File lastSelectedFile;
private final Reference<AntProjectHelper> antProjectHelper;
ReferenceResolver(
@NonNull final RefType type,
@NonNull final String id,
@NonNull final AntProjectHelper antProjectHelper) {
super (type, id);
this.antProjectHelper = new WeakReference<AntProjectHelper>(antProjectHelper);
}
abstract void updateReference(@NonNull final File file);
final void updateReferenceImpl(@NonNull final File file) {
final String reference = id;
final AntProjectHelper helper = antProjectHelper.get();
if (helper == null) {
//Closed and freed project, ignore
return;
}
FileObject myProjDirFO = helper.getProjectDirectory();
final String propertiesFile = AntProjectHelper.PRIVATE_PROPERTIES_PATH;
final String path = file.getAbsolutePath();
Project p;
try {
p = ProjectManager.getDefault().findProject(myProjDirFO);
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
p = null;
}
final Project proj = p;
ProjectManager.mutex().postWriteRequest(new Runnable() {
public @Override void run() {
EditableProperties props = helper.getProperties(propertiesFile);
if (!path.equals(props.getProperty(reference))) {
props.setProperty(reference, path);
helper.putProperties(propertiesFile, props);
}
if (proj != null) {
try {
ProjectManager.getDefault().saveProject(proj);
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
}
});
}
}
private static class ProjectResolver extends ReferenceResolver {
ProjectResolver(@NonNull final String id, @NonNull AntProjectHelper antProjectHelper) {
super (RefType.PROJECT, id, antProjectHelper);
}
@Override
@NonNull
@NbBundle.Messages({
"LBL_BrokenLinksCustomizer_Resolve_Project=Browse Project \"{0}\""
})
public Future<ProjectProblemsProvider.Result> resolve() {
ProjectProblemsProvider.Status result = ProjectProblemsProvider.Status.UNRESOLVED;
final JFileChooser chooser = ProjectChooser.projectChooser();
chooser.setDialogTitle(LBL_BrokenLinksCustomizer_Resolve_Project(getDisplayId(type, id)));
if (lastSelectedFile != null) {
chooser.setSelectedFile(lastSelectedFile);
}
int option = chooser.showOpenDialog(null);
if (option == JFileChooser.APPROVE_OPTION) {
updateReference(chooser.getSelectedFile());
lastSelectedFile = chooser.getSelectedFile();
result = ProjectProblemsProvider.Status.RESOLVED;
}
return new Done(ProjectProblemsProvider.Result.create(result));
}
@Override
void updateReference(@NonNull final File file) {
updateReferenceImpl(file);
}
}
private static class FileResolver extends ReferenceResolver {
private final Queue<? extends FileResolver> peers;
private ProjectProblemsProvider.Status resolved =
ProjectProblemsProvider.Status.UNRESOLVED;
FileResolver(
@NonNull final String id,
@NonNull final AntProjectHelper antProjectHelper,
@NonNull final Queue<? extends FileResolver> peers) {
super(RefType.FILE, id, antProjectHelper);
this.peers = peers;
}
@Override
@NonNull
@NbBundle.Messages({
"LBL_BrokenLinksCustomizer_Resolve_File=Browse \"{0}\""
})
public Future<ProjectProblemsProvider.Result> resolve() {
final JFileChooser chooser = new JFileChooser();
chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
chooser.setDialogTitle(LBL_BrokenLinksCustomizer_Resolve_File(getDisplayId(type, id)));
if (lastSelectedFile != null) {
chooser.setSelectedFile(lastSelectedFile);
}
int option = chooser.showOpenDialog(null);
if (option == JFileChooser.APPROVE_OPTION) {
updateReference(chooser.getSelectedFile());
lastSelectedFile = chooser.getSelectedFile();
resolved = ProjectProblemsProvider.Status.RESOLVED;
}
return new Done(ProjectProblemsProvider.Result.create(resolved));
}
@Override
void updateReference(@NonNull final File file) {
updateReferenceImpl(file);
final File parentFolder = file.getParentFile();
for (FileResolver peer : peers) {
if (this != peer && peer.resolved == ProjectProblemsProvider.Status.UNRESOLVED) {
final File f = new File(parentFolder, getDisplayId(type, id));
if (f.exists()) {
updateReferenceImpl(f);
}
}
}
}
}
private static class SourceTargetResolver implements ProjectProblemResolver {
private final String type;
private final String platformProp;
private final Collection<? extends String> invalidVersionProps;
private final SpecificationVersion minVersion;
private final SpecificationVersion platformVersion;
private final SpecificationVersion minProjectSupportedVersion;
private final Reference<AntProjectHelper> helperRef;
private final BrokenReferencesSupport.PlatformUpdatedCallBack hook;
SourceTargetResolver(
@NonNull final AntProjectHelper helper,
@NullAllowed final BrokenReferencesSupport.PlatformUpdatedCallBack hook,
@NonNull final String type,
@NonNull final String platformProp,
@NonNull final Collection<? extends String> invalidVersionProps,
@NonNull final SpecificationVersion minVersion,
@NonNull final SpecificationVersion platformVersion,
@NonNull final SpecificationVersion minProjectSupportedVersion) {
Parameters.notNull("helper", helper); //NOI18N
Parameters.notNull("type", type); //NOI18N
Parameters.notNull("platformProp", platformProp); //NOI18N
Parameters.notNull("invalidVersionProps", invalidVersionProps); //NOI18N
Parameters.notNull("minVersion", minVersion); //NOI18N
Parameters.notNull("platformVersion", platformVersion); //NOI18N
Parameters.notNull("minProjectSupportedVersion", minProjectSupportedVersion); //NOI18N
this.helperRef = new WeakReference<AntProjectHelper>(helper);
this.hook = hook;
this.type = type;
this.platformProp = platformProp;
this.invalidVersionProps = invalidVersionProps;
this.minVersion = minVersion;
this.platformVersion = platformVersion;
this.minProjectSupportedVersion = minProjectSupportedVersion;
}
@NbBundle.Messages({"LBL_ResolveJDKVersion=Resolve Invalid Java Platform Version - \"{0}\" Project"})
@Override
public Future<Result> resolve() {
final AntProjectHelper helper = helperRef.get();
if (helper != null) {
final Project project = FileOwnerQuery.getOwner(helper.getProjectDirectory());
final FixProjectSourceLevel changeVersion = new FixProjectSourceLevel(type, minVersion, platformVersion, minProjectSupportedVersion);
final DialogDescriptor dd = new DialogDescriptor(changeVersion, LBL_ResolveJDKVersion(ProjectUtils.getInformation(project).getDisplayName()));
if (DialogDisplayer.getDefault().notify(dd) == DialogDescriptor.OK_OPTION) {
final Callable<ProjectProblemsProvider.Result> resultFnc = () -> ProjectManager.mutex().writeAccess((Mutex.ExceptionAction<Result>) () -> {
if (changeVersion.isDowngradeLevel()) {
final EditableProperties props = helper.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
for (String p : invalidVersionProps) {
props.put(p, platformVersion.toString());
}
helper.putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, props);
ProjectManager.getDefault().saveProject(FileOwnerQuery.getOwner(helper.getProjectDirectory()));
return ProjectProblemsProvider.Result.create(ProjectProblemsProvider.Status.RESOLVED);
} else {
final JavaPlatform jp = changeVersion.getSelectedPlatform();
if (jp != null) {
final String antName = jp.getProperties().get(PLAT_PROP_ANT_NAME);
if (antName != null) {
final EditableProperties props = helper.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
props.setProperty(platformProp, antName);
helper.putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, props);
if (hook != null) {
hook.platformPropertyUpdated(jp);
}
ProjectManager.getDefault().saveProject(project);
return ProjectProblemsProvider.Result.create(ProjectProblemsProvider.Status.RESOLVED);
}
}
}
return ProjectProblemsProvider.Result.create(ProjectProblemsProvider.Status.UNRESOLVED);
});
final RunnableFuture<Result> result = new FutureTask<Result>(resultFnc);
RP.post(result);
return result;
}
}
return new Done(
Result.create(ProjectProblemsProvider.Status.UNRESOLVED));
}
@Override
public boolean equals(Object other) {
if (!(other instanceof SourceTargetResolver)) {
return false;
}
return true;
}
@Override
public int hashCode() {
return 17;
}
}
private static class UpgradeSourceTargetResolver implements ProjectProblemResolver {
private final AntProjectHelper helper;
private final Collection<? extends String> invalidVersionProps;
private final SpecificationVersion minSourceVersion;
UpgradeSourceTargetResolver(
@NonNull final AntProjectHelper helper,
@NonNull final Collection<? extends String> invalidVersionProps,
@NonNull final SpecificationVersion minSourceVersion) {
assert helper != null;
assert invalidVersionProps != null;
assert minSourceVersion != null;
this.helper = helper;
this.invalidVersionProps = invalidVersionProps;
this.minSourceVersion = minSourceVersion;
}
@Override
@NbBundle.Messages({
"TITLE_UpgradeSourceLevel=Upgrade Source/Binary Format - \"{0}\" Project",
"MSG_UpgradeSourceLevel=Upgrade the project source/binary format to the minimal supported one ({0})."
})
public Future<Result> resolve() {
final Project project = FileOwnerQuery.getOwner(helper.getProjectDirectory());
final Object option = DialogDisplayer.getDefault().notify(new NotifyDescriptor.Confirmation(
MSG_UpgradeSourceLevel(minSourceVersion),
TITLE_UpgradeSourceLevel(ProjectUtils.getInformation(project).getDisplayName()),
NotifyDescriptor.OK_CANCEL_OPTION,
NotifyDescriptor.QUESTION_MESSAGE));
if (option == NotifyDescriptor.OK_OPTION) {
return RP.submit(new Callable<ProjectProblemsProvider.Result>(){
@Override
@NonNull
public Result call() throws Exception {
return ProjectManager.mutex().writeAccess(new Mutex.Action<ProjectProblemsProvider.Result>() {
@Override
@NonNull
public ProjectProblemsProvider.Result run() {
try {
final EditableProperties ep = helper.getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
for (String prop : invalidVersionProps) {
ep.setProperty(prop, minSourceVersion.toString());
}
helper.putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, ep);
ProjectManager.getDefault().saveProject(project);
return ProjectProblemsProvider.Result.create(ProjectProblemsProvider.Status.RESOLVED);
} catch (IOException ioe) {
return ProjectProblemsProvider.Result.create(ProjectProblemsProvider.Status.UNRESOLVED, ioe.getMessage());
}
}
});
}
});
} else {
return new Done(ProjectProblemsProvider.Result.create(ProjectProblemsProvider.Status.UNRESOLVED));
}
}
}
private static final class Done implements Future<ProjectProblemsProvider.Result> {
private final ProjectProblemsProvider.Result result;
Done(@NonNull final ProjectProblemsProvider.Result result) {
Parameters.notNull("result", result); //NOI18N
this.result = result;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return false;
}
@Override
public boolean isCancelled() {
return false;
}
@Override
public boolean isDone() {
return true;
}
@Override
public ProjectProblemsProvider.Result get() throws InterruptedException, ExecutionException {
return result;
}
@Override
public ProjectProblemsProvider.Result get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return get();
}
}
//</editor-fold>
//<editor-fold defaultstate="collapsed" desc="ProjectProblemsProvider implementations">
private static final class ReferenceProblemProviderImpl implements ProjectProblemsProvider, PropertyChangeListener, FileChangeListener {
private final ProjectProblemsProviderSupport problemsProviderSupport = new ProjectProblemsProviderSupport(this);
private final AtomicBoolean listenersInitialized = new AtomicBoolean();
private final AntProjectHelper helper;
private final PropertyEvaluator eval;
private final ReferenceHelper refHelper;
private final BrokenReferencesSupport.PlatformUpdatedCallBack callback;
private final String[] refProps;
private final String[] platformProps;
//@GuardedBy("this")
private final Set<File> currentFiles;
private Map<URL,Object[]> activeLibManLocs;
ReferenceProblemProviderImpl(
@NonNull final AntProjectHelper helper,
@NonNull final PropertyEvaluator eval,
@NonNull final ReferenceHelper refHelper,
@NullAllowed final BrokenReferencesSupport.PlatformUpdatedCallBack callback,
@NonNull final String[] refProps,
@NonNull final String[] platformProps) {
assert helper != null;
assert eval != null;
assert refHelper != null;
assert refProps != null;
assert platformProps != null;
this.helper = helper;
this.eval = eval;
this.refHelper = refHelper;
this.callback = callback;
this.refProps = Arrays.copyOf(refProps, refProps.length);
this.platformProps = Arrays.copyOf(platformProps, platformProps.length);
this.currentFiles = new HashSet<>();
}
@Override
public void addPropertyChangeListener(@NonNull final PropertyChangeListener listener) {
Parameters.notNull("listener", listener); //NOI18N
problemsProviderSupport.addPropertyChangeListener(listener);
}
@Override
public void removePropertyChangeListener(@NonNull final PropertyChangeListener listener) {
Parameters.notNull("listener", listener); //NOI18N
problemsProviderSupport.removePropertyChangeListener(listener);
}
@Override
public Collection<? extends ProjectProblem> getProblems() {
return problemsProviderSupport.getProblems(new ProjectProblemsProviderSupport.ProblemsCollector() {
@Override
public Collection<? extends ProjectProblemsProvider.ProjectProblem> collectProblems() {
Collection<? extends ProjectProblemsProvider.ProjectProblem> currentProblems = ProjectManager.mutex().readAccess((Mutex.Action<Collection<? extends ProjectProblem>>) () -> {
final Set<ProjectProblem> newProblems = new LinkedHashSet<ProjectProblem>();
final Set<File> allFiles = new HashSet<>();
newProblems.addAll(getReferenceProblems(helper,eval,refHelper,refProps,allFiles,false));
newProblems.addAll(getPlatformProblems(eval, helper, callback, platformProps,false));
updateFileListeners(allFiles);
return Collections.unmodifiableSet(newProblems);
});
return currentProblems;
}
});
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (LibraryManager.PROP_OPEN_LIBRARY_MANAGERS.equals(evt.getPropertyName())) {
addLibraryManagerListener();
}
problemsProviderSupport.fireProblemsChange();
}
@Override
public void fileDataCreated(FileEvent fe) {
problemsProviderSupport.fireProblemsChange();
}
@Override
public void fileFolderCreated(FileEvent fe) {
problemsProviderSupport.fireProblemsChange();
}
@Override
public void fileDeleted(FileEvent fe) {
problemsProviderSupport.fireProblemsChange();
}
@Override
public void fileRenamed(FileRenameEvent fe) {
problemsProviderSupport.fireProblemsChange();
}
@Override
public void fileChanged(FileEvent fe) {
}
@Override
public void fileAttributeChanged(FileAttributeEvent fe) {
}
void attachListeners() {
if (listenersInitialized.compareAndSet(false, true)) {
eval.addPropertyChangeListener(this);
JavaPlatformManager.getDefault().addPropertyChangeListener(WeakListeners.propertyChange(this, JavaPlatformManager.getDefault()));
LibraryManager.addOpenManagersPropertyChangeListener(new OpenManagersWeakListener(this));
addLibraryManagerListener();
} else {
throw new IllegalStateException();
}
}
private synchronized void updateFileListeners(@NonNull final Collection<? extends File> newFiles) {
final Collection<File> toAdd = new HashSet<>(newFiles);
toAdd.removeAll(currentFiles);
final Collection<File> toRemove = new HashSet<>(currentFiles);
toRemove.removeAll(newFiles);
for (File f : toRemove) {
FileUtil.removeFileChangeListener(this, f);
}
for (File f : toAdd) {
FileUtil.addFileChangeListener(this, f);
}
currentFiles.addAll(toAdd);
currentFiles.removeAll(toRemove);
}
private void addLibraryManagerListener() {
final Map<URL,Object[]> oldLMs;
final boolean attachToDefault;
synchronized (this) {
attachToDefault = activeLibManLocs == null;
if (attachToDefault) {
activeLibManLocs = new HashMap<URL,Object[]>();
}
oldLMs = new HashMap<URL,Object[]>(activeLibManLocs);
}
if (attachToDefault) {
final LibraryManager manager = LibraryManager.getDefault();
manager.addPropertyChangeListener(WeakListeners.propertyChange(this, manager));
}
final Collection<? extends LibraryManager> managers = LibraryManager.getOpenManagers();
final Map<URL,LibraryManager> managerByLocation = new HashMap<URL, LibraryManager>();
for (LibraryManager manager : managers) {
final URL url = manager.getLocation();
if (url != null) {
managerByLocation.put(url, manager);
}
}
final HashMap<URL,Object[]> toRemove = new HashMap<URL,Object[]>(oldLMs);
toRemove.keySet().removeAll(managerByLocation.keySet());
for (Object[] pair : toRemove.values()) {
((LibraryManager)pair[0]).removePropertyChangeListener((PropertyChangeListener)pair[1]);
}
managerByLocation.keySet().removeAll(oldLMs.keySet());
final HashMap<URL,Object[]> toAdd = new HashMap<URL,Object[]>();
for (Map.Entry<URL,LibraryManager> e : managerByLocation.entrySet()) {
final LibraryManager manager = e.getValue();
final PropertyChangeListener listener = WeakListeners.propertyChange(this, manager);
manager.addPropertyChangeListener(listener);
toAdd.put(e.getKey(), new Object[] {manager, listener});
}
synchronized (this) {
activeLibManLocs.keySet().removeAll(toRemove.keySet());
activeLibManLocs.putAll(toAdd);
}
}
}
private static final class PlatformVersionProblemProviderImpl implements ProjectProblemsProvider, PropertyChangeListener {
private final ProjectProblemsProviderSupport problemsProviderSupport = new ProjectProblemsProviderSupport(this);
private final AtomicBoolean listenersInitialized = new AtomicBoolean();
private final AntProjectHelper helper;
private final PropertyEvaluator eval;
private final BrokenReferencesSupport.PlatformUpdatedCallBack hook;
private final String platformType;
private final SpecificationVersion minimalVersion;
private final String platformProp;
private final Set<String> versionProps;
PlatformVersionProblemProviderImpl(
@NonNull final AntProjectHelper helper,
@NonNull final PropertyEvaluator eval,
@NullAllowed final BrokenReferencesSupport.PlatformUpdatedCallBack hook,
@NonNull final String platformType,
@NonNull final SpecificationVersion minimalVersion,
@NonNull final String platformProp,
@NonNull final String... versionProps) {
assert helper != null;
assert eval != null;
assert platformType != null;
assert minimalVersion != null;
assert platformProp != null;
assert versionProps != null;
this.helper = helper;
this.eval = eval;
this.hook = hook;
this.platformType = platformType;
this.minimalVersion = minimalVersion;
this.platformProp = platformProp;
this.versionProps = new HashSet<String>(Arrays.asList(versionProps));
}
@Override
public void addPropertyChangeListener(@NonNull final PropertyChangeListener listener) {
Parameters.notNull("listener", listener); //NOI18N
problemsProviderSupport.addPropertyChangeListener(listener);
}
@Override
public void removePropertyChangeListener(@NonNull final PropertyChangeListener listener) {
Parameters.notNull("listener", listener); //NOI18N
problemsProviderSupport.removePropertyChangeListener(listener);
}
@Override
@NbBundle.Messages({
"LBL_Invalid_JDK_Version=Invalid Java Platform Version",
"HINT_Invalid_JDK_Vernsion=The active project platform is an older version than it's required by project source/binary format.",
"LBL_Unsupported_Source=Unsupported source/binary format.",
"HINT_Unsupported_Source=The project source/binary format is older than minimal supported one ({0})."
})
public Collection<? extends ProjectProblem> getProblems() {
return problemsProviderSupport.getProblems(() -> {
Collection<? extends ProjectProblemsProvider.ProjectProblem> currentProblems = ProjectManager.mutex().readAccess((Mutex.Action<Collection<? extends ProjectProblem>>) () -> {
final JavaPlatform activePlatform = getActivePlatform();
final SpecificationVersion platformVersion = activePlatform == null ?
null:
activePlatform.getSpecification().getVersion();
final Collection<String> invalidVersionProps = new ArrayList<>(versionProps.size());
SpecificationVersion minVersion = getInvalidJdkVersion(
platformVersion,
invalidVersionProps);
if (minVersion != null) {
return Collections.singleton(ProjectProblem.createError(
LBL_Invalid_JDK_Version(),
HINT_Invalid_JDK_Vernsion(),
new SourceTargetResolver(
helper,
hook,
platformType,
platformProp,
invalidVersionProps,
minVersion,
platformVersion,
minimalVersion)));
}
invalidVersionProps.clear();
if (getOutdatedJdkVersion(invalidVersionProps, minimalVersion)) {
return Collections.singleton(ProjectProblem.createError(
LBL_Unsupported_Source(),
HINT_Unsupported_Source(minimalVersion),
new UpgradeSourceTargetResolver(
helper,
invalidVersionProps,
minimalVersion)
));
}
return Collections.<ProjectProblem>emptySet();
});
return currentProblems;
});
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
final String propName = evt.getPropertyName();
if (propName == null || platformProp.equals(propName) || versionProps.contains(propName)) {
problemsProviderSupport.fireProblemsChange();
}
}
void attachListeners() {
if (listenersInitialized.compareAndSet(false, true)) {
eval.addPropertyChangeListener(this);
} else {
throw new IllegalStateException();
}
}
/**
* Gets minimal required JDK version or null if the project platform
* satisfy the required JDK versions.
* @return The minimal {@link SpecificationVersion} of platform or null.
*/
@CheckForNull
private SpecificationVersion getInvalidJdkVersion (
@NullAllowed final SpecificationVersion platformVersion,
@NonNull final Collection<? super String> invalidVersionProps) {
SpecificationVersion minVersion = null;
if (platformVersion != null) {
for (String vp : versionProps) {
final String value = this.eval.getProperty(vp);
if (value == null || value.isEmpty()) {
continue;
}
try {
final SpecificationVersion vpVersion = new SpecificationVersion (value);
if (vpVersion.compareTo(platformVersion) > 0) {
invalidVersionProps.add(vp);
minVersion = max(minVersion,vpVersion);
}
} catch (NumberFormatException nfe) {
LOG.log(
Level.WARNING,
"Property: {0} holds non valid version: {1}", //NOI18N
new Object[]{
vp,
value
});
}
}
}
return minVersion;
}
private boolean getOutdatedJdkVersion(
@NonNull final Collection<? super String> invalidVersionProps,
@NonNull final SpecificationVersion minVersion) {
boolean res = false;
for (String vp : versionProps) {
final String value = this.eval.getProperty(vp);
if (value == null || value.isEmpty()) {
continue;
}
try {
final SpecificationVersion vpVersion = new SpecificationVersion(value);
if (vpVersion.compareTo(minVersion) < 0) {
invalidVersionProps.add(vp);
res = true;
}
} catch (NumberFormatException nfe) {
LOG.log(
Level.WARNING,
"Property: {0} holds non valid version: {1}", //NOI18N
new Object[]{
vp,
value
});
}
}
return res;
}
@CheckForNull
private SpecificationVersion max (
@NullAllowed final SpecificationVersion a,
@NullAllowed final SpecificationVersion b) {
if (a == null) {
return b;
} else if (b == null) {
return a;
} else if (a.compareTo(b)>=0) {
return a;
} else {
return b;
}
}
@CheckForNull
private JavaPlatform getActivePlatform() {
final String activePlatformId = this.eval.getProperty(platformProp);
final JavaPlatformManager pm = JavaPlatformManager.getDefault();
if (activePlatformId == null) {
return pm.getDefaultPlatform();
}
final JavaPlatform[] installedPlatforms = pm.getPlatforms(
null,
new Specification(platformType, null));
for (JavaPlatform javaPlatform : installedPlatforms) {
final String antName = javaPlatform.getProperties().get(PLAT_PROP_ANT_NAME);
if (activePlatformId.equals(antName)) {
return javaPlatform;
}
}
return null;
}
}
private static class OpenManagersWeakListener extends WeakReference<PropertyChangeListener> implements Runnable, PropertyChangeListener {
public OpenManagersWeakListener(final PropertyChangeListener listener) {
super(listener, Utilities.activeReferenceQueue());
}
@Override
public void run() {
LibraryManager.removeOpenManagersPropertyChangeListener(this);
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
final PropertyChangeListener listener = get();
if (listener != null) {
listener.propertyChange(evt);
}
}
}
//</editor-fold>
}