diff options
-rw-r--r-- | src/library/scala/collection/JavaConversions.scala | 71 | ||||
-rw-r--r-- | src/library/scala/collection/mutable/ConcurrentMap.scala | 79 |
2 files changed, 148 insertions, 2 deletions
diff --git a/src/library/scala/collection/JavaConversions.scala b/src/library/scala/collection/JavaConversions.scala index 7d05227336..7af138067b 100644 --- a/src/library/scala/collection/JavaConversions.scala +++ b/src/library/scala/collection/JavaConversions.scala @@ -179,10 +179,17 @@ object JavaConversions { * @return A Java <code>Map</code> view of the argument. */ implicit def asMap[A, B](m : mutable.Map[A, B])(implicit ma : ClassManifest[A]) : ju.Map[A, B] = m match { + //case JConcurrentMapWrapper(wrapped) => wrapped case JMapWrapper(wrapped) => wrapped case _ => new MutableMapWrapper(m)(ma) } + implicit def asConcurrentMap[A, B](m: mutable.ConcurrentMap[A, B]) + (implicit ma: ClassManifest[A], mb: ClassManifest[B]): juc.ConcurrentMap[A, B] = m match { + case JConcurrentMapWrapper(wrapped) => wrapped + case _ => new ConcurrentMapWrapper(m)(ma, mb) + } + // Java => Scala /** @@ -304,11 +311,26 @@ object JavaConversions { * @return A Scala mutable <code>Map</code> view of the argument. */ implicit def asMap[A, B](m : ju.Map[A, B]) = m match { + //case ConcurrentMapWrapper(wrapped) => wrapped case MutableMapWrapper(wrapped) => wrapped case _ => new JMapWrapper(m) } + /** + * Implicitly converts a Java <code>ConcurrentMap</code> to a Scala mutable <code>ConcurrentMap</code>. + * The returned Scala <code>ConcurrentMap</code> is backed by the provided Java + * <code>ConcurrentMap</code> and any side-effects of using it via the Scala interface will + * be visible via the Java interface and vice versa. + * <p> + * If the Java <code>ConcurrentMap</code> was previously obtained from an implicit or + * explicit call of <code>asConcurrentMap(scala.collection.mutable.ConcurrentMap)</code> then the original + * Scala <code>ConcurrentMap</code> will be returned. + * + * @param m The <code>ConcurrentMap</code> to be converted. + * @return A Scala mutable <code>ConcurrrentMap</code> view of the argument. + */ implicit def asConcurrentMap[A, B](m: juc.ConcurrentMap[A, B]) = m match { + case ConcurrentMapWrapper(wrapped) => wrapped case _ => new JConcurrentMapWrapper(m) } @@ -419,7 +441,8 @@ object JavaConversions { override def empty = JSetWrapper(new ju.HashSet[A]) } - case class MutableMapWrapper[A, B](underlying : mutable.Map[A, B])(m : ClassManifest[A]) extends ju.AbstractMap[A, B] { + abstract class MutableMapWrapperLike[A, B](underlying: mutable.Map[A, B])(m: ClassManifest[A]) + extends ju.AbstractMap[A, B] { self => override def size = underlying.size @@ -471,6 +494,9 @@ object JavaConversions { } } + case class MutableMapWrapper[A, B](underlying : mutable.Map[A, B])(m : ClassManifest[A]) + extends MutableMapWrapperLike[A, B](underlying)(m) + abstract class JMapWrapperLike[A, B, +Repr <: mutable.MapLike[A, B, Repr] with mutable.Map[A, B]] (underlying: ju.Map[A, B]) extends mutable.Map[A, B] with mutable.MapLike[A, B, Repr] { @@ -517,8 +543,49 @@ object JavaConversions { override def empty = JMapWrapper(new ju.HashMap[A, B]) } + case class ConcurrentMapWrapper[A, B](underlying: mutable.ConcurrentMap[A, B]) + (m: ClassManifest[A], mv: ClassManifest[B]) + extends MutableMapWrapperLike[A, B](underlying)(m) with juc.ConcurrentMap[A, B] { + self => + + override def remove(k : AnyRef) = { + if (!m.erasure.isInstance(k)) + null.asInstanceOf[B] + else { + val k1 = k.asInstanceOf[A] + underlying.remove(k1) match { + case Some(v) => v + case None => null.asInstanceOf[B] + } + } + } + + def putIfAbsent(k: A, v: B) = underlying.putIfAbsent(k, v) match { + case Some(v) => v + case None => null.asInstanceOf[B] + } + + def remove(k: AnyRef, v: AnyRef) = { + if (!m.erasure.isInstance(k) || !mv.erasure.isInstance(v)) + false + else { + val k1 = k.asInstanceOf[A] + val v1 = v.asInstanceOf[B] + underlying.remove(k1, v1) + } + } + + def replace(k: A, v: B): B = underlying.replace(k, v) match { + case Some(v) => v + case None => null.asInstanceOf[B] + } + + def replace(k: A, oldval: B, newval: B) = underlying.replace(k, oldval, newval) + + } + case class JConcurrentMapWrapper[A, B](underlying: juc.ConcurrentMap[A, B]) - extends JMapWrapperLike[A, B, JConcurrentMapWrapper[A, B]](underlying) { + extends JMapWrapperLike[A, B, JConcurrentMapWrapper[A, B]](underlying) with mutable.ConcurrentMap[A, B] { override def get(k: A) = { val v = underlying.get(k) if (v != null) Some(v) diff --git a/src/library/scala/collection/mutable/ConcurrentMap.scala b/src/library/scala/collection/mutable/ConcurrentMap.scala new file mode 100644 index 0000000000..d09bf57e1b --- /dev/null +++ b/src/library/scala/collection/mutable/ConcurrentMap.scala @@ -0,0 +1,79 @@ +package scala.collection.mutable + + + + + + +/** + * A template trait for mutable maps that allow concurrent access. + * $concurrentmapinfo + * + * @tparam A the key type of the map + * @tparam B the value type of the map + * + * @define concurrentmapinfo + * This is a base trait for all Scala concurrent map implementations. It + * provides all of the methods a Map does, with the difference that all the + * changes are atomic. It also describes methods specific to concurrent maps. + * Note: The concurrent maps do not accept `null` for keys or values. + * + * @define atomicop + * This is done atomically. + * + * @since 2.8 + */ +trait ConcurrentMap[A, B] extends Map[A, B] { + + /** + * Associates the given key with a given value, unless the key was already associated with some other value. + * $atomicop + * + * @param k key with which the specified value is to be associated with + * @param v value to be associated with the specified key + * @return `Some(oldvalue)` if there was a value `oldvalue` previously associated with the + * specified key, or `None` if there was no mapping for the specified key + */ + def putIfAbsent(k: A, v: B): Option[B] + + /** + * Removes the entry for the specified key if its currently mapped to the specified value. + * $atomicop + * + * @param k key for which the entry should be removed + * @param v value expected to be associated with the specified key if the removal is to take place + * @return `true` if the removal took place, `false` otherwise + */ + def remove(k: A, v: B): Boolean + + /** + * Replaces the entry for the given key only if it was previously mapped to a given value. + * $atomicop + * + * @param k key for which the entry should be replaced + * @param oldvalue value expected to be associated with the specified key if replacing is to happen + * @param newvalue value to be associated with the specified key + * @return `true` if the entry was replaced, `false` otherwise + */ + def replace(k: A, oldvalue: B, newvalue: B): Boolean + + /** + * Replaces the entry for the given key only if it was previously mapped to some value. + * $atomicop + * + * @param k key for which the entry should be replaced + * @param v value to be associated with the specified key + * @return `Some(v)` if the given key was previously mapped to some value `v`, or `None` otherwise + */ + def replace(k: A, v: B): Option[B] + +} + + + + + + + + + |