[Schmitzm-commits] r2011 - in trunk/schmitzm-core/src/main/java/de/schmitzm: lang regex

scm-commit at wald.intevation.org scm-commit at wald.intevation.org
Sat May 26 22:06:33 CEST 2012


Author: alfonx
Date: 2012-05-26 22:06:33 +0200 (Sat, 26 May 2012)
New Revision: 2011

Added:
   trunk/schmitzm-core/src/main/java/de/schmitzm/lang/LimitedConcurrentHashMap.java
   trunk/schmitzm-core/src/main/java/de/schmitzm/lang/TimedConcurrentHashMap.java
Modified:
   trunk/schmitzm-core/src/main/java/de/schmitzm/lang/TimedHashMap.java
   trunk/schmitzm-core/src/main/java/de/schmitzm/regex/RegexCache.java
Log:
Created a TimedConcurrentHashMap and a LimitedConcurrentHashMap and used it everywhere in Immobrain


Added: trunk/schmitzm-core/src/main/java/de/schmitzm/lang/LimitedConcurrentHashMap.java
===================================================================
--- trunk/schmitzm-core/src/main/java/de/schmitzm/lang/LimitedConcurrentHashMap.java	                        (rev 0)
+++ trunk/schmitzm-core/src/main/java/de/schmitzm/lang/LimitedConcurrentHashMap.java	2012-05-26 20:06:33 UTC (rev 2011)
@@ -0,0 +1,329 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Martin O. J. Schmitz.
+ * 
+ * This file is part of the SCHMITZM library - a collection of utility 
+ * classes based on Java 1.6, focusing (not only) on Java Swing 
+ * and the Geotools library.
+ * 
+ * The SCHMITZM project is hosted at:
+ * http://wald.intevation.org/projects/schmitzm/
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public License (license.txt)
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * or try this link: http://www.gnu.org/licenses/lgpl.html
+ * 
+ * Contributors:
+ *     Martin O. J. Schmitz - initial API and implementation
+ *     Stefan A. Tzeggai - additional utility classes
+ ******************************************************************************/
+package de.schmitzm.lang;
+
+import java.util.Map;
+import java.util.Vector;
+import java.util.concurrent.ConcurrentHashMap;
+
+import de.schmitzm.data.event.AbstractObjectTraceable;
+import de.schmitzm.data.event.GeneralObjectChangeEvent;
+import de.schmitzm.data.event.Invoker;
+import de.schmitzm.data.event.ObjectChangeEvent;
+import de.schmitzm.data.event.ObjectEvent;
+import de.schmitzm.data.event.ObjectListener;
+import de.schmitzm.data.event.ObjectTraceable;
+
+/**
+ * This class represents a {@link ConcurrentHashMap} which is limited to a number of
+ * elements. When the maximum number of elements is exceeded, the "oldest"
+ * element is removed.<br>
+ * 
+ * @author <a href="mailto:martin.schmitz at koeln.de">Martin Schmitz</a>
+ *         (University of Bonn/Germany)
+ * @version 1.0
+ */
+public class LimitedConcurrentHashMap<K, V> extends ConcurrentHashMap<K, V> implements
+		ObjectTraceable {
+	private static final long serialVersionUID = -565382699338290630L;
+
+	/**
+	 * Defines which element is removed when the maximum number of elements is
+	 * exceeded.
+	 */
+	public enum TRUNC_METHOD {
+		/**
+		 * The oldest element in the map (with the oldest {@code put(.)}-call)
+		 * is removed.
+		 */
+		OLDEST_PUT,
+		/**
+		 * The element with the oldest accessed (oldest {@code get(.)}-call) is
+		 * removed.
+		 */
+		OLDEST_GET
+	}
+
+	/**
+	 * Defines the behavior to truncate the map when the maximum number of
+	 * elements is exceeded.
+	 */
+	protected TRUNC_METHOD truncMethod = null;
+	/** Holds the maximum count of elements, the map can hold. */
+	protected int maxLoad = 0;
+	/** Holds the keys of the map in the order of age. */
+	protected Vector<K> oldestKeys = new Vector<K>();
+
+	// Einfache Implementierung von ObjectTraceable, um die Methoden der
+	// Listener-Verwaltung nicht doppelt implementieren zu muessen
+	private AbstractObjectTraceable objectTraceableProxy = new AbstractObjectTraceable() {
+	};
+
+	/**
+	 * Creates an empty map using the {@link TRUNC_METHOD#OLDEST_GET} method to
+	 * remove objects when map load is exceeded
+	 * 
+	 * @param maxLoad
+	 *            max. number of elements the map can hold
+	 */
+	public LimitedConcurrentHashMap(int maxLoad) {
+		this(maxLoad, TRUNC_METHOD.OLDEST_GET);
+	}
+
+	/**
+	 * Creates an empty map.
+	 * 
+	 * @param maxLoad
+	 *            max. number of elements the map can hold
+	 * @param truncMethod
+	 *            method applied if the maximum map load is exceeded
+	 */
+	public LimitedConcurrentHashMap(int maxLoad, TRUNC_METHOD truncMethod) {
+		super(maxLoad);
+		this.truncMethod = truncMethod;
+		setMaxLoad(maxLoad);
+	}
+
+	/**
+	 * Returns the maximum number of elements, the map can hold.
+	 */
+	public int getMaxLoad() {
+		return maxLoad;
+	}
+
+	/**
+	 * Sets the maximum number of elements, the map can hold. If this number is
+	 * exceeded, the oldest element is removed.
+	 * 
+	 * @param maxLoad
+	 *            maximum number of elements, the map can hold
+	 * @see TRUNC_METHOD
+	 */
+	public void setMaxLoad(int maxLoad) {
+		this.maxLoad = maxLoad;
+		truncate();
+	}
+
+	/**
+	 * Returns the behavior when truncating surplus elements.
+	 */
+	public TRUNC_METHOD getTruncMethod() {
+		return this.truncMethod;
+	}
+
+	/**
+	 * Removes the oldest element from the map until the maximum element count
+	 * is reached.
+	 */
+	protected void truncate() {
+		while (size() > maxLoad) {
+			// determine the oldest element key (and remove it)
+			K oldestKey = oldestKeys.remove(0);
+			// remove element from map
+			remove(oldestKey);
+		}
+	}
+
+	/**
+	 * Von Stefan Tzeggai nachgepflegt: Bestimmt nicht optimal, aber hauptsache nicht ganz am LImited-Algorithmus
+	 * vorbei.
+	 */
+	@Override
+	public void putAll(Map<? extends K, ? extends V> m) {
+		for (K key : m.keySet()) {
+			put(key, m.get(key));
+		}
+	}
+
+	// /////////////////////////////////////////////////////////////////
+	// /////////////// Ueberschreiben von Vector ///////////////////
+	// /////////////////////////////////////////////////////////////////
+	/**
+	 * Adds an element to the map and removes all surplus elements. An
+	 * {@link ObjectChangeEvent} is initiated if the map changes.
+	 * 
+	 * @param key
+	 *            element key
+	 * @param value
+	 *            element value
+	 * @return the former value mapped to {@code key} ({@code null} if there was
+	 *         no such element)
+	 */
+	@Override
+	public V put(K key, V value) {
+		// int oldSize = size();
+		V oldValue = super.put(key, value);
+
+		// truncate the map before updating the oldest list, so that
+		// the new element is ignored in this truncate
+		truncate();
+
+		// update the "oldest" list
+		switch (truncMethod) {
+		// OLDEST_PUT: the new element is the youngest, so add the key of the
+		// new element at the end of the oldest list
+		case OLDEST_PUT:
+			oldestKeys.add(key);
+			break;
+		// MS-01.sc: Adding the new element to the top of the oldest-list causes
+		// that
+		// it might be removed immediately on the next put/truncate!
+		// Although this would be consequent, this is not practical.
+		// Instead we put the new element at the end of the oldest-list
+		// so that the element stays in the map at least "for a while".
+		// // OLDEST_GET: the new element is not yet accessed by get, so add the
+		// key
+		// // at the front of the oldest list
+		// case OLDEST_GET: oldestKeys.add(0,key);
+		// break;
+		// OLDEST_GET: to keep the new element in the map at least
+		// "for a while",
+		// it is added at the end of the oldest-list (like OLDEST_PUT)
+		case OLDEST_GET:
+			oldestKeys.add(key);
+			break;
+		// MS-01.ec
+		}
+
+		// fire an event if the map has changed
+		if (oldValue != value)
+			fireEvent(new ObjectChangeEvent(new Invoker(this), oldValue, value));
+
+		return oldValue;
+	}
+
+	/**
+	 * Returns an element of the map.
+	 * 
+	 * @param key
+	 *            element key
+	 */
+	@Override
+	public V get(Object key) {
+		V value = super.get(key);
+
+		// update the oldest list if the truncate method is OLDEST_GET
+		if (oldestKeys.contains(key)
+				&& TRUNC_METHOD.OLDEST_GET.equals(truncMethod)) {
+			oldestKeys.remove(key); // remove from current position
+			oldestKeys.add((K) key); // add to the end
+		}
+
+		return value;
+	}
+
+	/**
+	 * Removes an element from the map and fires an {@link ObjectChangeEvent},
+	 * if the map has changed.
+	 * 
+	 * @param key
+	 *            element key
+	 * @return the former value mapped to {@code key} ({@code null} if there was
+	 *         no such element)
+	 */
+	@Override
+	public V remove(Object key) {
+		// remove element from map
+		V oldValue = super.remove(key);
+		// remove key from oldest list
+		oldestKeys.remove(key);
+
+		// fire change event if the list has changed
+		if (oldValue != null)
+			fireEvent(new ObjectChangeEvent(new Invoker(this), oldValue, null));
+
+		return oldValue;
+	}
+
+	/**
+	 * Clears the map and fires an {@link GeneralObjectChangeEvent} if the list
+	 * changes.
+	 */
+	@Override
+	public void clear() {
+		if (isEmpty())
+			return;
+		// clear the map
+		super.clear();
+		// clear the oldest list
+		oldestKeys.clear();
+
+		fireEvent(new GeneralObjectChangeEvent(this));
+	}
+
+	// /////////////////////////////////////////////////////////////////
+	// ////////// Implementierung von ObjectTraceable //////////////
+	// /////////////////////////////////////////////////////////////////
+	/**
+	 * Adds a listener to the map, which is informed on map changes.
+	 */
+	public void addObjectListener(ObjectListener listener) {
+		this.objectTraceableProxy.addObjectListener(listener);
+	}
+
+	/**
+	 * Removes a listener from the map.
+	 */
+	public void removeObjectListener(ObjectListener listener) {
+		this.objectTraceableProxy.removeObjectListener(listener);
+	}
+
+	/**
+	 * Checks, whether a listener is already connected to the map.
+	 */
+	public boolean containsObjectListener(ObjectListener l) {
+		return this.objectTraceableProxy.containsObjectListener(l);
+	}
+
+	/**
+	 * Returns all listener of a special type.
+	 * 
+	 * @param type
+	 *            kind of listener (filter)
+	 */
+	public ObjectListener[] getObjectListener(Class type) {
+		return this.objectTraceableProxy.getObjectListener(type);
+	}
+
+	/**
+	 * Fires an event to all connected listeners.
+	 */
+	public void fireEvent(ObjectEvent e) {
+		this.objectTraceableProxy.fireEvent(e);
+	}
+
+	/**
+	 * Fires an event to all listeners of a special type.
+	 */
+	public void fireEvent(ObjectEvent e, Class c) {
+		this.objectTraceableProxy.fireEvent(e, c);
+	}
+
+}


Property changes on: trunk/schmitzm-core/src/main/java/de/schmitzm/lang/LimitedConcurrentHashMap.java
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Added: trunk/schmitzm-core/src/main/java/de/schmitzm/lang/TimedConcurrentHashMap.java
===================================================================
--- trunk/schmitzm-core/src/main/java/de/schmitzm/lang/TimedConcurrentHashMap.java	                        (rev 0)
+++ trunk/schmitzm-core/src/main/java/de/schmitzm/lang/TimedConcurrentHashMap.java	2012-05-26 20:06:33 UTC (rev 2011)
@@ -0,0 +1,129 @@
+package de.schmitzm.lang;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.swing.Timer;
+
+/**
+ * Erweiterung der {@link LimitedHashMap}. Erlaubt es in Millisekunden anzugeben, wie lange ein Eintrag gültig ist.
+ * Diese Klasse ist eine Kopie von {@link TimedHashMap}, extended aber nicht {@link HashMap}, sondern
+ * {@link ConcurrentHashMap}.
+ */
+public class TimedConcurrentHashMap<K, V> extends ConcurrentHashMap<K, V> {
+
+	private static final long serialVersionUID = -175821101775531256L;
+
+	private final int timeout;
+
+	HashMap<K, Long> inputTimes = new HashMap<K, Long>();
+
+	Timer cleanTask;
+
+	/**
+	 * Erweiterung der {@link LimitedHashMap}. Erlaubt es in Millisekunden anzugeben, wie lange ein Eintrag gültig ist.
+	 * 
+	 * @param timeout
+	 *            Anz. ms die Einträge gültig sind.
+	 */
+	public TimedConcurrentHashMap(Number timeout) {
+		this.timeout = timeout.intValue();
+	}
+
+	@Override
+	public void putAll(Map<? extends K, ? extends V> m) {
+		final Long now = Long.valueOf(System.currentTimeMillis());
+		super.putAll(m);
+		for (K k : m.keySet()) {
+			inputTimes.put(k, now);
+		}
+	}
+
+	@Override
+	public V remove(Object key) {
+		inputTimes.remove(key);
+		return super.remove(key);
+	}
+
+	@Override
+	synchronized public V put(K key, V value) {
+
+		if (timeout > 0) {
+
+			// Timeour ms Nach dem letzten put die komplette hashmap leeren.
+			if (cleanTask != null) {
+				cleanTask.stop();
+			}
+
+			cleanTask = new Timer(timeout + 1, new ActionListener() {
+
+				@Override
+				public void actionPerformed(ActionEvent arg0) {
+					clear();
+					inputTimes.clear();
+					if (cleanTask != null) {
+						cleanTask.stop();
+						cleanTask = null;
+					}
+				}
+
+			});
+			cleanTask.setInitialDelay(timeout);
+			cleanTask.setRepeats(false);
+			cleanTask.start();
+
+			inputTimes.put(key, Long.valueOf(System.currentTimeMillis()));
+			return super.put(key, value);
+		} else
+			return null;
+	}
+
+	@Override
+	public V get(Object key) {
+
+		// Bei einem Timeout von 0 wird der Wert nie gespeichert
+		if (timeout == 0)
+			return null;
+
+		V v = super.get(key);
+		if (v != null) {
+			Long inputTime = inputTimes.get(key);
+			// ??? Wie kann inputTime null sein? Aber ich hatte hier schon Ex,
+			// es geht?!
+			if (inputTime == null) {
+				inputTime = System.currentTimeMillis();
+				inputTimes.put((K) key, inputTime);
+			}
+			if ((System.currentTimeMillis() - inputTime) >= timeout) {
+				remove(key);
+				inputTimes.remove(key);
+				return null;
+			}
+		}
+		return v;
+	}
+
+	/**
+	 * Liefert das eingestelte Timeout
+	 * 
+	 * @return
+	 */
+	public int getTimeout() {
+		return timeout;
+	}
+
+	@Override
+	public boolean containsKey(Object key) {
+		return get(key) != null;
+	}
+
+	@Override
+	public void clear() {
+		super.clear();
+		if (cleanTask != null)
+			cleanTask.stop();
+	}
+}


Property changes on: trunk/schmitzm-core/src/main/java/de/schmitzm/lang/TimedConcurrentHashMap.java
___________________________________________________________________
Added: svn:mime-type
   + text/plain

Modified: trunk/schmitzm-core/src/main/java/de/schmitzm/lang/TimedHashMap.java
===================================================================
--- trunk/schmitzm-core/src/main/java/de/schmitzm/lang/TimedHashMap.java	2012-05-26 19:28:30 UTC (rev 2010)
+++ trunk/schmitzm-core/src/main/java/de/schmitzm/lang/TimedHashMap.java	2012-05-26 20:06:33 UTC (rev 2011)
@@ -12,6 +12,8 @@
  */
 public class TimedHashMap<K, V> extends HashMap<K, V> {
 
+	private static final long serialVersionUID = -1758211041775531256L;
+
 	private final int timeout;
 
 	HashMap<K, Long> inputTimes = new HashMap<K, Long>();
@@ -19,7 +21,7 @@
 	Timer cleanTask;
 
 	/**
-	 * Erweiterung der {@link LimitedHashMap}. Erlaubt es in Millisekunden anzugeben, wie lange ein Eintrag gültig ist.
+	 * Erweiterung der {@link HashMap}. Erlaubt es in Millisekunden anzugeben, wie lange ein Eintrag gültig ist.
 	 * 
 	 * @param timeout
 	 *            Anz. ms die Einträge gültig sind.

Modified: trunk/schmitzm-core/src/main/java/de/schmitzm/regex/RegexCache.java
===================================================================
--- trunk/schmitzm-core/src/main/java/de/schmitzm/regex/RegexCache.java	2012-05-26 19:28:30 UTC (rev 2010)
+++ trunk/schmitzm-core/src/main/java/de/schmitzm/regex/RegexCache.java	2012-05-26 20:06:33 UTC (rev 2011)
@@ -1,6 +1,5 @@
 package de.schmitzm.regex;
 
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
@@ -8,8 +7,8 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import de.schmitzm.lang.LimitedHashMap;
-import de.schmitzm.lang.LimitedHashMap.TRUNC_METHOD;
+import de.schmitzm.lang.LimitedConcurrentHashMap;
+import de.schmitzm.lang.LimitedConcurrentHashMap.TRUNC_METHOD;
 
 /**
  * Cached compilierte Pattern und auch Ergebnisse von RegExes. Use the matchers methods to obtain cached result and add
@@ -48,8 +47,9 @@
 		return singletonInstance != null ? singletonInstance : new RegexCache();
 	}
 
-	private final LimitedHashMap<Pattern, LimitedHashMap<String, MyMatchResult>> matchers = new LimitedHashMap<Pattern, LimitedHashMap<String, MyMatchResult>>(
-			CACHE_VALUES_TO_RESULT_SIZE, LimitedHashMap.TRUNC_METHOD.OLDEST_GET);
+	private final LimitedConcurrentHashMap<Pattern, LimitedConcurrentHashMap<String, MyMatchResult>> matchers = new LimitedConcurrentHashMap<Pattern, LimitedConcurrentHashMap<String, MyMatchResult>>(
+			CACHE_VALUES_TO_RESULT_SIZE, LimitedConcurrentHashMap.TRUNC_METHOD.OLDEST_GET);
+	
 	private final HashMap<String, Pattern> patterns = new HashMap<String, Pattern>();
 
 	private RegexCache() {
@@ -88,12 +88,12 @@
 		}
 	}
 
-//	final static Map<String, ThreadLocal<Matcher>> matchersCachedThreadLocalPerRegex = Collections
-//			.synchronizedMap(new LimitedHashMap<String, ThreadLocal<Matcher>>(10000, TRUNC_METHOD.OLDEST_GET));
+	// final static Map<String, ThreadLocal<Matcher>> matchersCachedThreadLocalPerRegex = Collections
+	// .synchronizedMap(new LimitedHashMap<String, ThreadLocal<Matcher>>(10000, TRUNC_METHOD.OLDEST_GET));
 
-	final static Map<String, ThreadLocal<Matcher>> matchersCachedThreadLocalPerRegex = new ConcurrentHashMap<String, ThreadLocal<Matcher>>(10000);
+	final static Map<String, ThreadLocal<Matcher>> matchersCachedThreadLocalPerRegex = new ConcurrentHashMap<String, ThreadLocal<Matcher>>(
+			10000);
 
-	
 	/**
 	 * Diese Methode ist gedacht um die Erstellung von Matcher-Objekten in der JVM zu reduzieren. Es für bis zu 10000
 	 * RegEx ein Cache verwaltet. Jeder dieser Caches ist ein ThreadLocal-Cache von Matchern. Somit liefert diese Methde
@@ -122,8 +122,7 @@
 
 		return threadLocal.get().reset(text);
 	}
-	
-	
+
 	/**
 	 * Diese Methode ist gedacht um die Erstellung von Matcher-Objekten in der JVM zu reduzieren. Es für bis zu 10000
 	 * RegEx ein Cache verwaltet. Jeder dieser Caches ist ein ThreadLocal-Cache von Matchern. Somit liefert diese Methde
@@ -131,30 +130,29 @@
 	 * Die Matcher werden für die Regex gecached. Wird ein Matcher für eine gecachte Regex mit einem anderen TEXT
 	 * angeforderd, wird zuerst {@link Matcher#reset(CharSequence)} ausgeführt.
 	 */
-//	public Matcher getMatcher(final String regex, final String text) {
-//		ThreadLocal<Matcher> threadLocal = matchersCachedThreadLocalPerRegex.get(regex);
-//
-//		synchronized (regex) {
-//
-//			if (threadLocal == null) {
-//
-//				threadLocal = new ThreadLocal<Matcher>() {
-//
-//					@Override
-//					protected Matcher initialValue() {
-//						return getPattern(regex).matcher(text);
-//					}
-//				};
-//				matchersCachedThreadLocalPerRegex.put(regex, threadLocal);
-//			}
-//
-//		}
-//
-//		return threadLocal.get().reset(text);
-		
-//		return getPattern(regex).matcher(text);
-//	}
+	// public Matcher getMatcher(final String regex, final String text) {
+	// ThreadLocal<Matcher> threadLocal = matchersCachedThreadLocalPerRegex.get(regex);
+	//
+	// synchronized (regex) {
+	//
+	// if (threadLocal == null) {
+	//
+	// threadLocal = new ThreadLocal<Matcher>() {
+	//
+	// @Override
+	// protected Matcher initialValue() {
+	// return getPattern(regex).matcher(text);
+	// }
+	// };
+	// matchersCachedThreadLocalPerRegex.put(regex, threadLocal);
+	// }
+	//
+	// }
+	//
+	// return threadLocal.get().reset(text);
 
+	// return getPattern(regex).matcher(text);
+	// }
 
 	/**
 	 * Will throw java exceptions when pattern won't compile.
@@ -176,12 +174,12 @@
 
 		final Pattern pattern = getPattern(regex);
 
-		LimitedHashMap<String, MyMatchResult> m;
+		LimitedConcurrentHashMap<String, MyMatchResult> m;
 		synchronized (matchers) {
 			m = matchers.get(pattern);
 			if (m == null) {
-				matchers.put(pattern, m = new LimitedHashMap<String, MyMatchResult>(CACHE_VALUES_TO_RESULT_SIZE,
-						TRUNC_METHOD.OLDEST_GET));
+				matchers.put(pattern, m = new LimitedConcurrentHashMap<String, MyMatchResult>(
+						CACHE_VALUES_TO_RESULT_SIZE, TRUNC_METHOD.OLDEST_GET));
 			}
 		}
 



More information about the Schmitzm-commits mailing list