package com.siberika.idea.pascal.lang.compiled;
import com.intellij.openapi.diagnostic.Logger;
import com.siberika.idea.pascal.PascalBundle;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* Author: George Bakhtadze
* Date: 20/11/2013
*/
public class PPUDumpParser {
private static final Logger LOG = Logger.getInstance(PPUDumpParser.class);
public static final String UNRESOLVED_INTERNAL = "__INTERNAL__";
public static final String INDENT = " ";
private static final String LF = "\n@";
public static Section parse(InputStream inputStream, PPUDecompilerCache cache) throws ParseException, ParserConfigurationException, SAXException, IOException {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
XMLHandler handler = new XMLHandler(cache);
parser.parse(inputStream, handler);
return handler.result;
}
public static Section parse(@NotNull String xml, PPUDecompilerCache cache) throws ParseException, ParserConfigurationException, SAXException, IOException {
return parse(new ByteArrayInputStream(xml.getBytes("utf-8")), cache);
}
private static class XMLHandler extends DefaultHandler {
public static final Set<String> TYPES = new HashSet<String>(Arrays.asList("/ord", "/ptr", "/string", "/float", "/type", "/file", "/variant", "/set"));
public static final Set<String> DIRECTIVES = new HashSet<String>(Arrays.asList(
"overload", "virtual", "dynamic", "static", "override",
"abstract", "final",
"inline", "assembler,",
"cdecl", "pascal", "register", "safecall", "stdcall", "export"));
public static final Set<String> DATA = new HashSet<String>(Arrays.asList("ptr/ptr", "eltype", "rangetype", "vartype", "rettype"));
public static final Set<String> COMMENTS = new HashSet<String>(Arrays.asList("version", "targetcpu", "targetos", "crc", "interfacecrc"));
public static final Set<String> IGNORED = new HashSet<String>(Arrays.asList("1"));
public static final Map<String, String> NAME_SUB = new HashMap<String, String>();
public static final Map<String, Section> SECTIONS = new LinkedHashMap<String, Section>();
static {
addSection("/unit/uses/unit", LF, "", "", ",", 0);
addSection("/unit/files/file", "", "", "", ", ", 0);
addSection("/enum", "", null, "", "", 0);
addSection("/elements", "", "", "", "", 2);
addSection("/elements/const", "", " = ", "", ", ", 0);
addSection("/unit", "unit ", ";", "\ninterface\n", "implementation\n" + PascalBundle.message("decompiled.unit.footer") + "\nend.", 0);
addSection("/units", "", "", "", "", 0).ignore = true;
addSection("/uses", LF + "uses", "", "", ";\n", 1);
addSection("/files", "\n{" + PascalBundle.message("decompiled.unit.files") + " ", "", "", "}\n", 2);
addSection("/interface", "", "", "", "", 0);
// addSection("/fields/rec", "record\n", "", "", "end", 0);
// addSection("/fields/proctype", "procedure", "", "", "", 0);
addSection("/rec", "", null, "", "", 0);
addSection("/fields", "", "", "", "", 0);
addSection("/field", LF, ": ", "", ";", 0);
addSection("/options", "", "", "", "", 0).ignore = true;
addSection("/undefined", "", "", "", "", 0).ignore = true;
addSection("/formal", "", "", "", "", 0).ignore = true;
addSection("/proc", "", "", "", ";\n", 0);
addSection("/proctype", "", null, "", "", 0);
addSection("prop/params", "[", "", "", "]", 2);
addSection("/params", "(", "", "", ")", 2);
addSection("/param", "", "", "", "; ", 0);
addSection("/const", LF + "const ", " = ", "", ";\n", 0);
addSection("/var", LF + "var ", ": ", "", ";\n", 0);
addSection("/array", "", null, "", "", 0);
addSection("/obj", "", null, "", LF + "end;\n", 0);
addSection("/classref", "", null, "", "", 0);
addSection("/prop", LF + "property ", "", "", ";\n", 0);
for (String name : TYPES) {
addSection(name, "", null, "", "", 0);
}
NAME_SUB.put("$assign", ":= ");
NAME_SUB.put("$or", "or ");
NAME_SUB.put("$and", "and ");
NAME_SUB.put("$xor", "xor ");
NAME_SUB.put("$not", "not ");
NAME_SUB.put("$shl", "shl ");
NAME_SUB.put("$shr", "shr ");
NAME_SUB.put("$plus", "+ ");
NAME_SUB.put("$minus", "- ");
NAME_SUB.put("$star", "* ");
NAME_SUB.put("$slash", "/ ");
NAME_SUB.put("$starstar", "** ");
NAME_SUB.put("$div", "div ");
NAME_SUB.put("$mod", "mod ");
NAME_SUB.put("$equal", "= ");
NAME_SUB.put("$lower", "< ");
NAME_SUB.put("$greater", "> ");
NAME_SUB.put("$greater_or_equal", ">= ");
NAME_SUB.put("$lower_or_equal", "<= ");
NAME_SUB.put("True", "__True");
NAME_SUB.put("False", "__False");
}
private final PPUDecompilerCache cache;
private XMLHandler(PPUDecompilerCache cache) {
this.cache = cache;
}
private static Section addSection(String id, String textBegin, String afterName, String beforeSubsec, String textEnd, int removeChars) {
Section res = new Section(id, textBegin, afterName, beforeSubsec, textEnd, removeChars);
SECTIONS.put(id, res);
return res;
}
public final Map<String, String> idNameMap = new HashMap<String, String>();
public final Map<String, String> symidNameMap = new HashMap<String, String>();
public final List<String> units = new ArrayList<String>();
private Deque<Section> stack = new ArrayDeque<Section>();
private StringBuilder chars = new StringBuilder();
private Section result = null;
private String path = "";
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
chars.append(ch, start, length);
}
private Section getSection() {
if (isIgnored(path)) {
return null;
}
for (Map.Entry<String, Section> entry : SECTIONS.entrySet()) {
if (path.endsWith(entry.getKey())) {
return entry.getValue();
}
}
return null;
}
private boolean isIgnored(String path) {
for (String str : IGNORED) {
if (str.startsWith("/")) {
if (str.equalsIgnoreCase(path)) {
return true;
}
} else {
if (path.endsWith(str)) {
return true;
}
}
}
return IGNORED.contains(path);
}
private boolean sectionBegin(Section sec) {
if (null == sec) {
return false;
}
sec = sec.copy();
if (!stack.isEmpty()) {
stack.getFirst().appendBeforeSubsec();
}
sec.reset();
sec.setIndent(stack.size());
stack.push(sec);
sec.appendBegin();
return true;
}
private void sectionEnd(Section sec) {
if (null == sec) {
return;
}
assert sec.type.equals(stack.getFirst().type);
sec = stack.pop();
sec.doRemoveChars();
handleSectionEnd(sec);
sec.sb.append(sec.textEnd);
if (!sec.ignore) {
if (!sec.isAnonimous()) {
if (stack.isEmpty()) {
result = sec;
fixupUndefined(sec);
result.idNameMap = idNameMap;
result.symidNameMap = symidNameMap;
}
} else {
if (!StringUtils.isBlank(sec.getDataStr("id"))) {
idNameMap.put(sec.getDataStr("id"), sec.sb.toString());
}
if (!StringUtils.isBlank(sec.getDataStr("symid"))) {
symidNameMap.put(sec.getDataStr("symid"), sec.sb.toString());
}
}
if (!stack.isEmpty()) {
stack.getFirst().merge(sec);
}
} else if (LOG.isDebugEnabled()) {
LOG.debug("Section ignored: " + sec);
}
}
private void handleSectionEnd(Section sec) {
if (TYPES.contains(sec.type)) {
insertTypeDeclName(sec);
if ("/ord".equalsIgnoreCase(sec.type)) {
if (sec.getDataStr("ordtype").endsWith("ool")) {
sec.sb.append("false..true");
} else if (sec.getDataStr("ordtype").endsWith("int")) {
sec.sb.append(sec.getDataStr("low")).append("..").append(sec.getDataStr("high"));
} else {
sec.sb.append(sec.name);
}
} else if ("/ptr".equalsIgnoreCase(sec.type)) {
sec.sb.append("^");
appendReference(sec, sec.sb.length(), "ptr", "", "", UNRESOLVED_INTERNAL);
} else if ("/set".equalsIgnoreCase(sec.type)) {
sec.sb.append("set");
appendReference(sec, sec.sb.length(), "eltype", " of ", "", UNRESOLVED_INTERNAL);
} else {
sec.sb.append(sec.name);
}
appendLineEnd(sec);
} else if ("/array".equalsIgnoreCase(sec.type)) {
insertTypeDeclName(sec);
sec.sb.append(" array");
if (!hasOption(sec, "dynamic") && allDataPresent(sec, "low", "high") && !"-1".equals(sec.getDataStr("high"))) {
sec.sb.append("[").append(sec.getDataStr("low")).append("..").append(sec.getDataStr("high")).append("]");
}
sec.sb.append(" of ");
appendReference(sec, sec.sb.length(), "eltype", "", "", UNRESOLVED_INTERNAL);
appendLineEnd(sec);
} else if ("/options".equalsIgnoreCase(sec.type)) {
if (!stack.isEmpty()) {
stack.getFirst().data.putAll(sec.data);
}
} else if ("/param".equalsIgnoreCase(sec.type)) {
sec.insertText(0, sec.getDataStr("spez") + " ");
appendReference(sec, sec.sb.length(), "vartype", ": ", "", UNRESOLVED_INTERNAL);
} else if ("/field".equalsIgnoreCase(sec.type)) {
sec.insertVisibility(LF.length(), getDefaultVisibility());
appendReference(sec, sec.sb.length(), "vartype", "", "", UNRESOLVED_INTERNAL);
} else if ("/var".equalsIgnoreCase(sec.type)) {
appendReference(sec, sec.sb.length(), "vartype", "", "", UNRESOLVED_INTERNAL);
} else if ("/proc".equalsIgnoreCase(sec.type)) {
String comment = "";
sec.insertText(0, LF);
if (hasOption(sec, "procedure")) {
sec.insertText(LF.length(), "procedure ");
} else if (hasOption(sec, "constructor")) {
sec.insertText(LF.length(), "constructor ");
} else if (hasOption(sec, "destructor")) {
sec.insertText(LF.length(), "destructor ");
} else if (hasOption(sec, "operator")) {
sec.insertText(LF.length(), "function __");
sec.sb.append(": ");
appendReference(sec, sec.sb.length(), "rettype", "", "", UNRESOLVED_INTERNAL);
comment = "; // operator " + NAME_SUB.get("$" + sec.name);
} else if (hasOption(sec, "function")) {
sec.insertText(LF.length(), "function ");
sec.sb.append(": ");
appendReference(sec, sec.sb.length(), "rettype", "", "", UNRESOLVED_INTERNAL);
}
if (hasOption(sec, "classmethod")) {
sec.insertText(LF.length(), "class ");
}
for (String directive : DIRECTIVES) {
if (hasOption(sec, directive)) {
sec.sb.append("; ").append(directive);
}
}
sec.sb.append(comment);
sec.insertVisibility(LF.length(), getDefaultVisibility());
} else if ("/obj".equalsIgnoreCase(sec.type)) {
StringBuilder psb = new StringBuilder(LF + "type ");
psb.append(sec.name).append(" = ").append(sec.getDataStr("objtype"));
int pos = psb.length();
if (!StringUtils.isBlank(sec.getDataStr("iid"))) {
psb.append(LF).append(INDENT).append("['").append(sec.getDataStr("iid")).append("']");
}
sec.insertText(0, psb.toString());
appendReference(sec, pos, "ancestor", "(", ")", UNRESOLVED_INTERNAL);
} else if ("/rec".equalsIgnoreCase(sec.type)) {
StringBuilder psb = new StringBuilder("");
if (!StringUtils.isBlank(sec.name)) {
psb.append(LF).append("type ").append(sec.name).append(" = record");
} else {
psb.append("record ");
}
sec.insertText(0, psb.toString());
sec.sb.append(LF + "end");
appendLineEnd(sec);
} else if ("/classref".equalsIgnoreCase(sec.type)) {
insertTypeDeclName(sec);
sec.sb.append("class of ");
appendReference(sec, sec.sb.length(), "ref", "", "", UNRESOLVED_INTERNAL);
appendLineEnd(sec);
} else if ("/enum".equalsIgnoreCase(sec.type)) {
int pos = 0;
if (sec.name != null) {
pos = sec.insertText(0, LF + "type " + sec.name + " = ");
}
sec.insertText(pos, "(");
sec.sb.append(")");
appendLineEnd(sec);
} else if ("/proctype".equalsIgnoreCase(sec.type)) {
if (StringUtils.isBlank(sec.getDataStr("rettype/id"))) {
sec.insertText(0, "procedure ");
} else {
sec.insertText(0, "function ");
sec.sb.append(": ");
appendReference(sec, sec.sb.length(), "rettype", "", "", UNRESOLVED_INTERNAL);
}
if (sec.name != null) {
sec.insertText(0, LF + "type " + sec.name + " = ");
}
if ("1".equals(sec.getDataStr("methodptr"))) {
sec.sb.append(" of object");
}
appendLineEnd(sec);
} else if ("/prop".equalsIgnoreCase(sec.type)) {
sec.sb.append(": ");
appendReference(sec, sec.sb.length(), "proptype", "", "", UNRESOLVED_INTERNAL);
appendIfAllNotBlank(sec.sb, " read ", retrieveReference(null, sec.data.get("getter/id"), sec.data.get("getter/symid"), ""));
appendIfAllNotBlank(sec.sb, " write ", retrieveReference(null, sec.data.get("setter/id"), sec.data.get("setter/symid"), ""));
sec.insertVisibility(LF.length(), getDefaultVisibility());
}
}
private String getDefaultVisibility() {
Iterator<Section> it = stack.iterator();
it.next();
if (it.hasNext()) {
Section sec = it.next();
if (sec.isStructuredType()) {
return "public ";
}
}
return "";
}
private void appendLineEnd(Section sec) {
if (!StringUtils.isBlank(sec.name)) {
sec.sb.append(";\n");
}
}
private void insertTypeDeclName(Section sec) {
if (!StringUtils.isBlank(sec.name)) {
sec.sb.append(LF).append("type ").append(sec.name).append(" = ");
}
}
private void appendIfAllNotBlank(StringBuilder sb, String...args) {
for (String arg : args) {
if (StringUtils.isBlank(arg)) {
return;
}
}
for (String arg : args) {
sb.append(arg);
}
}
private boolean hasOption(Section sec, String option) {
return sec.data.containsKey("/options/" + option);
}
private boolean allDataPresent(Section sec, String...keys) {
for (String key : keys) {
if (null == sec.getDataStr(key)) {
return false;
}
}
return true;
}
/**
* prefix/postfix and default are mutually exclusive
*/
private void appendReference(Section sec, int pos, String refName, String prefix, String postfix, String def) {
Object unit = sec.data.get(refName + "/unit");
if (unit != null) {
pos = sec.insertText(pos, prefix);
pos = resolveUsed(sec, pos, sec.data.get(refName + "/id"), sec.data.get(refName + "/symid"), Integer.parseInt((String) unit));
pos = sec.insertText(pos, postfix);
} else {
appendLocalReference(sec, pos, sec.data.get(refName + "/id"), sec.data.get(refName + "/symid"), prefix, postfix, def,
idNameMap, symidNameMap);
}
}
@SuppressWarnings("UnusedAssignment")
private int appendLocalReference(Section sec, int pos, Object id, Object symid, String prefix, String postfix, String def,
Map<String, String> idNameMap, Map<String, String> symidNameMap) {
Map<String, String> nameMap = idNameMap;
if (null == id) {
id = symid;
nameMap = symidNameMap;
}
if (id != null) {
@SuppressWarnings("SuspiciousMethodCalls")
String ref = nameMap != null ? nameMap.get(id) : null;
if (StringUtils.isBlank(ref)) {
pos = sec.insertText(pos, prefix + postfix);
if (idNameMap != null) {
if (sec.undefined != null) {
sec.undefined.put(pos - postfix.length(), (nameMap == idNameMap ? "i" : "s") + id);
}
} else {
pos = sec.insertText(pos, prefix + def + postfix);
}
} else if (ref.startsWith("$")) {
pos = sec.insertText(pos, def);
} else {
pos = sec.insertText(pos, prefix + ref + postfix);
}
}
return pos;
}
@SuppressWarnings("UnusedAssignment")
private int resolveUsed(Section sec, int pos, Object id, Object symid, int unitIndex) {
String unitName = getUnit(unitIndex);
pos = sec.insertText(pos, unitName + ".");
String def = "__unresolved_" + id;
Section section = cache != null ? cache.getContents(unitName, null) : null;
if (section != null) {
return appendLocalReference(sec, pos, id, symid, "", "", def, section.idNameMap, section.symidNameMap);
} else {
return sec.insertText(pos, def);
}
}
private String getUnit(int unitIndex) {
if ((unitIndex >= 0) && (unitIndex < units.size())) {
return units.get(unitIndex);
}
return "";
}
private String retrieveReference(Object unit, Object id, Object symid, String defalut) {
String res = "";
if (unit != null) {
int ind = Integer.parseInt((String) unit);
res = getUnit(ind) + ".";
}
Map<String, String> nameMap = idNameMap;
if (null == id) {
id = symid;
nameMap = symidNameMap;
}
if (id != null) {
if (unit != null) {
res = res + "__" + id;
} else {
@SuppressWarnings("SuspiciousMethodCalls")
String ref = nameMap.get(id);
if (StringUtils.isBlank(ref) || (ref.startsWith("$"))) {
res = defalut;
} else {
res = res + ref;
}
}
} else {
LOG.info("ERROR: retrieveReference: id is null");
}
return res;
}
private boolean handleDataTag(String qname, boolean start) {
if (stack.isEmpty()) { return false; }
Section sec = stack.getFirst();
if (sec.dataPrefix != null) {
if (start) {
sec.appendDataPrefix(qname);
} else {
sec.removeDataPrefix(qname);
}
return true;
}
for (String dpath : DATA) {
if (path.endsWith(dpath)) {
if (start) {
sec.dataPrefix = dpath.substring(dpath.indexOf("/") + 1) + "/";
} else {
sec.dataPrefix = null;
}
return true;
}
}
return false;
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
path = path + "/" + qName;
chars = new StringBuilder();
if (!handleDataTag(qName, true)) {
if (!sectionBegin(getSection()) && !stack.isEmpty()) {
stack.getFirst().appendDataPrefix(qName);
}
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (!handleDataTag(qName, false)) {
sectionEnd(getSection());
}
String txt = chars.toString().trim();
if ((txt.length() > 0) && (!isIgnored(path)) && !stack.isEmpty()) {
Section sec = stack.getFirst();
if (COMMENTS.contains(qName)) {
sec.sb.append(" {").append(qName).append(":").append(txt).append("}");
} else if ("name".equalsIgnoreCase(qName)) {
if (NAME_SUB.get(txt) != null) {
if (txt.startsWith("$")) {
sec.name = txt.substring(1);
} else {
sec.name = NAME_SUB.get(txt);
}
} else {
sec.name = txt;
}
if (!path.endsWith(sec.type + "/name")) {
LOG.info("ERROR: ! name for section: " + sec.type + ", path: " + path);
}
if (sec.name.startsWith("$")) {
sec.ignore = true;
}
if (sec.textAfterName != null) {
sec.sb.append(sec.name);
sec.sb.append(sec.textAfterName);
}
} else if ("id".equalsIgnoreCase(qName) && (sec.name != null)) {
if (null == sec.dataPrefix) {
idNameMap.put(txt.toString(), sec.name);
}
} else if ("symid".equalsIgnoreCase(qName) && (sec.name != null)) {
if (null == sec.dataPrefix) {
symidNameMap.put(txt.toString(), sec.name);
}
} else if ("value".equalsIgnoreCase(qName)) {
if ("/units".equalsIgnoreCase(sec.type)) {
units.add(txt);
} else if ("/options".equalsIgnoreCase(sec.type)) {
sec.putData(sec.type + "/" + txt, "");
}
if (null == sec.dataPrefix) {
sec.sb.append(txt);
}
}
if (txt.length() > 0) {
sec.putData(qName, txt);
}
}
chars = new StringBuilder();
path = path.substring(0, path.lastIndexOf("/"));
}
public void fixupUndefined(Section sec) {
for (Integer pos : sec.undefined.keySet()) {
String key = sec.undefined.get(pos);
Map<String, String> nameMap = idNameMap;
if ('s' == key.charAt(0)) {
nameMap = symidNameMap;
}
String res = nameMap.get(key.substring(1));
res = res != null ? res : "__unresolved__";
result.insertText(pos, res);
}
result.insertText(0, PascalBundle.message("decompiled.unit.header"));
}
}
static class Section {
final String type;
final String textBegin;
final String textAfterName; // if null name is not printed
final String beforeSubsec;
final String textEnd;
final int removeChars;
Map<String, Object> data = new TreeMap<String, Object>();
StringBuilder sb = new StringBuilder();
public Map<String, String> idNameMap;
public Map<String, String> symidNameMap;
Map<Integer, String> undefined = newUndef();
private String indent = "";
private static Map<Integer, String> newUndef() {
return new TreeMap<Integer, String>(new Comparator<Integer>() {
public int compare(Integer o1, Integer o2) {
return o2-o1; // reverse order
}
});
}
String name;
private boolean ignore = false;
private String dataPrefix = null;
private boolean beforeSubsecAdded = false;
public Section(String error) {
this(null, null, null, null, null, 0);
sb = new StringBuilder(error);
}
private Section(String type, String textBegin, String textAfterName, String beforeSubsec, String textEnd, int removeChars) {
this.type = type;
this.textBegin = textBegin;
this.textAfterName = textAfterName;
this.beforeSubsec = beforeSubsec;
this.textEnd = textEnd;
this.removeChars = removeChars;
}
public boolean isError() {
return (null == type) && (null == textBegin) && (null == textAfterName) && (null == beforeSubsec) && (null == textEnd);
}
void reset() {
data = new TreeMap<String, Object>();
sb = new StringBuilder();
}
public void appendBeforeSubsec() {
if (beforeSubsecAdded) { return; }
sb.append(beforeSubsec);
beforeSubsecAdded = true;
}
public void doRemoveChars() {
sb.delete(sb.length() - removeChars, sb.length());
}
Section copy() {
Section res = new Section(type, textBegin, textAfterName, beforeSubsec, textEnd, removeChars);
res.ignore = ignore;
res.dataPrefix = dataPrefix;
return res;
}
public void appendBegin() {
sb.append(textBegin);
for (int i = 0; i < removeChars; i++) {
sb.append(" ");
}
}
public void putData(String name, Object obj) {
data.put((dataPrefix != null ? dataPrefix : "") + name, obj);
}
public String getDataStr(String name) {
Object res = data.get(name);
return res != null ? (String) res : "";
}
public void appendDataPrefix(String qName) {
if (dataPrefix != null) {
dataPrefix = dataPrefix + qName + "/";
} else {
dataPrefix = qName + "/";
}
}
public void removeDataPrefix(String qname) {
assert (dataPrefix.endsWith(qname + "/"));
dataPrefix = dataPrefix.substring(0, dataPrefix.length() - (qname.length() + 1));
if (dataPrefix.length() == 0) {
dataPrefix = null;
}
}
@Override
public String toString() {
return "Section{" +
"type='" + type + '\'' +
"name='" + name + '\'' +
", ignore=" + ignore +
", dataPrefix='" + dataPrefix + '\'' +
", sb=" + sb +
'}';
}
public void insertVisibility(int pos, String defaultVisibility) {
insertText(pos, StringUtils.isBlank(getDataStr("visibility")) ? defaultVisibility : getDataStr("visibility") + " ");
}
public void merge(Section sec) {
// add indents
int lastPos = 0;
int pos = sec.sb.indexOf(LF, lastPos);
while ((pos >= 0) && (pos <= sec.sb.length() - LF.length())) {
lastPos = sec.replaceText(pos, LF.length(), "\n" + sec.indent);
pos = sec.sb.indexOf(LF, lastPos);
}
// fix undefined references
for (Integer key : sec.undefined.keySet()) {
undefined.put(key + sb.length(), sec.undefined.get(key));
}
// merge buffers
if (!sec.isAnonimous()) {
sb.append(sec.sb);
}
}
private int replaceText(int pos, int len, String text) {
sb.delete(pos, pos + len);
sb.insert(pos, text);
Map<Integer, String> newUndef = newUndef();
for (Map.Entry<Integer, String> entry : undefined.entrySet()) {
newUndef.put(entry.getKey() < pos ? entry.getKey() : entry.getKey() + text.length() - len, entry.getValue());
}
undefined = newUndef;
return pos + text.length() - len;
}
public int insertText(int pos, String text) {
if (!StringUtils.isBlank(text)) {
return doInsertText(pos, text);
} else {
return pos;
}
}
private int doInsertText(int pos, String text) {
sb.insert(pos, text);
Map<Integer, String> newUndef = newUndef();
for (Map.Entry<Integer, String> entry : undefined.entrySet()) {
newUndef.put(entry.getKey() < pos ? entry.getKey() : entry.getKey() + text.length(), entry.getValue());
}
undefined = newUndef;
return pos + text.length();
}
public String getResult() {
return sb.toString();
}
public boolean isAnonimous() {
return StringUtils.isBlank(name) &&
(!StringUtils.isBlank(getDataStr("id")) || !StringUtils.isBlank(getDataStr("symid")));
}
public void setIndent(int size) {
StringBuilder isb = new StringBuilder();
for (int i = 0; i < size; i++) {
isb.append(INDENT);
}
indent = isb.toString();
}
public boolean isStructuredType() {
return "/obj".equals(type);
}
}
/* static class PPUDecompilerCache {
public Section getContents(String unitName) {
try {
return DumpParser.parse("<unit></unit>", this);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
private static class PascalBundle {
public static String message(String s) {
return s + "\n";
}
}
private static class Logger {
private static Logger log = new Logger();
public static Logger getInstance(Class<DumpParser> ppuDumpParserClass) {
return log;
}
public void warn(String s) {
System.out.println("W: " + s);
}
}*/
}