From 24f70c6048ddcd145edc201b0c13e3d06fd3707a Mon Sep 17 00:00:00 2001 From: brinn <brinn> Date: Sun, 7 Nov 2010 17:35:49 +0000 Subject: [PATCH] add: support for multiple keys to TableMapNonUniqueKey (merged from cifex, 10.06.x) SVN: 18569 --- .../collections/IMultiKeyExtractor.java | 32 ++++++++ .../collections/TableMapNonUniqueKey.java | 82 ++++++++++++++----- .../collections/TableMapNonUniqueKeyTest.java | 33 +++++++- 3 files changed, 124 insertions(+), 23 deletions(-) create mode 100644 common/source/java/ch/systemsx/cisd/common/collections/IMultiKeyExtractor.java diff --git a/common/source/java/ch/systemsx/cisd/common/collections/IMultiKeyExtractor.java b/common/source/java/ch/systemsx/cisd/common/collections/IMultiKeyExtractor.java new file mode 100644 index 00000000000..3ab68b5cfc7 --- /dev/null +++ b/common/source/java/ch/systemsx/cisd/common/collections/IMultiKeyExtractor.java @@ -0,0 +1,32 @@ +/* + * Copyright 2007 ETH Zuerich, CISD + * + * 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 ch.systemsx.cisd.common.collections; + +import java.util.Collection; + +/** + * Interface defining the role of a key extractor when an entity can have multiple keys. + * + * @author Franz-Josef Elmer + */ +public interface IMultiKeyExtractor<K, E> +{ + /** + * Returns the keys of type <code>K</code> from an entity <code>E</code>. + */ + public Collection<K> getKey(E e); +} diff --git a/common/source/java/ch/systemsx/cisd/common/collections/TableMapNonUniqueKey.java b/common/source/java/ch/systemsx/cisd/common/collections/TableMapNonUniqueKey.java index bf657c1b6aa..68e3895233e 100644 --- a/common/source/java/ch/systemsx/cisd/common/collections/TableMapNonUniqueKey.java +++ b/common/source/java/ch/systemsx/cisd/common/collections/TableMapNonUniqueKey.java @@ -16,6 +16,8 @@ package ch.systemsx.cisd.common.collections; +import java.util.Collection; +import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -58,7 +60,7 @@ public class TableMapNonUniqueKey<K, E> implements Iterable<E> private final Map<K, Set<E>> map = new LinkedHashMap<K, Set<E>>(); - private final IKeyExtractor<K, E> extractor; + private final IMultiKeyExtractor<K, E> extractor; private final UniqueValueViolationStrategy uniqueValueViolationStrategy; @@ -74,6 +76,18 @@ public class TableMapNonUniqueKey<K, E> implements Iterable<E> this(rows, extractor, UniqueValueViolationStrategy.ERROR); } + /** + * Creates a new instance for the specified rows and key extractor. + * + * @param rows Collection of rows of type <code>E</code>. + * @param extractor Strategy to extract a key of type <code>E</code> for an object of type + * <code>E</code>. + */ + public TableMapNonUniqueKey(final Iterable<E> rows, final IMultiKeyExtractor<K, E> extractor) + { + this(rows, extractor, UniqueValueViolationStrategy.ERROR); + } + /** * Creates a new instance for the specified rows and key extractor. * @@ -83,6 +97,33 @@ public class TableMapNonUniqueKey<K, E> implements Iterable<E> */ public TableMapNonUniqueKey(final Iterable<E> rows, final IKeyExtractor<K, E> extractor, UniqueValueViolationStrategy uniqueValueViolationStrategy) + { + assert rows != null : "Unspecified collection of rows."; + assert extractor != null : "Unspecified key extractor."; + assert uniqueValueViolationStrategy != null : "Unspecified unique value violation strategy."; + this.extractor = new IMultiKeyExtractor<K, E>() + { + public Collection<K> getKey(E e) + { + return Collections.singleton(extractor.getKey(e)); + } + }; + this.uniqueValueViolationStrategy = uniqueValueViolationStrategy; + for (final E row : rows) + { + add(row); + } + } + + /** + * Creates a new instance for the specified rows and key extractor. + * + * @param rows Collection of rows of type <code>E</code>. + * @param extractor Strategy to extract a key of type <code>E</code> for an object of type + * <code>E</code>. + */ + public TableMapNonUniqueKey(final Iterable<E> rows, final IMultiKeyExtractor<K, E> extractor, + UniqueValueViolationStrategy uniqueValueViolationStrategy) { assert rows != null : "Unspecified collection of rows."; assert extractor != null : "Unspecified key extractor."; @@ -115,25 +156,28 @@ public class TableMapNonUniqueKey<K, E> implements Iterable<E> */ public final void add(final E row) throws UniqueValueViolationException { - final K key = extractor.getKey(row); - Set<E> set = map.get(key); - if (set == null) - { - set = new LinkedHashSet<E>(); - map.put(key, set); - set.add(row); - } else if (uniqueValueViolationStrategy == UniqueValueViolationStrategy.KEEP_FIRST - || set.contains(row) == false) - { - set.add(row); - } else if (uniqueValueViolationStrategy == UniqueValueViolationStrategy.KEEP_LAST) + final Collection<K> keys = extractor.getKey(row); + for (K key : keys) { - set.remove(row); - set.add(row); - } else if (uniqueValueViolationStrategy == UniqueValueViolationStrategy.ERROR) - { - throw new UniqueValueViolationException("Row '" + row.toString() - + "' already stored in the map."); + Set<E> set = map.get(key); + if (set == null) + { + set = new LinkedHashSet<E>(); + map.put(key, set); + set.add(row); + } else if (uniqueValueViolationStrategy == UniqueValueViolationStrategy.KEEP_FIRST + || set.contains(row) == false) + { + set.add(row); + } else if (uniqueValueViolationStrategy == UniqueValueViolationStrategy.KEEP_LAST) + { + set.remove(row); + set.add(row); + } else if (uniqueValueViolationStrategy == UniqueValueViolationStrategy.ERROR) + { + throw new UniqueValueViolationException("Row '" + row.toString() + + "' already stored in the map."); + } } } diff --git a/common/sourceTest/java/ch/systemsx/cisd/common/collections/TableMapNonUniqueKeyTest.java b/common/sourceTest/java/ch/systemsx/cisd/common/collections/TableMapNonUniqueKeyTest.java index 0a6f190d19b..5ba87233c9f 100644 --- a/common/sourceTest/java/ch/systemsx/cisd/common/collections/TableMapNonUniqueKeyTest.java +++ b/common/sourceTest/java/ch/systemsx/cisd/common/collections/TableMapNonUniqueKeyTest.java @@ -16,13 +16,16 @@ package ch.systemsx.cisd.common.collections; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import static org.testng.AssertJUnit.*; +import org.apache.commons.lang.StringUtils; import org.testng.annotations.Test; /** @@ -33,17 +36,24 @@ import org.testng.annotations.Test; public class TableMapNonUniqueKeyTest { - final IKeyExtractor<Integer, String> integerExtractor = new IKeyExtractor<Integer, String>() + final IMultiKeyExtractor<Integer, String> integerExtractor = new IMultiKeyExtractor<Integer, String>() { - public Integer getKey(String e) + public Collection<Integer> getKey(String e) { final int i = e.indexOf(':'); if (i >= 0) { - return Integer.parseInt(e.substring(i + 1)); + String intStr = e.substring(i + 1); + String[] intStrs = StringUtils.split(intStr, ','); + Collection<Integer> ints = new ArrayList<Integer>(intStrs.length); + for (String inStr : intStrs) + { + ints.add(Integer.parseInt(inStr)); + } + return ints; } else { - return Integer.parseInt(e); + return Collections.singleton(Integer.parseInt(e)); } } }; @@ -146,4 +156,19 @@ public class TableMapNonUniqueKeyTest .tryGet(42)); } + @Test + public void testTryGetNonUniqueMultiKey() + { + final TableMapNonUniqueKey<Integer, String> tableMap = + new TableMapNonUniqueKey<Integer, String>(Arrays.asList("a:42,8", "7", "b:42", "0", + "b:7", "c:42"), integerExtractor); + assertNull(tableMap.tryGet(10)); + assertEquals(Collections.singleton("0"), tableMap.tryGet(0)); + assertEquals(new HashSet<String>(Arrays.asList("7", "b:7")), tableMap.tryGet(7)); + assertEquals(new HashSet<String>(Arrays.asList("a:42,8", "b:42", "c:42")), tableMap + .tryGet(42)); + assertEquals(new HashSet<String>(Arrays.asList("a:42,8")), tableMap + .tryGet(8)); + } + } -- GitLab