/*
* Copyright (C) 2005-2012 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco 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.
*
* Alfresco 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see .
*/
package org.alfresco.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import junit.framework.TestCase;
/**
* Tests {@link ValueProtectingMap}
*
* @author Derek Hulley
* @since 3.4.9
* @since 4.0.1
*/
public class ValueProtectingMapTest extends TestCase
{
private static Set> moreImmutableClasses;
static
{
moreImmutableClasses = new HashSet>(13);
moreImmutableClasses.add(TestImmutable.class);
}
/**
* A class that is immutable
*/
@SuppressWarnings("serial")
private static class TestImmutable implements Serializable
{
}
/**
* A class that is mutable
*/
@SuppressWarnings("serial")
private static class TestMutable extends TestImmutable
{
public int i = 0;
public void increment()
{
i++;
}
@Override
public boolean equals(Object obj)
{
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
TestMutable other = (TestMutable) obj;
if (i != other.i) return false;
return true;
}
}
private List valueList;
private Map valueMap;
private Date valueDate;
private TestImmutable valueImmutable;
private TestMutable valueMutable;
private ValueProtectingMap map;
private Map holyMap;
@Override
protected void setUp() throws Exception
{
valueList = new ArrayList(4);
valueList.add("ONE");
valueList.add("TWO");
valueList.add("THREE");
valueList.add("FOUR");
valueList = Collections.unmodifiableList(valueList);
valueMap = new HashMap(5);
valueMap.put("ONE", "ONE");
valueMap.put("TWO", "TWO");
valueMap.put("THREE", "THREE");
valueMap.put("FOUR", "FOUR");
valueMap = Collections.unmodifiableMap(valueMap);
valueDate = new Date();
valueImmutable = new TestImmutable();
valueMutable = new TestMutable();
holyMap = new HashMap();
holyMap.put("DATE", valueDate);
holyMap.put("LIST", (Serializable) valueList);
holyMap.put("MAP", (Serializable) valueMap);
holyMap.put("IMMUTABLE", valueImmutable);
holyMap.put("MUTABLE", valueMutable);
// Now wrap our 'holy' map so that it cannot be modified
holyMap = Collections.unmodifiableMap(holyMap);
map = new ValueProtectingMap(holyMap, moreImmutableClasses);
}
/**
* Make sure that NOTHING has changed in our 'holy' map
*/
private void checkMaps(boolean expectMapClone)
{
assertEquals("Holy map size is wrong: ", 5, holyMap.size());
// Note that the immutability of the maps and lists means that we don't need
// to check every value within the lists and maps
if (expectMapClone)
{
// Make sure that the holy map has been released
assertTrue("Expect holy map to have been released: ", map.getProtectedMap() != holyMap);
// Do some updates to the backing map and ensure that they stick
Map mapClone = map.getProtectedMap();
mapClone.put("ONE", "ONE");
assertEquals("Modified the backing directly but value is not visible: ", map.get("ONE"), "ONE");
map.put("TWO", "TWO");
assertTrue("Backing map was changed again!", mapClone == map.getProtectedMap());
mapClone.containsKey("TWO");
}
else
{
// Make sure that the holy map is still acting as the backing map
assertTrue("Expect holy map to still be in use: ", map.getProtectedMap() == holyMap);
}
}
public void testSetup()
{
checkMaps(false);
}
/**
* No matter how many times we wrap instances in instances, the backing map must remain
* the same.
*/
public void testMapWrapping()
{
ValueProtectingMap mapTwo = new ValueProtectingMap(map);
assertTrue("Backing map must be shared: ", mapTwo.getProtectedMap() == map.getProtectedMap());
ValueProtectingMap mapThree = new ValueProtectingMap(map);
assertTrue("Backing map must be shared: ", mapThree.getProtectedMap() == map.getProtectedMap());
}
public void testMapClear()
{
map.clear();
assertEquals("Map should be empty: ", 0, map.size());
checkMaps(true);
}
public void testMapContainsKey()
{
assertTrue(map.containsKey("LIST"));
assertFalse(map.containsKey("LISTXXX"));
checkMaps(false);
}
public void testMapContainsValue()
{
assertTrue(map.containsValue(valueMutable));
assertFalse(map.containsValue("Dassie"));
checkMaps(false);
}
public void testMapEntrySet()
{
map.entrySet();
checkMaps(true);
}
/**
* Ensures that single, immutable values are given out as-is and
* without affecting the backing storage
*/
public void testMapGetImmutable()
{
assertTrue("Immutable value instance incorrect", map.get("IMMUTABLE") == valueImmutable);
checkMaps(false);
}
/**
* Ensures that single, immutable values are cloned before being given out
* without affecting the backing storage
*/
public void testMapGetMutable()
{
TestMutable mutable = (TestMutable) map.get("MUTABLE");
assertFalse("Mutable value instance incorrect", mutable == valueMutable);
checkMaps(false);
// Modify the instance
mutable.increment();
assertEquals("Backing mutable should not have changed: ", 0, valueMutable.i);
}
public void testMapIsEmpty()
{
assertFalse(map.isEmpty());
checkMaps(false);
}
public void testMapKeySet()
{
map.keySet();
checkMaps(true);
}
public void testMapPut()
{
map.put("ANOTHER", "VALUE");
checkMaps(true);
}
public void testMapPutAll()
{
map.putAll(holyMap);
checkMaps(true);
}
@SuppressWarnings("unchecked")
public void testSerializability() throws Exception
{
map.put("MORE", "STUFF");
checkMaps(true);
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
ObjectOutputStream os = new ObjectOutputStream(baos);
os.writeObject(map);
os.close();
// Read it back in
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
ValueProtectingMap reloadedMap = (ValueProtectingMap) ois.readObject();
ois.close();
// Make sure it has the value
assertEquals("Reloaded object not same.", "STUFF", reloadedMap.get("MORE"));
}
}