abapmerge
Version:
Merge ABAP INCLUDEs into single file
195 lines • 9.13 kB
JavaScript
;
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