| /* |
| * 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> |
| } |