jena-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From a...@apache.org
Subject [2/2] jena git commit: JENA-1122: Move RefCountingMap to jena-base/oaj.atlas.lib
Date Sat, 20 Feb 2016 17:50:44 GMT
JENA-1122: Move RefCountingMap to jena-base/oaj.atlas.lib

Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/f38360ab
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/f38360ab
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/f38360ab

Branch: refs/heads/master
Commit: f38360ab910afd4bb9090addc5b56bb10e000db9
Parents: d279db2
Author: Andy Seaborne <andy@apache.org>
Authored: Sat Feb 20 17:42:51 2016 +0000
Committer: Andy Seaborne <andy@apache.org>
Committed: Sat Feb 20 17:42:51 2016 +0000

----------------------------------------------------------------------
 .../apache/jena/atlas/lib/RefCountingMap.java   | 185 +++++++++++++++++++
 .../java/org/apache/jena/atlas/lib/TS_Lib.java  |   1 +
 .../jena/atlas/lib/TestRefCountingMap.java      | 150 +++++++++++++++
 .../org/apache/jena/fuseki/build/TS_Build.java  |   4 +-
 .../jena/fuseki/build/TestRefCountingMap.java   | 150 ---------------
 5 files changed, 337 insertions(+), 153 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/f38360ab/jena-base/src/main/java/org/apache/jena/atlas/lib/RefCountingMap.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/lib/RefCountingMap.java b/jena-base/src/main/java/org/apache/jena/atlas/lib/RefCountingMap.java
new file mode 100644
index 0000000..7aa5e23
--- /dev/null
+++ b/jena-base/src/main/java/org/apache/jena/atlas/lib/RefCountingMap.java
@@ -0,0 +1,185 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.jena.atlas.lib;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * A key -> value 'map' which reference counts entries.  
+ * 
+ * <p>
+ *   The same (key,value) pair can be added to the map several times and then 
+ *   removed several times.  A reference count is incremented for each addition
+ *   and, provided the count is greater than 0, decremented on removal.
+ * <p>
+ * 
+ * <p>
+ *   The pair is removed from the map when a remove decrements the reference count to 0.
+ * </p>
+ * 
+ * <p>
+ *   This class is thread safe.
+ * </p>
+ * 
+ * @param <K>
+ * @param <T>
+ */
+public class RefCountingMap<K, T> {
+	
+	/*
+	 * Uses CountedRef instances which are pairs of an integer and
+	 * and a reference.  These instances are immutable - a new instance
+	 * is created on each increment/decrement operation.  This could
+	 * result in churn in the garbage collector under heavy use.
+	 */
+	protected Map<K, CountedRef<T>> map = new ConcurrentHashMap<>() ;
+	   
+    public RefCountingMap() {}   
+
+    public boolean contains(K key)         { return map.containsKey(key) ; }
+    public Collection<K> keys()            { return map.keySet() ; }
+    public int size()                      { return map.size() ; }
+    public boolean isEmpty()               { return map.isEmpty() ; }
+
+    /** Clear the map of all keys regardless of reference counts. */
+    public void clear()                    { map.clear() ; }
+    
+	public Set<K> keySet()                 { return map.keySet(); }
+	public boolean containsKey(Object key) { return map.containsKey(key); }
+    
+	/**
+	 * Add a key value pair to the map.
+	 * 
+	 * <p>
+	 *   if there is no entry in the map for the key, then a key value pair is added
+	 *   to the map with a reference count of 1.
+	 * </p>
+	 * 
+	 * <p>
+	 *   If there is already an entry in the map for the same key and value,
+	 *   the reference count for that entry is incremented.
+	 * </p>
+	 * 
+	 * <p>
+	 *   if there is an entry in the map for the key, but with a different value,
+	 *   then that entry is replaced with a new entry for the key and value with
+	 *   a reference count of 1.
+	 * </p>
+	 * 
+	 * @param key
+	 * @param value
+	 */
+    public void add(K key, T value) {
+    	// map.compute is atomic
+    	map.compute(key,
+    			(k, v) -> {
+    				int refCount = 1 ;
+    				if (v != null && ( v.getRef().equals(value) ) ) {
+    					refCount = v.getCount() + 1;
+    				}
+    				return new CountedRef<T>(value, refCount );
+    			});
+    }
+    
+    /**
+     * Decrement the reference count for a key, and remove the corresponding entry from the
map
+     * if the result is 0.
+     * 
+     * <p>
+     *   Do nothing if there is no entry in the map corresponding to the key.
+     * </p>
+     * @param key
+     */
+    public void remove(K key) {
+    	// map.compute is atomic
+    	map.compute(key, 
+    			(k, v) -> {
+    				if (v == null)
+    					return null ;
+    				int refCount = v.getCount() - 1 ;
+    				if ( refCount == 0 )
+    				    return null ;
+    				else
+    				    return new CountedRef<T>(v.getRef(), refCount);
+    			});
+    }
+    
+    /**
+     * Remove the entry corresponding to the key from the map completely.
+     * 
+     * <p>
+     *   This method ignores the reference count.
+     * </p>
+     * 
+     * @param key
+     */
+    public void removeAll(K key) {
+    	map.remove(key);
+    }
+
+    /**
+     * Return the reference count for the entry corresponding to a key in the map.
+     * 
+     * <p>
+     *   Returns 0 if there is no entry in the map corresponding to the key.
+     * </p>
+     * @param key
+     * @return the reference count for the entry corresponding to key in the map,
+     *         or 0 if there is no corresponding entry.
+     */
+    public int refCount(K key) { 
+    	CountedRef<T> ref = map.get(key);
+    	if (ref == null) {
+    		return 0 ;
+    	} else {
+    		return ref.getCount();
+    	}
+    }
+
+    /**
+     * Return the value associated with a key in the map.
+     * 
+     * @param key
+     * @return the value associated with the key, or null if there is no such value.
+     */
+	public T get(Object key) {
+		CountedRef<T> ref = map.get(key);
+		if ( ref == null ) return null ;
+		return ref.getRef();
+	}
+
+	/*
+	 * An immutable pair of an integer count and an object reference
+	 */
+    class CountedRef<R> {
+  	    final int refCount;
+	    final R    ref;
+	
+	    CountedRef(R ref, int refCount) {
+	    	this.refCount = refCount;
+	    	this.ref = ref;
+	    }
+	
+	    int getCount()  { return refCount ; }
+	    R   getRef()    { return ref; }
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/f38360ab/jena-base/src/test/java/org/apache/jena/atlas/lib/TS_Lib.java
----------------------------------------------------------------------
diff --git a/jena-base/src/test/java/org/apache/jena/atlas/lib/TS_Lib.java b/jena-base/src/test/java/org/apache/jena/atlas/lib/TS_Lib.java
index 25a05e0..98a4bc5 100644
--- a/jena-base/src/test/java/org/apache/jena/atlas/lib/TS_Lib.java
+++ b/jena-base/src/test/java/org/apache/jena/atlas/lib/TS_Lib.java
@@ -49,6 +49,7 @@ import org.junit.runners.Suite ;
     , TestNumberUtils.class
     , TestDateTimeUtils.class
     , TestCacheSimple.class
+    , TestRefCountingMap.class
 } )
 
 public class TS_Lib

http://git-wip-us.apache.org/repos/asf/jena/blob/f38360ab/jena-base/src/test/java/org/apache/jena/atlas/lib/TestRefCountingMap.java
----------------------------------------------------------------------
diff --git a/jena-base/src/test/java/org/apache/jena/atlas/lib/TestRefCountingMap.java b/jena-base/src/test/java/org/apache/jena/atlas/lib/TestRefCountingMap.java
new file mode 100644
index 0000000..3bc32c3
--- /dev/null
+++ b/jena-base/src/test/java/org/apache/jena/atlas/lib/TestRefCountingMap.java
@@ -0,0 +1,150 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.jena.atlas.lib;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+public class TestRefCountingMap {
+	
+    private static final String key1     = "key1";
+    private static final String value1   = "value1";
+    private static final String value1_1 = "value1_1";
+	
+	private final RefCountingMap<String,String> map = new RefCountingMap<String, String>();
+	public TestRefCountingMap() {} 
+	
+	@Test
+	public void add0() {	
+		assertEquals(0, map.refCount(key1));
+		String result = map.get(key1);
+		assertEquals(null, result);
+	}
+	
+	@Test
+	public void add1() {		
+		map.add(key1, value1);
+		String result = map.get(key1);
+		assertEquals(value1, result);
+		assertEquals(1, map.refCount(key1));
+	}
+	
+	@Test
+	public void add2() {
+	    assertEquals(0, map.refCount(key1)) ;
+		map.add(key1, value1);
+        assertEquals(1, map.refCount(key1)) ;
+		map.add(key1, value1);
+		assertEquals(2, map.refCount(key1)) ;
+	}
+	
+	@Test
+	public void testRemove1() {
+		map.remove(key1);
+		String result = map.get(key1);
+		assertEquals(null, result);
+		assertEquals(0, map.refCount(key1));		
+	}
+	
+    @Test
+    public void add1Remove1() {
+        map.add(key1, value1);
+        assertEquals(1, map.refCount(key1));        
+        map.remove(key1);
+        assertEquals(0, map.refCount(key1));
+        String result = map.get(key1);
+        assertEquals(null, result);
+    }
+
+    @Test
+	public void add2Remove1() {
+		map.add(key1, value1);
+		map.add(key1, value1);
+		map.remove(key1);
+		String result = map.get(key1);
+		assertEquals(value1, result);
+		assertEquals(1, map.refCount(key1));		
+	}
+	
+	@Test
+	public void add2Remove2() {
+		map.add(key1, value1);
+		map.add(key1, value1);
+		map.remove(key1);
+		map.remove(key1);
+		String result = map.get(key1);
+		assertEquals(null, result);
+		assertEquals(0, map.refCount(key1));		
+	}
+	
+	@Test
+	public void add2Remove3() {
+		map.add(key1, value1);
+		map.add(key1, value1);
+		map.remove(key1);
+		map.remove(key1);
+		map.remove(key1);
+		String result = map.get(key1);
+		assertEquals(null, result);
+		assertEquals(0, map.refCount(key1));		
+	}
+	
+	@Test
+	public void add2Remove3Add1() {
+		map.add(key1, value1);
+		map.add(key1, value1);
+		map.remove(key1);
+		map.remove(key1);
+		map.remove(key1);
+		map.add(key1, value1);
+		String result = map.get(key1);
+		assertEquals(value1, result);
+		assertEquals(1, map.refCount(key1));		
+	}
+	
+	@Test
+	public void add1Replace1() {
+		map.add(key1, value1);
+		map.add(key1, value1_1);
+		String result = map.get(key1);
+		assertEquals(value1_1, result);
+		assertEquals(1, map.refCount(key1));		
+	}
+	
+	@Test
+	public void add2ForceRemove() {
+		map.add(key1, value1);
+		map.add(key1, value1);
+		map.removeAll(key1);
+		String result = map.get(key1);
+		assertEquals(null, result);
+		assertEquals(0, map.refCount(key1));		
+	}
+	
+	@Test
+	public void add2DifferentValueObjects() {
+		String value1Copy = new String(value1);
+		map.add(key1, value1);
+		map.add(key1, value1Copy);
+		String result = map.get(key1);
+		assertEquals(value1, result);
+		assertEquals(2, map.refCount(key1));
+	}
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/f38360ab/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/build/TS_Build.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/build/TS_Build.java
b/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/build/TS_Build.java
index 2cf1c9b..6efa6ef 100644
--- a/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/build/TS_Build.java
+++ b/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/build/TS_Build.java
@@ -23,9 +23,7 @@ import org.junit.runners.Suite ;
 
 @RunWith(Suite.class)
 @Suite.SuiteClasses( {
-    TestRefCountingMap.class
-    , TestBuilder.class
-    
+    TestBuilder.class
 })
 
 public class TS_Build {}

http://git-wip-us.apache.org/repos/asf/jena/blob/f38360ab/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/build/TestRefCountingMap.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/build/TestRefCountingMap.java
b/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/build/TestRefCountingMap.java
deleted file mode 100644
index 4c00fb8..0000000
--- a/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/build/TestRefCountingMap.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.apache.jena.fuseki.build;
-
-import static org.junit.Assert.assertEquals;
-
-import org.junit.Test;
-
-public class TestRefCountingMap {
-	
-    private static final String key1     = "key1";
-    private static final String value1   = "value1";
-    private static final String value1_1 = "value1_1";
-	
-	private final RefCountingMap<String,String> map = new RefCountingMap<String, String>();
-	public TestRefCountingMap() {} 
-	
-	@Test
-	public void add0() {	
-		assertEquals(0, map.refCount(key1));
-		String result = map.get(key1);
-		assertEquals(null, result);
-	}
-	
-	@Test
-	public void add1() {		
-		map.add(key1, value1);
-		String result = map.get(key1);
-		assertEquals(value1, result);
-		assertEquals(1, map.refCount(key1));
-	}
-	
-	@Test
-	public void add2() {
-	    assertEquals(0, map.refCount(key1)) ;
-		map.add(key1, value1);
-        assertEquals(1, map.refCount(key1)) ;
-		map.add(key1, value1);
-		assertEquals(2, map.refCount(key1)) ;
-	}
-	
-	@Test
-	public void testRemove1() {
-		map.remove(key1);
-		String result = map.get(key1);
-		assertEquals(null, result);
-		assertEquals(0, map.refCount(key1));		
-	}
-	
-    @Test
-    public void add1Remove1() {
-        map.add(key1, value1);
-        assertEquals(1, map.refCount(key1));        
-        map.remove(key1);
-        assertEquals(0, map.refCount(key1));
-        String result = map.get(key1);
-        assertEquals(null, result);
-    }
-
-    @Test
-	public void add2Remove1() {
-		map.add(key1, value1);
-		map.add(key1, value1);
-		map.remove(key1);
-		String result = map.get(key1);
-		assertEquals(value1, result);
-		assertEquals(1, map.refCount(key1));		
-	}
-	
-	@Test
-	public void add2Remove2() {
-		map.add(key1, value1);
-		map.add(key1, value1);
-		map.remove(key1);
-		map.remove(key1);
-		String result = map.get(key1);
-		assertEquals(null, result);
-		assertEquals(0, map.refCount(key1));		
-	}
-	
-	@Test
-	public void add2Remove3() {
-		map.add(key1, value1);
-		map.add(key1, value1);
-		map.remove(key1);
-		map.remove(key1);
-		map.remove(key1);
-		String result = map.get(key1);
-		assertEquals(null, result);
-		assertEquals(0, map.refCount(key1));		
-	}
-	
-	@Test
-	public void add2Remove3Add1() {
-		map.add(key1, value1);
-		map.add(key1, value1);
-		map.remove(key1);
-		map.remove(key1);
-		map.remove(key1);
-		map.add(key1, value1);
-		String result = map.get(key1);
-		assertEquals(value1, result);
-		assertEquals(1, map.refCount(key1));		
-	}
-	
-	@Test
-	public void add1Replace1() {
-		map.add(key1, value1);
-		map.add(key1, value1_1);
-		String result = map.get(key1);
-		assertEquals(value1_1, result);
-		assertEquals(1, map.refCount(key1));		
-	}
-	
-	@Test
-	public void add2ForceRemove() {
-		map.add(key1, value1);
-		map.add(key1, value1);
-		map.removeAll(key1);
-		String result = map.get(key1);
-		assertEquals(null, result);
-		assertEquals(0, map.refCount(key1));		
-	}
-	
-	@Test
-	public void add2DifferentValueObjects() {
-		String value1Copy = new String(value1);
-		map.add(key1, value1);
-		map.add(key1, value1Copy);
-		String result = map.get(key1);
-		assertEquals(value1, result);
-		assertEquals(2, map.refCount(key1));
-	}
-}


Mime
View raw message