package org.json.simple.serialization; import org.json.simple.serialization.collections.ArrayCodec; import org.json.simple.serialization.collections.CollectionCodec; import org.json.simple.serialization.collections.MapCodec; import org.json.simple.serialization.collections.PrimitiveArrayCodec; import org.json.simple.serialization.primitives.CharacterCodec; import org.json.simple.serialization.primitives.DateCodec; import org.json.simple.serialization.primitives.StringCodec; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.*; /** * @author karl.wettin@kodapan.se * @since 2011-02-03 22.31 */ public class JavascriptCompiler { private CodecRegistry registry; private PrintWriter js; public JavascriptCompiler(CodecRegistry registry, PrintWriter js) { this.registry = registry; this.js = js; } public void compile() throws IllegalAccessException, InstantiationException { Date now = new Date(); Map<Class, String> compiled = new HashMap<Class, String>(); for (BeanCodec codec : new ArrayList<BeanCodec>(registry.getBeanCodecs().values())) { StringWriter sw = new StringWriter(); PrintWriter js = new PrintWriter(sw); if (!Modifier.isAbstract(codec.getBeanClass().getModifiers()) && !Modifier.isInterface(codec.getBeanClass().getModifiers())) { js.println("(options) { "); js.flush(); for (Field f : (Collection<Field>) codec.getFieldsByName().values()) { Codec fieldCodec = registry.getCodec(f.getType()); js.print(" this." + f.getName() + " = "); Object defaultValue = codec.getDefaultValue(f); if (defaultValue == null) { js.print("null"); } else { if (fieldCodec instanceof ArrayCodec || fieldCodec instanceof CollectionCodec || fieldCodec instanceof PrimitiveArrayCodec) { js.print("new Array()"); } else if (fieldCodec instanceof BeanCodec) { js.print("new " + f.getType().getName() + "()"); } else if (fieldCodec instanceof StringCodec) { js.print("\"" + defaultValue + "\""); } else if (fieldCodec instanceof CharacterCodec) { js.print("\"" + defaultValue + "\""); } else if (fieldCodec instanceof DateCodec) { Date date = (Date)defaultValue; long diff = date.getTime() - now.getTime(); if (diff <0) { diff *= -1; } if (diff < 1000 * 60) { js.print("new Date()"); }else { js.print("new Date(" + ((Date) defaultValue).getTime() +")"); } } else if (fieldCodec instanceof EnumCodec) { js.print("\"" + defaultValue + "\""); } else { js.print(defaultValue); } } js.println(";"); js.flush(); } js.println(" for (var key in options) {"); js.print(" "); for (Field f : (Collection<Field>) codec.getFieldsByName().values()) { js.println("if (key == \"" + f.getName() + "\") {"); js.println(" this." + f.getName() + " = options[key];"); js.print(" } else "); } js.println("{"); js.println(" throw \"Unknown option key \"+ key + \" in "+codec.getBeanClass().getName()+"\";"); js.println(" }"); js.println(" }"); // end constructor // serializer js.println(" this.serialize = function(){"); boolean hasCollections = false; for (Field f : (Collection<Field>) codec.getFieldsByName().values()) { Codec fieldCodec = registry.getCodec(f.getType()); if (fieldCodec instanceof CollectionCodec) { hasCollections = true; } } if (hasCollections) { js.println(" var i, length;"); } js.println(" var json = '{ \"_class\" : \"" + codec.getBeanClass().getName() + "\"';"); for (Field f : (Collection<Field>) codec.getFieldsByName().values()) { js.println(" if (this." + f.getName() + " != null) {"); Codec fieldCodec = registry.getCodec(f.getType()); if (registry.getBeanCodecs().containsKey(f.getType())) { js.println(" json += ', \"" + f.getName() + "\" : ' + this." + f.getName() + ".serialize();"); } else { if (fieldCodec instanceof CollectionCodec) { CollectionCodec collectionCodec = (CollectionCodec) fieldCodec; js.println(" for (i = 0, length = this." + f.getName() + ".length; i < length; i++) {"); js.println(" if (i > 0) { json += ', '; }"); Codec valueCodec = registry.getCodec(collectionCodec.getPrimitiveGenericType()); if (valueCodec instanceof BeanCodec) { js.println(" json += this." + f.getName() + "[i].serialize();"); } else if (valueCodec instanceof StringCodec) { js.println(" json += ', \"" + f.getName() + "\" : \"' + this." + f.getName() + "[i]+'\"';"); } else if (valueCodec instanceof CharacterCodec) { js.println(" json += ', \"" + f.getName() + "\" : \"' + this." + f.getName() + "[i]+'\"';"); } else if (valueCodec instanceof DateCodec) { js.println(" json += ', \"" + f.getName() + "\" : { \"_class\" : \"java.util.Date\", \"time\" : ' + this." + f.getName() + "[i].getTime()+' }';"); } else { js.println(" json += ', \"" + f.getName() + "\" : ' + this." + f.getName() + "[i];"); } js.println(" }"); } else if (fieldCodec instanceof ArrayCodec) { ArrayCodec arrayCodec = (ArrayCodec) fieldCodec; js.println(" for (i = 0, length = this." + f.getName() + ".length; i < length; i++) {"); js.println(" if (i > 0) { json += ', '; }"); Codec valueCodec = registry.getCodec(arrayCodec.getPrimitiveGenericType()); if (valueCodec instanceof BeanCodec) { js.println(" json += this." + f.getName() + "[i].serialize();"); } else if (valueCodec instanceof StringCodec) { js.println(" json += ', \"" + f.getName() + "\" : \"' + this." + f.getName() + "[i]+'\"';"); } else if (valueCodec instanceof CharacterCodec) { js.println(" json += ', \"" + f.getName() + "\" : \"' + this." + f.getName() + "[i]+'\"';"); } else if (valueCodec instanceof DateCodec) { js.println(" json += ', \"" + f.getName() + "\" : { \"_class\" : \"java.util.Date\", \"time\" : ' + this." + f.getName() + "[i].getTime()+' }';"); } else { js.println(" json += ', \"" + f.getName() + "\" : ' + this." + f.getName() + "[i];"); } js.println(" }"); } else if (fieldCodec instanceof PrimitiveArrayCodec) { PrimitiveArrayCodec arrayCodec = (PrimitiveArrayCodec) fieldCodec; js.println(" for (i = 0, length = this." + f.getName() + ".length; i < length; i++) {"); js.println(" if (i > 0) { json += ', '; }"); Codec valueCodec = registry.getCodec(arrayCodec.getPrimitiveGenericType()); if (valueCodec instanceof BeanCodec) { js.println(" json += this." + f.getName() + "[i].serialize();"); } else if (valueCodec instanceof StringCodec) { js.println(" json += ', \"" + f.getName() + "\" : \"' + this." + f.getName() + "[i]+'\"';"); } else if (valueCodec instanceof CharacterCodec) { js.println(" json += ', \"" + f.getName() + "\" : \"' + this." + f.getName() + "[i]+'\"';"); } else if (valueCodec instanceof DateCodec) { js.println(" json += ', \"" + f.getName() + "\" : { \"_class\" : \"java.util.Date\", \"time\" : ' + this." + f.getName() + "[i].getTime()+' }';"); } else { js.println(" json += ', \"" + f.getName() + "\" : ' + this." + f.getName() + "[i];"); } js.println(" }"); } else if (fieldCodec instanceof MapCodec) { throw new UnsupportedOperationException("Maps not supported"); } else if (fieldCodec instanceof StringCodec) { js.println(" json += ', \"" + f.getName() + "\" : \"' + this." + f.getName() + "+'\"';"); } else if (fieldCodec instanceof CharacterCodec) { js.println(" json += ', \"" + f.getName() + "\" : \"' + this." + f.getName() + "+'\"';"); } else if (fieldCodec instanceof DateCodec) { js.println(" json += ', \"" + f.getName() + "\" : { \"_class\" : \"java.util.Date\", \"time\" : ' + this." + f.getName() + ".getTime()+' }';"); } else { js.println(" json += ', \"" + f.getName() + "\" : ' + this." + f.getName() + ";"); } } js.println(" }"); // end if not null } js.println(" json += ' }';"); js.println(" return json;"); js.println(" };"); // end serializer js.println("};"); // end class compiled.put(codec.getBeanClass(), sw.toString()); } js.flush(); } List<Map.Entry<Class, String>> ordered = new ArrayList<Map.Entry<Class, String>>(compiled.entrySet()); Collections.sort(ordered, new Comparator<Map.Entry<Class, String>>() { @Override public int compare(Map.Entry<Class, String> classStringEntry, Map.Entry<Class, String> classStringEntry1) { return classStringEntry.getKey().getName().compareTo(classStringEntry1.getKey().getName()); } }); Map<List<String>, Set<Class>> packages = new HashMap<List<String>, Set<Class>>(); for (Class type : compiled.keySet()) { List<String> path = Arrays.asList(type.getPackage().getName().split("\\.")); for (int i = 1; i <= path.size(); i++) { List<String> pack = new ArrayList<String>(path.subList(0, i)); Set<Class> classes = packages.get(pack); if (classes == null) { classes = new HashSet<Class>(); packages.put(pack, classes); } classes.add(type); } } List<List<String>> orderedPackages = new ArrayList<List<String>>(packages.keySet()); Collections.sort(orderedPackages, new Comparator<List<String>>() { @Override public int compare(List<String> strings, List<String> strings1) { int ret; for (int i = 0; i < strings.size() && i < strings1.size(); i++) { ret = strings.get(i).compareTo(strings1.get(i)); if (ret != 0) { return ret; } } return strings.size() - strings1.size(); } }); Set<List<String>> defined = new HashSet<List<String>>(); for (List<String> pack : orderedPackages) { for (int i = 0; i < pack.size(); i++) { List<String> path = pack.subList(0, i); if (defined.add(path)) { if (i == 0) { js.print("var "); js.print(pack.get(0)); } else { for (int i2 = 0; i2 <= i; i2++) { if (i2 > 0) { js.print("."); } js.print(pack.get(i2)); } } js.println(" = new Object();"); } } } for (Map.Entry<Class, String> e : ordered) { js.print(e.getKey().getName()); js.print(" = function"); js.println(e.getValue()); js.flush(); } js.flush(); } }