package com.jsoniter.output; import com.jsoniter.spi.JsonException; import com.jsoniter.spi.Encoder; import com.jsoniter.spi.Extension; import com.jsoniter.spi.JsoniterSpi; import com.jsoniter.spi.TypeLiteral; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.*; class Codegen { static EncodingMode mode = EncodingMode.REFLECTION_MODE; static boolean isDoingStaticCodegen; // only read/write when generating code with synchronized protection private final static Map<String, CodegenResult> generatedSources = new HashMap<String, CodegenResult>(); private volatile static Map<String, Encoder> reflectionEncoders = new HashMap<String, Encoder>(); static { String envMode = System.getenv("JSONITER_ENCODING_MODE"); if (envMode != null) { mode = EncodingMode.valueOf(envMode); } } public static void setMode(EncodingMode mode) { Codegen.mode = mode; } public static Encoder getReflectionEncoder(String cacheKey, Type type) { Encoder encoder = CodegenImplNative.NATIVE_ENCODERS.get(type); if (encoder != null) { return encoder; } encoder = reflectionEncoders.get(cacheKey); if (encoder != null) { return encoder; } synchronized (Codegen.class) { encoder = reflectionEncoders.get(cacheKey); if (encoder != null) { return encoder; } Type[] typeArgs = new Type[0]; Class clazz; if (type instanceof ParameterizedType) { ParameterizedType pType = (ParameterizedType) type; clazz = (Class) pType.getRawType(); typeArgs = pType.getActualTypeArguments(); } else { clazz = (Class) type; } encoder = ReflectionEncoderFactory.create(clazz, typeArgs); HashMap<String, Encoder> copy = new HashMap<String, Encoder>(reflectionEncoders); copy.put(cacheKey, encoder); reflectionEncoders = copy; return encoder; } } public static Encoder getEncoder(String cacheKey, Type type) { Encoder encoder = JsoniterSpi.getEncoder(cacheKey); if (encoder != null) { return encoder; } return gen(cacheKey, type); } private static synchronized Encoder gen(String cacheKey, Type type) { Encoder encoder = JsoniterSpi.getEncoder(cacheKey); if (encoder != null) { return encoder; } List<Extension> extensions = JsoniterSpi.getExtensions(); for (Extension extension : extensions) { encoder = extension.createEncoder(cacheKey, type); if (encoder != null) { JsoniterSpi.addNewEncoder(cacheKey, encoder); return encoder; } } encoder = CodegenImplNative.NATIVE_ENCODERS.get(type); if (encoder != null) { JsoniterSpi.addNewEncoder(cacheKey, encoder); return encoder; } Type[] typeArgs = new Type[0]; Class clazz; if (type instanceof ParameterizedType) { ParameterizedType pType = (ParameterizedType) type; clazz = (Class) pType.getRawType(); typeArgs = pType.getActualTypeArguments(); } else { clazz = (Class) type; } if (mode == EncodingMode.REFLECTION_MODE) { encoder = ReflectionEncoderFactory.create(clazz, typeArgs); JsoniterSpi.addNewEncoder(cacheKey, encoder); return encoder; } if (!isDoingStaticCodegen) { try { encoder = (Encoder) Class.forName(cacheKey).newInstance(); JsoniterSpi.addNewEncoder(cacheKey, encoder); return encoder; } catch (Exception e) { if (mode == EncodingMode.STATIC_MODE) { throw new JsonException("static gen should provide the encoder we need, but failed to create the encoder", e); } } } clazz = chooseAccessibleSuper(clazz); CodegenResult source = genSource(cacheKey, clazz, typeArgs); try { generatedSources.put(cacheKey, source); if (isDoingStaticCodegen) { staticGen(clazz, cacheKey, source); } else { encoder = DynamicCodegen.gen(clazz, cacheKey, source); } JsoniterSpi.addNewEncoder(cacheKey, encoder); return encoder; } catch (Exception e) { String msg = "failed to generate encoder for: " + type + " with " + Arrays.toString(typeArgs) + ", exception: " + e; msg = msg + "\n" + source; throw new JsonException(msg, e); } } private static Class chooseAccessibleSuper(Class clazz) { if (Modifier.isPublic(clazz.getModifiers())) { return clazz; } return chooseAccessibleSuper(clazz.getSuperclass()); } public static CodegenResult getGeneratedSource(String cacheKey) { return generatedSources.get(cacheKey); } private static void staticGen(Class clazz, String cacheKey, CodegenResult source) throws IOException { createDir(cacheKey); String fileName = cacheKey.replace('.', '/') + ".java"; FileOutputStream fileOutputStream = new FileOutputStream(fileName); try { OutputStreamWriter writer = new OutputStreamWriter(fileOutputStream); try { staticGen(clazz, cacheKey, writer, source); } finally { writer.close(); } } finally { fileOutputStream.close(); } } private static void staticGen(Class clazz, String cacheKey, OutputStreamWriter writer, CodegenResult source) throws IOException { String className = cacheKey.substring(cacheKey.lastIndexOf('.') + 1); String packageName = cacheKey.substring(0, cacheKey.lastIndexOf('.')); writer.write("package " + packageName + ";\n"); writer.write("public class " + className + " extends com.jsoniter.spi.EmptyEncoder {\n"); writer.write(source.generateWrapperCode(clazz)); writer.write(source.toString()); writer.write("}\n"); } private static void createDir(String cacheKey) { String[] parts = cacheKey.split("\\."); File parent = new File("."); for (int i = 0; i < parts.length - 1; i++) { String part = parts[i]; File current = new File(parent, part); current.mkdir(); parent = current; } } private static CodegenResult genSource(String cacheKey, Class clazz, Type[] typeArgs) { if (clazz.isArray()) { return CodegenImplArray.genArray(cacheKey, clazz); } if (Map.class.isAssignableFrom(clazz)) { return CodegenImplMap.genMap(cacheKey, clazz, typeArgs); } if (Collection.class.isAssignableFrom(clazz)) { return CodegenImplArray.genCollection(cacheKey, clazz, typeArgs); } if (clazz.isEnum()) { return CodegenImplNative.genEnum(clazz); } return CodegenImplObject.genObject(clazz); } public static void staticGenEncoders(TypeLiteral[] typeLiterals) { isDoingStaticCodegen = true; for (TypeLiteral typeLiteral : typeLiterals) { gen(typeLiteral.getEncoderCacheKey(), typeLiteral.getType()); } } }