'Antrl4 Listener callbacks populating Java list of classes

I am using Antlr4 v4.9.2 in Java mode. I am converting adaptation ascii text files to XML format.

The parser is working great and do its job. Each element of the adpatation has a start, middle (element attributes) and an end, and each of these 3 parts has a overridden listener callback. enterElement, elementAtrributes and exitElement.

The main program declares a public List of element classes:

public static elements element_list = new elements(); 

and the elements class is thus:

package main.java.adaptation;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

import main.java.adaptation.element;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBContextFactory;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;  
import javax.xml.bind.annotation.XmlElement;  
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;  
import javax.xml.bind.annotation.adapters.XmlAdapter;

@XmlRootElement(name = "elements")
@XmlAccessorType (XmlAccessType.FIELD)
public class elements
{
    @XmlElement(name = "id")
    private List<element> elements = new ArrayList<element>();
     
    public List<element> getElements() {
      return elements;
    }
      
    public void setElements(List<elelent> elements) {
      this.elements = elements;
    }
    
    public void addElements ( element e ) {
        this.elements.add(e);
    }
}

and each element has a simple class definition

@XmlRootElement(name = "element")
@XmlAccessorType (XmlAccessType.FIELD)
public class element implements Serializable {
    private static String           Id;
    private static String           MagneticDeclination;
    private static String           Latitude;
}

The listener override class definition is:

public class AdaptListener extends AdaptParserBaseListener {
    
    /** Element class */
    private element tmpElement;

    /**
     */
    public AdaptListener() {
        this.tmpElement = null;
    }
    
    /**
     * {@inheritDoc}
     *
     * <p>The default implementation does nothing.</p>
     */
    @Override public void enterElement(AdaptParser.ElementContext ctx) {
        Token t = ctx.getStart();
        int lineno = t.getLine();
        
        tmpElement = new element();
        
        /** Code block */
    }
    /**
     * {@inheritDoc}
     *
     * <p>The default implementation does nothing.</p>
     */
    @Override public void exitElement(AdaptParser.ElementContext ctx) {
        if ( tmpElement != null ) {
            AdaptationAnalyser.logger.debug ("Exit element : " + tmpElement);
            AdaptationAnalyser.element_list.addFix( tmpElement );
        }
    }

    
    /**
     * {@inheritDoc}
     *
     * <p>The real implementation element attributes.</p>
     */
    @Override public void enterElement_Attributes(AdaptParser.Element_syntaxContext ctx) {
            tmpElement.setLatitude(ctx.ID(0).toString());
            tmpElement.setLatitude(ctx.LAT_LONG(0).toString());
            .......
        }       
    }

Okay thats the background, now the problem:

tmpElement is correctly populated every time and the first list add element, reflects the first tmpElement added. Each subsequent addition to the list sets every element in the list to the same values as the last tmpElement. After correctly finding thousands of individual elements, each element of the final list is set to the last element identified! Why.



Solution 1:[1]

Answer provided my l4mpi. The class attributes that I defined had static fields. These should have been non-static. In other words defining the class with static attributes stopped me from changing or allocating new values.

Solution 2:[2]

(Apologies, I did a cursory read of your question and misread the problem)

I’m pretty sure the problem you’re having is created by nested elements. You’ll enter(and exit) any child, grandchild, etc. before you exit the root element.

Something like this:

Enter Root
  Enter Child
    Enter Grandchild
    Exit Grandchild
  Exit Child
  Enter Second Child
  Exit Second Child
Exit Root

There are two ways to address this:

1 - in your particular case, you only need additional data from the attributes, and you’ll encounter them before any children. With that in mind, add you tmpElement to you list at the end of the enterElement_Attributes1st method. (You won’t have anything to do when you exitElement`)

2 - The more generalized way of handling nested nodes is to use a Stack.

  • on enter you create your object and push it onto the stack
    • This is probably the better time to add it to your list. It will keep your list in the order that you encountered elements. If you add them to the list as you exit, then the top level element will be the last item in your list, since it’ll be the last item you exit.
  • during processing, use the item at the top of the stack
  • on exit you finish your processing, and pop the item from the stack.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2