transaction based install with android handler refactored

Project: http://git-wip-us.apache.org/repos/asf/cordova-plugman/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugman/commit/5e1b502e
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugman/tree/5e1b502e
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugman/diff/5e1b502e

Branch: refs/heads/master
Commit: 5e1b502ecb548d049b50262766dfa4f88f2210a6
Parents: e036b56
Author: Fil Maj <[email protected]>
Authored: Mon Apr 22 16:07:35 2013 -0700
Committer: Fil Maj <[email protected]>
Committed: Mon Apr 22 16:07:35 2013 -0700

----------------------------------------------------------------------
 spec/install.spec.js     |   10 +-
 spec/uninstall.spec.js   |   14 ++-
 src/fetch.js             |    5 +-
 src/install.js           |  131 ++++++++++++---------
 src/platforms/android.js |  251 +++++++++++++----------------------------
 src/platforms/common.js  |   36 ++++++-
 6 files changed, 208 insertions(+), 239 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/5e1b502e/spec/install.spec.js
----------------------------------------------------------------------
diff --git a/spec/install.spec.js b/spec/install.spec.js
index 88c2ca4..9b93a39 100644
--- a/spec/install.spec.js
+++ b/spec/install.spec.js
@@ -33,10 +33,11 @@ describe('install', function() {
             shell.cp('-rf', dummyplugin, plugins_dir);
         });
 
-        it('should call fetch if provided plugin cannot be resolved locally');
-        it('should generate an array of transactions required to run an 
installation');
-        it('should pass transaction log to appropriate platform handler\'s 
install');
-
+        it('should call fetch if provided plugin cannot be resolved locally', 
function() {
+        });
+        // TODO: possibly test how diff platform transaction logs are created
+        it('should generate an array of transactions required to run an 
installation and pass into appropriate platform handler\'s install method', 
function() {
+        });
         it('should call prepare after a successful install', function() {
             shell.cp('-rf', android_one_project, temp);
             var s = spyOn(plugman, 'prepare');
@@ -58,5 +59,6 @@ describe('install', function() {
             }).toThrow('Variable(s) missing: API_KEY');
         });
         it('should handle a failed install by passing completed transactions 
into appropriate handler\'s uninstall method'); 
+        it('should throw if plugin is already installed into project');
     });
 });

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/5e1b502e/spec/uninstall.spec.js
----------------------------------------------------------------------
diff --git a/spec/uninstall.spec.js b/spec/uninstall.spec.js
index c0fd48a..d95e89b 100644
--- a/spec/uninstall.spec.js
+++ b/spec/uninstall.spec.js
@@ -30,8 +30,15 @@ describe('uninstall', function() {
     });
 
     describe('success', function() {
-        it('should generate an array of transactions required to run an 
uninstallation');
-        it('should pass transaction log to appropriate platform handler\'s 
uninstall');
+        beforeEach(function() {
+            shell.cp('-rf', android_one_project, temp);
+            shell.cp('-rf', dummyplugin, plugins_dir);
+            install('android', temp, 'DummyPlugin', plugins_dir, {});
+        });
+
+        // TODO: possibly test how diff platform transaction logs are created
+        it('should generate and pass uninstall transaction log to appropriate 
platform handler\'s uninstall', function() {
+        });
     });
 
     describe('failure', function() {
@@ -40,9 +47,6 @@ describe('uninstall', function() {
             shell.cp('-rf', dummyplugin, plugins_dir);
             install('android', temp, 'DummyPlugin', plugins_dir, {});
         });
-        afterEach(function() {
-            shell.rm('-rf', temp);
-        });
         it('should throw if platform is unrecognized', function() {
             expect(function() {
                 uninstall('atari', temp, 'SomePlugin', plugins_dir, {});

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/5e1b502e/src/fetch.js
----------------------------------------------------------------------
diff --git a/src/fetch.js b/src/fetch.js
index 08ddf77..d20cf08 100644
--- a/src/fetch.js
+++ b/src/fetch.js
@@ -16,8 +16,11 @@ module.exports = function fetchPlugin(plugin_dir, 
plugins_dir, link, callback) {
         } else {
             plugins.clonePluginGitRepo(plugin_dir, plugins_dir, callback);
         }
-    } else { // Copy from the local filesystem.
+    } else {
+        // Copy from the local filesystem.
         var dest = path.join(plugins_dir, path.basename(plugin_dir));
+        // TODO: throw if local cant be resolved
+        // TODO: if local cant be resolved, query remote service.
 
         shell.rm('-rf', dest);
         if (link) {

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/5e1b502e/src/install.js
----------------------------------------------------------------------
diff --git a/src/install.js b/src/install.js
index d246b15..f3cb776 100644
--- a/src/install.js
+++ b/src/install.js
@@ -1,16 +1,46 @@
 var path = require('path'),
     fs   = require('fs'),
     et   = require('elementtree'),
+    fetch= require('./fetch'),
     platform_modules = require('./platforms');
 
+module.exports = function installPlugin(platform, project_dir, name, 
plugins_dir, cli_variables, callback) {
+    if (!platform_modules[platform]) {
+        var err = new Error(platform + " not supported.");
+        if (callback) { 
+            callback(err);
+            return;
+        }
+        else throw err;
+    }
+
+    var plugin_dir = path.join(plugins_dir, name);
+
+    // Check that the plugin has already been fetched.
+    if (!fs.existsSync(plugin_dir)) {
+        // if plugin doesnt exist, use fetch to get it.
+        fetch(name, plugins_dir, false, function(err) {
+            if (err) {
+                callback(err);
+            } else {
+                runInstall(platform, project_dir, plugin_dir, plugins_dir, 
cli_variables, callback);
+            }
+        });
+    } else {
+        runInstall(platform, project_dir, plugin_dir, plugins_dir, 
cli_variables, callback);
+    }
+};
+
 function runInstall(platform, project_dir, plugin_dir, plugins_dir, 
cli_variables, callback) {
     var xml_path     = path.join(plugin_dir, 'plugin.xml')
       , xml_text     = fs.readFileSync(xml_path, 'utf-8')
       , plugin_et    = new et.ElementTree(et.XML(xml_text))
       , filtered_variables = {}
       , info;
-
-    // checking preferences
+    var name         = plugin_et.findall('name').text;
+    var plugin_id    = plugin_et._root.attrib['id'];
+    
+    // checking preferences, if certain variables are not provided, we should 
throw.
     prefs = plugin_et.findall('./preference') || [];
     prefs = 
prefs.concat(plugin_et.findall('./platform[@name="'+platform+'"]/preference'));
     var missing_vars = [];
@@ -30,65 +60,54 @@ function runInstall(platform, project_dir, plugin_dir, 
plugins_dir, cli_variable
         else throw err;
     }
 
-    // Log out plugin INFO element contents in case additional install steps 
are necessary
-    if((info = plugin_et.find('./platform[@name="'+platform+'"]/info'))) {
-        console.log(info.text);
-    }
-
-    // run the platform-specific function
-    try {
-        platform_modules[platform].handleInstall(project_dir, plugin_dir, 
plugin_et, filtered_variables);
-        require('./../plugman').prepare(project_dir, platform, plugins_dir);
+    var platformTag = plugin_et.find('./platform[@name="'+platform+'"]');
+    if (!platformTag) {
+        // Either this plugin doesn't support this platform, or it's a JS-only 
plugin.
+        // Either way, return now.
         if (callback) callback();
-    } catch(e) {
-        var err;
-        try {
-            platform_modules[platform].forceUninstall(project_dir, plugin_dir, 
plugin_et, filtered_variables);
-            err = new Error('Error during installation of plugin, reverted all 
changes. Install error: ' + e.message + '\n' + e.stack + '\n----');
-        } catch(etwo) {
-            err = new Error('Error during installation of plugin, reverting 
changes also caused issues! Reversion probably incomplete. Install error: ' + 
e.message + '\n' + e.stack + ', reversion error: ' + etwo.message + '\n' + 
etwo.stack + '\n----');
-        }
-        if (callback) callback(err);
-        else throw err;
+        return;
     }
-}
+    var handler = platform_modules[platform];
 
-module.exports = function installPlugin(platform, project_dir, name, 
plugins_dir, cli_variables, callback) {
-    if (!platform_modules[platform]) {
-        var err = new Error(platform + " not supported.");
-        if (callback) { 
-            callback(err);
-            return;
-        }
-        else throw err;
-    }
+    // TODO: check if platform has plugin installed already.
 
-    var plugin_dir = path.join(plugins_dir, name);
-    var plugin_xml_path = path.join(plugin_dir, 'plugin.xml');
+    // parse plugin.xml into transactions
+    var txs = [];
+    var sourceFiles = platformTag.findall('./source-file'),
+        libFiles = platformTag.findall('./library-file'),
+        headerFiles = platformTag.findall('./header-file'),
+        resourceFiles = platformTag.findall('./resource-file'),
+        assets = plugin_et.findall('./asset'),
+        frameworks = platformTag.findall('./framework'),
+        configChanges = platformTag.findall('./config-file');
 
-    // Check that the plugin has already been fetched.
-    if (!fs.existsSync(plugin_dir) || !fs.existsSync(plugin_xml_path)) {
-        // try querying the plugin database
-        plugins.getPluginInfo(plugin_dir,
-            function(err, plugin_info) {
-                if (err) {
-                    var err_obj = new Error('install failed. plugin.xml at "' 
+ plugin_xml_path + '" not found and couldnt query remote server for 
information about the plugin because ' + err.message);
-                    if (callback) callback(err_obj);
-                    else throw err_obj;
+    txs = txs.concat(sourceFiles, libFiles, headerFiles, resourceFiles, 
frameworks, configChanges, assets);
+
+    // pass platform-specific transactions into install
+    handler.install(txs, plugin_id, project_dir, plugin_dir, 
filtered_variables, function(err) {
+        if (err) {
+            // FAIL
+            handler.uninstall(err.transactions.executed, plugin_id, 
project_dir, plugin_dir, function(superr) {
+                var issue = '';
+                if (superr) {
+                    // Even reversion failed. super fail.
+                    issue = 'Install failed, then reversion of installation 
failed. Sorry :(. Instalation issue: ' + err.message + ', reversion issue: ' + 
superr.message;
                 } else {
-                    plugins.clonePluginGitRepo(plugin_info.url, plugins_dir, 
function(err, plugin_dir) {
-                        if (err) {
-                            var err_obj = new Error('install failed. 
plugin.xml at "' + plugin_xml_path + '" not found and couldnt clone plugin repo 
because ' + err.message);
-                            if (callback) callback(err_obj);
-                            else throw err_obj;
-                        } else {
-                            runInstall(platform, project_dir, plugin_dir, 
plugins_dir, cli_variables, callback);
-                        }
-                    });
+                    issue = 'Install failed, plugin reversion successful so 
you should be good to go. Installation issue: ' + err.message;
                 }
+                if (callback) callback(issue);
+                else console.error(issue);
+            });
+        } else {
+            // WIN!
+            // call prepare after a successful install
+            require('./../plugman').prepare(project_dir, platform, 
plugins_dir);
+
+            // Log out plugin INFO element contents in case additional install 
steps are necessary
+            if((info = platformTag.findAll('./info'))) {
+                console.log(info.text);
             }
-        );
-    } else {
-        runInstall(platform, project_dir, plugin_dir, plugins_dir, 
cli_variables, callback);
-    }
-};
+            if (callback) callback();
+        }
+    });
+}

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/5e1b502e/src/platforms/android.js
----------------------------------------------------------------------
diff --git a/src/platforms/android.js b/src/platforms/android.js
index 90a828a..0914165 100644
--- a/src/platforms/android.js
+++ b/src/platforms/android.js
@@ -22,6 +22,7 @@ var fs = require('fs')  // use existsSync in 0.6.x
    , util = require('util')
    , shell = require('shelljs')
    , et = require('elementtree')
+   , common = require('./common')
    , getConfigChanges = require(path.join(__dirname, '..', 'util', 
'config-changes'))
    , searchAndReplace = require(path.join(__dirname, '..', 'util', 
'search-and-replace'))
    , xml_helpers = require(path.join(__dirname, '..', 'util', 'xml-helpers'))
@@ -29,158 +30,100 @@ var fs = require('fs')  // use existsSync in 0.6.x
    , sourceDir = 'src';
 
 module.exports = {
-    handleInstall:function(project_dir, plugin_dir, plugin_et, variables) {
-        handlePlugin('install', project_dir, plugin_dir, plugin_et, variables);
+    install:function(transactions, plugin_id, project_dir, plugin_dir, 
variables, callback) {
+        handlePlugin('install', plugin_id, transactions, project_dir, 
plugin_dir, variables, callback);
     },
-    handleUninstall:function(project_dir, plugin_dir, plugin_et, variables) {
-        handlePlugin('uninstall', project_dir, plugin_dir, plugin_et, 
variables);
-    },
-    forceInstall:function(project_dir, plugin_dir, plugin_et, variables) {
-        handlePlugin('force-install', project_dir, plugin_dir, plugin_et, 
variables);
-    },
-    forceUninstall:function(project_dir, plugin_dir, plugin_et, variables) {
-        handlePlugin('force-uninstall', project_dir, plugin_dir, plugin_et, 
variables);
+    uninstall:function(transactions, plugin_id, project_dir, plugin_dir, 
callback) {
+        handlePlugin('uninstall', plugin_id, transactions, project_dir, 
plugin_dir, null, callback);
     },
     www_dir:function(project_dir) {
         return path.join(project_dir, 'assets', 'www');
     }
 };
 
-function handlePlugin(action, project_dir, plugin_dir, plugin_et, variables) {
-    var plugin_id = plugin_et._root.attrib['id']
-      , version = plugin_et._root.attrib['version']
-      , external_hosts = []
-      , i = 0
-      , PACKAGE_NAME = androidPackageName(project_dir)
-
-
-    var platformTag = plugin_et.find('./platform[@name="android"]');
-    if (!platformTag) {
-        // Either this plugin doesn't support this platform, or it's a JS-only 
plugin.
-        // Either way, return now.
-        return;
-    }
-
-    var sourceFiles = platformTag.findall('./source-file')
-      , libFiles = platformTag.findall('./library-file')
-      , configChanges = getConfigChanges(platformTag);
-
-    variables = variables || {}
-
-         // get config.xml filename
-       var config_xml_filename = 'res/xml/config.xml';
-    if(fs.existsSync(path.resolve(project_dir, 'res/xml/plugins.xml'))) {
-        config_xml_filename = 'res/xml/plugins.xml';
-    }
-
-    // collision detection 
-    if(action.match(/force-/) == null) {
-        if(action == "install" && pluginInstalled(plugin_et, project_dir, 
config_xml_filename)) {
-            throw new Error("Plugin "+plugin_id+" already installed");
-        } else if(action == "uninstall" && !pluginInstalled(plugin_et, 
project_dir, config_xml_filename)) {
-            throw new Error("Plugin "+plugin_id+" not installed");
-        }
-    } else {
-        action = action.replace('force-', '');
-    }
+function handlePlugin(action, plugin_id, txs, project_dir, plugin_dir, 
variables, callback) {
+    var PACKAGE_NAME = androidPackageName(project_dir);
+    variables = variables || {};
 
+    // TODO: adding access tags?
+    // TODO: move this to prepare?
+    /*
     var root = et.Element("config-file");
-    root.attrib['parent'] = '.'
-        plugin_et.findall('./access').forEach(function (tag) { 
+    root.attrib['parent'] = '.';
+    plugin_et.findall('./access').forEach(function (tag) { 
         root.append(tag);
     });
-
-    if (root.len()) {
-        (configChanges[config_xml_filename]) ?
-            configChanges[config_xml_filename].push(root) :
-            configChanges[config_xml_filename] = [root];
-    }
-
-    // find which config-files we're interested in
-    Object.keys(configChanges).forEach(function (configFile) {
-        if (!fs.existsSync(path.resolve(project_dir, configFile))) {
-            delete configChanges[configFile];
-        }
-    });
-
-    // move source files
-    sourceFiles.forEach(function (sourceFile) {
-        
-        var srcDir = path.resolve(project_dir,
-                                sourceFile.attrib['target-dir'])
-          , destFile = path.resolve(srcDir,
-                                path.basename(sourceFile.attrib['src']));
-
-        if (action == 'install') {
-            shell.mkdir('-p', srcDir);
-            var srcFile = srcPath(plugin_dir, sourceFile.attrib['src']);
-            shell.cp(srcFile, destFile);
-        } else {
-            fs.unlinkSync(destFile);
-            // check if directory is empty
-            var curDir = srcDir;
-            while(curDir !== path.join(project_dir, 'src')) {
-                if(fs.readdirSync(curDir).length == 0) {
-                    fs.rmdirSync(curDir);
-                    curDir = path.resolve(path.join(curDir, '..'));
-                } else {
-                    // directory not empty...do nothing
+    */
+    var completed = [];
+    console.log(txs);
+    return;
+    while(txs.length) {
+        var mod = txs.shift();
+        try {
+            switch(mod.tag.toLowerCase()) {
+                case 'source-file':
+                    var destFile = path.join(mod.attrib['target-dir'], 
path.basename(mod.attrib['src']));
+
+                    if (action == 'install') {
+                        common.straightCopy(plugin_dir, mod.attrib['src'], 
project_dir, destFile);
+                    } else {
+                        common.deleteJava(project_dir, destFile);
+                    }
+                    break;
+                case 'library-file':
+                    var destFile = path.join(mod.attrib['target-dir'], 
path.basename(mod.attrib['src']));
+
+                    if (action == 'install') {
+                        common.straightCopy(plugin_dir, mod.attrib['src'], 
project_dir, destFile);
+                    } else {
+                        fs.unlinkSync(path.resolve(project_dir, destFile));
+                    }
+                    break;
+                case 'config-file':
+                    // Only modify config files that exist.
+                    var config_file = path.resolve(project_dir, 
mod.attrib['target']);
+                    if (fs.existsSync(config_file)) {
+                        var xmlDoc = 
xml_helpers.parseElementtreeSync(config_file);
+                        var selector = mod.attrib["parent"];
+                        var children = mod.findall('*');
+
+                        if( action == 'install') {
+                            if (!xml_helpers.graftXML(xmlDoc, children, 
selector)) {
+                                throw new Error('failed to add config-file 
children to "' + filename + '"');
+                            }
+                        } else {
+                            if (!xml_helpers.pruneXML(xmlDoc, children, 
selector)) {
+                                throw new Error('failed to remove config-file 
children from "' + filename + '"');
+                            }
+                        }
+
+                        var output = xmlDoc.write({indent: 4});
+                        fs.writeFileSync(config_file, output);
+                    }
+                    break;
+                case 'asset':
+                    if (action == 'uninstall') {
+                        var target = mod.attrib.target;
+                        shell.rm('-rf', path.resolve(module.exports.www_dir(), 
target));
+                        shell.rm('-rf', path.resolve(module.exports.www_dir(), 
'plugins', plugin_id));
+                    }
+                    break;
+                default:
+                    throw new Error('Unrecognized plugin.xml element/action in 
android installer: ' + mod.tag);
                     break;
-                }
-            }
-        }
-    })
-
-    // move library files
-    libFiles.forEach(function (libFile) {
-        var libDir = path.resolve(project_dir,
-                                libFile.attrib['target-dir'])
-
-        if (action == 'install') {
-            shell.mkdir('-p', libDir);
-            var src = path.resolve(plugin_dir,
-                                        libFile.attrib['src']),
-                dest = path.resolve(libDir,
-                                path.basename(libFile.attrib['src']));
-
-            shell.cp(src, dest);
-        } else {
-            var destFile = path.resolve(libDir,
-                            path.basename(libFile.attrib['src']));
-
-            fs.unlinkSync(destFile);
-            // check if directory is empty
-            var files = fs.readdirSync(libDir);
-            if(files.length == 0) {
-                shell.rm('-rf', libDir);
             }
+        } catch(e) {
+            // propagate error up and provide completed tx log
+            e.transactions = {
+                executed:completed,
+                incomplete:txs.unshift(mod)
+            };
+            if (callback) callback(e);
+            else throw e;
+            return;
         }
-    })
-
-    // edit configuration files
-    Object.keys(configChanges).forEach(function (filename) {
-        var filepath = path.resolve(project_dir, filename),
-            xmlDoc = xml_helpers.parseElementtreeSync(filepath),
-            output;
-
-        configChanges[filename].forEach(function (configNode) {
-            var selector = configNode.attrib["parent"],
-                children = configNode.findall('*');
-
-            if( action == 'install') {
-                if (!xml_helpers.graftXML(xmlDoc, children, selector)) {
-                    throw new Error('failed to add children to ' + filename);
-                }
-            } else {
-                if (!xml_helpers.pruneXML(xmlDoc, children, selector)) {
-                    throw new Error('failed to remove children from' + 
filename);
-                }
-            }
-        });
-
-        output = xmlDoc.write({indent: 4});
-        fs.writeFileSync(filepath, output);
-    });
+        completed.push(mod);
+    }
 
     if (action == 'install') {
         variables['PACKAGE_NAME'] = androidPackageName(project_dir);
@@ -188,25 +131,6 @@ function handlePlugin(action, project_dir, plugin_dir, 
plugin_et, variables) {
         searchAndReplace(path.resolve(project_dir, 'AndroidManifest.xml'), 
variables);
     }
 
-    // Remove all assets and JS modules installed by this plugin.
-    if (action == 'uninstall') {
-        var assets = plugin_et.findall('./asset');
-        assets && assets.forEach(function(asset) {
-            var target = asset.attrib.target;
-            shell.rm('-rf', path.join(project_dir, assetsDir, target));
-        });
-
-        shell.rm('-rf', path.join(project_dir, assetsDir, 'plugins', 
plugin_id));
-    }
-}
-
-// TODO: resolvePath, and should be used everywhere (even across platform 
impls)
-function srcPath(pluginPath, filename) {
-    var file = path.resolve(pluginPath, filename);
-    if (!fs.existsSync(file)) {
-        throw new Error('Path "' + file + '" does not exist.');
-    }
-    return file;
 }
 
 // reads the package name out of the Android Manifest file
@@ -218,16 +142,3 @@ function androidPackageName(project_dir) {
 
     return mDoc._root.attrib['package'];
 }
-
-function pluginInstalled(plugin_et, project_dir, config_xml_filename) {
-    var tag_xpath = 
util.format('./platform[@name="android"]/config-file[@target="%s"]/plugin', 
config_xml_filename);
-
-    var plugin_tag = plugin_et.find(tag_xpath);
-    if (!plugin_tag) {
-        return false;
-    }
-    var plugin_name = plugin_tag.attrib.name;
-
-    return (fs.readFileSync(path.resolve(project_dir, config_xml_filename), 
'utf8')
-           .match(new RegExp(plugin_name, "g")) != null);
-}

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/5e1b502e/src/platforms/common.js
----------------------------------------------------------------------
diff --git a/src/platforms/common.js b/src/platforms/common.js
index cd95d84..523b38a 100644
--- a/src/platforms/common.js
+++ b/src/platforms/common.js
@@ -1,12 +1,42 @@
-
+var shell = require('shelljs'),
+    path  = require('path'),
+    fs    = require('fs');
 
 module.exports = {
     // helper for resolving source paths from plugin.xml
     // throws File Not Found
-    resolveSrcPath:function() {
+    resolveSrcPath:function(plugin_dir, relative_path) {
+        var full_path = path.resolve(plugin_dir, relative_path);
+        if (!fs.existsSync(full_path)) throw new Error('"' + full_path + '" 
not found!');
+        else return full_path;
     },
     // helper for resolving target paths from plugin.xml into a cordova project
     // throws File Exists
-    resolveTargetPath:function() {
+    resolveTargetPath:function(project_dir, relative_path) {
+        var full_path = path.resolve(project_dir, relative_path);
+        if (fs.existsSync(full_path)) throw new Error('"' + full_path + '" 
already exists!');
+        else return full_path;
+    },
+    // Many times we simply need to copy shit over, knowing if a source path 
doesnt exist or if a target path already exists
+    straightCopy:function(plugin_dir, src, project_dir, dest) {
+        src = module.exports.resolveSrcPath(plugin_dir, src);
+        dest = module.exports.resolveTargetPath(project_dir, dest);
+        shell.mkdir('-p', path.dirname(dest));
+        shell.cp('-r', src, dest);
+    },
+    // Sometimes we want to remove some java, and prune any unnecessary empty 
directories
+    deleteJava:function(project_dir, destFile) {
+        fs.unlinkSync(destFile);
+        // check if directory is empty
+        var curDir = path.resolve(project_dir, path.basename(destFile));
+        while(curDir !== path.resolve(project_dir, 'src')) {
+            if(fs.readdirSync(curDir).length == 0) {
+                fs.rmdirSync(curDir);
+                curDir = path.resolve(curDir, '..');
+            } else {
+                // directory not empty...do nothing
+                break;
+            }
+        }
     }
 };

Reply via email to