/*************************************************************************
* Copyright 2009-2016 Eucalyptus Systems, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3 of the License.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
* Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta
* CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need
* additional information or have any questions.
*
* This file may incorporate work covered under the following copyright
* and permission notice:
*
* Software License Agreement (BSD License)
*
* Copyright (c) 2008, Regents of the University of California
* All rights reserved.
*
* Redistribution and use of this software in source and binary forms,
* with or without modification, are permitted provided that the
* following conditions are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. USERS OF THIS SOFTWARE ACKNOWLEDGE
* THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE LICENSED MATERIAL,
* COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS SOFTWARE,
* AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
* IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA,
* SANTA BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY,
* WHICH IN THE REGENTS' DISCRETION MAY INCLUDE, WITHOUT LIMITATION,
* REPLACEMENT OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO
* IDENTIFIED, OR WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT
* NEEDED TO COMPLY WITH ANY SUCH LICENSES OR RIGHTS.
************************************************************************/
package com.eucalyptus.util;
import static com.eucalyptus.util.Parameters.checkParam;
import static org.hamcrest.Matchers.*;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import com.eucalyptus.bootstrap.ServiceJarDiscovery;
import com.eucalyptus.records.EventRecord;
import com.eucalyptus.records.EventType;
import com.eucalyptus.system.Ats;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.SortedSetMultimap;
import com.google.common.collect.TreeMultimap;
public class TypeMappers {
private enum CompareClasses implements Comparator<Class> {
INSTANCE;
@Override
public int compare( Class o1, Class o2 ) {
if ( o1 == null && o2 == null ) {
return 0;
} else if ( o1 != null && o2 != null ) {
return ( "" + o1.toString( ) ).compareTo( "" + o2.toString( ) );
} else {
return ( o1 != null
? 1
: -1 );
}
}
}
private static Logger LOG = Logger.getLogger( TypeMappers.class );
private static SortedSetMultimap<Class, Class> knownMappers = TreeMultimap.create( CompareClasses.INSTANCE, CompareClasses.INSTANCE );
private static Map<String, Function> mappers = Maps.newConcurrentMap();
private static Map<String, java.util.function.Function>
jdkMmappers = Maps.newConcurrentMap();
private static final Joiner keyJoiner = Joiner.on( "=>" );
public static <A, B> B transform( A from, Class<B> to ) {
Class target = from.getClass( );
Function func = lookupUnsafe( target, to );
if ( func == null ) {
for ( Class p : Classes.ancestors( from ) ) {
if ( knownMappers.containsKey( p ) && knownMappers.get( p ).contains( to ) ) {
target = p;
break;
}
}
func = lookup( target, to );
recordMapper( from.getClass( ), to, func );
}
return ( B ) func.apply( from );
}
public static <A, B> Function<A, B> lookup( Class<A> a, Class<B> b ) {
final Function<A,B> mapper = lookupUnsafe( a, b );
if ( mapper == null ) {
checkParam( knownMappers.keySet(), hasItem( a ) );
checkParam( knownMappers.get( a ), hasItem( b ) );
}
return mapper;
}
public static <A, B> java.util.function.Function<A, B> lookupF( Class<A> a, Class<B> b ) {
final String key = key( a, b );
java.util.function.Function<A, B> mapperF = jdkMmappers.get( key );
if ( mapperF == null ) {
final Function<A,B> mapper = lookup( a, b );
mapperF = mapper::apply;
jdkMmappers.putIfAbsent( key, mapperF );
}
return mapperF;
}
private static <A, B> Function<A, B> lookupUnsafe( Class<A> a, Class<B> b ) {
final String key = key( a, b );
return mappers.get( key );
}
public static class TypeMapperDiscovery extends ServiceJarDiscovery {
@Override
public boolean processClass( Class candidate ) throws Exception {
if ( Ats.from( candidate ).has( TypeMapper.class ) && Function.class.isAssignableFrom( candidate ) ) {
TypeMapper mapper = Ats.from( candidate ).get( TypeMapper.class );
Class[] types = mapper.value( );
List<Class> generics = Lists.newArrayList( );
try {
generics.addAll( Classes.genericsToClasses( Classes.newInstance( candidate ) ) );
} catch ( Exception ex ) {
LOG.error( ex, ex );
}
if ( generics.size( ) != 2 ) {
LOG.error( candidate + " looks like it is a @TypeMapper but needs generics: "
+ generics );
return false;
} else {
try {
registerMapper( generics.get( 0 ), generics.get( 1 ), ( Function ) Classes.newInstance( candidate ) );
return true;
} catch ( Exception ex1 ) {
LOG.error( "Error registering type mapper: " + candidate, ex1 );
}
}
}
return false;
}
@Override
public Double getPriority( ) {
return 0.3d;
}
}
private static String key( final Class<?> from, Class<?> to ) {
return keyJoiner.join( from, to );
}
private static void registerMapper( Class from, Class to, Function mapper ) {
EventRecord.here( TypeMapperDiscovery.class, EventType.BOOTSTRAP_INIT_DISCOVERY, "mapper", from.getCanonicalName( ), to.getCanonicalName( ),
mapper.getClass( ).getCanonicalName( ) ).info( );
String key = key( from, to );
checkParam( knownMappers.get( from ), not( hasItem( to ) ) );
checkParam( mappers, not( hasKey( key ) ) );
knownMappers.put( from, to );
recordMapper( from, to, mapper );
}
private static void recordMapper( Class from, Class to, Function mapper ) {
String key = key( from, to );
mappers.put( key, mapper );
}
}