/** * CreatePlugin.java * * Copyright (C) 2010, Volker Boerchers * * CreatePlugin.java 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. * * FormatTranslation.java 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. */ package org.freeplane.ant; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.Writer; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.apache.tools.ant.Task; /** creates a skeleton of a new Freeplane plugin. */ public class CreatePlugin extends Task { private static final String FREEPLANE_PLUGIN_PREFIX = "freeplane_plugin_"; private String pluginName; private Boolean hasAction; private File newPluginDir; private File pluginTemplateDir; private File baseDir; public void execute() { // Freeplane has no build.xml on root level but only in projects -> use parentDir baseDir = (baseDir == null) ? getProject().getBaseDir().getParentFile() : baseDir; readAndValidateParameters(); newPluginDir = new File(baseDir, FREEPLANE_PLUGIN_PREFIX + pluginName); if (newPluginDir.exists()) fatal("won't overwrite output directory " + newPluginDir + " - please remove it first"); createDirs(); try { createSources(); createOtherFiles(); } catch (IOException e) { throw new BuildException("error creating files: " + e.getMessage(), e); } finalWords(); } private void readAndValidateParameters() { pluginTemplateDir = getPluginTemplateDir(); if (!pluginTemplateDir.isDirectory()) fatal("cannot find Freeplane source directory: " + pluginTemplateDir + " does not exist"); if (pluginName == null) { pluginName = TaskUtils.ask(getProject(), "=> Please enter required plugin name:", null); assertNotNull(pluginName, "property 'pluginName' is required"); } pluginName = pluginName.replaceAll(FREEPLANE_PLUGIN_PREFIX, "").toLowerCase(); if (!pluginName.matches("[a-z]+")) fatal("plugin name may only contain letters from the range [a-z]"); if (hasAction == null) hasAction = String.valueOf( TaskUtils.multipleChoice(getProject(), "=> Optional: Does this plugin contribute to the GUI?", "yes,no", "yes")) .equalsIgnoreCase("yes"); } private void createDirs() { String[] subdirs = { ".settings" // , "ant" // , "lib" // , "META-INF" // , "src" // , "src/org" // , "src/org/freeplane" // , "src/org/freeplane/plugin" // , "src/org/freeplane/plugin/" + pluginName // }; mkdir(newPluginDir); for (String dir : subdirs) { mkdir(new File(newPluginDir, dir)); } } private void createSources() throws IOException { if (hasAction) createAction(); createActivator(); } private void createAction() throws IOException { final String capPluginName = TaskUtils.firstToUpper(pluginName); String source = "" // + "package " + packageName() + ";\n" // + "\n" // + "import java.awt.event.ActionEvent;\n" // + "\n" // + "import org.freeplane.core.controller.Controller;\n" // + "import org.freeplane.core.ui.AFreeplaneAction;\n" // + "import org.freeplane.core.ui.components.UITools;\n" // + "\n" // + "public class " + capPluginName + "Action extends AFreeplaneAction {\n" // + " private static final long serialVersionUID = 1L;\n" // + "\n" // + " public " + capPluginName + "Action() {\n" // + " super(\"" + capPluginName + "\", controller, \"" + capPluginName + "\", null);\n" // + " }\n" // + "\n" // + " public void actionPerformed(final ActionEvent e) {\n" // + " /*TODO: enter your GUI code here*/\n" // + " UITools.informationMessage(\"Hi!\\n\\tThis is plugin " + capPluginName + "\");\n" // + " }\n" // + "}\n"; write(new File(sourceDir(), capPluginName + "Action.java"), source); } private void createActivator() throws IOException { final String registerAction = hasAction ? " " + "final MenuBuilder menuBuilder = modeController.getUserInputListenerFactory().getMenuBuilder();\n" + " menuBuilder.addAnnotatedAction(new " + TaskUtils.firstToUpper(pluginName) + "Action(modeController.getController()));\n" : ""; String source = "" // + "package " + packageName() + ";\n" // + "\n" // + "import java.util.Hashtable;\n" // + "\n" // + "import org.freeplane.core.ui.MenuBuilder;\n" // + "import org.freeplane.features.common.map.ModeController;\n" // + "import org.freeplane.features.mindmapmode.MModeController;\n" // + "import org.freeplane.main.osgi.IModeControllerExtensionProvider;\n" // + "import org.osgi.framework.BundleActivator;\n" // + "import org.osgi.framework.BundleContext;\n" // + "\n" // + "public class Activator implements BundleActivator {\n" // + " /*\n" // + " * (non-Javadoc)\n" // + " * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)\n" // + " */\n" // + " public void start(final BundleContext context) throws Exception {\n" // + " final Hashtable<String, String[]> props = new Hashtable<String, String[]>();\n" // + " props.put(\"mode\", new String[] { MModeController.MODENAME /*TODO: other modes too?*/});\n" // + " context.registerService(IModeControllerExtensionProvider.class.getName(),\n" // + " new IModeControllerExtensionProvider() {\n" // + " public void installExtension() {\n" // + registerAction + " }\n" // + " /*TODO: further initializations*/}, props);\n" // + " }\n" // + "\n" // + " /*\n" // + " * (non-Javadoc)\n" // + " * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)\n" // + " */\n" // + " public void stop(final BundleContext context) throws Exception {\n" // + " }\n" // + "}\n"; write(new File(sourceDir(), "Activator.java"), source); } private void createOtherFiles() throws IOException { String[] files = { // ".classpath" // , ".project" // , ".settings/org.eclipse.core.resources.prefs" // , ".settings/org.eclipse.core.runtime.prefs" // , ".settings/org.eclipse.jdt.core.prefs" // , ".settings/org.eclipse.pde.core.prefs" // , "ant/ant.properties" // , "ant/build.xml" // , "META-INF/MANIFEST.MF" // }; for (String fileName : files) { final String content = TaskUtils.readFile(new File(pluginTemplateDir, fileName)); final File newFile = new File(newPluginDir, fileName); write(newFile, transform(content)); } // build.properties were missing in 1_0_x so don't try to copy them write(new File(newPluginDir, "build.properties"), "source.lib/plugin.jar = src/\n"); } private String transform(String content) { return content // .replaceAll("<classpathentry kind=\"lib\"[^>]*>\\s*", "") // .classpath special .replaceAll("(jlatexmath.jar = )", "# $1") // ant.properties special .replaceAll("lib/jlatexmath.jar,\\s*(lib/plugin.jar)", "$1") // MANIFEST.MF special .replace("${commons-lang.jar}:${forms.jar}:${SimplyHTML.jar}:${jlatexmath.jar}", "") // build.xml special .replaceAll("latex", pluginName) // .replaceAll("Latex", TaskUtils.firstToUpper(pluginName)) // .replaceAll("LATEX", pluginName.toUpperCase()) // ; } private void write(File file, String content) throws IOException { Writer output = new BufferedWriter(new FileWriter(file)); try { // assuming that default encoding is OK! output.write(content); } finally { output.close(); } } private void finalWords() { String buildFragment = " <antcall target=\"makePlugin\" inheritall=\"false\">\n" // + " <param name=\"anttarget\" value=\"dist\"/>\n" // + " <param name=\"targetdir\" value=\"plugins\"/>\n" // + " <param name=\"plugindir\" value=\"freeplane_plugin_" + pluginName + "\"/>\n" // + " <param name=\"pluginname\" value=\"org.freeplane.plugin." + pluginName + "\"/>\n" // + " </antcall>\n"; log("New plugin created in " + newPluginDir); log("What next?"); log("* import plugin into Eclipse via Import... -> Existing Projects into Workspace"); log("* add required external jars to " + new File(newPluginDir, "lib")); log("* add required external jars and required Freeplane projects to classpath"); log("* search for \"TODO\" in the project and fill the gaps"); log("* add the following element to freeplane_framework/ant/build.xml:\n" + buildFragment); } private File sourceDir() { return new File(newPluginDir, "src/org/freeplane/plugin/" + pluginName); } private String packageName() { return "org.freeplane.plugin." + pluginName; } private File getPluginTemplateDir() { return new File(baseDir, "freeplane_plugin_latex"); } private void mkdir(File dir) { if (!dir.mkdir()) fatal(("cannot create directory " + dir)); } private void assertNotNull(Object property, String message) { if (property == null) fatal(message); } private void fatal(String message) { log(message, Project.MSG_ERR); throw new BuildException(message); } // == properties public String getPluginName() { return pluginName; } public void setPluginName(String pluginName) { this.pluginName = pluginName; } public File getBaseDir() { return baseDir; } public void setBaseDir(File baseDir) { this.baseDir = baseDir; } public void setBaseDir(String baseDir) { setBaseDir(new File(baseDir)); } public Boolean getHasAction() { return hasAction; } public void setHasAction(Boolean hasAction) { this.hasAction = hasAction; } }