diff options
11 files changed, 923 insertions, 225 deletions
diff --git a/bundles/org.eclipse.osgi/.settings/.api_filters b/bundles/org.eclipse.osgi/.settings/.api_filters index 4be9af3b0..47cc4b2cc 100644 --- a/bundles/org.eclipse.osgi/.settings/.api_filters +++ b/bundles/org.eclipse.osgi/.settings/.api_filters @@ -900,12 +900,35 @@ </filter> </resource> <resource path="osgi/src/org/osgi/framework/Version.java" type="org.osgi.framework.Version"> + <filter comment="Ignore OSGi API" id="1141899266"> + <message_arguments> + <message_argument value="1.7"/> + <message_argument value="3.8"/> + <message_argument value="Version(int, int, int, String, boolean)"/> + </message_arguments> + </filter> + <filter comment="Ignore OSGi API" id="1141899266"> + <message_arguments> + <message_argument value="1.7"/> + <message_argument value="3.8"/> + <message_argument value="isReleaseVersion()"/> + </message_arguments> + </filter> <filter id="1143996420"> <message_arguments> <message_argument value="compareTo(Version)"/> </message_arguments> </filter> </resource> + <resource path="osgi/src/org/osgi/framework/VersionRange.java" type="org.osgi.framework.VersionRange"> + <filter comment="Ignore OSGi API" id="1108344834"> + <message_arguments> + <message_argument value="1.7"/> + <message_argument value="3.8"/> + <message_argument value="org.osgi.framework.VersionRange"/> + </message_arguments> + </filter> + </resource> <resource path="osgi/src/org/osgi/framework/hooks/bundle/CollisionHook.java" type="org.osgi.framework.hooks.bundle.CollisionHook"> <filter comment="Ignore OSGi API" id="1110441988"> <message_arguments> diff --git a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/PackageAdminImpl.java b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/PackageAdminImpl.java index 3f469ddaa..91d5aea3f 100644 --- a/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/PackageAdminImpl.java +++ b/bundles/org.eclipse.osgi/core/framework/org/eclipse/osgi/framework/internal/core/PackageAdminImpl.java @@ -21,6 +21,7 @@ import org.eclipse.osgi.internal.loader.BundleLoader; import org.eclipse.osgi.internal.loader.BundleLoaderProxy; import org.eclipse.osgi.internal.profile.Profile; import org.eclipse.osgi.service.resolver.*; +import org.eclipse.osgi.service.resolver.VersionRange; import org.eclipse.osgi.util.NLS; import org.osgi.framework.*; import org.osgi.framework.wiring.FrameworkWiring; diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Bundle.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Bundle.java index f1c96fd81..96cd89a60 100644 --- a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Bundle.java +++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Bundle.java @@ -1,6 +1,6 @@ /* * Copyright (c) OSGi Alliance (2000, 2011). All Rights Reserved. - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -30,18 +30,18 @@ import org.osgi.framework.wiring.FrameworkWiring; /** * An installed bundle in the Framework. - * + * * <p> * A {@code Bundle} object is the access point to define the lifecycle of an * installed bundle. Each bundle installed in the OSGi environment must have an * associated {@code Bundle} object. - * + * * <p> * A bundle must have a unique identity, a {@code long}, chosen by the * Framework. This identity must not change during the lifecycle of a bundle, * even when the bundle is updated. Uninstalling and then reinstalling the * bundle must create a new unique identity. - * + * * <p> * A bundle can be in one of six states: * <ul> @@ -56,24 +56,24 @@ import org.osgi.framework.wiring.FrameworkWiring; * Values assigned to these states have no specified ordering; they represent * bit values that may be ORed together to determine if a bundle is in one of * the valid states. - * + * * <p> * A bundle should only have active threads of execution when its state is one * of {@code STARTING},{@code ACTIVE}, or {@code STOPPING}. An {@code * UNINSTALLED} bundle can not be set to another state; it is a zombie and can * only be reached because references are kept somewhere. - * + * * <p> * The Framework is the only entity that is allowed to create {@code Bundle} * objects, and these objects are only valid within the Framework that created * them. - * + * * <p> * Bundles have a natural ordering such that if two {@code Bundle}s have the * same {@link #getBundleId() bundle id} they are equal. A {@code Bundle} is * less than another {@code Bundle} if it has a lower {@link #getBundleId() * bundle id} and is greater if it has a higher bundle id. - * + * * @ThreadSafe * @noimplement * @version $Id$ @@ -81,7 +81,7 @@ import org.osgi.framework.wiring.FrameworkWiring; public interface Bundle extends Comparable<Bundle> { /** * The bundle is uninstalled and may not be used. - * + * * <p> * The {@code UNINSTALLED} state is only visible after a bundle is * uninstalled; the bundle is in an unusable state but references to the @@ -93,7 +93,7 @@ public interface Bundle extends Comparable<Bundle> { /** * The bundle is installed but not yet resolved. - * + * * <p> * A bundle is in the {@code INSTALLED} state when it has been installed in * the Framework but is not or cannot be resolved. @@ -108,7 +108,7 @@ public interface Bundle extends Comparable<Bundle> { /** * The bundle is resolved and is able to be started. - * + * * <p> * A bundle is in the {@code RESOLVED} state when the Framework has * successfully resolved the bundle's code dependencies. These dependencies @@ -135,7 +135,7 @@ public interface Bundle extends Comparable<Bundle> { /** * The bundle is in the process of starting. - * + * * <p> * A bundle is in the {@code STARTING} state when its {@link #start(int) * start} method is active. A bundle must be in this state when the bundle's @@ -154,7 +154,7 @@ public interface Bundle extends Comparable<Bundle> { /** * The bundle is in the process of stopping. - * + * * <p> * A bundle is in the {@code STOPPING} state when its {@link #stop(int) * stop} method is active. A bundle must be in this state when the bundle's @@ -168,7 +168,7 @@ public interface Bundle extends Comparable<Bundle> { /** * The bundle is now running. - * + * * <p> * A bundle is in the {@code ACTIVE} state when it has been successfully * started and activated. @@ -180,13 +180,13 @@ public interface Bundle extends Comparable<Bundle> { /** * The bundle start operation is transient and the persistent autostart * setting of the bundle is not modified. - * + * * <p> * This bit may be set when calling {@link #start(int)} to notify the * framework that the autostart setting of the bundle must not be modified. * If this bit is not set, then the autostart setting of the bundle is * modified. - * + * * @since 1.4 * @see #start(int) */ @@ -196,12 +196,12 @@ public interface Bundle extends Comparable<Bundle> { * The bundle start operation must activate the bundle according to the * bundle's declared {@link Constants#BUNDLE_ACTIVATIONPOLICY activation * policy}. - * + * * <p> * This bit may be set when calling {@link #start(int)} to notify the * framework that the bundle must be activated using the bundle's declared * activation policy. - * + * * @since 1.4 * @see Constants#BUNDLE_ACTIVATIONPOLICY * @see #start(int) @@ -211,13 +211,13 @@ public interface Bundle extends Comparable<Bundle> { /** * The bundle stop is transient and the persistent autostart setting of the * bundle is not modified. - * + * * <p> * This bit may be set when calling {@link #stop(int)} to notify the * framework that the autostart setting of the bundle must not be modified. * If this bit is not set, then the autostart setting of the bundle is * modified. - * + * * @since 1.4 * @see #stop(int) */ @@ -225,7 +225,7 @@ public interface Bundle extends Comparable<Bundle> { /** * Request that all certificates used to sign the bundle be returned. - * + * * @since 1.5 * @see #getSignerCertificates(int) */ @@ -234,7 +234,7 @@ public interface Bundle extends Comparable<Bundle> { /** * Request that only certificates used to sign the bundle that are trusted * by the framework be returned. - * + * * @since 1.5 * @see #getSignerCertificates(int) */ @@ -242,10 +242,10 @@ public interface Bundle extends Comparable<Bundle> { /** * Returns this bundle's current state. - * + * * <p> * A bundle can be in only one state at any time. - * + * * @return An element of {@code UNINSTALLED},{@code INSTALLED}, * {@code RESOLVED}, {@code STARTING}, {@code STOPPING}, * {@code ACTIVE}. @@ -254,7 +254,7 @@ public interface Bundle extends Comparable<Bundle> { /** * Starts this bundle. - * + * * <p> * If this bundle's state is {@code UNINSTALLED} then an * {@code IllegalStateException} is thrown. @@ -264,7 +264,7 @@ public interface Bundle extends Comparable<Bundle> { * <li>If the {@link #START_TRANSIENT} option is set, then a * {@code BundleException} is thrown indicating this bundle cannot be * started due to the Framework's current start level. - * + * * <li>Otherwise, the Framework must set this bundle's persistent autostart * setting to <em>Started with declared activation</em> if the * {@link #START_ACTIVATION_POLICY} option is set or @@ -281,21 +281,21 @@ public interface Bundle extends Comparable<Bundle> { * before continuing. If this does not occur in a reasonable time, a * {@code BundleException} is thrown to indicate this bundle was unable to * be started. - * + * * <li>If this bundle's state is {@code ACTIVE} then this method returns * immediately. - * + * * <li>If the {@link #START_TRANSIENT} option is not set then set this * bundle's autostart setting to <em>Started with declared activation</em> * if the {@link #START_ACTIVATION_POLICY} option is set or * <em>Started with eager activation</em> if not set. When the Framework is * restarted and this bundle's autostart setting is not <em>Stopped</em>, * this bundle must be automatically started. - * + * * <li>If this bundle's state is not {@code RESOLVED}, an attempt is made to * resolve this bundle. If the Framework cannot resolve this bundle, a * {@code BundleException} is thrown. - * + * * <li>If the {@link #START_ACTIVATION_POLICY} option is set and this * bundle's declared activation policy is {@link Constants#ACTIVATION_LAZY * lazy} then: @@ -309,9 +309,9 @@ public interface Bundle extends Comparable<Bundle> { * </ul> * <i></i> * <li>This bundle's state is set to {@code STARTING}. - * + * * <li>A bundle event of type {@link BundleEvent#STARTING} is fired. - * + * * <li>The {@link BundleActivator#start(BundleContext)} method of this * bundle's {@code BundleActivator}, if one is specified, is called. If the * {@code BundleActivator} is invalid or throws an exception then: @@ -329,12 +329,12 @@ public interface Bundle extends Comparable<Bundle> { * <li>If this bundle's state is {@code UNINSTALLED}, because this bundle * was uninstalled while the {@code BundleActivator.start} method was * running, a {@code BundleException} is thrown. - * + * * <li>This bundle's state is set to {@code ACTIVE}. - * + * * <li>A bundle event of type {@link BundleEvent#STARTED} is fired. * </ol> - * + * * <b>Preconditions </b> * <ul> * <li>{@code getState()} in { {@code INSTALLED}, {@code RESOLVED} @@ -357,7 +357,7 @@ public interface Bundle extends Comparable<Bundle> { * <li>{@code getState()} not in { {@code STARTING}, {@code ACTIVE} * }. * </ul> - * + * * @param options The options for starting this bundle. See * {@link #START_TRANSIENT} and {@link #START_ACTIVATION_POLICY}. The * Framework must ignore unrecognized options. @@ -379,10 +379,10 @@ public interface Bundle extends Comparable<Bundle> { /** * Starts this bundle with no options. - * + * * <p> * This method performs the same function as calling {@code start(0)}. - * + * * @throws BundleException If this bundle could not be started. * BundleException types thrown by this method include: * {@link BundleException#NATIVECODE_ERROR}, @@ -400,13 +400,13 @@ public interface Bundle extends Comparable<Bundle> { /** * Stops this bundle. - * + * * <p> * The following steps are required to stop a bundle: * <ol> * <li>If this bundle's state is {@code UNINSTALLED} then an * {@code IllegalStateException} is thrown. - * + * * <li>If this bundle is in the process of being activated or deactivated * then this method must wait for activation or deactivation to complete * before continuing. If this does not occur in a reasonable time, a @@ -416,34 +416,34 @@ public interface Bundle extends Comparable<Bundle> { * bundle's persistent autostart setting to to <em>Stopped</em>. When the * Framework is restarted and this bundle's autostart setting is * <em>Stopped</em>, this bundle must not be automatically started. - * + * * <li>If this bundle's state is not {@code STARTING} or {@code ACTIVE} then * this method returns immediately. - * + * * <li>This bundle's state is set to {@code STOPPING}. - * + * * <li>A bundle event of type {@link BundleEvent#STOPPING} is fired. - * + * * <li>If this bundle's state was {@code ACTIVE} prior to setting the state * to {@code STOPPING}, the {@link BundleActivator#stop(BundleContext)} * method of this bundle's {@code BundleActivator}, if one is specified, is * called. If that method throws an exception, this method must continue to * stop this bundle and a {@code BundleException} must be thrown after * completion of the remaining steps. - * + * * <li>Any services registered by this bundle must be unregistered. * <li>Any services used by this bundle must be released. * <li>Any listeners registered by this bundle must be removed. - * + * * <li>If this bundle's state is {@code UNINSTALLED}, because this bundle * was uninstalled while the {@code BundleActivator.stop} method was * running, a {@code BundleException} must be thrown. - * + * * <li>This bundle's state is set to {@code RESOLVED}. - * + * * <li>A bundle event of type {@link BundleEvent#STOPPED} is fired. * </ol> - * + * * <b>Preconditions </b> * <ul> * <li>{@code getState()} in { {@code ACTIVE} }. @@ -462,7 +462,7 @@ public interface Bundle extends Comparable<Bundle> { * <li>Bundle autostart setting is modified unless the * {@link #STOP_TRANSIENT} option was set. * </ul> - * + * * @param options The options for stopping this bundle. See * {@link #STOP_TRANSIENT}. The Framework must ignore unrecognized * options. @@ -480,10 +480,10 @@ public interface Bundle extends Comparable<Bundle> { /** * Stops this bundle with no options. - * + * * <p> * This method performs the same function as calling {@code stop(0)}. - * + * * @throws BundleException BundleException types thrown by this method * include: {@link BundleException#STATECHANGE_ERROR} and * {@link BundleException#ACTIVATOR_ERROR}. @@ -498,53 +498,53 @@ public interface Bundle extends Comparable<Bundle> { /** * Updates this bundle from an {@code InputStream}. - * + * * <p> * If the specified {@code InputStream} is {@code null}, the Framework must * create the {@code InputStream} from which to read the updated bundle by * interpreting, in an implementation dependent manner, this bundle's * {@link Constants#BUNDLE_UPDATELOCATION Bundle-UpdateLocation} Manifest * header, if present, or this bundle's original location. - * + * * <p> * If this bundle's state is {@code ACTIVE}, it must be stopped before the * update and started after the update successfully completes. - * + * * <p> * If this bundle has exported any packages that are imported by another * bundle, these packages must remain exported until the * {@link FrameworkWiring#refreshBundles(java.util.Collection, FrameworkListener...) * FrameworkWiring.refreshBundles} method has been has been called or the * Framework is relaunched. - * + * * <p> * The following steps are required to update a bundle: * <ol> * <li>If this bundle's state is {@code UNINSTALLED} then an * {@code IllegalStateException} is thrown. - * + * * <li>If this bundle's state is {@code ACTIVE}, {@code STARTING} or * {@code STOPPING}, this bundle is stopped as described in the * {@code Bundle.stop} method. If {@code Bundle.stop} throws an exception, * the exception is rethrown terminating the update. - * + * * <li>The updated version of this bundle is read from the input stream and * installed. If the Framework is unable to install the updated version of * this bundle, the original version of this bundle must be restored and a * {@code BundleException} must be thrown after completion of the remaining * steps. - * + * * <li>This bundle's state is set to {@code INSTALLED}. - * + * * <li>If the updated version of this bundle was successfully installed, a * bundle event of type {@link BundleEvent#UPDATED} is fired. - * + * * <li>If this bundle's state was originally {@code ACTIVE}, the updated * bundle is started as described in the {@code Bundle.start} method. If * {@code Bundle.start} throws an exception, a Framework event of type * {@link FrameworkEvent#ERROR} is fired containing the exception. * </ol> - * + * * <b>Preconditions </b> * <ul> * <li>{@code getState()} not in { {@code UNINSTALLED} }. @@ -561,7 +561,7 @@ public interface Bundle extends Comparable<Bundle> { * {@code ACTIVE} }. * <li>Original bundle is still used; no update occurred. * </ul> - * + * * @param input The {@code InputStream} from which to read the new bundle or * {@code null} to indicate the Framework must create the input * stream from this bundle's {@link Constants#BUNDLE_UPDATELOCATION @@ -590,11 +590,11 @@ public interface Bundle extends Comparable<Bundle> { /** * Updates this bundle. - * + * * <p> * This method performs the same function as calling * {@link #update(InputStream)} with a {@code null} InputStream. - * + * * @throws BundleException If this bundle could not be updated. * BundleException types thrown by this method include: * {@link BundleException#READ_ERROR}, @@ -616,40 +616,40 @@ public interface Bundle extends Comparable<Bundle> { /** * Uninstalls this bundle. - * + * * <p> * This method causes the Framework to notify other bundles that this bundle * is being uninstalled, and then puts this bundle into the * {@code UNINSTALLED} state. The Framework must remove any resources * related to this bundle that it is able to remove. - * + * * <p> * If this bundle has exported any packages, the Framework must continue to * make these packages available to their importing bundles until the * {@link FrameworkWiring#refreshBundles(java.util.Collection, FrameworkListener...) * FrameworkWiring.refreshBundles} method has been called or the Framework * is relaunched. - * + * * <p> * The following steps are required to uninstall a bundle: * <ol> * <li>If this bundle's state is {@code UNINSTALLED} then an * {@code IllegalStateException} is thrown. - * + * * <li>If this bundle's state is {@code ACTIVE}, {@code STARTING} or * {@code STOPPING}, this bundle is stopped as described in the * {@code Bundle.stop} method. If {@code Bundle.stop} throws an exception, a * Framework event of type {@link FrameworkEvent#ERROR} is fired containing * the exception. - * + * * <li>This bundle's state is set to {@code UNINSTALLED}. - * + * * <li>A bundle event of type {@link BundleEvent#UNINSTALLED} is fired. - * + * * <li>This bundle and any persistent storage area provided for this bundle * by the Framework are removed. * </ol> - * + * * <b>Preconditions </b> * <ul> * <li>{@code getState()} not in { {@code UNINSTALLED} }. @@ -664,7 +664,7 @@ public interface Bundle extends Comparable<Bundle> { * <li>{@code getState()} not in { {@code UNINSTALLED} }. * <li>This Bundle has not been uninstalled. * </ul> - * + * * @throws BundleException If the uninstall failed. This can occur if * another thread is attempting to change this bundle's state and * does not complete in a timely manner. BundleException types @@ -683,21 +683,21 @@ public interface Bundle extends Comparable<Bundle> { * Returns this bundle's Manifest headers and values. This method returns * all the Manifest headers and values from the main section of this * bundle's Manifest file; that is, all lines prior to the first blank line. - * + * * <p> * Manifest header names are case-insensitive. The methods of the returned * {@code Dictionary} object must operate on header names in a * case-insensitive manner. - * + * * If a Manifest header value starts with "%", it must be * localized according to the default locale. If no localization is found * for a header value, the header value without the leading "%" is * returned. - * + * * <p> * For example, the following Manifest headers and values are included if * they are present in the Manifest file: - * + * * <pre> * Bundle-Name * Bundle-Vendor @@ -706,11 +706,11 @@ public interface Bundle extends Comparable<Bundle> { * Bundle-DocURL * Bundle-ContactAddress * </pre> - * + * * <p> * This method must continue to return Manifest header information while * this bundle is in the {@code UNINSTALLED} state. - * + * * @return An unmodifiable {@code Dictionary} object containing this * bundle's Manifest headers and values. * @throws SecurityException If the caller does not have the appropriate @@ -724,7 +724,7 @@ public interface Bundle extends Comparable<Bundle> { * Returns this bundle's unique identifier. This bundle is assigned a unique * identifier by the Framework when it was installed in the OSGi * environment. - * + * * <p> * A bundle's unique identifier has the following attributes: * <ul> @@ -735,28 +735,28 @@ public interface Bundle extends Comparable<Bundle> { * <li>Does not change while a bundle remains installed. * <li>Does not change when a bundle is updated. * </ul> - * + * * <p> * This method must continue to return this bundle's unique identifier while * this bundle is in the {@code UNINSTALLED} state. - * + * * @return The unique identifier of this bundle. */ long getBundleId(); /** * Returns this bundle's location identifier. - * + * * <p> * The location identifier is the location passed to {@code * BundleContext.installBundle} when a bundle is installed. The location * identifier does not change while this bundle remains installed, even if * this bundle is updated. - * + * * <p> * This method must continue to return this bundle's location identifier * while this bundle is in the {@code UNINSTALLED} state. - * + * * @return The string representation of this bundle's location identifier. * @throws SecurityException If the caller does not have the appropriate * {@code AdminPermission[this,METADATA]}, and the Java Runtime @@ -767,18 +767,18 @@ public interface Bundle extends Comparable<Bundle> { /** * Returns this bundle's {@code ServiceReference} list for all services it * has registered or {@code null} if this bundle has no registered services. - * + * * <p> * If the Java runtime supports permissions, a {@code ServiceReference} * object to a service is included in the returned list only if the caller * has the {@code ServicePermission} to get the service using at least one * of the named classes the service was registered under. - * + * * <p> * The list is valid at the time of the call to this method, however, as the * Framework is a very dynamic environment, services can be modified or * unregistered at anytime. - * + * * @return An array of {@code ServiceReference} objects or {@code null}. * @throws IllegalStateException If this bundle has been uninstalled. * @see ServiceRegistration @@ -792,7 +792,7 @@ public interface Bundle extends Comparable<Bundle> { * is using or returns {@code null} if this bundle is not using any * services. A bundle is considered to be using a service if its use count * for that service is greater than zero. - * + * * <p> * If the Java Runtime Environment supports permissions, a {@code * ServiceReference} object to a service is included in the returned list @@ -802,7 +802,7 @@ public interface Bundle extends Comparable<Bundle> { * The list is valid at the time of the call to this method, however, as the * Framework is a very dynamic environment, services can be modified or * unregistered at anytime. - * + * * @return An array of {@code ServiceReference} objects or {@code null}. * @throws IllegalStateException If this bundle has been uninstalled. * @see ServiceReference @@ -812,7 +812,7 @@ public interface Bundle extends Comparable<Bundle> { /** * Determines if this bundle has the specified permissions. - * + * * <p> * If the Java Runtime Environment does not support permissions, this method * always returns {@code true}. @@ -821,13 +821,13 @@ public interface Bundle extends Comparable<Bundle> { * {@code java.security.Permission} class directly. This is to allow the * Framework to be implemented in Java environments which do not support * permissions. - * + * * <p> * If the Java Runtime Environment does support permissions, this bundle and * all its resources including embedded JAR files, belong to the same * {@code java.security.ProtectionDomain}; that is, they must share the same * set of permissions. - * + * * @param permission The permission to verify. * @return {@code true} if this bundle has the specified permission or the * permissions possessed by this bundle imply the specified @@ -840,7 +840,7 @@ public interface Bundle extends Comparable<Bundle> { /** * Find the specified resource from this bundle's class loader. - * + * * This bundle's class loader is called to search for the specified * resource. If this bundle's state is {@code INSTALLED}, this method must * attempt to resolve this bundle before attempting to get the specified @@ -852,7 +852,7 @@ public interface Bundle extends Comparable<Bundle> { * Note: Jar and zip files are not required to include directory entries. * URLs to directory entries will not be returned if the bundle contents do * not contain directory entries. - * + * * @param name The name of the resource. See {@code ClassLoader.getResource} * for a description of the format of a resource name. * @return A URL to the named resource, or {@code null} if the resource @@ -870,17 +870,17 @@ public interface Bundle extends Comparable<Bundle> { /** * Returns this bundle's Manifest headers and values localized to the * specified locale. - * + * * <p> * This method performs the same function as {@code Bundle.getHeaders()} * except the manifest header values are localized to the specified locale. - * + * * <p> * If a Manifest header value starts with "%", it must be * localized according to the specified locale. If a locale is specified and * cannot be found, then the header values must be returned using the * default locale. Localizations are searched for in the following order: - * + * * <pre> * bn + "_" + Ls + "_" + Cs + "_" + Vs * bn + "_" + Ls + "_" + Cs @@ -890,24 +890,24 @@ public interface Bundle extends Comparable<Bundle> { * bn + "_" + Ld * bn * </pre> - * + * * Where {@code bn} is this bundle's localization basename, {@code Ls}, * {@code Cs} and {@code Vs} are the specified locale (language, country, * variant) and {@code Ld}, {@code Cd} and {@code Vd} are the default locale * (language, country, variant). - * + * * If {@code null} is specified as the locale string, the header values must * be localized using the default locale. If the empty string ("") * is specified as the locale string, the header values must not be * localized and the raw (unlocalized) header values, including any leading * "%", must be returned. If no localization is found for a header * value, the header value without the leading "%" is returned. - * + * * <p> * This method must continue to return Manifest header information while * this bundle is in the {@code UNINSTALLED} state, however the header * values must only be available in the raw and default locale values. - * + * * @param locale The locale name into which the header values are to be * localized. If the specified locale is {@code null} then the locale * returned by {@code java.util.Locale.getDefault} is used. If the @@ -930,11 +930,11 @@ public interface Bundle extends Comparable<Bundle> { * Bundle-SymbolicName} manifest header. The bundle symbolic name should be * based on the reverse domain name naming convention like that used for * java packages. - * + * * <p> * This method must continue to return this bundle's symbolic name while * this bundle is in the {@code UNINSTALLED} state. - * + * * @return The symbolic name of this bundle or {@code null} if this bundle * does not have a symbolic name. * @since 1.3 @@ -943,25 +943,25 @@ public interface Bundle extends Comparable<Bundle> { /** * Loads the specified class using this bundle's class loader. - * + * * <p> * If this bundle is a fragment bundle then this method must throw a {@code * ClassNotFoundException}. - * + * * <p> * If this bundle's state is {@code INSTALLED}, this method must attempt to * resolve this bundle before attempting to load the class. - * + * * <p> * If this bundle cannot be resolved, a Framework event of type * {@link FrameworkEvent#ERROR} is fired containing a {@code * BundleException} with details of the reason this bundle could not be * resolved. This method must then throw a {@code ClassNotFoundException}. - * + * * <p> * If this bundle's state is {@code UNINSTALLED}, then an {@code * IllegalStateException} is thrown. - * + * * @param name The name of the class to load. * @return The Class object for the requested class. * @throws ClassNotFoundException If no such class can be found or if this @@ -975,7 +975,7 @@ public interface Bundle extends Comparable<Bundle> { /** * Find the specified resources from this bundle's class loader. - * + * * This bundle's class loader is called to search for the specified * resources. If this bundle's state is {@code INSTALLED}, this method must * attempt to resolve this bundle before attempting to get the specified @@ -987,7 +987,7 @@ public interface Bundle extends Comparable<Bundle> { * Note: Jar and zip files are not required to include directory entries. * URLs to directory entries will not be returned if the bundle contents do * not contain directory entries. - * + * * @param name The name of the resource. See {@code * ClassLoader.getResources} for a description of the format of a * resource name. @@ -1019,7 +1019,7 @@ public interface Bundle extends Comparable<Bundle> { * Note: Jar and zip files are not required to include directory entries. * Paths to directory entries will not be returned if the bundle contents do * not contain directory entries. - * + * * @param path The path name for which to return entry paths. * @return An Enumeration of the entry paths ({@code String} objects) or * {@code null} if no entry could be found or if the caller does not @@ -1042,7 +1042,7 @@ public interface Bundle extends Comparable<Bundle> { * Note: Jar and zip files are not required to include directory entries. * URLs to directory entries will not be returned if the bundle contents do * not contain directory entries. - * + * * @param path The path name of the entry. * @return A URL to the entry, or {@code null} if no entry could be found or * if the caller does not have the appropriate {@code @@ -1056,11 +1056,11 @@ public interface Bundle extends Comparable<Bundle> { /** * Returns the time when this bundle was last modified. A bundle is * considered to be modified when it is installed, updated or uninstalled. - * + * * <p> * The time value is the number of milliseconds since January 1, 1970, * 00:00:00 UTC. - * + * * @return The time when this bundle was last modified. * @since 1.3 */ @@ -1123,8 +1123,8 @@ public interface Bundle extends Comparable<Bundle> { * @param recurse If {@code true}, recurse into subdirectories. Otherwise * only return entries from the specified path. * @return An enumeration of URL objects for each matching entry, or - * {@code null} if no matching entry could not be found or if the - * caller does not have the appropriate + * {@code null} if no matching entry could be found or if the caller + * does not have the appropriate * {@code AdminPermission[this,RESOURCE]}, and the Java Runtime * Environment supports permissions. The URLs are sorted such that * entries from this bundle are returned first followed by the @@ -1140,13 +1140,13 @@ public interface Bundle extends Comparable<Bundle> { /** * Returns this bundle's {@link BundleContext}. The returned {@code * BundleContext} can be used by the caller to act on behalf of this bundle. - * + * * <p> * If this bundle is not in the {@link #STARTING}, {@link #ACTIVE}, or * {@link #STOPPING} states or this bundle is a fragment bundle, then this * bundle has no valid {@code BundleContext}. This method will return * {@code null} if this bundle has no valid {@code BundleContext}. - * + * * @return A {@code BundleContext} for this bundle or {@code null} if this * bundle has no valid {@code BundleContext}. * @throws SecurityException If the caller does not have the appropriate @@ -1159,7 +1159,7 @@ public interface Bundle extends Comparable<Bundle> { /** * Return the certificates for the signers of this bundle and the * certificate chains for those signers. - * + * * @param signersType If {@link #SIGNERS_ALL} is specified, then information * on all signers of this bundle is returned. If * {@link #SIGNERS_TRUSTED} is specified, then only information on @@ -1185,11 +1185,11 @@ public interface Bundle extends Comparable<Bundle> { * Returns the version of this bundle as specified by its {@code * Bundle-Version} manifest header. If this bundle does not have a specified * version then {@link Version#emptyVersion} is returned. - * + * * <p> * This method must continue to return this bundle's version while this * bundle is in the {@code UNINSTALLED} state. - * + * * @return The version of this bundle. * @since 1.5 */ @@ -1197,12 +1197,12 @@ public interface Bundle extends Comparable<Bundle> { /** * Adapt this bundle to the specified type. - * + * * <p> * Adapting this bundle to the specified type may require certain checks, * including security checks, to succeed. If a check does not succeed, then * this bundle cannot be adapted and {@code null} is returned. - * + * * @param <A> The type to which this bundle is to be adapted. * @param type Class object for the type to which this bundle is to be * adapted. @@ -1221,18 +1221,18 @@ public interface Bundle extends Comparable<Bundle> { * provided for this bundle by the Framework. This method will return * {@code null} if the platform does not have file system support or this * bundle is a fragment bundle. - * + * * <p> * A {@code File} object for the base directory of the persistent storage * area provided for this bundle by the Framework can be obtained by calling * this method with an empty string as {@code filename}. - * + * * <p> * If the Java Runtime Environment supports permissions, the Framework will * ensure that this bundle has the {@code java.io.FilePermission} with * actions {@code read},{@code write},{@code delete} for all files * (recursively) in the persistent storage area provided for this bundle. - * + * * @param filename A relative name to the file to be accessed. * @return A {@code File} object that represents the requested file or * {@code null} if the platform does not have file system support or diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Version.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Version.java index a71e2e2d7..270c519d3 100644 --- a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Version.java +++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/Version.java @@ -1,6 +1,6 @@ /* - * Copyright (c) OSGi Alliance (2004, 2010). All Rights Reserved. - * + * Copyright (c) OSGi Alliance (2004, 2011). All Rights Reserved. + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -20,21 +20,29 @@ import java.util.NoSuchElementException; import java.util.StringTokenizer; /** - * Version identifier for bundles and packages. - * + * Version identifier for capabilities such as bundles and packages. + * * <p> * Version identifiers have four components. * <ol> * <li>Major version. A non-negative integer.</li> * <li>Minor version. A non-negative integer.</li> * <li>Micro version. A non-negative integer.</li> - * <li>Qualifier. A text string. See {@code Version(String)} for the - * format of the qualifier string.</li> + * <li>Qualifier. A text string. See {@code Version(String)} for the format of + * the qualifier string.</li> * </ol> - * + * + * <p> + * Versions can also be identified as release versions or pre-release versions. + * Given the same numerical components, the qualifiers of all pre-release + * version sort lower than the qualifiers of release versions. In the external + * format, {@code String}, of a version, release versions use {@code "."} to + * separate the numerical components from the qualifier and pre-release versions + * use {@code "-"} to separate the numerical components from the qualifier. + * * <p> * {@code Version} objects are immutable. - * + * * @since 1.3 * @Immutable * @version $Id$ @@ -45,20 +53,28 @@ public class Version implements Comparable<Version> { private final int minor; private final int micro; private final String qualifier; - private static final String SEPARATOR = "."; - private transient String versionString; + private final boolean release; + private transient String versionString /* default to null */; + private transient int hash /* default to 0 */; + + private static final String DOT_SEPARATOR = "."; + private static final String DASH_SEPARATOR = "-"; + private static final String QUALIFIER_SEPARATORS = DOT_SEPARATOR + + DASH_SEPARATOR; /** * The empty version "0.0.0". */ - public static final Version emptyVersion = new Version(0, 0, 0); + public static final Version emptyVersion = new Version(0, 0, 0); /** - * Creates a version identifier from the specified numerical components. - * + * Creates a release version identifier from the specified numerical + * components. + * * <p> - * The qualifier is set to the empty string. - * + * The qualifier is set to the empty string and the version is a release + * version. + * * @param major Major component of the version identifier. * @param minor Minor component of the version identifier. * @param micro Micro component of the version identifier. @@ -66,22 +82,45 @@ public class Version implements Comparable<Version> { * negative. */ public Version(int major, int minor, int micro) { - this(major, minor, micro, null); + this(major, minor, micro, null, true); } /** - * Creates a version identifier from the specified components. - * + * Creates a release version identifier from the specified components. + * + * <p> + * The version is a release version. + * * @param major Major component of the version identifier. * @param minor Minor component of the version identifier. * @param micro Micro component of the version identifier. * @param qualifier Qualifier component of the version identifier. If - * {@code null} is specified, then the qualifier will be set to - * the empty string. + * {@code null} is specified, then the qualifier will be set to the + * empty string. * @throws IllegalArgumentException If the numerical components are negative * or the qualifier string is invalid. */ public Version(int major, int minor, int micro, String qualifier) { + this(major, minor, micro, qualifier, true); + } + + /** + * Creates a version identifier from the specified components. + * + * @param major Major component of the version identifier. + * @param minor Minor component of the version identifier. + * @param micro Micro component of the version identifier. + * @param qualifier Qualifier component of the version identifier. If + * {@code null} is specified, then the qualifier will be set to the + * empty string. + * @param release {@code true} if a release version or {@code false} if a + * pre-release version. + * @throws IllegalArgumentException If the numerical components are negative + * or the qualifier string is invalid. + * @since 1.7 + */ + public Version(int major, int minor, int micro, String qualifier, + boolean release) { if (qualifier == null) { qualifier = ""; } @@ -90,57 +129,77 @@ public class Version implements Comparable<Version> { this.minor = minor; this.micro = micro; this.qualifier = qualifier; - versionString = null; + this.release = release; validate(); } /** - * Created a version identifier from the specified string. - * + * Creates a version identifier from the specified string. + * * <p> - * Here is the grammar for version strings. - * + * Version string grammar: + * * <pre> - * version ::= major('.'minor('.'micro('.'qualifier)?)?)? + * version ::= major('.'minor('.'micro(('.'|'-')qualifier)?)?)? * major ::= digit+ * minor ::= digit+ * micro ::= digit+ - * qualifier ::= (alpha|digit|'_'|'-')+ + * qualifier ::= (alpha|digit|'_'|'-')* * digit ::= [0..9] * alpha ::= [a..zA..Z] * </pre> - * - * There must be no whitespace in version. - * - * @param version String representation of the version identifier. + * + * @param version String representation of the version identifier. There + * must be no whitespace in the argument. * @throws IllegalArgumentException If {@code version} is improperly * formatted. */ public Version(String version) { + this(version, true); + } + + /** + * Creates a version identifier from the specified string and specified + * default for release version. + * + * @param version String representation of the version identifier. There + * must be no whitespace in the argument. + * @param rel {@code true} if the parsed version should default to a release + * version or {@code false} if the parsed version should default to a + * pre-release version when the version has no qualifier. + * @throws IllegalArgumentException If {@code version} is improperly + * formatted. + * @since 1.7 + */ + private Version(String version, boolean rel) { int maj = 0; int min = 0; int mic = 0; String qual = ""; try { - StringTokenizer st = new StringTokenizer(version, SEPARATOR, true); - maj = Integer.parseInt(st.nextToken()); + StringTokenizer st = new StringTokenizer(version, DOT_SEPARATOR, + true); + maj = parseInt(st.nextToken(), version); if (st.hasMoreTokens()) { // minor st.nextToken(); // consume delimiter - min = Integer.parseInt(st.nextToken()); + min = parseInt(st.nextToken(), version); if (st.hasMoreTokens()) { // micro st.nextToken(); // consume delimiter - mic = Integer.parseInt(st.nextToken()); - - if (st.hasMoreTokens()) { // qualifier - st.nextToken(); // consume delimiter - qual = st.nextToken(""); // remaining string - - if (st.hasMoreTokens()) { // fail safe - throw new IllegalArgumentException( - "invalid format: " + version); + mic = parseInt(st.nextToken(QUALIFIER_SEPARATORS), version); + + if (st.hasMoreTokens()) { // qualifier separator + rel = DOT_SEPARATOR.equals(st.nextToken()); + if (st.hasMoreTokens()) { // qualifier + qual = st.nextToken(""); // remaining string + + if (st.hasMoreTokens()) { // fail safe + throw new IllegalArgumentException( + "invalid version \"" + version + + "\": invalid format"); + } } } } @@ -148,7 +207,7 @@ public class Version implements Comparable<Version> { } catch (NoSuchElementException e) { IllegalArgumentException iae = new IllegalArgumentException( - "invalid format: " + version); + "invalid version \"" + version + "\": invalid format"); iae.initCause(e); throw iae; } @@ -157,29 +216,50 @@ public class Version implements Comparable<Version> { minor = min; micro = mic; qualifier = qual; - versionString = null; + release = rel; validate(); } /** + * Parse numeric component into an int. + * + * @param value Numeric component + * @param version Complete version string for exception message, if any + * @return int value of numeric component + */ + private static int parseInt(String value, String version) { + try { + return Integer.parseInt(value); + } + catch (NumberFormatException e) { + IllegalArgumentException iae = new IllegalArgumentException( + "invalid version \"" + version + "\": non-numeric \"" + + value + "\""); + iae.initCause(e); + throw iae; + } + } + + /** * Called by the Version constructors to validate the version components. - * + * * @throws IllegalArgumentException If the numerical components are negative * or the qualifier string is invalid. */ private void validate() { if (major < 0) { - throw new IllegalArgumentException("negative major"); + throw new IllegalArgumentException("invalid version \"" + + toString0() + "\": negative number \"" + major + "\""); } if (minor < 0) { - throw new IllegalArgumentException("negative minor"); + throw new IllegalArgumentException("invalid version \"" + + toString0() + "\": negative number \"" + minor + "\""); } if (micro < 0) { - throw new IllegalArgumentException("negative micro"); + throw new IllegalArgumentException("invalid version \"" + + toString0() + "\": negative number \"" + micro + "\""); } - char[] chars = qualifier.toCharArray(); - for (int i = 0, length = chars.length; i < length; i++) { - char ch = chars[i]; + for (char ch : qualifier.toCharArray()) { if (('A' <= ch) && (ch <= 'Z')) { continue; } @@ -192,42 +272,67 @@ public class Version implements Comparable<Version> { if ((ch == '_') || (ch == '-')) { continue; } - throw new IllegalArgumentException("invalid qualifier: " - + qualifier); + throw new IllegalArgumentException("invalid version \"" + + toString0() + "\": invalid qualifier \"" + qualifier + + "\""); } } /** * Parses a version identifier from the specified string. - * + * * <p> * See {@code Version(String)} for the format of the version string. - * + * * @param version String representation of the version identifier. Leading * and trailing whitespace will be ignored. - * @return A {@code Version} object representing the version - * identifier. If {@code version} is {@code null} or - * the empty string then {@code emptyVersion} will be - * returned. + * @return A {@code Version} object representing the version identifier. If + * {@code version} is {@code null} or the empty string then + * {@code emptyVersion} will be returned. * @throws IllegalArgumentException If {@code version} is improperly * formatted. */ public static Version parseVersion(String version) { + return parseVersion(version, true); + } + + /** + * Parses a version identifier from the specified string and specified + * default for release version. + * + * <p> + * This method is used by {@link VersionRange} when parsing versions since + * the default for a release version varies depending upon left or right and + * open or closed endpoint. + * + * @param version String representation of the version identifier. Leading + * and trailing whitespace will be ignored. + * @param rel {@code true} if the parsed version should default to a release + * version or {@code false} if the parsed version should default to a + * pre-release version when the version has no qualifier. + * @return A {@code Version} object representing the version identifier. If + * {@code version} is {@code null} or the empty string then + * {@code emptyVersion} will be returned. + * @throws IllegalArgumentException If {@code version} is improperly + * formatted. + * @since 1.7 + */ + static Version parseVersion(String version, boolean rel) { if (version == null) { - return emptyVersion; + return rel ? emptyVersion : new Version(0, 0, 0, null, false); } version = version.trim(); if (version.length() == 0) { - return emptyVersion; + return rel ? emptyVersion : new Version(0, 0, 0, null, false); } - return new Version(version); + return new Version(version, rel); } /** * Returns the major component of this version identifier. - * + * * @return The major component. */ public int getMajor() { @@ -236,7 +341,7 @@ public class Version implements Comparable<Version> { /** * Returns the minor component of this version identifier. - * + * * @return The minor component. */ public int getMinor() { @@ -245,7 +350,7 @@ public class Version implements Comparable<Version> { /** * Returns the micro component of this version identifier. - * + * * @return The micro component. */ public int getMicro() { @@ -254,7 +359,7 @@ public class Version implements Comparable<Version> { /** * Returns the qualifier component of this version identifier. - * + * * @return The qualifier component. */ public String getQualifier() { @@ -262,55 +367,105 @@ public class Version implements Comparable<Version> { } /** + * Returns {@code true} if the version is a release version and + * {@code false} if the version is a pre-release version. + * + * @return {@code true} if the version is a release version and + * {@code false} if the version is a pre-release version. + * @since 1.7 + */ + public boolean isReleaseVersion() { + return release; + } + + /** * Returns the string representation of this version identifier. - * + * * <p> - * The format of the version string will be {@code major.minor.micro} - * if qualifier is the empty string or - * {@code major.minor.micro.qualifier} otherwise. - * + * The format of the version string will be + * {@code major.minor.micro.qualifier} if it is a + * {@link #isReleaseVersion() release version} or + * {@code major.minor.micro-qualifier} if the version is a pre-release + * version. + * * @return The string representation of this version identifier. */ public String toString() { + return toString0(); + } + + /** + * Internal toString behavior + * + * @return The string representation of this version identifier. + */ + private String toString0() { if (versionString != null) { return versionString; } int q = qualifier.length(); StringBuffer result = new StringBuffer(20 + q); result.append(major); - result.append(SEPARATOR); + result.append(DOT_SEPARATOR); result.append(minor); - result.append(SEPARATOR); + result.append(DOT_SEPARATOR); result.append(micro); - if (q > 0) { - result.append(SEPARATOR); + if (release) { + if (q > 0) { + result.append(DOT_SEPARATOR); + result.append(qualifier); + } + } + else { + result.append(DASH_SEPARATOR); result.append(qualifier); } return versionString = result.toString(); } /** + * Package private method to append the version string to the specified + * string buffer. The version string includes a trailing dot for empty + * release qualifiers. + * + * @param buf The string buffer to receive the version string. + */ + void appendTo(StringBuffer buf) { + buf.append(toString0()); + if (release && (qualifier.length() == 0)) { + buf.append(DOT_SEPARATOR); + } + } + + /** * Returns a hash code value for the object. - * + * * @return An integer which is a hash code value for this object. */ public int hashCode() { - return (major << 24) + (minor << 16) + (micro << 8) - + qualifier.hashCode(); + if (hash != 0) { + return hash; + } + int h = release ? 31 * 17 : 31 * 19; + h = 31 * h + major; + h = 31 * h + minor; + h = 31 * h + micro; + h = 31 * h + qualifier.hashCode(); + return hash = h; } /** * Compares this {@code Version} object to another object. - * + * * <p> * A version is considered to be <b>equal to </b> another version if the - * major, minor and micro components are equal and the qualifier component - * is equal (using {@code String.equals}). - * + * major, minor and micro components are equal, the qualifier component is + * equal (using {@code String.equals}) and both versions are release or + * pre-release. + * * @param object The {@code Version} object to be compared. - * @return {@code true} if {@code object} is a - * {@code Version} and is equal to this object; - * {@code false} otherwise. + * @return {@code true} if {@code object} is a {@code Version} and is equal + * to this object; {@code false} otherwise. */ public boolean equals(Object object) { if (object == this) { // quicktest @@ -323,27 +478,31 @@ public class Version implements Comparable<Version> { Version other = (Version) object; return (major == other.major) && (minor == other.minor) - && (micro == other.micro) && qualifier.equals(other.qualifier); + && (micro == other.micro) && (release == other.release) + && qualifier.equals(other.qualifier); } /** * Compares this {@code Version} object to another {@code Version}. - * + * * <p> - * A version is considered to be <b>less than </b> another version if its + * A version is considered to be <b>less than</b> another version if its * major component is less than the other version's major component, or the * major components are equal and its minor component is less than the other * version's minor component, or the major and minor components are equal * and its micro component is less than the other version's micro component, - * or the major, minor and micro components are equal and it's qualifier - * component is less than the other version's qualifier component (using - * {@code String.compareTo}). - * + * or the major, minor and micro components are equal and it's a pre-release + * version and the other version is a release version, or the major, minor + * and micro components are equal and both versions are release or + * pre-release and it's qualifier component is less than the other version's + * qualifier component (using {@code String.compareTo}). + * * <p> * A version is considered to be <b>equal to</b> another version if the - * major, minor and micro components are equal and the qualifier component - * is equal (using {@code String.compareTo}). - * + * major, minor and micro components are equal, both versions are release or + * pre-release and the qualifier components are equal (using + * {@code String.compareTo}). + * * @param other The {@code Version} object to be compared. * @return A negative integer, zero, or a positive integer if this version * is less than, equal to, or greater than the specified @@ -371,6 +530,11 @@ public class Version implements Comparable<Version> { return result; } + result = (release ? 1 : 0) - (other.release ? 1 : 0); + if (result != 0) { + return result; + } + return qualifier.compareTo(other.qualifier); } } diff --git a/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/VersionRange.java b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/VersionRange.java new file mode 100644 index 000000000..fdd638c67 --- /dev/null +++ b/bundles/org.eclipse.osgi/osgi/src/org/osgi/framework/VersionRange.java @@ -0,0 +1,504 @@ +/* + * Copyright (c) OSGi Alliance (2011). All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.osgi.framework; + +import java.util.NoSuchElementException; +import java.util.StringTokenizer; + +/** + * Version range. A version range is an interval describing a set of + * {@link Version versions}. + * + * <p> + * A range has a left (lower) endpoint and a right (upper) endpoint. Each + * endpoint can be open (excluded from the set) or closed (included in the set). + * + * <p> + * {@code VersionRange} objects are immutable. + * + * @since 1.7 + * @Immutable + * @version $Id$ + */ + +public class VersionRange { + /** + * The left endpoint is open and is excluded from the range. + * <p> + * The value of {@code LEFT_OPEN} is {@code '('}. + */ + public static final char LEFT_OPEN = '('; + /** + * The left endpoint is closed and is included in the range. + * <p> + * The value of {@code LEFT_CLOSED} is {@code '['}. + */ + public static final char LEFT_CLOSED = '['; + /** + * The right endpoint is open and is excluded from the range. + * <p> + * The value of {@code RIGHT_OPEN} is {@code ')'}. + */ + public static final char RIGHT_OPEN = ')'; + /** + * The right endpoint is closed and is included in the range. + * <p> + * The value of {@code RIGHT_CLOSED} is {@code ']'}. + */ + public static final char RIGHT_CLOSED = ']'; + + private final boolean leftClosed; + private final Version left; + private final Version right; + private final boolean rightClosed; + private final boolean empty; + + private transient String versionRangeString /* default to null */; + private transient int hash /* default to 0 */; + + private static final String LEFT_OPEN_DELIMITER = "("; + private static final String LEFT_CLOSED_DELIMITER = "["; + private static final String LEFT_DELIMITERS = LEFT_CLOSED_DELIMITER + + LEFT_OPEN_DELIMITER; + private static final String RIGHT_OPEN_DELIMITER = ")"; + private static final String RIGHT_CLOSED_DELIMITER = "]"; + private static final String RIGHT_DELIMITERS = RIGHT_OPEN_DELIMITER + + RIGHT_CLOSED_DELIMITER; + private static final String ENDPOINT_DELIMITER = ","; + + /** + * Creates a version range from the specified versions. + * + * @param leftType Must be either {@link #LEFT_CLOSED} or {@link #LEFT_OPEN} + * . + * @param leftEndpoint Left endpoint of range. Must not be {@code null}. + * @param rightEndpoint Right endpoint of range. May be {@code null} to + * indicate the right endpoint is <i>Infinity</i>. + * @param rightType Must be either {@link #RIGHT_CLOSED} or + * {@link #RIGHT_OPEN}. + * @throws IllegalArgumentException If the arguments are invalid. + */ + public VersionRange(char leftType, Version leftEndpoint, + Version rightEndpoint, char rightType) { + if ((leftType != LEFT_CLOSED) && (leftType != LEFT_OPEN)) { + throw new IllegalArgumentException("invalid leftType \"" + + leftType + "\""); + } + if ((rightType != RIGHT_OPEN) && (rightType != RIGHT_CLOSED)) { + throw new IllegalArgumentException("invalid rightType \"" + + rightType + "\""); + } + if (leftEndpoint == null) { + throw new IllegalArgumentException("null leftEndpoint argument"); + } + leftClosed = leftType == LEFT_CLOSED; + rightClosed = rightType == RIGHT_CLOSED; + left = leftEndpoint; + right = rightEndpoint; + empty = isEmpty0(); + } + + /** + * Creates a version range from the specified string. + * + * <p> + * Version range string grammar: + * + * <pre> + * range ::= interval | atleast + * interval ::= ( '[' | '(' ) left ',' right ( ']' | ')' ) + * left ::= version + * right ::= version + * atleast ::= version + * </pre> + * + * @param range String representation of the version range. The versions in + * the range must contain no whitespace. Other whitespace in the + * range string is ignored. + * @throws IllegalArgumentException If {@code range} is improperly + * formatted. + */ + public VersionRange(String range) { + boolean closedLeft; + boolean closedRight; + Version endpointLeft; + Version endpointRight; + + try { + StringTokenizer st = new StringTokenizer(range, LEFT_DELIMITERS, + true); + String token = st.nextToken().trim(); // whitespace or left delim + if (token.length() == 0) { // leading whitespace + token = st.nextToken(); // left delim + } + closedLeft = LEFT_CLOSED_DELIMITER.equals(token); + if (!closedLeft && !LEFT_OPEN_DELIMITER.equals(token)) { + // first token is not a delimiter, so it must be "atleast" + if (st.hasMoreTokens()) { // there must be no more tokens + throw new IllegalArgumentException("invalid range \"" + + range + "\": invalid format"); + } + leftClosed = true; + rightClosed = false; + left = parseVersion(token, true, range); + right = null; + empty = false; + return; + } + String version = st.nextToken(ENDPOINT_DELIMITER); + endpointLeft = parseVersion(version, !closedLeft, range); + token = st.nextToken(); // consume comma + version = st.nextToken(RIGHT_DELIMITERS); + token = st.nextToken(); // right delim + closedRight = RIGHT_CLOSED_DELIMITER.equals(token); + if (!closedRight && !RIGHT_OPEN_DELIMITER.equals(token)) { + throw new IllegalArgumentException("invalid range \"" + range + + "\": invalid format"); + } + endpointRight = parseVersion(version, closedRight, range); + + if (st.hasMoreTokens()) { // any more tokens have to be whitespace + token = st.nextToken("").trim(); + if (token.length() != 0) { // trailing whitespace + throw new IllegalArgumentException("invalid range \"" + + range + "\": invalid format"); + } + } + } + catch (NoSuchElementException e) { + IllegalArgumentException iae = new IllegalArgumentException( + "invalid range \"" + range + "\": invalid format"); + iae.initCause(e); + throw iae; + } + + leftClosed = closedLeft; + rightClosed = closedRight; + left = endpointLeft; + right = endpointRight; + empty = isEmpty0(); + } + + /** + * Parse version component into a Version. + * + * @param version version component string + * @param rel version is a release version + * @param range Complete range string for exception message, if any + * @return Version + */ + private static Version parseVersion(String version, boolean rel, + String range) { + try { + return Version.parseVersion(version, rel); + } + catch (IllegalArgumentException e) { + IllegalArgumentException iae = new IllegalArgumentException( + "invalid range \"" + range + "\": " + e.getMessage()); + iae.initCause(e); + throw iae; + } + } + + /** + * Returns the left endpoint of this version range. + * + * @return The left endpoint. + */ + public Version getLeft() { + return left; + } + + /** + * Returns the right endpoint of this version range. + * + * @return The right endpoint. May be {@code null} which indicates the right + * endpoint is <i>Infinity</i>. + */ + public Version getRight() { + return right; + } + + /** + * Returns the type of the left endpoint of this version range. + * + * @return {@link #LEFT_CLOSED} if the left endpoint is closed or + * {@link #LEFT_OPEN} if the left endpoint is open. + */ + public char getLeftType() { + return leftClosed ? LEFT_CLOSED : LEFT_OPEN; + } + + /** + * Returns the type of the right endpoint of this version range. + * + * @return {@link #RIGHT_CLOSED} if the right endpoint is closed or + * {@link #RIGHT_OPEN} if the right endpoint is open. + */ + public char getRightType() { + return rightClosed ? RIGHT_CLOSED : RIGHT_OPEN; + } + + /** + * Returns whether this version range includes the specified version. + * + * @param version The version to test for inclusion in this version range. + * @return {@code true} if the specified version is included in this version + * range; {@code false} otherwise. + */ + public boolean includes(Version version) { + if (empty) { + return false; + } + if (left.compareTo(version) >= (leftClosed ? 1 : 0)) { + return false; + } + if (right == null) { + return true; + } + return right.compareTo(version) >= (rightClosed ? 0 : 1); + } + + /** + * Returns the intersection of this version range with the specified version + * ranges. + * + * @param ranges The version ranges to intersect with this version range. + * @return A version range representing the intersection of this version + * range and the specified version ranges. If no version ranges are + * specified, then this version range is returned. + */ + public VersionRange intersection(VersionRange... ranges) { + if ((ranges == null) || (ranges.length == 0)) { + return this; + } + // prime with data from this version range + boolean closedLeft = leftClosed; + boolean closedRight = rightClosed; + Version endpointLeft = left; + Version endpointRight = right; + + for (VersionRange range : ranges) { + int comparison = endpointLeft.compareTo(range.left); + if (comparison == 0) { + closedLeft = closedLeft && range.leftClosed; + } + else { + if (comparison < 0) { // move endpointLeft to the right + endpointLeft = range.left; + closedLeft = range.leftClosed; + } + } + if (range.right != null) { + if (endpointRight == null) { + endpointRight = range.right; + closedRight = range.rightClosed; + } + else { + comparison = endpointRight.compareTo(range.right); + if (comparison == 0) { + closedRight = closedRight && range.rightClosed; + } + else { + if (comparison > 0) { // move endpointRight to the left + endpointRight = range.right; + closedRight = range.rightClosed; + } + } + } + } + } + + return new VersionRange(closedLeft ? LEFT_CLOSED : LEFT_OPEN, + endpointLeft, endpointRight, closedRight ? RIGHT_CLOSED + : RIGHT_OPEN); + } + + /** + * Returns whether this version range is empty. A version range is empty if + * the set of versions defined by the interval is empty. + * + * @return {@code true} if this version range is empty; {@code false} + * otherwise. + */ + public boolean isEmpty() { + return empty; + } + + /** + * Internal isEmpty behavior. + * + * @return {@code true} if this version range is empty; {@code false} + * otherwise. + */ + private boolean isEmpty0() { + if (right == null) { // infinity + return false; + } + int comparison = left.compareTo(right); + if (comparison == 0) { // endpoints equal + return !leftClosed || !rightClosed; + } + return comparison > 0; // true if left > right + } + + /** + * Returns the string representation of this version range. + * + * <p> + * The format of the version range string will be a version string if the + * right end point is <i>Infinity</i> ({@code null}) or an interval string. + * + * @return The string representation of this version range. + */ + public String toString() { + if (versionRangeString != null) { + return versionRangeString; + } + String leftVersion = left.toString(); + if (right == null) { + return versionRangeString = leftVersion; + } + String rightVerion = right.toString(); + StringBuffer result = new StringBuffer(leftVersion.length() + + rightVerion.length() + 5); + result.append(leftClosed ? LEFT_CLOSED : LEFT_OPEN); + left.appendTo(result); + result.append(ENDPOINT_DELIMITER); + right.appendTo(result); + result.append(rightClosed ? RIGHT_CLOSED : RIGHT_OPEN); + return versionRangeString = result.toString(); + } + + /** + * Returns a hash code value for the object. + * + * @return An integer which is a hash code value for this object. + */ + public int hashCode() { + if (hash != 0) { + return hash; + } + if (empty) { + return hash = 31; + } + int h = 31 + (leftClosed ? 7 : 5); + h = 31 * h + left.hashCode(); + if (right != null) { + h = 31 * h + right.hashCode(); + h = 31 * h + (rightClosed ? 7 : 5); + } + return hash = h; + } + + /** + * Compares this {@code VersionRange} object to another object. + * + * <p> + * A version range is considered to be <b>equal to </b> another version + * range if both the endpoints and their types are equal or if both version + * ranges are {@link #isEmpty() empty}. + * + * @param object The {@code VersionRange} object to be compared. + * @return {@code true} if {@code object} is a {@code VersionRange} and is + * equal to this object; {@code false} otherwise. + */ + public boolean equals(Object object) { + if (object == this) { // quicktest + return true; + } + if (!(object instanceof VersionRange)) { + return false; + } + VersionRange other = (VersionRange) object; + if (empty && other.empty) { + return true; + } + if (right == null) { + return (leftClosed == other.leftClosed) && (other.right == null) + && left.equals(other.left); + } + return (leftClosed == other.leftClosed) + && (rightClosed == other.rightClosed) + && left.equals(other.left) && right.equals(other.right); + } + + /** + * Returns the filter string for this version range using the specified + * attribute name. + * + * @param attributeName The attribute name to use in the returned filter + * string. + * @return A filter string for this version range using the specified + * attribute name. + * @throws IllegalArgumentException If the specified attribute name is not a + * valid attribute name. + * + * @see "Core Specification, Filters, for a description of the filter string syntax." + */ + public String toFilterString(String attributeName) { + if (attributeName.length() == 0) { + throw new IllegalArgumentException("invalid attributeName \"" + + attributeName + "\""); + } + for (char ch : attributeName.toCharArray()) { + if ((ch == '=') || (ch == '>') || (ch == '<') || (ch == '~') + || (ch == '(') || (ch == ')')) { + throw new IllegalArgumentException("invalid attributeName \"" + + attributeName + "\""); + } + } + + StringBuffer result = new StringBuffer(128); + if (right != null) { + result.append("(&"); + } + if (leftClosed) { + result.append('('); + result.append(attributeName); + result.append(">="); + left.appendTo(result); + result.append(')'); + } + else { + result.append("(!("); + result.append(attributeName); + result.append("<="); + left.appendTo(result); + result.append("))"); + } + if (right != null) { + if (rightClosed) { + result.append('('); + result.append(attributeName); + result.append("<="); + right.appendTo(result); + result.append(')'); + } + else { + result.append("(!("); + result.append(attributeName); + result.append(">="); + right.appendTo(result); + result.append("))"); + } + result.append(')'); + } + + return result.toString(); + } +} diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/NativeCodeDescriptionImpl.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/NativeCodeDescriptionImpl.java index 8469f8e6f..f1f54b17e 100644 --- a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/NativeCodeDescriptionImpl.java +++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/NativeCodeDescriptionImpl.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2010 IBM Corporation and others. + * Copyright (c) 2007, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -15,6 +15,7 @@ import java.util.*; import org.eclipse.osgi.framework.internal.core.Constants; import org.eclipse.osgi.framework.internal.core.FilterImpl; import org.eclipse.osgi.service.resolver.*; +import org.eclipse.osgi.service.resolver.VersionRange; import org.osgi.framework.*; public class NativeCodeDescriptionImpl extends BaseDescriptionImpl implements NativeCodeDescription { diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateBuilder.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateBuilder.java index 3a1dce124..a2b6e5a71 100644 --- a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateBuilder.java +++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateBuilder.java @@ -16,6 +16,7 @@ import java.util.*; import org.eclipse.osgi.framework.internal.core.*; import org.eclipse.osgi.framework.internal.core.Constants; import org.eclipse.osgi.service.resolver.*; +import org.eclipse.osgi.service.resolver.VersionRange; import org.eclipse.osgi.util.ManifestElement; import org.eclipse.osgi.util.NLS; import org.osgi.framework.*; diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateObjectFactoryImpl.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateObjectFactoryImpl.java index 264ead854..1b684ca67 100644 --- a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateObjectFactoryImpl.java +++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateObjectFactoryImpl.java @@ -15,6 +15,7 @@ import java.util.*; import org.eclipse.osgi.framework.internal.core.Constants; import org.eclipse.osgi.internal.module.ResolverImpl; import org.eclipse.osgi.service.resolver.*; +import org.eclipse.osgi.service.resolver.VersionRange; import org.eclipse.osgi.storagemanager.StorageManager; import org.eclipse.osgi.util.ManifestElement; import org.osgi.framework.*; diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateReader.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateReader.java index a300dba35..445ab78bf 100644 --- a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateReader.java +++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateReader.java @@ -19,6 +19,7 @@ import java.util.Map.Entry; import org.eclipse.osgi.framework.util.ObjectPool; import org.eclipse.osgi.framework.util.SecureAction; import org.eclipse.osgi.service.resolver.*; +import org.eclipse.osgi.service.resolver.VersionRange; import org.osgi.framework.*; /** diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateWriter.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateWriter.java index e1ec84ff1..338043f23 100644 --- a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateWriter.java +++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/StateWriter.java @@ -14,6 +14,7 @@ package org.eclipse.osgi.internal.resolver; import java.io.*; import java.util.*; import org.eclipse.osgi.service.resolver.*; +import org.eclipse.osgi.service.resolver.VersionRange; import org.osgi.framework.*; /** diff --git a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/VersionConstraintImpl.java b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/VersionConstraintImpl.java index 45359bf6e..34f7698b6 100644 --- a/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/VersionConstraintImpl.java +++ b/bundles/org.eclipse.osgi/resolver/src/org/eclipse/osgi/internal/resolver/VersionConstraintImpl.java @@ -17,6 +17,7 @@ import java.util.Map; import org.eclipse.osgi.framework.internal.core.Constants; import org.eclipse.osgi.internal.resolver.BaseDescriptionImpl.BaseCapability; import org.eclipse.osgi.service.resolver.*; +import org.eclipse.osgi.service.resolver.VersionRange; import org.eclipse.osgi.service.resolver.extras.SpecificationReference; import org.eclipse.osgi.util.ManifestElement; import org.osgi.framework.*; |