You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2011/04/19 02:18:20 UTC
svn commit: r1094823 - in /tapestry/tapestry5/trunk/tapestry-core/src:
main/java/org/apache/tapestry5/internal/util/NamedSet.java
test/groovy/org/apache/tapestry5/internal/util/
test/groovy/org/apache/tapestry5/internal/util/NamedSetTests.groovy
Author: hlship
Date: Tue Apr 19 00:18:20 2011
New Revision: 1094823
URL: http://svn.apache.org/viewvc?rev=1094823&view=rev
Log:
TAP5-1508: Add a simple and (hopefully) efficient NamedSet
Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/NamedSet.java
tapestry/tapestry5/trunk/tapestry-core/src/test/groovy/org/apache/tapestry5/internal/util/
tapestry/tapestry5/trunk/tapestry-core/src/test/groovy/org/apache/tapestry5/internal/util/NamedSetTests.groovy
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/NamedSet.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/NamedSet.java?rev=1094823&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/NamedSet.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/util/NamedSet.java Tue Apr 19 00:18:20 2011
@@ -0,0 +1,133 @@
+// Copyright 2011 The Apache Software Foundation
+//
+// 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.apache.tapestry5.internal.util;
+
+import java.util.Set;
+
+import org.apache.commons.collections.map.CaseInsensitiveMap;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+
+/**
+ * Simple, thread-safe associative array that relates a name to a value. Names are case-insensitive.
+ * This is optimized to use less memory (than a {@link CaseInsensitiveMap} (it uses a singly-liked list),
+ * though the cost of a lookup is more expensive. However, this is a good match against many of the structures inside
+ * a page instance, where most lookups occur only during page constructions, and the number of values is often small.
+ * <p>
+ * We use simply synchronization, as uncontested synchronized locks are very, very cheap.
+ *
+ * @param <T>
+ * the type of value stored
+ */
+public class NamedSet<T>
+{
+ private NamedRef<T> first;
+
+ private static class NamedRef<T>
+ {
+ NamedRef<T> next;
+
+ String name;
+
+ T value;
+
+ public NamedRef(String name, T value)
+ {
+ this.name = name;
+ this.value = value;
+ }
+ }
+
+ /**
+ * Returns a set of the names of all stored values.
+ */
+ public synchronized Set<String> getNames()
+ {
+ Set<String> result = CollectionFactory.newSet();
+
+ NamedRef<T> cursor = first;
+
+ while (cursor != null)
+ {
+ result.add(cursor.name);
+ cursor = cursor.next;
+ }
+
+ return result;
+ }
+
+ /**
+ * Gets the value for the provided name.
+ *
+ * @param name
+ * used to locate the value
+ * @return the value, or null if not found
+ */
+ public synchronized T get(String name)
+ {
+ NamedRef<T> cursor = first;
+
+ while (cursor != null)
+ {
+ if (cursor.name.equalsIgnoreCase(name)) { return cursor.value; }
+
+ cursor = cursor.next;
+ }
+
+ return null;
+ }
+
+ /**
+ * Stores a new value into the set, replacing any previous value with the same name. Name comparisons are case
+ * insensitive.
+ *
+ * @param name
+ * to store the value. May not be blank.
+ * @param newValue
+ * non-null value to store
+ */
+ public synchronized void put(String name, T newValue)
+ {
+ assert InternalUtils.isNonBlank(name);
+ assert newValue != null;
+
+ NamedRef<T> prev = null;
+ NamedRef<T> cursor = first;
+
+ while (cursor != null)
+ {
+ if (cursor.name.equalsIgnoreCase(name))
+ {
+ // Retain the case of the name as put(), even if it doesn't match
+ // the existing case
+
+ cursor.name = name;
+ cursor.value = newValue;
+
+ return;
+ }
+
+ prev = cursor;
+ cursor = cursor.next;
+ }
+
+ NamedRef<T> newRef = new NamedRef<T>(name, newValue);
+
+ if (prev == null)
+ first = newRef;
+ else
+ prev.next = newRef;
+ }
+}
Added: tapestry/tapestry5/trunk/tapestry-core/src/test/groovy/org/apache/tapestry5/internal/util/NamedSetTests.groovy
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/groovy/org/apache/tapestry5/internal/util/NamedSetTests.groovy?rev=1094823&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/groovy/org/apache/tapestry5/internal/util/NamedSetTests.groovy (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/groovy/org/apache/tapestry5/internal/util/NamedSetTests.groovy Tue Apr 19 00:18:20 2011
@@ -0,0 +1,66 @@
+// Copyright 2011 The Apache Software Foundation
+//
+// 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.apache.tapestry5.internal.util
+
+import org.testng.Assert
+import org.testng.annotations.Test
+
+class NamedSetTests extends Assert
+{
+ @Test
+ void empty_set_yields_rempty_names() {
+ assert new NamedSet().names.empty
+ }
+
+ @Test
+ void stored_names_retain_case() {
+ NamedSet ns = new NamedSet()
+
+ ns.put "Fred", 100
+ ns.put "Barney", 200
+
+ assert ns.get("fred") == 100
+ assert ns.get("barney") == 200
+
+ assert ns.names.sort() == ["Barney", "Fred"]
+ }
+
+ @Test
+ void replace_a_named_value() {
+ NamedSet ns = new NamedSet()
+
+ ns.put "Fred", 100
+ ns.put "Barney", 200
+
+ assert ns.get("fred") == 100
+ assert ns.get("barney") == 200
+
+ ns.put "FRED", 110
+ ns.put "barNEY", 120
+
+ assert ns.get("fred") == 110
+ assert ns.get("barney") == 120
+
+ assert ns.names.sort() == ["FRED", "barNEY"]
+ }
+
+ @Test
+ void missing_key_returns_null() {
+ NamedSet ns = new NamedSet()
+
+
+ assert ns.get("anything") == null
+ }
+}