Index: projects/libraries/apichanges.xml diff -u projects/libraries/apichanges.xml:1.9 projects/libraries/apichanges.xml:1.6.14.5 --- projects/libraries/apichanges.xml:1.9 Wed Sep 26 14:04:11 2007 +++ projects/libraries/apichanges.xml Fri Jan 18 01:28:14 2008 @@ -103,6 +103,49 @@ + + + + Support for project libraries + + + + +

+ LibraryManager.addLibrary will not work with areas, + so it was deprecated in favor of the new createLibrary. +

+

+ Code which obtained a Library of arbitrary origin, + and then assumed it could refer to that library henceforth by name only, + will not work with project libraries. + The simplest persistent identifier for a new-style library is pair of name + and URL of manager (null for the default manager). +

+
+ +

+ There is a new API and SPI for project libraries + (or, more generally, libraries with specific storage locations). + LibraryChooser, ArealLibraryProvider, + and LibraryStorageArea are new, as are methods + Library.getManager, + LibraryManager.getDisplayName, + LibraryManager.getLocation, + LibraryManager.createLibrary, + LibraryManager.forLocation, and + LibraryManager.getOpenManagers. + (LibraryProvider was also generified.) +

+
+ + + + + + + +
Index: projects/libraries/nbproject/project.xml diff -u projects/libraries/nbproject/project.xml:1.16 projects/libraries/nbproject/project.xml:1.13.10.7 --- projects/libraries/nbproject/project.xml:1.16 Thu Oct 18 14:20:32 2007 +++ projects/libraries/nbproject/project.xml Fri Jan 4 12:32:46 2008 @@ -47,6 +47,15 @@ org.netbeans.modules.project.libraries + org.jdesktop.layout + + + + 1 + 1.5 + + + org.netbeans.modules.projectapi @@ -145,6 +154,7 @@ org.openide.util + Index: projects/libraries/src/org/netbeans/api/project/libraries/Bundle.properties diff -u projects/libraries/src/org/netbeans/api/project/libraries/Bundle.properties:1.6 projects/libraries/src/org/netbeans/api/project/libraries/Bundle.properties:1.5.16.3 --- projects/libraries/src/org/netbeans/api/project/libraries/Bundle.properties:1.6 Wed Sep 26 14:04:16 2007 +++ projects/libraries/src/org/netbeans/api/project/libraries/Bundle.properties Fri Jan 4 12:32:47 2008 @@ -38,3 +38,16 @@ # made subject to such option by the copyright holder. TXT_LibrariesManager=Library Manager + +LibraryChooserGUI.add.title=Add Library +LibraryChooserGUI.add.button=Add Library +LibraryChooserGUI.import.title=Import Library +LibraryChooserGUI.import.button=Import Library +LibraryChooserGUI.manage=&Manage Libraries... +LibraryChooserGUI.accessibleDescription=The library manager allows you to add predefined libraries to the project classpath or to edit library definitions. +LibraryChooserGUI.librariesLabel=Available &Libraries: +LibraryChooserGUI.createButton.text=Create... +LibraryChooserGUI.importButton.text=Import... +LibraryChooserGUI.manageLibrariesButton.text=Manage Libraries... +LibrariesCustomizer.createLibrary.title=Create New Library +LibrariesCustomizer.customizeLibrary.title=Customize Library Index: projects/libraries/src/org/netbeans/api/project/libraries/LibrariesCustomizer.java diff -u projects/libraries/src/org/netbeans/api/project/libraries/LibrariesCustomizer.java:1.5 projects/libraries/src/org/netbeans/api/project/libraries/LibrariesCustomizer.java:1.4.16.5 --- projects/libraries/src/org/netbeans/api/project/libraries/LibrariesCustomizer.java:1.5 Wed Sep 26 14:04:16 2007 +++ projects/libraries/src/org/netbeans/api/project/libraries/LibrariesCustomizer.java Tue Jan 15 13:52:10 2008 @@ -45,6 +45,13 @@ import org.openide.DialogDisplayer; import org.openide.util.NbBundle; import java.awt.Dialog; +import javax.swing.border.EmptyBorder; +import org.netbeans.modules.project.libraries.LibraryTypeRegistry; +import org.netbeans.modules.project.libraries.ui.LibrariesModel; +import org.netbeans.modules.project.libraries.ui.NewLibraryPanel; +import org.netbeans.spi.project.libraries.LibraryImplementation; +import org.netbeans.spi.project.libraries.LibraryStorageArea; +import org.netbeans.spi.project.libraries.LibraryTypeProvider; /** Provides method for opening Libraries customizer * @@ -55,32 +62,116 @@ } /** - * Shows libraries customizer + * Shows libraries customizer for given library manager. * @param activeLibrary if not null the activeLibrary is selected in the opened customizer * @return true if user pressed OK and libraries were sucessfully modified */ - public static boolean showCustomizer (Library activeLibrary) { + public static boolean showCustomizer (Library activeLibrary, LibraryManager libraryManager) { org.netbeans.modules.project.libraries.ui.LibrariesCustomizer customizer = - new org.netbeans.modules.project.libraries.ui.LibrariesCustomizer (); + new org.netbeans.modules.project.libraries.ui.LibrariesCustomizer (libraryManager.getArea()); + customizer.setBorder(new EmptyBorder(12, 12, 0, 12)); if (activeLibrary != null) customizer.setSelectedLibrary (activeLibrary.getLibraryImplementation ()); DialogDescriptor descriptor = new DialogDescriptor (customizer,NbBundle.getMessage(LibrariesCustomizer.class, "TXT_LibrariesManager")); - Dialog dlg = null; + Dialog dlg = DialogDisplayer.getDefault().createDialog(descriptor); try { - dlg = DialogDisplayer.getDefault().createDialog (descriptor); dlg.setVisible(true); if (descriptor.getValue() == DialogDescriptor.OK_OPTION) { return customizer.apply(); + } else { + return false; + } + } finally { + dlg.dispose(); + } + } + + /** + * Shows libraries customizer for global libraries. + * @param activeLibrary if not null the activeLibrary is selected in the opened customizer + * @return true if user pressed OK and libraries were sucessfully modified + */ + public static boolean showCustomizer (Library activeLibrary) { + return showCustomizer(activeLibrary, LibraryManager.getDefault()); + } + + /** + * Show customizer for creating new library in the given library manager. + * @param manager manager + * @return created persisted library or null if user cancelled operation + * @since org.netbeans.modules.project.libraries/1 1.16 + */ + public static Library showCreateNewLibraryCustomizer(LibraryManager manager) { + if (manager == null) { + manager = LibraryManager.getDefault(); + } + LibraryStorageArea area = manager.getArea(); + if (area == null) { + area = LibrariesModel.GLOBAL_AREA; + } + org.netbeans.modules.project.libraries.ui.LibrariesCustomizer customizer = + new org.netbeans.modules.project.libraries.ui.LibrariesCustomizer (area); + NewLibraryPanel p = new NewLibraryPanel(customizer.getModel(), null, area); + DialogDescriptor dd = new DialogDescriptor (p, + NbBundle.getMessage(LibrariesCustomizer.class,"LibrariesCustomizer.createLibrary.title"), + true, DialogDescriptor.OK_CANCEL_OPTION, null, null); + p.setDialogDescriptor(dd); + Dialog dlg = DialogDisplayer.getDefault().createDialog (dd); + dlg.setVisible(true); + if (dd.getValue() == DialogDescriptor.OK_OPTION) { + LibraryImplementation impl; + if (area != LibrariesModel.GLOBAL_AREA) { + impl = customizer.getModel().createArealLibrary(p.getLibraryType(), p.getLibraryName(), manager.getArea()); + } else { + LibraryTypeProvider provider = LibraryTypeRegistry.getDefault().getLibraryTypeProvider(p.getLibraryType()); + if (provider == null) { + return null; + } + impl = provider.createLibrary(); + impl.setName(p.getLibraryName()); + } + customizer.getModel().addLibrary(impl); + customizer.forceTreeRecreation(); + if (customizeLibrary(customizer, impl)) { + return manager.getLibrary(impl.getName()); } - else { - customizer.cancel(); + } + return null; + } + + /** + * Show library customizer for the given library. + * @param library library + * @return true if library was modified or not + * @since org.netbeans.modules.project.libraries/1 1.16 + */ + public static boolean showSingleLibraryCustomizer(Library library) { + org.netbeans.modules.project.libraries.ui.LibrariesCustomizer customizer = + new org.netbeans.modules.project.libraries.ui.LibrariesCustomizer (library.getManager().getArea()); + return customizeLibrary(customizer, library.getLibraryImplementation()); + } + + private static boolean customizeLibrary(org.netbeans.modules.project.libraries.ui.LibrariesCustomizer customizer, + LibraryImplementation activeLibrary) { + customizer.hideLibrariesList(); + customizer.setBorder(new EmptyBorder(12, 8, 0, 10)); + customizer.setSelectedLibrary (activeLibrary); + DialogDescriptor descriptor = new DialogDescriptor (customizer,NbBundle.getMessage(LibrariesCustomizer.class, + "LibrariesCustomizer.customizeLibrary.title")); + Dialog dlg = DialogDisplayer.getDefault().createDialog(descriptor); + try { + dlg.setVisible(true); + if (descriptor.getValue() == DialogDescriptor.OK_OPTION) { + customizer.apply(); + return true; + } else { + return false; } } finally { - if (dlg != null) - dlg.dispose(); + dlg.dispose(); } - return false; } + } Index: projects/libraries/src/org/netbeans/api/project/libraries/Library.java diff -u projects/libraries/src/org/netbeans/api/project/libraries/Library.java:1.12 projects/libraries/src/org/netbeans/api/project/libraries/Library.java:1.10.12.6 --- projects/libraries/src/org/netbeans/api/project/libraries/Library.java:1.12 Wed Sep 26 14:04:18 2007 +++ projects/libraries/src/org/netbeans/api/project/libraries/Library.java Thu Nov 22 08:39:57 2007 @@ -45,7 +45,6 @@ import java.beans.PropertyChangeEvent; import java.net.URL; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.MissingResourceException; import java.util.ResourceBundle; @@ -76,11 +75,9 @@ private List listeners; - /** - * Creates new library instance - * - */ - private Library (LibraryImplementation impl) { + private final LibraryManager manager; + + Library(LibraryImplementation impl, LibraryManager manager) { this.impl = impl; this.impl.addPropertyChangeListener (new PropertyChangeListener () { public void propertyChange(PropertyChangeEvent evt) { @@ -88,9 +85,19 @@ Library.this.fireChange (propName,evt.getOldValue(),evt.getNewValue()); } }); + this.manager = manager; } // end create /** + * Gets the associated library manager. + * @return the manager (may be the "default" global manager, or a local manager) + * @since org.netbeans.modules.project.libraries/1 1.15 + */ + public LibraryManager getManager() { + return manager; + } + + /** * Access typed but raw library data. *

* The contents are defined by SPI providers and identified @@ -169,11 +176,6 @@ return impl.hashCode(); } - @Override - public String toString () { - return this.getName(); - } - /** * Adds PropertyChangeListener * @param listener @@ -235,11 +237,16 @@ return key; } } + + @Override + public String toString() { + return "Library[" + getName() + "]"; // NOI18N + } static { LibraryAccessor.DEFAULT = new LibraryAccessor () { public Library createLibrary (LibraryImplementation impl) { - return new Library (impl); + return new Library(impl, LibraryManager.getDefault()); } }; } Index: projects/libraries/src/org/netbeans/api/project/libraries/LibraryChooser.java diff -u /dev/null projects/libraries/src/org/netbeans/api/project/libraries/LibraryChooser.java:1.1.2.6 --- /dev/null Fri Jan 18 01:33:13 2008 +++ projects/libraries/src/org/netbeans/api/project/libraries/LibraryChooser.java Fri Jan 4 12:32:47 2008 @@ -0,0 +1,135 @@ +/* + * The contents of this file are subject to the terms of the Common Development + * and Distribution License (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at http://www.netbeans.org/cddl.html + * or http://www.netbeans.org/cddl.txt. + * + * When distributing Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://www.netbeans.org/cddl.txt. + * If applicable, add the following below the CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans.api.project.libraries; + +import java.awt.Component; +import java.beans.PropertyChangeListener; +import java.io.IOException; +import java.util.Set; + +/** + * Visual picker for libraries. + * @since org.netbeans.modules.project.libraries/1 1.16 + */ +public final class LibraryChooser { + + private LibraryChooser() { + } + + /** + * Create a chooser showing libraries from given library manager and let the + * user to pick some. + * @param manager manager; can be null in which case global libraries are listed + * @param filter optional libraries filter; null for no filtering + * @param handler handler to perform library importing; can be null in which case + * import will not be allowed in UI + * @return a nonempty set of libraries that were selected, or null if the dialog was cancelled + */ + public static Set showDialog(LibraryManager manager, Filter filter, LibraryImportHandler handler) { + return LibraryChooserGUI.showChooser(manager, filter, handler, true); + } + + /** + * Create a picker as an embeddable panel. + * Might be used in a wizard, for example. + * @param manager library manager to use or null for global libraries + * @param filter optional libraries filter; null for no filtering + * @return a panel controller + */ + public static Panel createPanel(LibraryManager manager, Filter filter) { + return LibraryChooserGUI.createPanel(manager, filter); + } + + /** + * Filter for use by {@link LibraryChooser#createPanel()} or + * {@link LibraryChooser#showDialog()}. + */ + public interface Filter { + + /** + * Accepts or rejects a library. + * @param library a library found in one of the managers + * @return true to display, false to hide + */ + boolean accept(Library library); + + } + + /** + * Represents operations permitted by {@link #createPanel}. + * Not to be implemented by foreign code (methods may be added in the future). + */ + public interface Panel { + + /** + * Produces the actual component you can display. + * @return an embeddable GUI component + */ + Component getVisualComponent(); + + /** + * Gets the set of libraries which are currently selected. + * @return a (possibly empty) set of libraries + */ + Set getSelectedLibraries(); + + /** + * Property fired when {@link #getSelectedLibraries} changes. + * Do not expect the old and new values to be non-null. + */ + String PROP_SELECTED_LIBRARIES = "selectedLibraries"; // NOI18N + + /** + * Add a listener for {@link #PROP_SELECTED_LIBRARIES}. + * @param listener the listener to add + */ + void addPropertyChangeListener(PropertyChangeListener listener); + + /** + * Remove a listener. + * @param listener the listener to remove + */ + void removePropertyChangeListener(PropertyChangeListener listener); + + } + + /** + * Handler for library importing. The handler is used from library chooser + * UI in order to import global library to sharable libraries location. A + * library is only imported if there is no library with the same library + * name in destination library manager. + */ + public interface LibraryImportHandler { + + /** + * Implementation is expected to copy given global library to + * sharable libraries location, that is to library manager the library + * chooser was created for. + * + * @param library library to copy + * @return newly created library + * @throws java.io.IOException any IO failure + * @throws IllegalArgumentException if there already exists library + * with this name + */ + Library importLibrary(Library library) throws IOException; + } + +} Index: projects/libraries/src/org/netbeans/api/project/libraries/LibraryChooserGUI.form diff -u /dev/null projects/libraries/src/org/netbeans/api/project/libraries/LibraryChooserGUI.form:1.1.2.3 --- /dev/null Fri Jan 18 01:33:13 2008 +++ projects/libraries/src/org/netbeans/api/project/libraries/LibraryChooserGUI.form Fri Jan 4 12:32:47 2008 @@ -0,0 +1,113 @@ + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Index: projects/libraries/src/org/netbeans/api/project/libraries/LibraryChooserGUI.java diff -u /dev/null projects/libraries/src/org/netbeans/api/project/libraries/LibraryChooserGUI.java:1.1.2.8 --- /dev/null Fri Jan 18 01:33:13 2008 +++ projects/libraries/src/org/netbeans/api/project/libraries/LibraryChooserGUI.java Fri Jan 4 12:32:47 2008 @@ -0,0 +1,382 @@ +/* + * The contents of this file are subject to the terms of the Common Development + * and Distribution License (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at http://www.netbeans.org/cddl.html + * or http://www.netbeans.org/cddl.txt. + * + * When distributing Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://www.netbeans.org/cddl.txt. + * If applicable, add the following below the CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans.api.project.libraries; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Image; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyVetoException; +import java.io.IOException; +import java.text.Collator; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.border.EmptyBorder; +import org.openide.DialogDescriptor; +import org.openide.DialogDisplayer; +import org.openide.NotifyDescriptor; +import org.openide.explorer.ExplorerManager; +import org.openide.filesystems.Repository; +import org.openide.loaders.DataFolder; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.Children; +import org.openide.nodes.Node; +import org.openide.nodes.NodeNotFoundException; +import org.openide.nodes.NodeOp; +import org.openide.util.Exceptions; +import org.openide.util.HelpCtx; +import org.openide.util.NbBundle; +import org.openide.util.lookup.Lookups; + +class LibraryChooserGUI extends JPanel implements ExplorerManager.Provider, HelpCtx.Provider, LibraryChooser.Panel { + + private final LibraryManager manager; + private final LibraryChooser.Filter filter; + private final ExplorerManager explorer; + private LibraryChooser.LibraryImportHandler importHandler; + + private LibraryChooserGUI(LibraryManager manager, LibraryChooser.Filter filter, + LibraryChooser.LibraryImportHandler importHandler) { + if (manager == null) { + manager = LibraryManager.getDefault(); + } + this.manager = manager; + this.filter = filter; + this.importHandler = importHandler; + explorer = new ExplorerManager(); + initComponents(); + tree.setDefaultActionAllowed(false); + } + + public static LibraryChooser.Panel createPanel(LibraryManager manager, + LibraryChooser.Filter filter) { + LibraryChooserGUI l = new LibraryChooserGUI(manager, filter, null); + l.configureForEmbedded(); + return l; + } + + public static Set showChooser(LibraryManager manager, LibraryChooser.Filter filter, + LibraryChooser.LibraryImportHandler handler, boolean addOperation) { + LibraryChooserGUI l = new LibraryChooserGUI(manager, filter, handler); + return l.showDialog(addOperation); + } + + private Set showDialog(boolean addOperatation) { + // show manage button only in embedded panel: + manageLibrariesButton.setVisible(false); + // import enabled only for non-global library manager + importButton.setVisible(manager.getLocation() != null && importHandler != null); + JPanel inset = new JPanel(new BorderLayout()); + inset.setBorder(new EmptyBorder(12,12,0,12)); + inset.add(this); + String title; + String buttonLabel; + if (addOperatation) { + title = NbBundle.getMessage(LibraryChooserGUI.class, "LibraryChooserGUI.add.title"); + buttonLabel = NbBundle.getMessage(LibraryChooserGUI.class, "LibraryChooserGUI.add.button"); + } else { + title = NbBundle.getMessage(LibraryChooserGUI.class, "LibraryChooserGUI.import.title"); + buttonLabel = NbBundle.getMessage(LibraryChooserGUI.class, "LibraryChooserGUI.import.button"); + createButton.setVisible(false); + } + DialogDescriptor dd = new DialogDescriptor(inset, title); + dd.setModal(true); + final JButton add = new JButton(buttonLabel); + add.setEnabled(false); + add.setDefaultCapable(true); + explorer.addPropertyChangeListener(new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent evt) { + add.setEnabled(!getSelectedLibraries().isEmpty()); + } + }); + dd.setOptions(new Object[] {add, NotifyDescriptor.CANCEL_OPTION}); + dd.setClosingOptions(new Object[] {add, NotifyDescriptor.CANCEL_OPTION}); + if (DialogDisplayer.getDefault().notify(dd) == add) { + Set selection = getSelectedLibraries(); + assert !selection.isEmpty(); + return selection; + } else { + return null; + } + } + + private void configureForEmbedded() { + explorer.addPropertyChangeListener(new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent evt) { + firePropertyChange(PROP_SELECTED_LIBRARIES, null, null); + } + }); + createButton.setVisible(false); + importButton.setVisible(false); + } + + public Set getSelectedLibraries() { + Set s = new HashSet(); + for (Node n : explorer.getSelectedNodes()) { + Library l = n.getLookup().lookup(Library.class); + if (l != null) { + s.add(l); + } else { + return Collections.emptySet(); + } + } + return s; + } + + public Component getVisualComponent() { + return this; + } + + public ExplorerManager getExplorerManager() { + return explorer; + } + + public HelpCtx getHelpCtx() { + return new HelpCtx(LibraryChooserGUI.class); + } + + private void setRootNode() { + explorer.setRootContext(new AbstractNode(new LibraryManagerChildren())); + tree.expandAll(); + try { + if (explorer.getRootContext().getChildren().getNodes(true).length > 0) { + explorer.setSelectedNodes(new Node[] {explorer.getRootContext().getChildren().getNodes(true)[0]}); + } + } catch (PropertyVetoException x) { + Exceptions.printStackTrace(x); + } + /* XXX Nothing seems to work to scroll to top; how is it done? + tree.getViewport().setViewPosition(new Point()); + tree.getViewport().scrollRectToVisible(new Rectangle(0, 0, 1, 1)); + */ + tree.requestFocus(); + } + + @Override + public void addNotify() { + super.addNotify(); + setRootNode(); + } + + private class LibraryManagerChildren extends Children.Keys { + + @Override + protected void addNotify() { + super.addNotify(); + if (manager != null) { + setKeys(Collections.singleton(manager)); + } + } + + protected Node[] createNodes(LibraryManager mgr) { + List libs = new ArrayList(); + for (Library lib : mgr.getLibraries()) { + if (filter == null || filter.accept(lib)) { + libs.add(lib); + } + } + if (libs.isEmpty()) { + return new Node[0]; + } else { + Collections.sort(libs,new Comparator() { + Collator COLL = Collator.getInstance(); + public int compare(Library lib1, Library lib2) { + return COLL.compare(lib1.getDisplayName(), lib2.getDisplayName()); + } + }); + Node n = new AbstractNode(new LibraryChildren(libs)) { + Node iconDelegate = DataFolder.findFolder(Repository.getDefault().getDefaultFileSystem().getRoot()).getNodeDelegate(); + public Image getIcon(int type) { + return iconDelegate.getIcon(type); + } + public Image getOpenedIcon(int type) { + return iconDelegate.getOpenedIcon(type); + } + }; + n.setName(mgr.getDisplayName()); + n.setDisplayName(mgr.getDisplayName()); + return new Node[] {n}; + } + } + + } + + private class LibraryChildren extends Children.Keys { + + LibraryChildren(List libs) { + setKeys(libs); + } + + protected Node[] createNodes(Library lib) { + AbstractNode n = new AbstractNode(Children.LEAF, Lookups.singleton(lib)); + n.setName(lib.getName()); + n.setDisplayName(lib.getDisplayName()); + n.setShortDescription(lib.getDescription()); + n.setIconBaseWithExtension("org/netbeans/modules/project/libraries/resources/libraries.gif"); // NOI18N + return new Node[] {n}; + } + + } + + // //GEN-BEGIN:initComponents + private void initComponents() { + + librariesLabel = new javax.swing.JLabel(); + tree = new org.openide.explorer.view.BeanTreeView(); + createButton = new javax.swing.JButton(); + importButton = new javax.swing.JButton(); + manageLibrariesButton = new javax.swing.JButton(); + + librariesLabel.setLabelFor(tree); + org.openide.awt.Mnemonics.setLocalizedText(librariesLabel, org.openide.util.NbBundle.getMessage(LibraryChooserGUI.class, "LibraryChooserGUI.librariesLabel")); // NOI18N + + tree.setBorder(javax.swing.BorderFactory.createEtchedBorder()); + tree.setPopupAllowed(false); + tree.setRootVisible(false); + + createButton.setText(org.openide.util.NbBundle.getMessage(LibraryChooserGUI.class, "LibraryChooserGUI.createButton.text")); // NOI18N + createButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + createButtonActionPerformed(evt); + } + }); + + importButton.setText(org.openide.util.NbBundle.getMessage(LibraryChooserGUI.class, "LibraryChooserGUI.importButton.text")); // NOI18N + importButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + importButtonActionPerformed(evt); + } + }); + + manageLibrariesButton.setText(org.openide.util.NbBundle.getMessage(LibraryChooserGUI.class, "LibraryChooserGUI.manageLibrariesButton.text")); // NOI18N + manageLibrariesButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + manageLibrariesButtonActionPerformed(evt); + } + }); + + org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(librariesLabel) + .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup() + .add(tree, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 257, Short.MAX_VALUE) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING) + .add(createButton) + .add(importButton))) + .add(layout.createSequentialGroup() + .add(manageLibrariesButton) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .add(librariesLabel) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .add(createButton) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(importButton)) + .add(tree, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 284, Short.MAX_VALUE)) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(manageLibrariesButton)) + ); + + getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(LibraryChooserGUI.class, "LibraryChooserGUI.accessibleDescription")); // NOI18N + }// //GEN-END:initComponents + + private void manageLibrariesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_manageLibrariesButtonActionPerformed + if (LibrariesCustomizer.showCustomizer(null, manager)) { + setRootNode(); + } + }//GEN-LAST:event_manageLibrariesButtonActionPerformed + + private void createButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_createButtonActionPerformed + Library l = LibrariesCustomizer.showCreateNewLibraryCustomizer(manager); + if (l != null) { + setRootNode(); + selectLibrary(Collections.singleton(l)); + } + }//GEN-LAST:event_createButtonActionPerformed + + private void selectLibrary(Collection libraries) { + Node root = explorer.getRootContext(); + List selection = new ArrayList(); + for (Library lib : libraries) { + String[] path = {lib.getManager().getDisplayName(), lib.getName()}; + try { + Node node = NodeOp.findPath(root, path); + if (node != null) { + selection.add(node); + } + } catch (NodeNotFoundException e) { + //Ignore it + } + } + try { + explorer.setSelectedNodes(selection.toArray(new Node[selection.size()])); + } catch (PropertyVetoException e) { + //Ignore it + } + } + + private void importButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_importButtonActionPerformed + Set libs = showChooser(LibraryManager.getDefault(), + new IgnoreAlreadyImportedLibrariesFilter(), null, false); + if (libs != null) { + Set importedLibs = new HashSet(); + try { + for (Library lib : libs) { + importedLibs.add(importHandler.importLibrary(lib)); + } + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + setRootNode(); + selectLibrary(importedLibs); + } + }//GEN-LAST:event_importButtonActionPerformed + + private class IgnoreAlreadyImportedLibrariesFilter implements LibraryChooser.Filter { + public boolean accept(Library library) { + return manager.getLibrary(library.getName()) == null; + } + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton createButton; + private javax.swing.JButton importButton; + private javax.swing.JLabel librariesLabel; + private javax.swing.JButton manageLibrariesButton; + private org.openide.explorer.view.BeanTreeView tree; + // End of variables declaration//GEN-END:variables + +} Index: projects/libraries/src/org/netbeans/api/project/libraries/LibraryManager.java diff -u projects/libraries/src/org/netbeans/api/project/libraries/LibraryManager.java:1.9 projects/libraries/src/org/netbeans/api/project/libraries/LibraryManager.java:1.8.12.12 --- projects/libraries/src/org/netbeans/api/project/libraries/LibraryManager.java:1.9 Wed Sep 26 14:04:18 2007 +++ projects/libraries/src/org/netbeans/api/project/libraries/LibraryManager.java Mon Jan 14 02:28:11 2008 @@ -41,24 +41,29 @@ package org.netbeans.api.project.libraries; - -import org.netbeans.api.project.libraries.Library; -import org.netbeans.spi.project.libraries.LibraryImplementation; -import org.netbeans.spi.project.libraries.LibraryProvider; -import org.openide.util.Lookup; -import org.openide.util.LookupListener; -import org.openide.util.LookupEvent; - import java.beans.PropertyChangeSupport; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeEvent; import java.io.IOException; -import java.util.*; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.netbeans.modules.project.libraries.LibraryAccessor; import org.netbeans.modules.project.libraries.WritableLibraryProvider; -import org.netbeans.spi.project.libraries.LibraryFactory; +import org.netbeans.modules.project.libraries.ui.LibrariesModel; +import org.netbeans.spi.project.libraries.ArealLibraryProvider; +import org.netbeans.spi.project.libraries.LibraryImplementation; +import org.netbeans.spi.project.libraries.LibraryProvider; +import org.netbeans.spi.project.libraries.LibraryStorageArea; +import org.netbeans.spi.project.libraries.LibraryTypeProvider; import org.netbeans.spi.project.libraries.support.LibrariesSupport; - -// XXX make getLibraries return Set not array +import org.openide.util.Lookup; +import org.openide.util.LookupListener; +import org.openide.util.LookupEvent; /** * LibraryManager provides registry of the installed libraries. @@ -67,19 +72,79 @@ */ public final class LibraryManager { + /** + * Property fired when the set of libraries changes. + */ public static final String PROP_LIBRARIES = "libraries"; //NOI18N private static LibraryManager instance; private Lookup.Result result; - private Collection currentStorages = new ArrayList(); - private PropertyChangeListener plistener; - private PropertyChangeSupport listeners; - private Collection cache; + private final Collection currentStorages = new ArrayList(); + private final PropertyChangeListener plistener = new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent evt) { + if (LibraryProvider.PROP_LIBRARIES.equals(evt.getPropertyName())) { + resetCache(); + } + } + }; + private final PropertyChangeSupport listeners = new PropertyChangeSupport(this); + private static final PropertyChangeSupport openLibraryManagerListListeners = + new PropertyChangeSupport(LibraryManager.class); + private static final PropertyChangeListener AREAL_LIBRARY_PROVIDER_LISTENER = new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent evt) { + openLibraryManagerListListeners.firePropertyChange(PROP_OPEN_LIBRARY_MANAGERS, null, null); + } + }; + + /** Property fired when list of open library managers changes. */ + public static final String PROP_OPEN_LIBRARY_MANAGERS = "openManagers"; // NOI18N + private static Lookup.Result areaProvidersLookupResult = null; + private static Collection currentAreaProviders = new ArrayList(); + private Collection cache; + /** null for default manager */ + private final ArealLibraryProvider alp; + /** null for default manager */ + private final LibraryStorageArea area; private LibraryManager () { - this.listeners = new PropertyChangeSupport(this); + alp = null; + area = null; + } + + private LibraryManager(ArealLibraryProvider alp, LibraryStorageArea area) { + this.alp = alp; + this.area = area; + LibraryProvider lp = LibraryAccessor.getLibraries(alp, area); + lp.addPropertyChangeListener(plistener); + currentStorages.add(lp); + } + + /** + * Gets a human-readable description of this manager. + * This may be used to visually differentiate the global manager from various local managers. + * @return a localized display name + * @see LibraryStorageArea#getDisplayName + * @since org.netbeans.modules.project.libraries/1 1.15 + */ + public String getDisplayName() { + if (area == null) { + return LibrariesModel.GLOBAL_AREA.getDisplayName(); + } else { + return area.getDisplayName(); + } + } + + /** + * Gets the location associated with this manager. + * @return a location where library definitions are kept, or null in the case of {@link #getDefault} + * @see LibraryStorageArea#getLocation + * @see #forLocation + * @since org.netbeans.modules.project.libraries/1 1.15 + */ + public URL getLocation() { + return area != null ? area.getLocation() : null; } /** @@ -99,44 +164,44 @@ } /** - * List all library defined in the IDE. - * - * @return Library[] library definitions never null + * Lists all libraries defined in this manager. + * @return library definitions (never null) */ public synchronized Library[] getLibraries() { if (this.cache == null) { - if (this.result == null) { - plistener = new PropertyChangeListener() { - public void propertyChange(PropertyChangeEvent evt) { - resetCache (); - } - }; - result = Lookup.getDefault().lookupResult(LibraryProvider.class); - result.addLookupListener (new LookupListener() { - public void resultChanged(LookupEvent ev) { - resetCache (); + List l = new ArrayList(); + if (area == null) { + if (result == null) { + result = Lookup.getDefault().lookupResult(LibraryProvider.class); + result.addLookupListener(new LookupListener() { + public void resultChanged(LookupEvent ev) { + resetCache(); + } + }); + } + Collection instances = result.allInstances(); + Collection added = new HashSet(instances); + added.removeAll(currentStorages); + Collection removed = new HashSet(currentStorages); + removed.removeAll(instances); + currentStorages.clear(); + for (LibraryProvider storage : instances) { + this.currentStorages.add(storage); + for (LibraryImplementation impl : storage.getLibraries()) { + l.add(new Library(impl, this)); } - }); + } + for (LibraryProvider p : removed) { + p.removePropertyChangeListener(this.plistener); + } + for (LibraryProvider p : added) { + p.addPropertyChangeListener(this.plistener); + } + } else { + for (LibraryImplementation impl : currentStorages.iterator().next().getLibraries()) { + l.add(new Library(impl, this)); + } } - List l = new ArrayList(); - Collection instances = result.allInstances(); - Collection added = new HashSet(instances); - added.removeAll (currentStorages); - Collection removed = new HashSet(currentStorages); - removed.removeAll (instances); - currentStorages.clear(); - for (LibraryProvider storage : instances) { - this.currentStorages.add (storage); - for (LibraryImplementation impl : storage.getLibraries()) { - l.add(LibraryFactory.createLibrary(impl)); - } - } - for (LibraryProvider p : removed) { - p.removePropertyChangeListener(this.plistener); - } - for (LibraryProvider p : added) { - p.addPropertyChangeListener(this.plistener); - } this.cache = l; } return this.cache.toArray(new Library[this.cache.size()]); @@ -160,9 +225,11 @@ * @throws IOException when the library cannot be stored * @throws IllegalArgumentException if the library is not recognized by any * {@link org.netbeans.spi.project.libraries.LibraryTypeProvider} or the library - * of the same name already exists. + * of the same name already exists, or if this manager is not {@link #getDefault}. * @since org.netbeans.modules.project.libraries/1 1.14 + * @deprecated Use {@link #createLibrary} instead, as this properly supports local managers. */ + @Deprecated public void addLibrary (final Library library) throws IOException, IllegalArgumentException { assert library != null; if (LibrariesSupport.getLibraryTypeProvider(library.getType()) == null) { @@ -176,7 +243,41 @@ assert providers.size() == 1; providers.iterator().next().addLibrary(library.getLibraryImplementation()); } - + + /** + * Creates a new library definition and adds it to the list. + * @param type the type of library, as in {@link LibraryTypeProvider#getLibraryType} or {@link LibraryImplementation#getType} + * @param name the identifying name of the new library (must not duplicate a name already in use by a library in this manager) + * @param contents the initial contents of the library's volumes, as a map from volume type to volume content + * @return a newly created library + * @throws IOException if the new definition could not be stored + * @throws IllegalArgumentException if the library type or one of the content volume types is not supported, + * or if a library of the same name already exists in this manager + * @see ArealLibraryProvider#createLibrary + * @since org.netbeans.modules.project.libraries/1 1.15 + */ + public Library createLibrary(String type, String name, Map> contents) throws IOException { + if (getLibrary(name) != null) { + throw new IllegalArgumentException("Name already in use: " + name); // NOI18N + } + LibraryImplementation impl; + if (area == null) { + LibraryTypeProvider ltp = LibrariesSupport.getLibraryTypeProvider(type); + if (ltp == null) { + throw new IllegalArgumentException("Trying to add a library of unknown type: " + type); // NOI18N + } + impl = ltp.createLibrary(); + impl.setName(name); + for (Map.Entry> entry : contents.entrySet()) { + impl.setContent(entry.getKey(), entry.getValue()); + } + Lookup.getDefault().lookup(WritableLibraryProvider.class).addLibrary(impl); + } else { + impl = LibraryAccessor.createLibrary(alp, type, name, area, contents); + } + return new Library(impl, this); + } + /** * Removes installed library * @param library to be removed. @@ -187,9 +288,13 @@ */ public void removeLibrary (final Library library) throws IOException, IllegalArgumentException { assert library != null; - final Collection providers = Lookup.getDefault().lookupAll(WritableLibraryProvider.class); - assert providers.size() == 1; - providers.iterator().next().removeLibrary(library.getLibraryImplementation()); + if (area == null) { + final Collection providers = Lookup.getDefault().lookupAll(WritableLibraryProvider.class); + assert providers.size() == 1; + providers.iterator().next().removeLibrary(library.getLibraryImplementation()); + } else { + LibraryAccessor.remove(alp, library.getLibraryImplementation()); + } } /** @@ -229,7 +334,109 @@ return instance; } + /** + * Gets a library manager which loads library definitions from a particular location. + * There is no guarantee that the return value is the same object from call to call with the same location. + * @param location any storage location supported by an installed provider + * @return a library manager whose {@link #getLocation} matches the supplied location + * @throws IllegalArgumentException if no installed provider is able to manage locations of this kind + * @see ArealLibraryProvider#loadArea + * @see ArealLibraryProvider#getLibraries + * @since org.netbeans.modules.project.libraries/1 1.15 + */ + public static LibraryManager forLocation(URL location) throws IllegalArgumentException { + for (ArealLibraryProvider alp : Lookup.getDefault().lookupAll(ArealLibraryProvider.class)) { + LibraryStorageArea area = alp.loadArea(location); + if (area != null) { + return new LibraryManager(alp, area); + } + } + throw new IllegalArgumentException(location.toExternalForm()); + } + + /** + * Gets an unspecified collection of managers which are somehow to be represented as open. + * For example, library storages referred to from open projects might be returned. + * You can listen on changes in list of open managers via {@link #addOpenManagersPropertyChangeListener}. + * There is no guarantee that the non-default managers are the same objects from call to call + * even if the locations remain the same. + * @see ArealLibraryProvider#getOpenAreas + * @return a set of managers, always including at least {@link #getDefault} + * @since org.netbeans.modules.project.libraries/1 1.15 + */ + public static Collection getOpenManagers() { + List managers = new ArrayList(); + managers.add(getDefault()); + Set locations = new HashSet(); + for (ArealLibraryProvider alp : Lookup.getDefault().lookupAll(ArealLibraryProvider.class)) { + for (LibraryStorageArea area : LibraryAccessor.getOpenAreas(alp)) { + if (locations.add(area.getLocation())) { + managers.add(new LibraryManager(alp, area)); + } + } + } + for (ArealLibraryProvider alp : Lookup.getDefault().lookupAll(ArealLibraryProvider.class)) { + for (URL location : LibrariesModel.createdAreas) { + LibraryStorageArea area = alp.loadArea(location); + if (area != null) { + assert area.getLocation().equals(location) : "Bad location " + area.getLocation() + " does not match " + location + " from " + alp.getClass().getName(); + if (locations.add(location)) { + managers.add(new LibraryManager(alp, area)); + } + } + } + } + return managers; + } + + /** + * Adds PropertyChangeListener on list of open library managers. + * The listener is notified when list of open library managers changes via + * {@link #PROP_OPEN_LIBRARY_MANAGERS}. + * @param listener to be notified + */ + public static synchronized void addOpenManagersPropertyChangeListener (PropertyChangeListener listener) { + assert listener != null; + if (areaProvidersLookupResult == null) { + areaProvidersLookupResult = Lookup.getDefault().lookupResult(ArealLibraryProvider.class); + attachListeners(areaProvidersLookupResult.allInstances()); + areaProvidersLookupResult.addLookupListener(new LookupListener() { + public void resultChanged(LookupEvent ev) { + attachListeners(areaProvidersLookupResult.allInstances()); + } + }); + } + openLibraryManagerListListeners.addPropertyChangeListener (listener); + } + + private static synchronized void attachListeners(Collection currentProviders) { + for (ArealLibraryProvider provider : currentAreaProviders) { + provider.removePropertyChangeListener(AREAL_LIBRARY_PROVIDER_LISTENER); + } + for (ArealLibraryProvider provider : currentProviders) { + provider.addPropertyChangeListener(AREAL_LIBRARY_PROVIDER_LISTENER); + } + currentAreaProviders = currentProviders; + } + + /** + * Removes PropertyChangeListener + * @param listener + */ + public static void removeOpenManagersPropertyChangeListener (PropertyChangeListener listener) { + assert listener != null; + openLibraryManagerListListeners.removePropertyChangeListener (listener); + } + + @Override + public String toString() { + URL loc = getLocation(); + return "LibraryManager[" + (loc != null ? loc : "default") + "]"; // NOI18N + } + LibraryStorageArea getArea() { + return area; + } } // end LibraryManager Index: projects/libraries/src/org/netbeans/modules/project/libraries/DefaultLibraryImplementation.java diff -u projects/libraries/src/org/netbeans/modules/project/libraries/DefaultLibraryImplementation.java:1.8 projects/libraries/src/org/netbeans/modules/project/libraries/DefaultLibraryImplementation.java:1.7.8.2 --- projects/libraries/src/org/netbeans/modules/project/libraries/DefaultLibraryImplementation.java:1.8 Wed Sep 26 14:04:19 2007 +++ projects/libraries/src/org/netbeans/modules/project/libraries/DefaultLibraryImplementation.java Thu Nov 22 08:40:02 2007 @@ -139,8 +139,8 @@ this.listeners.remove (l); } - public String toString () { - return "LibraryImplementation[Name="+this.name+"]"; //NOI18N + public @Override String toString() { + return "DefaultLibraryImplementation[" + name + "]"; // NOI18N } private void firePropertyChange (String propName, Object oldValue, Object newValue) { Index: projects/libraries/src/org/netbeans/modules/project/libraries/LibrariesStorage.java diff -u projects/libraries/src/org/netbeans/modules/project/libraries/LibrariesStorage.java:1.26 projects/libraries/src/org/netbeans/modules/project/libraries/LibrariesStorage.java:1.25.14.2 --- projects/libraries/src/org/netbeans/modules/project/libraries/LibrariesStorage.java:1.26 Wed Sep 26 14:04:19 2007 +++ projects/libraries/src/org/netbeans/modules/project/libraries/LibrariesStorage.java Thu Nov 22 08:40:05 2007 @@ -76,7 +76,7 @@ import org.xml.sax.InputSource; import org.xml.sax.SAXException; -public class LibrariesStorage extends FileChangeAdapter implements WritableLibraryProvider { +public class LibrariesStorage extends FileChangeAdapter implements WritableLibraryProvider { private static final String NB_HOME_PROPERTY = "netbeans.home"; //NOI18N private static final String LIBRARIES_REPOSITORY = "org-netbeans-api-project-libraries/Libraries"; //NOI18N Index: projects/libraries/src/org/netbeans/modules/project/libraries/LibraryAccessor.java diff -u projects/libraries/src/org/netbeans/modules/project/libraries/LibraryAccessor.java:1.2 projects/libraries/src/org/netbeans/modules/project/libraries/LibraryAccessor.java:1.1.14.2 --- projects/libraries/src/org/netbeans/modules/project/libraries/LibraryAccessor.java:1.2 Wed Sep 26 14:04:20 2007 +++ projects/libraries/src/org/netbeans/modules/project/libraries/LibraryAccessor.java Thu Nov 22 08:40:06 2007 @@ -41,14 +41,18 @@ package org.netbeans.modules.project.libraries; +import java.io.IOException; +import java.net.URL; +import java.util.List; +import java.util.Map; +import java.util.Set; import org.netbeans.api.project.libraries.Library; +import org.netbeans.spi.project.libraries.ArealLibraryProvider; import org.netbeans.spi.project.libraries.LibraryImplementation; +import org.netbeans.spi.project.libraries.LibraryProvider; +import org.netbeans.spi.project.libraries.LibraryStorageArea; import org.openide.util.Exceptions; -/** - * - * @author Tomas Zezula - */ public abstract class LibraryAccessor { public static LibraryAccessor DEFAULT; @@ -63,5 +67,47 @@ } public abstract Library createLibrary (LibraryImplementation libraryImplementation); - + + // RADIKAL GENERIC HAX! + + /** + * Type-safe accessor for {@link ArealLibraryProvider#remove}. + * @throws ClassCastException if the runtime types do not match + */ + public static void remove(ArealLibraryProvider alp, LibraryImplementation lib) throws IOException { + remove0((ArealLibraryProvider) alp, lib); + } + private static void remove0(ArealLibraryProvider alp, LibraryImplementation lib) throws IOException { + alp.remove(alp.libraryType().cast(lib)); + } + + /** + * Type-safe accessor for {@link ArealLibraryProvider#getOpenAreas}. + */ + public static Set getOpenAreas(ArealLibraryProvider alp) { + return ((ArealLibraryProvider) alp).getOpenAreas(); + } + + /** + * Type-safe accessor for {@link ArealLibraryProvider#createLibrary}. + * @throws ClassCastException if the runtime types do not match + */ + public static LibraryImplementation createLibrary(ArealLibraryProvider alp, String type, String name, LibraryStorageArea area, Map> contents) throws IOException { + return createLibrary0(((ArealLibraryProvider) alp), type, name, area, contents); + } + private static LibraryImplementation createLibrary0(ArealLibraryProvider alp, String type, String name, LibraryStorageArea area, Map> contents) throws IOException { + return alp.createLibrary(type, name, alp.areaType().cast(area), contents); + } + + /** + * Type-safe accessor for {@link ArealLibraryProvider#getLibraries}. + * @throws ClassCastException if the runtime types do not match + */ + public static LibraryProvider getLibraries(ArealLibraryProvider alp, LibraryStorageArea area) { + return getLibraries0((ArealLibraryProvider) alp, area); + } + private static LibraryProvider getLibraries0(ArealLibraryProvider alp, LibraryStorageArea area) { + return alp.getLibraries(alp.areaType().cast(area)); + } + } Index: projects/libraries/src/org/netbeans/modules/project/libraries/WritableLibraryProvider.java diff -u projects/libraries/src/org/netbeans/modules/project/libraries/WritableLibraryProvider.java:1.2 projects/libraries/src/org/netbeans/modules/project/libraries/WritableLibraryProvider.java:1.1.16.2 --- projects/libraries/src/org/netbeans/modules/project/libraries/WritableLibraryProvider.java:1.2 Wed Sep 26 14:04:20 2007 +++ projects/libraries/src/org/netbeans/modules/project/libraries/WritableLibraryProvider.java Thu Nov 22 08:40:15 2007 @@ -44,7 +44,7 @@ import org.netbeans.spi.project.libraries.LibraryProvider; import java.io.IOException; -public interface WritableLibraryProvider extends LibraryProvider { +public interface WritableLibraryProvider extends LibraryProvider { void addLibrary(LibraryImplementation library) throws IOException; Index: projects/libraries/src/org/netbeans/modules/project/libraries/ui/AllLibrariesCustomizer.form diff -u /dev/null projects/libraries/src/org/netbeans/modules/project/libraries/ui/AllLibrariesCustomizer.form:1.1.2.1 --- /dev/null Fri Jan 18 01:33:14 2008 +++ projects/libraries/src/org/netbeans/modules/project/libraries/ui/AllLibrariesCustomizer.form Tue Jan 15 13:52:11 2008 @@ -0,0 +1,64 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Index: projects/libraries/src/org/netbeans/modules/project/libraries/ui/AllLibrariesCustomizer.java diff -u /dev/null projects/libraries/src/org/netbeans/modules/project/libraries/ui/AllLibrariesCustomizer.java:1.1.2.1 --- /dev/null Fri Jan 18 01:33:14 2008 +++ projects/libraries/src/org/netbeans/modules/project/libraries/ui/AllLibrariesCustomizer.java Tue Jan 15 13:52:11 2008 @@ -0,0 +1,166 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Microsystems, Inc. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ + +package org.netbeans.modules.project.libraries.ui; + +import org.netbeans.api.project.libraries.*; +import org.netbeans.modules.project.libraries.ui.*; +import java.io.File; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import javax.swing.DefaultComboBoxModel; +import org.netbeans.api.project.libraries.LibraryManager; +import org.netbeans.modules.project.libraries.LibraryAccessor; +import org.netbeans.spi.project.libraries.ArealLibraryProvider; +import org.netbeans.spi.project.libraries.LibraryStorageArea; +import org.netbeans.spi.project.libraries.support.LibrariesSupport; +import org.openide.util.Lookup; +import org.openide.util.NbBundle; + +/** + * + */ +class AllLibrariesCustomizer extends javax.swing.JPanel { + + private org.netbeans.modules.project.libraries.ui.LibrariesCustomizer librariesCustomizer; + /** Creates new form AllLibrariesCustomizer */ + public AllLibrariesCustomizer() { + initComponents(); + librariesCustomizer = new org.netbeans.modules.project.libraries.ui.LibrariesCustomizer(null); + placeholder.add(librariesCustomizer); + initModel(); + } + + public boolean apply() { + return librariesCustomizer.apply(); + } + + private void initModel() { + List items = new ArrayList(); + items.add(NbBundle.getMessage(AllLibrariesCustomizer.class, "LABEL_Global_Libraries")); + for (LibraryManager man : LibraryManager.getOpenManagers()) { + if (man.getLocation() == null) { + continue; + } + items.add(LibrariesSupport.convertURLToFile(man.getLocation()).getPath()); + } + libraryManagerComboBox.setModel(new DefaultComboBoxModel(items.toArray(new String[items.size()]))); + } + + /** This method is called from within the constructor to + * initialize the form. + * WARNING: Do NOT modify this code. The content of this method is + * always regenerated by the Form Editor. + */ + // //GEN-BEGIN:initComponents + private void initComponents() { + + libraryManagerComboBox = new javax.swing.JComboBox(); + placeholder = new javax.swing.JPanel(); + jLabel1 = new javax.swing.JLabel(); + + libraryManagerComboBox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + libraryManagerComboBoxActionPerformed(evt); + } + }); + + placeholder.setLayout(new java.awt.BorderLayout()); + + jLabel1.setText(java.text.MessageFormat.format(org.openide.util.NbBundle.getMessage(AllLibrariesCustomizer.class, "AllLibrariesCustomizer.jLabel1.text"), new Object[] {})); // NOI18N + + org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup() + .addContainerGap() + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING) + .add(org.jdesktop.layout.GroupLayout.LEADING, placeholder, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 377, Short.MAX_VALUE) + .add(layout.createSequentialGroup() + .add(jLabel1) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(libraryManagerComboBox, 0, 289, Short.MAX_VALUE))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) + .add(layout.createSequentialGroup() + .addContainerGap() + .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) + .add(jLabel1) + .add(libraryManagerComboBox, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) + .add(placeholder, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 230, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + private void libraryManagerComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_libraryManagerComboBoxActionPerformed + int index = libraryManagerComboBox.getSelectedIndex(); + if (index == -1) { + return; + } else if (index == 0) { + librariesCustomizer.setLibraryStorageArea(null); + } else if (index > 0) { + URL u = LibrariesSupport.convertFileToURL(new File ((String)libraryManagerComboBox.getModel().getSelectedItem())); + librariesCustomizer.setLibraryStorageArea(findLibraryStorageArea(u)); + } + }//GEN-LAST:event_libraryManagerComboBoxActionPerformed + + private LibraryStorageArea findLibraryStorageArea(URL u) { + for (ArealLibraryProvider alp : Lookup.getDefault().lookupAll(ArealLibraryProvider.class)) { + for (LibraryStorageArea area : LibraryAccessor.getOpenAreas(alp)) { + if (u.equals(area.getLocation())) { + return area; + } + } + } + return null; + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel jLabel1; + private javax.swing.JComboBox libraryManagerComboBox; + private javax.swing.JPanel placeholder; + // End of variables declaration//GEN-END:variables + +} Index: projects/libraries/src/org/netbeans/modules/project/libraries/ui/Bundle.properties diff -u projects/libraries/src/org/netbeans/modules/project/libraries/ui/Bundle.properties:1.24 projects/libraries/src/org/netbeans/modules/project/libraries/ui/Bundle.properties:1.22.6.5 --- projects/libraries/src/org/netbeans/modules/project/libraries/ui/Bundle.properties:1.24 Wed Sep 26 14:04:21 2007 +++ projects/libraries/src/org/netbeans/modules/project/libraries/ui/Bundle.properties Tue Jan 15 13:52:11 2008 @@ -63,3 +63,9 @@ AD_NewLibraryPanel=N/A TXT_LibrariesPanel=&Libraries\: +LBL_global=Global Libraries + +areaLabel.text=Storage &Location\: +LABEL_Global_Libraries=Global Libraries +TXT_LibrariesManager=Library Manager +AllLibrariesCustomizer.jLabel1.text=Libraries location: Index: projects/libraries/src/org/netbeans/modules/project/libraries/ui/LibrariesCustomizer.form diff -u projects/libraries/src/org/netbeans/modules/project/libraries/ui/LibrariesCustomizer.form:1.10 projects/libraries/src/org/netbeans/modules/project/libraries/ui/LibrariesCustomizer.form:1.10.92.4 --- projects/libraries/src/org/netbeans/modules/project/libraries/ui/LibrariesCustomizer.form:1.10 Fri Apr 15 05:47:54 2005 +++ projects/libraries/src/org/netbeans/modules/project/libraries/ui/LibrariesCustomizer.form Tue Jan 15 13:52:12 2008 @@ -1,17 +1,26 @@ -
+ + + + + + + + + + - + @@ -27,7 +36,7 @@ - + @@ -42,14 +51,14 @@ - + - + @@ -95,7 +104,7 @@ - + @@ -134,7 +143,7 @@ - + @@ -151,7 +160,7 @@ - + Index: projects/libraries/src/org/netbeans/modules/project/libraries/ui/LibrariesCustomizer.java diff -u projects/libraries/src/org/netbeans/modules/project/libraries/ui/LibrariesCustomizer.java:1.28 projects/libraries/src/org/netbeans/modules/project/libraries/ui/LibrariesCustomizer.java:1.27.14.14 --- projects/libraries/src/org/netbeans/modules/project/libraries/ui/LibrariesCustomizer.java:1.28 Wed Sep 26 14:04:21 2007 +++ projects/libraries/src/org/netbeans/modules/project/libraries/ui/LibrariesCustomizer.java Tue Jan 15 13:52:12 2008 @@ -43,6 +43,7 @@ import java.awt.Dialog; import java.awt.Dimension; +import java.awt.EventQueue; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Image; @@ -54,16 +55,22 @@ import java.beans.PropertyVetoException; import java.beans.VetoableChangeListener; import java.io.IOException; +import java.net.URL; +import java.text.Collator; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; import java.util.MissingResourceException; import java.util.ResourceBundle; import javax.swing.JComponent; import javax.swing.JPanel; -import javax.swing.event.ListDataEvent; -import javax.swing.event.ListDataListener; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; import org.netbeans.modules.project.libraries.LibraryTypeRegistry; import org.netbeans.spi.project.libraries.LibraryImplementation; +import org.netbeans.spi.project.libraries.LibraryStorageArea; import org.netbeans.spi.project.libraries.LibraryTypeProvider; import org.openide.DialogDescriptor; import org.openide.DialogDisplayer; @@ -80,38 +87,73 @@ import org.openide.nodes.NodeOp; import org.openide.util.HelpCtx; import org.openide.util.NbBundle; +import org.openide.util.Utilities; import org.openide.util.lookup.Lookups; -/** - * - * @author tom - */ public final class LibrariesCustomizer extends JPanel implements ExplorerManager.Provider, HelpCtx.Provider { private ExplorerManager manager; private LibrariesModel model; private BeanTreeView libraries; + private LibraryStorageArea libraryStorageArea; - /** Creates new form LibrariesCustomizer */ - public LibrariesCustomizer () { + public LibrariesCustomizer (LibraryStorageArea libraryStorageArea) { this.model = new LibrariesModel (); + this.libraryStorageArea = (libraryStorageArea != null ? libraryStorageArea : LibrariesModel.GLOBAL_AREA); initComponents(); postInitComponents (); + expandTree(); + } + + private void expandTree() { + // get first library node + Node[] n = getExplorerManager().getRootContext().getChildren().getNodes()[0].getChildren().getNodes(); + if (n.length != 0) { + try { + getExplorerManager().setSelectedNodes(new Node[]{n[0]}); + } catch (PropertyVetoException ex) { + // OK to ignore - it is just selection initialization + } + } + } + + public void setLibraryStorageArea(LibraryStorageArea libraryStorageArea) { + this.libraryStorageArea = (libraryStorageArea != null ? libraryStorageArea : LibrariesModel.GLOBAL_AREA); + forceTreeRecreation(); + expandTree(); + } + + public LibrariesModel getModel() { + return model; + } + + public void hideLibrariesList() { + libsPanel.setVisible(false); + jLabel2.setVisible(false); + createButton.setVisible(false); + deleteButton.setVisible(false); + } + + /** + * Force nodes recreation after LibrariesModel change. The nodes listen on + * model and eventually refresh themselves but usually it is too late. + * So forcing recreation makes sure that any subsequent call to + * NodeOp.findPath is successful and selects just created library node. + */ + public void forceTreeRecreation() { + getExplorerManager().setRootContext(buildTree()); } - public void setSelectedLibrary (LibraryImplementation library) { if (library == null) return; - ExplorerManager manager = this.getExplorerManager(); - Node root = manager.getRootContext(); - String[] path = new String[2]; - path[0]=library.getType(); - path[1]=library.getName(); + ExplorerManager currentManager = this.getExplorerManager(); + Node root = currentManager.getRootContext(); + String[] path = {library.getType(), library.getName()}; try { Node node = NodeOp.findPath(root, path); if (node != null) { - manager.setSelectedNodes(new Node[] {node}); + currentManager.setSelectedNodes(new Node[] {node}); } } catch (NodeNotFoundException e) { //Ignore it @@ -135,33 +177,27 @@ } } - public void cancel () { - this.model.cancel(); - } - public void addNotify() { super.addNotify(); expandAllNodes(this.libraries,this.getExplorerManager().getRootContext()); //Select first library if nothing selected if (this.getExplorerManager().getSelectedNodes().length == 0) { - Node root = this.getExplorerManager().getRootContext(); - Node[] nodes = root.getChildren().getNodes (true); - for (int i = 0; i< nodes.length; i++) { - Node[] lnodes = nodes[i].getChildren().getNodes(true); - if (lnodes.length > 0) { - try { - this.getExplorerManager().setSelectedNodes(new Node[] {lnodes[0]}); - } catch (PropertyVetoException e) { - //Ignore it + SELECTED: for (Node areaNode : getExplorerManager().getRootContext().getChildren().getNodes(true)) { + for (Node typeNode : areaNode.getChildren().getNodes(true)) { + for (Node libNode : typeNode.getChildren().getNodes(true)) { + try { + getExplorerManager().setSelectedNodes(new Node[] {libNode}); + } catch (PropertyVetoException e) { + //Ignore it + } + break SELECTED; } - break; } } } this.libraries.requestFocus(); } - public ExplorerManager getExplorerManager () { if (this.manager == null) { this.manager = new ExplorerManager (); @@ -187,12 +223,11 @@ } } }); - this.manager.setRootContext (buildTree(this.model)); + manager.setRootContext(buildTree()); } return this.manager; } - private void postInitComponents () { this.libraries = new LibrariesView (); GridBagConstraints c = new GridBagConstraints (); @@ -217,19 +252,23 @@ }); } - private void nameChanged () { Node[] nodes = this.getExplorerManager().getSelectedNodes(); - if (nodes.length == 1 && (nodes[0] instanceof LibraryNode)) { - LibraryNode node = (LibraryNode) nodes[0]; + if (nodes.length == 1) { + LibraryImplementation lib = nodes[0].getLookup().lookup(LibraryImplementation.class); + if (lib == null) { + return; + } String newName = this.libraryName.getText(); + if (newName.equals(lib.getName())) { + return; + } if (newName.length () == 0) { DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message ( NbBundle.getMessage(LibrariesCustomizer.class, "ERR_InvalidName"), NotifyDescriptor.ERROR_MESSAGE)); - } - else if (isValidName (this.model, newName)) { - node.getLibrary().setName(newName); + } else if (isValidName(model, newName, model.getArea(lib))) { + lib.setName(newName); } else { DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message ( @@ -239,7 +278,6 @@ } } - private void selectLibrary (Node[] nodes) { int tabCount = this.properties.getTabCount(); for (int i=0; i//GEN-BEGIN:initComponents + // //GEN-BEGIN:initComponents private void initComponents() { java.awt.GridBagConstraints gridBagConstraints; @@ -301,13 +348,14 @@ libsPanel = new javax.swing.JPanel(); jLabel2 = new javax.swing.JLabel(); + setMinimumSize(new java.awt.Dimension(642, 395)); setLayout(new java.awt.GridBagLayout()); - getAccessibleContext().setAccessibleDescription(java.util.ResourceBundle.getBundle("org/netbeans/modules/project/libraries/ui/Bundle").getString("AD_LibrariesCustomizer")); jLabel1.setLabelFor(libraryName); - org.openide.awt.Mnemonics.setLocalizedText(jLabel1, java.util.ResourceBundle.getBundle("org/netbeans/modules/project/libraries/ui/Bundle").getString("CTL_CustomizerLibraryName")); + java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("org/netbeans/modules/project/libraries/ui/Bundle"); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(jLabel1, bundle.getString("CTL_CustomizerLibraryName")); // NOI18N gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 2; + gridBagConstraints.gridx = 3; gridBagConstraints.gridy = 1; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.insets = new java.awt.Insets(0, 6, 12, 6); @@ -315,96 +363,96 @@ libraryName.setEditable(false); gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 3; + gridBagConstraints.gridx = 4; gridBagConstraints.gridy = 1; gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.weightx = 0.6; - gridBagConstraints.insets = new java.awt.Insets(0, 0, 12, 12); + gridBagConstraints.insets = new java.awt.Insets(0, 0, 12, 0); add(libraryName, gridBagConstraints); - libraryName.getAccessibleContext().setAccessibleDescription(java.util.ResourceBundle.getBundle("org/netbeans/modules/project/libraries/ui/Bundle").getString("AD_LibraryName")); + libraryName.getAccessibleContext().setAccessibleDescription(bundle.getString("AD_LibraryName")); // NOI18N jPanel1.setLayout(new java.awt.BorderLayout()); properties.setPreferredSize(new java.awt.Dimension(400, 300)); jPanel1.add(properties, java.awt.BorderLayout.CENTER); - properties.getAccessibleContext().setAccessibleName(java.util.ResourceBundle.getBundle("org/netbeans/modules/project/libraries/ui/Bundle").getString("AN_LibrariesCustomizerProperties")); - properties.getAccessibleContext().setAccessibleDescription(java.util.ResourceBundle.getBundle("org/netbeans/modules/project/libraries/ui/Bundle").getString("AD_LibrariesCustomizerProperties")); + properties.getAccessibleContext().setAccessibleName(bundle.getString("AN_LibrariesCustomizerProperties")); // NOI18N + properties.getAccessibleContext().setAccessibleDescription(bundle.getString("AD_LibrariesCustomizerProperties")); // NOI18N gridBagConstraints = new java.awt.GridBagConstraints(); - gridBagConstraints.gridx = 2; + gridBagConstraints.gridx = 3; gridBagConstraints.gridy = 2; gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER; gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.weightx = 1.0; gridBagConstraints.weighty = 1.0; - gridBagConstraints.insets = new java.awt.Insets(0, 6, 12, 12); + gridBagConstraints.insets = new java.awt.Insets(0, 6, 12, 0); add(jPanel1, gridBagConstraints); - org.openide.awt.Mnemonics.setLocalizedText(createButton, java.util.ResourceBundle.getBundle("org/netbeans/modules/project/libraries/ui/Bundle").getString("CTL_NewLibrary")); + org.openide.awt.Mnemonics.setLocalizedText(createButton, bundle.getString("CTL_NewLibrary")); // NOI18N createButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { createLibrary(evt); } }); - gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 3; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new java.awt.Insets(0, 12, 0, 12); + gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 12); add(createButton, gridBagConstraints); - createButton.getAccessibleContext().setAccessibleDescription(java.util.ResourceBundle.getBundle("org/netbeans/modules/project/libraries/ui/Bundle").getString("AD_NewLibrary")); + createButton.getAccessibleContext().setAccessibleDescription(bundle.getString("AD_NewLibrary")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(deleteButton, java.util.ResourceBundle.getBundle("org/netbeans/modules/project/libraries/ui/Bundle").getString("CTL_DeleteLibrary")); + org.openide.awt.Mnemonics.setLocalizedText(deleteButton, bundle.getString("CTL_DeleteLibrary")); // NOI18N deleteButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { deleteLibrary(evt); } }); - gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 3; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 12); add(deleteButton, gridBagConstraints); - deleteButton.getAccessibleContext().setAccessibleDescription(java.util.ResourceBundle.getBundle("org/netbeans/modules/project/libraries/ui/Bundle").getString("AD_DeleteLibrary")); + deleteButton.getAccessibleContext().setAccessibleDescription(bundle.getString("AD_DeleteLibrary")); // NOI18N + libsPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder()); libsPanel.setLayout(new java.awt.GridBagLayout()); - - libsPanel.setBorder(new javax.swing.border.EtchedBorder()); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 1; - gridBagConstraints.gridwidth = 2; + gridBagConstraints.gridwidth = 3; gridBagConstraints.gridheight = 2; gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.weighty = 1.0; - gridBagConstraints.insets = new java.awt.Insets(0, 12, 12, 6); + gridBagConstraints.insets = new java.awt.Insets(0, 0, 12, 6); add(libsPanel, gridBagConstraints); - libsPanel.getAccessibleContext().setAccessibleDescription(java.util.ResourceBundle.getBundle("org/netbeans/modules/project/libraries/ui/Bundle").getString("AD_libsPanel")); + libsPanel.getAccessibleContext().setAccessibleDescription(bundle.getString("AD_libsPanel")); // NOI18N jLabel2.setLabelFor(libsPanel); - org.openide.awt.Mnemonics.setLocalizedText(jLabel2, java.util.ResourceBundle.getBundle("org/netbeans/modules/project/libraries/ui/Bundle").getString("TXT_LibrariesPanel")); + org.openide.awt.Mnemonics.setLocalizedText(jLabel2, bundle.getString("TXT_LibrariesPanel")); // NOI18N gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 0; - gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; - gridBagConstraints.insets = new java.awt.Insets(12, 12, 2, 12); + gridBagConstraints.insets = new java.awt.Insets(0, 0, 2, 12); add(jLabel2, gridBagConstraints); - } - // //GEN-END:initComponents + getAccessibleContext().setAccessibleDescription(bundle.getString("AD_LibrariesCustomizer")); // NOI18N + }// //GEN-END:initComponents private void deleteLibrary(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteLibrary Node[] nodes = this.getExplorerManager().getSelectedNodes(); - if (nodes.length == 1 && (nodes[0] instanceof LibraryNode)) { + if (nodes.length == 1) { + LibraryImplementation library = nodes[0].getLookup().lookup(LibraryImplementation.class); + if (library == null) { + return; + } Node[] sib = nodes[0].getParentNode().getChildren().getNodes(true); Node selNode = null; for (int i=0; i < sib.length; i++) { @@ -417,7 +465,7 @@ } } } - model.removeLibrary (((LibraryNode)nodes[0]).getLibrary()); + model.removeLibrary(library); try { if (selNode != null) { this.getExplorerManager().setSelectedNodes(new Node[] {selNode}); @@ -433,14 +481,19 @@ Dialog dlg = null; try { String preselectedLibraryType = null; + LibraryStorageArea area = null; Node[] preselectedNodes = this.getExplorerManager().getSelectedNodes(); if (preselectedNodes.length == 1) { - LibraryCategory lc = preselectedNodes[0].getLookup().lookup(LibraryCategory.class); - if (lc != null) { - preselectedLibraryType = lc.getCategoryType(); + LibraryTypeProvider provider = preselectedNodes[0].getLookup().lookup(LibraryTypeProvider.class); + if (provider != null) { + preselectedLibraryType = provider.getLibraryType(); } + area = preselectedNodes[0].getLookup().lookup(LibraryStorageArea.class); + } + if (area == null) { + area = LibrariesModel.GLOBAL_AREA; } - NewLibraryPanel p = new NewLibraryPanel (this.model, preselectedLibraryType); + NewLibraryPanel p = new NewLibraryPanel(model, preselectedLibraryType, area); DialogDescriptor dd = new DialogDescriptor (p, NbBundle.getMessage(LibrariesCustomizer.class,"CTL_CreateLibrary"), true, DialogDescriptor.OK_CANCEL_OPTION, null, null); p.setDialogDescriptor(dd); @@ -448,17 +501,21 @@ dlg.setVisible(true); if (dd.getValue() == DialogDescriptor.OK_OPTION) { String libraryType = p.getLibraryType(); - String libraryName = p.getLibraryName(); - LibraryTypeProvider provider = LibraryTypeRegistry.getDefault().getLibraryTypeProvider (libraryType); - if (provider == null) { - return; + String currentLibraryName = p.getLibraryName(); + LibraryImplementation impl; + if (area != LibrariesModel.GLOBAL_AREA) { + impl = model.createArealLibrary(libraryType, currentLibraryName, area); + } else { + LibraryTypeProvider provider = LibraryTypeRegistry.getDefault().getLibraryTypeProvider(libraryType); + if (provider == null) { + return; + } + impl = provider.createLibrary(); + impl.setName(currentLibraryName); } - LibraryImplementation impl = provider.createLibrary(); - impl.setName (libraryName); model.addLibrary (impl); - String[] path = new String[2]; - path[0] = impl.getType(); - path[1] = impl.getName(); + forceTreeRecreation(); + String[] path = {impl.getType(), impl.getName()}; ExplorerManager mgr = this.getExplorerManager(); try { Node node = NodeOp.findPath(mgr.getRootContext(),path); @@ -484,18 +541,15 @@ } }//GEN-LAST:event_createLibrary - - static boolean isValidName (LibrariesModel model, String name) { - int count = model.getSize(); - for (int i=0; i { - - private LibrariesModel model; - - public RootChildren (LibrariesModel model) { - this.model = model; + private class AreaChildren extends Children.Keys implements ChangeListener { + + @Override + protected void addNotify() { + super.addNotify(); + model.addChangeListener(this); + computeKeys(); } - + + @Override + protected void removeNotify() { + super.removeNotify(); + model.removeChangeListener(this); + setKeys(Collections.emptySet()); + } + + private void computeKeys() { + setKeys(getSortedAreas(model)); + } + + protected Node[] createNodes(LibraryStorageArea area) { + return new Node[] {new AreaNode(area)}; + } + + public void stateChanged(ChangeEvent e) { + EventQueue.invokeLater(new Runnable() { + public void run() { + computeKeys(); + } + }); + } + + } + + static Collection getSortedAreas(LibrariesModel model) { + List areas = new ArrayList(model.getAreas()); + Collections.sort(areas,new Comparator() { + Collator COLL = Collator.getInstance(); + public int compare(LibraryStorageArea a1, LibraryStorageArea a2) { + return COLL.compare(a1.getDisplayName(), a2.getDisplayName()); + } + }); + areas.add(0, LibrariesModel.GLOBAL_AREA); + assert !areas.contains(null); + return areas; + } + + private final class AreaNode extends AbstractNode { + + private final LibraryStorageArea area; + + AreaNode(LibraryStorageArea area) { + super(new TypeChildren(area), Lookups.singleton(area)); + this.area = area; + } + + @Override + public String getName() { + return getDisplayName(); + } + + @Override + public String getDisplayName() { + return area.getDisplayName(); + } + + private Node delegate() { + return DataFolder.findFolder(Repository.getDefault().getDefaultFileSystem().getRoot()).getNodeDelegate(); + } + + public Image getIcon(int type) { + return delegate().getIcon(type); + } + + public Image getOpenedIcon(int type) { + return delegate().getOpenedIcon(type); + } + + } + + private class TypeChildren extends Children.Keys { + + private final LibraryStorageArea area; + + TypeChildren(LibraryStorageArea area) { + this.area = area; + } + public void addNotify () { + // Could also filter by area (would then need to listen to model too) this.setKeys(LibraryTypeRegistry.getDefault().getLibraryTypeProviders()); } @@ -576,34 +708,18 @@ } protected Node[] createNodes(LibraryTypeProvider provider) { - return new Node[] {new CategoryNode(provider, model)}; + return new Node[] {new CategoryNode(provider, area)}; } } - - private static final class LibraryCategory { - - private final String name; - - LibraryCategory (String name) { - this.name = name; - } - - public String getCategoryType () { - return this.name; - } - - } - - private static class CategoryNode extends AbstractNode { - + private class CategoryNode extends AbstractNode { private LibraryTypeProvider provider; private Node iconDelegate; - public CategoryNode (LibraryTypeProvider provider, LibrariesModel model) { - super (new CategoryChildren(provider, model), Lookups.singleton(new LibraryCategory (provider.getLibraryType()))); + public CategoryNode(LibraryTypeProvider provider, LibraryStorageArea area) { + super(new CategoryChildren(provider, area), Lookups.fixed(provider, area)); this.provider = provider; this.iconDelegate = DataFolder.findFolder (Repository.getDefault().getDefaultFileSystem().getRoot()).getNodeDelegate(); } @@ -625,23 +741,22 @@ } } - - private static class CategoryChildren extends Children.Keys implements ListDataListener { + + private class CategoryChildren extends Children.Keys implements ChangeListener { private LibraryTypeProvider provider; - private LibrariesModel model; + private final LibraryStorageArea area; - public CategoryChildren (LibraryTypeProvider provider, LibrariesModel model) { + public CategoryChildren(LibraryTypeProvider provider, LibraryStorageArea area) { this.provider = provider; - this.model = model; - this.model.addListDataListener(this); + this.area = area; + model.addChangeListener(this); } public void addNotify () { Collection keys = new ArrayList(); - for (int i=0; i getContent(String volumeType) throws IllegalArgumentException { + return lib.getContent(volumeType); + } + + public void setName(String name) { + lib.setName(name); + } + + public void setDescription(String text) { + lib.setDescription(text); + } + + public void setLocalizingBundle(String resourceName) { + lib.setLocalizingBundle(resourceName); + } + + public void addPropertyChangeListener(PropertyChangeListener l) { + lib.addPropertyChangeListener(l); + } + + public void removePropertyChangeListener(PropertyChangeListener l) { + lib.removePropertyChangeListener(l); + } + + public void setContent(String volumeType, List path) throws IllegalArgumentException { + lib.setContent(volumeType, path); + } + + public URL getLocation() { + return area.getLocation(); + } + + public String getDisplayName() { + return area.getDisplayName(); } } - - private static Node buildTree (LibrariesModel model) { - return new AbstractNode (new RootChildren (model)); - } - } Index: projects/libraries/src/org/netbeans/modules/project/libraries/ui/LibrariesCustomizerAction.java diff -u projects/libraries/src/org/netbeans/modules/project/libraries/ui/LibrariesCustomizerAction.java:1.5 projects/libraries/src/org/netbeans/modules/project/libraries/ui/LibrariesCustomizerAction.java:1.4.16.2 --- projects/libraries/src/org/netbeans/modules/project/libraries/ui/LibrariesCustomizerAction.java:1.5 Wed Sep 26 14:04:22 2007 +++ projects/libraries/src/org/netbeans/modules/project/libraries/ui/LibrariesCustomizerAction.java Tue Jan 15 13:52:12 2008 @@ -40,6 +40,9 @@ */ package org.netbeans.modules.project.libraries.ui; +import java.awt.Dialog; +import org.openide.DialogDescriptor; +import org.openide.DialogDisplayer; import org.openide.util.HelpCtx; import org.openide.util.NbBundle; import org.openide.util.actions.CallableSystemAction; @@ -54,7 +57,7 @@ } public void performAction() { - org.netbeans.api.project.libraries.LibrariesCustomizer.showCustomizer(null); + showCustomizer(); } public String getName() { @@ -68,5 +71,28 @@ protected boolean asynchronous () { return false; } + + /** + * Shows libraries customizer displaying all currently open library managers. + * @return true if user pressed OK and libraries were sucessfully modified + */ + private static boolean showCustomizer () { + AllLibrariesCustomizer customizer = + new AllLibrariesCustomizer(); + DialogDescriptor descriptor = new DialogDescriptor (customizer, + NbBundle.getMessage(LibrariesCustomizerAction.class, "TXT_LibrariesManager")); + Dialog dlg = DialogDisplayer.getDefault().createDialog(descriptor); + try { + dlg.setVisible(true); + if (descriptor.getValue() == DialogDescriptor.OK_OPTION) { + return customizer.apply(); + } else { + return false; + } + } finally { + dlg.dispose(); + } + } + } Index: projects/libraries/src/org/netbeans/modules/project/libraries/ui/LibrariesModel.java diff -u projects/libraries/src/org/netbeans/modules/project/libraries/ui/LibrariesModel.java:1.12 projects/libraries/src/org/netbeans/modules/project/libraries/ui/LibrariesModel.java:1.11.14.12 --- projects/libraries/src/org/netbeans/modules/project/libraries/ui/LibrariesModel.java:1.12 Wed Sep 26 14:04:22 2007 +++ projects/libraries/src/org/netbeans/modules/project/libraries/ui/LibrariesModel.java Tue Jan 15 13:52:12 2008 @@ -41,86 +41,173 @@ package org.netbeans.modules.project.libraries.ui; - +import java.awt.EventQueue; +import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; import java.io.IOException; -import java.util.*; - - +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.event.ChangeListener; +import org.netbeans.modules.project.libraries.LibraryAccessor; import org.netbeans.spi.project.libraries.LibraryImplementation; import org.netbeans.spi.project.libraries.LibraryProvider; import org.netbeans.modules.project.libraries.WritableLibraryProvider; +import org.netbeans.spi.project.libraries.ArealLibraryProvider; +import org.netbeans.spi.project.libraries.LibraryStorageArea; import org.openide.util.Lookup; -import org.openide.util.LookupListener; -import org.openide.util.LookupEvent; -import org.openide.ErrorManager; - -/** - * - * @author tom - */ -class LibrariesModel extends javax.swing.AbstractListModel implements PropertyChangeListener, LookupListener { - - private List actualLibraries; - private List addedLibraries; - private List removedLibraries; - private List changedLibraries; - private Collection currentStorages; - private Map storageByLib; - private Lookup.Result lresult; +import org.openide.util.ChangeSupport; +import org.openide.util.NbBundle; +import org.openide.util.WeakListeners; + +public class LibrariesModel implements PropertyChangeListener { + + public static final LibraryStorageArea GLOBAL_AREA = new LibraryStorageArea() { + public String getDisplayName() { + return NbBundle.getMessage(LibrariesModel.class, "LBL_global"); + } + public URL getLocation() { + throw new AssertionError(); + } + }; + + private static final Logger LOG = Logger.getLogger(LibrariesModel.class.getName()); + + /** + * Set of areas which have been explicitly created/loaded in this IDE session (thus static). + * Keep only URL, not LibraryStorageArea, to avoid memory leaks. + * Could also be modified to persist a LRU in NbPreferences, etc. + */ + public static final Set createdAreas = Collections.synchronizedSet(new HashSet()); + + private final Map library2Area = new HashMap(); + private final Map area2Storage = new HashMap(); + private final Map storageByLib = new HashMap(); + private final Map area2Provider = new HashMap(); + private final Collection actualLibraries = new TreeSet(new LibrariesComparator()); + private final List addedLibraries; + private final List removedLibraries; + private final List changedLibraries; private WritableLibraryProvider writableProvider; + private final ChangeSupport cs = new ChangeSupport(this); - /** Creates a new instance of LibrariesModel */ public LibrariesModel () { this.addedLibraries = new ArrayList(); this.removedLibraries = new ArrayList(); this.changedLibraries = new ArrayList(); - this.currentStorages = Collections.emptySet(); - this.storageByLib = new HashMap(); - this.getLibraries (); + for (LibraryProvider lp : Lookup.getDefault().lookupAll(LibraryProvider.class)) { + lp.addPropertyChangeListener(WeakListeners.propertyChange(this, lp)); + if (writableProvider == null && lp instanceof WritableLibraryProvider) { + writableProvider = (WritableLibraryProvider) lp; + } + } + for (ArealLibraryProvider alp : Lookup.getDefault().lookupAll(ArealLibraryProvider.class)) { + alp.addPropertyChangeListener(WeakListeners.propertyChange(this, alp)); + } + this.computeLibraries(); } - public Object getElementAt(int index) { - if (index < 0 || index >= this.actualLibraries.size()) - return null; - return actualLibraries.get(index); - } - - public int getSize() { - return this.actualLibraries.size(); + public synchronized Collection getLibraries() { + return actualLibraries; + } + + public void addChangeListener(ChangeListener l) { + cs.addChangeListener(l); + } + + public void removeChangeListener(ChangeListener l) { + cs.removeChangeListener(l); + } + + public LibraryStorageArea createArea() { + for (ArealLibraryProvider alp : Lookup.getDefault().lookupAll(ArealLibraryProvider.class)) { + LibraryStorageArea area = alp.createArea(); + if (area != null) { + createdAreas.add(area.getLocation()); + area2Storage.put(area, alp); + propertyChange(null); // recompute libraries & fire change + return area; + } + } + return null; + } + + public LibraryImplementation createArealLibrary(String type, String name, LibraryStorageArea area) { + LibraryImplementation impl = new DummyArealLibrary(type, name); + library2Area.put(impl, area); + return impl; + } + + public Collection getAreas() { + Set areas = new HashSet(); + for (ArealLibraryProvider alp : Lookup.getDefault().lookupAll(ArealLibraryProvider.class)) { + for (LibraryStorageArea area : LibraryAccessor.getOpenAreas(alp)) { + area2Storage.put(area, alp); + areas.add(area); + } + } + for (ArealLibraryProvider alp : Lookup.getDefault().lookupAll(ArealLibraryProvider.class)) { + for (URL location : createdAreas) { + LibraryStorageArea area = alp.loadArea(location); + if (area != null) { + assert area.getLocation().equals(location) : "Bad location " + area.getLocation() + " does not match " + location + " from " + alp.getClass().getName(); + area2Storage.put(area, alp); + areas.add(area); + } + } + } + return areas; + } + + public LibraryStorageArea getArea(LibraryImplementation library) { + LibraryStorageArea area = getAreaOrNull(library); + return area != null ? area : GLOBAL_AREA; + } + private LibraryStorageArea getAreaOrNull(LibraryImplementation library) { + if (library instanceof ProxyLibraryImplementation) { + library = ((ProxyLibraryImplementation) library).getOriginal(); + } + return library2Area.get(library); } public void addLibrary (LibraryImplementation impl) { - this.addedLibraries.add (impl); - int index=0; - Comparator c = new LibrariesComparator(); - for (; index < this.actualLibraries.size(); index++) { - LibraryImplementation tmp = this.actualLibraries.get(index); - if (c.compare(impl,tmp)<0) - break; + synchronized (this) { + addedLibraries.add(impl); + actualLibraries.add(impl); } - this.actualLibraries.add (index, impl); - this.fireIntervalAdded (this,index,index); + cs.fireChange(); } public void removeLibrary (LibraryImplementation impl) { - if (this.addedLibraries.contains(impl)) { - this.addedLibraries.remove(impl); - } - else { - this.removedLibraries.add (((ProxyLibraryImplementation)impl).getOriginal()); + synchronized (this) { + if (addedLibraries.contains(impl)) { + addedLibraries.remove(impl); + } else { + removedLibraries.add(((ProxyLibraryImplementation) impl).getOriginal()); + } + actualLibraries.remove(impl); } - int index = this.actualLibraries.indexOf(impl); - this.actualLibraries.remove(index); - this.fireIntervalRemoved(this,index,index); + cs.fireChange(); } public void modifyLibrary(ProxyLibraryImplementation impl) { - if (!this.addedLibraries.contains (impl) && !this.changedLibraries.contains(impl)) { - this.changedLibraries.add(impl); + synchronized (this) { + if (!addedLibraries.contains(impl) && !changedLibraries.contains(impl)) { + changedLibraries.add(impl); + } } - int index = this.actualLibraries.indexOf (impl); - this.fireContentsChanged(this,index,index); + cs.fireChange(); } public boolean isLibraryEditable (LibraryImplementation impl) { @@ -128,67 +215,68 @@ return true; LibraryProvider provider = storageByLib.get (((ProxyLibraryImplementation)impl).getOriginal()); - //Todo: Currently just one WritableLibraryProvider - //Todo: if changed, must be rewritten to handle it - return (provider == this.writableProvider); + return provider == writableProvider || getAreaOrNull(impl) != null; } public void apply () throws IOException { - //Todo: Currently just one WritableLibraryProvider - //Todo: if changed, must be rewritten to handle it for (LibraryImplementation impl : removedLibraries) { LibraryProvider storage = storageByLib.get(impl); if (storage == this.writableProvider) { this.writableProvider.removeLibrary (impl); - } - else { - ErrorManager.getDefault().log ("Can not find storage for library: "+impl.getName()); //NOI18N + } else { + LibraryStorageArea area = getAreaOrNull(impl); + if (area != null) { + LibraryAccessor.remove(area2Storage.get(area), impl); + } else { + throw new IOException("Cannot find storage for library: " + impl.getName()); // NOI18N + } } } - if (this.writableProvider != null) { - for (LibraryImplementation impl : addedLibraries) { + for (LibraryImplementation impl : addedLibraries) { + LibraryStorageArea area = getAreaOrNull(impl); + if (area != null) { + ArealLibraryProvider alp = area2Storage.get(area); + assert alp != null : area; + LibraryAccessor.createLibrary(alp, impl.getType(), impl.getName(), area, ((DummyArealLibrary) impl).contents); + } else if (writableProvider != null) { writableProvider.addLibrary(impl); + } else { + throw new IOException("Cannot add libraries, no WritableLibraryProvider."); // NOI18N } } - else { - ErrorManager.getDefault().log("Cannot add libraries, no WritableLibraryProvider."); //NOI18N - } for (ProxyLibraryImplementation proxy : changedLibraries) { - LibraryProvider storage = storageByLib.get(proxy.getOriginal()); + LibraryImplementation orig = proxy.getOriginal(); + LibraryProvider storage = storageByLib.get(orig); if (storage == this.writableProvider) { - this.writableProvider.updateLibrary (proxy.getOriginal(), proxy); - } - else { - ErrorManager.getDefault().log ("Can not find storage for library: "+proxy.getOriginal().getName()); //NOI18N + this.writableProvider.updateLibrary(orig, proxy); + } else { + LibraryStorageArea area = library2Area.get(orig); + if (area != null) { + if (proxy.newContents != null) { + for (Map.Entry> entry : proxy.newContents.entrySet()) { + orig.setContent(entry.getKey(), entry.getValue()); + } + } + } else { + throw new IOException("Cannot find storage for library: " + orig.getName()); // NOI18N + } } } - cleanUp (); - } - - public void cancel() { - cleanUp (); - } - - public void propertyChange(java.beans.PropertyChangeEvent evt) { - this.storagesChanged(); - } - - public void resultChanged(LookupEvent ev) { - this.storagesChanged(); } - public void storagesChanged () { - int oldSize; - synchronized (this) { - oldSize = this.actualLibraries == null ? 0 : this.actualLibraries.size(); - getLibraries(); - } - this.fireContentsChanged(this, 0, Math.max(oldSize,this.actualLibraries.size())); + public void propertyChange(PropertyChangeEvent evt) { + // compute libraries later in AWT thread and not in calling thread + // to prevent deadlocks + EventQueue.invokeLater(new Runnable() { + public void run() { + computeLibraries(); + cs.fireChange(); + } + }); } - private LibraryImplementation findModified (LibraryImplementation impl) { - for (Iterator it = changedLibraries.iterator(); it.hasNext();) { - ProxyLibraryImplementation proxy = (ProxyLibraryImplementation) it.next(); + private ProxyLibraryImplementation findModified (LibraryImplementation impl) { + for (ProxyLibraryImplementation proxy : changedLibraries) { if (proxy.getOriginal().equals (impl)) { return proxy; } @@ -196,68 +284,119 @@ return null; } - private synchronized void cleanUp () { - this.addedLibraries.clear(); - this.removedLibraries.clear(); - this.changedLibraries.clear(); - for (LibraryProvider p : currentStorages) { - p.removePropertyChangeListener (this); - } - this.currentStorages = Collections.emptySet(); - } - - private synchronized void getLibraries () { - List libraries = new ArrayList(); - if (this.lresult == null) { - //First time - this.lresult = Lookup.getDefault().lookupResult(LibraryProvider.class); - this.lresult.addLookupListener (this); - } - Collection instances = this.lresult.allInstances(); - Collection toAdd = new HashSet(instances); - toAdd.removeAll(this.currentStorages); - Collection toRemove = new HashSet(this.currentStorages); - toRemove.removeAll (instances); - this.currentStorages = instances; - this.storageByLib.clear(); - for (LibraryProvider storage : instances) { - //TODO: in case of more WritableLibraryProvider must be changed - if (this.writableProvider == null && storage instanceof WritableLibraryProvider) { - this.writableProvider = (WritableLibraryProvider) storage; - } + private synchronized void computeLibraries() { + actualLibraries.clear(); + for (LibraryProvider storage : Lookup.getDefault().lookupAll(LibraryProvider.class)) { for (LibraryImplementation lib : storage.getLibraries()) { - LibraryImplementation proxy = null; - if (removedLibraries.contains(lib)) { - this.storageByLib.put (lib,storage); - } - else if ((proxy = findModified (lib))!=null) { - libraries.add (proxy); - this.storageByLib.put (lib,storage); - } - else { - libraries.add(new ProxyLibraryImplementation(lib,this)); - this.storageByLib.put (lib,storage); + ProxyLibraryImplementation proxy = findModified(lib); + if (proxy != null) { + actualLibraries.add(proxy); + } else { + actualLibraries.add(proxy = new ProxyLibraryImplementation(lib, this)); } + storageByLib.put(lib, storage); + LOG.log(Level.FINER, "computeLibraries: storage={0} lib={1} proxy={2}", new Object[] {storage, lib, proxy}); } } - libraries.addAll (this.addedLibraries); - Collections.sort(libraries, new LibrariesComparator()); - - for (LibraryProvider p : toRemove) { - p.removePropertyChangeListener(this); - } - for (LibraryProvider p : toAdd) { - p.addPropertyChangeListener(this); + for (LibraryStorageArea area : getAreas()) { + ArealLibraryProvider alp = area2Storage.get(area); + assert alp != null : area; + LibraryProvider prov = area2Provider.get(area); + if (prov == null) { + prov = LibraryAccessor.getLibraries(alp, area); + prov.addPropertyChangeListener(this); // need not be weak, we just created the source + area2Provider.put(area, prov); + } + for (LibraryImplementation lib : prov.getLibraries()) { + ProxyLibraryImplementation proxy = findModified(lib); + if (proxy != null) { + actualLibraries.add(proxy); + } else { + actualLibraries.add(proxy = new ProxyLibraryImplementation(lib, this)); + } + library2Area.put(lib, area); + LOG.log(Level.FINER, "computeLibraries: alp={0} area={1} lib={2} proxy={3}", new Object[] {alp, area, lib, proxy}); + } } - this.actualLibraries = libraries; + actualLibraries.addAll(addedLibraries); + LOG.log(Level.FINE, "computeLibraries: actualLibraries={0} library2Area={1}", new Object[] {actualLibraries, library2Area}); } private static class LibrariesComparator implements Comparator { public int compare(LibraryImplementation lib1, LibraryImplementation lib2) { String name1 = LibrariesCustomizer.getLocalizedString(lib1.getLocalizingBundle(), lib1.getName()); String name2 = LibrariesCustomizer.getLocalizedString(lib2.getLocalizingBundle(), lib2.getName()); - return name1.compareToIgnoreCase(name2); - } + int r = name1.compareToIgnoreCase(name2); + return r != 0 ? r : System.identityHashCode(lib1) - System.identityHashCode(lib2); + } + } + + private static final class DummyArealLibrary implements LibraryImplementation { + + private final String type, name; + final Map> contents = new HashMap>(); + private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); + + public DummyArealLibrary(String type, String name) { + this.type = type; + this.name = name; + } + + public String getType() { + return type; + } + + public String getName() { + return name; + } + + public String getDescription() { + return null; + } + + public String getLocalizingBundle() { + return null; + } + + public List getContent(String volumeType) throws IllegalArgumentException { + List content = contents.get(volumeType); + if (content != null) { + return content; + } else { + return Collections.emptyList(); + } + } + + public void setName(String name) { + throw new UnsupportedOperationException(); + } + + public void setDescription(String text) { + throw new UnsupportedOperationException(); + } + + public void setLocalizingBundle(String resourceName) { + throw new UnsupportedOperationException(); + } + + public void addPropertyChangeListener(PropertyChangeListener l) { + pcs.addPropertyChangeListener(l); + } + + public void removePropertyChangeListener(PropertyChangeListener l) { + pcs.removePropertyChangeListener(l); + } + + public void setContent(String volumeType, List path) throws IllegalArgumentException { + contents.put(volumeType, path); + pcs.firePropertyChange(LibraryImplementation.PROP_CONTENT, null, null); + } + + @Override + public String toString() { + return "DummyArealLibrary[" + name + "]"; // NOI18N + } + } } Index: projects/libraries/src/org/netbeans/modules/project/libraries/ui/NewLibraryPanel.form diff -u projects/libraries/src/org/netbeans/modules/project/libraries/ui/NewLibraryPanel.form:1.7 projects/libraries/src/org/netbeans/modules/project/libraries/ui/NewLibraryPanel.form:1.7.6.2 --- projects/libraries/src/org/netbeans/modules/project/libraries/ui/NewLibraryPanel.form:1.7 Wed Jan 24 06:21:06 2007 +++ projects/libraries/src/org/netbeans/modules/project/libraries/ui/NewLibraryPanel.form Thu Jan 3 08:07:05 2008 @@ -1,14 +1,15 @@ - + + - + @@ -81,7 +82,7 @@ - + Index: projects/libraries/src/org/netbeans/modules/project/libraries/ui/NewLibraryPanel.java diff -u projects/libraries/src/org/netbeans/modules/project/libraries/ui/NewLibraryPanel.java:1.18 projects/libraries/src/org/netbeans/modules/project/libraries/ui/NewLibraryPanel.java:1.17.6.6 --- projects/libraries/src/org/netbeans/modules/project/libraries/ui/NewLibraryPanel.java:1.18 Wed Sep 26 14:04:22 2007 +++ projects/libraries/src/org/netbeans/modules/project/libraries/ui/NewLibraryPanel.java Fri Jan 4 12:32:48 2008 @@ -42,32 +42,32 @@ package org.netbeans.modules.project.libraries.ui; import java.awt.Color; +import java.awt.Component; import java.util.HashMap; import java.util.Map; import java.util.regex.Pattern; +import javax.swing.DefaultComboBoxModel; +import javax.swing.DefaultListCellRenderer; +import javax.swing.JList; import org.openide.DialogDescriptor; import org.openide.util.NbBundle; import org.netbeans.modules.project.libraries.LibraryTypeRegistry; +import org.netbeans.spi.project.libraries.LibraryStorageArea; import org.netbeans.spi.project.libraries.LibraryTypeProvider; - - -/** - * - * @author tom - */ public class NewLibraryPanel extends javax.swing.JPanel { private LibrariesModel model; private Map typeMap; + private LibraryStorageArea area; private DialogDescriptor dd; private static final Pattern VALID_LIBRARY_NAME = Pattern.compile("[-._a-zA-Z0-9]+"); // NOI18N - /** Creates new form NewLibraryPanel */ - public NewLibraryPanel (LibrariesModel model, String preselectedLibraryType) { + public NewLibraryPanel (LibrariesModel model, String preselectedLibraryType, LibraryStorageArea area) { this.model = model; + this.area = area; initComponents(); this.name.setColumns(25); this.name.getDocument().addDocumentListener(new javax.swing.event.DocumentListener () { @@ -84,7 +84,7 @@ } }); - this.initModel (preselectedLibraryType); + initModel(preselectedLibraryType); Color c = javax.swing.UIManager.getColor("nb.errorForeground"); //NOI18N if (c == null) { c = new Color(89,79,191); // RGB suggested by Bruce in #28466 @@ -92,8 +92,10 @@ status.setForeground(c); } - void setDialogDescriptor(DialogDescriptor dd) { + public void setDialogDescriptor(DialogDescriptor dd) { + assert this.dd == null; this.dd = dd; + nameChanged(); } public String getLibraryType () { @@ -110,7 +112,7 @@ } - private void initModel (String preselectedLibraryType) { + private void initModel(String preselectedLibraryType) { this.typeMap = new HashMap(); this.name.setText (NbBundle.getMessage (NewLibraryPanel.class,"TXT_NewLibrary")); LibraryTypeRegistry regs = LibraryTypeRegistry.getDefault(); @@ -142,7 +144,7 @@ message = NbBundle.getMessage(NewLibraryPanel.class,"ERR_InvalidName"); } else { - valid = LibrariesCustomizer.isValidName (model, name); + valid = LibrariesCustomizer.isValidName(model, name, area); if (valid) { if (isReasonableAntProperty(name)) { message = " "; //NOI18N @@ -174,7 +176,7 @@ * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ - // //GEN-BEGIN:initComponents + // //GEN-BEGIN:initComponents private void initComponents() { java.awt.GridBagConstraints gridBagConstraints; @@ -219,6 +221,8 @@ libraryType.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(NewLibraryPanel.class, "AD_LibraryType")); // NOI18N gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 3; gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER; gridBagConstraints.gridheight = java.awt.GridBagConstraints.REMAINDER; gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; Index: projects/libraries/src/org/netbeans/modules/project/libraries/ui/ProxyLibraryImplementation.java diff -u projects/libraries/src/org/netbeans/modules/project/libraries/ui/ProxyLibraryImplementation.java:1.5 projects/libraries/src/org/netbeans/modules/project/libraries/ui/ProxyLibraryImplementation.java:1.4.14.3 --- projects/libraries/src/org/netbeans/modules/project/libraries/ui/ProxyLibraryImplementation.java:1.5 Wed Sep 26 14:04:22 2007 +++ projects/libraries/src/org/netbeans/modules/project/libraries/ui/ProxyLibraryImplementation.java Thu Nov 22 08:40:27 2007 @@ -51,6 +51,7 @@ import org.netbeans.spi.project.libraries.LibraryImplementation; import org.openide.util.WeakListeners; + /** * * @author tom @@ -59,12 +60,11 @@ private final LibraryImplementation original; private final LibrariesModel model; - private Map> newContents; + Map> newContents; private String newName; private String newDescription; private PropertyChangeSupport support; - /** Creates a new instance of ProxyLibraryImplementation */ public ProxyLibraryImplementation (LibraryImplementation original, LibrariesModel model) { assert original != null && model != null; this.original = original; @@ -167,8 +167,8 @@ return false; } - public final String toString() { - return "Proxy for: " + this.original.toString(); //NOI18N + public @Override String toString() { + return "Proxy[" + original + "]"; // NOI18N } } Index: projects/libraries/src/org/netbeans/spi/project/libraries/ArealLibraryProvider.java diff -u /dev/null projects/libraries/src/org/netbeans/spi/project/libraries/ArealLibraryProvider.java:1.1.2.2 --- /dev/null Fri Jan 18 01:33:17 2008 +++ projects/libraries/src/org/netbeans/spi/project/libraries/ArealLibraryProvider.java Mon May 21 12:45:22 2007 @@ -0,0 +1,124 @@ +/* + * The contents of this file are subject to the terms of the Common Development + * and Distribution License (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at http://www.netbeans.org/cddl.html + * or http://www.netbeans.org/cddl.txt. + * + * When distributing Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://www.netbeans.org/cddl.txt. + * If applicable, add the following below the CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans.spi.project.libraries; + +import java.beans.PropertyChangeListener; +import java.io.IOException; +import java.net.URL; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Library provider which can define libraries in particular areas. + * There is no explicit method to save a library; setters on {@link LibraryImplementation} should do this. + * @param A the type of storage area used by this provider + * @param L the type of library created by this provider + * @since org.netbeans.modules.project.libraries/1 1.15 + */ +public interface ArealLibraryProvider { + + /** + * Property to fire when {@link #getOpenAreas} might have changed. + */ + String PROP_OPEN_AREAS = "openAreas"; // NOI18N + + /** + * Adds a listener to {@link #PROP_OPEN_AREAS}. + * @param listener a listener to add + */ + void addPropertyChangeListener(PropertyChangeListener listener); + + /** + * Removes a listener to {@link #PROP_OPEN_AREAS}. + * @param listener a listener to remove + */ + void removePropertyChangeListener(PropertyChangeListener listener); + + /** + * Gets the runtime type of the area used by this provider. + * @return the area type + */ + Class areaType(); + + /** + * Gets the runtime type of the libraries created by this provider. + * @return the library type + */ + Class libraryType(); + + /** + * Creates or otherwise picks a storage area interactively. + * This might actually create a fresh area, or just load an existing one, + * or even do nothing (and return null). + * The implementor is free to show a dialog here. + * @return a new or existing storage area, or null + */ + A createArea(); + + /** + * Loads a storage area (which may or may exist yet). + * @param location an abstract storage location which may or may not be recognized by this provider + * @return an area whose {@link LibraryStorageArea#getLocation} matches the provided location, + * or null if this type of location is not recognized by this provider + */ + A loadArea(URL location); + + /** + * Looks for areas which should be somehow listed as open. + * For example, a provider which refers to library areas from project metadata + * could list all areas referred to from currently open projects. + * It is not necessary to include areas recently mentioned e.g. by {@link #createArea}. + * @return a (possibly empty) collection of areas + */ + Set getOpenAreas(); + + /** + * Gets all libraries defined in a given area. + * No two libraries in this area may share a given name (as in {@link LibraryImplementation#getName}, + * though it is permitted for libraries from different areas to have the same name. + * Changes in the set of libraries defined in this area should be fired through {@link LibraryProvider#PROP_LIBRARIES}. + * Since {@link IOException} is not thrown either from this method or from {@link LibraryProvider#getLibraries}, + * it is expected that any problems loading library definitions will be logged and that those libraries will be skipped. + * @param area some storage area (which might not even exist yet, in which case the set of libraries will initially be empty) + * @return a listenable set of libraries in this area + * (it is permitted to return distinct objects from call to call on the same area, + * i.e. no caching by the implementation is necessary) + */ + LibraryProvider getLibraries(A area); + + /** + * Creates a new library. + * @param type the kind of library to make, as in {@link LibraryTypeProvider#getLibraryType} or {@link LibraryImplementation#getType} + * @param name the library name, as in {@link LibraryImplementation#getName} + * @param area the location to define the library + * @param contents initial volume contents (keys must be contained in the appropriate {@link LibraryTypeProvider#getSupportedVolumeTypes}) + * @return a new library with matching type, name, area, and contents + * @throws IOException if an error occurs creating the library definition */ + L createLibrary(String type, String name, A area, Map> contents) throws IOException; + + /** + * Deletes an existing library. + * @param library a library produced by this provider + * @throws IOException if a problem can encountered deleting the library definition + */ + void remove(L library) throws IOException; + +} Index: projects/libraries/src/org/netbeans/spi/project/libraries/LibraryFactory.java diff -u projects/libraries/src/org/netbeans/spi/project/libraries/LibraryFactory.java:1.2 projects/libraries/src/org/netbeans/spi/project/libraries/LibraryFactory.java:1.1.14.2 --- projects/libraries/src/org/netbeans/spi/project/libraries/LibraryFactory.java:1.2 Wed Sep 26 14:04:23 2007 +++ projects/libraries/src/org/netbeans/spi/project/libraries/LibraryFactory.java Thu Nov 22 08:40:28 2007 @@ -58,11 +58,10 @@ private LibraryFactory() { } - /** * Creates Library for LibraryImplementation * @param libraryImplementation the library SPI object - * @return Library API instance + * @return Library API instance, for which the {@link Library#getManager} will be {@link LibraryManager#getDefault} */ public static Library createLibrary (LibraryImplementation libraryImplementation) { assert libraryImplementation != null; Index: projects/libraries/src/org/netbeans/spi/project/libraries/LibraryProvider.java diff -u projects/libraries/src/org/netbeans/spi/project/libraries/LibraryProvider.java:1.6 projects/libraries/src/org/netbeans/spi/project/libraries/LibraryProvider.java:1.4.16.2 --- projects/libraries/src/org/netbeans/spi/project/libraries/LibraryProvider.java:1.6 Wed Sep 26 14:04:23 2007 +++ projects/libraries/src/org/netbeans/spi/project/libraries/LibraryProvider.java Thu Nov 22 08:40:31 2007 @@ -38,6 +38,7 @@ * Version 2 license, then the option applies only if the new code is * made subject to such option by the copyright holder. */ + package org.netbeans.spi.project.libraries; import java.beans.PropertyChangeListener; @@ -47,29 +48,31 @@ * Library storage is a source of libraries used by LibraryManager. * LibraryManager allows existence of multiple LibraryProviders registered in * the default lookup. + * @param L the type of implementation which will be produced by this provider */ -public interface LibraryProvider { +public interface LibraryProvider { /** * Name of libraries property */ - public static final String PROP_LIBRARIES = "libraries"; //NOI18N + String PROP_LIBRARIES = "libraries"; // NOI18N /** * Returns libraries provided by the implemented provider. - * @return LibraryImplementation[] never return null, may return empty array. + * @return (possibly empty but not null) list of libraries */ - public LibraryImplementation[] getLibraries(); + L[] getLibraries(); /** * Adds property change listener, the listener is notified when the libraries changed * @param listener */ - public void addPropertyChangeListener (PropertyChangeListener listener); + void addPropertyChangeListener (PropertyChangeListener listener); /** * Removes property change listener * @param listener */ - public void removePropertyChangeListener (PropertyChangeListener listener); + void removePropertyChangeListener (PropertyChangeListener listener); + } Index: projects/libraries/src/org/netbeans/spi/project/libraries/LibraryStorageArea.java diff -u /dev/null projects/libraries/src/org/netbeans/spi/project/libraries/LibraryStorageArea.java:1.1.2.2 --- /dev/null Fri Jan 18 01:33:17 2008 +++ projects/libraries/src/org/netbeans/spi/project/libraries/LibraryStorageArea.java Mon May 21 12:45:22 2007 @@ -0,0 +1,46 @@ +/* + * The contents of this file are subject to the terms of the Common Development + * and Distribution License (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at http://www.netbeans.org/cddl.html + * or http://www.netbeans.org/cddl.txt. + * + * When distributing Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://www.netbeans.org/cddl.txt. + * If applicable, add the following below the CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans.spi.project.libraries; + +import java.net.URL; + +/** + * Abstract location where zero or more libraries are defined. + * {@link Object#equals} and {@link Object#hashCode} are expected to be defined + * such that object identity (within the implementing class) are driven by {@link #getLocation}. + * @see ArealLibraryProvider + * @since org.netbeans.modules.project.libraries/1 1.15 + */ +public interface LibraryStorageArea { + + /** + * Gets an associated storage location. + * The contents of the URL (if it is even accessible) are unspecified. + * @return an associated URL uniquely identifying this location + */ + URL getLocation(); + + /** + * Gets a human-readable display label for this area. + * @return a localized display name + */ + String getDisplayName(); + +} Index: projects/libraries/src/org/netbeans/spi/project/libraries/LibraryTypeProvider.java diff -u projects/libraries/src/org/netbeans/spi/project/libraries/LibraryTypeProvider.java:1.8 projects/libraries/src/org/netbeans/spi/project/libraries/LibraryTypeProvider.java:1.6.16.4 --- projects/libraries/src/org/netbeans/spi/project/libraries/LibraryTypeProvider.java:1.8 Wed Sep 26 14:04:23 2007 +++ projects/libraries/src/org/netbeans/spi/project/libraries/LibraryTypeProvider.java Thu Dec 6 07:25:00 2007 @@ -75,6 +75,8 @@ /** * Creates a new empty library implementation. + * Generally will use {@link LibrariesSupport#createLibraryImplementation}. + * This method is not used by {@link LibraryManager#createLibrary} except in the case of {@link LibraryManager#getDefault}. * @return the created library model, never null */ public LibraryImplementation createLibrary (); @@ -85,6 +87,7 @@ * If the LibraryTypeProvider implementation requires clean of * additional settings (e.g. remove properties in the build.properties) * it should be done in this method. + * This method is not used by {@link LibraryManager#createLibrary} except in the case of {@link LibraryManager#getDefault}. * @param libraryImpl */ public void libraryDeleted (LibraryImplementation libraryImpl); @@ -96,13 +99,13 @@ * If the LibraryTypeProvider implementation requires initialization of * additional settings (e.g. adding properties into the build.properties) * it should be done in this method. - * + * This method is not used by {@link LibraryManager#createLibrary} except in the case of {@link LibraryManager#getDefault}. */ public void libraryCreated (LibraryImplementation libraryImpl); /** * Returns customizer for given volume's type, or null if the volume is not customizable. - * The object of the LibraryImplementation type is + * The object of the LibraryImplementation type and optionally LibraryStorageArea type is * passed to the customizer's setObject method. * The customized object describes the library created by this * provider, but the customizer cannot assume that the customized Index: projects/libraries/src/org/netbeans/spi/project/libraries/support/LibrariesSupport.java diff -u projects/libraries/src/org/netbeans/spi/project/libraries/support/LibrariesSupport.java:1.5 projects/libraries/src/org/netbeans/spi/project/libraries/support/LibrariesSupport.java:1.4.14.5 --- projects/libraries/src/org/netbeans/spi/project/libraries/support/LibrariesSupport.java:1.5 Wed Sep 26 14:04:23 2007 +++ projects/libraries/src/org/netbeans/spi/project/libraries/support/LibrariesSupport.java Wed Jan 9 04:18:42 2008 @@ -40,10 +40,19 @@ */ package org.netbeans.spi.project.libraries.support; +import java.io.File; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; import org.netbeans.modules.project.libraries.LibraryTypeRegistry; import org.netbeans.spi.project.libraries.LibraryImplementation; import org.netbeans.modules.project.libraries.DefaultLibraryImplementation; import org.netbeans.spi.project.libraries.LibraryTypeProvider; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +import org.openide.filesystems.URLMapper; +import org.openide.util.Parameters; /** * SPI Support class. @@ -85,4 +94,130 @@ public static LibraryTypeProvider[] getLibraryTypeProviders () { return LibraryTypeRegistry.getDefault().getLibraryTypeProviders(); } + + /** + * Properly converts possibly relative file to URL. + * @param f file to convert; can be relative; cannot be null + * @return url + */ + public static URL convertFileToURL(File f) { + try { + if (f.isAbsolute()) { + return f.toURI().toURL(); + } else { + // create hierarchical relative URI (that is no schema) + // to encode OS characters + URI uri = new URI(null, null, f.getPath().replace('\\', '/'), null); + return new URL("file", null, uri.getRawPath()); + } + + } catch (URISyntaxException ex) { + IllegalArgumentException y = new IllegalArgumentException(); + y.initCause(ex); + throw y; + } catch (MalformedURLException ex) { + IllegalArgumentException y = new IllegalArgumentException(); + y.initCause(ex); + throw y; + } + } + + /** + * Properly converts possibly relative URL to file. + * @param url file URL to convert; can be relative; cannot be null + * @return url + */ + public static File convertURLToFile(URL url) { + if (!"file".equals(url.getProtocol())) { + throw new IllegalArgumentException("not file URL "+url); //NOI18N + } + try { + if (isAbsoluteURL(url)) { + return new File(new URI(url.toExternalForm())); + } else { + // workaround to decode URL path - created fake absolute URI + // just to construct File instance and properly decoded path: + URI uri3 = new URI("file:/"+url.getPath()); + return new File(new File(uri3).getPath().substring(1)); + } + } catch (URISyntaxException ex) { + IllegalArgumentException y = new IllegalArgumentException(); + y.initCause(ex); + throw y; + } + } + + /** + * Is given URL absolute? + * + * @param url url to test; cannot be null + * @return is absolute + */ + public static boolean isAbsoluteURL(URL url) { + if ("jar".equals(url.getProtocol())) { // NOI18N + url = FileUtil.getArchiveFile(url); + } + return url.getPath().startsWith("/"); + } + + /** + * Helper method to resolve (possibly relative) library content URL to FileObject. + * + * @param libraryLocation library location file; can be null for global libraries + * @param libraryEntry library entry to resolve + * @return file object + */ + public static FileObject resolveLibraryEntryFileObject(URL libraryLocation, URL libraryEntry) { + URL u = resolveLibraryEntryURL(libraryLocation, libraryEntry); + return URLMapper.findFileObject(u); + } + + /** + * Helper method to resolve (possibly relative) library content URL. + * + * @param libraryLocation library location file; can be null for global libraries + * @param libraryEntry library entry to resolve + * @return absolute URL + */ + public static URL resolveLibraryEntryURL(URL libraryLocation, URL libraryEntry) { + Parameters.notNull("libraryEntry", libraryEntry); //NOI18N + if (isAbsoluteURL(libraryEntry)) { + return libraryEntry; + } else { + if (libraryLocation == null) { + throw new IllegalArgumentException("cannot resolve relative URL without library location"); //NOI18N + } + if (!"file".equals(libraryLocation.getProtocol())) { //NOI18N + throw new IllegalArgumentException("not file: protocol - "+libraryLocation.toExternalForm()); //NOI18N + } + File libLocation = new File(URI.create(libraryLocation.toExternalForm())); + if (!libLocation.getName().endsWith(".properties")) { //NOI18N + throw new IllegalArgumentException("library location must be a file - "+libraryLocation.toExternalForm()); //NOI18N + } + File libBase = libLocation.getParentFile(); + boolean archiveURL = false; + if ("jar".equals(libraryEntry.getProtocol())) { + libraryEntry = FileUtil.getArchiveFile(libraryEntry); + archiveURL = true; + } + File f = convertURLToFile(libraryEntry); + f = FileUtil.normalizeFile(new File(libBase, f.getPath())); + URL u; + try { + u = f.toURI().toURL(); + } catch (MalformedURLException ex) { + IllegalArgumentException y = new IllegalArgumentException(); + y.initCause(ex); + throw y; + } + if (archiveURL) { + u = FileUtil.getArchiveRoot(u); + } + return u; + } + } + + // TODO: add method which compares two libraries: compare content and file sizes and ... + + // TODO: move some of these methods to openide.FileUtil } Index: projects/libraries/test/.cvsignore diff -u projects/libraries/test/.cvsignore:1.1 projects/libraries/test/.cvsignore:1.1.134.1 --- projects/libraries/test/.cvsignore:1.1 Sun Sep 5 02:23:47 2004 +++ projects/libraries/test/.cvsignore Fri Apr 27 10:45:02 2007 @@ -1,3 +1,4 @@ +coverage lib -work results +work Index: projects/libraries/test/unit/src/org/netbeans/api/project/libraries/LibraryManagerTest.java diff -u projects/libraries/test/unit/src/org/netbeans/api/project/libraries/LibraryManagerTest.java:1.8 projects/libraries/test/unit/src/org/netbeans/api/project/libraries/LibraryManagerTest.java:1.7.12.9 --- projects/libraries/test/unit/src/org/netbeans/api/project/libraries/LibraryManagerTest.java:1.8 Wed Sep 26 14:04:24 2007 +++ projects/libraries/test/unit/src/org/netbeans/api/project/libraries/LibraryManagerTest.java Thu Nov 22 08:40:39 2007 @@ -41,24 +41,16 @@ package org.netbeans.api.project.libraries; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeSupport; -import java.io.IOException; +import static org.netbeans.modules.project.libraries.TestUtil.*; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Set; import java.util.StringTokenizer; -import org.netbeans.api.project.TestUtil; import org.netbeans.junit.NbTestCase; -import org.netbeans.modules.project.libraries.WritableLibraryProvider; +import org.netbeans.modules.project.libraries.ui.LibrariesModel; import org.netbeans.spi.project.libraries.LibraryFactory; import org.netbeans.spi.project.libraries.LibraryImplementation; import org.netbeans.spi.project.libraries.LibraryTypeProvider; @@ -68,37 +60,31 @@ import org.openide.loaders.DataFolder; import org.openide.loaders.InstanceDataObject; import org.openide.util.lookup.Lookups; -import org.openide.util.lookup.Lookups; -/** - * - * @author Tomas Zezula - */ +import org.openide.util.test.MockLookup; +import org.openide.util.test.MockPropertyChangeListener; + public class LibraryManagerTest extends NbTestCase { - - private TestLibraryProvider lp; - + + private WLP lp; + private static final String LIBRARY_TYPE = "j2se"; //NOI18N private static final String[] VOLUME_TYPES = new String[] { "bin", "src", "doc" }; - - /** Creates a new instance of LibraryManagerTest */ + public LibraryManagerTest (String testName) { super (testName); } - + protected void setUp() throws Exception { super.setUp(); - lp = new TestLibraryProvider (); - TestUtil.setLookup (Lookups.fixed(new Object[] { - lp - })); + lp = new WLP(); + MockLookup.setLookup(Lookups.singleton(lp)); registerLibraryTypeProvider(); } - - + private static void registerLibraryTypeProvider () throws Exception { StringTokenizer tk = new StringTokenizer("org-netbeans-api-project-libraries/LibraryTypeProviders","/"); FileObject root = Repository.getDefault().getDefaultFileSystem().getRoot(); @@ -114,30 +100,28 @@ InstanceDataObject.create (DataFolder.findFolder(root),"TestLibraryTypeProvider",TestLibraryTypeProvider.class); } } - - public void testGetLibraries () throws Exception { + + public void testGetLibraries () throws Exception { LibraryManager lm = LibraryManager.getDefault(); - TestListener tl = new TestListener (); - lm.addPropertyChangeListener(tl); + MockPropertyChangeListener pcl = new MockPropertyChangeListener(); + lm.addPropertyChangeListener(pcl); Library[] libs = lm.getLibraries(); assertEquals ("Libraries count", 0, libs.length); LibraryImplementation[] impls = createTestLibs (); - lp.setLibraries(impls); + lp.set(impls); libs = lm.getLibraries(); - assertEventsEquals(tl.getEventNames(), new String[] {LibraryManager.PROP_LIBRARIES}); - tl.reset(); + pcl.assertEvents(LibraryManager.PROP_LIBRARIES); assertEquals ("Libraries count", 2, libs.length); assertLibsEquals (libs, impls); - lp.setLibraries(new LibraryImplementation[0]); - assertEventsEquals(tl.getEventNames(), new String[] {LibraryManager.PROP_LIBRARIES}); - tl.reset(); + lp.set(); + pcl.assertEvents(LibraryManager.PROP_LIBRARIES); libs = lm.getLibraries(); assertEquals ("Libraries count", 0, libs.length); } - + public void testGetLibrary () throws Exception { LibraryImplementation[] impls = createTestLibs (); - lp.setLibraries(impls); + lp.set(impls); LibraryManager lm = LibraryManager.getDefault(); Library[] libs = lm.getLibraries(); assertEquals ("Libraries count", 2, libs.length); @@ -151,10 +135,11 @@ lib = lm.getLibrary("Library3"); assertNull ("Nonexisting library", lib); } - + + @SuppressWarnings("deprecation") public void testAddRemoveLibrary () throws Exception { final LibraryImplementation[] impls = createTestLibs(); - lp.setLibraries(impls); + lp.set(impls); final LibraryManager lm = LibraryManager.getDefault(); Library[] libs = lm.getLibraries(); assertEquals ("Libraries count", 2, libs.length); @@ -175,11 +160,65 @@ assertEquals("Libraries count",2,libs.length); assertLibsEquals (libs, impls); } - + + public void testCreateRemoveLibrary() throws Exception { + LibraryManager mgr = LibraryManager.getDefault(); + URL fooJar = mkJar("foo.jar"); + Library lib = mgr.createLibrary(LIBRARY_TYPE, "NewLib", Collections.singletonMap("bin", Arrays.asList(fooJar))); + assertEquals(mgr, lib.getManager()); + assertEquals(Collections.singletonList(lib), Arrays.asList(mgr.getLibraries())); + assertEquals(1, lp.libs.size()); + assertLibsEquals(new Library[] {lib}, lp.libs.toArray(new LibraryImplementation[1])); + mgr.removeLibrary(lib); + assertEquals(Collections.emptyList(), Arrays.asList(mgr.getLibraries())); + assertEquals(0, lp.libs.size()); + } + + public void testArealLibraryManagers() throws Exception { + ALP alp = new ALP(); + MockLookup.setLookup(Lookups.fixed(lp, alp)); + new LibrariesModel().createArea(); + Area home = new Area("home"); + Area away = new Area("away"); + alp.setOpen(home, away); + List locations = new ArrayList(); // use list, not set, to confirm size also + for (LibraryManager mgr : LibraryManager.getOpenManagers()) { + URL loc = mgr.getLocation(); + locations.add(loc != null ? loc.toString() : ""); + } + Collections.sort(locations); + assertEquals("[, http://nowhere.net/away, http://nowhere.net/home, http://nowhere.net/new]", locations.toString()); + alp.setOpen(); + try { + LibraryManager.forLocation(new URL("http://even-less-anywhere.net/")); + fail(); + } catch (IllegalArgumentException x) {} + LibraryManager mgr = LibraryManager.forLocation(home.getLocation()); + assertEquals(home.getLocation(), mgr.getLocation()); + assertEquals("home", mgr.getDisplayName()); + assertEquals(0, mgr.getLibraries().length); + MockPropertyChangeListener pcl = new MockPropertyChangeListener(); + mgr.addPropertyChangeListener(pcl); + URL fooJar = mkJar("foo.jar"); + Library lib = mgr.createLibrary(LIBRARY_TYPE, "NewLib", Collections.singletonMap("bin", Arrays.asList(fooJar))); + assertEquals(mgr, lib.getManager()); + pcl.assertEvents(LibraryManager.PROP_LIBRARIES); + assertEquals(Collections.singletonList(lib), Arrays.asList(mgr.getLibraries())); + assertEquals(1, alp.libs.get(home).size()); + LibraryImplementation impl = alp.libs.get(home).iterator().next(); + assertEquals("NewLib", impl.getName()); + assertEquals(LIBRARY_TYPE, impl.getType()); + assertEquals(Arrays.asList(fooJar), impl.getContent("bin")); + mgr.removeLibrary(lib); + pcl.assertEvents(LibraryManager.PROP_LIBRARIES); + assertEquals(Collections.emptyList(), Arrays.asList(mgr.getLibraries())); + assertEquals(0, alp.libs.get(home).size()); + } + static LibraryImplementation[] createTestLibs () throws MalformedURLException { - LibraryImplementation[] impls = new LibraryImplementation[] { - new TestLibraryImplementation (), - new TestLibraryImplementation () + LibraryImplementation[] impls = { + LibrariesSupport.createLibraryImplementation(LIBRARY_TYPE, VOLUME_TYPES), + LibrariesSupport.createLibraryImplementation(LIBRARY_TYPE, VOLUME_TYPES), }; impls[0].setName ("Library1"); impls[1].setName ("Library2"); @@ -189,7 +228,7 @@ impls[1].setContent("bin", Collections.singletonList(new URL("file:/lib/libest2.so"))); return impls; } - + static void assertLibsEquals (Library[] libs, LibraryImplementation[] impls) { assertEquals ("libs equals (size)", impls.length, libs.length); for (int i=0; i< libs.length; i++) { @@ -206,77 +245,9 @@ assertEquals ("libs equals (content doc)", ic, lc); } } - - static void assertEventsEquals (List eventNames, String[] expectedName) { - assertEquals ("Events equals", Arrays.asList(expectedName), eventNames); - } - - - static class TestLibraryProvider implements WritableLibraryProvider { - - private PropertyChangeSupport support; - private List libraries; - - public TestLibraryProvider () { - this.support = new PropertyChangeSupport (this); - this.libraries = new ArrayList(); - } - - public void removePropertyChangeListener(PropertyChangeListener listener) { - this.support.removePropertyChangeListener(listener); - } - - public void addPropertyChangeListener(PropertyChangeListener listener) { - this.support.addPropertyChangeListener(listener); - } - - public LibraryImplementation[] getLibraries() { - return this.libraries.toArray(new LibraryImplementation[libraries.size()]); - } - - public void setLibraries (LibraryImplementation[] libraries) { - this.libraries = new ArrayList(Arrays.asList(libraries)); - this.support.firePropertyChange(PROP_LIBRARIES,null,null); - } - - public void addLibrary(LibraryImplementation library) throws IOException { - this.libraries.add (library); - this.support.firePropertyChange(PROP_LIBRARIES,null,null); - } - - public void removeLibrary(LibraryImplementation library) throws IOException { - this.libraries.remove (library); - this.support.firePropertyChange(PROP_LIBRARIES,null,null); - } - - public void updateLibrary(LibraryImplementation oldLibrary, LibraryImplementation newLibrary) throws IOException { - this.libraries.remove(oldLibrary); - this.libraries.add (newLibrary); - this.support.firePropertyChange(PROP_LIBRARIES,null,null); - } - - } - - static class TestListener implements PropertyChangeListener { - - private List events = new ArrayList(); - - public void propertyChange(PropertyChangeEvent event) { - this.events.add(event.getPropertyName()); - } - - public void reset () { - this.events.clear(); - } - - public List getEventNames () { - return this.events; - } - } - - + public static class TestLibraryTypeProvider implements LibraryTypeProvider { - + public String getDisplayName() { return LIBRARY_TYPE; @@ -308,84 +279,5 @@ return null; } } - - static class TestLibraryImplementation implements LibraryImplementation { - - private static final Set supportedTypes; - - private String name; - private String description; - private Map> contents; - private PropertyChangeSupport support; - - static { - supportedTypes = Collections.unmodifiableSet(new HashSet(Arrays.asList(VOLUME_TYPES))); - } - - public TestLibraryImplementation () { - this.contents = new HashMap>(); - this.support = new PropertyChangeSupport (this); - } - - public String getType() { - return LIBRARY_TYPE; - } - - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - this.support.firePropertyChange(PROP_NAME, null, null); - } - - public String getLocalizingBundle() { - return null; - } - - public void setLocalizingBundle(String resourceName) { - } - - public String getDescription() { - return this.description; - } - - public void setDescription(String text) { - this.description = text; - this.support.firePropertyChange(PROP_DESCRIPTION, null, null); - } - - public List getContent(String volumeType) throws IllegalArgumentException { - if (supportedTypes.contains(volumeType)) { - List l = contents.get(volumeType); - if (l == null) { - l = Collections.emptyList(); - } - return Collections.unmodifiableList(l); - } - else { - throw new IllegalArgumentException (); - } - } - - public void setContent(String volumeType, List path) throws IllegalArgumentException { - if (supportedTypes.contains(volumeType)) { - this.contents.put (volumeType, path); - this.support.firePropertyChange(PROP_CONTENT, null, null); - } - else { - throw new IllegalArgumentException (); - } - } - - public void removePropertyChangeListener(PropertyChangeListener l) { - this.support.removePropertyChangeListener(l); - } - public void addPropertyChangeListener(PropertyChangeListener l) { - this.support.addPropertyChangeListener(l); - } - } - } Index: projects/libraries/test/unit/src/org/netbeans/api/project/libraries/LibraryTest.java diff -u projects/libraries/test/unit/src/org/netbeans/api/project/libraries/LibraryTest.java:1.5 projects/libraries/test/unit/src/org/netbeans/api/project/libraries/LibraryTest.java:1.4.14.2 --- projects/libraries/test/unit/src/org/netbeans/api/project/libraries/LibraryTest.java:1.5 Wed Sep 26 14:04:24 2007 +++ projects/libraries/test/unit/src/org/netbeans/api/project/libraries/LibraryTest.java Thu Nov 22 08:40:40 2007 @@ -41,64 +41,57 @@ package org.netbeans.api.project.libraries; +import static org.netbeans.modules.project.libraries.TestUtil.*; import java.net.URL; import java.util.ArrayList; import java.util.List; -import org.netbeans.api.project.TestUtil; import org.netbeans.junit.NbTestCase; import org.netbeans.spi.project.libraries.LibraryImplementation; -import org.netbeans.spi.project.libraries.LibraryProvider; import org.openide.util.lookup.Lookups; -/** - * - * @author Tomas Zezula - */ +import org.openide.util.test.MockLookup; +import org.openide.util.test.MockPropertyChangeListener; + public class LibraryTest extends NbTestCase { - private LibraryManagerTest.TestLibraryProvider lp; + private WLP lp; - /** Creates a new instance of LibraryManagerTest */ public LibraryTest (String testName) { super (testName); } protected void setUp() throws Exception { super.setUp(); - lp = new LibraryManagerTest.TestLibraryProvider (); - TestUtil.setLookup (Lookups.fixed(new Object[] {lp})); + lp = new WLP(); + MockLookup.setLookup(Lookups.singleton(lp)); } public void testGetLibraries () throws Exception { LibraryManager lm = LibraryManager.getDefault(); Library[] libs = lm.getLibraries(); LibraryImplementation[] impls = LibraryManagerTest.createTestLibs (); - lp.setLibraries(impls); + lp.set(impls); libs = lm.getLibraries(); assertEquals ("Libraries count", 2, libs.length); LibraryManagerTest.assertLibsEquals (libs, impls); - LibraryManagerTest.TestListener tl = new LibraryManagerTest.TestListener (); - libs[0].addPropertyChangeListener(tl); + MockPropertyChangeListener pcl = new MockPropertyChangeListener(); + libs[0].addPropertyChangeListener(pcl); impls[0].setName("NewLibrary1"); - LibraryManagerTest.assertEventsEquals(tl.getEventNames(), new String[] {Library.PROP_NAME}); - tl.reset(); + pcl.assertEvents(Library.PROP_NAME); LibraryManagerTest.assertLibsEquals (new Library[] {libs[0]}, new LibraryImplementation[] {impls[0]}); impls[0].setDescription("NewLibrary1Description"); - LibraryManagerTest.assertEventsEquals(tl.getEventNames(), new String[] {Library.PROP_DESCRIPTION}); - tl.reset(); + pcl.assertEvents(Library.PROP_DESCRIPTION); LibraryManagerTest.assertLibsEquals (new Library[] {libs[0]}, new LibraryImplementation[] {impls[0]}); List urls = new ArrayList(); urls.add (new URL ("file:/lib/libnew1.so")); urls.add (new URL ("file:/lib/libnew2.so")); impls[0].setContent ("bin",urls); - LibraryManagerTest.assertEventsEquals(tl.getEventNames(), new String[] {Library.PROP_CONTENT}); - tl.reset(); + pcl.assertEvents(Library.PROP_CONTENT); LibraryManagerTest.assertLibsEquals (new Library[] {libs[0]}, new LibraryImplementation[] {impls[0]}); urls = new ArrayList(); urls.add (new URL ("file:/src/new/src/")); impls[0].setContent ("src",urls); - LibraryManagerTest.assertEventsEquals(tl.getEventNames(), new String[] {Library.PROP_CONTENT}); - tl.reset(); + pcl.assertEvents(Library.PROP_CONTENT); LibraryManagerTest.assertLibsEquals (new Library[] {libs[0]}, new LibraryImplementation[] {impls[0]}); } Index: projects/libraries/test/unit/src/org/netbeans/modules/project/libraries/TestUtil.java diff -u /dev/null projects/libraries/test/unit/src/org/netbeans/modules/project/libraries/TestUtil.java:1.1.2.2 --- /dev/null Fri Jan 18 01:33:17 2008 +++ projects/libraries/test/unit/src/org/netbeans/modules/project/libraries/TestUtil.java Wed Apr 25 14:19:22 2007 @@ -0,0 +1,245 @@ +/* + * The contents of this file are subject to the terms of the Common Development + * and Distribution License (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at http://www.netbeans.org/cddl.html + * or http://www.netbeans.org/cddl.txt. + * + * When distributing Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://www.netbeans.org/cddl.txt. + * If applicable, add the following below the CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans.modules.project.libraries; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.netbeans.spi.project.libraries.ArealLibraryProvider; +import org.netbeans.spi.project.libraries.LibraryImplementation; +import org.netbeans.spi.project.libraries.LibraryProvider; +import org.netbeans.spi.project.libraries.LibraryStorageArea; +import org.netbeans.spi.project.libraries.support.LibrariesSupport; +import org.openide.util.WeakSet; + +/** + * Common support classes for unit tests in this module. + */ +public class TestUtil { + + private TestUtil() {} + + public static class NWLP implements LibraryProvider { + + public final List libs = new ArrayList(); + final PropertyChangeSupport pcs = new PropertyChangeSupport(this); + + public void set(LibraryImplementation... nue) { + libs.clear(); + libs.addAll(Arrays.asList(nue)); + pcs.firePropertyChange(PROP_LIBRARIES, null, null); + } + + public LibraryImplementation[] getLibraries() { + return libs.toArray(new LibraryImplementation[0]); + } + + public void addPropertyChangeListener(PropertyChangeListener listener) { + pcs.addPropertyChangeListener(listener); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) { + pcs.removePropertyChangeListener(listener); + } + + } + + public static final class WLP extends NWLP implements WritableLibraryProvider { + + public void addLibrary(LibraryImplementation library) throws IOException { + libs.add(library); + pcs.firePropertyChange(PROP_LIBRARIES, null, null); + } + + public void removeLibrary(LibraryImplementation library) throws IOException { + libs.remove(library); + pcs.firePropertyChange(PROP_LIBRARIES, null, null); + } + + public void updateLibrary(LibraryImplementation oldLibrary, LibraryImplementation newLibrary) throws IOException { + libs.remove(oldLibrary); + libs.add(newLibrary); + pcs.firePropertyChange(PROP_LIBRARIES, null, null); + } + + } + + public static final class Area implements LibraryStorageArea { + + final String id; + + public Area(String id) { + this.id = id; + } + + public URL getLocation() { + try { + return new URL("http://nowhere.net/" + id); + } catch (MalformedURLException x) { + throw new AssertionError(x); + } + } + + public String getDisplayName() { + return id; + } + + public boolean equals(Object obj) { + return obj instanceof Area && ((Area) obj).id.equals(id); + } + + public int hashCode() { + return id.hashCode(); + } + + public @Override String toString() { + return "Area[" + id + "]"; + } + + } + + public static final class ALP implements ArealLibraryProvider { + + final PropertyChangeSupport pcs = new PropertyChangeSupport(this); + public final Map> libs = new HashMap>(); + final Set lps = new WeakSet(); + final Set open = new HashSet(); + + public void addPropertyChangeListener(PropertyChangeListener listener) { + pcs.addPropertyChangeListener(listener); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) { + pcs.removePropertyChangeListener(listener); + } + + public Class areaType() { + return Area.class; + } + + public Class libraryType() { + return LibraryImplementation.class; + } + + public Area createArea() { + return new Area("new"); + } + + public Area loadArea(URL location) { + Matcher m = Pattern.compile("http://nowhere\\.net/(.+)$").matcher(location.toExternalForm()); + if (m.matches()) { + return new Area(m.group(1)); + } else { + return null; + } + } + + public Set getOpenAreas() { + return open; + } + + public void setOpen(Area... areas) { + open.clear(); + open.addAll(Arrays.asList(areas)); + pcs.firePropertyChange(PROP_OPEN_AREAS, null, null); + } + + private class LP implements LibraryProvider { + + final PropertyChangeSupport pcs = new PropertyChangeSupport(this); + final Area area; + + LP(Area area) { + this.area = area; + lps.add(this); + } + + public LibraryImplementation[] getLibraries() { + if (libs.containsKey(area)) { + return libs.get(area).toArray(new LibraryImplementation[0]); + } else { + return new LibraryImplementation[0]; + } + } + + public void addPropertyChangeListener(PropertyChangeListener listener) { + pcs.addPropertyChangeListener(listener); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) { + pcs.removePropertyChangeListener(listener); + } + + } + + public LibraryProvider getLibraries(Area area) { + return new LP(area); + } + + public LibraryImplementation createLibrary(String type, String name, Area area, Map> contents) throws IOException { + LibraryImplementation lib = LibrariesSupport.createLibraryImplementation(type, contents.keySet().toArray(new String[0])); + lib.setName(name); + for (Map.Entry> entry : contents.entrySet()) { + lib.setContent(entry.getKey(), entry.getValue()); + } + List l = libs.get(area); + if (l == null) { + l = new ArrayList(); + libs.put(area, l); + } + l.add(lib); + for (LP lp : lps) { + if (lp.area.equals(area)) { + lp.pcs.firePropertyChange(LibraryProvider.PROP_LIBRARIES, null, null); + } + } + return lib; + } + + public void remove(LibraryImplementation library) throws IOException { + for (Map.Entry> entry : libs.entrySet()) { + if (entry.getValue().remove(library)) { + for (LP lp : lps) { + if (lp.area.equals(entry.getKey())) { + lp.pcs.firePropertyChange(LibraryProvider.PROP_LIBRARIES, null, null); + } + } + } + } + } + + } + + public static URL mkJar(String name) throws MalformedURLException { + return new URL("jar:http://nowhere.net/" + name + "!/"); + } + +} Index: projects/libraries/test/unit/src/org/netbeans/modules/project/libraries/ui/LibrariesModelTest.java diff -u /dev/null projects/libraries/test/unit/src/org/netbeans/modules/project/libraries/ui/LibrariesModelTest.java:1.1.2.8 --- /dev/null Fri Jan 18 01:33:17 2008 +++ projects/libraries/test/unit/src/org/netbeans/modules/project/libraries/ui/LibrariesModelTest.java Fri Apr 27 20:39:02 2007 @@ -0,0 +1,186 @@ +/* + * The contents of this file are subject to the terms of the Common Development + * and Distribution License (the License). You may not use this file except in + * compliance with the License. + * + * You can obtain a copy of the License at http://www.netbeans.org/cddl.html + * or http://www.netbeans.org/cddl.txt. + * + * When distributing Covered Code, include this CDDL Header Notice in each file + * and include the License file at http://www.netbeans.org/cddl.txt. + * If applicable, add the following below the CDDL Header, with the fields + * enclosed by brackets [] replaced by your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans.modules.project.libraries.ui; + +import static org.netbeans.modules.project.libraries.TestUtil.*; +import java.net.URL; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; +import org.netbeans.junit.NbTestCase; +import org.netbeans.spi.project.libraries.LibraryImplementation; +import org.netbeans.spi.project.libraries.LibraryStorageArea; +import org.netbeans.spi.project.libraries.support.LibrariesSupport; +import org.openide.util.test.MockChangeListener; +import org.openide.util.test.MockLookup; + +public class LibrariesModelTest extends NbTestCase { + + // XXX test: + // editing global libs + // library editability flag is correct + // IOException thrown by providers handled reasonably + + public LibrariesModelTest(String n) { + super(n); + } + + private LibrariesModel model; + private LibraryImplementation lib1, lib2, lib3; + private URL jar1, jar2, jar3; + private MockChangeListener cl; + private WLP wlp; + private NWLP nwlp; + private ALP alp; + + @Override + protected void setUp() throws Exception { + super.setUp(); + wlp = new WLP(); + nwlp = new NWLP(); + alp = new ALP(); + MockLookup.setInstances(wlp, nwlp, alp); + LibrariesModel.createdAreas.clear(); + model = new LibrariesModel(); + cl = new MockChangeListener(); + model.addChangeListener(cl); + lib1 = mkLib("lib1"); + lib2 = mkLib("lib2"); + lib3 = mkLib("lib3"); + jar1 = mkJar("lib1.jar"); + jar2 = mkJar("lib2.jar"); + jar3 = mkJar("lib3.jar"); + cl.assertNoEvents(); + assertEquals(0, model.getLibraries().size()); + } + + public void testNWLP() throws Exception { + nwlp.set(lib1, lib2); + cl.assertEvent(); + assertEquals(2, model.getLibraries().size()); + } + + public void testWLP() throws Exception { + model.addLibrary(lib1); + cl.assertEvent(); + assertEquals(1, model.getLibraries().size()); + model.addLibrary(lib2); + cl.assertEvent(); + assertEquals(2, model.getLibraries().size()); + model.removeLibrary(lib1); + cl.assertEvent(); + assertEquals(1, model.getLibraries().size()); + model.apply(); + assertEquals(1, wlp.libs.size()); + LibraryImplementation lib = wlp.libs.iterator().next(); + assertEquals("lib2", lib.getName()); + } + + public void testAreas() throws Exception { + assertEquals("[]", areas2S()); + alp.setOpen(new Area("o1"), new Area("o2")); + cl.assertEvent(); + assertEquals("[o1, o2]", areas2S()); + assertEquals("new", model.createArea().getDisplayName()); + cl.assertEvent(); + assertEquals("[new, o1, o2]", areas2S()); + } + private String areas2S() { + SortedSet names = new TreeSet(); + for (LibraryStorageArea area : model.getAreas()) { + names.add(area.getDisplayName()); + } + return names.toString(); + } + + public void testArealLibs() throws Exception { + Area a = new Area("home"); + alp.setOpen(a); + LibraryImplementation lib1 = model.createArealLibrary("jar", "mylib1", a); + assertEquals(a, model.getArea(lib1)); + model.addLibrary(lib1); + LibraryImplementation lib2 = model.createArealLibrary("jar", "mylib2", a); + model.addLibrary(lib2); + model.removeLibrary(lib2); + lib1.setContent("cp", Arrays.asList(jar1, jar2)); + model.apply(); + assertEquals(1, alp.libs.size()); + assertEquals(a, alp.libs.keySet().iterator().next()); + List libs = alp.libs.get(a); + assertEquals(1, libs.size()); + LibraryImplementation lib = libs.iterator().next(); + assertEquals("mylib1", lib.getName()); + assertEquals(Arrays.asList(jar1, jar2), lib.getContent("cp")); + assertEquals(a, model.getArea(lib)); + model = new LibrariesModel(); + assertEquals(1, model.getLibraries().size()); + lib = model.getLibraries().iterator().next(); + assertEquals(a, model.getArea(lib)); + lib.setContent("cp", Collections.singletonList(jar3)); + model.apply(); + assertEquals(1, alp.libs.size()); + assertEquals(a, alp.libs.keySet().iterator().next()); + libs = alp.libs.get(a); + assertEquals(1, libs.size()); + lib = libs.iterator().next(); + assertEquals("mylib1", lib.getName()); + assertEquals(Collections.singletonList(jar3), lib.getContent("cp")); + assertEquals(a, model.getArea(lib)); + model = new LibrariesModel(); + assertEquals(1, model.getLibraries().size()); + lib = model.getLibraries().iterator().next(); + model.removeLibrary(lib); + model.apply(); + assertEquals(1, alp.libs.size()); + assertEquals(a, alp.libs.keySet().iterator().next()); + libs = alp.libs.get(a); + assertEquals(0, libs.size()); + Area a2 = new Area("away"); + assertEquals(0, model.getLibraries().size()); + model.addLibrary(model.createArealLibrary("jar", "generic", a)); + model.addLibrary(model.createArealLibrary("jar", "generic", a2)); + assertEquals(2, model.getLibraries().size()); + } + + public void testLoadLibsAfterCreateArea() throws Exception { + LibraryStorageArea a = model.createArea(); + model.addLibrary(model.createArealLibrary("jar", "foo", a)); + model.apply(); + LibrariesModel.createdAreas.clear(); + model = new LibrariesModel(); + cl = new MockChangeListener(); + model.addChangeListener(cl); + assertEquals(a, model.createArea()); + cl.assertEvent(); + Collection libs = model.getLibraries(); + assertEquals(1, libs.size()); + assertEquals("foo", libs.iterator().next().getName()); + } + + private LibraryImplementation mkLib(String name) { + LibraryImplementation lib = LibrariesSupport.createLibraryImplementation("jar", new String[] {"cp"}); + lib.setName(name); + return lib; + } + +} Index: projects/libraries/test/unit/src/org/netbeans/spi/project/libraries/support/LibrariesSupportTest.java diff -u /dev/null projects/libraries/test/unit/src/org/netbeans/spi/project/libraries/support/LibrariesSupportTest.java:1.1.2.2 --- /dev/null Fri Jan 18 01:33:18 2008 +++ projects/libraries/test/unit/src/org/netbeans/spi/project/libraries/support/LibrariesSupportTest.java Wed Jan 9 04:18:43 2008 @@ -0,0 +1,114 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2007 Sun Microsystems, Inc. + */ + +package org.netbeans.spi.project.libraries.support; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import org.netbeans.junit.NbTestCase; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; + +/** + */ +public class LibrariesSupportTest extends NbTestCase { + + public LibrariesSupportTest(String testName) { + super(testName); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + /** + * Test of convertFileToURL method, of class LibrariesSupport. + */ + public void testConvertFileToURL() { + File f = new File("/aa/bb/c c.ext".replace('/', File.separatorChar)); + URL u = LibrariesSupport.convertFileToURL(f); + assertEquals("file:/aa/bb/c%20c.ext", u.toExternalForm()); + f = new File("../zz/re l.ext".replace('/', File.separatorChar)); + u = LibrariesSupport.convertFileToURL(f); + assertEquals("file:../zz/re%20l.ext", u.toExternalForm()); + } + + /** + * Test of convertURLToFile method, of class LibrariesSupport. + */ + public void testConvertURLToFile() throws MalformedURLException{ + URL u = new URL("file:/aa/bb/c%20c.ext"); + File f = LibrariesSupport.convertURLToFile(u); + assertEquals("/aa/bb/c c.ext".replace('/', File.separatorChar), f.getPath()); + u = new URL("file:../zz/re%20l.ext"); + f = LibrariesSupport.convertURLToFile(u); + assertEquals("../zz/re l.ext".replace('/', File.separatorChar), f.getPath()); + } + + /** + * Test of isAbsoluteURL method, of class LibrariesSupport. + */ + public void testIsAbsoluteURL() throws MalformedURLException{ + URL u = new URL("file", null, "/test/absolute"); + assertTrue(u.toExternalForm(), LibrariesSupport.isAbsoluteURL(u)); + u = new URL("file", null, "../relative"); + assertFalse(u.toExternalForm(), LibrariesSupport.isAbsoluteURL(u)); + } + + /** + * Test of resolveLibraryEntryFileObject method, of class LibrariesSupport. + */ + public void testResolveLibraryEntry() throws Exception { + File f = new File(this.getWorkDir(), "knihovna.properties"); + File f2 = new File(this.getWorkDir(), "bertie.jar"); + f.createNewFile(); + f2.createNewFile(); + FileObject fo = LibrariesSupport.resolveLibraryEntryFileObject( + f.toURI().toURL(), + new URL("file", null, "bertie.jar")); + assertEquals(f2.getPath(), FileUtil.toFile(fo).getPath()); + fo = LibrariesSupport.resolveLibraryEntryFileObject( + null, + f2.toURI().toURL()); + assertEquals(f2.getPath(), FileUtil.toFile(fo).getPath()); + } + +}