diff --git a/build/project-template-gradle/build-tools/android-static-binding-generator/.gitignore b/build/project-template-gradle/build-tools/android-static-binding-generator/.gitignore deleted file mode 100644 index 4f3ae2d9d..000000000 --- a/build/project-template-gradle/build-tools/android-static-binding-generator/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/node_modules -/_ast_parser/node_modules -/_ast_parser/bindings.txt \ No newline at end of file diff --git a/build/project-template-gradle/build-tools/android-static-binding-generator/README.md b/build/project-template-gradle/build-tools/android-static-binding-generator/README.md deleted file mode 100644 index 63bc4bc02..000000000 --- a/build/project-template-gradle/build-tools/android-static-binding-generator/README.md +++ /dev/null @@ -1 +0,0 @@ -Deals with generating the bindings necessary for a nativescript project \ No newline at end of file diff --git a/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/.gitignore b/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/.gitignore deleted file mode 100644 index 1e98953d3..000000000 --- a/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/node_modules -/bindings.txt -/test_app diff --git a/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/example_command.txt b/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/example_command.txt deleted file mode 100644 index a9e2c140c..000000000 --- a/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/example_command.txt +++ /dev/null @@ -1 +0,0 @@ -node traverse_files.js D:\work\Extend_Class_Generator\_interface_name_generator\out\interface-names.txt .\test_app AST.txt \ No newline at end of file diff --git a/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/helpers/file_helpers.js b/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/helpers/file_helpers.js new file mode 100644 index 000000000..d8b3fd359 --- /dev/null +++ b/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/helpers/file_helpers.js @@ -0,0 +1,38 @@ +var fs = require("fs"), + path = require("path"); +module.exports = function Helpers(config) { + + function cleanOutFile(filePath) { + fs.truncateSync(filePath, 0); + if(config && config.logger) { + config.logger.info("+cleared out file: " + filePath); + } + } + + function createFile(filePath) { + if(ensureDirectories(filePath)) { + fs.writeFileSync(filePath, ""); + if(config && config.logger) { + config.logger.info("+created ast output file: "); + } + } + cleanOutFile(filePath) + } + + function ensureDirectories(filePath) { + var parentDir = path.dirname(filePath); + if(fs.existsSync(parentDir)) { + return true; + } + + ensureDirectories(parentDir); + fs.mkdirSync(parentDir); + return true; + } + + return { + cleanOutFile: cleanOutFile, + createFile: createFile, + ensureDirectories: ensureDirectories + } +}; \ No newline at end of file diff --git a/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/helpers/json_extension.js b/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/helpers/json_extension.js new file mode 100644 index 000000000..9ca1d54de --- /dev/null +++ b/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/helpers/json_extension.js @@ -0,0 +1,47 @@ +var stringifyOnce = function(obj, replacer, indent){ + var printedObjects = []; + var printedObjectKeys = []; + var passingIndent = 4; + if(indent) { + passingIndent = indent; + } + + function printOnceReplacer(key, value){ + if ( printedObjects.length > 2000){ // browsers will not print more than 20K, I don't see the point to allow 2K.. algorithm will not be fast anyway if we have too many objects + return 'object too long'; + } + var printedObjIndex = false; + printedObjects.forEach(function(obj, index){ + if(obj===value){ + printedObjIndex = index; + } + }); + + if ( key == ''){ //root element + printedObjects.push(obj); + printedObjectKeys.push("root"); + return value; + } + + else if(printedObjIndex+"" != "false" && typeof(value)=="object"){ + if ( printedObjectKeys[printedObjIndex] == "root"){ + return "(pointer to root)"; + }else{ + return "(see " + ((!!value && !!value.constructor) ? value.constructor.name.toLowerCase() : typeof(value)) + " with key " + printedObjectKeys[printedObjIndex] + ")"; + } + }else{ + + var qualifiedKey = key || "(empty key)"; + printedObjects.push(value); + printedObjectKeys.push(qualifiedKey); + if(replacer){ + return replacer(key, value); + }else{ + return value; + } + } + } + return JSON.stringify(obj, printOnceReplacer, passingIndent); +}; + +module.exports = stringifyOnce; \ No newline at end of file diff --git a/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/helpers/logger.js b/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/helpers/logger.js new file mode 100644 index 000000000..eb467a0a9 --- /dev/null +++ b/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/helpers/logger.js @@ -0,0 +1,93 @@ +var fs = require('fs'), + path = require('path'), + os = require('os'), + fileHelpers = require("./file_helpers")(); + +module.exports = function(setting){ + + var logDirectory = path.dirname(setting.logPath); + if (!fs.existsSync(logDirectory)){ + console.error("couldn't find logDirectory so it will be created in place:" + setting.logPath); + fileHelpers.ensureDirectories(setting.logPath); + } + if (os.type().indexOf('Windows') === -1) { + var appLogStat = fs.statSync(logDirectory); + if (canWrite(process.uid === appLogStat.uid, process.gid === appLogStat.gid, appLogStat.mode)){ + console.error("ERROR WRITING TO LOG FILE DIRECTORY : " + logDirectory); + process.exit(-1); + } + } + + var appLog = createLog(setting.APP_NAME, logDirectory, setting); + + if(setting.disable) { + for(var prop in appLog) { + appLog[prop] = function() {}; + } + } + return appLog; +}; + +function canWrite(owner, inGroup, mode){ + return owner && (mode & 00200) || // User is owner and owner can write. + inGroup && (mode & 00020) || // User is in group and group can write. + (mode & 00002); // Anyone can write. +} + +function createLog(appName, logDirectory, setting){ + + var appLog = {}; + + function getRequestId() { + return (process.domain && process.domain.id) || ""; + } + + function getLogDate(){ + var today = new Date(); + + var fullYear = today.getFullYear(); + var month = today.getMonth(); + var day = today.getDate(); + var hours = today.getHours(); + var minutes = today.getMinutes(); + var seconds = today.getSeconds(); + + var logDate = fullYear + "-" + month + "-" + day + "/" + hours + ":" + minutes + ":" + seconds; + return logDate; + } + + function setMessageWithFormat(message) { + var res = getLogDate() + "\t" + message; + return res; + } + + function appendToLogFile(line) { + var logFile = logDirectory + path.sep + appName + ".log"; + var augmentedLine = getLogDate() + "\t" + line + os.EOL + fs.appendFile(logFile, augmentedLine, function (err) { + if(err) { + throw "couldn't write to " + logFile; + } + }); + } + + appLog.log = function (input) { + console.log(setMessageWithFormat(input)); + appendToLogFile(input); + } + + appLog.info = appLog.log; + + appLog.warn = function (input) { + console.warn(setMessageWithFormat(input)); + appendToLogFile(input); + } + + appLog.error = function (input) { + console.error(setMessageWithFormat(input)); + appendToLogFile(input); + } + + return appLog; +} + diff --git a/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/input_parced_typescript/.gitignore b/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/input_parced_typescript/.gitignore new file mode 100644 index 000000000..c96a04f00 --- /dev/null +++ b/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/input_parced_typescript/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/js_parser.js b/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/js_parser.js new file mode 100644 index 000000000..23cc53e5f --- /dev/null +++ b/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/js_parser.js @@ -0,0 +1,253 @@ +/* +* Code takes care of static analysis and generates "out_parsed_typescript.txt" +* The output file consists of information about custom and common bindings that should be generated. +* +* test command: +* node transpiled_ts_parser.js "input\dir\path" "output\file\path" "interface\names\file\path" +* node transpiled_ts_parser.js "D:\work\android-static-binding-generator\project\input_parced_typescript" "D:\work\android-static-binding-generator\project\out\out_parsed_typescript.txt" "D:\work\android-static-binding-generator\project\interface-name-generator\interfaces-names.txt" +*/ + +///////////////// CONFIGURATION ///////////////// + +var disableLogger = true; +if(process.env.AST_PARSER_DISABLE_LOGGING && process.env.AST_PARSER_DISABLE_LOGGING.trim() === "true") { + disableLogger = true; +} + +loggingSettings = { + "logPath" : require("path").dirname(require.main.filename) + "/logs/i.txt", + "strategy" : "console", + "APP_NAME" : "ast_parser", + "disable": disableLogger +}; + +var fs = require("fs"), + babelParser = require("babylon"), + traverse = require("babel-traverse"), + logger = require('./helpers/logger')(loggingSettings), + fileHelpers = require("./helpers/file_helpers")({logger: logger}), + path = require("path"), + stringify = require("./helpers/json_extension"), + es5_visitors = require("./visitors/es5-visitors"), + t = require("babel-types"), + filewalker = require('filewalker'), + lazy = require("lazy"), + eol = require('os').EOL, + + arguments = process.argv, + appDir = path.dirname(require.main.filename), + extendDecoratorName = "JavaProxy", + outFile = "out/out_parsed_typescript.txt", //default out file + inputDir = "input_parced_typescript", //default input folder + interfacesNamesFilePath = "../interfaces-names.txt", //default interace_names file path + interfaceNames = []; + +//env variables +if(process.env.AST_PARSER_OUT_FILE) { + outFile = process.env.AST_PARSER_OUT_FILE.trim(); +} +if(process.env.AST_PARSER_INPUT_DIR) { + inputDir = process.env.AST_PARSER_INPUT_DIR.trim(); +} +if(process.env.AST_PARSER_INTERFACE_FILE_PATH) { + interfacesNamesFilePath = process.env.AST_PARSER_INTERFACE_FILE_PATH.trim(); +} + + +//console variables have priority +if(arguments && arguments.length >= 3) { + inputDir = arguments[2] + console.log("inputDir: " + inputDir) +} +if(arguments && arguments.length >= 4) { + outFile = arguments[3] + console.log("outFile: " + outFile) +} +if(arguments && arguments.length >= 5) { + interfacesNamesFilePath = arguments[4] + console.log("interface names path: " + interfacesNamesFilePath) +} + +/////////////// PREPARATION //////////////// +fileHelpers.createFile(outFile) + +/////////////// EXECUTE //////////////// + +/* +* Traverses a given input directory and attempts to visit every ".js" file. +* It passes each found file down the line. +*/ +var traverseFilesDir = function(filesDir) { + + if(!fs.existsSync(filesDir)) { + throw "The input dir: " + filesDir + " does not exist!"; + } + + filewalker(filesDir) + .on("file", function (file, info) { + if(file.substring(file.length - 3, file.length) === '.js') { + var currentFileName = path.join(filesDir, file); + + readFile(currentFileName) + .then(astFromFileContent) + // .then(writeToFile) + .then(visitAst) + .then(writeToFile) + .catch(exceptionHandler) + } + }) + .on('error', function(err) { + reject(err); + }) + .walk(); +} + +// ENTRY POINT! +/* +* Get's pregenerated interface names from "interfacesNamesFilePath" +* After reading interface names runs the visiting api +*/ +function readInterfaceNames() { + return new Promise(function (resolve, reject) { + new lazy(fs.createReadStream(interfacesNamesFilePath)) + .lines + .forEach(function(line){ + interfaceNames.push(line.toString()); + // console.log(line.toString()); + }).on('pipe', function (err) { + if(err) { + reject(false); + } + resolve(inputDir); + }); + }) +} +readInterfaceNames().then(traverseFilesDir) + + +/* +* Gets the file content as text and passes it down the line. +*/ +var readFile = function (filePath, err) { + return new Promise(function (resolve, reject) { + + fs.readFile(filePath, function (err, data) { + + if(err) { + logger.warn("+DIDN'T get content of file!"); + return reject(err); + } + + logger.info("+got content of file!"); + var fileInfo = { + filePath: filePath, + data: data.toString() + } + return resolve(fileInfo); + }); + }); +} + +/* +* Get's the AST (https://en.wikipedia.org/wiki/Abstract_syntax_tree) from the file content and passes it down the line. +*/ +var astFromFileContent = function (data, err) { + return new Promise(function (resolve, reject) { + + if(err) { + logger.warn("+DIDN'T parse ast from file!"); + return reject(err); + } + + logger.info("+parsing ast from file!"); + // console.log("data: " + data.data); + var ast = babelParser.parse(data.data, { + minify: false, + plugins: ["decorators"] + }); + data.ast = ast; + return resolve(data); + }); +}; + +//only unique filter +function onlyUnique(value, index, self) { + return self.indexOf(value) === index; +} + +/* +* Visist's the passed AST with a given visitor and extracts nativescript speciffic data. +* Passes the extracted bindings data down the line. +*/ +var visitAst = function (data, err) { + return new Promise (function (resolve, reject) { + if(err) { + logger.warn("+DIDN'T visit ast!"); + return reject(err); + } + + logger.info("+visiting ast with given visitor library!"); + + traverse.default(data.ast, { + enter(path) { + + var decoratorConfig = { + logger: logger, + extendDecoratorName: extendDecoratorName, + filePath: data.filePath.substring(inputDir.length + 1, (data.filePath.length - 3)), + fullPathName: data.filePath.substring(inputDir.length + 1).replace(/[\\]/g, "/"), + interfaceNames: interfaceNames + }; + es5_visitors.es5Visitor(path, decoratorConfig); + } + }) + + var customExtendsArr = es5_visitors.es5Visitor.getProxyExtendInfo() + var normalExtendsArr = es5_visitors.es5Visitor.getCommonExtendInfo() + var interfacesArr = es5_visitors.es5Visitor.getInterfaceInfo() + + var res = customExtendsArr.concat(normalExtendsArr).concat(interfacesArr).filter(onlyUnique).join(eol); + return resolve(res) + }); +} + +var writeToFile = function(data, err) { + + return new Promise (function (resolve, reject) { + + if(data.trim() != "") { + + // fs.appendFile(outFile, stringify(data), function (writeFileError) { + fs.appendFile(outFile, data + eol, function (writeFileError) { + if(err) { + logger.warn("Error from writeToFile: " + err); + return reject(err); + } + if(writeFileError) { + logger.warn("Error writing file: " + writeFileError); + return reject(writeFileError); + } + + logger.info("+appended '" + data + "' to file: " + outFile); + return resolve(data); + + }); + } + }); +} + +/* +* If there is an exception anywhere down the line it's caught here +* If the error is criticalthe process is exited. +*/ +var exceptionHandler = function (reason) { + if(reason.errCode && reason.errCode === 1) { + logger.error("(*)(*)(*)Error: Exception Handler Caught: " + reason.message); + logger.error("PROCESS EXITING..."); + process.stderr.write(reason.message); + process.exit(4); + } + else { + logger.error("(*)(*)(*)Error: Exception Handler Caught: " + reason); + } +} \ No newline at end of file diff --git a/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/logs/.gitignore b/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/logs/.gitignore new file mode 100644 index 000000000..c96a04f00 --- /dev/null +++ b/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/logs/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/out/.gitignore b/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/out/.gitignore new file mode 100644 index 000000000..c96a04f00 --- /dev/null +++ b/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/out/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/package.json b/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/package.json index 350eb1159..3545a964e 100644 --- a/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/package.json +++ b/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/package.json @@ -1,13 +1,18 @@ { - "name": "ast_parser", + "name": "ast-parser", "version": "1.0.0", - "description": "Parses javascript code and extracts extend class and interfaces names", - "main": "traverse_files.js", - "private": true, + "description": "", + "main": "js_parser.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, "author": "", + "license": "ISC", "dependencies": { + "babel-traverse": "^6.4.5", + "babel-types": "^6.4.5", + "babylon": "^6.4.5", "filewalker": "^0.1.2", - "lazy": "^1.0.11", - "uglify-js": "^2.4.23" + "lazy": "^1.0.11" } -} +} \ No newline at end of file diff --git a/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/traverse_files.js b/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/traverse_files.js deleted file mode 100644 index 18f246467..000000000 --- a/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/traverse_files.js +++ /dev/null @@ -1,370 +0,0 @@ -/** -* example command: -* node traverse_files.js [interface-names.txt] [dir_to_traverse] [out_dir_name] -* -* This file generates AST (http://en.wikipedia.org/wiki/Abstract_syntax_tree) on given js file and -* extracts class and interface full names -* e.g "android.widget.button.(extend)" -> extended class -* "android.view.View.OnClickListener" -> interface -* necessary to create bindings and -* outputs them into a file -*/ - -var uglifyjs = require('uglify-js'); -var filewalker = require('filewalker'); -var lazy = require("lazy"); -var fs = require('fs'); - -var arguments = process.argv; -var outFile; -var LINE_OFFSET = 2; //line offset because of module wrapper - -//no idea why exactly 5 and 3 (maybe because of tabs difference in parsers) -var CLASS_COLUMN_OFFSET = 5; -var INTERFACE_COLUMN_OFFSET = 3; - -if(arguments.length == 3) { - console.log('you need to pass input file to traverse e.g. "node traverse_files.js [interface-names.txt] [dir_to_traverse]"'); - return; -} -if(arguments.length == 4) { - // console.log('you can pass an out file name as a parameter! e.g. "node traverse_files.js [interface-names.txt] [dir_to_traverse] [out_dir_name]"'); - outFile = 'bindings.txt'; -} -else { - outFile = arguments[4]; -} - -var interfacesNamesFileName = arguments[2]; -var inputDir = arguments[3]; - -var classNamesNeedExtending = []; -var filesToTraverse = []; -var interfaceNames = []; -var javaSeparator = '_'; -var fileSeparator = javaSeparator + 'f';// + javaSeparator + javaSeparator; -var lineSeparator = javaSeparator + 'l'; -var columnSeparator = javaSeparator + 'c'; -var customClassNameSeparator = javaSeparator + javaSeparator; -var resultJson; - -new lazy(fs.createReadStream(interfacesNamesFileName)).lines - .forEach(function(line){ - interfaceNames.push(line.toString()); - }).on('pipe', function (err) { - var filename = inputDir; - traverseFolder(filename); - - if (err) throw err; - - console.log('OK: ' + filename + '\n'); - }); - -function traverseFolder(inputFolder){ - - filewalker(inputFolder) - .on('file', function(p, s) { - - //if in the folder there is a file with .js extensions - if(p.substring(p.length - 3, p.length) === '.js' && p.indexOf("ios.js") == -1) { - var currentFileName = inputFolder + '/' + p; - - //TODO: think how to fix circular reference problem - if(currentFileName.indexOf('easysax.js') == -1) { //exception because of maximum call stack exceeded (circular reference) - filesToTraverse.push(currentFileName); - // console.log(currentFileName); - } - } - }) - .on('error', function(err) { - console.error(err); - }) - .on('done', function() { - createClearFile(); - - for(var index in filesToTraverse) { - parseFile(filesToTraverse[index]); - } - }) - .walk(); -} - -function parseFile(fullFilename) { - fs.readFile(fullFilename, 'utf8', function(err, data) { - if (err) { - throw err; - } - - classNamesNeedExtending = []; - - parseDataFromFile(data, fullFilename); - }); -} - -function parseDataFromFile(data, fullFilename) { - - var ast = uglifyjs.parse(data.toString()); - - traverseAstTree(ast, checkPattern, fullFilename); -} - -//traverse nodes with some visitor -function traverseAstTree(node, visitor, fullFilename){ - - //visitor checks for patterns - visitor(node, fullFilename); - - // if(typeof node === 'object') { - - for(var item in node) { - - if(typeof node[item] === 'object' && node[item] !== null && node[item] != 0) { - - traverseAstTree(node[item], visitor, fullFilename); - } - } - // } -} - -//extract information from classes and interfaces necessary to generate proxy -function checkPattern(node, fullFilename){ - - if(node.start) { - - var className; - var customExtendClassName; - var overridenMethods; - - if(node.expression) { - - className = extractFullNodeName(node); - overridenMethods = getOverridenMethods(node); - - var fullLocationOfFile = fileSeparator + getFilename(fullFilename); - fullLocationOfFile += lineSeparator + (parseInt(node.expression.end.line));// + LINE_OFFSET); - - //check if this expression is an interface (PATTERN) - if(node.start.value == 'new') { - - for(var index in interfaceNames) { - - if(interfaceNames[index].trim() == className) { - customExtendClassName = getExtendedClassName(node); - fullLocationOfFile += columnSeparator + (parseInt(node.expression.start.col) - INTERFACE_COLUMN_OFFSET); - className += fullLocationOfFile; - className += customClassNameSeparator + customExtendClassName; - - if(!nameContainsInvalidSymbols(customExtendClassName)) { - // console.log(className); - var lineToWrite = className + ' ' + overridenMethods.join(); - appendToFile(lineToWrite); - return; - } - } - } - } - - //check if expression is extended class (PATTERN) - if(node.expression.property == 'extend') { - customExtendClassName = getExtendedClassName(node); - fullLocationOfFile += columnSeparator + (parseInt(node.expression.end.endcol) - CLASS_COLUMN_OFFSET); - className += fullLocationOfFile; - className += customClassNameSeparator + customExtendClassName; - - if(!nameContainsInvalidSymbols(customExtendClassName)) {// && className.indexOf('com.tns.tests') == -1) { //binding generator takes care of this - var lineToWrite = className + ' ' + overridenMethods.join(); - // console.log(className); - appendToFile(lineToWrite); - return; - } - } - - // check if expression is extended typescript class (PATTERN) - var isValidExtendCandidate = false; - if(node.expression.body) { - for(var index in node.expression.body){ - var currentAstToken = node.expression.body[index]; - if(currentAstToken.body) { - if(currentAstToken.body.expression) { - - // the function "__extends" is the way typescript extensions are done - if( currentAstToken.body.expression.name == '__extends') { - - // get class name to extend from what's passed to iife - className = extractFullNodeName(node.args[0]); - - // get return type of iife - for(var i in node.expression.body) { - var possibleOverridenMethod = node.expression.body[i]; - if(currentAstToken.start) { - if(possibleOverridenMethod.start.value == 'return') { - customExtendClassName = possibleOverridenMethod.value.name; - isValidExtendCandidate = true; - } - } - } - - // find overriden methods from top node - for(var i in node.expression.body) { - var possibleOverridenMethod = node.expression.body[i]; - if(possibleOverridenMethod.body) { - if(possibleOverridenMethod.body.left) { - var overridenMethodName = getExtendingCtorName(possibleOverridenMethod.body.left); - if(overridenMethodName == customExtendClassName) { - var possibleOverridenProp = possibleOverridenMethod.body.left.property; - - if(typeof possibleOverridenProp == 'string') { - overridenMethods.push(possibleOverridenProp); - } - } - } - } - } - } - } - } - } - if(isValidExtendCandidate) { - var extendLocation = '_frnal_prepareExtend_l60_c37__'; - className += extendLocation; - className += customExtendClassName; - if(!nameContainsInvalidSymbols(customExtendClassName)) { - if(customExtendClassName) { - var lineToWrite = className + ' ' + overridenMethods.join(); - // console.log(className); - appendToFile(lineToWrite); - return; - } - } - } - } - } - } -} - -function getExtendingCtorName(node) { - if(node.expression) { - return getExtendingCtorName(node.expression); - } - if(node.name) { - return node.name; - } -} - -function nameContainsInvalidSymbols(name) { - for(var index in name) { - if(!(name[index] >= 'a' && name[index] <= 'z') && - !(name[index] >= 'A' && name[index] <= 'Z') && - !(name[index] >= '0' && name[index] <= '9') && - name[index] != javaSeparator) { - return true; - } - } - return false; -} - -function getFilename(fullFilename){ - var finalFilename; - var startIndex = inputDir.length + 1; - var endIndexWithAndroid = fullFilename.indexOf('.android.js'); - var endIndexWithoutAndroid = fullFilename.indexOf('.js'); - if(endIndexWithAndroid != -1) { - finalFilename = fullFilename.substring(startIndex, endIndexWithAndroid); - } - else if(endIndexWithoutAndroid != -1) { - finalFilename = fullFilename.substring(startIndex, endIndexWithoutAndroid); - } - - finalFilename = finalFilename.replace(/[-/]/g, javaSeparator); //replace ['/', '-'] ---> with '_' - - return finalFilename; -} - -function extractFullNodeName(node) { - var expressionArr = []; - - // if(node) { - getNameInfo(node, expressionArr); - // } - // else { - // getNameInfo(node.expression, expressionArr); - // } - - expressionArr.reverse(); - var resultString = expressionArr.join('.'); - - return resultString; -} - -function getOverridenMethods(node) { - var overridenMethods = []; - - if(node.args ){// && (node.start.value == 'new' || node.property !== 'extend')) { - getOverridenMethodsNames(node.args, overridenMethods); - } - - return overridenMethods; -} - -function getOverridenMethodsNames(argumentsNode, overridenMethods) { - for(var index in argumentsNode) { - var functionArgument = argumentsNode[index]; - - if(functionArgument.properties){ - var props = functionArgument.properties; - for(var index in props) { - var nameOfOverriddenMethod = props[index].key; - if(typeof nameOfOverriddenMethod == 'string') { - overridenMethods.push(nameOfOverriddenMethod); - } - } - } - } -} - -function getExtendedClassName(node) { - - var customExtendClassName = ""; - - if(node.args) { - - if(node.args.length == 2) { - var functionArgument = node.args[0]; - customExtendClassName = functionArgument.value; - } - } - - return customExtendClassName; -} - -function getNameInfo(node, expressionArr) { - - if(node.property && node.property !== 'extend'){ - expressionArr.push(node.property); - } - else if(node.name) { - expressionArr.push(node.name); - } - - if(node.expression) { - getNameInfo(node.expression, expressionArr); - } -} - -function createClearFile() { - // create and clear file - if (fs.existsSync(outFile)) { - fs.truncate(outFile, 0, function(err) { - - if(err) throw err; - - console.log('created \ cleared file') - }); - } -} - -function appendToFile(line) { - fs.appendFile(outFile, line + '\n', function (err) { - if (err) throw err; - }); -} \ No newline at end of file diff --git a/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/visitors/es5-visitors.js b/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/visitors/es5-visitors.js new file mode 100644 index 000000000..05883baab --- /dev/null +++ b/build/project-template-gradle/build-tools/android-static-binding-generator/ast-parser/visitors/es5-visitors.js @@ -0,0 +1,461 @@ +var es5_visitors = (function () { + + var t = require("babel-types"), + + defaultExtendDecoratorName = "JavaProxy", + columnOffset = 1, + FILE_SEPARATOR = "_f", + LINE_SEPARATOR = "_l", + COLUMN_SEPARATOR = "_c", + DECLARED_CLASS_SEPARATOR = "__", + TYPESCRIPT_EXTEND_STRING = FILE_SEPARATOR + "rnal_ts_helpers_l47_c38", + customExtendsArr = [], + normalExtendsArr = [], + interfacesArr = []; + + /* ENTRY POINT! + * Traverses each passed node with several visitors. + * Result from visit can be got from static methods. + * + * Input parameters: + * path - node to visit + * config - filename, decorator name ... + */ + function es5Visitor(path, config) { + + if(!config.filePath) { + config.filePath = "No file path provided"; + } + + if(path.node.skipMeOnVisit) { + return; + } + + // ES5 Syntax + // anchor is extend (normal extend pattern + custom extend pattern) + if (t.isMemberExpression(path) && path.node.property.name === "extend") { + traverseEs5Extend(path, config); + } + + //anchor is new keyword (interface pattern) + if(t.isNewExpression(path)) { + traverseInterface(path, config); + } + // // Parsed Typescript to ES5 Syntax (normal extend pattern + custom extend pattern) + // // anchor is __extends + if(t.isIdentifier(path) && path.node.name === "__extends") { + traverseTsExtend(path, config); + } + + // Maybe it's not a good idea to expose this scenario because it can be explicitly covered + // //anchor is JavaProxy (optional) + // var customDecoratorName = config.extendDecoratorName === undefined ? defaultExtendDecoratorName : config.extendDecoratorName; + // if(t.isIdentifier(path) && path.node.name === customDecoratorName) { + // if(path.node.skipMeOnVisit) { + // return; + // } + // console.log("enters because there is a java proxy down the way") + // traverseJavaProxyExtend(path, config, customDecoratorName); + // } + + } + + /* + * Returns the custom extends array generated from visitor + */ + es5Visitor.getProxyExtendInfo = function () { + var res = customExtendsArr.slice(); + customExtendsArr = []; + return res; + } + + /* + * Returns the common extends array generated from visitor + */ + es5Visitor.getCommonExtendInfo = function () { + var res = normalExtendsArr.slice().filter(function (p) { + if(p.startsWith("*")) { + return false; + } + return p; + }); + normalExtendsArr = []; + return res; + } + + /* + * Returns the extended interfaces array generated from visitor + */ + es5Visitor.getInterfaceInfo = function() { + var res = interfacesArr.slice(); + interfacesArr = []; + return res; + } + + /* + * Traverses the typescript extend case (__extends()) + * Write results in "normalExtendsArr" or "customExtendsArr". + */ + function traverseTsExtend(path, config) { + + //this is the information for a normal extend + var extendClass = _getArgumentFromNodeAsString(path, 5, config) + var overriddenMethodNames = _getOverriddenMethodsTypescript(path, 3) + var extendParent = _getParrent(path, 1, config); + var declaredClassName = ""; + if(t.isCallExpression(extendParent)) { + declaredClassName = extendParent.node.arguments[0].name; + } + + // todo: check the found names in some list with predefined classes + + // check for _decorate (normal typescript extend + java proxy) + + var isDecorated = traverseToFindDecorate(path, config, extendClass, overriddenMethodNames); + if(!isDecorated) { + var lineToWrite = _generateLineToWrite("", extendClass, overriddenMethodNames, TYPESCRIPT_EXTEND_STRING + DECLARED_CLASS_SEPARATOR + declaredClassName, ""); + if(config.logger) { + config.logger.info(lineToWrite) + } + normalExtendsArr.push(lineToWrite); + } + } + + /* + * Traverses the passed node to find if there is a __decorate inside it. + * If there is __decorate identifier, then the whole node is treated as a custom extend + * This node is skipped next time because it's already traversed once here. + */ + function traverseToFindDecorate(path, config, extendClass, overriddenMethodNames) { + var iifeRoot = _getParrent(path, 3) + var body = iifeRoot.node.body; + for(var index in body) { + var ci = body[index]; + if(t.isExpressionStatement(ci) && + t.isAssignmentExpression(ci.expression) && + ci.expression.right.callee && + ci.expression.right.callee.name === "__decorate" && + ci.expression.right.arguments && + t.isArrayExpression(ci.expression.right.arguments[0])) { + + for(var i in ci.expression.right.arguments[0].elements) { + var currentDecorator = ci.expression.right.arguments[0].elements[i] + + if(t.isCallExpression) { + if(currentDecorator.callee.name === config.extendDecoratorName) { + currentDecorator.callee.skipMeOnVisit = true; + var customDecoratorName = config.extendDecoratorName === undefined ? defaultExtendDecoratorName : config.extendDecoratorName; + + traverseJavaProxyExtend(currentDecorator.arguments[0].value, config, customDecoratorName, extendClass, overriddenMethodNames); + return true; + } + } + } + } + } + } + + /* + * Traverses the node, which is a "new" expression and find if it's a native interface or not. + * Write results in "interfacesArr". + */ + function traverseInterface(path, config) { + if(!config.interfaceNames) { + throw "No interface names are provided! You can pass them in config.interfaceNames as an array!" + } + + var o = path.node.callee, + interfaceArr = _getWholeName(o), + foundInterface = false, + interfaceNames = config.interfaceNames + + var currentInterface = interfaceArr.reverse().join("."); + for(var i in interfaceNames) { + var interfaceName = interfaceNames[i].trim(); + if(interfaceName === currentInterface) { + currentInterface = interfaceName; + foundInterface = true; + break; + } + } + + if(foundInterface) { + var arg0 = "", + arg1; + if(path.node.arguments.length === 1) { + arg1 = path.node.arguments[0]; + } + else if(path.node.arguments.length === 2) { + arg0 = path.node.arguments[0]; + arg1 = path.node.arguments[1]; + } + else { + throw { + message: "Not enough or too many arguments passed(" + path.node.arguments.length + ") when trying to extend interface in file: " + config.filePath, + errCode: 1 + } + } + + var isCorrectInterfaceName = _testClassName(arg0.value); + var overriddenInterfaceMethods = _getOverriddenMethods(arg1, config); + var extendInfo = FILE_SEPARATOR + config.filePath + LINE_SEPARATOR + path.node.loc.start.line + COLUMN_SEPARATOR + (path.node.loc.start.column + columnOffset) + DECLARED_CLASS_SEPARATOR + (isCorrectInterfaceName ? arg0.value : ""); + var lineToWrite = _generateLineToWrite("", currentInterface, overriddenInterfaceMethods.join(","), extendInfo, ""); + if(config.logger) { + config.logger.info(lineToWrite) + } + interfacesArr.push(lineToWrite) + } + } + + /* + * Finds the java proxy name from custom class decorator. + * Write results in "customExtendsArr" + */ + function traverseJavaProxyExtend(path, config, customDecoratorName, extendClass, overriddenMethodNames) { + if(config.logger) { + config.logger.info("\t+in "+customDecoratorName+" anchor"); + } + + var classNameFromDecorator = path;//_getDecoratorArgument(path, config, customDecoratorName); + + var lineToWrite = _generateLineToWrite(classNameFromDecorator, extendClass, overriddenMethodNames, "", config.fullPathName); + if(config.logger) { + config.logger.info(lineToWrite) + } + customExtendsArr.push(lineToWrite) + } + + /* + * Finds the normal extend name, overridden methods and possibly java proxy name from passed node. + * Writes to "customExtendsArr" or "normalExtendsArr". + * Left whole for readability. + */ + function traverseEs5Extend(path, config) { + var callee = path.parent.callee; + + if(callee) { + var o = callee.object + extendClass = _getWholeName(o); + + var extendArguments = path.parent.arguments; + var arg0, + arg1; + if (extendArguments.length === 1 && t.isObjectExpression(arg0)) { + arg0 = extendArguments[0]; + } + else if(t.isStringLiteral(arg0)){ + + } + + var arg0 = "", + arg1; + if(extendArguments.length) { + if(extendArguments.length === 1 && t.isObjectExpression(extendArguments[0])) { + arg1 = extendArguments[0]; + } + else if(extendArguments.length === 2) { + if(t.isStringLiteral(extendArguments[0]) && t.isObjectExpression(extendArguments[1])) { + arg0 = extendArguments[0]; + arg1 = extendArguments[1]; + } + } + else { + throw { + message: "Not enough or too many arguments passed(" + extendArguments.length + ") when trying to extend class in file: " + config.filePath, + errCode: 1 + } + } + } + else { + throw { + message: "You need to call the extend with parameters. Example: '...extend(\"a.b.C\", {...overrides...})') in file: " + config.filePath, + errCode: 1 + } + } + + className = arg0.value ? arg0.value : ""; + overriddenMethodNames = _getOverriddenMethods(arg1, config); + + var isCorrectExtendClassName = _testJavaProxyName(className); + var isCorrectClassName = _testClassName(className); + if(className && !isCorrectClassName && !isCorrectExtendClassName) { + throw { + message: "The 'extend' you are trying to make has an invalid name. Example: '...extend(\"a.b.C\", {...overrides...})'), file: " + config.filePath, + errCode: 1 + } + } + + var lineToWrite = ""; + if(isCorrectExtendClassName) { + if(config.logger) { + config.logger.info(lineToWrite) + } + lineToWrite = _generateLineToWrite(isCorrectExtendClassName ? className : "", extendClass.reverse().join("."), overriddenMethodNames, "", config.fullPathName); + customExtendsArr.push(lineToWrite) + return; + } + + if(config.logger) { + config.logger.info(lineToWrite) + } + var extendInfo = FILE_SEPARATOR + config.filePath + LINE_SEPARATOR + path.node.property.loc.start.line + COLUMN_SEPARATOR + (path.node.property.loc.start.column + columnOffset) + DECLARED_CLASS_SEPARATOR + className; + lineToWrite = _generateLineToWrite(isCorrectExtendClassName ? className : "", extendClass.reverse().join("."), overriddenMethodNames, extendInfo, ""); + normalExtendsArr.push(lineToWrite) + } + else { + throw { + message: "You need to call the extend '...extend(\"extend_name\", {...overrides...})'), file: " + config.filePath, + errCode: 1 + } + } + } + +/* +* HELPER METHODS +*/ + function _getOverriddenMethods(node, config) { + var overriddenMethodNames = []; + if(t.isObjectExpression(node)) { + var objectProperties = node.properties; + for(var index in objectProperties) { + overriddenMethodNames.push(objectProperties[index].key.name) + } + } + + return overriddenMethodNames; + } + + function _getWholeName(node) { + var arr = [], + isAndroidInterface = false; + + while (node !== undefined) { + if (!t.isMemberExpression(node)) { + if(isAndroidInterface) { + arr.push(node.name) + } + break; + } + + isAndroidInterface = true; + arr.push(node.property.name) + node = node.object + } + + return arr; + } + + function _getArgumentFromNodeAsString(path, count, config) { + + var extClassArr = []; + var extendedClass = _getParrent(path, count, config); + + if(extendedClass) { + if(t.isCallExpression(extendedClass.node)) { + var o = extendedClass.node.arguments[0]; + } + else { + throw { + message: "Node type is not a call expression. File" + config.filePath, + errCode: 1 + } + } + } + + extClassArr = _getWholeName(o); + + return extClassArr.reverse().join("."); + } + + function _getDecoratorArgument(path, config, customDecoratorName) { + if(path.parent && t.isCallExpression(path.parent)) { + + if(path.parent.arguments && path.parent.arguments.length > 0) { + + var classNameFromDecorator = path.parent.arguments[0].value + var isCorrectExtendClassName = _testJavaProxyName(classNameFromDecorator); + if(isCorrectExtendClassName) { + return path.parent.arguments[0].value; + } + else { + throw { + message: "The first argument '" + classNameFromDecorator + "' of the "+customDecoratorName+" decorator is not following the right pattern which is: '[namespace.]ClassName'. Example: '"+customDecoratorName+"(\"a.b.ClassName\", {overrides...})', file: " + config.filePath, + errCode: 1 + } + } + } + else { + throw { + message: "No arguments passed to "+customDecoratorName+" decorator. Example: '"+customDecoratorName+"(\"a.b.ClassName\", {overrides...})', file: " + config.filePath, + errCode: 1 + } + } + } + else { + throw { + message: "Decorator "+customDecoratorName+" must be called with parameters: Example: '"+customDecoratorName+"(\"a.b.ClassName\", {overrides...})', file: " + config.filePath, + errCode: 1 + } + } + return undefined; + } + + function _getOverriddenMethodsTypescript(path, count) { + var methods = []; + + var cn = _getParrent(path, count) + + // this pattern follows typescript generated syntax + for(var item in cn.node.body) { + var ci = cn.node.body[item]; + if(t.isExpressionStatement(ci)) { + if(t.isAssignmentExpression(ci.expression)) { + if(ci.expression.left.property) { + methods.push(ci.expression.left.property.name) + } + } + } + } + + return methods; + } + + function _getParrent(node, numberOfParrents, config) { + if(!node) { + throw { + message: "No parent found for node in file: " + config.filePath, + errCode: 1 + } + } + if(numberOfParrents === 0) { + return node; + } + + return _getParrent(node.parentPath, --numberOfParrents) + } + + function _testJavaProxyName(name) { + if(name) { + return /^((\w+\.)+\w+)$/.test(name) + } + return false; + } + + function _testClassName(name) { + if(name && name != "") { + return /^(\w+)$/.test(name) + } + return false; + } + + function _generateLineToWrite(classNameFromDecorator, extendClass, overriddenMethodNames, extendInfo, filePath) { + var lineToWrite = extendClass + "*" + extendInfo.replace(/[\\-]/g, "_") + "*" + overriddenMethodNames + "*" + classNameFromDecorator + "*" + filePath; + return lineToWrite; + } + + return { + es5Visitor: es5Visitor + } +})(); + +module.exports = es5_visitors; \ No newline at end of file diff --git a/build/project-template-gradle/build-tools/android-static-binding-generator/binding-generator/.gitignore b/build/project-template-gradle/build-tools/android-static-binding-generator/binding-generator/.gitignore deleted file mode 100644 index 14f8bd9a2..000000000 --- a/build/project-template-gradle/build-tools/android-static-binding-generator/binding-generator/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -/jars -/build -/generated_bindings -/.gradle -bindings.txt \ No newline at end of file diff --git a/build/project-template-gradle/build-tools/android-static-binding-generator/binding-generator/build.gradle b/build/project-template-gradle/build-tools/android-static-binding-generator/binding-generator/build.gradle deleted file mode 100644 index 7b5f62a64..000000000 --- a/build/project-template-gradle/build-tools/android-static-binding-generator/binding-generator/build.gradle +++ /dev/null @@ -1,14 +0,0 @@ -/* -* This script generates a jar used to generate bindings for a nativescript project -* to run: -* gradle clean jar -*/ - -apply plugin: "java" - -jar { - manifest { - attributes("Manifest-Version": "1.0", - "Main-Class": "com.extend.generator.ExtendClassGenerator") - } -} \ No newline at end of file diff --git a/build/project-template-gradle/build-tools/android-static-binding-generator/binding-generator/src/main/java/com/extend/generator/ExtendClassGenerator.java b/build/project-template-gradle/build-tools/android-static-binding-generator/binding-generator/src/main/java/com/extend/generator/ExtendClassGenerator.java deleted file mode 100644 index ce1c0e2bc..000000000 --- a/build/project-template-gradle/build-tools/android-static-binding-generator/binding-generator/src/main/java/com/extend/generator/ExtendClassGenerator.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.extend.generator; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; - -public class ExtendClassGenerator { - - public static String jarFilesDir; - public static String outFilesDir; - - public static void main(String[] args) throws Exception { - - String jarsDir = "./jars"; - String bindingsPath = "./bindings.txt"; - String outName = "./generated_bindings"; - - if (args != null ) - { - if(args.length > 0) { - bindingsPath = args[0]; - } - if(args.length > 1) { - outName = args[1]; - } - - //rest of the arguments are jar paths - } - - new File(outName).mkdir(); //make shure there is an out folder - - String pathToBindingsFile = new File(bindingsPath).getCanonicalPath(); - outFilesDir = new File(outName).getCanonicalPath(); - - HashMap> proxyNames = ReadProxyNames(pathToBindingsFile); - - JarLister.startGenerateBindings(args, proxyNames); - } - -private static HashMap> ReadProxyNames(String bindingsPath) throws IOException { - - HashMap> result = new HashMap>(); - - // Open the file - FileInputStream fstream = new FileInputStream(bindingsPath); - BufferedReader br = new BufferedReader(new InputStreamReader(fstream)); - - String strLine; - - //Read File Line By Line - while ((strLine = br.readLine()) != null) { - - String[] lineArguments = strLine.split(" "); - - String key = lineArguments[0]; - - - if((key.indexOf("com.tns.tests") == -1)) { - - if(!result.containsKey(key)) { - result.put(key, new HashSet()); - } - - if(lineArguments.length > 1) { - - String[] methodArguments = lineArguments[1].split(","); - - for(String methodName : methodArguments) { - result.get(key).add(methodName); - } - } - } - } - - // for(String key : result.keySet()) { - // System.out.println(key); - // for(String mn : result.get(key)){ - // System.out.println("\t" + mn); - // } - // } - - //Close the input stream - br.close(); - - return result; - } -} diff --git a/build/project-template-gradle/build-tools/android-static-binding-generator/binding-generator/src/main/java/com/extend/generator/JarLister.java b/build/project-template-gradle/build-tools/android-static-binding-generator/binding-generator/src/main/java/com/extend/generator/JarLister.java deleted file mode 100644 index e04f578d8..000000000 --- a/build/project-template-gradle/build-tools/android-static-binding-generator/binding-generator/src/main/java/com/extend/generator/JarLister.java +++ /dev/null @@ -1,1021 +0,0 @@ -package com.extend.generator; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.util.ArrayList; -import java.util.jar.JarEntry; -import java.util.jar.JarInputStream; -import java.io.OutputStreamWriter; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import com.extend.generator.NSClassLoader; - -public class JarLister { - private static HashMap> overridenClasses = new HashMap>(); - private static NSClassLoader loader; - private static String LOCATION_SEPARATOR = "_f"; - - private static class MethodNameComparator implements Comparator { - @Override - public int compare(Method o1, Method o2) { - return o1.getName().compareTo(o2.getName()); - } - } - - private static void ensureDirectories(String outDir, String[] parts) { - File curDir = new File(outDir); - - for (int i = 0; i < parts.length; i++) { - String dir = parts[i]; - - String nextDir = curDir.getPath() + "/" + dir; - - File next = new File(nextDir); - - if (!next.exists()) { - next.mkdir(); - } - curDir = next; - } - } - - private static void generateJavaBindings(Class clazz, String outDir) throws Exception { - - if (clazz.isSynthetic()) { - return; - } - - int clazzModifiers = clazz.getModifiers(); - - if (!Modifier.isPublic(clazzModifiers)) { - return; - } - - if (Modifier.isStatic(clazzModifiers) && !clazz.isInterface()) { - return; - } - - boolean isFinalClass = Modifier.isFinal(clazzModifiers); - - if (isFinalClass) { - boolean hasNestedInterfaces = checkForPublicNestedInterfaces(clazz); - boolean hasNestedClasses = checkForPublicNestedStaticClasses(clazz); - - if (!hasNestedInterfaces && !hasNestedClasses) - return; - } - - if (clazz.getCanonicalName().startsWith("java.nio.")) { - return; - } - - String baseDir = outDir; - - Package classPackage = clazz.getPackage(); - - String[] parts = classPackage.getName().split("\\."); - - ensureDirectories(baseDir, parts); - - String path = classPackage.getName().replace('.', '/') + "/"; - - String packagePrefix = "com.tns.gen."; - - FileOutputStream fos = null; - OutputStreamWriter out = null; - - boolean hasPublicCtors = false; - - for (Constructor c : clazz.getConstructors()) { - if (c.isSynthetic()) { - continue; - } - - int modifiers = c.getModifiers(); - - boolean isPublic = Modifier.isPublic(modifiers); - boolean isStatic = Modifier.isStatic(modifiers); - - if (isPublic && !isStatic) { - hasPublicCtors = checkForPublicSignatureTypes(c.getParameterTypes()); - if (hasPublicCtors) { - break; - } - } - } - - if (!clazz.isInterface() && !hasPublicCtors && !isFinalClass) { - return; - } - - for (String key : overridenClasses.keySet()) { - String currentCannonicalName = key.substring(0, key.indexOf(LOCATION_SEPARATOR)); - if (currentCannonicalName.equals(clazz.getCanonicalName())) { - String fullClassName = getFullClassName(clazz, key); - fos = new FileOutputStream(baseDir + path + fullClassName + ".java"); - out = new OutputStreamWriter(fos, "UTF-8"); - - out.write("package " + packagePrefix + classPackage.getName() + ";\n\n"); - - boolean hasInitOverride = ((HashSet) overridenClasses.get(key)).contains("init"); - generateJavaBindingsRec(clazz, out, 0, key, hasInitOverride); - - out.flush(); - fos.flush(); - } - } - } - - private static String getLocationOfClass(String key) { - String extendLocation = null; - - int indexOfFileSeparator = key.indexOf(LOCATION_SEPARATOR); - extendLocation = key.substring(indexOfFileSeparator); - - return extendLocation; - } - - private static String getFullClassName(Class clazz, String key) { - Class enclosingClassName = clazz.getEnclosingClass(); - - String fullClassName = clazz.getSimpleName(); - if (enclosingClassName != null) { - fullClassName = enclosingClassName.getSimpleName() + "_" + clazz.getSimpleName(); - } - String fileLocation = getLocationOfClass(key); - return fullClassName + fileLocation; - } - - private static boolean checkForPublicSignatureTypes(Class[] params) { - boolean allTypesArePublic = true; - - if (params != null) { - for (Class p : params) { - int modifiers = p.getModifiers(); - - if (!Modifier.isPublic(modifiers)) { - allTypesArePublic = false; - break; - } - } - } - - return allTypesArePublic; - } - - private static boolean checkForPublicNestedInterfaces(Class clazz) { - boolean found = false; - - Class[] declClasses = clazz.getDeclaredClasses(); - - if (declClasses != null) { - for (Class c : declClasses) { - int modifiers = c.getModifiers(); - - if (Modifier.isPublic(modifiers) && c.isInterface()) { - found = true; - break; - } - } - } - - return found; - } - - private static boolean checkForPublicNestedStaticClasses(Class clazz) { - boolean found = false; - - Class[] declClasses = clazz.getDeclaredClasses(); - - if (declClasses != null) { - for (Class c : declClasses) { - int modifiers = c.getModifiers(); - - if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers) && !c.isInterface()) { - - Constructor[] ctors = c.getConstructors(); - - if ((ctors != null) && (ctors.length > 0)) { - found = true; - break; - } - } - } - } - - return found; - } - - private static boolean equalMethodSignatures(Method x, Method y) { - if (x.equals(y)) - return true; - - if (!x.getName().equals(y.getName())) - return false; - - Class[] xParams = x.getParameterTypes(); - Class[] yParams = y.getParameterTypes(); - - if (xParams.length != yParams.length) - return false; - - boolean result = true; - - for (int i = 0; i < xParams.length; i++) { - if (!xParams[i].equals(yParams[i])) { - result = false; - break; - } - } - - return result; - } - - private static String bridge = "com.tns.Platform."; - - private static void getEligibleMethodsHelper(Class clazz, List methods, List finals) { - Method[] declMethods = clazz.getDeclaredMethods(); - - if (declMethods != null) { - for (Method m : declMethods) { - int modifiers = m.getModifiers(); - - boolean isStaticMethod = Modifier.isStatic(modifiers); - boolean isFinalMethod = Modifier.isFinal(modifiers); - boolean isPublicMethod = Modifier.isPublic(modifiers); - boolean isProtectedMethod = Modifier.isProtected(modifiers); - boolean isVisible = isPublicMethod || isProtectedMethod; - - if (!isVisible) - continue; - - if (isFinalMethod) { - boolean found = false; - for (Method finalMethod : finals) { - if (equalMethodSignatures(m, finalMethod)) { - found = true; - break; - } - } - - if (!found) { - finals.add(m); - } - - continue; - } else if (!isStaticMethod) { - boolean found = false; - for (Method finalMethod : finals) { - if (equalMethodSignatures(m, finalMethod)) { - found = true; - break; - } - } - - if (found) - continue; - - for (Method addedMethod : methods) { - if (equalMethodSignatures(m, addedMethod)) { - found = true; - break; - } - } - - if (!found) { - methods.add(m); - } - } - } - } - } - - private static List getEligibleMethods(Class clazz) { - List result = new ArrayList(); - List finals = new ArrayList(); - LinkedList> interfaces = new LinkedList>(); - - LinkedList> q = new LinkedList>(); - q.addLast(clazz); - - Class c; - while ((c = q.pollFirst()) != null) { - getEligibleMethodsHelper(c, result, finals); - - // - Class[] subs = c.getInterfaces(); - if (subs != null) { - for (Class sub : subs) { - boolean found = false; - for (Class i : interfaces) { - if (sub.equals(i)) { - found = true; - break; - } - } - if (!found) { - interfaces.addLast(sub); - } - } - } - // - - Class base = c.getSuperclass(); - if (base != null) { - q.addLast(base); - } - } - - q.addLast(clazz); - - for (Class i : interfaces) { - q.addLast(i); - } - - while ((c = q.pollFirst()) != null) { - getEligibleMethodsHelper(c, result, finals); - - Class[] subs = c.getInterfaces(); - if (subs != null) { - for (Class sub : subs) { - q.addLast(sub); - } - } - } - - return result; - } - - private static void generateJavaBindingsRec(Class clazz, OutputStreamWriter out, int level, String currentKey, - boolean hasInitOverride) throws Exception { - int clazzModifiers = clazz.getModifiers(); - - if (!Modifier.isPublic(clazzModifiers)) { - return; - } - - String fullClassName = getFullClassName(clazz, currentKey); - - boolean isFinalClass = Modifier.isFinal(clazzModifiers); - - String tabs = getTabsForLevel(level); - - if (isFinalClass) { - if (level == 0) { - out.write(tabs + "public final class " + fullClassName + " {\n"); - } else { - out.write(tabs + "public static final class " + fullClassName + " {\n"); - } - - out.write(tabs + "\tprivate " + clazz.getSimpleName() + "() {\n"); - out.write(tabs + "\t}\n"); - - out.write(tabs + "}\n"); - - return; - } - - boolean hasPublicCtors = false; - - if (clazz.isInterface()) { - hasPublicCtors = true; - - if (level > 0) { - out.write(tabs + "public static class " + fullClassName + " implements " + clazz.getCanonicalName() - + ", com.tns.NativeScriptHashCodeProvider {\n"); - } else { - out.write(tabs + "public class " + fullClassName + " implements " + clazz.getCanonicalName() - + ", com.tns.NativeScriptHashCodeProvider {\n"); - } - -// for (Class nested : clazz.getDeclaredClasses()) { -// -// generateJavaBindingsRec(nested, out, level + 1, currentKey, hasInitOverride); -// } - } else { - for (Constructor c : clazz.getConstructors()) { - if (c.isSynthetic()) { - continue; - } - - if (!hasPublicCtors) { - if (Modifier.isStatic(clazzModifiers)) { - out.write(tabs + "public static class " + fullClassName + " extends " - + clazz.getCanonicalName() + " implements com.tns.NativeScriptHashCodeProvider {\n"); - } else { - out.write(tabs + "public class " + fullClassName + " extends " + clazz.getCanonicalName() + " implements com.tns.NativeScriptHashCodeProvider {\n"); - } - hasPublicCtors = true; - - // for (Class nested : clazz.getDeclaredClasses()) { - // generateJavaBindingsRec(nested, out, level + 1); - // } - } - - writeConstructorSignature(out, level, c, fullClassName); - writeExceptionSignature(out, c); - writeConstructorBody(out, level, clazz, c, hasInitOverride); - } - } - - if (!hasPublicCtors) { - return; - } - - boolean isAndroidApplicationClass = isAndroidApplicationClass(clazz); - - if (isAndroidApplicationClass) { - writeKimeraLoadLibraryStaticSection(out, level); - } - - List methods2 = getEligibleMethods(clazz); - - Collections.sort(methods2, new Comparator() { - @Override - public int compare(Method x, Method y) { - return x.getName().compareTo(y.getName()); - } - }); - - int methodGroupIdx = -1; - ArrayList methodGroups = new ArrayList(); - String lastMethodGroupName = ""; - - for (Method m : methods2) { - if (m.isSynthetic()) { - continue; - } - - int modifiers = m.getModifiers(); - - boolean isFinal = Modifier.isFinal(modifiers); - boolean isStatic = Modifier.isStatic(modifiers); - boolean isAbstract = Modifier.isAbstract(modifiers); - - if (!overridenClasses.get(currentKey).contains(m.getName()) && !clazz.isInterface() && !isAbstract) { - continue; - } - - if (!isFinal && !isStatic) { - // - if (!m.getName().equals(lastMethodGroupName)) { - ++methodGroupIdx; - String currentMethodName = m.getName(); - methodGroups.add(currentMethodName); - lastMethodGroupName = currentMethodName; - } - // - - Map map = getGenericParentsMap2(clazz); - - Type genRetType = m.getGenericReturnType(); - - Type res = genRetType; - while (map.containsKey(res)) { - res = map.get(res); - } - - String retType; - if (res instanceof Class) { - retType = ((Class) res).getCanonicalName(); - } else { - retType = m.getReturnType().getCanonicalName(); - } - - Type[] genTypes = m.getGenericParameterTypes(); - - boolean isAndroidApplicationOnCreateMethod = isAndroidApplicationClass - && m.getName().equals("onCreate"); - - String methodName = isAndroidApplicationOnCreateMethod ? "onCreateInternal" : null; - - writeMethodSignature(out, level, m, retType, map, genTypes, methodName); - writeExceptionSignature(out, m); - - if (clazz.isInterface()) { - if (!overridenClasses.get(currentKey).contains(m.getName())) { - writeThrowExceptionImplementation(out, level, m, retType); - } else { - writeInterfaceMethodImplementation(out, level, m, retType); - } - } else { - if (isAbstract) { - if(!overridenClasses.get(currentKey).contains(m.getName())) { - writeThrowExceptionImplementation(out, level, m, retType); - } else { - writeMethodBody(out, level, clazz, m, retType, methodGroupIdx); -// writeAbstractMethodImplementation(out, level, m, retType); - } - } else { - writeMethodBody(out, level, clazz, m, retType, methodGroupIdx); - } - } - - if (isAndroidApplicationOnCreateMethod) { - writeMethodSignature(out, level, m, retType, map, genTypes, null); - writeExceptionSignature(out, m); - - writeAndroidApplicationOnCreateMethodBody(out, level); - } - - if (isAbstract) - continue; - } - } - - writeNativeScriptHashCodeProviderMethods(out, level, clazz, methodGroupIdx, methodGroups); - - out.write(tabs + "}\n"); - } - - private static void writeThrowExceptionImplementation(OutputStreamWriter out, int level, Method m, String retType) - throws Exception { - - String className = m.getDeclaringClass().getName(); - String methodName = m.getName(); - String errorMessageString = "You haven't overriden " + methodName + " in class " + className; - - Class[] paramTypes = m.getParameterTypes(); - String tabs = getTabsForLevel(level); - - out.write(" {\n"); - out.write(tabs + "\t\tthrow new UnsupportedOperationException(\"" + errorMessageString + "\");\n"); - out.write(tabs + "\t}\n\n"); - } - - private static void writeNativeScriptHashCodeProviderMethods(OutputStreamWriter out, int level, Class clazz, - int methodGroupIdx, ArrayList methodGroups) throws Exception { - String tabs = getTabsForLevel(level); - - out.write(tabs + "\tpublic boolean equals__super(java.lang.Object other) {\n"); - out.write(tabs + "\t\treturn super.equals(other);\n"); - out.write(tabs + "\t}\n"); - - out.write(tabs + "\tpublic int hashCode__super() {\n"); - out.write(tabs + "\t\treturn super.hashCode();\n"); - out.write(tabs + "\t}\n"); - - if (!clazz.isInterface()) { - writeSetKimeraOverrides(out, level, clazz, methodGroupIdx, methodGroups); - } - } - - private static void writeConstructorSignature(OutputStreamWriter out, int level, Constructor c, - String fullClassName) throws Exception { - String tabs = getTabsForLevel(level); - - Class declClass = c.getDeclaringClass(); - - out.write(tabs + "\tpublic " + fullClassName); - out.write("("); - Class[] paramTypes = c.getParameterTypes(); - - int modifiers = declClass.getModifiers(); - - boolean isStaticType = Modifier.isStatic(modifiers); - boolean isNestedType = declClass.getEnclosingClass() != null; - - int startIndex = (isNestedType && !isStaticType) ? 1 : 0; - for (int i = startIndex; i < paramTypes.length; i++) { - if (i > startIndex) { - out.write(", "); - } - out.write(paramTypes[i].getCanonicalName() + " param_" + i); - } - out.write(")"); - } - - private static void writeMethodSignature(OutputStreamWriter out, int level, Method m, String retType, - Map map, Type[] genTypes, String methodName) throws Exception { - String tabs = getTabsForLevel(level); - - Class[] paramTypes = m.getParameterTypes(); - - int modifiers = m.getModifiers(); - - boolean isPublic = Modifier.isPublic(modifiers); - - if (isPublic) { - out.write(tabs + "\tpublic " + retType + " " + ((methodName == null) ? m.getName() : methodName)); - } else { - out.write(tabs + "\tprotected " + retType + " " + ((methodName == null) ? m.getName() : methodName)); - } - out.write("("); - - for (int i = 0; i < genTypes.length; i++) { - if (i > 0) { - out.write(", "); - } - if (genTypes[i] instanceof TypeVariable) { - Type res = genTypes[i]; - while (map.containsKey(res)) { - res = map.get(res); - } - if (res instanceof Class) { - out.write(((Class) res).getCanonicalName() + " param_" + i); - } else { - out.write(paramTypes[i].getCanonicalName() + " param_" + i); - } - } else { - out.write(paramTypes[i].getCanonicalName() + " param_" + i); - } - } - out.write(")"); - } - - private static void writeAndroidApplicationOnCreateMethodBody(OutputStreamWriter out, int level) throws Exception { - String tabs = getTabsForLevel(level); - - out.write(" {\n"); - out.write(tabs + "\t\tcom.tns.Platform.onCreateApplication(this);\n"); - out.write(tabs + "\t}\n\n"); - } - - private static void writeMethodBody(OutputStreamWriter out, int level, Class clazz, Method m, String retType, - int methodGroupIdx) throws Exception { - String tabs = getTabsForLevel(level); - - Class[] paramTypes = m.getParameterTypes(); - - out.write(" {\n"); - - if (checkIfMustWriteInitializationSection(clazz, m)) { - boolean shouldInitializeWithIntent = clazz.getName().equals("android.app.Activity") - && m.getName().equals("onCreate"); - - writeCheckForInitialization(out, level, shouldInitializeWithIntent); - } - - if (paramTypes.length == 0) { - out.write(tabs + "\t\t\tjava.lang.Object[] params = null;\n"); - } else { - out.write(tabs + "\t\t\tjava.lang.Object[] params = new Object[" + paramTypes.length + "];\n"); - for (int i = 0; i < paramTypes.length; i++) { - out.write(tabs + "\t\t\tparams[" + i + "] = param_" + i + ";\n"); - } - } - out.write(tabs + "\t\t\t"); - if (retType != "void") { - String wrappedRetType = wrapPrimitiveType(retType); - out.write("return (" + wrappedRetType + ")"); - } - if (m.getName().equals("init")) { - out.write(bridge + "callJSMethod(this, \"" + m.getName() + "\", " + retType + ".class, false, params);\n"); - } else { - out.write(bridge + "callJSMethod(this, \"" + m.getName() + "\", " + retType + ".class, params);\n"); - } - - out.write(tabs + "\t}\n\n"); - - } - - private static boolean checkIfMustWriteInitializationSection(Class clazz, Constructor c) { - String className = clazz.getCanonicalName(); - - if (className.equals("android.app.Activity")) { - return false; - } else if (isAndroidApplicationClass(clazz)) { - return false; - } - - return true; - } - - private static boolean checkIfMustWriteInitializationSection(Class clazz, Method m) { - String className = clazz.getCanonicalName(); - - if (className.equals("android.app.Activity")) { - String methodName = m.getName(); - - if (methodName.equals("attachBaseContext") || methodName.equals("getSystemService") - || methodName.equals("getBaseContext") || methodName.equals("setTheme") - || methodName.equals("getResources") || methodName.equals("getApplicationInfo") - || methodName.equals("onApplyThemeResource")) { - return false; - } - } else if (className.equals("android.app.Application")) { - return false; - } - - return true; - } - - private static void writeConstructorBody(OutputStreamWriter out, int level, Class clazz, Constructor c, - boolean hasInitOverride) throws Exception { - String tabs = getTabsForLevel(level); - - Class declClass = c.getDeclaringClass(); - - int modifiers = declClass.getModifiers(); - - boolean isStaticType = Modifier.isStatic(modifiers); - boolean isNestedType = declClass.getEnclosingClass() != null; - - Class[] paramTypes = c.getParameterTypes(); - - int startIndex = (isNestedType && !isStaticType) ? 1 : 0; - - out.write(" {\n"); - - out.write(tabs + "\t\tsuper("); - for (int i = startIndex; i < paramTypes.length; i++) { - if (i > startIndex) { - out.write(", "); - } - out.write("param_" + i); - } - out.write(");\n"); - - if (checkIfMustWriteInitializationSection(clazz, c)) { - writeCheckForInitialization(out, level, false /* shouldInitializedWithIntent */); - } - - if (hasInitOverride) { - int len1 = paramTypes.length - startIndex; - if (len1 == 0) { - out.write(tabs + "\t\t\tjava.lang.Object[] params = null;\n"); - } else { - out.write(tabs + "\t\t\tjava.lang.Object[] params = new Object[" + paramTypes.length + "];\n"); - for (int i = startIndex; i < paramTypes.length; i++) { - out.write(tabs + "\t\t\tparams[" + i + "] = param_" + i + ";\n"); - } - } - out.write(tabs + "\t\tcom.tns.Platform.callJSMethod(this, \"init\", void.class, true, params);\n"); - } - out.write(tabs + "\t}\n\n"); - - } - - private static void writeCheckForInitialization(OutputStreamWriter out, int level, - boolean shouldInitializedWithIntent) throws Exception { - String tabs = getTabsForLevel(level); - - out.write(tabs + "\t\tif (!__initialized) {\n"); - out.write(tabs + "\t\t\t__initialized = true;\n"); - if (shouldInitializedWithIntent) { - out.write(tabs + "\t\t\tcom.tns.Platform.initInstance(this, super.getIntent());\n"); - } else { - out.write(tabs + "\t\t\tcom.tns.Platform.initInstance(this);\n"); - } - out.write(tabs + "\t\t}\n"); - } - - private static void writeExceptionSignature(OutputStreamWriter out, Constructor c) throws Exception { - Class[] exc = c.getExceptionTypes(); - if (exc.length > 0) { - out.write(" throws "); - for (int i = 0; i < exc.length; i++) { - if (i > 0) { - out.write(", "); - } - out.write(exc[i].getCanonicalName()); - } - } - } - - private static void writeExceptionSignature(OutputStreamWriter out, Method m) throws Exception { - Class[] exc = m.getExceptionTypes(); - if (exc.length > 0) { - out.write(" throws "); - for (int i = 0; i < exc.length; i++) { - if (i > 0) { - out.write(", "); - } - out.write(exc[i].getCanonicalName()); - } - } - } - - private static void writeInterfaceMethodImplementation(OutputStreamWriter out, int level, Method m, String retType) - throws Exception { - out.write(" {\n"); - - Class[] paramTypes = m.getParameterTypes(); - - String tabs = getTabsForLevel(level); - - if (paramTypes.length == 0) { - out.write(tabs + "\t\tjava.lang.Object[] params = null;\n"); - } else { - out.write(tabs + "\t\tjava.lang.Object[] params = new Object[" + paramTypes.length + "];\n"); - for (int i = 0; i < paramTypes.length; i++) { - out.write(tabs + "\t\tparams[" + i + "] = param_" + i + ";\n"); - } - } - out.write(tabs + "\t\t"); - if (retType != "void") { - String wrappedRetType = wrapPrimitiveType(retType); - out.write("return (" + wrappedRetType + ")"); - } - out.write(bridge + "callJSMethod(this, \"" + m.getName() + "\", " + retType + ".class, params);\n"); - - out.write(tabs + "\t}\n\n"); - } - - private static void writeAbstractMethodImplementation(OutputStreamWriter out, int level, Method m, String retType) - throws Exception { - out.write(" {\n"); - - Class[] paramTypes = m.getParameterTypes(); - - String tabs = getTabsForLevel(level); - - if (paramTypes.length == 0) { - out.write(tabs + "\t\tjava.lang.Object[] params = null;\n"); - } else { - out.write(tabs + "\t\tjava.lang.Object[] params = new Object[" + paramTypes.length + "];\n"); - for (int i = 0; i < paramTypes.length; i++) { - out.write(tabs + "\t\tparams[" + i + "] = param_" + i + ";\n"); - } - } - out.write(tabs + "\t\t"); - if (retType != "void") { - String wrappedRetType = wrapPrimitiveType(retType); - out.write("return (" + wrappedRetType + ")"); - } - out.write(bridge + "callJSMethod(this, \"" + m.getName() + "\", " + retType + ".class, params);\n"); - - out.write(tabs + "\t}\n\n"); - } - - private static boolean isAndroidApplicationClass(Class clazz) { - return clazz.getCanonicalName().equals("android.app.Application"); - } - - private static void writeKimeraLoadLibraryStaticSection(OutputStreamWriter out, int level) throws Exception { - String tabs = getTabsForLevel(level); - - out.write(tabs + "\tstatic {\n"); - out.write(tabs + "\t\tSystem.loadLibrary(\"NativeScript\");\n"); - out.write(tabs + "\t\tif (BuildConfig.DEBUG) {\n"); - out.write(tabs + "\t\t\tandroid.os.Debug.waitForDebugger();\n"); - out.write(tabs + "\t\t}\n"); - out.write(tabs + "\t}\n\n"); - } - - private static void writeSetKimeraOverrides(OutputStreamWriter out, int level, Class clazz, int methodGroupIdx, - List methodGroups) throws Exception { - String tabs = getTabsForLevel(level); - - if (isAndroidApplicationClass(clazz)) { - out.write(tabs + "\tprivate boolean __initialized = true;\n"); - } else { - out.write(tabs + "\tprivate boolean __initialized;\n"); - } - } - - private static String getTabsForLevel(int level) { - String tabs = ""; - for (int i = 0; i < level; i++) { - tabs += "\t"; - } - - return tabs; - } - - public static Map getGenericParentsMap2(Class clazz) { - List parents = new ArrayList(); - - Class oldParent = clazz; - Class parentClass = clazz.getSuperclass(); - - while ((parentClass != null) && (parentClass != Object.class)) { - Type nextParent = oldParent.getGenericSuperclass(); - parents.add(nextParent); - oldParent = parentClass; - parentClass = parentClass.getSuperclass(); - } - - Map map = new HashMap(); - - for (Type p : parents) { - if (p instanceof ParameterizedType) { - ParameterizedType pt = (ParameterizedType) p; - Type[] typeParameters = ((Class) pt.getRawType()).getTypeParameters(); - Type[] actualTypeArgs = pt.getActualTypeArguments(); - for (int i = 0; i < typeParameters.length; i++) { - map.put(typeParameters[i], actualTypeArgs[i]); - } - } - } - return map; - } - - private static String wrapPrimitiveType(String type) { - String wrapped; - - if (type.equals("boolean") || type.equals("byte") || type.equals("short") || type.equals("long") - || type.equals("float") || type.equals("double")) { - wrapped = type.substring(0, 1).toUpperCase() + type.substring(1); - } else if (type.equals("char")) { - wrapped = "Character"; - } else if (type.equals("int")) { - wrapped = "Integer"; - } else { - wrapped = type; - } - - return wrapped; - } - - public static void startGenerateBindings(String[] args, HashMap> proxyNames) - throws Exception { - overridenClasses = proxyNames; - - loader = NSClassLoader.getInstance(); - loader.loadJars(args); - - String[] jars = loader.getJarNames(); - - String[] outDirs = new String[jars.length]; - - for (int i = 0; i < jars.length; i++) { - String[] dirs = { "com/", "tns/", "gen/" }; - String outputDir = ExtendClassGenerator.outFilesDir + '/'; - for (String d : dirs) { - outputDir += d; - File fd = new File(outputDir); - if (!fd.exists()) { - fd.mkdir(); - } - } - outDirs[i] = outputDir; - } - - start(args, jars, outDirs); - } - - private static void start(String[] args, String[] jars, String[] outDirs) throws Exception { - - for (int i = 0; i < jars.length; i++) { - String jarFile = jars[i]; - String outDir = outDirs[i]; - - JarInputStream input = null; - - String jarFilename = new File(jarFile).getCanonicalPath(); - try { - input = new JarInputStream(new FileInputStream(jarFilename)); - } - catch(FileNotFoundException e) { - System.out.println("The jar could not be found: " + e.getMessage()); - continue; - } - - JarEntry entry = input.getNextJarEntry(); - ArrayList classes = new ArrayList(); - while (entry != null) { - try { - String name = entry.getName(); - - if (!name.endsWith(".class")) - continue; - - name = name.substring(0, name.length() - 6).replace('/', '.'); - classes.add(name); - } finally { - entry = input.getNextJarEntry(); - } - } - - // - Collections.sort(classes); - // - - for (String className : classes) { - // - if (jarFile.endsWith("nativescript.jar") && className.startsWith("com.tns.com.tns.tests.")) { - continue; - } - // - - try { - Class clazz = Class.forName(className, false, loader); - generateJavaBindings(clazz, outDir); - } catch (NoClassDefFoundError e) { - System.out.println("No deffinition could be found for: " + e.getMessage()); - continue; - } finally { - if (input != null) { - input.close(); - } - } - } - } - } - -} diff --git a/build/project-template-gradle/build-tools/android-static-binding-generator/binding-generator/src/main/java/com/extend/generator/NSClassLoader.java b/build/project-template-gradle/build-tools/android-static-binding-generator/binding-generator/src/main/java/com/extend/generator/NSClassLoader.java deleted file mode 100644 index b3d03f6ec..000000000 --- a/build/project-template-gradle/build-tools/android-static-binding-generator/binding-generator/src/main/java/com/extend/generator/NSClassLoader.java +++ /dev/null @@ -1,196 +0,0 @@ -package com.extend.generator; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.Collections; -import java.util.jar.JarEntry; -import java.util.jar.JarInputStream; - -public class NSClassLoader extends URLClassLoader -{ - private static NSClassLoader instance; - private ArrayList> classNames; - private ArrayList jarFiles; - - public NSClassLoader(URL[] urls, ClassLoader parent) - { - super(urls, parent); - - this.classNames = new ArrayList>(); - this.jarFiles = new ArrayList(); - } - - public ArrayList> getClassNames() - { - return this.classNames; - } - - public String[] getJarNames() { - String[] jarNames = new String[this.jarFiles.size()]; - - for(int i = 0; i < this.jarFiles.size(); i++) { - - String jarName = this.jarFiles.get(i); - // String currentName = jarName.substring(jarName.lastIndexOf('\\') + 1); - jarNames[i] = jarName; - } - - return jarNames; - } - - public void loadDir(String path) - { - File dir = new File(path); - this.traverseDir(dir); - this.populateClassNames(); - } - - public void loadJars(String[] args) { - for(int i = 2; i < args.length; i ++) { - String currentJar = args[i]; - onFile(new File(currentJar)); - } - } - - private void populateClassNames() - { - JarInputStream input = null; - - for (String jar : this.jarFiles) - { - ArrayList jarClassNames = new ArrayList(); - try - { - input = new JarInputStream(new FileInputStream(jar)); - JarEntry entry = input.getNextJarEntry(); - - while (entry != null) - { - String name = entry.getName(); - if (this.isValidClass(jar, name)) - { - name = name.substring(0, name.length() - 6) - .replace('/', '.'); - jarClassNames.add(name); - } - entry = input.getNextJarEntry(); - } - - } - catch (IOException e) - { - // TODO Auto-generated catch block - e.printStackTrace(); - continue; - } - finally - { - if (input != null) - { - try - { - input.close(); - } - catch (IOException e) - { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - } - - Collections.sort(jarClassNames); - this.classNames.add(jarClassNames); - } - } - - private boolean isValidClass(String jar, String className) - { - if (!className.endsWith(".class")) - { - return false; - } - - if (jar.equals("nativescript.jar") && className.startsWith("com.tns.com.tns.tests.")) - { - return false; - } - - return true; - } - - private void traverseDir(File file) - { - if (!file.exists() || !file.isDirectory()) - { - return; - } - - File[] files = file.listFiles(); - for (File childFile : files) - { - if (childFile.isDirectory()) - { - traverseDir(childFile); - } - else - { - onFile(childFile); - } - } - } - - private void onFile(File file) - { - if (!isJarFile(file)) - { - return; - } - - try - { - URL url = file.toURI() - .toURL(); - this.addURL(url); - this.jarFiles.add(file.getPath()); - } - catch (MalformedURLException e) - { - e.printStackTrace(); - } - } - - private boolean isJarFile(File file) - { - return file.getPath() - .endsWith(".jar"); - } - - public static NSClassLoader getInstance() - { - if (instance == null) - { - URL[] urls; - ClassLoader systemLoader = ClassLoader.getSystemClassLoader(); - if (systemLoader instanceof URLClassLoader) - { - urls = ((URLClassLoader) systemLoader).getURLs(); - } - else - { - urls = new URL[0]; - } - - instance = new NSClassLoader(urls, systemLoader); - } - - return instance; - } -} diff --git a/build/project-template-gradle/build-tools/android-static-binding-generator/binding-generator/test_coommand.txt b/build/project-template-gradle/build-tools/android-static-binding-generator/binding-generator/test_coommand.txt deleted file mode 100644 index c16a6c8e9..000000000 --- a/build/project-template-gradle/build-tools/android-static-binding-generator/binding-generator/test_coommand.txt +++ /dev/null @@ -1 +0,0 @@ -java -jar extend_classes_grator.jar D:\work\!TEMP\testapp\lib\Android ./../ast-parser/bindings.txt ./out diff --git a/build/project-template-gradle/build-tools/android-static-binding-generator/build.gradle b/build/project-template-gradle/build-tools/android-static-binding-generator/build.gradle index bc1062e96..96cb4e7b1 100644 --- a/build/project-template-gradle/build-tools/android-static-binding-generator/build.gradle +++ b/build/project-template-gradle/build-tools/android-static-binding-generator/build.gradle @@ -1,80 +1,49 @@ /* -* The android static binding generator will generate bindings for some javascript code you specify. -* The passed output directory will be created if necessary -* to run: -* gradle generatebindings -PjsCodeDir=[js_code_dir] -PjarsDir=[jars_dir] -PoutDir=[out_dir] +* The android static binding generator will generate bindings for the Javascript code you specify. */ -def rDir = "$rootDir/build-tools/android-static-binding-generator/" -def distDir = "$rDir/dist" -def interfaceNameGeneratorDir = "$rDir/interface-name-generator" -def interfaceNameGeneratorLibsDir = "${interfaceNameGeneratorDir}/build/libs" -def bindingGeneratorDir = "$rDir/binding-generator" -def bindingGeneratorLibsDir = "${bindingGeneratorDir}/build/libs" -def astParserDir = "$rDir/ast-parser" -def defaultJarsDir = "$rDir/jars" -def jarsPath = "" - -def pathToBindingsFile = "${astParserDir}/bindings.txt" //this file is generated by ast-parser def isWinOs = System.properties['os.name'].toLowerCase().contains('windows') -def isJsCodePathPassed = project.hasProperty("jsCodeDir") -def isJarsDirPassed = project.hasProperty("jarsDir") -def isOutDirPassed = project.hasProperty("outDir") +def bgRootDir = "$rootDir/build-tools/android-static-binding-generator" +def astParserDir = "$bgRootDir/ast-parser" +def interfaceNamesFilePath = "$bgRootDir/interfaces-names.txt" +def bindingsFilePath = "$bgRootDir/bindings.txt" +def cachedJarsFilePath = "$bgRootDir/cached.dat" -// if(!isJsCodePathPassed) { - // throw new GradleException("\n\t<< ERROR >>: Please pass 'jsCodeDir' property. Static binding generator needs to know where is the javascript code you want to generate bindings for. \n\t<>: -PjsCodeDir=[path_to_js_code_dir]\n") -// } -// if(!isJarsDirPassed) { - // throw new GradleException("\n\t<< ERROR >>: Please pass 'jarsDir' property. The static binding generator needs to know where are the jars you will use for your project, in order to generate bindings for them. Please specify a path to those jars. \n\t<>: -PjarsDir=[path_to_jars_dir]\n") -// } +def absoluteOutDir = "$rootDir/$project.outDir" +def absoluteJsCodeDir = "$rootDir/$project.jsCodeDir" -// if(!isOutDirPassed) { - // throw new GradleException("\n\t<< ERROR >>: Please pass 'outDir' property. Static binding generator needs to know where do you want to put the bindings that will be generated. Please provide an out directory or specify one that will be created if missing. \n\t<>: -PoutDir=[path_to_output_dir]\n") -// } - -task cleanDist (type: Delete) { - delete distDir -} - -task mkDirDist { - def distFolder = file(distDir) - if(!isJarsDirPassed) { - file(defaultJarsDir).mkdirs() - } - distFolder.mkdirs() -} - -// task buildInterfaceGenerator (dependsOn: "ing:jar") -// { - // dependsOn "ing:jar" - // workingDir interfaceNameGeneratorDir +// depends on passed jars and generated interface-names +task generateInterfaceNamesList(type: JavaExec) { + def cachedJars = new File(cachedJarsFilePath) - // if(isWinOs) { - // commandLine "cmd", "/c", "gradle", "jar" - // } - // else { - // commandLine "gradle", "jar" - // } -// } + inputs.file(cachedJars) + outputs.files(interfaceNamesFilePath) -task generateInterfaceNamesList(type: JavaExec) { doFirst { - workingDir interfaceNameGeneratorLibsDir - main "-jar" - jarsPath = rootProject.jarsDir.toString().replaceAll(/[\[\]]/, "").split(", ") + def jarsAsStr = rootProject.jarFiles.toString(); + def jarsArr = jarsAsStr.replaceAll(/[\[\]]/, "").split(", ") def str = new LinkedList (); - str.add("${interfaceNameGeneratorDir}/build/libs/ing.jar") - str.addAll(jarsPath) + str.add("interfacenamegenerator.jar") + str.addAll(jarsArr) args str.toArray() + + cachedJars.text = jarsAsStr } } +// won't run if node_modules are installed task runNpmInstallForAstParser (type: Exec) { + + //check this way so it doesn't slow down the build by snapshot-ing the node_modules + outputs.upToDateWhen { + (new File("$astParserDir/node_modules")).exists() + } + workingDir astParserDir if(isWinOs) { @@ -85,65 +54,41 @@ task runNpmInstallForAstParser (type: Exec) { } } +// if there are new dependencies the parser will run again task runAstParser (type: Exec) { + + inputs.files fileTree(dir: absoluteJsCodeDir) + outputs.files(bindingsFilePath) + workingDir astParserDir if(isWinOs) { - commandLine "cmd", "/c", "node", "traverse_files.js", "${interfaceNameGeneratorLibsDir}/interfaces-names.txt", jsCodeDir + commandLine "cmd", "/c", "node", "js_parser.js" , absoluteJsCodeDir, "../bindings.txt" } else { - commandLine "node", "traverse_files.js", "${interfaceNameGeneratorLibsDir}/interfaces-names.txt", jsCodeDir + commandLine "node", "js_parser.js", absoluteJsCodeDir, "../bindings.txt" } } -// task buildBindingGenerator (type: Exec) { - // workingDir bindingGeneratorDir - - // if(isWinOs) { - // commandLine "cmd", "/c", "gradle", "jar" - // } - // else { - // commandLine "gradle", "jar" - // } -// } - +// run the static binding generator task generateBindings(type: JavaExec) { - doFirst { - workingDir bindingGeneratorLibsDir - - main "-jar" - - def str = new LinkedList (); - - str.add("bg.jar") - - //generated bindings names - str.add(pathToBindingsFile) - //out dir (optional) - str.add("$rDir/${outDir}") + inputs.files(bindingsFilePath) + outputs.dir(absoluteOutDir) - //jar folder to run through - str.addAll(jarsPath) //fix hardcoded path (will be passed as project parameter) - - - args str.toArray() + doFirst { + main "-jar" + + def str = new LinkedList (); + str.add("staticbindinggenerator.jar") + str.add(bindingsFilePath) + str.add(absoluteOutDir) + str.addAll(project.jarFiles) + + args str.toArray() } } -mkDirDist.dependsOn(cleanDist) - -// run interface names generator -// buildInterfaceGenerator.dependsOn(mkDirDist) -generateInterfaceNamesList.dependsOn(mkDirDist) - -//run ast parser runNpmInstallForAstParser.dependsOn(generateInterfaceNamesList) runAstParser.dependsOn(runNpmInstallForAstParser) - -// buildBindingGenerator.dependsOn(runAstParser) -generateBindings.dependsOn(runAstParser) - -task generatebindings { - dependsOn generateBindings -} \ No newline at end of file +generateBindings.dependsOn(runAstParser) \ No newline at end of file diff --git a/build/project-template-gradle/build-tools/android-static-binding-generator/interface-name-generator/.gitignore b/build/project-template-gradle/build-tools/android-static-binding-generator/interface-name-generator/.gitignore deleted file mode 100644 index 00fd4dddb..000000000 --- a/build/project-template-gradle/build-tools/android-static-binding-generator/interface-name-generator/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/build -/.gradle \ No newline at end of file diff --git a/build/project-template-gradle/build-tools/android-static-binding-generator/interface-name-generator/build.gradle b/build/project-template-gradle/build-tools/android-static-binding-generator/interface-name-generator/build.gradle deleted file mode 100644 index 42a2cfffc..000000000 --- a/build/project-template-gradle/build-tools/android-static-binding-generator/interface-name-generator/build.gradle +++ /dev/null @@ -1,8 +0,0 @@ -apply plugin: "java" - -jar { - manifest { - attributes("Manifest-Version": "1.0", - "Main-Class": "com.ig.GetInterfaceNames") - } -} \ No newline at end of file diff --git a/build/project-template-gradle/build-tools/android-static-binding-generator/interface-name-generator/src/main/java/com/ig/GetInterfaceNames.java b/build/project-template-gradle/build-tools/android-static-binding-generator/interface-name-generator/src/main/java/com/ig/GetInterfaceNames.java deleted file mode 100644 index 752fe7ed4..000000000 --- a/build/project-template-gradle/build-tools/android-static-binding-generator/interface-name-generator/src/main/java/com/ig/GetInterfaceNames.java +++ /dev/null @@ -1,98 +0,0 @@ -package com.ig; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.PrintWriter; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.Enumeration; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; - -public class GetInterfaceNames { - - private static String currentDir; - - public static void main(String[] args) throws IOException, ClassNotFoundException { - currentDir = System.getProperty("user.dir"); - String outputFileName = "interfaces-names.txt"; - - // System.out.println("inside: " + args); - if (args != null) { - if(args.length < 1) { - throw new IllegalArgumentException("There are no parameters passed!"); - } - } - - PrintWriter out = ensureOutputFile(outputFileName); - - for(String pathToJar : args) { - // System.out.println("\t+jar: " + pathToJar); - if(pathToJar.endsWith(".jar")) { - generateInterfaceNames(pathToJar, out); - } - } - - out.close(); - } - - private static void generateInterfaceNames(String pathToJar, PrintWriter out) throws IOException, ClassNotFoundException { - - if(pathToJar == null) { - return; - } - - JarFile jarFile = new JarFile(pathToJar); - Enumeration currentJarFile = jarFile.entries(); - - URLClassLoader cl = getClassLoader(pathToJar); - - while (currentJarFile.hasMoreElements()) { - JarEntry jarEntry = (JarEntry) currentJarFile.nextElement(); - - if (jarEntry.isDirectory() || !jarEntry.getName().endsWith(".class")) { - continue; - } - - String className = jarEntry.getName().substring(0, jarEntry.getName().length() - 6);// -6 because of .class - className = className.replace('/', '.'); - - Class c = null; - try { - c = cl.loadClass(className); - } - catch (NoClassDefFoundError e) { - } - - if (c != null && c.isInterface() == true) { - String res = c.getName().replace('$', '.'); - // System.out.println(res); - out.println(res); - } - } - jarFile.close(); - } - - private static PrintWriter ensureOutputFile(String outputFileName) throws IOException { - File checkFile = new File(currentDir, outputFileName); - if(checkFile.exists()) { - checkFile.delete(); - } - else { - checkFile.getParentFile().mkdirs(); - checkFile.createNewFile(); - } - - PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(checkFile.getAbsolutePath(), true))); - return out; - } - - private static URLClassLoader getClassLoader(String pathToJar) throws MalformedURLException { - URL[] urls = { new URL("jar:file:" + pathToJar + "!/") }; - URLClassLoader cl = URLClassLoader.newInstance(urls); - return cl; - } -} diff --git a/build/project-template-gradle/build-tools/android-static-binding-generator/interfacenamegenerator.jar b/build/project-template-gradle/build-tools/android-static-binding-generator/interfacenamegenerator.jar new file mode 100644 index 000000000..bebc3ef33 Binary files /dev/null and b/build/project-template-gradle/build-tools/android-static-binding-generator/interfacenamegenerator.jar differ diff --git a/build/project-template-gradle/build-tools/android-static-binding-generator/staticbindinggenerator.jar b/build/project-template-gradle/build-tools/android-static-binding-generator/staticbindinggenerator.jar new file mode 100644 index 000000000..78fae94e9 Binary files /dev/null and b/build/project-template-gradle/build-tools/android-static-binding-generator/staticbindinggenerator.jar differ diff --git a/build/project-template-gradle/build.gradle b/build/project-template-gradle/build.gradle index 47af10903..02e06cf60 100644 --- a/build/project-template-gradle/build.gradle +++ b/build/project-template-gradle/build.gradle @@ -14,8 +14,6 @@ * -PbuildToolsVersion=[build_tools_version] (default is 22.0.1) * -PsupportVersion=[support_version] (default (22.2.0) * -PcompileSdk=[compile_sdk_version] (default 22) -* -* -PrunSBGenerator //this flag will triger static binding generation */ buildscript { @@ -40,8 +38,6 @@ def nodeModulesDir = "../../node_modules/" def libDir = "$projectDir/../../lib/Android/" def pluginNames = new ArrayList() def configDir = file(configurationsDir) -def appResExists = false -def appResourcesName = "NativescriptAppResources" def compiteCompileSdkVersion () { if(project.hasProperty("compileSdk")) { @@ -136,7 +132,12 @@ android { def variantName = variant.name.capitalize() def compileSourcesTaskName = "compile${variantName}Sources" def compileSourcesTask = project.tasks.findByName(compileSourcesTaskName) - compileSourcesTask.finalizedBy "buildMetadata" + + def generateBuildConfigTask = variant.generateBuildConfig; + generateBuildConfigTask.finalizedBy(collectAllJars) + collectAllJars.finalizedBy(setProperties) + + compileSourcesTask.finalizedBy(buildMetadata) } } @@ -365,7 +366,25 @@ task collectAllJars { for(def i = 0; i < allJarPaths.size(); i++) { metadataParams.add(allJarPaths.get(i)); } + } +} + + +task buildMetadata (type: JavaExec) { + description "builds metadata with provided jar dependencies" + + inputs.files(allJarPaths) + inputs.dir("build/intermediates/classes") + + outputs.files("metadata/output/assets/metadata/treeNodeStream.dat", "metadata/output/assets/metadata/treeStringsStream.dat", "metadata/output/assets/metadata/treeValueStream.dat") + + doFirst { + workingDir "build-tools" + main "-jar" + + // get compiled classes to pass to metadata generator + // these need to be called after the classes have compiled def classesDir = "$buildDir/intermediates/classes" def classesSubDirs = new File(classesDir).listFiles() @@ -461,14 +480,20 @@ task deleteExplodedAarFolder (type: Delete) { //////////////////////////////////////////////////////////////////////////////////// ////////////////////////////// OPTIONAL TASKS ////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// -task runBindingGenerator(dependsOn: "ing:jar") { - rootProject.jarsDir = allJarPaths -} -runBindingGenerator.dependsOn("bg:jar") - -task rbg (dependsOn: "asbg:generatebindings") - +task setProperties { + project.ext.jarFiles = [] + doLast { + def list = []; + allJarPaths.each({f -> + if(f.endsWith(".jar")) { + list.add(f); + } + }) + project.jarFiles = list; + } +} +setProperties.finalizedBy("asbg:generateBindings") //////////////////////////////////////////////////////////////////////////////////// ////////////////////////////// EXECUTION ORDER ///////////////////////////////////// diff --git a/build/project-template-gradle/gradle.properties b/build/project-template-gradle/gradle.properties index 839129533..c468e4613 100644 --- a/build/project-template-gradle/gradle.properties +++ b/build/project-template-gradle/gradle.properties @@ -1,3 +1,2 @@ -jsCodeDir=../../../src/main/assets/app -outDir=../../src/main/java -jarsDir="" \ No newline at end of file +jsCodeDir=src/main/assets/app +outDir=src/main/java \ No newline at end of file diff --git a/build/project-template-gradle/settings.gradle b/build/project-template-gradle/settings.gradle index 0a8b8b6ba..adb2c7142 100644 --- a/build/project-template-gradle/settings.gradle +++ b/build/project-template-gradle/settings.gradle @@ -1,5 +1,3 @@ rootProject.name = "__PROJECT_NAME__" -include "asbg", "ing", "bg" -project(":asbg").projectDir = file("build-tools/android-static-binding-generator") -project(":ing").projectDir = file("build-tools/android-static-binding-generator/interface-name-generator") -project(":bg").projectDir = file("build-tools/android-static-binding-generator/binding-generator") \ No newline at end of file +include "asbg" +project(":asbg").projectDir = file("build-tools/android-static-binding-generator") \ No newline at end of file