com.willowtreeapps.androidcontentprovidergenerator.Main.java Source code

Java tutorial

Introduction

Here is the source code for com.willowtreeapps.androidcontentprovidergenerator.Main.java

Source

/*
 * This source is part of the
 *      _____  ___   ____
 *  __ / / _ \/ _ | / __/___  _______ _
 * / // / , _/ __ |/ _/_/ _ \/ __/ _ `/
 * \___/_/|_/_/ |_/_/ (_)___/_/  \_, /
 *                              /___/
 * repository.
 * 
 * Copyright 2012 Benoit 'BoD' Lubek (BoD@JRAF.org).  All Rights Reserved.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.willowtreeapps.androidcontentprovidergenerator;

import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.beust.jcommander.JCommander;
import com.willowtreeapps.androidcontentprovidergenerator.model.Constraint;
import com.willowtreeapps.androidcontentprovidergenerator.model.Entity;
import com.willowtreeapps.androidcontentprovidergenerator.model.Field;
import com.willowtreeapps.androidcontentprovidergenerator.model.Model;

import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;

public class Main {
    private static String TAG = Constants.TAG + Main.class.getSimpleName();

    private static String FILE_CONFIG = "_config.json";

    public static class Json {
        public static final String TOOL_VERSION = "toolVersion";
        public static final String PROJECT_PACKAGE_ID = "projectPackageId";
        public static final String PROVIDER_JAVA_PACKAGE = "providerJavaPackage";
        public static final String PROVIDER_CLASS_NAME = "providerClassName";
        public static final String SQLITE_HELPER_CLASS_NAME = "sqliteHelperClassName";
        public static final String AUTHORITY = "authority";
        public static final String DATABASE_FILE_NAME = "databaseFileName";
        public static final String ENABLE_FOREIGN_KEY = "enableForeignKeys";
        public static final String PROJECT_BASE_URL = "projectBaseUrl";

        public static final String GENERATE_PROVIDER = "generateProvider";
        public static final String GENERATE_MODELS = "generateModels";
        public static final String GENERATE_VIEWS = "generateViews";
        public static final String GENERATE_API = "generateApi";
        public static final String GENERATE_FRAGMENT = "generateFragments";
    }

    private Configuration mFreemarkerConfig;
    private JSONObject mConfig;

    private Configuration getFreeMarkerConfig() {
        if (mFreemarkerConfig == null) {
            mFreemarkerConfig = new Configuration();
            mFreemarkerConfig.setClassForTemplateLoading(getClass(), "");
            mFreemarkerConfig.setObjectWrapper(new DefaultObjectWrapper());
        }
        return mFreemarkerConfig;
    }

    private void loadModel(File inputDir) throws IOException, JSONException {
        File[] entityFiles = inputDir.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return !pathname.getName().startsWith("_") && pathname.getName().endsWith(".json");
            }
        });
        for (File entityFile : entityFiles) {
            if (Config.LOGD)
                Log.d(TAG, entityFile.getCanonicalPath());
            String entityName = FilenameUtils.getBaseName(entityFile.getCanonicalPath());
            if (Config.LOGD)
                Log.d(TAG, "entityName=" + entityName);
            Entity entity = new Entity(entityName);
            String fileContents = FileUtils.readFileToString(entityFile);
            JSONObject entityJson = new JSONObject(fileContents);
            entity.setUrl(entityJson.optString("urlPath"));
            // Fields
            JSONArray fieldsJson = entityJson.getJSONArray("fields");
            int len = fieldsJson.length();
            for (int i = 0; i < len; i++) {
                JSONObject fieldJson = fieldsJson.getJSONObject(i);
                if (Config.LOGD)
                    Log.d(TAG, "fieldJson=" + fieldJson);
                String name = fieldJson.getString(Field.Json.NAME);
                String serializedName = fieldJson.optString(Field.Json.SERIALIZED_NAME);
                String type = fieldJson.getString(Field.Json.TYPE);
                boolean isIndex = fieldJson.optBoolean(Field.Json.INDEX, false);
                boolean isNullable = fieldJson.optBoolean(Field.Json.NULLABLE, true);
                String defaultValue = fieldJson.optString(Field.Json.DEFAULT_VALUE);
                String enumName = fieldJson.optString(Field.Json.ENUM_NAME);
                JSONArray enumValuesJson = fieldJson.optJSONArray(Field.Json.ENUM_VALUES);
                List<String> enumValues = new ArrayList<String>();
                if (enumValuesJson != null) {
                    int enumLen = enumValuesJson.length();
                    for (int j = 0; j < enumLen; j++) {
                        String valueName = enumValuesJson.getString(j);
                        enumValues.add(valueName);
                    }
                }
                Field field = new Field(name, serializedName, type, isIndex, isNullable, defaultValue, enumName,
                        enumValues);
                entity.addField(field);
            }

            // Constraints (optional)
            JSONArray constraintsJson = entityJson.optJSONArray("constraints");
            if (constraintsJson != null) {
                len = constraintsJson.length();
                for (int i = 0; i < len; i++) {
                    JSONObject constraintJson = constraintsJson.getJSONObject(i);
                    if (Config.LOGD)
                        Log.d(TAG, "constraintJson=" + constraintJson);
                    String name = constraintJson.getString(Constraint.Json.NAME);
                    String definition = constraintJson.getString(Constraint.Json.DEFINITION);
                    Constraint constraint = new Constraint(name, definition);
                    entity.addConstraint(constraint);
                }
            }

            // QueryParams (optional)
            JSONArray paramsJson = entityJson.optJSONArray("queryParams");
            if (paramsJson != null) {
                len = paramsJson.length();
                for (int i = 0; i < len; i++) {
                    JSONObject constraintJson = paramsJson.getJSONObject(i);
                    String name = constraintJson.getString(Constraint.Json.NAME);
                    entity.addQueryParam(name);
                }
            }

            Model.get().addEntity(entity);
        }
        // Header (optional)
        File headerFile = new File(inputDir, "header.txt");
        if (headerFile.exists()) {
            String header = FileUtils.readFileToString(headerFile).trim();
            Model.get().setHeader(header);
        }
        if (Config.LOGD)
            Log.d(TAG, Model.get().toString());
    }

    private JSONObject getConfig(File inputDir) throws IOException, JSONException {
        if (mConfig == null) {
            File configFile = new File(inputDir, FILE_CONFIG);
            String fileContents = FileUtils.readFileToString(configFile);
            mConfig = new JSONObject(fileContents);
        }

        validateConfig();

        return mConfig;
    }

    private void validateConfig() {
        // Ensure the input files are compatible with this version of the tool
        String configVersion;
        try {
            configVersion = mConfig.getString(Json.TOOL_VERSION);
        } catch (JSONException e) {
            throw new IllegalArgumentException(
                    "Could not find 'toolVersion' field in _config.json, which is mandatory and must be equals to '"
                            + Constants.VERSION + "'.");
        }
        if (!configVersion.equals(Constants.VERSION)) {
            throw new IllegalArgumentException("Invalid 'toolVersion' value in _config.json: found '"
                    + configVersion + "' but expected '" + Constants.VERSION + "'.");
        }

        // Ensure mandatory fields are present
        if (mConfig.optBoolean(Json.GENERATE_PROVIDER, true)) {
            ensureString(Json.PROVIDER_JAVA_PACKAGE);
            ensureString(Json.PROVIDER_CLASS_NAME);
            ensureString(Json.SQLITE_HELPER_CLASS_NAME);
            ensureString(Json.AUTHORITY);
            ensureString(Json.DATABASE_FILE_NAME);
            ensureBoolean(Json.ENABLE_FOREIGN_KEY);
        }
        if (mConfig.optBoolean(Json.GENERATE_API, true)) {
            ensureString(Json.PROJECT_BASE_URL);
        }
        ensureString(Json.PROJECT_PACKAGE_ID);

    }

    private void ensureString(String field) {
        try {
            mConfig.getString(field);
        } catch (JSONException e) {
            throw new IllegalArgumentException("Could not find '" + field
                    + "' field in _config.json, which is mandatory and must be a string.");
        }
    }

    private void ensureBoolean(String field) {
        try {
            mConfig.getBoolean(field);
        } catch (JSONException e) {
            throw new IllegalArgumentException("Could not find '" + field
                    + "' field in _config.json, which is mandatory and must be a boolean.");
        }
    }

    private void generateColumns(Arguments arguments) throws IOException, JSONException, TemplateException {
        Template template = getFreeMarkerConfig().getTemplate("columns.ftl");
        JSONObject config = getConfig(arguments.inputDir);
        String providerJavaPackage = config.getString(Json.PROVIDER_JAVA_PACKAGE);

        File providerDir = new File(arguments.outputDir, providerJavaPackage.replace('.', '/'));
        Map<String, Object> root = new HashMap<String, Object>();
        root.put("config", getConfig(arguments.inputDir));
        root.put("header", Model.get().getHeader());

        // Entities
        for (Entity entity : Model.get().getEntities()) {
            File outputDir = new File(providerDir, entity.getNameLowerCase());
            outputDir.mkdirs();
            File outputFile = new File(outputDir, entity.getNameCamelCase() + "Columns.java");
            Writer out = new OutputStreamWriter(new FileOutputStream(outputFile));

            root.put("entity", entity);

            template.process(root, out);
            IOUtils.closeQuietly(out);
        }
    }

    private void generateWrappers(Arguments arguments) throws IOException, JSONException, TemplateException {
        JSONObject config = getConfig(arguments.inputDir);
        String providerJavaPackage = config.getString(Json.PROVIDER_JAVA_PACKAGE);
        File providerDir = new File(arguments.outputDir, providerJavaPackage.replace('.', '/'));
        File baseClassesDir = new File(providerDir, "base");
        baseClassesDir.mkdirs();

        Map<String, Object> root = new HashMap<String, Object>();
        root.put("config", getConfig(arguments.inputDir));
        root.put("header", Model.get().getHeader());

        // AbstractCursor
        Template template = getFreeMarkerConfig().getTemplate("abstractcursor.ftl");
        File outputFile = new File(baseClassesDir, "AbstractCursor.java");
        Writer out = new OutputStreamWriter(new FileOutputStream(outputFile));
        template.process(root, out);
        IOUtils.closeQuietly(out);

        // AbstractContentValuesWrapper
        template = getFreeMarkerConfig().getTemplate("abstractcontentvalues.ftl");
        outputFile = new File(baseClassesDir, "AbstractContentValues.java");
        out = new OutputStreamWriter(new FileOutputStream(outputFile));
        template.process(root, out);
        IOUtils.closeQuietly(out);

        // AbstractSelection
        template = getFreeMarkerConfig().getTemplate("abstractselection.ftl");
        outputFile = new File(baseClassesDir, "AbstractSelection.java");
        out = new OutputStreamWriter(new FileOutputStream(outputFile));
        template.process(root, out);
        IOUtils.closeQuietly(out);

        // Entities
        for (Entity entity : Model.get().getEntities()) {
            File entityDir = new File(providerDir, entity.getNameLowerCase());
            entityDir.mkdirs();

            // Cursor wrapper
            outputFile = new File(entityDir, entity.getNameCamelCase() + "Cursor.java");
            out = new OutputStreamWriter(new FileOutputStream(outputFile));
            root.put("entity", entity);
            template = getFreeMarkerConfig().getTemplate("cursor.ftl");
            template.process(root, out);
            IOUtils.closeQuietly(out);

            // ContentValues wrapper
            outputFile = new File(entityDir, entity.getNameCamelCase() + "ContentValues.java");
            out = new OutputStreamWriter(new FileOutputStream(outputFile));
            root.put("entity", entity);
            template = getFreeMarkerConfig().getTemplate("contentvalues.ftl");
            template.process(root, out);
            IOUtils.closeQuietly(out);

            // Selection builder
            outputFile = new File(entityDir, entity.getNameCamelCase() + "Selection.java");
            out = new OutputStreamWriter(new FileOutputStream(outputFile));
            root.put("entity", entity);
            template = getFreeMarkerConfig().getTemplate("selection.ftl");
            template.process(root, out);
            IOUtils.closeQuietly(out);

            // Enums (if any)
            for (Field field : entity.getFields()) {
                if (field.isEnum()) {
                    outputFile = new File(entityDir, field.getEnumName() + ".java");
                    out = new OutputStreamWriter(new FileOutputStream(outputFile));
                    root.put("entity", entity);
                    root.put("field", field);
                    template = getFreeMarkerConfig().getTemplate("enum.ftl");
                    template.process(root, out);
                    IOUtils.closeQuietly(out);
                }
            }
        }
    }

    private void generateContentProvider(Arguments arguments) throws IOException, JSONException, TemplateException {
        Template template = getFreeMarkerConfig().getTemplate("contentprovider.ftl");
        JSONObject config = getConfig(arguments.inputDir);
        String providerJavaPackage = config.getString(Json.PROVIDER_JAVA_PACKAGE);
        File providerDir = new File(arguments.outputDir, providerJavaPackage.replace('.', '/'));
        providerDir.mkdirs();
        File outputFile = new File(providerDir, config.getString(Json.PROVIDER_CLASS_NAME) + ".java");
        Writer out = new OutputStreamWriter(new FileOutputStream(outputFile));

        Map<String, Object> root = new HashMap<String, Object>();
        root.put("config", config);
        root.put("model", Model.get());
        root.put("header", Model.get().getHeader());

        template.process(root, out);
    }

    private void generateIntentService(Arguments arguments) throws IOException, JSONException, TemplateException {
        Template template = getFreeMarkerConfig().getTemplate("intentservice.ftl");
        JSONObject config = getConfig(arguments.inputDir);
        String apiJavaPackage = config.getString(Json.PROJECT_PACKAGE_ID) + ".api";
        File apiDir = new File(arguments.outputDir, apiJavaPackage.replace('.', '/'));
        apiDir.mkdirs();
        File outputFile = new File(apiDir, "ApiService.java");
        Writer out = new OutputStreamWriter(new FileOutputStream(outputFile));

        Map<String, Object> root = new HashMap<String, Object>();
        root.put("config", config);
        root.put("model", Model.get());
        root.put("header", Model.get().getHeader());

        template.process(root, out);
    }

    private void generateRestService(Arguments arguments) throws IOException, JSONException, TemplateException {
        Template template = getFreeMarkerConfig().getTemplate("retroservice.ftl");
        JSONObject config = getConfig(arguments.inputDir);
        String apiJavaPackage = config.getString(Json.PROJECT_PACKAGE_ID) + ".api";
        File apiDir = new File(arguments.outputDir, apiJavaPackage.replace('.', '/'));
        apiDir.mkdirs();
        File outputFile = new File(apiDir, "RestService.java");
        Writer out = new OutputStreamWriter(new FileOutputStream(outputFile));

        Map<String, Object> root = new HashMap<String, Object>();
        root.put("config", config);
        root.put("model", Model.get());
        root.put("header", Model.get().getHeader());

        template.process(root, out);
    }

    private void generateManifestItems(Arguments arguments) throws IOException, JSONException, TemplateException {
        Template template = getFreeMarkerConfig().getTemplate("add_to_manifest.ftl");
        JSONObject config = getConfig(arguments.inputDir);
        File outputFile = new File(arguments.outputDir, "__add_to_manifest.txt");
        Writer out = new OutputStreamWriter(new FileOutputStream(outputFile));

        Map<String, Object> root = new HashMap<String, Object>();
        root.put("config", config);
        root.put("model", Model.get());
        root.put("header", Model.get().getHeader());

        template.process(root, out);
    }

    private void generateSqliteHelper(Arguments arguments) throws IOException, JSONException, TemplateException {
        Template template = getFreeMarkerConfig().getTemplate("sqlitehelper.ftl");
        JSONObject config = getConfig(arguments.inputDir);
        String providerJavaPackage = config.getString(Json.PROVIDER_JAVA_PACKAGE);
        File providerDir = new File(arguments.outputDir, providerJavaPackage.replace('.', '/'));
        providerDir.mkdirs();
        File outputFile = new File(providerDir, config.getString(Json.SQLITE_HELPER_CLASS_NAME) + ".java");
        Writer out = new OutputStreamWriter(new FileOutputStream(outputFile));

        Map<String, Object> root = new HashMap<String, Object>();
        root.put("config", config);
        root.put("model", Model.get());
        root.put("header", Model.get().getHeader());

        template.process(root, out);
    }

    private void generateModels(Arguments arguments) throws IOException, JSONException, TemplateException {
        JSONObject config = getConfig(arguments.inputDir);
        File baseDir = new File(arguments.outputDir, config.getString(Json.PROJECT_PACKAGE_ID).replace('.', '/'));
        File modelClassesDir = new File(baseDir, "model");
        modelClassesDir.mkdirs();
        Map<String, Object> root = new HashMap<String, Object>();
        root.put("config", getConfig(arguments.inputDir));
        root.put("header", Model.get().getHeader());
        for (Entity entity : Model.get().getEntities()) {
            File outputFile = new File(modelClassesDir, entity.getNameCamelCase() + "Model.java");
            Writer out = new OutputStreamWriter(new FileOutputStream(outputFile));
            root.put("entity", entity);
            Template template = getFreeMarkerConfig().getTemplate("model.ftl");
            template.process(root, out);
            IOUtils.closeQuietly(out);
        }
    }

    private void generateFragments(Arguments arguments) throws IOException, JSONException, TemplateException {
        JSONObject config = getConfig(arguments.inputDir);
        File baseDir = new File(arguments.outputDir, config.getString(Json.PROJECT_PACKAGE_ID).replace('.', '/'));
        File fragmentClassDir = new File(baseDir, "fragment");
        fragmentClassDir.mkdirs();
        Map<String, Object> root = new HashMap<String, Object>();
        root.put("config", getConfig(arguments.inputDir));
        root.put("header", Model.get().getHeader());
        for (Entity entity : Model.get().getEntities()) {
            File outputFile = new File(fragmentClassDir, entity.getNameCamelCase() + "ListFragment.java");
            Writer out = new OutputStreamWriter(new FileOutputStream(outputFile));
            root.put("entity", entity);
            Template template = getFreeMarkerConfig().getTemplate("fragment.ftl");
            template.process(root, out);
            IOUtils.closeQuietly(out);
        }
    }

    private void generateViews(Arguments arguments) throws IOException, JSONException, TemplateException {
        JSONObject config = getConfig(arguments.inputDir);
        File baseDir = new File(arguments.outputDir, config.getString(Json.PROJECT_PACKAGE_ID).replace('.', '/'));
        File viewDir = new File(baseDir, "ui/viewmodel");
        File resDir = new File(arguments.outputDir + "/res", "layout");
        viewDir.mkdirs();
        resDir.mkdirs();
        Map<String, Object> root = new HashMap<String, Object>();
        root.put("config", getConfig(arguments.inputDir));
        root.put("header", Model.get().getHeader());
        for (Entity entity : Model.get().getEntities()) {
            File outputFile = new File(viewDir, entity.getNameCamelCase() + "View.java");
            Writer out = new OutputStreamWriter(new FileOutputStream(outputFile));
            root.put("entity", entity);
            Template template = getFreeMarkerConfig().getTemplate("view.ftl");
            template.process(root, out);
            IOUtils.closeQuietly(out);

            outputFile = new File(resDir, "view_" + entity.getNameLowerCase() + ".xml");
            out = new OutputStreamWriter(new FileOutputStream(outputFile));
            root.put("entity", entity);
            template = getFreeMarkerConfig().getTemplate("layout.ftl");
            template.process(root, out);
            IOUtils.closeQuietly(out);
        }
    }

    private void go(String[] args) throws IOException, JSONException, TemplateException {
        Arguments arguments = new Arguments();
        JCommander jCommander = new JCommander(arguments, args);
        jCommander.setProgramName("GenerateAndroidProvider");

        if (arguments.help) {
            jCommander.usage();
            return;
        }

        JSONObject config = getConfig(arguments.inputDir);

        loadModel(arguments.inputDir);
        if (config.optBoolean(Json.GENERATE_PROVIDER, true)) {
            generateColumns(arguments);
            generateWrappers(arguments);
            generateContentProvider(arguments);
            generateSqliteHelper(arguments);
        }
        if (config.optBoolean(Json.GENERATE_API, true)) {
            generateIntentService(arguments);
            generateRestService(arguments);
        }
        if (config.optBoolean(Json.GENERATE_API, true) || config.optBoolean(Json.GENERATE_PROVIDER, true)) {
            generateManifestItems(arguments);
        }
        if (config.optBoolean(Json.GENERATE_VIEWS, true)) {
            generateViews(arguments);
        }

        if (config.optBoolean(Json.GENERATE_MODELS, true)) {
            generateModels(arguments);
        }

        if (config.optBoolean(Json.GENERATE_FRAGMENT)) {
            generateFragments(arguments);
        }
    }

    public static void main(String[] args) throws Exception {
        new Main().go(args);
    }
}