aurelienribon.gdxsetupui.ProjectSetup.java Source code

Java tutorial

Introduction

Here is the source code for aurelienribon.gdxsetupui.ProjectSetup.java

Source

/*******************************************************************************
 * Copyright 2011 See AUTHORS file.
 * 
 * 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 aurelienribon.gdxsetupui;

import aurelienribon.utils.Res;
import aurelienribon.utils.TemplateManager;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;

/**
 * Standalone class used to completely setup a libgdx project.
 * Uses a ProjectConfiguration instance as parameter, and provides several
 * methods to initialize the sub-projects step-by-step.
 * <p/>
 *
 * The raw projects are contained in a zip file. The first thing to do is
 * to inflate this file on the hard drive to get the files adn folders of the
 * projects. Then, selected libraries should be inflated too in libs folders
 * of the projects, and configurated. Post-process is required to update the
 * files of the projects, and then a call to copy() moves everything to the
 * right location.
 * <pre>
 * 1. inflateProjects
 * 2. inflateLibraries
 * 3. configureLibraries
 * 4. postProcess
 * 5. copy
 * 6. clean
 * </pre>
 *
 * @author Aurelien Ribon | http://www.aurelienribon.com/
 */
public class ProjectSetup {
    private final ProjectSetupConfiguration cfg;
    private final LibraryManager libs;
    private final File tmpDst = new File("__libgdx_setup_tmp");
    private final TemplateManager templateManager = new TemplateManager();

    public ProjectSetup(ProjectSetupConfiguration cfg, LibraryManager libs) {
        this.cfg = cfg;
        this.libs = libs;

        // General definitions
        templateManager.define("PROJECT_NAME", cfg.projectName);
        templateManager.define("MAINCLASS_NAME", cfg.mainClassName);
        templateManager.define("PACKAGE_NAME", cfg.packageName);
        templateManager.define("PACKAGE_NAME_AS_PATH", cfg.packageName.replace('.', '/'));

        // Project specific definitions
        templateManager.define("PRJ_COMMON_NAME", cfg.projectName + cfg.suffixCommon);
        if (cfg.isDesktopIncluded)
            templateManager.define("PRJ_DESKTOP_NAME", cfg.projectName + cfg.suffixDesktop);
        if (cfg.isAndroidIncluded)
            templateManager.define("PRJ_ANDROID_NAME", cfg.projectName + cfg.suffixAndroid);
        if (cfg.isHtmlIncluded)
            templateManager.define("PRJ_HTML_NAME", cfg.projectName + cfg.suffixHtml);
        if (cfg.isIosIncluded)
            templateManager.define("PRJ_IOS_NAME", cfg.projectName + cfg.suffixIos);

        // Android manifest definitions
        if (!cfg.androidMinSdkVersion.equals(""))
            templateManager.define("ANDROID_MIN_SDK", cfg.androidMinSdkVersion);
        if (!cfg.androidTargetSdkVersion.equals(""))
            templateManager.define("ANDROID_TARGET_SDK", cfg.androidTargetSdkVersion);
        if (!cfg.androidMaxSdkVersion.equals(""))
            templateManager.define("ANDROID_MAX_SDK", cfg.androidMaxSdkVersion);
        if (!cfg.androidMinSdkVersion.equals("") || !cfg.androidTargetSdkVersion.equals("")
                || !cfg.androidMaxSdkVersion.equals(""))
            templateManager.define("ANDROID_USES_SDK");
    }

    // -------------------------------------------------------------------------
    // Public API
    // -------------------------------------------------------------------------

    /**
     * The raw structure of all projects is contained in a zip file. This
     * method inflates this file in a temporary folder.
     * @throws IOException
     */
    public void inflateProjects() throws IOException {
        FileUtils.forceMkdir(tmpDst);
        FileUtils.cleanDirectory(tmpDst);

        InputStream is = Res.getStream("projects.zip");
        ZipInputStream zis = new ZipInputStream(is);
        ZipEntry entry;

        while ((entry = zis.getNextEntry()) != null) {
            File file = new File(tmpDst, entry.getName());
            if (entry.isDirectory()) {
                FileUtils.forceMkdir(file);
            } else {
                OutputStream os = new FileOutputStream(file);
                IOUtils.copy(zis, os);
                os.close();
            }
        }

        zis.close();
    }

    /**
     * Selected libraries are inflated from their zip files, and put in the
     * libs folders of the projects.
     * @throws IOException
     */
    public void inflateLibraries() throws IOException {
        File commonPrjLibsDir = new File(tmpDst, "/prj-common/libs");
        File desktopPrjLibsDir = new File(tmpDst, "/prj-desktop/libs");
        File androidPrjLibsDir = new File(tmpDst, "/prj-android/libs");
        File htmlPrjLibsDir = new File(tmpDst, "/prj-html/war/WEB-INF/lib");
        File iosPrjLibsDir = new File(tmpDst, "/prj-ios/libs");
        File dataDir = new File(tmpDst, "/prj-android/assets");

        for (String library : cfg.libraries) {
            InputStream is = new FileInputStream(cfg.librariesZipPaths.get(library));
            ZipInputStream zis = new ZipInputStream(is);
            ZipEntry entry;

            LibraryDef def = libs.getDef(library);

            while ((entry = zis.getNextEntry()) != null) {
                if (entry.isDirectory())
                    continue;
                String entryName = entry.getName();

                for (String elemName : def.libsCommon)
                    if (entryName.endsWith(elemName))
                        copyEntry(zis, elemName, commonPrjLibsDir);
                for (String elemName : def.libsDesktop)
                    if (entryName.endsWith(elemName))
                        copyEntry(zis, elemName, desktopPrjLibsDir);
                for (String elemName : def.libsAndroid)
                    if (entryName.endsWith(elemName))
                        copyEntry(zis, elemName, androidPrjLibsDir);
                for (String elemName : def.libsHtml)
                    if (entryName.endsWith(elemName))
                        copyEntry(zis, elemName, htmlPrjLibsDir);
                for (String elemName : def.libsIos)
                    if (entryName.endsWith(elemName))
                        copyEntry(zis, elemName, iosPrjLibsDir);
                for (String elemName : def.data)
                    if (entryName.endsWith(elemName))
                        copyEntry(zis, elemName, dataDir);
            }

            zis.close();
        }
    }

    /**
     * Classpaths are configurated according to the selected libraries.
     * @throws IOException
     */
    public void configureLibraries() throws IOException {
        List<String> entriesCommon = new ArrayList<String>();
        List<String> entriesDesktop = new ArrayList<String>();
        List<String> entriesAndroid = new ArrayList<String>();
        List<String> entriesHtml = new ArrayList<String>();
        List<String> gwtInherits = new ArrayList<String>();

        for (String library : cfg.libraries) {
            LibraryDef def = libs.getDef(library);

            for (String file : def.libsCommon) {
                if (!isLibJar(file))
                    continue;
                String source = getSource(def.libsCommon, file);
                entriesCommon.add(buildClasspathEntry(file, source, "libs/", true));
                entriesAndroid.add(buildClasspathEntry(file, source, "/@{PRJ_COMMON_NAME}/libs/", true));
                entriesHtml.add(buildClasspathEntry(file, source, "/@{PRJ_COMMON_NAME}/libs/", false));
                if (source != null)
                    entriesHtml.add(buildClasspathEntry(source, null, "/@{PRJ_COMMON_NAME}/libs/", false));
            }

            for (String file : def.libsDesktop) {
                if (!isLibJar(file))
                    continue;
                String source = getSource(def.libsDesktop, file);
                entriesDesktop.add(buildClasspathEntry(file, source, "libs/", false));
            }

            for (String file : def.libsAndroid) {
                if (!isLibJar(file))
                    continue;
                String source = getSource(def.libsAndroid, file);
                entriesAndroid.add(buildClasspathEntry(file, source, "libs/", true));
            }

            for (String file : def.libsHtml) {
                if (!isLibJar(file))
                    continue;
                String source = getSource(def.libsHtml, file);
                entriesHtml.add(buildClasspathEntry(file, source, "war/WEB-INF/lib/", false));
                if (source != null)
                    entriesHtml.add(buildClasspathEntry(source, null, "war/WEB-INF/lib/", false));
            }

            if (def.gwtModuleName != null) {
                gwtInherits.add("<inherits name='" + def.gwtModuleName + "' />");
            }
        }

        templateManager.define("CLASSPATHENTRIES_COMMON", flatten(entriesCommon, "\t", "\n").trim());
        templateManager.define("CLASSPATHENTRIES_DESKTOP", flatten(entriesDesktop, "\t", "\n").trim());
        templateManager.define("CLASSPATHENTRIES_ANDROID", flatten(entriesAndroid, "\t", "\n").trim());
        templateManager.define("CLASSPATHENTRIES_HTML", flatten(entriesHtml, "\t", "\n").trim());
        templateManager.define("GWT_INHERITS", flatten(gwtInherits, "\t", "\n").trim());
        templateManager.processOver(new File(tmpDst, "prj-common/.classpath"));
        templateManager.processOver(new File(tmpDst, "prj-desktop/.classpath"));
        templateManager.processOver(new File(tmpDst, "prj-android/.classpath"));
        templateManager.processOver(new File(tmpDst, "prj-html/.classpath"));
        templateManager.processOver(new File(tmpDst, "prj-html/src/GwtDefinition.gwt.xml"));
    }

    /**
     * Launchers and packages are set up according to the user choices.
     * @throws IOException
     */
    public void postProcess() throws IOException {
        {
            File src = new File(tmpDst, "prj-common");
            File dst = new File(tmpDst, cfg.projectName + cfg.suffixCommon);
            move(src, "src/MyGame.java",
                    "src/" + cfg.packageName.replace('.', '/') + "/" + cfg.mainClassName + ".java");
            move(src, "src/MyGame.gwt.xml", "src/" + cfg.mainClassName + ".gwt.xml");
            templateDir(src);
            FileUtils.moveDirectory(src, dst);
        }

        if (cfg.isDesktopIncluded) {
            File src = new File(tmpDst, "prj-desktop");
            File dst = new File(tmpDst, cfg.projectName + cfg.suffixDesktop);
            move(src, "src/Main.java", "src/" + cfg.packageName.replace('.', '/') + "/Main.java");
            templateDir(src);
            FileUtils.moveDirectory(src, dst);
        }

        if (cfg.isAndroidIncluded) {
            File src = new File(tmpDst, "prj-android");
            File dst = new File(tmpDst, cfg.projectName + cfg.suffixAndroid);
            move(src, "src/MainActivity.java", "src/" + cfg.packageName.replace('.', '/') + "/MainActivity.java");
            templateDir(src);
            FileUtils.moveDirectory(src, dst);
        }

        if (cfg.isHtmlIncluded) {
            File src = new File(tmpDst, "prj-html");
            File dst = new File(tmpDst, cfg.projectName + cfg.suffixHtml);
            move(src, "src/GwtDefinition.gwt.xml",
                    "src/" + cfg.packageName.replace('.', '/') + "/GwtDefinition.gwt.xml");
            move(src, "src/client", "src/" + cfg.packageName.replace('.', '/') + "/client");
            templateDir(src);
            FileUtils.moveDirectory(src, dst);
        }

        if (cfg.isIosIncluded) {
            File src = new File(tmpDst, "prj-ios");
            File dst = new File(tmpDst, cfg.projectName + cfg.suffixIos);
            move(src, "my-gdx-game-ios.csproj", cfg.projectName + cfg.suffixIos + ".csproj");
            move(src, "my-gdx-game-ios.sln", cfg.projectName + cfg.suffixIos + ".sln");
            templateDir(src);
            FileUtils.moveDirectory(src, dst);
        }
    }

    /**
     * Everything is moved to the right location.
     * @throws IOException
     */
    public void copy() throws IOException {
        File src = new File(tmpDst, cfg.projectName + cfg.suffixCommon);
        File dst = new File(cfg.destinationPath);
        FileUtils.copyDirectoryToDirectory(src, dst);

        if (cfg.isDesktopIncluded) {
            src = new File(tmpDst, cfg.projectName + cfg.suffixDesktop);
            FileUtils.copyDirectoryToDirectory(src, dst);
        }

        if (cfg.isAndroidIncluded) {
            src = new File(tmpDst, cfg.projectName + cfg.suffixAndroid);
            FileUtils.copyDirectoryToDirectory(src, dst);
        }

        if (cfg.isHtmlIncluded) {
            src = new File(tmpDst, cfg.projectName + cfg.suffixHtml);
            FileUtils.copyDirectoryToDirectory(src, dst);
        }

        if (cfg.isIosIncluded) {
            src = new File(tmpDst, cfg.projectName + cfg.suffixIos);
            FileUtils.copyDirectoryToDirectory(src, dst);
        }
    }

    /**
     * Temporary folder is deleted.
     */
    public void clean() {
        FileUtils.deleteQuietly(tmpDst);
    }

    // -------------------------------------------------------------------------
    // Helpers
    // -------------------------------------------------------------------------

    private void templateDir(File dir) throws IOException {
        if (dir.getName().equals("libs"))
            return;

        for (File file : dir.listFiles()) {
            if (file.isDirectory()) {
                templateDir(file);
            } else {
                if (endsWith(file.getName(), ".jar", ".zip", ".dll", ".a", ".png"))
                    continue;
                templateManager.processOver(file);
            }
        }
    }

    private void copyEntry(ZipInputStream zis, String name, File dst) throws IOException {
        File file = new File(dst, name);
        file.getParentFile().mkdirs();

        OutputStream os = new FileOutputStream(file);
        IOUtils.copy(zis, os);
        os.close();
    }

    private void move(File base, String path1, String path2) throws IOException {
        if (path1.equals(path2))
            return;
        File file1 = new File(base, FilenameUtils.normalize(path1));
        File file2 = new File(base, FilenameUtils.normalize(path2));
        FileUtils.deleteQuietly(file2);
        if (file1.isDirectory())
            FileUtils.moveDirectory(file1, file2);
        else
            FileUtils.moveFile(file1, file2);
    }

    private boolean endsWith(String str, String... ends) {
        for (String end : ends)
            if (str.endsWith(end))
                return true;
        return false;
    }

    private boolean isLibJar(String file) {
        if (!file.endsWith(".jar"))
            return false;
        String name = FilenameUtils.getBaseName(file);
        if (endsWith(name, "-source", "-sources", "-src"))
            return false;
        return true;
    }

    private String getSource(List<String> files, String file) {
        String path = FilenameUtils.getFullPath(file);
        String name = FilenameUtils.getBaseName(file);
        String ext = FilenameUtils.getExtension(file);

        if (files.contains(path + name + "-source." + ext))
            return path + name + "-source." + ext;
        if (files.contains(path + name + "-sources." + ext))
            return path + name + "-sources." + ext;
        if (files.contains(path + name + "-src." + ext))
            return path + name + "-src." + ext;
        return null;
    }

    private String buildClasspathEntry(String file, String sourceFile, String path, boolean exported) {
        String str = "<classpathentry kind=\"lib\" ";
        if (exported)
            str += "exported=\"true\" ";
        str += "path=\"" + path + file + "\"";
        if (sourceFile != null)
            str += " sourcepath=\"" + path + sourceFile + "\"";
        str += "/>";
        return str;
    }

    private String flatten(List<String> strs, String begin, String end) {
        String ret = "";
        for (String str : strs)
            ret += begin + str + end;
        return ret;
    }
}