'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 |