blob: 349f1dd09a39bdddfdaaf038d3e7ae48acbf52b6 [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.configurator.impl.json;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.json.Json;
import javax.json.JsonStructure;
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;
public class TypeConverter {
public static Converter getConverter() {
return Converters.standardConverter().newConverterBuilder().rule(new TargetRule() {
@Override
public Type getTargetType() {
return String.class;
}
@Override
public ConverterFunction getFunction() {
return new ConverterFunction() {
@Override
public Object apply(final Object obj, final Type targetType) throws Exception {
if ( obj instanceof Map || obj instanceof List ) {
final JsonStructure json = JSONUtil.build(obj);
final StringWriter w = new StringWriter();
Json.createWriter(w).write(json);
return w.toString();
}
return CANNOT_HANDLE;
}
};
}
}).build();
}
private static final Map<String, Class<?>> TYPE_MAP = new HashMap<>();
static {
// scalar types and primitive types
TYPE_MAP.put("String", String.class);
TYPE_MAP.put("Integer", Integer.class);
TYPE_MAP.put("int", Integer.class);
TYPE_MAP.put("Long", Long.class);
TYPE_MAP.put("long", Long.class);
TYPE_MAP.put("Float", Float.class);
TYPE_MAP.put("float", Float.class);
TYPE_MAP.put("Double", Double.class);
TYPE_MAP.put("double", Double.class);
TYPE_MAP.put("Byte", Byte.class);
TYPE_MAP.put("byte", Byte.class);
TYPE_MAP.put("Short", Short.class);
TYPE_MAP.put("short", Short.class);
TYPE_MAP.put("Character", Character.class);
TYPE_MAP.put("char", Character.class);
TYPE_MAP.put("Boolean", Boolean.class);
TYPE_MAP.put("boolean", Boolean.class);
// array of scalar types and primitive types
TYPE_MAP.put("String[]", String[].class);
TYPE_MAP.put("Integer[]", Integer[].class);
TYPE_MAP.put("int[]", int[].class);
TYPE_MAP.put("Long[]", Long[].class);
TYPE_MAP.put("long[]", long[].class);
TYPE_MAP.put("Float[]", Float[].class);
TYPE_MAP.put("float[]", float[].class);
TYPE_MAP.put("Double[]", Double[].class);
TYPE_MAP.put("double[]", double[].class);
TYPE_MAP.put("Byte[]", Byte[].class);
TYPE_MAP.put("byte[]", byte[].class);
TYPE_MAP.put("Short[]", Short[].class);
TYPE_MAP.put("short[]", short[].class);
TYPE_MAP.put("Boolean[]", Boolean[].class);
TYPE_MAP.put("boolean[]", boolean[].class);
TYPE_MAP.put("Character[]", Character[].class);
TYPE_MAP.put("char[]", char[].class);
}
private final List<File> allFiles = new ArrayList<>();
private final List<File> files = new ArrayList<>();
private final BinUtil.ResourceProvider provider;
/**
* Create a new instance
* @param provider The bundle provider, might be {@code null}.
*/
public TypeConverter(final BinUtil.ResourceProvider provider) {
this.provider = provider;
}
/**
* Convert a value to the given type
* @param value The value
* @param typeInfo Optional type info, might be {@code null}
* @return The converted value or {@code null} if the conversion failed.
* @throws IOException If an error happens
*/
public Object convert(
final String pid,
final Object value,
final String typeInfo) throws IOException {
if ( typeInfo == null ) {
if ( value instanceof String || value instanceof Boolean ) {
return value;
} else if ( value instanceof Long || value instanceof Double ) {
return value;
} else if ( value instanceof Integer ) {
return ((Integer)value).longValue();
} else if ( value instanceof Short ) {
return ((Short)value).longValue();
} else if ( value instanceof Byte ) {
return ((Byte)value).longValue();
} else if ( value instanceof Float ) {
return ((Float)value).doubleValue();
}
if ( value instanceof List ) {
@SuppressWarnings("unchecked")
final List<Object> list = (List<Object>)value;
if ( list.isEmpty() ) {
return new String[0];
}
final Object firstObject = list.get(0);
boolean hasListOrMap = false;
for(final Object v : list) {
if ( v instanceof List || v instanceof Map ) {
hasListOrMap = true;
break;
}
}
Object convertedValue = null;
if ( !hasListOrMap ) {
if ( firstObject instanceof Boolean ) {
convertedValue = getConverter().convert(list).defaultValue(null).to(Boolean[].class);
} else if ( firstObject instanceof Long || firstObject instanceof Integer || firstObject instanceof Byte || firstObject instanceof Short ) {
convertedValue = getConverter().convert(list).defaultValue(null).to(Long[].class);
} else if ( firstObject instanceof Double || firstObject instanceof Float ) {
convertedValue = getConverter().convert(list).defaultValue(null).to(Double[].class);
}
}
if ( convertedValue == null ) {
convertedValue = getConverter().convert(list).defaultValue(null).to(String[].class);
}
return convertedValue;
}
return null;
}
// binary
if ( "binary".equals(typeInfo) ) {
if ( provider == null ) {
throw new IOException("Binary files only allowed within a bundle");
}
final String path = getConverter().convert(value).defaultValue(null).to(String.class);
if ( path == null ) {
throw new IOException("Invalid path for binary property: " + value);
}
final File filePath;
try {
filePath = BinUtil.extractFile(provider, pid, path);
} catch ( final IOException ioe ) {
throw new IOException("Unable to read " + path +
" in bundle " + provider.getIdentifier() +
" for pid " + pid +
" and write to " + BinUtil.binDirectory + " : " + ioe.getMessage(), ioe);
}
if ( filePath == null ) {
throw new IOException("Entry " + path + " not found in bundle " + provider.getIdentifier());
}
files.add(filePath);
allFiles.add(filePath);
return filePath.getAbsolutePath();
} else if ( "binary[]".equals(typeInfo) ) {
if ( provider == null ) {
throw new IOException("Binary files only allowed within a bundle");
}
final String[] paths = getConverter().convert(value).defaultValue(null).to(String[].class);
if ( paths == null ) {
throw new IOException("Invalid paths for binary[] property: " + value);
}
final String[] filePaths = new String[paths.length];
int i = 0;
while ( i < paths.length ) {
final File filePath;
try {
filePath = BinUtil.extractFile(provider, pid, paths[i]);
} catch ( final IOException ioe ) {
throw new IOException("Unable to read " + paths[i] +
" in bundle " + provider.getIdentifier() +
" for pid " + pid +
" and write to " + BinUtil.binDirectory + " : " + ioe.getMessage(), ioe);
}
if ( filePath == null ) {
throw new IOException("Entry " + paths[i] + " not found in bundle " + provider.getIdentifier());
}
files.add(filePath);
allFiles.add(filePath);
filePaths[i] = filePath.getAbsolutePath();
i++;
}
return filePaths;
}
final Class<?> typeClass = TYPE_MAP.get(typeInfo);
if ( typeClass != null ) {
return getConverter().convert(value).defaultValue(null).to(typeClass);
}
// Collections of scalar types
if ( "Collection<String>".equals(typeInfo) ) {
return getConverter().convert(value).defaultValue(null).to(new TypeReference<ArrayList<String>>() {});
} else if ( "Collection<Integer>".equals(typeInfo) ) {
return getConverter().convert(value).defaultValue(null).to(new TypeReference<ArrayList<Integer>>() {});
} else if ( "Collection<Long>".equals(typeInfo) ) {
return getConverter().convert(value).defaultValue(null).to(new TypeReference<ArrayList<Long>>() {});
} else if ( "Collection<Float>".equals(typeInfo) ) {
return getConverter().convert(value).defaultValue(null).to(new TypeReference<ArrayList<Float>>() {});
} else if ( "Collection<Double>".equals(typeInfo) ) {
return getConverter().convert(value).defaultValue(null).to(new TypeReference<ArrayList<Double>>() {});
} else if ( "Collection<Byte>".equals(typeInfo) ) {
return getConverter().convert(value).defaultValue(null).to(new TypeReference<ArrayList<Byte>>() {});
} else if ( "Collection<Short>".equals(typeInfo) ) {
return getConverter().convert(value).defaultValue(null).to(new TypeReference<ArrayList<Short>>() {});
} else if ( "Collection<Character>".equals(typeInfo) ) {
return getConverter().convert(value).defaultValue(null).to(new TypeReference<ArrayList<Character>>() {});
} else if ( "Collection<Boolean>".equals(typeInfo) ) {
return getConverter().convert(value).defaultValue(null).to(new TypeReference<ArrayList<Boolean>>() {});
} else if ( "Collection".equals(typeInfo) ) {
if ( value instanceof List ) {
@SuppressWarnings("unchecked")
final List<Object> list = (List<Object>)value;
if ( list.isEmpty() ) {
return Collections.EMPTY_LIST;
}
final Object firstObject = list.get(0);
boolean hasListOrMap = false;
for(final Object v : list) {
if ( v instanceof List || v instanceof Map ) {
hasListOrMap = true;
break;
}
}
Object convertedValue = null;
if ( !hasListOrMap ) {
if ( firstObject instanceof Boolean ) {
convertedValue = getConverter().convert(list).defaultValue(null).to(new TypeReference<ArrayList<Boolean>>() {});
} else if ( firstObject instanceof Long || firstObject instanceof Integer || firstObject instanceof Byte || firstObject instanceof Short) {
convertedValue = getConverter().convert(list).defaultValue(null).to(new TypeReference<ArrayList<Long>>() {});
} else if ( firstObject instanceof Double || firstObject instanceof Float ) {
convertedValue = getConverter().convert(list).defaultValue(null).to(new TypeReference<ArrayList<Double>>() {});
}
}
if ( convertedValue == null ) {
convertedValue = getConverter().convert(list).defaultValue(null).to(new TypeReference<List<String>>() {});
}
return convertedValue;
}
return getConverter().convert(value).defaultValue(null).to(ArrayList.class);
}
// unknown type - ignore configuration
throw new IOException("Invalid type information: " + typeInfo);
}
public void cleanupFiles() {
for(final File f : allFiles) {
f.delete();
}
}
public List<File> flushFiles() {
if ( this.files.isEmpty() ) {
return null;
} else {
final List<File> result = new ArrayList<>(this.files);
this.files.clear();
return result;
}
}
}