diff --git a/project.libraries/apichanges.xml b/project.libraries/apichanges.xml --- a/project.libraries/apichanges.xml +++ b/project.libraries/apichanges.xml @@ -104,6 +104,37 @@ is the proper place. + + + URI used for relative library entry instead of URL + + + + +

+ Signatures of some methods previously using URL were changed + to use URI. Some URL handling methods were removed and/or + replaced with URI counterpart. +

+
+ +

+ API review of Sharable Libraries requested using URI for + relative paths instead of URL. +

+

+ Added LibraryCustomizerContext class to provide richer + context to libraries customizer. +

+
+ + + + + + +
+ Support for project libraries diff --git a/project.libraries/manifest.mf b/project.libraries/manifest.mf --- a/project.libraries/manifest.mf +++ b/project.libraries/manifest.mf @@ -2,7 +2,7 @@ OpenIDE-Module: org.netbeans.modules.pro OpenIDE-Module: org.netbeans.modules.project.libraries/1 OpenIDE-Module-Install: org/netbeans/modules/project/libraries/LibrariesModule.class OpenIDE-Module-Layer: org/netbeans/modules/project/libraries/resources/mf-layer.xml -OpenIDE-Module-Specification-Version: 1.16 +OpenIDE-Module-Specification-Version: 1.18 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/project/libraries/resources/Bundle.properties AutoUpdate-Show-In-Client: false diff --git a/project.libraries/src/org/netbeans/api/project/libraries/Library.java b/project.libraries/src/org/netbeans/api/project/libraries/Library.java --- a/project.libraries/src/org/netbeans/api/project/libraries/Library.java +++ b/project.libraries/src/org/netbeans/api/project/libraries/Library.java @@ -43,15 +43,22 @@ package org.netbeans.api.project.librari import java.beans.PropertyChangeListener; import java.beans.PropertyChangeEvent; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; import java.net.URL; import java.util.ArrayList; +import java.util.List; import java.util.List; import java.util.MissingResourceException; import java.util.ResourceBundle; import org.netbeans.modules.project.libraries.LibraryAccessor; +import org.netbeans.modules.project.libraries.ui.LibrariesModel; import org.netbeans.spi.project.libraries.LibraryImplementation; +import org.netbeans.spi.project.libraries.LibraryImplementation2; import org.netbeans.spi.project.libraries.support.LibrariesSupport; import org.openide.ErrorManager; +import org.openide.util.Exceptions; import org.openide.util.NbBundle; /** @@ -99,8 +106,7 @@ public final class Library { } /** - * Access typed library data. Any relative URL provided by SPI is made absolute - * before being passed to client. See {@link #getRawContent} if you need raw library data. + * Access typed raw library data as URLs. *

* The contents are defined by SPI providers and identified * by the volume types. For example the j2se library supports the following @@ -109,19 +115,14 @@ public final class Library { *

* * @param volumeType which resources to return. - * @return path of URLs of given type (possibly empty but never null) + * @return list of URLs of given volume type (possibly empty but never null) */ public List getContent(final String volumeType) { - List urls = this.impl.getContent (volumeType); - List resolvedUrls = new ArrayList(urls.size()); - for (URL u : urls) { - resolvedUrls.add(LibrariesSupport.resolveLibraryEntryURL(manager.getLocation(), u)); - } - return resolvedUrls; + return impl.getContent (volumeType); } // end getContent /** - * Access typed but raw library data. + * Access typed raw library data as possibly relative URIs. *

* The contents are defined by SPI providers and identified * by the volume types. For example the j2se library supports the following @@ -130,10 +131,15 @@ public final class Library { *

* * @param volumeType which resources to return. - * @return path of URLs of given type (possibly empty but never null) + * @return list of URIs of given volume type (possibly empty but never null) + * @since org.netbeans.modules.project.libraries/1 1.18 */ - public List getRawContent(final String volumeType) { - return this.impl.getContent (volumeType); + public List getURIContent(final String volumeType) { + if (impl instanceof LibraryImplementation2) { + return ((LibraryImplementation2)impl).getURIContent(volumeType); + } else { + return LibrariesModel.convertURLsToURIs(impl.getContent(volumeType)); + } } // end getContent diff --git a/project.libraries/src/org/netbeans/api/project/libraries/LibraryManager.java b/project.libraries/src/org/netbeans/api/project/libraries/LibraryManager.java --- a/project.libraries/src/org/netbeans/api/project/libraries/LibraryManager.java +++ b/project.libraries/src/org/netbeans/api/project/libraries/LibraryManager.java @@ -45,9 +45,12 @@ import java.beans.PropertyChangeListener import java.beans.PropertyChangeListener; import java.beans.PropertyChangeEvent; import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; import java.net.URL; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -57,6 +60,7 @@ import org.netbeans.modules.project.libr 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.LibraryImplementation2; import org.netbeans.spi.project.libraries.LibraryProvider; import org.netbeans.spi.project.libraries.LibraryStorageArea; import org.netbeans.spi.project.libraries.LibraryTypeProvider; @@ -273,6 +277,44 @@ public final class LibraryManager { } Lookup.getDefault().lookup(WritableLibraryProvider.class).addLibrary(impl); } else { + Map> cont = new HashMap>(); + for (Map.Entry> entry : contents.entrySet()) { + cont.put(entry.getKey(), LibrariesModel.convertURLsToURIs(entry.getValue())); + } + impl = LibraryAccessor.createLibrary(alp, type, name, area, cont); + } + return new Library(impl, this); + } + + /** + * 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.18 + */ + public Library createURILibrary(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(), LibrariesModel.convertURIsToURLs(entry.getValue())); + } + Lookup.getDefault().lookup(WritableLibraryProvider.class).addLibrary(impl); + } else { impl = LibraryAccessor.createLibrary(alp, type, name, area, contents); } return new Library(impl, this); @@ -293,7 +335,8 @@ public final class LibraryManager { assert providers.size() == 1; providers.iterator().next().removeLibrary(library.getLibraryImplementation()); } else { - LibraryAccessor.remove(alp, library.getLibraryImplementation()); + assert library.getLibraryImplementation() instanceof LibraryImplementation2; + LibraryAccessor.remove(alp, (LibraryImplementation2)library.getLibraryImplementation()); } } @@ -315,7 +358,6 @@ public final class LibraryManager { assert listener != null; this.listeners.removePropertyChangeListener (listener); } - private synchronized void resetCache () { this.cache = null; diff --git a/project.libraries/src/org/netbeans/modules/project/libraries/LibraryAccessor.java b/project.libraries/src/org/netbeans/modules/project/libraries/LibraryAccessor.java --- a/project.libraries/src/org/netbeans/modules/project/libraries/LibraryAccessor.java +++ b/project.libraries/src/org/netbeans/modules/project/libraries/LibraryAccessor.java @@ -42,13 +42,14 @@ package org.netbeans.modules.project.lib package org.netbeans.modules.project.libraries; import java.io.IOException; -import java.net.URL; +import java.net.URI; 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.LibraryImplementation2; import org.netbeans.spi.project.libraries.LibraryProvider; import org.netbeans.spi.project.libraries.LibraryStorageArea; import org.openide.util.Exceptions; @@ -74,10 +75,10 @@ public abstract class LibraryAccessor { * 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 { + public static void remove(ArealLibraryProvider alp, LibraryImplementation2 lib) throws IOException { remove0((ArealLibraryProvider) alp, lib); } - private static void remove0(ArealLibraryProvider alp, LibraryImplementation lib) throws IOException { + private static void remove0(ArealLibraryProvider alp, LibraryImplementation2 lib) throws IOException { alp.remove(alp.libraryType().cast(lib)); } @@ -92,10 +93,10 @@ public abstract class LibraryAccessor { * 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 { + public static LibraryImplementation2 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 { + private static LibraryImplementation2 createLibrary0(ArealLibraryProvider alp, String type, String name, LibraryStorageArea area, Map> contents) throws IOException { return alp.createLibrary(type, name, alp.areaType().cast(area), contents); } diff --git a/project.libraries/src/org/netbeans/modules/project/libraries/ui/AllLibrariesCustomizer.java b/project.libraries/src/org/netbeans/modules/project/libraries/ui/AllLibrariesCustomizer.java --- a/project.libraries/src/org/netbeans/modules/project/libraries/ui/AllLibrariesCustomizer.java +++ b/project.libraries/src/org/netbeans/modules/project/libraries/ui/AllLibrariesCustomizer.java @@ -41,9 +41,10 @@ package org.netbeans.modules.project.libraries.ui; -import org.netbeans.api.project.libraries.*; +import java.io.File; +import java.net.MalformedURLException; +import java.net.URI; import org.netbeans.modules.project.libraries.ui.*; -import java.io.File; import java.net.URL; import java.util.ArrayList; import java.util.List; @@ -52,7 +53,7 @@ import org.netbeans.modules.project.libr 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.Exceptions; import org.openide.util.Lookup; import org.openide.util.NbBundle; @@ -81,7 +82,7 @@ class AllLibrariesCustomizer extends jav if (man.getLocation() == null) { continue; } - items.add(LibrariesSupport.convertURLToFilePath(man.getLocation())); + items.add(URI.create(man.getLocation().toExternalForm()).getPath()); } libraryManagerComboBox.setModel(new DefaultComboBoxModel(items.toArray(new String[items.size()]))); } @@ -141,7 +142,12 @@ class AllLibrariesCustomizer extends jav } else if (index == 0) { librariesCustomizer.setLibraryStorageArea(null); } else if (index > 0) { - URL u = LibrariesSupport.convertFilePathToURL((String)libraryManagerComboBox.getModel().getSelectedItem()); + URL u = null; + try { + u = new File((String) libraryManagerComboBox.getModel().getSelectedItem()).toURL(); + } catch (MalformedURLException ex) { + Exceptions.printStackTrace(ex); + } librariesCustomizer.setLibraryStorageArea(findLibraryStorageArea(u)); } }//GEN-LAST:event_libraryManagerComboBoxActionPerformed diff --git a/project.libraries/src/org/netbeans/modules/project/libraries/ui/LibrariesCustomizer.java b/project.libraries/src/org/netbeans/modules/project/libraries/ui/LibrariesCustomizer.java --- a/project.libraries/src/org/netbeans/modules/project/libraries/ui/LibrariesCustomizer.java +++ b/project.libraries/src/org/netbeans/modules/project/libraries/ui/LibrariesCustomizer.java @@ -70,6 +70,7 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.netbeans.modules.project.libraries.LibraryTypeRegistry; +import org.netbeans.spi.project.libraries.LibraryCustomizerContext; import org.netbeans.spi.project.libraries.LibraryImplementation; import org.netbeans.spi.project.libraries.LibraryStorageArea; import org.netbeans.spi.project.libraries.LibraryTypeProvider; @@ -302,15 +303,14 @@ public final class LibrariesCustomizer e LibraryTypeProvider provider = nodes[0].getLookup().lookup(LibraryTypeProvider.class); if (provider == null) return; - // a library customizer needs to know location of sharable library in order to - // relativize paths. that's why object implementing both LibraryImplementation - // and LibraryStorageArea is passed to JComponent here: + LibraryCustomizerContextWrapper customizerContext; LibraryStorageArea area = nodes[0].getLookup().lookup(LibraryStorageArea.class); if (area != null && area != LibrariesModel.GLOBAL_AREA) { - impl = new LibraryImplementationWrapper(impl, area); + customizerContext = new LibraryCustomizerContextWrapper(impl, area); File f = new File(URI.create(area.getLocation().toExternalForm())); this.libraryLocation.setText(f.getPath()); } else { + customizerContext = new LibraryCustomizerContextWrapper(impl, null); this.libraryLocation.setText(NbBundle.getMessage(LibrariesCustomizer.class,"LABEL_Global_Libraries")); } @@ -318,7 +318,7 @@ public final class LibrariesCustomizer e for (int i=0; i< volumeTypes.length; i++) { Customizer c = provider.getCustomizer (volumeTypes[i]); if (c instanceof JComponent) { - c.setObject (impl); + c.setObject (customizerContext); JComponent component = (JComponent) c; component.setEnabled (editable); String tabName = component.getName(); @@ -817,67 +817,60 @@ public final class LibrariesCustomizer e private Node buildTree() { return new AbstractNode(new TypeChildren(libraryStorageArea)); } - - private static class LibraryImplementationWrapper implements LibraryImplementation, LibraryStorageArea { + + /** + * This is backward compatible wrapper which can be passed to libraries customizer + * via JComponent.setObject and which provides to customizer both LibraryImplementation + * (old contract) and LibraryCustomizerContext (new contract). + */ + private static class LibraryCustomizerContextWrapper extends LibraryCustomizerContext implements LibraryImplementation { - private LibraryImplementation lib; - private LibraryStorageArea area; - - public LibraryImplementationWrapper(LibraryImplementation lib, LibraryStorageArea area) { - this.lib = lib; - this.area = area; + public LibraryCustomizerContextWrapper(LibraryImplementation lib, LibraryStorageArea area) { + super(lib, area); } public String getType() { - return lib.getType(); + return getLibraryImplementation().getType(); } public String getName() { - return lib.getName(); + return getLibraryImplementation().getName(); } public String getDescription() { - return lib.getDescription(); + return getLibraryImplementation().getDescription(); } public String getLocalizingBundle() { - return lib.getLocalizingBundle(); + return getLibraryImplementation().getLocalizingBundle(); } public List getContent(String volumeType) throws IllegalArgumentException { - return lib.getContent(volumeType); + return getLibraryImplementation().getContent(volumeType); } public void setName(String name) { - lib.setName(name); + getLibraryImplementation().setName(name); } public void setDescription(String text) { - lib.setDescription(text); + getLibraryImplementation().setDescription(text); } public void setLocalizingBundle(String resourceName) { - lib.setLocalizingBundle(resourceName); + getLibraryImplementation().setLocalizingBundle(resourceName); } public void addPropertyChangeListener(PropertyChangeListener l) { - lib.addPropertyChangeListener(l); + getLibraryImplementation().addPropertyChangeListener(l); } public void removePropertyChangeListener(PropertyChangeListener l) { - lib.removePropertyChangeListener(l); + getLibraryImplementation().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(); + getLibraryImplementation().setContent(volumeType, path); } } diff --git a/project.libraries/src/org/netbeans/modules/project/libraries/ui/LibrariesModel.java b/project.libraries/src/org/netbeans/modules/project/libraries/ui/LibrariesModel.java --- a/project.libraries/src/org/netbeans/modules/project/libraries/ui/LibrariesModel.java +++ b/project.libraries/src/org/netbeans/modules/project/libraries/ui/LibrariesModel.java @@ -46,6 +46,8 @@ import java.beans.PropertyChangeListener import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; import java.net.URL; import java.util.ArrayList; import java.util.Collection; @@ -65,7 +67,9 @@ import org.netbeans.spi.project.librarie 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.LibraryImplementation2; import org.netbeans.spi.project.libraries.LibraryStorageArea; +import org.openide.util.Exceptions; import org.openide.util.Lookup; import org.openide.util.ChangeSupport; import org.openide.util.NbBundle; @@ -224,9 +228,10 @@ public class LibrariesModel implements P if (storage == this.writableProvider) { this.writableProvider.removeLibrary (impl); } else { + assert impl instanceof LibraryImplementation2; LibraryStorageArea area = getAreaOrNull(impl); if (area != null) { - LibraryAccessor.remove(area2Storage.get(area), impl); + LibraryAccessor.remove(area2Storage.get(area), (LibraryImplementation2)impl); } else { throw new IOException("Cannot find storage for library: " + impl.getName()); // NOI18N } @@ -331,10 +336,10 @@ public class LibrariesModel implements P } } - private static final class DummyArealLibrary implements LibraryImplementation { + private static final class DummyArealLibrary implements LibraryImplementation2 { private final String type, name; - final Map> contents = new HashMap>(); + final Map> contents = new HashMap>(); private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); public DummyArealLibrary(String type, String name) { @@ -359,7 +364,11 @@ public class LibrariesModel implements P } public List getContent(String volumeType) throws IllegalArgumentException { - List content = contents.get(volumeType); + return convertURIsToURLs(getURIContent(volumeType)); + } + + public List getURIContent(String volumeType) throws IllegalArgumentException { + List content = contents.get(volumeType); if (content != null) { return content; } else { @@ -388,6 +397,10 @@ public class LibrariesModel implements P } public void setContent(String volumeType, List path) throws IllegalArgumentException { + setURIContent(volumeType, convertURLsToURIs(path)); + } + + public void setURIContent(String volumeType, List path) throws IllegalArgumentException { contents.put(volumeType, path); pcs.firePropertyChange(LibraryImplementation.PROP_CONTENT, null, null); } @@ -399,4 +412,25 @@ public class LibrariesModel implements P } + public static List convertURIsToURLs(List uris) { + List content = new ArrayList(); + for (URI uri : uris) { + try { + content.add(uri.toURL()); + } catch (MalformedURLException ex) { + Exceptions.printStackTrace(ex); + } + } + return content; + } + + public static List convertURLsToURIs(List entry) { + List content = new ArrayList(); + for (URL url : entry) { + content.add(URI.create(url.toExternalForm())); + } + return content; + } + + } diff --git a/project.libraries/src/org/netbeans/spi/project/libraries/ArealLibraryProvider.java b/project.libraries/src/org/netbeans/spi/project/libraries/ArealLibraryProvider.java --- a/project.libraries/src/org/netbeans/spi/project/libraries/ArealLibraryProvider.java +++ b/project.libraries/src/org/netbeans/spi/project/libraries/ArealLibraryProvider.java @@ -21,6 +21,7 @@ package org.netbeans.spi.project.librari import java.beans.PropertyChangeListener; import java.io.IOException; +import java.net.URI; import java.net.URL; import java.util.List; import java.util.Map; @@ -33,7 +34,7 @@ import java.util.Set; * @param L the type of library created by this provider * @since org.netbeans.modules.project.libraries/1 1.15 */ -public interface ArealLibraryProvider { +public interface ArealLibraryProvider { /** * Property to fire when {@link #getOpenAreas} might have changed. @@ -112,7 +113,7 @@ public interface ArealLibraryProvider> contents) throws IOException; + L createLibrary(String type, String name, A area, Map> contents) throws IOException; /** * Deletes an existing library. diff --git a/project.libraries/src/org/netbeans/spi/project/libraries/LibraryCustomizerContext.java b/project.libraries/src/org/netbeans/spi/project/libraries/LibraryCustomizerContext.java new file mode 100644 --- /dev/null +++ b/project.libraries/src/org/netbeans/spi/project/libraries/LibraryCustomizerContext.java @@ -0,0 +1,78 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2008 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.spi.project.libraries; + +/** + * Context class which is passed to library customizer (via JComponent.setObject). + * Do not extend or instantiate this class directly. + * + * @since org.netbeans.modules.project.libraries/1 1.18 + */ +public class LibraryCustomizerContext { + + private LibraryImplementation libraryImplementation; + private LibraryStorageArea libraryStorageArea; + + public LibraryCustomizerContext(LibraryImplementation libraryImplementation, LibraryStorageArea libraryStorageArea) { + this.libraryImplementation = libraryImplementation; + this.libraryStorageArea = libraryStorageArea; + } + + /** + * Library implementation to be customized. + * + * @return always non-null + */ + public LibraryImplementation getLibraryImplementation() { + return libraryImplementation; + } + + /** + * Area of library being customized. + * + * @return can be null for global library + */ + public LibraryStorageArea getLibraryStorageArea() { + return libraryStorageArea; + } + +} diff --git a/project.libraries/src/org/netbeans/spi/project/libraries/LibraryImplementation2.java b/project.libraries/src/org/netbeans/spi/project/libraries/LibraryImplementation2.java new file mode 100644 --- /dev/null +++ b/project.libraries/src/org/netbeans/spi/project/libraries/LibraryImplementation2.java @@ -0,0 +1,73 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2008 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.spi.project.libraries; + +import java.net.URI; +import java.util.List; + +/** + * Library enhancement allowing setting/getting library content as URI list. + * Useful for example for storing relative library entries. + * + * @since org.netbeans.modules.project.libraries/1 1.18 + */ +public interface LibraryImplementation2 extends LibraryImplementation { + + /** + * Returns List of resources contained in the given volume. + * The returned list is unmodifiable. To change the content of + * the given volume use setContent method. + * @param volumeType the type of volume for which the content should be returned. + * @return list of resource URIs (never null) + * @throws IllegalArgumentException if the library does not support given type of volume + */ + List getURIContent(String volumeType) throws IllegalArgumentException; + + /** + * Sets content of given volume + * @param volumeType the type of volume for which the content should be set + * @param path the list of resource URIs + * @throws IllegalArgumentException if the library does not support given volumeType + */ + void setURIContent(String volumeType, List path) throws IllegalArgumentException; + +} diff --git a/project.libraries/src/org/netbeans/spi/project/libraries/LibraryTypeProvider.java b/project.libraries/src/org/netbeans/spi/project/libraries/LibraryTypeProvider.java --- a/project.libraries/src/org/netbeans/spi/project/libraries/LibraryTypeProvider.java +++ b/project.libraries/src/org/netbeans/spi/project/libraries/LibraryTypeProvider.java @@ -105,8 +105,8 @@ public interface LibraryTypeProvider ext /** * Returns customizer for given volume's type, or null if the volume is not customizable. - * The object of the LibraryImplementation type and optionally LibraryStorageArea type is - * passed to the customizer's setObject method. + * The LibraryCustomizerContext instance 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 * object is of the same type as the object created by {@link #createLibrary}. diff --git a/project.libraries/src/org/netbeans/spi/project/libraries/support/LibrariesSupport.java b/project.libraries/src/org/netbeans/spi/project/libraries/support/LibrariesSupport.java --- a/project.libraries/src/org/netbeans/spi/project/libraries/support/LibrariesSupport.java +++ b/project.libraries/src/org/netbeans/spi/project/libraries/support/LibrariesSupport.java @@ -50,8 +50,8 @@ import org.netbeans.modules.project.libr 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.Exceptions; import org.openide.util.Parameters; /** @@ -96,28 +96,22 @@ public final class LibrariesSupport { } /** - * Properly converts possibly relative file to URL. - * @param f file to convert; can be relative; cannot be null - * @return url - * @since org.netbeans.modules.project.libraries/1 1.17 + * Properly converts possibly relative file path to URI. + * @param path file path to convert; can be relative; cannot be null + * @return uri + * @since org.netbeans.modules.project.libraries/1 1.18 */ - public static URL convertFilePathToURL(String path) { + public static URI convertFilePathToURI(String path) { try { File f = new File(path); if (f.isAbsolute()) { - return f.toURI().toURL(); + return f.toURI(); } else { // create hierarchical relative URI (that is no schema) - // to encode OS characters - URI uri = new URI(null, null, path.replace('\\', '/'), null); - return new URL("file", null, uri.getRawPath()); + return new URI(null, null, path.replace('\\', '/'), null); } } catch (URISyntaxException ex) { - IllegalArgumentException y = new IllegalArgumentException(); - y.initCause(ex); - throw y; - } catch (MalformedURLException ex) { IllegalArgumentException y = new IllegalArgumentException(); y.initCause(ex); throw y; @@ -125,69 +119,49 @@ public final class LibrariesSupport { } /** - * Properly converts possibly relative URL to file. - * @param url file URL to convert; can be relative; cannot be null - * @return url - * @since org.netbeans.modules.project.libraries/1 1.17 + * Properly converts possibly relative URI to file path. + * @param uri URI convert; can be relative URI; cannot be null + * @return file path + * @since org.netbeans.modules.project.libraries/1 1.18 */ - public static String convertURLToFilePath(URL url) { - if (!"file".equals(url.getProtocol())) { - throw new IllegalArgumentException("not file URL "+url); //NOI18N + public static String convertURIToFilePath(URI uri) { + if (uri.isAbsolute()) { + // uri.getPath() starts with extra slath, e.g. "/D:/path/" + return new File(uri).getPath(); + } else { + return uri.getPath().replace('/', File.separatorChar); } - try { - if (isAbsoluteURL(url)) { - return new File(new URI(url.toExternalForm())).getPath(); - } 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(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 - * @since org.netbeans.modules.project.libraries/1 1.17 - */ - 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. + * Helper method to resolve (possibly relative) library content URI to FileObject. * * @param libraryLocation library location file; can be null for global libraries * @param libraryEntry library entry to resolve * @return file object - * @since org.netbeans.modules.project.libraries/1 1.17 + * @since org.netbeans.modules.project.libraries/1 1.18 */ - public static FileObject resolveLibraryEntryFileObject(URL libraryLocation, URL libraryEntry) { - URL u = resolveLibraryEntryURL(libraryLocation, libraryEntry); - return URLMapper.findFileObject(u); + public static FileObject resolveLibraryEntryFileObject(URL libraryLocation, URI libraryEntry) { + URI u = resolveLibraryEntryURI(libraryLocation, libraryEntry); + try { + return URLMapper.findFileObject(u.toURL()); + } catch (MalformedURLException ex) { + Exceptions.printStackTrace(ex); + return null; + } } /** - * Helper method to resolve (possibly relative) library content URL. + * Helper method to resolve (possibly relative) library content URI. * - * @param libraryLocation library location file; can be null for global libraries - * @param libraryEntry library entry to resolve - * @return absolute URL - * @since org.netbeans.modules.project.libraries/1 1.17 + * @param libraryLocation library location file + * @param libraryEntry relative library entry to resolve + * @return absolute URI + * @since org.netbeans.modules.project.libraries/1 1.18 */ - public static URL resolveLibraryEntryURL(URL libraryLocation, URL libraryEntry) { + public static URI resolveLibraryEntryURI(URL libraryLocation, URI libraryEntry) { Parameters.notNull("libraryEntry", libraryEntry); //NOI18N - if (isAbsoluteURL(libraryEntry)) { + if (libraryEntry.isAbsolute()) { return libraryEntry; } else { if (libraryLocation == null) { @@ -201,35 +175,12 @@ public final class LibrariesSupport { throw new IllegalArgumentException("library location must be a file - "+libraryLocation.toExternalForm()); //NOI18N } File libBase = libLocation.getParentFile(); - String jarFolder = null; - if ("jar".equals(libraryEntry.getProtocol())) { - assert libraryEntry.toExternalForm().indexOf("!/") != -1 : libraryEntry.toExternalForm(); //NOI18N - jarFolder = libraryEntry.toExternalForm().substring(libraryEntry.toExternalForm().indexOf("!/")+2); //NOI18N - libraryEntry = FileUtil.getArchiveFile(libraryEntry); - } - String path = convertURLToFilePath(libraryEntry); - File f = FileUtil.normalizeFile(new File(libBase, path)); - URL u; - try { - u = f.toURI().toURL(); - } catch (MalformedURLException ex) { - IllegalArgumentException y = new IllegalArgumentException(); - y.initCause(ex); - throw y; - } - if (jarFolder != null) { - u = FileUtil.getArchiveRoot(u); - try { - u = new URL(u + jarFolder.replace('\\', '/')); //NOI18N - } catch (MalformedURLException e) { - throw new AssertionError(e); - } + URI u = libBase.toURI().resolve(libraryEntry); + if (libraryEntry.getPath().contains("!/")) { // NOI18N + u = URI.create("jar:"+u.toString()); // NOI18N } return u; } } - - // TODO: add method which compares two libraries: compare content and file sizes and ... - // TODO: move some of these methods to openide.FileUtil } diff --git a/project.libraries/test/unit/src/org/netbeans/modules/project/libraries/TestUtil.java b/project.libraries/test/unit/src/org/netbeans/modules/project/libraries/TestUtil.java --- a/project.libraries/test/unit/src/org/netbeans/modules/project/libraries/TestUtil.java +++ b/project.libraries/test/unit/src/org/netbeans/modules/project/libraries/TestUtil.java @@ -19,13 +19,16 @@ package org.netbeans.modules.project.libraries; +import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.IOException; import java.net.MalformedURLException; +import java.net.URI; 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; @@ -33,11 +36,12 @@ import java.util.Set; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +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.LibraryImplementation2; 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; /** @@ -126,10 +130,10 @@ public class TestUtil { } - public static final class ALP implements ArealLibraryProvider { + public static final class ALP implements ArealLibraryProvider { final PropertyChangeSupport pcs = new PropertyChangeSupport(this); - public final Map> libs = new HashMap>(); + public final Map> libs = new HashMap>(); final Set lps = new WeakSet(); final Set open = new HashSet(); @@ -145,8 +149,8 @@ public class TestUtil { return Area.class; } - public Class libraryType() { - return LibraryImplementation.class; + public Class libraryType() { + return TestLibraryImplementation.class; } public Area createArea() { @@ -172,7 +176,7 @@ public class TestUtil { pcs.firePropertyChange(PROP_OPEN_AREAS, null, null); } - private class LP implements LibraryProvider { + private class LP implements LibraryProvider { final PropertyChangeSupport pcs = new PropertyChangeSupport(this); final Area area; @@ -182,11 +186,11 @@ public class TestUtil { lps.add(this); } - public LibraryImplementation[] getLibraries() { + public TestLibraryImplementation[] getLibraries() { if (libs.containsKey(area)) { - return libs.get(area).toArray(new LibraryImplementation[0]); + return libs.get(area).toArray(new TestLibraryImplementation[0]); } else { - return new LibraryImplementation[0]; + return new TestLibraryImplementation[0]; } } @@ -200,19 +204,19 @@ public class TestUtil { } - public LibraryProvider getLibraries(Area area) { + 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])); + public TestLibraryImplementation createLibrary(String type, String name, Area area, Map> contents) throws IOException { + TestLibraryImplementation lib = new TestLibraryImplementation(type, contents.keySet().toArray(new String[0])); lib.setName(name); - for (Map.Entry> entry : contents.entrySet()) { - lib.setContent(entry.getKey(), entry.getValue()); + for (Map.Entry> entry : contents.entrySet()) { + lib.setURIContent(entry.getKey(), entry.getValue()); } - List l = libs.get(area); + List l = libs.get(area); if (l == null) { - l = new ArrayList(); + l = new ArrayList(); libs.put(area, l); } l.add(lib); @@ -224,8 +228,8 @@ public class TestUtil { return lib; } - public void remove(LibraryImplementation library) throws IOException { - for (Map.Entry> entry : libs.entrySet()) { + public void remove(TestLibraryImplementation library) throws IOException { + for (Map.Entry> entry : libs.entrySet()) { if (entry.getValue().remove(library)) { for (LP lp : lps) { if (lp.area.equals(entry.getKey())) { @@ -242,4 +246,114 @@ public class TestUtil { return new URL("jar:http://nowhere.net/" + name + "!/"); } + private static class TestLibraryImplementation implements LibraryImplementation2 { + private String description; + private Map> contents; + private String name; + private String libraryType; + private String localizingBundle; + private List listeners; + + /** + * Create new LibraryImplementation supporting given library. + */ + public TestLibraryImplementation(String libraryType, String[] volumeTypes) { + assert libraryType != null && volumeTypes != null; + this.libraryType = libraryType; + contents = new HashMap>(); + for (String vtype : volumeTypes) { + contents.put(vtype, Collections.emptyList()); + } + } + + + public String getType() { + return libraryType; + } + + public void setName(final String name) throws UnsupportedOperationException { + String oldName = this.name; + this.name = name; + firePropertyChange (PROP_NAME, oldName, this.name); + } + + public String getName() { + return name; + } + + public List getContent(String contentType) throws IllegalArgumentException { + return LibrariesModel.convertURIsToURLs(getURIContent(contentType)); + } + + public List getURIContent(String contentType) throws IllegalArgumentException { + List content = contents.get(contentType); + if (content == null) + throw new IllegalArgumentException (); + return Collections.unmodifiableList (content); + } + + public void setContent(String contentType, List path) throws IllegalArgumentException { + setURIContent(contentType, LibrariesModel.convertURLsToURIs(path)); + } + + public void setURIContent(String contentType, List path) throws IllegalArgumentException { + if (path == null) { + throw new IllegalArgumentException (); + } + if (contents.keySet().contains(contentType)) { + contents.put(contentType, new ArrayList(path)); + firePropertyChange(PROP_CONTENT,null,null); + } else { + throw new IllegalArgumentException ("Volume '"+contentType+ + "' is not support by this library. The only acceptable values are: "+contents.keySet()); + } + } + + public String getDescription () { + return description; + } + + public void setDescription (String text) { + String oldDesc = description; + description = text; + firePropertyChange (PROP_DESCRIPTION, oldDesc, description); + } + + public String getLocalizingBundle() { + return localizingBundle; + } + + public void setLocalizingBundle(String resourceName) { + localizingBundle = resourceName; + } + + public synchronized void addPropertyChangeListener (PropertyChangeListener l) { + if (listeners == null) + listeners = new ArrayList(); + listeners.add (l); + } + + public synchronized void removePropertyChangeListener (PropertyChangeListener l) { + if (listeners == null) + return; + listeners.remove (l); + } + + public @Override String toString() { + return "TestLibraryImplementation[" + name + "]"; // NOI18N + } + + private void firePropertyChange (String propName, Object oldValue, Object newValue) { + List ls; + synchronized (this) { + if (listeners == null) + return; + ls = new ArrayList(listeners); + } + PropertyChangeEvent event = new PropertyChangeEvent (this, propName, oldValue, newValue); + for (PropertyChangeListener l : ls) { + l.propertyChange(event); + } + } + } } diff --git a/project.libraries/test/unit/src/org/netbeans/spi/project/libraries/support/LibrariesSupportTest.java b/project.libraries/test/unit/src/org/netbeans/spi/project/libraries/support/LibrariesSupportTest.java --- a/project.libraries/test/unit/src/org/netbeans/spi/project/libraries/support/LibrariesSupportTest.java +++ b/project.libraries/test/unit/src/org/netbeans/spi/project/libraries/support/LibrariesSupportTest.java @@ -41,7 +41,7 @@ package org.netbeans.spi.project.librari import java.io.File; import java.net.MalformedURLException; -import java.net.URL; +import java.net.URI; import org.netbeans.junit.NbTestCase; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; @@ -62,53 +62,62 @@ public class LibrariesSupportTest extend /** * Test of convertFilePathToURL method, of class LibrariesSupport. */ - public void testConvertFilePathToURL() { - String path = "/aa/bb/c c.ext".replace('/', File.separatorChar); - URL u = LibrariesSupport.convertFilePathToURL(path); - assertEquals("file:/aa/bb/c%20c.ext", u.toExternalForm()); + public void testConvertFilePathToURI() throws Exception { + String path = getWorkDirPath()+"/aa/bb/c c.ext".replace('/', File.separatorChar); + URI u = LibrariesSupport.convertFilePathToURI(path); + assertEquals(FileUtil.normalizeFile(new File(path)).toURI(), u); path = "../zz/re l.ext".replace('/', File.separatorChar); - u = LibrariesSupport.convertFilePathToURL(path); - assertEquals("file:../zz/re%20l.ext", u.toExternalForm()); + u = LibrariesSupport.convertFilePathToURI(path); + assertEquals("../zz/re%20l.ext", u.toString()); } /** * Test of convertURLToFilePath method, of class LibrariesSupport. */ - public void testConvertURLToFilePath() throws MalformedURLException{ - URL u = new URL("file:/aa/bb/c%20c.ext"); - String path = LibrariesSupport.convertURLToFilePath(u); - assertEquals("/aa/bb/c c.ext".replace('/', File.separatorChar), path); - u = new URL("file:../zz/re%20l.ext"); - path = LibrariesSupport.convertURLToFilePath(u); + public void testConvertURIToFilePath() throws Exception{ + URI u = getWorkDir().toURI(); + String path = LibrariesSupport.convertURIToFilePath(u); + assertEquals(getWorkDir().getPath(), path); + u = new URI(null, null, "../zz/re l.ext", null); + path = LibrariesSupport.convertURIToFilePath(u); assertEquals("../zz/re l.ext".replace('/', File.separatorChar), path); - } - - /** - * 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 { + public void testResolveLibraryEntryFileObject() 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")); + new URI(null, null, "bertie.jar", null)); assertEquals(f2.getPath(), FileUtil.toFile(fo).getPath()); fo = LibrariesSupport.resolveLibraryEntryFileObject( null, - f2.toURI().toURL()); + f2.toURI()); assertEquals(f2.getPath(), FileUtil.toFile(fo).getPath()); } + public void testResolveLibraryEntryURI() throws Exception { + File f = new File(this.getWorkDir(), "knihovna.properties"); + File f2 = new File(this.getWorkDir(), "ber tie.jar"); + f.createNewFile(); + f2.createNewFile(); + URI u = LibrariesSupport.resolveLibraryEntryURI( + f.toURI().toURL(), + new URI(null, null, "ber tie.jar", null)); + assertEquals(new File(f.getParentFile(), f2.getName()).toURI(), u); + u = LibrariesSupport.resolveLibraryEntryURI( + f.toURI().toURL(), + f2.toURI()); + assertEquals(f2.toURI(), u); + u = LibrariesSupport.resolveLibraryEntryURI( + f.toURI().toURL(), + new URI(null, null, "ber tie.jar!/main/ja va", null)); + assertEquals(new URI("jar:"+ (new File(f.getParentFile(), f2.getName()).toURI().toString()) + "!/main/ja%20va"), u); + } + }