package org.codefx.libfx.collection.transform; import java.util.HashSet; import java.util.Set; /** * Demonstrates how to use the {@link TransformingSet}. * <p> * The demonstrated example is based on the situation that a {@link Set} of {@link String}s which only ever contains * natural numbers as character sequences is to be represented as a {@code Set} of {@link Integer}s. * <p> * This is not entirely trivial as leading zeros allow multiple strings to be mapped to the same integer which will make * the transformation function non-inverse. */ public class TransformingSetDemo { // #begin FIELDS /** * The set of strings which will be the inner/transformed set. */ private final Set<String> innerSet; /** * A view on the set which uses integers instead. */ private final Set<Integer> transformingSet; // #end FIELDS // #begin CONSTRUCTION, MAIN & TRANSFORMATION /** * Creates a new demo. */ public TransformingSetDemo() { innerSet = new HashSet<>(); transformingSet = TransformingCollectionBuilder .forInnerAndOuterType(String.class, Integer.class) .toOuter(this::stringToInteger) .toInner(this::integerToString) .transformSet(innerSet); print("-- Initial state --"); print("\t -> " + innerSet + " ~ " + transformingSet); print(); } private Integer stringToInteger(String string) { return Integer.parseInt(string); } private String integerToString(Integer integer) { return integer.toString(); } /** * Runs this demo. * * @param args * command line arguments (will not be used) */ public static void main(String[] args) { print("Outputs are written as 'Modification -> innerSet.toString ~ transformingSet.toString'".toUpperCase()); print(); TransformingSetDemo demo = new TransformingSetDemo(); demo.modifyingInnerSet(); demo.modifyingTransformedSet(); demo.breakingInverseFunctions(); demo.typeSafety(); } // #end CONSTRUCTION, MAIN & TRANSFORMATION // #begin DEMOS private void modifyingInnerSet() { print("-- Modifying the inner set --"); print("Insert '0', '1', '2'"); innerSet.add("0"); innerSet.add("1"); innerSet.add("2"); print("\t -> " + innerSet + " ~ " + transformingSet); print("Remove '1'"); innerSet.remove("1"); print("\t -> " + innerSet + " ~ " + transformingSet); print("Insert '10', '11', '12'"); innerSet.add("10"); innerSet.add("11"); innerSet.add("12"); print("\t -> " + innerSet + " ~ " + transformingSet); print("Remove strings ending in '0'"); innerSet.removeIf(string -> string.endsWith("0")); print("\t -> " + innerSet + " ~ " + transformingSet); print("Clear"); innerSet.clear(); print("\t -> " + innerSet + " ~ " + transformingSet); print(); } private void modifyingTransformedSet() { print("-- Modifying the transforming set --"); print("Insert 0, 1, 2"); transformingSet.add(0); transformingSet.add(1); transformingSet.add(2); print("\t -> " + innerSet + " ~ " + transformingSet); print("Remove 1"); transformingSet.remove(1); print("\t -> " + innerSet + " ~ " + transformingSet); print("Insert 10, 11, 12"); transformingSet.add(10); transformingSet.add(11); transformingSet.add(12); print("\t -> " + innerSet + " ~ " + transformingSet); print("Remove odd numbers"); transformingSet.removeIf(integer -> integer % 2 != 0); print("\t -> " + innerSet + " ~ " + transformingSet); print("Clear"); transformingSet.clear(); print("\t -> " + innerSet + " ~ " + transformingSet); print(); } private void breakingInverseFunctions() { print("-- Breaking the contract with non-inverse functions --"); print("The functions are non-inverse:"); String leadingZeroTenString = "010"; Integer ten = stringToInteger(leadingZeroTenString); String tenString = integerToString(ten); print("\t " + leadingZeroTenString + " -toInteger-> " + ten + " -toString-> " + tenString); print("This can be used to create unexpected behavior..."); print(); print("Insert '010' into inner set"); innerSet.add("010"); print("\t -> " + innerSet + " ~ " + transformingSet); print("Insert '10' into inner set"); innerSet.add("10"); print("\t -> " + innerSet + " ~ " + transformingSet); print("Sizes of different sets"); print("\t inner set: " + innerSet.size()); print("\t transforming set: " + transformingSet.size()); print("\t new set: " + new HashSet<>(transformingSet).size() + " (!)"); print("Now try to remove the value from the transforming set:"); print("\t before: " + transformingSet); print("\t remove 10 (returns " + transformingSet.remove(10) + ") -> " + transformingSet); print("\t remove 10 (returns " + transformingSet.remove(10) + ") -> " + transformingSet); print("\t Damn it!"); print("The transforming set does not contain its own elements:"); print("\t 'transformingSet.contains(transformingSet.iterator().next())' -> " + transformingSet.contains(transformingSet.iterator().next())); print("Clear"); innerSet.clear(); print("\t -> " + innerSet + " ~ " + transformingSet); print(); } private void typeSafety() { print("-- Using type tokens to increase type safety --"); Set<Integer> transformingSetWithoutTokens = TransformingCollectionBuilder .<String, Integer> forInnerAndOuterTypeUnknown() .toOuter(this::stringToInteger) .toInner(this::integerToString) .transformSet(innerSet); print("Insert 0, 1, 2"); transformingSet.add(0); transformingSet.add(1); transformingSet.add(2); print("\t -> " + innerSet + " ~ " + transformingSet + " ~ " + transformingSetWithoutTokens); print("Calling contains with an 'Object o'"); Object o = new Object(); print("\t 'innerSet.contains(o)': " + innerSet.contains(o)); print("\t 'transformingSet.contains(o)': " + transformingSet.contains(o)); try { print("\t 'transformingSetWithoutTokens.contains(o)': " + transformingSetWithoutTokens.contains(o)); } catch (ClassCastException ex) { print("\t 'transformingSetWithoutTokens.contains(o)': CLASS CAST EXEPTION"); } } // #end DEMOS private static void print() { System.out.println(); } private static void print(String text) { System.out.println(text); } }