You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ri...@apache.org on 2009/07/24 19:06:44 UTC

svn commit: r797561 [7/9] - in /felix/trunk: org.osgi.compendium/ org.osgi.compendium/src/main/java/info/dmtree/ org.osgi.compendium/src/main/java/info/dmtree/notification/ org.osgi.compendium/src/main/java/info/dmtree/notification/spi/ org.osgi.compen...

Modified: felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/measurement/State.java
URL: http://svn.apache.org/viewvc/felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/measurement/State.java?rev=797561&r1=797560&r2=797561&view=diff
==============================================================================
--- felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/measurement/State.java (original)
+++ felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/measurement/State.java Fri Jul 24 17:06:37 2009
@@ -1,7 +1,5 @@
 /*
- * $Header: /cvshome/build/org.osgi.util.measurement/src/org/osgi/util/measurement/State.java,v 1.8 2006/06/16 16:31:34 hargrave Exp $
- *
- * Copyright (c) OSGi Alliance (2002, 2006). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2002, 2008). 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.
@@ -27,12 +25,13 @@
  * <p>
  * A <code>State</code> object is immutable so that it may be easily shared.
  * 
- * @version $Revision: 1.8 $
+ * @Immutable
+ * @version $Revision: 5715 $
  */
 public class State {
-	final int		value;
-	final long		time;
-	final String	name;
+	private final int		value;
+	private final long		time;
+	private final String	name;
 
 	/**
 	 * Create a new <code>State</code> object.
@@ -108,9 +107,9 @@
 	 * @return A hash code value for this object.
 	 */
 	public int hashCode() {
-		int hash = value;
+		int hash = 31 * 17 + value;
 		if (name != null) {
-			hash ^= name.hashCode();
+			hash = 31 * hash + name.hashCode();
 		}
 		return hash;
 	}

Modified: felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/measurement/Unit.java
URL: http://svn.apache.org/viewvc/felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/measurement/Unit.java?rev=797561&r1=797560&r2=797561&view=diff
==============================================================================
--- felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/measurement/Unit.java (original)
+++ felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/measurement/Unit.java Fri Jul 24 17:06:37 2009
@@ -1,7 +1,5 @@
 /*
- * $Header: /cvshome/build/org.osgi.util.measurement/src/org/osgi/util/measurement/Unit.java,v 1.15 2006/06/16 16:31:34 hargrave Exp $
- *
- * Copyright (c) OSGi Alliance (2002, 2006). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2002, 2008). 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.
@@ -30,7 +28,8 @@
  * +63. Any operation which produces an exponent outside of this range will
  * result in a <code>Unit</code> object with undefined exponents.
  * 
- * @version $Revision: 1.15 $
+ * @Immutable
+ * @version $Revision: 5715 $
  */
 /*
  * This local class maintains the information about units. It can calculate new
@@ -279,9 +278,11 @@
 	private final static Unit[]	allUnits	= new Unit[] {m, s, kg, K, A, mol,
 			cd, rad, m_s, m_s2, m2, m3, Hz, N, Pa, J, W, C, V, F, Ohm, S, Wb,
 			T, lx, Gy, kat, unity			};
+	
+	/* @GuardedBy("this") */
 	private static Hashtable	base;
-	private String				name;
-	private long				type;
+	private final String		name;
+	private final long			type;
 
 	/**
 	 * Creates a new <code>Unit</code> instance.
@@ -290,6 +291,9 @@
 	 * @param type the type of the <code>Unit</code>
 	 */
 	private Unit(String name, long type) {
+		if (name == null) {
+			name = computeName(type);
+		}
 		this.name = name;
 		this.type = type;
 		//System.out.println( name + " " + Long.toHexString( type ) );
@@ -338,7 +342,7 @@
 	 * @return This object's hash code.
 	 */
 	public int hashCode() {
-		return (int) ((type >>> 32) ^ type);
+		return 31 * 17 + (int) (type ^ (type >>> 32));
 	}
 
 	/**
@@ -435,16 +439,12 @@
 	 * 
 	 * @return the <code>Unit</code>
 	 */
-	static Unit find(long type) {
+	static synchronized Unit find(long type) {
 		if (base == null) {
-			synchronized (Unit.class) {
-				if (base == null) {
-					int size = allUnits.length;
-					base = new Hashtable(size << 1);
-					for (int i = 0; i < size; i++) {
-						base.put(allUnits[i], allUnits[i]);
-					}
-				}
+			int size = allUnits.length;
+			base = new Hashtable(size << 1);
+			for (int i = 0; i < size; i++) {
+				base.put(allUnits[i], allUnits[i]);
 			}
 		}
 		Unit unit = new Unit(null, type);
@@ -462,43 +462,39 @@
 	 * @return A <code>String</code> object representing the <code>Unit</code>
 	 */
 	public String toString() {
-		if (name == null) {
-			int m = (int) (((type >> m_SHIFT) & MASK) - ZERO);
-			int s = (int) (((type >> s_SHIFT) & MASK) - ZERO);
-			int kg = (int) (((type >> kg_SHIFT) & MASK) - ZERO);
-			int K = (int) (((type >> K_SHIFT) & MASK) - ZERO);
-			int A = (int) (((type >> A_SHIFT) & MASK) - ZERO);
-			int mol = (int) (((type >> mol_SHIFT) & MASK) - ZERO);
-			int cd = (int) (((type >> cd_SHIFT) & MASK) - ZERO);
-			int rad = (int) (((type >> rad_SHIFT) & MASK) - ZERO);
-			StringBuffer numerator = new StringBuffer();
-			StringBuffer denominator = new StringBuffer();
-			addSIname(m, "m", numerator, denominator);
-			addSIname(s, "s", numerator, denominator);
-			addSIname(kg, "kg", numerator, denominator);
-			addSIname(K, "K", numerator, denominator);
-			addSIname(A, "A", numerator, denominator);
-			addSIname(mol, "mol", numerator, denominator);
-			addSIname(cd, "cd", numerator, denominator);
-			addSIname(rad, "rad", numerator, denominator);
-			if (denominator.length() > 0) {
-				if (numerator.length() == 0) {
-					numerator.append("1");
-				}
-				numerator.append("/");
-				numerator.append((Object) denominator); /*
-														 * we use (Object) to
-														 * avoid using new 1.4
-														 * method
-														 * append(StringBuffer)
-														 */
-			}
-			name = numerator.toString();
-		}
 		return name;
 	}
 
-	private void addSIname(int si, String name, StringBuffer numerator,
+	private static String computeName(long type) {
+		int m = (int) (((type >> m_SHIFT) & MASK) - ZERO);
+		int s = (int) (((type >> s_SHIFT) & MASK) - ZERO);
+		int kg = (int) (((type >> kg_SHIFT) & MASK) - ZERO);
+		int K = (int) (((type >> K_SHIFT) & MASK) - ZERO);
+		int A = (int) (((type >> A_SHIFT) & MASK) - ZERO);
+		int mol = (int) (((type >> mol_SHIFT) & MASK) - ZERO);
+		int cd = (int) (((type >> cd_SHIFT) & MASK) - ZERO);
+		int rad = (int) (((type >> rad_SHIFT) & MASK) - ZERO);
+		StringBuffer numerator = new StringBuffer();
+		StringBuffer denominator = new StringBuffer();
+		addSIname(m, "m", numerator, denominator);
+		addSIname(s, "s", numerator, denominator);
+		addSIname(kg, "kg", numerator, denominator);
+		addSIname(K, "K", numerator, denominator);
+		addSIname(A, "A", numerator, denominator);
+		addSIname(mol, "mol", numerator, denominator);
+		addSIname(cd, "cd", numerator, denominator);
+		addSIname(rad, "rad", numerator, denominator);
+		if (denominator.length() > 0) {
+			if (numerator.length() == 0) {
+				numerator.append("1");
+			}
+			numerator.append("/");
+			numerator.append(denominator.toString()); 
+		}
+		return numerator.toString();
+	}
+	
+	private static void addSIname(int si, String name, StringBuffer numerator,
 			StringBuffer denominator) {
 		if (si != 0) {
 			StringBuffer sb = (si > 0) ? numerator : denominator;

Added: felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/measurement/package.html
URL: http://svn.apache.org/viewvc/felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/measurement/package.html?rev=797561&view=auto
==============================================================================
--- felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/measurement/package.html (added)
+++ felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/measurement/package.html Fri Jul 24 17:06:37 2009
@@ -0,0 +1,10 @@
+<!-- $Revision: 6204 $ -->
+<BODY>
+<p>Measurement Package Version 1.0.
+<p>Bundles wishing to use this package must list the package
+in the Import-Package header of the bundle's manifest.
+For example:
+<pre>
+Import-Package: org.osgi.util.measurement; version=&quot;[1.0,2.0)&quot;
+</pre>
+</BODY>

Added: felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/measurement/packageinfo
URL: http://svn.apache.org/viewvc/felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/measurement/packageinfo?rev=797561&view=auto
==============================================================================
--- felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/measurement/packageinfo (added)
+++ felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/measurement/packageinfo Fri Jul 24 17:06:37 2009
@@ -0,0 +1 @@
+version 1.0.1
\ No newline at end of file

Modified: felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/mobile/UserPromptCondition.java
URL: http://svn.apache.org/viewvc/felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/mobile/UserPromptCondition.java?rev=797561&r1=797560&r2=797561&view=diff
==============================================================================
--- felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/mobile/UserPromptCondition.java (original)
+++ felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/mobile/UserPromptCondition.java Fri Jul 24 17:06:37 2009
@@ -1,7 +1,5 @@
 /*
- * $Header: /cvshome/build/org.osgi.util.mobile/src/org/osgi/util/mobile/UserPromptCondition.java,v 1.26 2006/07/10 08:18:30 pnagy Exp $
- *
- * Copyright (c) OSGi Alliance (2004, 2006). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2004, 2008). 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.

Added: felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/mobile/package.html
URL: http://svn.apache.org/viewvc/felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/mobile/package.html?rev=797561&view=auto
==============================================================================
--- felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/mobile/package.html (added)
+++ felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/mobile/package.html Fri Jul 24 17:06:37 2009
@@ -0,0 +1,10 @@
+<!-- $Revision: 6204 $ -->
+<BODY>
+<p>Mobile Conditions Package Version 1.0.
+<p>Bundles wishing to use this package must list the package
+in the Import-Package header of the bundle's manifest.
+For example:
+<pre>
+Import-Package: org.osgi.util.mobile; version=&quot;[1.0,2.0)&quot;
+</pre>
+</BODY>

Added: felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/mobile/packageinfo
URL: http://svn.apache.org/viewvc/felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/mobile/packageinfo?rev=797561&view=auto
==============================================================================
--- felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/mobile/packageinfo (added)
+++ felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/mobile/packageinfo Fri Jul 24 17:06:37 2009
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file

Modified: felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/position/Position.java
URL: http://svn.apache.org/viewvc/felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/position/Position.java?rev=797561&r1=797560&r2=797561&view=diff
==============================================================================
--- felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/position/Position.java (original)
+++ felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/position/Position.java Fri Jul 24 17:06:37 2009
@@ -1,7 +1,5 @@
 /*
- * $Header: /cvshome/build/org.osgi.util.position/src/org/osgi/util/position/Position.java,v 1.9 2006/07/12 21:21:35 hargrave Exp $
- *
- * Copyright (c) OSGi Alliance (2002, 2006). All Rights Reserved.
+ * Copyright (c) OSGi Alliance (2002, 2009). 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.
@@ -35,16 +33,19 @@
  * hashCode() because it is not clear how missing values should be handled. It
  * is up to the user of a position to determine how best to compare two position
  * objects. A <code>Position</code> object is immutable.
+ * 
+ * @Immutable
+ * @version $Revision: 6860 $
  */
 public class Position {
-	private Measurement	altitude;
-	private Measurement	longitude;
-	private Measurement	latitude;
-	private Measurement	speed;
-	private Measurement	track;
+	private final Measurement	altitude;
+	private final Measurement	longitude;
+	private final Measurement	latitude;
+	private final Measurement	speed;
+	private final Measurement	track;
 
 	/**
-	 * Contructs a <code>Position</code> object with the given values.
+	 * Constructs a <code>Position</code> object with the given values.
 	 * 
 	 * @param lat a <code>Measurement</code> object specifying the latitude in
 	 *        radians, or null
@@ -63,33 +64,87 @@
 			if (!Unit.rad.equals(lat.getUnit())) {
 				throw new IllegalArgumentException("Invalid Latitude");
 			}
-			this.latitude = lat;
 		}
 		if (lon != null) {
 			if (!Unit.rad.equals(lon.getUnit())) {
 				throw new IllegalArgumentException("Invalid Longitude");
 			}
-			this.longitude = lon;
 		}
-		normalizeLatLon();
 		if (alt != null) {
 			if (!Unit.m.equals(alt.getUnit())) {
 				throw new IllegalArgumentException("Invalid Altitude");
 			}
-			this.altitude = alt;
 		}
 		if (speed != null) {
 			if (!Unit.m_s.equals(speed.getUnit())) {
 				throw new IllegalArgumentException("Invalid Speed");
 			}
-			this.speed = speed;
 		}
 		if (track != null) {
 			if (!Unit.rad.equals(track.getUnit())) {
 				throw new IllegalArgumentException("Invalid Track");
 			}
-			this.track = normalizeTrack(track);
 		}
+
+		/*
+		 * Verify the longitude and latitude parameters so they fit the normal
+		 * coordinate system. A latitude is between -90 (south) and +90 (north).
+		 * A longitude is between -180 (Western hemisphere) and +180 (eastern
+		 * hemisphere). This method first normalizes the latitude and longitude
+		 * between +/- 180. If the |latitude| > 90, then the longitude is added
+		 * 180 and the latitude is normalized to fit +/-90. (Example are with
+		 * degrees though radians are used) <br> No normalization takes place
+		 * when either lon or lat is null.
+		 */
+		normalizeLatLon: {
+			if (lat == null || lon == null) {
+				break normalizeLatLon;
+			}
+			double dlat = lat.getValue();
+			double dlon = lon.getValue();
+			if (dlon >= -LON_RANGE && dlon < LON_RANGE && dlat >= -LAT_RANGE
+					&& dlat <= LAT_RANGE) {
+				break normalizeLatLon;
+			}
+			dlon = normalize(dlon, LON_RANGE);
+			dlat = normalize(dlat, LAT_RANGE * 2.0D); // First over 180 degree
+			// Check if we have to move to other side of the earth
+			if (dlat > LAT_RANGE || dlat < -LAT_RANGE) {
+				dlon = normalize(dlon - LON_RANGE, LON_RANGE);
+				dlat = normalize((LAT_RANGE * 2.0D) - dlat, LAT_RANGE);
+			}
+			lon = new Measurement(dlon, lon.getError(), lon.getUnit(), lon
+					.getTime());
+			lat = new Measurement(dlat, lat.getError(), lat.getUnit(), lat
+					.getTime());
+		}
+
+		/*
+		 * Normalize track to be a value such that: 0 <= value < +2PI. This
+		 * corresponds to 0 deg to +360 deg. 0 is North, 0.5PI is East, PI is
+		 * South, 1.5PI is West
+		 */
+		normalizeTrack: {
+			if (track == null) {
+				break normalizeTrack;
+			}
+			double dtrack = track.getValue();
+			if ((0.0D <= dtrack) && (dtrack < TRACK_RANGE)) {
+				break normalizeTrack; /* value is already normalized */
+			}
+			dtrack %= TRACK_RANGE;
+			if (dtrack < 0.0D) {
+				dtrack += TRACK_RANGE;
+			}
+			track = new Measurement(dtrack, track.getError(), track.getUnit(),
+					track.getTime());
+		}
+
+		this.latitude = lat;
+		this.longitude = lon;
+		this.altitude = alt;
+		this.speed = speed;
+		this.track = track;
 	}
 
 	/**
@@ -153,37 +208,6 @@
 	private static final double	LAT_RANGE	= Math.PI / 2.0D;
 
 	/**
-	 * Verify the longitude and latitude parameters so they fit the normal
-	 * coordinate system. A latitude is between -90 (south) and +90 (north). A A
-	 * longitude is between -180 (Western hemisphere) and +180 (eastern
-	 * hemisphere). This method first normalizes the latitude and longitude
-	 * between +/- 180. If the |latitude| > 90, then the longitude is added 180
-	 * and the latitude is normalized to fit +/-90. (Example are with degrees
-	 * though radians are used) <br>
-	 * No normalization takes place when either lon or lat is null.
-	 */
-	private void normalizeLatLon() {
-		if (longitude == null || latitude == null)
-			return;
-		double dlon = longitude.getValue();
-		double dlat = latitude.getValue();
-		if (dlon >= -LON_RANGE && dlon < LON_RANGE && dlat >= -LAT_RANGE
-				&& dlat <= LAT_RANGE)
-			return;
-		dlon = normalize(dlon, LON_RANGE);
-		dlat = normalize(dlat, LAT_RANGE * 2.0D); // First over 180 degree
-		// Check if we have to move to other side of the earth
-		if (dlat > LAT_RANGE || dlat < -LAT_RANGE) {
-			dlon = normalize(dlon - LON_RANGE, LON_RANGE);
-			dlat = normalize((LAT_RANGE * 2.0D) - dlat, LAT_RANGE);
-		}
-		longitude = new Measurement(dlon, longitude.getError(), longitude
-				.getUnit(), longitude.getTime());
-		latitude = new Measurement(dlat, latitude.getError(), latitude
-				.getUnit(), latitude.getTime());
-	}
-
-	/**
 	 * This function normalizes the a value according to a range. This is not
 	 * simple modulo (as I thought when I started), but requires some special
 	 * handling. For positive numbers we subtract 2*range from the number so
@@ -201,7 +225,7 @@
 	 * @param value The value that needs adjusting
 	 * @param range -range = < value < range
 	 */
-	private double normalize(double value, double range) {
+	private static double normalize(double value, double range) {
 		double twiceRange = 2.0D * range;
 		while (value >= range) {
 			value -= twiceRange;
@@ -213,25 +237,4 @@
 	}
 
 	private static final double	TRACK_RANGE	= Math.PI * 2.0D;
-
-	/**
-	 * Normalize track to be a value such that: 0 <= value < +2PI. This
-	 * corresponds to 0 deg to +360 deg. 0 is North 0.5*PI is East PI is South
-	 * 1.5*PI is West
-	 * 
-	 * @param track Value to be normalized
-	 * @return Normalized value
-	 */
-	private Measurement normalizeTrack(Measurement track) {
-		double value = track.getValue();
-		if ((0.0D <= value) && (value < TRACK_RANGE)) {
-			return track; /* value is already normalized */
-		}
-		value %= TRACK_RANGE;
-		if (value < 0.0D) {
-			value += TRACK_RANGE;
-		}
-		return new Measurement(value, track.getError(), track.getUnit(), track
-				.getTime());
-	}
 }

Added: felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/position/package.html
URL: http://svn.apache.org/viewvc/felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/position/package.html?rev=797561&view=auto
==============================================================================
--- felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/position/package.html (added)
+++ felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/position/package.html Fri Jul 24 17:06:37 2009
@@ -0,0 +1,10 @@
+<!-- $Revision: 6204 $ -->
+<BODY>
+<p>Position Package Version 1.0.
+<p>Bundles wishing to use this package must list the package
+in the Import-Package header of the bundle's manifest.
+For example:
+<pre>
+Import-Package: org.osgi.util.position; version=&quot;[1.0,2.0)&quot;
+</pre>
+</BODY>

Added: felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/position/packageinfo
URL: http://svn.apache.org/viewvc/felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/position/packageinfo?rev=797561&view=auto
==============================================================================
--- felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/position/packageinfo (added)
+++ felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/position/packageinfo Fri Jul 24 17:06:37 2009
@@ -0,0 +1 @@
+version 1.0.1
\ No newline at end of file

Added: felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/tracker/AbstractTracked.java
URL: http://svn.apache.org/viewvc/felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/tracker/AbstractTracked.java?rev=797561&view=auto
==============================================================================
--- felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/tracker/AbstractTracked.java (added)
+++ felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/tracker/AbstractTracked.java Fri Jul 24 17:06:37 2009
@@ -0,0 +1,449 @@
+/*
+ * Copyright (c) OSGi Alliance (2007, 2008). 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.util.tracker;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Abstract class to track items. If a Tracker is reused (closed then reopened),
+ * then a new AbstractTracked object is used. This class acts a map of tracked
+ * item -> customized object. Subclasses of this class will act as the listener
+ * object for the tracker. This class is used to synchronize access to the
+ * tracked items. This is not a public class. It is only for use by the
+ * implementation of the Tracker class.
+ * 
+ * @ThreadSafe
+ * @version $Revision: 5871 $
+ * @since 1.4
+ */
+abstract class AbstractTracked {
+	/* set this to true to compile in debug messages */
+	static final boolean		DEBUG	= false;
+
+	/**
+	 * Map of tracked items to customized objects.
+	 * 
+	 * @GuardedBy this
+	 */
+	private final Map			tracked;
+
+	/**
+	 * Modification count. This field is initialized to zero and incremented by
+	 * modified.
+	 * 
+	 * @GuardedBy this
+	 */
+	private int					trackingCount;
+
+	/**
+	 * List of items in the process of being added. This is used to deal with
+	 * nesting of events. Since events may be synchronously delivered, events
+	 * can be nested. For example, when processing the adding of a service and
+	 * the customizer causes the service to be unregistered, notification to the
+	 * nested call to untrack that the service was unregistered can be made to
+	 * the track method.
+	 * 
+	 * Since the ArrayList implementation is not synchronized, all access to
+	 * this list must be protected by the same synchronized object for
+	 * thread-safety.
+	 * 
+	 * @GuardedBy this
+	 */
+	private final List			adding;
+
+	/**
+	 * true if the tracked object is closed.
+	 * 
+	 * This field is volatile because it is set by one thread and read by
+	 * another.
+	 */
+	volatile boolean			closed;
+
+	/**
+	 * Initial list of items for the tracker. This is used to correctly process
+	 * the initial items which could be modified before they are tracked. This
+	 * is necessary since the initial set of tracked items are not "announced"
+	 * by events and therefore the event which makes the item untracked could be
+	 * delivered before we track the item.
+	 * 
+	 * An item must not be in both the initial and adding lists at the same
+	 * time. An item must be moved from the initial list to the adding list
+	 * "atomically" before we begin tracking it.
+	 * 
+	 * Since the LinkedList implementation is not synchronized, all access to
+	 * this list must be protected by the same synchronized object for
+	 * thread-safety.
+	 * 
+	 * @GuardedBy this
+	 */
+	private final LinkedList	initial;
+
+	/**
+	 * AbstractTracked constructor.
+	 */
+	AbstractTracked() {
+		tracked = new HashMap();
+		trackingCount = 0;
+		adding = new ArrayList(6);
+		initial = new LinkedList();
+		closed = false;
+	}
+
+	/**
+	 * Set initial list of items into tracker before events begin to be
+	 * received.
+	 * 
+	 * This method must be called from Tracker's open method while synchronized
+	 * on this object in the same synchronized block as the add listener call.
+	 * 
+	 * @param list The initial list of items to be tracked. <code>null</code>
+	 *        entries in the list are ignored.
+	 * @GuardedBy this
+	 */
+	void setInitial(Object[] list) {
+		if (list == null) {
+			return;
+		}
+		int size = list.length;
+		for (int i = 0; i < size; i++) {
+			Object item = list[i];
+			if (item == null) {
+				continue;
+			}
+			if (DEBUG) {
+				System.out.println("AbstractTracked.setInitial: " + item); //$NON-NLS-1$
+			}
+			initial.add(item);
+		}
+	}
+
+	/**
+	 * Track the initial list of items. This is called after events can begin to
+	 * be received.
+	 * 
+	 * This method must be called from Tracker's open method while not
+	 * synchronized on this object after the add listener call.
+	 * 
+	 */
+	void trackInitial() {
+		while (true) {
+			Object item;
+			synchronized (this) {
+				if (closed || (initial.size() == 0)) {
+					/*
+					 * if there are no more initial items
+					 */
+					return; /* we are done */
+				}
+				/*
+				 * move the first item from the initial list to the adding list
+				 * within this synchronized block.
+				 */
+				item = initial.removeFirst();
+				if (tracked.get(item) != null) {
+					/* if we are already tracking this item */
+					if (DEBUG) {
+						System.out
+								.println("AbstractTracked.trackInitial[already tracked]: " + item); //$NON-NLS-1$
+					}
+					continue; /* skip this item */
+				}
+				if (adding.contains(item)) {
+					/*
+					 * if this item is already in the process of being added.
+					 */
+					if (DEBUG) {
+						System.out
+								.println("AbstractTracked.trackInitial[already adding]: " + item); //$NON-NLS-1$
+					}
+					continue; /* skip this item */
+				}
+				adding.add(item);
+			}
+			if (DEBUG) {
+				System.out.println("AbstractTracked.trackInitial: " + item); //$NON-NLS-1$
+			}
+			trackAdding(item, null); /*
+									 * Begin tracking it. We call trackAdding
+									 * since we have already put the item in the
+									 * adding list.
+									 */
+		}
+	}
+
+	/**
+	 * Called by the owning Tracker object when it is closed.
+	 */
+	void close() {
+		closed = true;
+	}
+
+	/**
+	 * Begin to track an item.
+	 * 
+	 * @param item Item to be tracked.
+	 * @param related Action related object.
+	 */
+	void track(final Object item, final Object related) {
+		final Object object;
+		synchronized (this) {
+			if (closed) {
+				return;
+			}
+			object = tracked.get(item);
+			if (object == null) { /* we are not tracking the item */
+				if (adding.contains(item)) {
+					/* if this item is already in the process of being added. */
+					if (DEBUG) {
+						System.out
+								.println("AbstractTracked.track[already adding]: " + item); //$NON-NLS-1$
+					}
+					return;
+				}
+				adding.add(item); /* mark this item is being added */
+			}
+			else { /* we are currently tracking this item */
+				if (DEBUG) {
+					System.out
+							.println("AbstractTracked.track[modified]: " + item); //$NON-NLS-1$
+				}
+				modified(); /* increment modification count */
+			}
+		}
+
+		if (object == null) { /* we are not tracking the item */
+			trackAdding(item, related);
+		}
+		else {
+			/* Call customizer outside of synchronized region */
+			customizerModified(item, related, object);
+			/*
+			 * If the customizer throws an unchecked exception, it is safe to
+			 * let it propagate
+			 */
+		}
+	}
+
+	/**
+	 * Common logic to add an item to the tracker used by track and
+	 * trackInitial. The specified item must have been placed in the adding list
+	 * before calling this method.
+	 * 
+	 * @param item Item to be tracked.
+	 * @param related Action related object.
+	 */
+	private void trackAdding(final Object item, final Object related) {
+		if (DEBUG) {
+			System.out.println("AbstractTracked.trackAdding: " + item); //$NON-NLS-1$
+		}
+		Object object = null;
+		boolean becameUntracked = false;
+		/* Call customizer outside of synchronized region */
+		try {
+			object = customizerAdding(item, related);
+			/*
+			 * If the customizer throws an unchecked exception, it will
+			 * propagate after the finally
+			 */
+		}
+		finally {
+			synchronized (this) {
+				if (adding.remove(item) && !closed) {
+					/*
+					 * if the item was not untracked during the customizer
+					 * callback
+					 */
+					if (object != null) {
+						tracked.put(item, object);
+						modified(); /* increment modification count */
+						notifyAll(); /* notify any waiters */
+					}
+				}
+				else {
+					becameUntracked = true;
+				}
+			}
+		}
+		/*
+		 * The item became untracked during the customizer callback.
+		 */
+		if (becameUntracked && (object != null)) {
+			if (DEBUG) {
+				System.out
+						.println("AbstractTracked.trackAdding[removed]: " + item); //$NON-NLS-1$
+			}
+			/* Call customizer outside of synchronized region */
+			customizerRemoved(item, related, object);
+			/*
+			 * If the customizer throws an unchecked exception, it is safe to
+			 * let it propagate
+			 */
+		}
+	}
+
+	/**
+	 * Discontinue tracking the item.
+	 * 
+	 * @param item Item to be untracked.
+	 * @param related Action related object.
+	 */
+	void untrack(final Object item, final Object related) {
+		final Object object;
+		synchronized (this) {
+			if (initial.remove(item)) { /*
+										 * if this item is already in the list
+										 * of initial references to process
+										 */
+				if (DEBUG) {
+					System.out
+							.println("AbstractTracked.untrack[removed from initial]: " + item); //$NON-NLS-1$
+				}
+				return; /*
+						 * we have removed it from the list and it will not be
+						 * processed
+						 */
+			}
+
+			if (adding.remove(item)) { /*
+										 * if the item is in the process of
+										 * being added
+										 */
+				if (DEBUG) {
+					System.out
+							.println("AbstractTracked.untrack[being added]: " + item); //$NON-NLS-1$
+				}
+				return; /*
+						 * in case the item is untracked while in the process of
+						 * adding
+						 */
+			}
+			object = tracked.remove(item); /*
+											 * must remove from tracker before
+											 * calling customizer callback
+											 */
+			if (object == null) { /* are we actually tracking the item */
+				return;
+			}
+			modified(); /* increment modification count */
+		}
+		if (DEBUG) {
+			System.out.println("AbstractTracked.untrack[removed]: " + item); //$NON-NLS-1$
+		}
+		/* Call customizer outside of synchronized region */
+		customizerRemoved(item, related, object);
+		/*
+		 * If the customizer throws an unchecked exception, it is safe to let it
+		 * propagate
+		 */
+	}
+
+	/**
+	 * Returns the number of tracked items.
+	 * 
+	 * @return The number of tracked items.
+	 * 
+	 * @GuardedBy this
+	 */
+	int size() {
+		return tracked.size();
+	}
+
+	/**
+	 * Return the customized object for the specified item
+	 * 
+	 * @param item The item to lookup in the map
+	 * @return The customized object for the specified item.
+	 * 
+	 * @GuardedBy this
+	 */
+	Object getCustomizedObject(final Object item) {
+		return tracked.get(item);
+	}
+
+	/**
+	 * Return the list of tracked items.
+	 * 
+	 * @param list An array to contain the tracked items.
+	 * @return The specified list if it is large enough to hold the tracked
+	 *         items or a new array large enough to hold the tracked items.
+	 * @GuardedBy this
+	 */
+	Object[] getTracked(final Object[] list) {
+		return tracked.keySet().toArray(list);
+	}
+
+	/**
+	 * Increment the modification count. If this method is overridden, the
+	 * overriding method MUST call this method to increment the tracking count.
+	 * 
+	 * @GuardedBy this
+	 */
+	void modified() {
+		trackingCount++;
+	}
+
+	/**
+	 * Returns the tracking count for this <code>ServiceTracker</code> object.
+	 * 
+	 * The tracking count is initialized to 0 when this object is opened. Every
+	 * time an item is added, modified or removed from this object the tracking
+	 * count is incremented.
+	 * 
+	 * @GuardedBy this
+	 * @return The tracking count for this object.
+	 */
+	int getTrackingCount() {
+		return trackingCount;
+	}
+
+	/**
+	 * Call the specific customizer adding method. This method must not be
+	 * called while synchronized on this object.
+	 * 
+	 * @param item Item to be tracked.
+	 * @param related Action related object.
+	 * @return Customized object for the tracked item or <code>null</code> if
+	 *         the item is not to be tracked.
+	 */
+	abstract Object customizerAdding(final Object item, final Object related);
+
+	/**
+	 * Call the specific customizer modified method. This method must not be
+	 * called while synchronized on this object.
+	 * 
+	 * @param item Tracked item.
+	 * @param related Action related object.
+	 * @param object Customized object for the tracked item.
+	 */
+	abstract void customizerModified(final Object item, final Object related,
+			final Object object);
+
+	/**
+	 * Call the specific customizer removed method. This method must not be
+	 * called while synchronized on this object.
+	 * 
+	 * @param item Tracked item.
+	 * @param related Action related object.
+	 * @param object Customized object for the tracked item.
+	 */
+	abstract void customizerRemoved(final Object item, final Object related,
+			final Object object);
+}

Added: felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/tracker/BundleTracker.java
URL: http://svn.apache.org/viewvc/felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/tracker/BundleTracker.java?rev=797561&view=auto
==============================================================================
--- felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/tracker/BundleTracker.java (added)
+++ felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/tracker/BundleTracker.java Fri Jul 24 17:06:37 2009
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) OSGi Alliance (2007, 2008). 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.util.tracker;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.SynchronousBundleListener;
+
+/**
+ * The <code>BundleTracker</code> class simplifies tracking bundles much like
+ * the <code>ServiceTracker</code> simplifies tracking services.
+ * <p>
+ * A <code>BundleTracker</code> is constructed with state criteria and a
+ * <code>BundleTrackerCustomizer</code> object. A <code>BundleTracker</code> can
+ * use the <code>BundleTrackerCustomizer</code> to select which bundles are
+ * tracked and to create a customized object to be tracked with the bundle. The
+ * <code>BundleTracker</code> can then be opened to begin tracking all bundles
+ * whose state matches the specified state criteria.
+ * <p>
+ * The <code>getBundles</code> method can be called to get the
+ * <code>Bundle</code> objects of the bundles being tracked. The
+ * <code>getObject</code> method can be called to get the customized object for
+ * a tracked bundle.
+ * <p>
+ * The <code>BundleTracker</code> class is thread-safe. It does not call a
+ * <code>BundleTrackerCustomizer</code> while holding any locks.
+ * <code>BundleTrackerCustomizer</code> implementations must also be
+ * thread-safe.
+ * 
+ * @ThreadSafe
+ * @version $Revision: 5894 $
+ * @since 1.4
+ */
+public class BundleTracker implements BundleTrackerCustomizer {
+	/* set this to true to compile in debug messages */
+	static final boolean			DEBUG	= false;
+
+	/**
+	 * The Bundle Context used by this <code>BundleTracker</code>.
+	 */
+	protected final BundleContext	context;
+
+	/**
+	 * The <code>BundleTrackerCustomizer</code> object for this tracker.
+	 */
+	final BundleTrackerCustomizer	customizer;
+
+	/**
+	 * Tracked bundles: <code>Bundle</code> object -> customized Object and
+	 * <code>BundleListener</code> object
+	 */
+	private volatile Tracked		tracked;
+
+	/**
+	 * Accessor method for the current Tracked object. This method is only
+	 * intended to be used by the unsynchronized methods which do not modify the
+	 * tracked field.
+	 * 
+	 * @return The current Tracked object.
+	 */
+	private Tracked tracked() {
+		return tracked;
+	}
+
+	/**
+	 * State mask for bundles being tracked. This field contains the ORed values
+	 * of the bundle states being tracked.
+	 */
+	final int						mask;
+
+	/**
+	 * Create a <code>BundleTracker</code> for bundles whose state is present in
+	 * the specified state mask.
+	 * 
+	 * <p>
+	 * Bundles whose state is present on the specified state mask will be
+	 * tracked by this <code>BundleTracker</code>.
+	 * 
+	 * @param context The <code>BundleContext</code> against which the tracking
+	 *        is done.
+	 * @param stateMask The bit mask of the <code>OR</code>ing of the bundle
+	 *        states to be tracked.
+	 * @param customizer The customizer object to call when bundles are added,
+	 *        modified, or removed in this <code>BundleTracker</code>. If
+	 *        customizer is <code>null</code>, then this
+	 *        <code>BundleTracker</code> will be used as the
+	 *        <code>BundleTrackerCustomizer</code> and this
+	 *        <code>BundleTracker</code> will call the
+	 *        <code>BundleTrackerCustomizer</code> methods on itself.
+	 * @see Bundle#getState()
+	 */
+	public BundleTracker(BundleContext context, int stateMask,
+			BundleTrackerCustomizer customizer) {
+		this.context = context;
+		this.mask = stateMask;
+		this.customizer = (customizer == null) ? this : customizer;
+	}
+
+	/**
+	 * Open this <code>BundleTracker</code> and begin tracking bundles.
+	 * 
+	 * <p>
+	 * Bundle which match the state criteria specified when this
+	 * <code>BundleTracker</code> was created are now tracked by this
+	 * <code>BundleTracker</code>.
+	 * 
+	 * @throws java.lang.IllegalStateException If the <code>BundleContext</code>
+	 *         with which this <code>BundleTracker</code> was created is no
+	 *         longer valid.
+	 * @throws java.lang.SecurityException If the caller and this class do not
+	 *         have the appropriate
+	 *         <code>AdminPermission[context bundle,LISTENER]</code>, and the
+	 *         Java Runtime Environment supports permissions.
+	 */
+	public void open() {
+		final Tracked t;
+		synchronized (this) {
+			if (tracked != null) {
+				return;
+			}
+			if (DEBUG) {
+				System.out.println("BundleTracker.open"); //$NON-NLS-1$
+			}
+			t = new Tracked();
+			synchronized (t) {
+				context.addBundleListener(t);
+				Bundle[] bundles = context.getBundles();
+				if (bundles != null) {
+					int length = bundles.length;
+					for (int i = 0; i < length; i++) {
+						int state = bundles[i].getState();
+						if ((state & mask) == 0) {
+							/* null out bundles whose states are not interesting */
+							bundles[i] = null;
+						}
+					}
+					/* set tracked with the initial bundles */
+					t.setInitial(bundles); 
+				}
+			}
+			tracked = t;
+		}
+		/* Call tracked outside of synchronized region */
+		t.trackInitial(); /* process the initial references */
+	}
+
+	/**
+	 * Close this <code>BundleTracker</code>.
+	 * 
+	 * <p>
+	 * This method should be called when this <code>BundleTracker</code> should
+	 * end the tracking of bundles.
+	 * 
+	 * <p>
+	 * This implementation calls {@link #getBundles()} to get the list of
+	 * tracked bundles to remove.
+	 */
+	public void close() {
+		final Bundle[] bundles;
+		final Tracked outgoing;
+		synchronized (this) {
+			outgoing = tracked;
+			if (outgoing == null) {
+				return;
+			}
+			if (DEBUG) {
+				System.out.println("BundleTracker.close"); //$NON-NLS-1$
+			}
+			outgoing.close();
+			bundles = getBundles();
+			tracked = null;
+			try {
+				context.removeBundleListener(outgoing);
+			}
+			catch (IllegalStateException e) {
+				/* In case the context was stopped. */
+			}
+		}
+		if (bundles != null) {
+			for (int i = 0; i < bundles.length; i++) {
+				outgoing.untrack(bundles[i], null);
+			}
+		}
+	}
+
+	/**
+	 * Default implementation of the
+	 * <code>BundleTrackerCustomizer.addingBundle</code> method.
+	 * 
+	 * <p>
+	 * This method is only called when this <code>BundleTracker</code> has been
+	 * constructed with a <code>null BundleTrackerCustomizer</code> argument.
+	 * 
+	 * <p>
+	 * This implementation simply returns the specified <code>Bundle</code>.
+	 * 
+	 * <p>
+	 * This method can be overridden in a subclass to customize the object to be
+	 * tracked for the bundle being added.
+	 * 
+	 * @param bundle The <code>Bundle</code> being added to this
+	 *        <code>BundleTracker</code> object.
+	 * @param event The bundle event which caused this customizer method to be
+	 *        called or <code>null</code> if there is no bundle event associated
+	 *        with the call to this method.
+	 * @return The specified bundle.
+	 * @see BundleTrackerCustomizer#addingBundle(Bundle, BundleEvent)
+	 */
+	public Object addingBundle(Bundle bundle, BundleEvent event) {
+		return bundle;
+	}
+
+	/**
+	 * Default implementation of the
+	 * <code>BundleTrackerCustomizer.modifiedBundle</code> method.
+	 * 
+	 * <p>
+	 * This method is only called when this <code>BundleTracker</code> has been
+	 * constructed with a <code>null BundleTrackerCustomizer</code> argument.
+	 * 
+	 * <p>
+	 * This implementation does nothing.
+	 * 
+	 * @param bundle The <code>Bundle</code> whose state has been modified.
+	 * @param event The bundle event which caused this customizer method to be
+	 *        called or <code>null</code> if there is no bundle event associated
+	 *        with the call to this method.
+	 * @param object The customized object for the specified Bundle.
+	 * @see BundleTrackerCustomizer#modifiedBundle(Bundle, BundleEvent, Object)
+	 */
+	public void modifiedBundle(Bundle bundle, BundleEvent event, Object object) {
+		/* do nothing */
+	}
+
+	/**
+	 * Default implementation of the
+	 * <code>BundleTrackerCustomizer.removedBundle</code> method.
+	 * 
+	 * <p>
+	 * This method is only called when this <code>BundleTracker</code> has been
+	 * constructed with a <code>null BundleTrackerCustomizer</code> argument.
+	 * 
+	 * <p>
+	 * This implementation does nothing.
+	 * 
+	 * @param bundle The <code>Bundle</code> being removed.
+	 * @param event The bundle event which caused this customizer method to be
+	 *        called or <code>null</code> if there is no bundle event associated
+	 *        with the call to this method.
+	 * @param object The customized object for the specified bundle.
+	 * @see BundleTrackerCustomizer#removedBundle(Bundle, BundleEvent, Object)
+	 */
+	public void removedBundle(Bundle bundle, BundleEvent event, Object object) {
+		/* do nothing */
+	}
+
+	/**
+	 * Return an array of <code>Bundle</code>s for all bundles being tracked by
+	 * this <code>BundleTracker</code>.
+	 * 
+	 * @return An array of <code>Bundle</code>s or <code>null</code> if no
+	 *         bundles are being tracked.
+	 */
+	public Bundle[] getBundles() {
+		final Tracked t = tracked();
+		if (t == null) { /* if BundleTracker is not open */
+			return null;
+		}
+		synchronized (t) {
+			int length = t.size();
+			if (length == 0) {
+				return null;
+			}
+			return (Bundle[]) t.getTracked(new Bundle[length]);
+		}
+	}
+
+	/**
+	 * Returns the customized object for the specified <code>Bundle</code> if
+	 * the specified bundle is being tracked by this <code>BundleTracker</code>.
+	 * 
+	 * @param bundle The <code>Bundle</code> being tracked.
+	 * @return The customized object for the specified <code>Bundle</code> or
+	 *         <code>null</code> if the specified <code>Bundle</code> is not
+	 *         being tracked.
+	 */
+	public Object getObject(Bundle bundle) {
+		final Tracked t = tracked();
+		if (t == null) { /* if BundleTracker is not open */
+			return null;
+		}
+		synchronized (t) {
+			return t.getCustomizedObject(bundle);
+		}
+	}
+
+	/**
+	 * Remove a bundle from this <code>BundleTracker</code>.
+	 * 
+	 * The specified bundle will be removed from this <code>BundleTracker</code>
+	 * . If the specified bundle was being tracked then the
+	 * <code>BundleTrackerCustomizer.removedBundle</code> method will be called
+	 * for that bundle.
+	 * 
+	 * @param bundle The <code>Bundle</code> to be removed.
+	 */
+	public void remove(Bundle bundle) {
+		final Tracked t = tracked();
+		if (t == null) { /* if BundleTracker is not open */
+			return;
+		}
+		t.untrack(bundle, null);
+	}
+
+	/**
+	 * Return the number of bundles being tracked by this
+	 * <code>BundleTracker</code>.
+	 * 
+	 * @return The number of bundles being tracked.
+	 */
+	public int size() {
+		final Tracked t = tracked();
+		if (t == null) { /* if BundleTracker is not open */
+			return 0;
+		}
+		synchronized (t) {
+			return t.size();
+		}
+	}
+
+	/**
+	 * Returns the tracking count for this <code>BundleTracker</code>.
+	 * 
+	 * The tracking count is initialized to 0 when this
+	 * <code>BundleTracker</code> is opened. Every time a bundle is added,
+	 * modified or removed from this <code>BundleTracker</code> the tracking
+	 * count is incremented.
+	 * 
+	 * <p>
+	 * The tracking count can be used to determine if this
+	 * <code>BundleTracker</code> has added, modified or removed a bundle by
+	 * comparing a tracking count value previously collected with the current
+	 * tracking count value. If the value has not changed, then no bundle has
+	 * been added, modified or removed from this <code>BundleTracker</code>
+	 * since the previous tracking count was collected.
+	 * 
+	 * @return The tracking count for this <code>BundleTracker</code> or -1 if
+	 *         this <code>BundleTracker</code> is not open.
+	 */
+	public int getTrackingCount() {
+		final Tracked t = tracked();
+		if (t == null) { /* if BundleTracker is not open */
+			return -1;
+		}
+		synchronized (t) {
+			return t.getTrackingCount();
+		}
+	}
+
+	/**
+	 * Inner class which subclasses AbstractTracked. This class is the
+	 * <code>SynchronousBundleListener</code> object for the tracker.
+	 * 
+	 * @ThreadSafe
+	 * @since 1.4
+	 */
+	class Tracked extends AbstractTracked implements SynchronousBundleListener {
+		/**
+		 * Tracked constructor.
+		 */
+		Tracked() {
+			super();
+		}
+
+		/**
+		 * <code>BundleListener</code> method for the <code>BundleTracker</code>
+		 * class. This method must NOT be synchronized to avoid deadlock
+		 * potential.
+		 * 
+		 * @param event <code>BundleEvent</code> object from the framework.
+		 */
+		public void bundleChanged(final BundleEvent event) {
+			/*
+			 * Check if we had a delayed call (which could happen when we
+			 * close).
+			 */
+			if (closed) {
+				return;
+			}
+			final Bundle bundle = event.getBundle();
+			final int state = bundle.getState();
+			if (DEBUG) {
+				System.out
+						.println("BundleTracker.Tracked.bundleChanged[" + state + "]: " + bundle); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+
+			if ((state & mask) != 0) {
+				track(bundle, event);
+				/*
+				 * If the customizer throws an unchecked exception, it is safe
+				 * to let it propagate
+				 */
+			}
+			else {
+				untrack(bundle, event);
+				/*
+				 * If the customizer throws an unchecked exception, it is safe
+				 * to let it propagate
+				 */
+			}
+		}
+
+		/**
+		 * Call the specific customizer adding method. This method must not be
+		 * called while synchronized on this object.
+		 * 
+		 * @param item Item to be tracked.
+		 * @param related Action related object.
+		 * @return Customized object for the tracked item or <code>null</code>
+		 *         if the item is not to be tracked.
+		 */
+		Object customizerAdding(final Object item,
+				final Object related) {
+			return customizer
+					.addingBundle((Bundle) item, (BundleEvent) related);
+		}
+
+		/**
+		 * Call the specific customizer modified method. This method must not be
+		 * called while synchronized on this object.
+		 * 
+		 * @param item Tracked item.
+		 * @param related Action related object.
+		 * @param object Customized object for the tracked item.
+		 */
+		void customizerModified(final Object item,
+				final Object related, final Object object) {
+			customizer.modifiedBundle((Bundle) item, (BundleEvent) related,
+					object);
+		}
+
+		/**
+		 * Call the specific customizer removed method. This method must not be
+		 * called while synchronized on this object.
+		 * 
+		 * @param item Tracked item.
+		 * @param related Action related object.
+		 * @param object Customized object for the tracked item.
+		 */
+		void customizerRemoved(final Object item,
+				final Object related, final Object object) {
+			customizer.removedBundle((Bundle) item, (BundleEvent) related,
+					object);
+		}
+	}
+}

Added: felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/tracker/BundleTrackerCustomizer.java
URL: http://svn.apache.org/viewvc/felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/tracker/BundleTrackerCustomizer.java?rev=797561&view=auto
==============================================================================
--- felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/tracker/BundleTrackerCustomizer.java (added)
+++ felix/trunk/org.osgi.compendium/src/main/java/org/osgi/util/tracker/BundleTrackerCustomizer.java Fri Jul 24 17:06:37 2009
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) OSGi Alliance (2007, 2008). 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.util.tracker;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleEvent;
+
+/**
+ * The <code>BundleTrackerCustomizer</code> interface allows a
+ * <code>BundleTracker</code> to customize the <code>Bundle</code>s that are
+ * tracked. A <code>BundleTrackerCustomizer</code> is called when a bundle is
+ * being added to a <code>BundleTracker</code>. The
+ * <code>BundleTrackerCustomizer</code> can then return an object for the
+ * tracked bundle. A <code>BundleTrackerCustomizer</code> is also called when a
+ * tracked bundle is modified or has been removed from a
+ * <code>BundleTracker</code>.
+ * 
+ * <p>
+ * The methods in this interface may be called as the result of a
+ * <code>BundleEvent</code> being received by a <code>BundleTracker</code>.
+ * Since <code>BundleEvent</code>s are received synchronously by the
+ * <code>BundleTracker</code>, it is highly recommended that implementations of
+ * these methods do not alter bundle states while being synchronized on any
+ * object.
+ * 
+ * <p>
+ * The <code>BundleTracker</code> class is thread-safe. It does not call a
+ * <code>BundleTrackerCustomizer</code> while holding any locks.
+ * <code>BundleTrackerCustomizer</code> implementations must also be
+ * thread-safe.
+ * 
+ * @ThreadSafe
+ * @version $Revision: 5874 $
+ * @since 1.4
+ */
+public interface BundleTrackerCustomizer {
+	/**
+	 * A bundle is being added to the <code>BundleTracker</code>.
+	 * 
+	 * <p>
+	 * This method is called before a bundle which matched the search parameters
+	 * of the <code>BundleTracker</code> is added to the
+	 * <code>BundleTracker</code>. This method should return the object to be
+	 * tracked for the specified <code>Bundle</code>. The returned object is
+	 * stored in the <code>BundleTracker</code> and is available from the
+	 * {@link BundleTracker#getObject(Bundle) getObject} method.
+	 * 
+	 * @param bundle The <code>Bundle</code> being added to the
+	 *        <code>BundleTracker</code>.
+	 * @param event The bundle event which caused this customizer method to be
+	 *        called or <code>null</code> if there is no bundle event associated
+	 *        with the call to this method.
+	 * @return The object to be tracked for the specified <code>Bundle</code>
+	 *         object or <code>null</code> if the specified <code>Bundle</code>
+	 *         object should not be tracked.
+	 */
+	public Object addingBundle(Bundle bundle, BundleEvent event);
+
+	/**
+	 * A bundle tracked by the <code>BundleTracker</code> has been modified.
+	 * 
+	 * <p>
+	 * This method is called when a bundle being tracked by the
+	 * <code>BundleTracker</code> has had its state modified.
+	 * 
+	 * @param bundle The <code>Bundle</code> whose state has been modified.
+	 * @param event The bundle event which caused this customizer method to be
+	 *        called or <code>null</code> if there is no bundle event associated
+	 *        with the call to this method.
+	 * @param object The tracked object for the specified bundle.
+	 */
+	public void modifiedBundle(Bundle bundle, BundleEvent event,
+			Object object);
+
+	/**
+	 * A bundle tracked by the <code>BundleTracker</code> has been removed.
+	 * 
+	 * <p>
+	 * This method is called after a bundle is no longer being tracked by the
+	 * <code>BundleTracker</code>.
+	 * 
+	 * @param bundle The <code>Bundle</code> that has been removed.
+	 * @param event The bundle event which caused this customizer method to be
+	 *        called or <code>null</code> if there is no bundle event associated
+	 *        with the call to this method.
+	 * @param object The tracked object for the specified bundle.
+	 */
+	public void removedBundle(Bundle bundle, BundleEvent event,
+			Object object);
+}