# HG changeset patch # Parent 388e7b2e1320518df8e60b1519bc8fbdbb71fb53 Ability to resolve a broken library reference in a customized manner. diff --git a/java.project/manifest.mf b/java.project/manifest.mf --- a/java.project/manifest.mf +++ b/java.project/manifest.mf @@ -3,7 +3,7 @@ OpenIDE-Module-Layer: org/netbeans/modules/java/project/layer.xml OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/java/project/Bundle.properties OpenIDE-Module-Needs: javax.script.ScriptEngine.freemarker -OpenIDE-Module-Specification-Version: 1.34 +OpenIDE-Module-Specification-Version: 1.35 OpenIDE-Module-Recommends: org.netbeans.spi.java.project.runner.JavaRunnerImplementation AutoUpdate-Show-In-Client: false diff --git a/java.project/src/org/netbeans/modules/java/project/BrokenReferencesCustomizer.java b/java.project/src/org/netbeans/modules/java/project/BrokenReferencesCustomizer.java --- a/java.project/src/org/netbeans/modules/java/project/BrokenReferencesCustomizer.java +++ b/java.project/src/org/netbeans/modules/java/project/BrokenReferencesCustomizer.java @@ -44,7 +44,11 @@ package org.netbeans.modules.java.project; +import org.netbeans.api.project.libraries.Library; +import java.util.logging.Level; +import java.util.logging.Logger; import java.awt.Component; +import java.awt.EventQueue; import java.io.File; import javax.swing.DefaultListCellRenderer; import javax.swing.Icon; @@ -58,6 +62,7 @@ import org.openide.util.ImageUtilities; import static org.netbeans.modules.java.project.Bundle.*; import org.openide.util.NbBundle.Messages; +import org.openide.util.RequestProcessor; /** * @@ -65,6 +70,9 @@ */ public class BrokenReferencesCustomizer extends javax.swing.JPanel { + private static final RequestProcessor RP = new RequestProcessor(BrokenReferencesCustomizer.class); + private static final Logger LOG = Logger.getLogger(BrokenReferencesCustomizer.class.getName()); + private BrokenReferencesModel model; private File lastSelectedFile; @@ -179,10 +187,30 @@ if (index==-1) { return; } - BrokenReferencesModel.OneReference or = model.getOneReference(index); + final BrokenReferencesModel.OneReference or = model.getOneReference(index); if (or.getType() == BrokenReferencesModel.RefType.LIBRARY || or.getType() == BrokenReferencesModel.RefType.LIBRARY_CONTENT) { LibrariesCustomizer.showCustomizer(null, model.getProjectLibraryManager()); + } else if (or.getType() == BrokenReferencesModel.RefType.DEFINABLE_LIBRARY) { + RP.post(new Runnable() { + public @Override void run() { + try { + Library lib = or.define(); + LOG.log(Level.FINE, "found {0}", lib); + EventQueue.invokeLater(new Runnable() { + public @Override void run() { + model.refresh(); + updateSelection(); + } + }); + } catch (Exception x) { + LOG.log(Level.INFO, null, x); + // fallback: user may need to create library manually + LibrariesCustomizer.showCustomizer(null, model.getProjectLibraryManager()); + } + } + }); + return; } else if (or.getType() == BrokenReferencesModel.RefType.PLATFORM) { PlatformsCustomizer.showCustomizer(null); } else if (or.getType() == BrokenReferencesModel.RefType.VARIABLE || or.getType() == BrokenReferencesModel.RefType.VARIABLE_CONTENT) { @@ -231,6 +259,7 @@ @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.", @@ -243,6 +272,8 @@ switch (or.getType()) { case LIBRARY: return LBL_BrokenLinksCustomizer_BrokenLibraryDesc(or.getDisplayID()); + case DEFINABLE_LIBRARY: + return LBL_BrokenLinksCustomizer_BrokenDefinableLibraryDesc(or.getDisplayID()); case LIBRARY_CONTENT: return LBL_BrokenLinksCustomizer_BrokenLibraryContentDesc(or.getDisplayID()); case PROJECT: diff --git a/java.project/src/org/netbeans/modules/java/project/BrokenReferencesModel.java b/java.project/src/org/netbeans/modules/java/project/BrokenReferencesModel.java --- a/java.project/src/org/netbeans/modules/java/project/BrokenReferencesModel.java +++ b/java.project/src/org/netbeans/modules/java/project/BrokenReferencesModel.java @@ -54,6 +54,9 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.Callable; +import java.util.logging.Level; +import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.AbstractListModel; @@ -63,6 +66,7 @@ import org.netbeans.api.project.ProjectManager; import org.netbeans.api.project.libraries.Library; import org.netbeans.api.project.libraries.LibraryManager; +import org.netbeans.spi.java.project.support.ui.BrokenReferencesSupport.LibraryDefiner; import org.netbeans.spi.project.libraries.support.LibrariesSupport; import org.netbeans.spi.project.support.ant.AntProjectHelper; import org.netbeans.spi.project.support.ant.EditableProperties; @@ -71,11 +75,14 @@ import org.netbeans.spi.project.support.ant.ReferenceHelper; import org.openide.filesystems.FileObject; import org.openide.util.Exceptions; +import org.openide.util.Lookup; import static org.netbeans.modules.java.project.Bundle.*; import org.openide.util.NbBundle.Messages; public final class BrokenReferencesModel extends AbstractListModel { + private static final Logger LOG = Logger.getLogger(BrokenReferencesModel.class.getName()); + private String[] props; private String[] platformsProps; private AntProjectHelper helper; @@ -104,6 +111,7 @@ @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", @@ -116,6 +124,8 @@ switch (or.type) { case LIBRARY: return LBL_BrokenLinksCustomizer_BrokenLibrary(or.getDisplayID()); + case DEFINABLE_LIBRARY: + return LBL_BrokenLinksCustomizer_BrokenDefinableLibrary(or.getDisplayID()); case LIBRARY_CONTENT: return LBL_BrokenLinksCustomizer_BrokenLibraryContent(or.getDisplayID()); case PROJECT: @@ -169,6 +179,7 @@ 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 @@ -280,6 +291,7 @@ String libraryName = libraryRef.substring(5,libraryRef.length()-10); Library lib = refHelper.findLibrary(libraryName); if (lib == null) { + // Should already have been caught before? set.add(new OneReference(RefType.LIBRARY, libraryRef, true)); } else { @@ -358,6 +370,7 @@ } private static void updateReferencesList(List oldBroken, Set newBroken) { + LOG.log(Level.FINE, "References updated from {0} to {1}", new Object[] {oldBroken, newBroken}); for (OneReference or : oldBroken) { if (newBroken.contains(or)) { or.broken = true; @@ -454,21 +467,35 @@ FILE, PLATFORM, LIBRARY, + DEFINABLE_LIBRARY, LIBRARY_CONTENT, VARIABLE, VARIABLE_CONTENT, } - - public static class OneReference { + + public static final class OneReference { private final RefType type; private boolean broken; private final String ID; + private final Callable definer; public OneReference(RefType type, String ID, boolean broken) { + Callable _definer = null; + if (type == RefType.LIBRARY) { + String name = ID.substring(5, ID.length() - 10); + for (LibraryDefiner ld : Lookup.getDefault().lookupAll(LibraryDefiner.class)) { + _definer = ld.missingLibrary(name); + if (_definer != null) { + type = RefType.DEFINABLE_LIBRARY; + break; + } + } + } this.type = type; this.ID = ID; this.broken = broken; + definer = _definer; } public RefType getType() { @@ -479,6 +506,7 @@ switch (type) { case LIBRARY: + case DEFINABLE_LIBRARY: case LIBRARY_CONTENT: // libs..classpath return ID.substring(5, ID.length()-10); @@ -506,6 +534,14 @@ } } + public Library define() throws Exception { + return definer.call(); + } + + public @Override String toString() { + return type + ":" + ID + (broken ? "" : "[fixed]"); + } + public @Override boolean equals(Object o) { if (o == this) { return true; diff --git a/java.project/src/org/netbeans/spi/java/project/support/ui/BrokenReferencesSupport.java b/java.project/src/org/netbeans/spi/java/project/support/ui/BrokenReferencesSupport.java --- a/java.project/src/org/netbeans/spi/java/project/support/ui/BrokenReferencesSupport.java +++ b/java.project/src/org/netbeans/spi/java/project/support/ui/BrokenReferencesSupport.java @@ -48,11 +48,14 @@ import java.awt.Frame; import java.awt.event.WindowAdapter; import java.io.IOException; +import java.util.concurrent.Callable; import javax.swing.JButton; import javax.swing.SwingUtilities; +import org.netbeans.api.annotations.common.CheckForNull; import org.netbeans.api.project.Project; import org.netbeans.api.project.ProjectManager; import org.netbeans.api.project.ProjectUtils; +import org.netbeans.api.project.libraries.Library; import org.netbeans.modules.java.project.BrokenReferencesAlertPanel; import org.netbeans.modules.java.project.BrokenReferencesCustomizer; import org.netbeans.modules.java.project.BrokenReferencesModel; @@ -64,6 +67,7 @@ import org.openide.util.Exceptions; import org.openide.util.NbBundle.Messages; import org.openide.util.Parameters; +import org.openide.util.lookup.ServiceProvider; import org.openide.windows.WindowManager; import static org.netbeans.spi.java.project.support.ui.Bundle.*; @@ -133,6 +137,7 @@ * name is one and it is "platform.active". The name of the default * platform is expected to be "default_platform" and this platform * always exists. + * @see LibraryDefiner */ @Messages({ "LBL_BrokenLinksCustomizer_Close=Close", @@ -251,5 +256,19 @@ } } + /** + * Service which may be {@linkplain ServiceProvider registered} to download remote libraries or otherwise define them. + * @since org.netbeans.modules.java.project/1 1.35 + */ + public interface LibraryDefiner { + + /** + * Checks to see if a missing library definition can be created. + * @param name a desired {@link Library#getName} + * @return a callback which may be run (asynchronously) to create and return a library with the given name, or null if not recognized + */ + @CheckForNull Callable missingLibrary(String name); + + } }