/**
*
* Copyright
* 2009-2015 Jayway Products AB
* 2016-2017 Föreningen Sambruk
*
* Licensed under AGPL, Version 3.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.gnu.org/licenses/agpl.txt
*
* 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 se.streamsource.dci.api;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Map objects to role interfaces. An instance of RoleMap
* is created for each request, and is sent down in the interactions chain
* by InteractionMixin.subContext. Whenever an object is identified in the chain,
* add it to the roleMap.
*
* If an object has been added under a general role, then you can map it to a more specific
* role by using the map() method.
*
* If objects are registered under their generic types, and a role lookup does not match anything, the
* objects will be searched in the reverse insertion order, i.e. last registered object will be checked first.
*/
public class RoleMap
{
private static ThreadLocal<RoleMap> currentRoleMap = new ThreadLocal<RoleMap>();
public static RoleMap current()
{
return currentRoleMap.get();
}
public static <T> T role(Class<T> roleClass)
{
RoleMap map = currentRoleMap.get();
if (map == null)
throw new IllegalStateException("No current role map");
return map.get( roleClass );
}
public static void setCurrentRoleMap(RoleMap roleMap)
{
currentRoleMap.set( roleMap );
}
public static void newCurrentRoleMap()
{
currentRoleMap.set( new RoleMap() );
}
public static void clearCurrentRoleMap()
{
currentRoleMap.remove();
}
private Map<Class, Object> roles = new HashMap<Class, Object>( );
private RoleMap parentRoleMap;
/**
* Create new root roleMap
*/
public RoleMap()
{
}
public RoleMap( RoleMap parentRoleMap )
{
this.parentRoleMap = parentRoleMap;
}
public void set(Object object, Class... roleClasses)
{
if (object == null)
{
for (Class roleClass : roleClasses)
{
roles.remove( roleClass );
}
} else
{
if (roleClasses.length == 0)
{
roles.put( object.getClass(), object );
} else
{
for (Class roleClass : roleClasses)
{
roles.put( roleClass, object );
}
}
}
}
public <T> T get(Class<T> roleClass) throws IllegalArgumentException
{
Object object = roles.get( roleClass );
if (object == null)
{
// If no explicit mapping has been made, see if
// any other mapped objects could work
for (Map.Entry<Class, Object> entry : roles.entrySet())
{
if (roleClass.isAssignableFrom( entry.getKey()))
{
return roleClass.cast( entry.getValue() );
}
}
} else
{
return roleClass.cast( object );
}
if (parentRoleMap == null)
throw new IllegalArgumentException("No object in roleMap for role:"+roleClass.getSimpleName());
else
return parentRoleMap.get( roleClass );
}
/**
* Get all mapped objects, from this and all parent contexts.
*
* @param roleClass role of objects to be returned
* @return list of all objects in all stacked contexts that are mapped to the given role
*/
public <T> List<T> getAll(Class<T> roleClass)
{
List<T> list = new ArrayList<T>( );
getAll0(roleClass, list);
return list;
}
private <T> void getAll0( Class<T> roleClass, List<T> list )
{
for (Object obj : roles.values())
{
if (roleClass.isInstance(obj) && !list.contains(obj))
list.add((T) obj);
}
// Check parent roleMap
if (parentRoleMap != null)
parentRoleMap.getAll0( roleClass, list );
}
public void map(Class fromRoleClass, Class... toRoleClasses)
{
Object object = get(fromRoleClass);
for (Class toRoleClass : toRoleClasses)
{
if (toRoleClass.isInstance( object))
roles.put( toRoleClass, object );
else
throw new IllegalArgumentException(object +" does not implement role type "+toRoleClass.getName());
}
}
}