blob: 65bcdf15a2e9dd186cc2e44edabcfc4bbe2e93b8 [file] [log] [blame]
/*
* 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;
}
}