/** * Copyright 2008-2016 Qualogy Solutions B.V. * * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 * * 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 com.qualogy.qafe.business.integration.adapter; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.lang.StringUtils; import com.qualogy.qafe.bind.commons.type.AdapterAttribute; import com.qualogy.qafe.bind.commons.type.AdapterMapping; import com.qualogy.qafe.bind.commons.type.AttributeMapping; import com.qualogy.qafe.bind.commons.type.In; import com.qualogy.qafe.business.integration.builder.PredefinedClassTypeConverter; import com.qualogy.qafe.core.datastore.DataIdentifier; import com.qualogy.qafe.core.datastore.DataMap; import com.qualogy.qafe.core.datastore.DataStore; /** * Adapter for adapting an object (map or servicedefined outcome) to a complex type, defined within the type-definition tag. * * @author mvanderwurff */ public class MappingAdapter{ private static final Logger logger = Logger.getLogger(MappingAdapter.class.getName()); //TODO: check if correct method is used //TODO: implement adapt all //TODO: deal with conversion inside the adapters ==> string to int /** * method knows toBeMapped is not an instance of any type of Collection or is an array! * @param toBeMapped * @param mapping * @param result * @return */ protected static Object adaptFromService(Object toBeMapped, AdapterMapping mapping) { Object result = null; //TODO: check 3 nested parents...since object is null every time //its cheaper to convert all than doing a nested get on each and every attribute of the mapping toBeMapped = ObjectMapConverter.convert(toBeMapped); if(mapping.hasParent()){ result = adaptFromService(toBeMapped, mapping.getParent()); } List<AdapterAttribute> attributes = mapping.getAdapterAttributes(); if(attributes!=null){ if(result==null){ // result = new CaseInsensitiveMap(); result = new DataMap(); } for (Iterator<AdapterAttribute> iter = attributes.iterator(); iter.hasNext();) { AdapterAttribute attribute = (AdapterAttribute) iter.next(); Object value = null; if(attribute instanceof AttributeMapping){//simple attribute or complex attribute with adapter AttributeMapping attrmapping = (AttributeMapping)attribute; if(attrmapping.getAdapter()!=null){//attribute has an adapter value = adaptFromService(((Map)toBeMapped).remove(attrmapping.getName()),attrmapping.getAdapter()); }else{//simple attribute value = ((Map)toBeMapped).remove(attrmapping.getName());//TODO:simple conversion } }else if(attribute instanceof AdapterMapping){//internal adapter AdapterMapping adaptermapping = (AdapterMapping)attribute; value = adaptFromService(toBeMapped,adaptermapping); } ((Map)result).put(attribute.getName(), value); } } if(mapping.adaptAll() && ( (result==null && toBeMapped!=null && !((Map)toBeMapped).isEmpty()) || result instanceof Map )){ //adaptall left (removed adapted ones) if(result==null){ // result = new CaseInsensitiveMap(); result = new DataMap(); } ((Map)result).putAll((Map)toBeMapped); } return result; } /** * Adapts data in the datastore for key name from the in parameter to a * value form specified in the in's parameter adaptermapping. * @param id * @param in * @param jarFileLocation * @return */ protected static Object adaptToService(ClassLoader classLoader, DataIdentifier id, In in) { return adaptToService(id, in, in.getAdapter(), null, classLoader); } private static Object createServiceObj(String className, ClassLoader classLoader){ Object result = null; try { if(className==null) throw new UnableToAdaptException("Adapter states null outputclass"); Class<?> clazz = classLoader.loadClass(className); Constructor<?> c = clazz.getConstructor(new Class[0]); c.setAccessible(true); result = c.newInstance(new Object[0]); } catch (InstantiationException e) { throw new UnableToAdaptException("Cannot instantiate ["+className+"], hint: define a default constructor", e); } catch (IllegalAccessException e) { throw new UnableToAdaptException(e); } catch (Exception e) { throw new UnableToAdaptException(e); } return result; } private static Object adaptToService(DataIdentifier id, In in, AdapterMapping mapping, Object result, ClassLoader externalClassLoader) { if(result==null) result = createServiceObj(mapping.getOutputClass(), externalClassLoader); if(mapping.hasParent()){ result = adaptToService(id, in, mapping.getParent(), result, externalClassLoader); } List<AdapterAttribute> attributes = mapping.getAdapterAttributes(); if(attributes!=null){ for (Iterator<AdapterAttribute> iter = attributes.iterator(); iter.hasNext();) { AdapterAttribute attribute = (AdapterAttribute) iter.next(); Object value = null; String ref = ((in.getRef()==null || StringUtils.isBlank(in.getRef().stringValueOf())) ? "" : (in.getRef().toString() + ".")) + attribute.getName(); if(attribute instanceof AttributeMapping){//simple attribute or complex attribute with adapter AttributeMapping attrmapping = (AttributeMapping)attribute; if(attrmapping.getAdapter()!=null){//attribute has an adapter value = adaptToService(id, in, attrmapping.getAdapter(), null, externalClassLoader); }else{//simple attribute value = DataStore.findValue(id, ref);//TODO:simple conversion } }else if(attribute instanceof AdapterMapping){//internal adapter AdapterMapping adaptermapping = (AdapterMapping)attribute; value = adaptToService(id, in, adaptermapping, null, externalClassLoader); } result = adaptToJavaObject(mapping.getId(), attribute.getName(), result, value, externalClassLoader); } } return result; } private static Object adaptToJavaObject(String mappingId, String attributeName, Object result, Object valueToAdapt, ClassLoader classLoader){ try { if(valueToAdapt!=null){ Class resultClazz = classLoader.loadClass(result.getClass().getName()); Field field = resultClazz.getDeclaredField(attributeName); field.setAccessible(true); String fieldName = field.getType().getName(); String valueName = valueToAdapt.getClass().getName(); logger.info(field.getType().getName()); if(!fieldName.equals(valueName)){ if(PredefinedClassTypeConverter.isPredefined(field.getType())) valueToAdapt = PredefinedAdapterFactory.create(field.getType()).convert(valueToAdapt); else throw new IllegalArgumentException("Object passed ["+valueToAdapt+"] cannot be converted to type wanted["+field.getType()+"] for field["+field.getName()+"] in adapter["+mappingId+"]"); }else{ if(!PredefinedClassTypeConverter.isPredefined(field.getType())){ Class paramClazz = classLoader.loadClass(fieldName); valueToAdapt = paramClazz.cast(valueToAdapt); } } field.set(result, valueToAdapt); } } catch (IllegalArgumentException e) { throw new UnableToAdaptException("arg ["+valueToAdapt+"] is illegal for field with name ["+attributeName+"]", e); } catch (SecurityException e) { throw new UnableToAdaptException(e); } catch (IllegalAccessException e) { throw new UnableToAdaptException("field ["+attributeName+"] does not accessible on class ["+result.getClass()+"]", e); } catch (NoSuchFieldException e) { logger.log(Level.WARNING, "field ["+attributeName+"] does not exist on class ["+result.getClass()+"]", e); } catch (ClassNotFoundException e) { throw new UnableToAdaptException("field ["+attributeName+"] class not found ["+result.getClass()+"]", e); } return result; } }