org.turbogwt.net.http.rebind.JsonSerdesGenerator.java Source code

Java tutorial

Introduction

Here is the source code for org.turbogwt.net.http.rebind.JsonSerdesGenerator.java

Source

/*
 * Copyright 2014 Grow Bit
 *
 * Licensed 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.turbogwt.net.http.rebind;

import com.github.nmorel.gwtjackson.client.ObjectMapper;
import com.github.nmorel.gwtjackson.client.ObjectReader;
import com.github.nmorel.gwtjackson.client.ObjectWriter;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JPackage;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.turbogwt.core.util.client.Overlays;
import org.turbogwt.net.serialization.client.DeserializationContext;
import org.turbogwt.net.serialization.client.Deserializer;
import org.turbogwt.net.serialization.client.Serdes;
import org.turbogwt.net.serialization.client.SerializationContext;
import org.turbogwt.net.serialization.client.Serializer;
import org.turbogwt.net.serialization.client.json.JsonObjectSerdes;
import org.turbogwt.net.serialization.client.json.JsonRecordReader;
import org.turbogwt.net.serialization.client.json.JsonRecordWriter;
import org.turbogwt.net.serialization.shared.Json;

/**
 * Generator for {@link org.turbogwt.net.serialization.shared.Json} annotated types.
 *
 * @author Danilo Reinert
 */
public class JsonSerdesGenerator extends Generator {

    @Override
    public String generate(TreeLogger logger, GeneratorContext ctx, String typeName)
            throws UnableToCompleteException {
        TypeOracle typeOracle = ctx.getTypeOracle();
        assert typeOracle != null;

        JClassType intfType = typeOracle.findType(typeName);
        if (intfType == null) {
            logger.log(TreeLogger.ERROR, "Unable to find metadata for type '" + typeName + "'", null);
            throw new UnableToCompleteException();
        }

        if (intfType.isInterface() == null) {
            logger.log(TreeLogger.ERROR, intfType.getQualifiedSourceName() + " is not an interface", null);
            throw new UnableToCompleteException();
        }

        TreeLogger typeLogger = logger.branch(TreeLogger.ALL, "Generating SerDes powered by Gwt Jackson...", null);
        final SourceWriter sourceWriter = getSourceWriter(typeLogger, ctx, intfType);

        if (sourceWriter != null) {
            sourceWriter.println();

            final ArrayList<String> serdes = new ArrayList<>();
            for (JClassType type : typeOracle.getTypes()) {
                Json annotation = type.getAnnotation(Json.class);
                if (annotation != null) {
                    serdes.add(generateSerdes(sourceWriter, type, annotation));
                }
            }

            generateFields(sourceWriter);
            generateConstructor(sourceWriter, serdes);
            generateIteratorMethod(sourceWriter);

            sourceWriter.commit(typeLogger);
        }

        return typeName + "Impl";
    }

    private String getTypeSimpleName() {
        return "GeneratedJsonSerdesImpl";
    }

    private SourceWriter getSourceWriter(TreeLogger logger, GeneratorContext ctx, JClassType intfType) {
        JPackage serviceIntfPkg = intfType.getPackage();
        String packageName = serviceIntfPkg == null ? "" : serviceIntfPkg.getName();
        PrintWriter printWriter = ctx.tryCreate(logger, packageName, getTypeSimpleName());
        if (printWriter == null) {
            return null;
        }

        ClassSourceFileComposerFactory composerFactory = new ClassSourceFileComposerFactory(packageName,
                getTypeSimpleName());

        String[] imports = new String[] {
                // java
                ArrayList.class.getCanonicalName(), Collection.class.getCanonicalName(),
                HashSet.class.getCanonicalName(), Iterator.class.getCanonicalName(),
                LinkedHashSet.class.getCanonicalName(), LinkedList.class.getCanonicalName(),
                List.class.getCanonicalName(), Set.class.getCanonicalName(), TreeSet.class.getCanonicalName(),
                // com.github.nmorel.gwtjackson
                ObjectMapper.class.getCanonicalName(), ObjectReader.class.getCanonicalName(),
                ObjectWriter.class.getCanonicalName(),
                // com.google.gwt
                GWT.class.getCanonicalName(),
                // org.turbogwt
                Overlays.class.getCanonicalName(), DeserializationContext.class.getCanonicalName(),
                Deserializer.class.getCanonicalName(), JsonRecordReader.class.getCanonicalName(),
                JsonObjectSerdes.class.getCanonicalName(), JsonRecordWriter.class.getCanonicalName(),
                Serdes.class.getCanonicalName(), SerializationContext.class.getCanonicalName(),
                Serializer.class.getCanonicalName() };

        for (String imp : imports) {
            composerFactory.addImport(imp);
        }

        composerFactory.addImplementedInterface(intfType.getErasedType().getQualifiedSourceName());

        return composerFactory.createSourceWriter(ctx, printWriter);
    }

    /**
     * Create the serdes and return the field name.
     */
    private String generateSerdes(SourceWriter srcWriter, JClassType type, Json annotation) {
        final String qualifiedSourceName = type.getQualifiedSourceName();

        final String qualifiedCamelCaseFieldName = replaceDotByUpperCase(qualifiedSourceName);
        final String qualifiedCamelCaseTypeName = Character.toUpperCase(qualifiedCamelCaseFieldName.charAt(0))
                + qualifiedCamelCaseFieldName.substring(1);

        final String singleMapperType = qualifiedCamelCaseTypeName + "Mapper";
        final String arrayListMapperType = qualifiedCamelCaseTypeName + "ArrayListMapper";
        final String linkedListMapperType = qualifiedCamelCaseTypeName + "LinkedListMapper";
        final String hashSetMapperType = qualifiedCamelCaseTypeName + "HashSetMapper";
        final String linkedHashSetMapperType = qualifiedCamelCaseTypeName + "LinkedHashSetMapper";
        final String treeSetMapperType = qualifiedCamelCaseTypeName + "TreeSetMapper";

        // interfaces extending Gwt Jackson
        srcWriter.println("interface %s extends ObjectMapper<%s> {}", singleMapperType, qualifiedSourceName);
        srcWriter.println("interface %s extends ObjectMapper<ArrayList<%s>> {}", arrayListMapperType,
                qualifiedSourceName);
        srcWriter.println("interface %s extends ObjectMapper<LinkedList<%s>> {}", linkedListMapperType,
                qualifiedSourceName);
        srcWriter.println("interface %s extends ObjectMapper<HashSet<%s>> {}", hashSetMapperType,
                qualifiedSourceName);
        srcWriter.println("interface %s extends ObjectMapper<TreeSet<%s>> {}", treeSetMapperType,
                qualifiedSourceName);
        srcWriter.println("interface %s extends ObjectMapper<LinkedHashSet<%s>> {}", linkedHashSetMapperType,
                qualifiedSourceName);
        srcWriter.println();

        final String singleMapperField = qualifiedCamelCaseFieldName + "Mapper";
        final String arrayListMapperField = qualifiedCamelCaseFieldName + "ArrayListMapper";
        final String linkedListMapperField = qualifiedCamelCaseFieldName + "LinkedListMapper";
        final String hashSetMapperField = qualifiedCamelCaseFieldName + "HashSetMapper";
        final String linkedHashSetMapperField = qualifiedCamelCaseFieldName + "LinkedHashSetMapper";
        final String treeSetMapperField = qualifiedCamelCaseFieldName + "TreeSetMapper";

        // fields creating interfaces
        srcWriter.println("private final %s %s = GWT.create(%s.class);", singleMapperType, singleMapperField,
                singleMapperType);
        srcWriter.println("private final %s %s = GWT.create(%s.class);", arrayListMapperType, arrayListMapperField,
                arrayListMapperType);
        srcWriter.println("private final %s %s = GWT.create(%s.class);", linkedListMapperType,
                linkedListMapperField, linkedListMapperType);
        srcWriter.println("private final %s %s = GWT.create(%s.class);", hashSetMapperType, hashSetMapperField,
                hashSetMapperType);
        srcWriter.println("private final %s %s = GWT.create(%s.class);", linkedHashSetMapperType,
                linkedHashSetMapperField, linkedHashSetMapperType);
        srcWriter.println("private final %s %s = GWT.create(%s.class);", treeSetMapperType, treeSetMapperField,
                treeSetMapperType);
        srcWriter.println();

        final String serdesField = qualifiedCamelCaseFieldName + "Serdes";
        final String serdesType = "JsonObjectSerdes<" + qualifiedSourceName + ">";

        // serializer field as anonymous class
        srcWriter.println("private final %s %s = new %s(%s.class) {", serdesType, serdesField, serdesType,
                qualifiedSourceName);
        srcWriter.println();

        // static field to content-types
        srcWriter.println("    private final String[] PATTERNS = new String[]{ %s };",
                asStringCsv(annotation.value()));
        srcWriter.println();

        // readJson
        srcWriter.println("    @Override");
        srcWriter.println("    public %s readJson(JsonRecordReader r, DeserializationContext ctx) {",
                qualifiedSourceName);
        srcWriter.println("        return %s.read(Overlays.stringify(r));", singleMapperField);
        srcWriter.println("    }");
        srcWriter.println();

        // writeJson
        srcWriter.println("    @Override");
        srcWriter.println("    public void writeJson(%s o, JsonRecordWriter w, SerializationContext ctx) {",
                qualifiedSourceName);
        srcWriter.println("        return;");
        srcWriter.println("    }");
        srcWriter.println();

        // contentType
        srcWriter.println("    @Override");
        srcWriter.println("    public String[] contentType() {");
        srcWriter.println("        return PATTERNS;");
        srcWriter.println("    }");
        srcWriter.println();

        // deserialize
        srcWriter.println("    @Override");
        srcWriter.println("    public %s deserialize(String s, DeserializationContext ctx) {", qualifiedSourceName);
        srcWriter.println("        return %s.read(s);", singleMapperField);
        srcWriter.println("    }");
        srcWriter.println();

        // deserializeAsCollection
        srcWriter.println("    @Override");
        srcWriter.println("    public <C extends Collection<%s>> C deserializeAsCollection(Class<C> c, "
                + "String s, DeserializationContext ctx) {", qualifiedSourceName);
        srcWriter.println("        if (c == List.class || c == ArrayList.class || c == Collection.class)");
        srcWriter.println("            return (C) %s.read(s);", arrayListMapperField);
        srcWriter.println("        else if (c == LinkedList.class)");
        srcWriter.println("            return (C) %s.read(s);", linkedListMapperField);
        srcWriter.println("        else if (c == Set.class || c == HashSet.class)");
        srcWriter.println("            return (C) %s.read(s);", hashSetMapperField);
        srcWriter.println("        else if (c == TreeSet.class)");
        srcWriter.println("            return (C) %s.read(s);", treeSetMapperField);
        srcWriter.println("        else if (c == LinkedHashSet.class)");
        srcWriter.println("            return (C) %s.read(s);", linkedHashSetMapperField);
        srcWriter.println("        else");
        srcWriter.println("            return super.deserializeAsCollection(c, s, ctx);");
        srcWriter.println("    }");

        // serialize
        srcWriter.println("    @Override");
        srcWriter.println("    public String serialize(%s o, SerializationContext ctx) {", qualifiedSourceName);
        srcWriter.println("        return %s.write(o);", singleMapperField);
        srcWriter.println("    }");
        srcWriter.println();

        // serializeFromCollection
        srcWriter.println("    @Override");
        srcWriter.println("    public String serializeFromCollection(Collection<%s> c, SerializationContext ctx) {",
                qualifiedSourceName);
        srcWriter.println("        if (c.getClass() == ArrayList.class)");
        srcWriter.println("            return %s.write((ArrayList) c);", arrayListMapperField);
        srcWriter.println("        else if (c.getClass() == LinkedList.class)");
        srcWriter.println("            return %s.write((LinkedList) c);", linkedListMapperField);
        srcWriter.println("        else if (c.getClass() == HashSet.class)");
        srcWriter.println("            return %s.write((HashSet) c);", hashSetMapperField);
        srcWriter.println("        else if (c.getClass() == TreeSet.class)");
        srcWriter.println("            return %s.write((TreeSet) c);", treeSetMapperField);
        srcWriter.println("        else if (c.getClass() == LinkedHashSet.class)");
        srcWriter.println("            return %s.write((LinkedHashSet) c);", linkedHashSetMapperField);
        srcWriter.println("        else");
        srcWriter.println("            return super.serializeFromCollection(c, ctx);");
        srcWriter.println("    }");

        // end anonymous class
        srcWriter.println("};");
        srcWriter.println();

        return serdesField;
    }

    private String asStringCsv(String[] array) {
        StringBuilder result = new StringBuilder();
        for (String s : array) {
            result.append('"').append(s).append('"').append(", ");
        }
        result.replace(result.length() - 2, result.length(), "");
        return result.toString();
    }

    private String replaceDotByUpperCase(String s) {
        StringBuilder result = new StringBuilder();

        for (int i = 0; i < s.length(); i++) {
            final char c = s.charAt(i);
            if (c == '.') {
                result.append(Character.toUpperCase(s.charAt(++i)));
            } else {
                result.append(c);
            }
        }

        return result.toString();
    }

    private void generateFields(SourceWriter srcWriter) {
        // Initialize a field with binary name of the remote service interface
        srcWriter.println("private final ArrayList<Serdes<?>> serdesList = new ArrayList<Serdes<?>>();");
        srcWriter.println();
    }

    private void generateConstructor(SourceWriter srcWriter, ArrayList<String> serdes) {
        srcWriter.println("public GeneratedJsonSerdesImpl() {");
        for (String s : serdes) {
            srcWriter.println("    serdesList.add(%s);", s);
        }
        srcWriter.println("}");
        srcWriter.println();
    }

    private void generateIteratorMethod(SourceWriter srcWriter) {
        srcWriter.println("@Override");
        srcWriter.println("public Iterator<Serdes<?>> iterator() {");
        srcWriter.println("    return serdesList.iterator();");
        srcWriter.println("}");
        srcWriter.println();
    }
}