### Eclipse Workspace Patch 1.0
#P org.eclipse.core.jobs
Index: src/org/eclipse/core/internal/jobs/InternalJob.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/InternalJob.java,v
retrieving revision 1.14
diff -u -r1.14 InternalJob.java
--- src/org/eclipse/core/internal/jobs/InternalJob.java 6 Feb 2010 21:05:24 -0000 1.14
+++ src/org/eclipse/core/internal/jobs/InternalJob.java 6 Apr 2010 15:23:49 -0000
@@ -11,7 +11,6 @@
*******************************************************************************/
package org.eclipse.core.internal.jobs;
-import java.util.Map;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.*;
@@ -94,7 +93,7 @@
* Arbitrary properties (key,value) pairs, attached
* to a job instance by a third party.
*/
- private ObjectMap properties;
+ private ObjectMap properties = new ObjectMap();
/**
* Volatile because it is usually set via a Worker thread and is read via a
@@ -241,11 +240,7 @@
* @see Job#getProperty
*/
protected Object getProperty(QualifiedName key) {
- // thread safety: (Concurrency001 - copy on write)
- Map temp = properties;
- if (temp == null)
- return null;
- return temp.get(key);
+ return properties.get(key);
}
/* (non-Javadoc)
@@ -506,25 +501,15 @@
* @see Job#setProperty(QualifiedName,Object)
*/
protected void setProperty(QualifiedName key, Object value) {
- // thread safety: (Concurrency001 - copy on write)
- if (value == null) {
- if (properties == null)
- return;
- ObjectMap temp = (ObjectMap) properties.clone();
- temp.remove(key);
- if (temp.isEmpty())
- properties = null;
- else
- properties = temp;
- } else {
- ObjectMap temp = properties;
- if (temp == null)
- temp = new ObjectMap(5);
- else
- temp = (ObjectMap) properties.clone();
- temp.put(key, value);
- properties = temp;
- }
+ properties.put(key, value);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see Job#setPropertyIfAbsent(QualifiedName,Object)
+ */
+ protected Object setPropertyIfAbsent(QualifiedName key, Object value) {
+ return properties.putIfAbsent(key, value);
}
/**
Index: src/org/eclipse/core/internal/jobs/ObjectMap.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/ObjectMap.java,v
retrieving revision 1.5
diff -u -r1.5 ObjectMap.java
--- src/org/eclipse/core/internal/jobs/ObjectMap.java 22 Oct 2008 18:36:31 -0000 1.5
+++ src/org/eclipse/core/internal/jobs/ObjectMap.java 6 Apr 2010 15:23:49 -0000
@@ -11,6 +11,7 @@
package org.eclipse.core.internal.jobs;
import java.util.*;
+import java.util.concurrent.ConcurrentMap;
/**
* A specialized map implementation that is optimized for a small set of object
@@ -18,14 +19,17 @@
*
* Implemented as a single array that alternates keys and values.
*
- * Note: This class is copied from org.eclipse.core.resources
+ * Note: This class is copied from org.eclipse.core.resources and modified to have copy-on-write semantics
*/
public class ObjectMap implements Map {
- // 8 attribute keys, 8 attribute values
- protected static final int DEFAULT_SIZE = 16;
- protected static final int GROW_SIZE = 10;
- protected int count = 0;
- protected Object[] elements = null;
+
+ private static final Object[] emptyArray = new Object[0];
+
+ protected volatile Object[] elements = null;
+
+ public ObjectMap() {
+ elements = emptyArray;
+ }
/**
* Creates a new object map.
@@ -34,8 +38,7 @@
* The initial number of elements that will fit in the map.
*/
public ObjectMap(int initialCapacity) {
- if (initialCapacity > 0)
- elements = new Object[Math.max(initialCapacity * 2, 0)];
+ this();
}
/**
@@ -54,8 +57,7 @@
* @see Map#clear()
*/
public void clear() {
- elements = null;
- count = 0;
+ elements = emptyArray;
}
/**
@@ -69,10 +71,11 @@
* @see Map#containsKey(java.lang.Object)
*/
public boolean containsKey(Object key) {
- if (elements == null || count == 0)
+ Object[] e = elements;
+ if (e.length == 0)
return false;
- for (int i = 0; i < elements.length; i = i + 2)
- if (elements[i] != null && elements[i].equals(key))
+ for (int i = 0; i < e.length; i = i + 2)
+ if (e[i] != null && e[i].equals(key))
return true;
return false;
}
@@ -81,10 +84,11 @@
* @see Map#containsValue(java.lang.Object)
*/
public boolean containsValue(Object value) {
- if (elements == null || count == 0)
+ Object[] e = elements;
+ if (e.length == 0)
return false;
- for (int i = 1; i < elements.length; i = i + 2)
- if (elements[i] != null && elements[i].equals(value))
+ for (int i = 1; i < e.length; i = i + 2)
+ if (e[i] != null && e[i].equals(value))
return true;
return false;
}
@@ -97,70 +101,27 @@
* be bound to this map and will not remain in sync with this map.
*/
public Set entrySet() {
- return count == 0 ? Collections.EMPTY_SET : toHashMap().entrySet();
- }
-
- /**
- * @see Object#equals(java.lang.Object)
- */
- public boolean equals(Object o) {
- if (!(o instanceof Map))
- return false;
- Map other = (Map) o;
- //must be same size
- if (count != other.size())
- return false;
- //keysets must be equal
- if (!keySet().equals(other.keySet()))
- return false;
- //values for each key must be equal
- for (int i = 0; i < elements.length; i = i + 2) {
- if (elements[i] != null && (!elements[i + 1].equals(other.get(elements[i]))))
- return false;
- }
- return true;
+ return toHashMap().entrySet();
}
/**
* @see Map#get(java.lang.Object)
*/
public Object get(Object key) {
- if (elements == null || count == 0)
+ Object[] e = elements;
+ if (e.length == 0)
return null;
- for (int i = 0; i < elements.length; i = i + 2)
- if (elements[i] != null && elements[i].equals(key))
- return elements[i + 1];
+ for (int i = 0; i < e.length; i = i + 2)
+ if (e[i] != null && e[i].equals(key))
+ return e[i + 1];
return null;
}
/**
- * The capacity of the map has been exceeded, grow the array by GROW_SIZE to
- * accomodate more entries.
- */
- protected void grow() {
- Object[] expanded = new Object[elements.length + GROW_SIZE];
- System.arraycopy(elements, 0, expanded, 0, elements.length);
- elements = expanded;
- }
-
- /**
- * @see Object#hashCode()
- */
- public int hashCode() {
- int hash = 0;
- for (int i = 0; i < elements.length; i = i + 2) {
- if (elements[i] != null) {
- hash += elements[i].hashCode();
- }
- }
- return hash;
- }
-
- /**
* @see Map#isEmpty()
*/
public boolean isEmpty() {
- return count == 0;
+ return elements.length == 0;
}
/**
@@ -171,10 +132,11 @@
* be bound to this map and will not remain in sync with this map.
*/
public Set keySet() {
- Set result = new HashSet(size());
- for (int i = 0; i < elements.length; i = i + 2) {
- if (elements[i] != null) {
- result.add(elements[i]);
+ Object[] e = elements;
+ Set result = new HashSet(e.length);
+ for (int i = 0; i < e.length; i = i + 2) {
+ if (e[i] != null) {
+ result.add(e[i]);
}
}
return result;
@@ -183,24 +145,20 @@
/**
* @see Map#put(java.lang.Object, java.lang.Object)
*/
- public Object put(Object key, Object value) {
+ public synchronized Object put(Object key, Object value) {
if (key == null)
throw new NullPointerException();
if (value == null)
return remove(key);
// handle the case where we don't have any attributes yet
- if (elements == null)
- elements = new Object[DEFAULT_SIZE];
- if (count == 0) {
+ if (elements.length == 0) {
+ elements = new Object[elements.length + 2];
elements[0] = key;
elements[1] = value;
- count++;
return null;
}
- int emptyIndex = -1;
- // replace existing value if it exists
for (int i = 0; i < elements.length; i += 2) {
if (elements[i] != null) {
if (elements[i].equals(key)) {
@@ -208,49 +166,96 @@
elements[i + 1] = value;
return oldValue;
}
- } else if (emptyIndex == -1) {
- // keep track of the first empty index
- emptyIndex = i;
}
}
- // this will put the emptyIndex greater than the size but
- // that's ok because we will grow first.
- if (emptyIndex == -1)
- emptyIndex = count * 2;
-
- // otherwise add it to the list of elements.
- // grow if necessary
- if (elements.length <= (count * 2))
- grow();
- elements[emptyIndex] = key;
- elements[emptyIndex + 1] = value;
- count++;
+
+ // it's a new key, add it to the end
+ Object[] newElements = new Object[elements.length + 2];
+ System.arraycopy(elements, 0, newElements, 0, elements.length);
+ newElements[elements.length] = key;
+ newElements[elements.length + 1] = value;
+
+ elements = newElements;
return null;
}
/**
+ * {@link ConcurrentMap#putIfAbsent(Object, Object)}
+ */
+ public Object putIfAbsent(Object key, Object value) {
+ if (key == null)
+ throw new NullPointerException();
+
+ Object v = get(key);
+ if (v != null)
+ return v;
+ synchronized (this) {
+ // need to re-check under sync for concurrent putIfAbsents
+ Object val = get(key);
+ if (val != null)
+ return val;
+ // it's a new key, add it to the end
+ Object[] newElements = new Object[elements.length + 2];
+ System.arraycopy(elements, 0, newElements, 0, elements.length);
+ newElements[elements.length] = key;
+ newElements[elements.length + 1] = value;
+
+ elements = newElements;
+ return null;
+ }
+ }
+
+ /**
* @see Map#putAll(java.util.Map)
*/
public void putAll(Map map) {
- for (Iterator i = map.keySet().iterator(); i.hasNext();) {
- Object key = i.next();
- Object value = map.get(key);
- put(key, value);
+ Object[] newElements = new Object[map.size() * 2];
+ int i = 0;
+ for (Iterator itr = map.entrySet().iterator(); itr.hasNext();) {
+ Map.Entry entry = (Map.Entry) itr.next();
+ newElements[i++] = entry.getKey();
+ newElements[i++] = entry.getValue();
+ }
+ putAll(newElements);
+ }
+
+ private synchronized void putAll(Object[] newEntries) {
+ Object[] total = new Object[elements.length + newEntries.length];
+ int size = 0;
+ copy: for (int i = 0; i < elements.length; i = i + 2) {
+ for (int j = 0; j < newEntries.length; j = j + 2) {
+ if (elements[i].equals(newEntries[j])) {
+ total[size++] = elements[i];
+ total[size++] = newEntries[j + 1];
+ continue copy;
+ }
+ }
+ }
+ if (size == total.length) {
+ elements = total;
+ return;
}
+ //trim
+ Object[] newElements = new Object[size];
+ System.arraycopy(total, 0, newElements, 0, size);
+ elements = newElements;
}
/**
* @see Map#remove(java.lang.Object)
*/
- public Object remove(Object key) {
- if (elements == null || count == 0)
+ public synchronized Object remove(Object key) {
+ if (elements.length == 0)
return null;
+
for (int i = 0; i < elements.length; i = i + 2) {
- if (elements[i] != null && elements[i].equals(key)) {
- elements[i] = null;
+ if (elements[i].equals(key)) {
Object result = elements[i + 1];
- elements[i + 1] = null;
- count--;
+ // copy into new array, excluding match
+ Object[] newElements = new Object[elements.length - 2];
+ System.arraycopy(elements, 0, newElements, 0, i);
+ System.arraycopy(elements, i + 1, newElements, i, elements.length - 2);
+ elements = newElements;
return result;
}
}
@@ -261,17 +266,18 @@
* @see Map#size()
*/
public int size() {
- return count;
+ return elements.length / 2;
}
/**
* Creates a new hash map with the same contents as this map.
*/
private HashMap toHashMap() {
- HashMap result = new HashMap(size());
- for (int i = 0; i < elements.length; i = i + 2) {
- if (elements[i] != null) {
- result.put(elements[i], elements[i + 1]);
+ Object[] e = elements;
+ HashMap result = new HashMap(e.length);
+ for (int i = 0; i < e.length; i = i + 2) {
+ if (e[i] != null) {
+ result.put(e[i], e[i + 1]);
}
}
return result;
@@ -285,10 +291,11 @@
* be bound to this map and will not remain in sync with this map.
*/
public Collection values() {
- Set result = new HashSet(size());
- for (int i = 1; i < elements.length; i = i + 2) {
- if (elements[i] != null) {
- result.add(elements[i]);
+ Object[] e = elements;
+ Set result = new HashSet(e.length);
+ for (int i = 1; i < e.length; i = i + 2) {
+ if (e[i] != null) {
+ result.add(e[i]);
}
}
return result;
Index: src/org/eclipse/core/runtime/jobs/Job.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.core.jobs/src/org/eclipse/core/runtime/jobs/Job.java,v
retrieving revision 1.18
diff -u -r1.18 Job.java
--- src/org/eclipse/core/runtime/jobs/Job.java 19 Feb 2010 20:39:59 -0000 1.18
+++ src/org/eclipse/core/runtime/jobs/Job.java 6 Apr 2010 15:23:49 -0000
@@ -533,6 +533,32 @@
}
/**
+ * If the specified key is not already associated with a value, associate it with
+ * the given value. If the supplied value is null
, the property
+ * is removed from this resource.
This is equivalent to + *
+ * if (job.getProperty(key)==null) { + * job.setProperty(key, value); + * return null; + * } else { + * return job.getProperty(key); + * }except that the action is performed atomically. + *
+ * Properties are intended to be used as a caching mechanism + * by ISV plug-ins. They allow key-object associations to be stored with + * a job instance. These key-value associations are maintained in + * memory (at all times), and the information is never discarded automatically. + *
+ * The qualifier part of the property name must be the unique identifier
+ * of the declaring plug-in (e.g. "com.example.plugin"
).
+ * @return The previous value for the key, or null if no key if there was no mapping.
+ * @since 3.5
+ */
+ public Object setPropertyIfAbsent(QualifiedName key, Object value) {
+ return super.setPropertyIfAbsent(key, value);
+ }
+
+ /**
* Sets the scheduling rule to be used when scheduling this job. This method
* must be called before the job is scheduled.
*