package srcmlolol;

import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.ParseTreeListener;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.apache.commons.lang3.StringEscapeUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

public class XMLListener implements ParseTreeListener {
    private Map<Integer, String> token2name;
    private Map<String, Integer> name2token;
    private DocumentBuilderFactory factory;
    private DocumentBuilder builder;
    private Stack<Element> stack;
    public Document doc;

    public XMLListener(String filename, Map<String, Integer> tokenMap) throws ParserConfigurationException{
        stack = new Stack<Element>();

        token2name = new HashMap<Integer, String>();
        name2token = tokenMap;
        for (String key : name2token.keySet()){
            token2name.put(name2token.get(key), key);
        }

        factory = DocumentBuilderFactory.newInstance();
        builder = factory.newDocumentBuilder();
        doc = builder.newDocument();

        Element unit = doc.createElement("unit");
        unit.setAttribute("filename", filename);

        doc.appendChild(unit);
        stack.add(unit);
    }

    @Override
    public void enterEveryRule(ParserRuleContext ctx) {
        String name = ctx.getClass().getSimpleName().replace("Context", "");
        Element rule = doc.createElement(name);
        rule.setAttribute("start_line", Integer.toString(ctx.start.getLine()));
        rule.setAttribute("end_line", Integer.toString(ctx.stop.getLine()));
        rule.setAttribute("start_line_char", Integer.toString(ctx.start.getCharPositionInLine()));
        rule.setAttribute("end_line_char", Integer.toString(
                (ctx.stop.getCharPositionInLine() + ctx.stop.getText().length())));
        rule.setAttribute("start_char", Integer.toString(ctx.start.getStartIndex()));
        rule.setAttribute("end_char", Integer.toString(ctx.stop.getStopIndex()));

        // attaching to last elem on stack
        stack.peek().appendChild(rule);
        stack.add(rule);
    }

    @Override
    public void exitEveryRule(ParserRuleContext ctx) {
        // oh no!!
        stack.pop();
    }

    @Override
    public void visitErrorNode(ErrorNode node) {
    }

    @Override
    public void visitTerminal(TerminalNode node) {
        String text = node.getText();
        if (text.equals("<EOF>")) {
            return;
        }
        Token token = node.getSymbol();

        Element tokenelem = doc.createElement(token.getClass().getSimpleName());
        int type = token.getType();
        tokenelem.setAttribute("type", Integer.toString(type));
        tokenelem.setAttribute("name", token2name.get(type));
        tokenelem.appendChild(doc.createTextNode(StringEscapeUtils.escapeXml10(text)));
        tokenelem.setAttribute("start_line", Integer.toString(token.getLine()));
        tokenelem.setAttribute("end_line", Integer.toString(token.getLine()));
        tokenelem.setAttribute("start_line_char", Integer.toString(token.getCharPositionInLine()));
        tokenelem.setAttribute("end_line_char", Integer.toString(
                (token.getCharPositionInLine() + token.getText().length())));
        tokenelem.setAttribute("start_char", Integer.toString(token.getStartIndex()));
        tokenelem.setAttribute("end_char", Integer.toString(token.getStopIndex()));
        stack.peek().appendChild(tokenelem);
    }
}

@@ -1801,32 +1801,39 @@
 tAttribute(%22
-end_line
+start_line_char
 %22, Integer.t
@@ -1846,22 +1846,37 @@
 g(ctx.st
-op
+art
 .get
+CharPositionIn
 Line()))
@@ -1902,37 +1902,32 @@
 ttribute(%22start_
-line_
 char%22, Integer.t
@@ -1943,38 +1943,212 @@
 tx.start.get
-CharPositionIn
+StartIndex()));%0A%0A        // occasionally stop isn't set. prefer start token instead.%0A        if (ctx.stop == null)%7B%0A            rule.setAttribute(%22end_line%22, Integer.toString(ctx.start.get
 Line()));%0A  
@@ -2137,32 +2137,36 @@
 rt.getLine()));%0A
+    
         rule.set
@@ -2226,25 +2226,30 @@
         
+    
 (ctx.st
-op
+art
 .getChar
@@ -2269,26 +2269,27 @@
 e() + ctx.st
-op
+art
 .getText().l
@@ -2300,32 +2300,36 @@
 h())));%0A        
+ 
+   
 rule.setAttribut
@@ -2323,37 +2323,35 @@
 e.setAttribute(%22
-start
+end
 _char%22, Integer.
@@ -2370,38 +2370,299 @@
 .start.getSt
-artIndex()));%0A
+opIndex()));%0A        %7D%0A        else%7B%0A            rule.setAttribute(%22end_line%22, Integer.toString(ctx.stop.getLine()));%0A            rule.setAttribute(%22end_line_char%22, Integer.toString(%0A                    (ctx.stop.getCharPositionInLine() + ctx.stop.getText().length())));%0A    
         rule
@@ -2722,32 +2722,42 @@
 etStopIndex()));
+%0A        %7D
 %0A%0A        // att

Fix NPE when no stop token exist in the context