/*
 * Copyright (c) 2007, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Distribution License v. 1.0, which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

package org.glassfish.gmbal.impl ;

import org.glassfish.gmbal.impl.trace.TraceRuntime;
import org.glassfish.gmbal.impl.trace.TraceRegistrationFine;
import org.glassfish.pfl.tf.spi.annotation.InfoMethod;
import org.glassfish.gmbal.impl.trace.TraceRegistration;
import org.glassfish.pfl.basic.algorithm.DumpToString;
import org.glassfish.pfl.basic.contain.Pair;
import org.glassfish.pfl.basic.facet.FacetAccessor;
import org.glassfish.pfl.basic.algorithm.Algorithms;
import org.glassfish.pfl.basic.func.UnaryPredicate;

import java.lang.reflect.Array ;
import java.lang.reflect.Constructor ;
import java.lang.reflect.InvocationTargetException;

import java.util.Collection ;
import java.util.List ;
import java.util.ArrayList ;
import java.util.Map ;
import java.util.HashMap ;
import java.util.Iterator ;
import java.util.Enumeration ;
import java.util.Dictionary ;
import java.util.Arrays;

import javax.management.ObjectName ;
import javax.management.JMException;

import javax.management.openmbean.ArrayType ;
import javax.management.openmbean.OpenType ;
import javax.management.openmbean.OpenDataException ;
import javax.management.openmbean.SimpleType ;
import javax.management.openmbean.TabularType ;
import javax.management.openmbean.CompositeType ;
import javax.management.openmbean.CompositeData ;
import javax.management.openmbean.CompositeDataSupport ;
import javax.management.openmbean.TabularData ;
import javax.management.openmbean.TabularDataSupport ;

import org.glassfish.gmbal.AMXClient;
import org.glassfish.gmbal.ManagedObject ;
import org.glassfish.gmbal.ManagedData ;
import org.glassfish.gmbal.typelib.EvaluatedArrayType;
import org.glassfish.gmbal.typelib.EvaluatedClassAnalyzer;
import org.glassfish.gmbal.typelib.EvaluatedClassDeclaration;
import org.glassfish.gmbal.typelib.EvaluatedMethodDeclaration;
import org.glassfish.gmbal.typelib.EvaluatedType;

import static org.glassfish.gmbal.typelib.EvaluatedType.* ;

A ManagedEntity is one of the pre-defined Open MBean types: SimpleType, ObjectName, TabularData, or CompositeData.
/** A ManagedEntity is one of the pre-defined Open MBean types: SimpleType, * ObjectName, TabularData, or CompositeData. */
@TraceRuntime @TraceRegistration @TraceRegistrationFine public abstract class TypeConverterImpl implements TypeConverter { private static final Map<EvaluatedType,OpenType> simpleTypeMap = new HashMap<EvaluatedType,OpenType>() ; private static final Map<OpenType,EvaluatedClassDeclaration> simpleOpenTypeMap = new HashMap<OpenType,EvaluatedClassDeclaration>() ; // Map each type in types to otype in simpleTypeMap. // Map otype to the first type in types in simpleOpenTypeMap. private static void initMaps( final OpenType otype, final EvaluatedClassDeclaration... types ) { boolean first = true ; for (EvaluatedClassDeclaration type : types) { simpleTypeMap.put( type, otype ) ; if (first) { first = false ; simpleOpenTypeMap.put( otype, type ) ; } } } static { initMaps( SimpleType.BOOLEAN, EBOOLEANW, EBOOLEAN ) ; initMaps( SimpleType.CHARACTER, ECHARW, ECHAR ) ; initMaps( SimpleType.INTEGER, EINTW, EINT ) ; initMaps( SimpleType.SHORT, ESHORTW, ESHORT ) ; initMaps( SimpleType.LONG, ELONGW, ELONG ) ; initMaps( SimpleType.BYTE, EBYTEW, EBYTE ) ; initMaps( SimpleType.FLOAT, EFLOATW, EFLOAT ) ; initMaps( SimpleType.DOUBLE, EDOUBLEW, EDOUBLE ) ; initMaps( SimpleType.STRING, ESTRING ) ; initMaps( SimpleType.VOID, EVOID ) ; initMaps( SimpleType.DATE, EDATE ) ; initMaps( SimpleType.OBJECTNAME, EOBJECT_NAME ) ; initMaps( SimpleType.BIGDECIMAL, EBIG_DECIMAL ) ; initMaps( SimpleType.BIGINTEGER, EBIG_INTEGER ) ; } public static Class getJavaClass( final OpenType ot ) { if (ot instanceof SimpleType) { final SimpleType st = (SimpleType)ot ; return simpleOpenTypeMap.get( st ).cls() ; } else if (ot instanceof ArrayType) { // This code is rather odd. We need to get the opentype of the // array components, convert that to a java type, and then // construct a Java type (Class) that has that java type as its // component (java) type. I think the only way to do this // is to call Array.newInstance, and then take the class from the // resulting array instance. final ArrayType at = (ArrayType)ot ; final OpenType cot = at.getElementOpenType() ; final Class cjt = getJavaClass( cot ) ; final Object temp = Array.newInstance( cjt, 0 ) ; return temp.getClass() ; } else if (ot instanceof TabularType) { return TabularData.class ; } else if (ot instanceof CompositeType) { return CompositeData.class ; } else { throw Exceptions.self.unsupportedOpenType( ot ) ; } } // Make sure that EINT is replaced with EINTW, etc. // We only want wrapped types for primitives. private static EvaluatedType canonicalType( EvaluatedType et ) { OpenType ot = simpleTypeMap.get( et ) ; if (ot == null) { return et ; } else { return simpleOpenTypeMap.get( ot ) ; } } public static Class getJavaClass( final EvaluatedType type ) { if (type instanceof EvaluatedClassDeclaration) { return ((EvaluatedClassDeclaration)type).cls() ; } else if (type instanceof EvaluatedArrayType) { // Same trick as above. final EvaluatedArrayType gat = (EvaluatedArrayType)type ; final EvaluatedType ctype = canonicalType( gat.componentType() ) ; final Class cclass = getJavaClass( ctype ) ; final Object temp = Array.newInstance( cclass, 0 ) ; return temp.getClass() ; } else { throw Exceptions.self.cannotConvertToJavaType(type) ; } } /* Type mapping rules for OT : Type -> OpenType: * <pre> * Java Type Open Type * ----------------------------------------- * boolean, Boolean BOOLEAN * char, Character CHARACTER * byte, Byte BYTE * short, Short SHORT * int, Integer INTEGER * long, LONG LONG * float, Float FLOAT * double, Double DOUBLE * String STRING * void VOID * java.util.Date DATE * javax.management.ObjectName OBJECTNAME * java.math.BigDecimal BIGDECIMAL * java.math.BigInteger BIGINTEGER * (all of the above types have the same JT and OT representation. * * Enumeration type String (only valid values are keywords) * * @ManagedObject ObjectName * * @ManagedData CompositeType( * @ManagedData.name, * @ManagedData.description, * ALL @ManagedAttribute ID, (extracted from the method * name) * ALL @ManagedAttribute.description, * ALL @ManagedAttribute OT(Type) ) (extracted from the * method attribute * TYPE) * We also need to include @IncludeSubclass and * @InheritedAttribute(s) attributes * Also note that @InheritTable adds an attribute to the * class. * The inherited table is mapped to an array. * The InheritTable annotation specifies a class X, which must * be part of the Type C<X>, where C is a subclass of * Collection, Iterable, Iterator, or Enumerable. * JT -> OT: invoke attribute methods, collect results, * construct CompositeDataSupport * OT -> JT: NOT SUPPORTED (for now) * (can only be supported in Collection case) * * C<X>, C a subtype of Collection, Iterator, Iterable, or Enumeration * Mapped to array data containing OT(X) * JT -> OT: Construct ArrayType of type OT(X), * fill it with all JT elements as mapped to their OT type * OT -> JT: NOT SUPPORTED * (unless CompositeData -> Java type is supported) * What about SortedSet? * * M<K,V>, M a subtype of Map or Dictionary * Mapped to tabular data containing id="key" OT(K) as * the key and id="value" OT(V) * XXX What about SortedMap? * * X[] OT(X)[] * As for CompositeData, we will mostly treat this as * readonly. Mappings same as C<X> case. * * TypeVariable * WildCardType Both need to be supported. The basic idea is that * the upper bounds give us information on what interfaces * must be implemented by by an instance of the type * variable. This in turn tells us what * subclasses identified by @IncludeSubclass should be * used when mapping the actual data to a CompositeData * instance. * Not supported: lower bounds (what would that mean? * Do I need it?) * multiple upper bounds (I don't think I * need this?) * Multiple upper bounds PROBABLY means the intersection of * the included subclasses for all of the bounds * * Other String * JT -> OT: use toString * OT -> JT: requires a <init>(String) constructor * * Note that the CompositeData OpenType->JavaType mapping will be handled * the same way as in MXBeans. * XXX This is not yet implemented! */ @TraceRegistration public static TypeConverter makeTypeConverter( final EvaluatedType type, final ManagedObjectManagerInternal mom ) { TypeConverter result = null ; try { final OpenType stype = simpleTypeMap.get( type ) ; if (stype != null) { result = handleSimpleType( type, stype ) ; } else if (type instanceof EvaluatedClassDeclaration) { EvaluatedClassDeclaration cls = (EvaluatedClassDeclaration)type ; final ManagedObject mo = mom.getFirstAnnotationOnClass( cls, ManagedObject.class ) ; final ManagedData md = mom.getFirstAnnotationOnClass( cls, ManagedData.class ) ; if (mo != null) { result = handleManagedObject( cls, mom, mo ) ; } else if (md != null) { result = handleManagedData( cls, mom, md ) ; } else if (cls.cls().isEnum()) { result = handleEnum( cls ) ; } else { result = handleClass( cls, mom ) ; } } else if (type instanceof EvaluatedArrayType) { result = handleArrayType( (EvaluatedArrayType)type, mom ) ; } else { // this should not happen throw new IllegalArgumentException( "Unknown kind of Type " + type ) ; } } catch (RuntimeException exc) { throw exc ; } catch (OpenDataException exc) { throw new RuntimeException( exc ) ; } return result ; } private static TypeConverter handleSimpleType( final EvaluatedType type, final OpenType stype ) { final EvaluatedType canType = canonicalType( type ) ; return new TypeConverterImpl( type, stype ) { public Object toManagedEntity( final Object obj ) { return obj ; } @Override public Object fromManagedEntity( final Object entity ) { return entity ; } @Override public boolean isIdentity() { return canType.equals( type ) ; } } ; } public static final String NULL_STRING = "<NULL>" ; @TraceRegistration private static TypeConverter handleManagedObject( final EvaluatedClassDeclaration type, final ManagedObjectManagerInternal mom, final ManagedObject mo ) { TypeConverter result = null ; result = new TypeConverterImpl( type, SimpleType.OBJECTNAME ) { public Object toManagedEntity( Object obj ) { if (obj == null) { return AMXClient.NULL_OBJECTNAME ; } else { return mom.getObjectName( obj ) ; } } @Override public Object fromManagedEntity( final Object entity ) { if (!(entity instanceof ObjectName)) { throw Exceptions.self.entityNotObjectName( entity ) ; } final ObjectName oname = (ObjectName)entity ; if (oname.equals( AMXClient.NULL_OBJECTNAME )) { return null ; } else { return mom.getObject( oname ) ; } } } ; return result ; } @TraceRegistration private static Collection<AttributeDescriptor> analyzeManagedData( final EvaluatedClassDeclaration cls, final ManagedObjectManagerInternal mom ) { Collection<AttributeDescriptor> result = null ; final EvaluatedClassAnalyzer ca = mom.getClassAnalyzer( cls, ManagedData.class ).second() ; final Pair<Map<String,AttributeDescriptor>, Map<String,AttributeDescriptor>> ainfos = mom.getAttributes( ca, ManagedObjectManagerInternal.AttributeDescriptorType .COMPOSITE_DATA_ATTR ) ; result = ainfos.first().values() ; return result ; } // XXX need to make tf support static info methods @InfoMethod private static void describe( String msg, Object data ) {} @TraceRegistration private static CompositeType makeCompositeType( final EvaluatedClassDeclaration cls, final ManagedObjectManagerInternal mom, final ManagedData md, Collection<AttributeDescriptor> minfos ) { CompositeType result = null ; String name = md.name() ; if (name.equals( "" )) { name = mom.getTypeName( cls.cls(), "GMBAL_TYPE", md.name() ) ; } describe( "name", name ) ; final String mdDescription = mom.getDescription( cls ) ; describe( "mdDescription", mdDescription ) ; final int length = minfos.size() ; final String[] attrNames = new String[ length ] ; final String[] attrDescriptions = new String[ length ] ; final OpenType[] attrOTypes = new OpenType[ length ] ; int ctr = 0 ; for (AttributeDescriptor minfo : minfos) { attrNames[ctr] = minfo.id() ; attrDescriptions[ctr] = minfo.description() ; attrOTypes[ctr] = minfo.tc().getManagedType() ; ctr++ ; } describe( "attrNames=", Arrays.asList(attrNames) ) ; describe( "attrDescriptions=", Arrays.asList(attrDescriptions) ) ; describe( "attrOTypes=", Arrays.asList(attrOTypes) ) ; try { result = new CompositeType( name, mdDescription, attrNames, attrDescriptions, attrOTypes ) ; } catch (OpenDataException exc) { throw Exceptions.self.exceptionInMakeCompositeType(exc) ; } return result ; } @TraceRegistration private static TypeConverter handleManagedData( final EvaluatedClassDeclaration cls, final ManagedObjectManagerInternal mom, final ManagedData md ) { TypeConverter result = null ; final Collection<AttributeDescriptor> minfos = analyzeManagedData( cls, mom ) ; final CompositeType myType = makeCompositeType( cls, mom, md, minfos ) ; describe( "minfos=", minfos ) ; describe( "myType=", myType ) ; result = new TypeConverterImpl( cls, myType ) { @TraceRuntime public Object toManagedEntity( Object obj ) { Object runResult = null ; Map<String,Object> data = new HashMap<String,Object>() ; for (AttributeDescriptor minfo : minfos) { describe( "Fetching attribute ", minfo.id() ) ; Object value = null ; if (minfo.isApplicable( obj )) { try { FacetAccessor fa = mom.getFacetAccessor( obj ) ; value = minfo.get(fa ); } catch (JMException ex) { Exceptions.self.errorInConstructingOpenData( cls.name(), minfo.id(), ex ) ; } } data.put( minfo.id(), value ) ; } try { runResult = new CompositeDataSupport( myType, data ) ; } catch (OpenDataException exc) { throw Exceptions.self.exceptionInHandleManagedData(exc) ; } return runResult ; } } ; return result ; } private static TypeConverter handleEnum( final EvaluatedClassDeclaration cls ) { return new TypeConverterImpl( cls, SimpleType.STRING ) { public Object toManagedEntity( final Object obj ) { if (obj == null) return NULL_STRING ; else return obj.toString() ; } @Override @SuppressWarnings("unchecked") public Object fromManagedEntity( final Object entity ) { if (NULL_STRING.equals(entity)) { return null ; } if (!(entity instanceof String)) { throw Exceptions.self.notAString(entity) ; } return Enum.valueOf( cls.cls(), (String)entity ) ; } } ; } // Note: this is only needed on JDK 5, but should work fine on JDK 6 as well. private static ArrayType getArrayType( OpenType ot ) throws OpenDataException { ArrayType result ; if (ot instanceof ArrayType) { ArrayType atype = (ArrayType)ot ; int dim = atype.getDimension() ; OpenType lowestComponentType = atype.getElementOpenType() ; result = new ArrayType( dim + 1, lowestComponentType ) ; } else { result = new ArrayType( 1, ot ) ; } return result ; } @SuppressWarnings("unchecked") @TraceRegistration private static TypeConverter handleArrayType( final EvaluatedArrayType type, final ManagedObjectManagerInternal mom ) throws OpenDataException { TypeConverter result = null ; final EvaluatedType ctype = type.componentType() ; final TypeConverter ctypeTc = mom.getTypeConverter( ctype ) ; final OpenType cotype = ctypeTc.getManagedType() ; final OpenType ot = getArrayType( cotype ) ; describe( "ctype", ctype ) ; describe( "ctypeTc", ctypeTc ) ; describe( "cotype", cotype ) ; describe( "ot", ot ) ; result = new TypeConverterImpl( type, ot ) { @TraceRuntime public Object toManagedEntity( final Object obj ) { if (isIdentity()) { return obj ; } else { final Class cclass = getJavaClass( cotype ) ; final int length = obj == null ? 0 : Array.getLength( obj ) ; final Object result = Array.newInstance( cclass, length ) ; for (int ctr=0; ctr<length; ctr++) { describe( "Entering (handleArrayType):toManagedEntity", ctr ) ; try { final Object elem = Array.get( obj, ctr ) ; final Object relem = ctypeTc.toManagedEntity( elem ) ; Array.set( result, ctr, relem ) ; } finally { describe( "Exiting (handleArrayType):toManagedEntity", ctr ) ; } } return result ; } } @Override @TraceRuntime public Object fromManagedEntity( final Object entity ) { if (isIdentity()) { return entity ; } else { final Class cclass = getJavaClass( ctype ) ; final int length = entity == null ? 0 : Array.getLength( entity ) ; final Object result = Array.newInstance( cclass, length ) ; for (int ctr=0; ctr<length; ctr++) { describe( "Entering (handleArrayType):fromManagedEntity", ctr ) ; try { final Object elem = Array.get( entity, ctr ) ; final Object relem = ctypeTc.fromManagedEntity( elem ) ; Array.set( result, ctr, relem ) ; } finally { describe( "Exiting (handleArrayType):fromManagedEntity", ctr ) ; } } return result ; } } @Override public boolean isIdentity() { return ctypeTc.isIdentity() ; } } ; return result ; } private static final Runnable NoOp = new Runnable() { public void run() {} } ; private static EvaluatedMethodDeclaration findMethod( final EvaluatedClassAnalyzer eca, final String mname ) { return Algorithms.getFirst( eca.findMethods( new UnaryPredicate<EvaluatedMethodDeclaration>() { public boolean evaluate( EvaluatedMethodDeclaration m ) { return m.name().equals( mname ) ; } } ), NoOp ) ; } private static EvaluatedType getReturnType( EvaluatedClassDeclaration decl, String mname ) { EvaluatedClassAnalyzer eca = new EvaluatedClassAnalyzer( decl ) ; EvaluatedMethodDeclaration meth = findMethod( eca, mname ) ; if (meth == null) { return null ; } else { return meth.returnType() ; } } private static EvaluatedType getParameterType( EvaluatedClassDeclaration decl, String mname, int pindex ) { EvaluatedClassAnalyzer eca = new EvaluatedClassAnalyzer( decl ) ; EvaluatedMethodDeclaration meth = findMethod( eca, mname ) ; if (meth == null) { return null ; } else { if (pindex < meth.parameterTypes().size()) { return meth.parameterTypes().get( pindex ) ; } else { throw new IndexOutOfBoundsException( "Parameter index is out of bounds" ) ; } } } private static Table emptyTable() { return new Table() { public Object get(Object key) { return null ; } public Iterator iterator() { return emptyIterator() ; } } ; } private static Iterator emptyIterator() { List list = new ArrayList() ; return list.iterator() ; } @TraceRegistration private static TypeConverter handleClass( final EvaluatedClassDeclaration type, final ManagedObjectManagerInternal mom ) { TypeConverter result = null ; // Case 1: Some kind of collection. if (Iterable.class.isAssignableFrom(type.cls())) { EvaluatedClassDeclaration type2 = (EvaluatedClassDeclaration)getReturnType( type, "iterator") ; if (type2 == null) { throw Exceptions.self.iteratorNotFound(type) ; } EvaluatedType tcType = getReturnType( type2, "next" ) ; if (tcType == null) { throw Exceptions.self.nextNotFound(type) ; } TypeConverter tc = mom.getTypeConverter( tcType ) ; result = new TypeConverterListBase( type, tc ) { protected Iterator getIterator( Object obj ) { if (obj == null) { return emptyIterator() ; } else { return ((Iterable)obj).iterator() ; } } } ; } else if (Collection.class.isAssignableFrom(type.cls())) { EvaluatedClassDeclaration type2 = (EvaluatedClassDeclaration)getReturnType( type, "iterator") ; EvaluatedType tcType = getReturnType( type2, "next" ) ; TypeConverter tc = mom.getTypeConverter( tcType ) ; result = new TypeConverterListBase( type, tc ) { protected Iterator getIterator( Object obj ) { if (obj == null) { return emptyIterator() ; } else { return ((Iterable)obj).iterator() ; } } } ; } else if (Iterator.class.isAssignableFrom(type.cls())) { EvaluatedType tcType = getReturnType( type, "next" ) ; TypeConverter tc = mom.getTypeConverter( tcType ) ; result = new TypeConverterListBase( type, tc ) { protected Iterator getIterator( Object obj ) { if (obj == null) { return emptyIterator() ; } else { return (Iterator)obj ; } } } ; } else if (Enumeration.class.isAssignableFrom(type.cls())) { EvaluatedType tcType = getReturnType( type, "next" ) ; TypeConverter tc = mom.getTypeConverter( tcType ) ; result = new TypeConverterListBase( type, tc ) { @SuppressWarnings("unchecked") protected Iterator getIterator( Object obj ) { if (obj == null) { return emptyIterator() ; } else { return new EnumerationAdapter( (Enumeration)obj ) ; } } } ; } else if (Map.class.isAssignableFrom(type.cls())) { EvaluatedType type1 = getParameterType( type, "put", 0 ) ; TypeConverter firstTc = mom.getTypeConverter( type1 ) ; EvaluatedType type2 = getReturnType( type, "put" ) ; TypeConverter secondTc = mom.getTypeConverter( type2 ) ; result = new TypeConverterMapBase( type, firstTc, secondTc ) { @SuppressWarnings("unchecked") protected Table getTable( Object obj ) { if (obj == null) { return emptyTable() ; } else { return new TableMapImpl( (Map)obj ) ; } } } ; } else if (Dictionary.class.isAssignableFrom(type.cls())) { EvaluatedType type1 = getParameterType( type, "put", 0 ) ; TypeConverter firstTc = mom.getTypeConverter( type1 ) ; EvaluatedType type2 = getReturnType( type, "put" ) ; TypeConverter secondTc = mom.getTypeConverter( type2 ) ; result = new TypeConverterMapBase( type, firstTc, secondTc ) { @SuppressWarnings("unchecked") protected Table getTable( Object obj ) { if (obj == null) { return emptyTable() ; } else { return new TableDictionaryImpl( (Dictionary)obj ) ; } } } ; } else { result = handleAsString( type ) ; } return result ; } @SuppressWarnings({"unchecked"}) private static TypeConverter handleAsString( final EvaluatedClassDeclaration cls ) { Constructor tcons ; try { SecurityManager sman = System.getSecurityManager() ; if (sman == null) { tcons = cls.cls().getDeclaredConstructor(String.class) ; } else { tcons = Algorithms.doPrivileged( new Algorithms.Action<Constructor>() { public Constructor run() throws Exception { return cls.cls().getDeclaredConstructor(String.class ) ; } }) ; } } catch (Exception exc) { Exceptions.self.noStringConstructorAvailable( exc, cls.name() ) ; tcons = null ; } final Constructor cons = tcons ; return new TypeConverterImpl( cls, SimpleType.STRING ) { public Object toManagedEntity( Object obj ) { if (obj == null) { return NULL_STRING ; } else { return obj.toString() ; } } @Override public Object fromManagedEntity( final Object entity ) { if (entity == null) { return null ; } if (cons == null) { throw Exceptions.self.noStringConstructor(cls.cls()); } try { final String str = (String) entity; return cons.newInstance(str); } catch (InstantiationException exc) { throw Exceptions.self.stringConversionError(cls.cls(), exc ) ; } catch (IllegalAccessException exc) { throw Exceptions.self.stringConversionError(cls.cls(), exc ) ; } catch (InvocationTargetException exc) { throw Exceptions.self.stringConversionError(cls.cls(), exc ) ; } } } ; } private static class EnumerationAdapter<T> implements Iterator<T> { final private Enumeration<T> enumeration ; public EnumerationAdapter( final Enumeration<T> en ) { this.enumeration = en ; } public boolean hasNext() { return enumeration.hasMoreElements() ; } public T next() { return enumeration.nextElement() ; } public void remove() { throw Exceptions.self.removeNotSupported() ; } } // TypeConverter that throws exceptions for its methods. Used as a // place holder to detect recursive types. public static class TypeConverterPlaceHolderImpl implements TypeConverter { private EvaluatedType et ; public TypeConverterPlaceHolderImpl( EvaluatedType type ) { et = type ; } public EvaluatedType getDataType() { throw Exceptions.self.recursiveTypesNotSupported( et ) ; } public OpenType getManagedType() { throw Exceptions.self.recursiveTypesNotSupported( et ) ; } public Object toManagedEntity( Object obj ) { throw Exceptions.self.recursiveTypesNotSupported( et ) ; } public Object fromManagedEntity( Object entity ) { throw Exceptions.self.recursiveTypesNotSupported( et ) ; } public boolean isIdentity() { throw Exceptions.self.recursiveTypesNotSupported( et ) ; } } private abstract static class TypeConverterListBase extends TypeConverterImpl { final TypeConverter memberTc ; public TypeConverterListBase( final EvaluatedType dataType, final TypeConverter memberTc ) { super( dataType, makeArrayType( memberTc.getManagedType() ) ) ; this.memberTc = memberTc ; } @SuppressWarnings("unchecked") private static ArrayType makeArrayType( final OpenType ot ) { try { return getArrayType( ot ) ; } catch (OpenDataException exc) { throw Exceptions.self.openTypeInArrayTypeException( ot, exc) ; } } protected abstract Iterator getIterator( Object obj ) ; public Object toManagedEntity( final Object obj ) { final Iterator iter = getIterator( obj ) ; final List<Object> list = new ArrayList<Object>() ; while (iter.hasNext()) { list.add( iter.next() ) ; } final Class cclass = getJavaClass( memberTc.getManagedType() ) ; final Object result = Array.newInstance( cclass, list.size() ) ; int ctr = 0 ; for (Object elem : list) { final Object mappedElem = memberTc.toManagedEntity( elem ) ; Array.set( result, ctr++, mappedElem ) ; } return result ; } } private interface Table<K,V> extends Iterable<K> { V get( K key ) ; } private static class TableMapImpl<K,V> implements Table<K,V> { final private Map<K,V> map ; public TableMapImpl( final Map<K,V> map ) { this.map = map ; } public Iterator<K> iterator() { return map.keySet().iterator() ; } public V get( final K key ) { return map.get( key ) ; } } private static class TableDictionaryImpl<K,V> implements Table<K,V> { final private Dictionary<K,V> dict ; public TableDictionaryImpl( final Dictionary<K,V> dict ) { this.dict = dict ; } @SuppressWarnings("unchecked") public Iterator<K> iterator() { return new EnumerationAdapter( dict.keys() ) ; } public V get( final K key ) { return dict.get( key ) ; } } private abstract static class TypeConverterMapBase extends TypeConverterImpl { final private TypeConverter keyTypeConverter ; final private TypeConverter valueTypeConverter ; public TypeConverterMapBase( EvaluatedType dataType, TypeConverter keyTypeConverter, TypeConverter valueTypeConverter ) { super( dataType, makeMapTabularType( keyTypeConverter, valueTypeConverter ) ) ; this.keyTypeConverter = keyTypeConverter ; this.valueTypeConverter = valueTypeConverter ; } private static TabularType makeMapTabularType( final TypeConverter firstTc, final TypeConverter secondTc ) { final String mapType = firstTc + "->" + secondTc ; final String[] itemNames = new String[] { "key", "value" } ; final String description = Exceptions.self.rowTypeDescription( mapType) ; final String[] itemDescriptions = new String[] { Exceptions.self.keyFieldDescription(mapType), Exceptions.self.valueFieldDescription(mapType) } ; final OpenType[] itemTypes = new OpenType[] { firstTc.getManagedType(), secondTc.getManagedType() } ; try { final CompositeType rowType = new CompositeType( mapType, description, itemNames, itemDescriptions, itemTypes ) ; final String[] keys = new String[] { "key" } ; final String tableName = Exceptions.self.tableName( mapType ) ; final String tableDescription = Exceptions.self.tableDescription( mapType ) ; final TabularType result = new TabularType( tableName, tableDescription, rowType, keys ) ; return result ; } catch (OpenDataException exc) { throw Exceptions.self.exceptionInMakeMapTabularType(exc) ; } } protected abstract Table getTable( Object obj ) ; @SuppressWarnings("unchecked") public Object toManagedEntity( Object obj ) { try { final Table table = getTable( obj ) ; final TabularType ttype = (TabularType)getManagedType() ; final CompositeType ctype = ttype.getRowType() ; final TabularData result = new TabularDataSupport( ttype ) ; for (Object key : table) { @SuppressWarnings("unchecked") final Object value = table.get( key ) ; final Object mappedKey = keyTypeConverter.toManagedEntity( key ) ; final Object mappedValue = valueTypeConverter.toManagedEntity( value ) ; final Map items = new HashMap() ; items.put( "key", mappedKey ) ; items.put( "value", mappedValue ) ; CompositeDataSupport cdata = new CompositeDataSupport( ctype, items ) ; result.put( cdata ) ; } return result ; } catch (OpenDataException exc) { throw Exceptions.self.excInMakeMapTabularDataToManagedEntity( exc ) ; } } } // Basic support for all TypeConverters. @DumpToString protected final EvaluatedType dataType ; @DumpToString protected final OpenType managedType ; protected TypeConverterImpl( final EvaluatedType dataType, final OpenType managedType ) { this.dataType = dataType ; this.managedType = managedType ; } /* Java generic type of attribute in problem-domain Object. */ public final EvaluatedType getDataType() { return dataType ; } /* Open MBeans Open Type for management domain object. */ public final OpenType getManagedType() { return managedType ; } /* Convert from a problem-domain Object obj to a ManagedEntity. */ public abstract Object toManagedEntity( Object obj ) ; /* Convert from a ManagedEntity to a problem-domain Object. */ public Object fromManagedEntity( Object entity ) { throw Exceptions.self.openToJavaNotSupported(managedType, dataType) ; } /* Returns true if this TypeConverter is an identity transformation. */ public boolean isIdentity() { return false ; } private String displayOpenType( OpenType otype ) { if (otype instanceof SimpleType) { SimpleType stype = (SimpleType)otype ; return "SimpleType(" + stype.getTypeName() + ")" ; } else if (otype instanceof ArrayType) { ArrayType atype = (ArrayType)otype ; return "ArrayType(" + displayOpenType( atype.getElementOpenType() ) + "," + atype.getDimension() + ")" ; } else if (otype instanceof CompositeType) { CompositeType ctype = (CompositeType)otype ; return "CompositeType(" + ctype.getTypeName() + ")" ; } else if (otype instanceof TabularType) { TabularType ttype = (TabularType)otype ; return "TabularType(" + ttype.getTypeName() + "," + "rowType=" + ttype.getRowType() + "indexNames=" + ttype.getIndexNames() + ")" ; } else { return "UNKNOWN(" + otype + ")" ; } } @Override public String toString() { return "TypeConverter[dataType=" + dataType + ",managedType=" + displayOpenType( managedType ) + "]" ; } }