UNPKG

abapmerge

Version:

Merge ABAP INCLUDEs into single file

195 lines 9.13 kB
"use strict"; var __values = (this && this.__values) || function(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); }; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ClassParser = exports.AbapPublicClass = void 0; var class_1 = __importDefault(require("./class")); var utils_1 = __importDefault(require("./utils")); var AbapPublicClass = /** @class */ (function () { function AbapPublicClass() { } return AbapPublicClass; }()); exports.AbapPublicClass = AbapPublicClass; var ClassParser = /** @class */ (function () { function ClassParser() { } ClassParser.anonymizeTypeName = function (prefix15, type, name) { var typeSeed = utils_1.default.hashCodeOf(type); var typeAlias = utils_1.default.randomStringOfLength(typeSeed, 5); var nameSeed = utils_1.default.hashCodeOf(name); var nameAlias = utils_1.default.randomStringOfLength(nameSeed, 10); return typeAlias + prefix15 + nameAlias; }; ClassParser.renameLocalType = function (oldName, newName, parent, oldCode) { var e_1, _a; var occurrences = [ // interface declaration { context: "* renamed: " + parent + " :: " + oldName + "\n", regstr: "^(\s*interface\\s+)" + oldName + "(\\s*\\.)", }, // class declaration { context: "* renamed: " + parent + " :: " + oldName + "\n", regstr: "^(\s*class\\s+)" + oldName + "(\\s+definition\\b)", }, { context: "", regstr: "^([^*\"\\n]*\\b)" + oldName + "(\\b)", }, ]; var newCode = oldCode; try { for (var occurrences_1 = __values(occurrences), occurrences_1_1 = occurrences_1.next(); !occurrences_1_1.done; occurrences_1_1 = occurrences_1.next()) { var occurrence = occurrences_1_1.value; var regexp = new RegExp(occurrence.regstr, "igm"); while (newCode.match(regexp)) { newCode = newCode.replace(regexp, occurrence.context + "$1" + newName + "$2"); } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (occurrences_1_1 && !occurrences_1_1.done && (_a = occurrences_1.return)) _a.call(occurrences_1); } finally { if (e_1) throw e_1.error; } } return newCode; }; ClassParser.buildLocalFileName = function (part, publicClass) { return publicClass.name + ".clas.locals_" + part + ".abap"; }; ClassParser.findFileByName = function (filename, list) { filename = filename.toLowerCase(); var length = list.length(); for (var i = 0; i < length; ++i) { var file = list.get(i); if (filename === file.getFilename().toLowerCase()) { return file; } } return null; }; ClassParser.parseLocalContents = function (part, publicClass, local) { var global = local + "\n" + publicClass[part]; var regex = /^\s*((CLASS)\s+(\w+)\s+DEFINITION\s*[^\.]*|(INTERFACE)\s+(\w+)\s*)\./gim; var definition; while ((definition = regex.exec(local)) !== null) { if (definition[1].toUpperCase().includes("DEFINITION LOCAL FRIENDS")) { throw "Cannot merge LOCAL FRIENDS"; } var type = definition[2]; var name_1 = definition[3]; if (!type) { // not class, dealing with interface type = definition[4]; name_1 = definition[5]; } var alias = ClassParser.anonymizeTypeName(publicClass.hash, type, name_1); global = ClassParser.renameLocalType(name_1, alias, publicClass.name, global); // prepend (definition)? deferred var decl = type + " " + alias; if (type.toLowerCase() === "class") { decl = decl + " DEFINITION"; } global = decl + " DEFERRED.\n" + global; // types of the local definitions are used in the (public|local) // implementation. so, after we must update the implementation after // we finish processing the local definitions. if (part === "def") { // this is the only difference between processing definitions and // implementations - which can be handled by one function otherwise publicClass.imp = ClassParser.renameLocalType(name_1, alias, publicClass.name, publicClass.imp); } } publicClass[part] = global; }; ClassParser.tryProcessLocalFile = function (part, publicClass, list) { var filename = ClassParser.buildLocalFileName(part, publicClass); var file = ClassParser.findFileByName(filename, list); if (file === null) { return; } var contents = file.getContents(); ClassParser.parseLocalContents(part, publicClass, contents); file.markUsed(); }; ClassParser.parse = function (f, list) { var match = f.getContents().match(/^(([\s\S])*ENDCLASS\.)\s*(CLASS(.|\s)*)$/i); if (!match || !match[1] || !match[2] || !match[3]) { throw "error parsing class: " + f.getFilename(); } var publicClass = new AbapPublicClass(); publicClass.name = f.getFilename().split(".")[0]; publicClass.hash = utils_1.default.randomStringOfLength(utils_1.default.hashCodeOf(publicClass.name), 15); publicClass.def = this.makeLocal(publicClass.name, match[1]); publicClass.imp = match[3]; // make sure we parse the imp part at the very first step! ClassParser.tryProcessLocalFile("imp", publicClass, list); // because the local definitions are used in the implementation. ClassParser.tryProcessLocalFile("def", publicClass, list); var superMatch = publicClass.def.match(/INHERITING FROM (Z\w+)/i); // console.dir(superMatch); var dependencies = []; if (superMatch && superMatch[1]) { dependencies.push(superMatch[1].toLowerCase()); } var testing = publicClass.def.match(/ FOR TESTING/i) !== null; return new class_1.default(publicClass.name, publicClass.def, testing, publicClass.imp, dependencies); }; ClassParser.makeLocal = function (name, s) { var reg1 = new RegExp("CLASS\\s+" + name + "\\s+DEFINITION\\s+(ABSTRACT\\s+)?PUBLIC", "i"); var addAbstract = s.match(/\s+ABSTRACT\s+?PUBLIC/i) !== null; var ret = s.replace(reg1, "CLASS " + name + " DEFINITION" + (addAbstract ? " ABSTRACT" : "")); var reg2 = new RegExp("GLOBAL\\s+FRIENDS\\s+ZCL_", "i"); ret = ret.replace(reg2, "FRIENDS ZCL_"); return ret; }; ClassParser.createTestFriendsRemover = function (testclasses) { var isNotTest = function (name) { return !testclasses.find(function (tc) { return tc.getName().toUpperCase() === name.toUpperCase(); }); }; return function (cls) { var reg = new RegExp("CLASS\\s+".concat(cls.getName(), "\\s+DEFINITION[^\\.]*((?:global)?friends\\s+([^\\.]+))\\."), "i"); var _a = __read((cls.getDefinition().match(reg) || []).slice(1), 2), frienddec = _a[0], friendstr = _a[1]; if (!friendstr) return cls; var oldfriends = friendstr.split(/\s+/).filter(function (f) { return f; }); var newfriends = oldfriends.filter(isNotTest); if (newfriends.length === oldfriends.length) return cls; var newfd = newfriends.length ? frienddec.replace(friendstr, newfriends.join(" ")) : ""; var newdef = cls.getDefinition().replace(frienddec, newfd); var impl = cls.getImplementation(); return new class_1.default(cls.getName(), newdef, cls.isForTesting(), impl && impl.toString(), cls.getDependencies()); }; }; return ClassParser; }()); exports.ClassParser = ClassParser; //# sourceMappingURL=class_parser.js.map