| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 org.apache.felix.schematizer.impl; |
| |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Type; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.felix.schematizer.Node; |
| import org.apache.felix.schematizer.Schema; |
| import org.osgi.dto.DTO; |
| import org.osgi.util.converter.ConversionException; |
| import org.osgi.util.converter.Converter; |
| import org.osgi.util.converter.ConverterFunction; |
| import org.osgi.util.converter.Converters; |
| import org.osgi.util.converter.TargetRule; |
| import org.osgi.util.converter.TypeReference; |
| |
| import static org.apache.felix.schematizer.impl.Util.asDTO; |
| import static org.apache.felix.schematizer.impl.Util.rawClassOf; |
| |
| public class SchemaBasedConverter<T> implements TargetRule { |
| private final SchemaImpl schema; |
| private final Converter converter; |
| |
| public SchemaBasedConverter(SchemaImpl aSchema) { |
| schema = aSchema; |
| converter = Converters.standardConverter(); |
| // TODO: how can we add the error handler?? |
| // .newConverterBuilder() |
| // .errorHandler( (obj,type) -> new Exception( "Could not convert object " + obj.toString() + " of type " + type.getTypeName() ) ) |
| // .build(); |
| } |
| |
| @Override |
| public ConverterFunction getFunction() { |
| return (obj,t) -> { |
| if (!(obj instanceof Map) || schema == null) |
| return handleInvalid(); |
| return convertMap((Map<?,?>)obj, schema, "/"); |
| }; |
| } |
| |
| @Override |
| public Type getTargetType() { |
| return schema.rootNode().type(); |
| } |
| |
| @SuppressWarnings( "unchecked" ) |
| private T convertMap(Map<?,?> map, Schema s, String contextPath) { |
| Node node = s.nodeAtPath(contextPath); |
| Class<?> cls = Util.rawClassOf(node.type()); |
| |
| if (!asDTO(cls)) |
| return handleInvalid(); |
| |
| if (!contextPath.endsWith("/")) |
| contextPath = contextPath + "/"; |
| |
| return (T)convertToDTO((Class<? extends DTO>)cls, map, s, contextPath); |
| } |
| |
| @SuppressWarnings({ "rawtypes", "unchecked" }) |
| private <U extends DTO>U convertToDTO(Class<U> targetCls, Map<?,?> m, Schema schema, String contextPath) { |
| try { |
| U dto; |
| |
| try { |
| dto = targetCls.newInstance(); |
| } catch (Throwable t) { |
| throw new ConversionException("Cannot create instance of DTO " + targetCls + ". Bad constructor?", t); |
| } |
| |
| for (Map.Entry entry : m.entrySet()) { |
| try { |
| Field f = targetCls.getField(entry.getKey().toString()); |
| Object val = entry.getValue(); |
| if (val == null) |
| continue; |
| String path = contextPath + f.getName(); |
| Node node = schema.nodeAtPath(path); |
| Object obj; |
| if (node.typeReference().isPresent()) { |
| TypeReference<?> tr = Util.typeReferenceOf(node.typeReference().get()); |
| if (node.isCollection()) |
| if (!Collection.class.isAssignableFrom(val.getClass())) |
| // TODO: PANIC! Something is wrong... what should we do?? |
| obj = null; |
| else |
| obj = convertToCollection( (Class)Util.rawClassOf(tr), (Class)node.collectionType(), (Collection)val, schema, path); |
| else |
| obj = convertToDTO((Class<? extends DTO>)rawClassOf(tr), (Map<?,?>)val, schema, path + "/"); |
| } else { |
| if (node.isCollection()) { |
| Collection c = instantiateCollection(node.collectionType()); |
| Type type = node.type(); |
| for (Object o : (Collection)val) { |
| if (o == null) |
| c.add(null); |
| else if (asDTO(rawClassOf(type))) |
| c.add(convertToDTO((Class)Util.rawClassOf(type), (Map)o, schema, path + "/")); |
| else |
| c.add(converter.convert(o).to(type)); |
| } |
| obj = c; |
| } else { |
| Class<?> rawClass = rawClassOf(node.type()); |
| if (asDTO(rawClass)) |
| obj = convertToDTO((Class<? extends DTO>)rawClass, (Map<?,?>)val, schema, path + "/"); |
| else |
| obj = converter.convert(val).to(node.type()); |
| } |
| } |
| |
| f.set(dto, obj); |
| } catch (NoSuchFieldException e) { |
| } |
| } |
| |
| return dto; |
| } catch (Exception e) { |
| throw new ConversionException("Cannot create DTO " + targetCls, e); |
| } |
| } |
| |
| @SuppressWarnings({ "rawtypes", "unchecked" }) |
| private <U, V extends Collection<U>>V convertToCollection(Class<U> targetCls, Class<V> collectionClass, Collection sourceCollection, Schema schema, String path) { |
| try { |
| V targetCollection = instantiateCollection(collectionClass); |
| sourceCollection.stream() |
| .map(obj -> convertCollectionItemToObject(obj, targetCls, schema, path)) |
| .forEach(u -> targetCollection.add((U)u)); |
| |
| return targetCollection; |
| } catch (Exception e) { |
| throw new ConversionException("Cannot create DTO " + targetCls, e); |
| } |
| } |
| |
| @SuppressWarnings( "unchecked" ) |
| private <V extends Collection<?>>V instantiateCollection(Class<V> collectionClass) { |
| if (collectionClass == null) |
| return (V)new ArrayList<V>(); |
| if (Collection.class.equals(collectionClass) || List.class.isAssignableFrom(collectionClass)) |
| return (V)new ArrayList<V>(); |
| else |
| // TODO: incomplete |
| return null; |
| } |
| |
| private <U>U convertCollectionItemToObject(Object obj, Class<U> targetCls, Schema schema, String path) { |
| try |
| { |
| if (asDTO(targetCls)) |
| return convertCollectionItemToDTO(obj, targetCls, schema, path); |
| |
| U newItem = targetCls.newInstance(); |
| return newItem; |
| } |
| catch ( Exception e ) |
| { |
| e.printStackTrace(); |
| return null; |
| } |
| } |
| |
| @SuppressWarnings( "unchecked" ) |
| private <U>U convertCollectionItemToDTO(Object obj, Class<U> targetCls, Schema schema, String path) { |
| Node node = schema.nodeAtPath(path); |
| if (node.typeReference().isPresent()) { |
| TypeReference<U> tr = (TypeReference<U>)Util.typeReferenceOf(node.typeReference().get()); |
| return converter.convert(obj).to(tr); |
| } else { |
| Type type = node.type(); |
| type.toString(); |
| // TODO |
| // if (DTO.class.isAssignableFrom(Util.rawClassOf(type))) |
| // obj = convertToDTO((Class)Util.rawClassOf(type), (Map)val, schema, path + "/" ); |
| // else |
| // obj = converter.convert(val).to(type); |
| return null; |
| } |
| } |
| |
| // TODO |
| private T handleInvalid() { |
| return null; |
| } |
| } |