better/quicker way? java text adventure with stax

80 views Asked by At

I have a small bit of code that I have made. and I MAY be wrong, but I believe I did it in what is known as staX (tbh i'm not sure, I just learned it out of a random book I had around).It is perfectly functioning (or at least it was, but i changed my arraylists into arrays, and i also haven't thoroughly tested the gainitem and neededitem parts but they seemed to be working) but those are not my main focus either. I want to know if there is a better way (with staX) to refactor my code. I am also on the side attempting to make a program that uses classes to create objects with the code, so i only have to run through it a few times, and then everything is stored and I can use those, but for clarity, I do NOT want that for this answer. Also I noticed something else. when I run the program it starts at around 11MB. it slowly but surely gains. so I made it constantly run input to see what happens, and it will go up to about 500-550MB and stay around that. Does this mean I forgot to close something properly, or is that just expected and say like, the GC is just being slow? It definitely doesnt sound like thats the case, I feel much more like its my fault somewhere.

I have a main class that tests userInput, and keeps running until a termination number, but here are really the only things that may be important or useful from it that I know of.

currentGame.selectedOptionSetUp(userInputIntValue);
currentGame.getOptions();
currentGame.printOptions();

For the main part of my code. A lot of my if/else's do make the assumptions of an expected result where I am kinda running while's inside my if's until returns true, and I am aware of that, I wrote a comment beside one of my else statements of one.

Also in some parts of my code I believe there may be lingering close methods inside some nested statements, I believe simply nothing happens if they were called more than once, so I just kind of left them around as I was trying to figure out what was making the program able to take so much memory, am I correct in it being okay for the close methods to be called more than once?

The code:

 public class TheGame {

private int testAreaID;
private int areaID = -1;
private int sectorID= -1;
private String areaDescription = "";
private String sectorDescription = "";
private String[] possibleOptions = new String[10];
private String[] newAreaOptions = new String[5];
private String[] descriptorOptions = new String[5];
private String[] userGainedItems = new String[10];
private String neededItem = null;
private boolean optionRequiredItem= false;
private boolean descriptorReqItem = false;
private boolean areaReqItem = false;
private String gainedItemText = null;

private String gameFileName="";
private XMLInputFactory inputFactory = XMLInputFactory.newFactory();

public TheGame(String gameFile)
{
    gameFileName = gameFile;
    getIntro();
    printIntro();
    getOptions();
    printOptions();
}

private void getIntro(){
    try{
        FileReader gameFile = new FileReader(gameFileName); 
        XMLStreamReader gameFileXML = inputFactory.createXMLStreamReader(gameFile);

        if(findIntro(gameFileXML)){         
            while(findEventLoc(gameFileXML,XMLStreamConstants.END_ELEMENT,"Intro")== false){
                if (gameFileXML.getEventType() == XMLStreamConstants.START_ELEMENT && !(gameFileXML.getLocalName().equals("Intro"))){
                    String testString = gameFileXML.getLocalName();

                    switch (testString){
                        case "AreaID" : areaID = Integer.parseInt(gameFileXML.getElementText());
                                        break;
                        case "StartDescription" : areaDescription = gameFileXML.getElementText();
                                                  break;
                        case "StartSector" : sectorID = Integer.parseInt(gameFileXML.getElementText());
                    }
                }
                else gameFileXML.next();
            }

        }
            else gameFileXML.next();

        gameFile.close();
        gameFileXML.close();
        gameFile = null;
        gameFileXML = null;
    }   
    catch (IOException | XMLStreamException e){
        System.out.println(e);
    }
} // end of getIntro() method

private void printIntro()
{
    System.out.println(areaDescription + "\n");
    getSectorDescription();
    printSectorDescription();
}

private void getSectorDescription()

{
    try
    {
        FileReader gameFile = new FileReader(gameFileName); 
        XMLStreamReader gameFileXML = inputFactory.createXMLStreamReader(gameFile);
        completeBreak:
        while(gameFileXML.hasNext()){
            if (findCorrectSector(gameFileXML)){
                while(gameFileXML.hasNext()){
                    if(findEventLoc(gameFileXML,XMLStreamConstants.START_ELEMENT,"Description")){
                        sectorDescription = gameFileXML.getElementText();
                        break completeBreak;
                    }
                    else gameFileXML.next();
                }
            }
            else gameFileXML.next();
        }

        gameFile.close();
        gameFileXML.close();
        gameFile = null;
        gameFileXML = null;
    }

    catch (IOException | XMLStreamException e)
    {
        System.out.println(e);
    }
}

private void printSectorDescription()
{
    System.out.println(sectorDescription + "\n");
} // end of printSectorDescription() method

public void getOptions()
{
    try
    {
        FileReader gameFile = new FileReader(gameFileName); 
        XMLStreamReader gameFileXML = inputFactory.createXMLStreamReader(gameFile);

        if(findCorrectSector(gameFileXML)){
            while(gameFileXML.hasNext()){
                if(findEventLoc(gameFileXML,XMLStreamConstants.END_ELEMENT,"Sector")){
                    gameFileXML.close();
                    gameFile.close();
                    break;
                }           
                else if (findEventLoc(gameFileXML,XMLStreamConstants.START_ELEMENT,"Exit")){
                    if((neededItem = returnAttData(gameFileXML,"ReqItem"))!= null){
                        optionRequiredItem=true;

                    }

                    addToArray(possibleOptions,gameFileXML.getElementText());

                    gameFileXML.next();
                }
                else if (findEventLoc(gameFileXML,XMLStreamConstants.START_ELEMENT,"Leave")){
                    if((neededItem = returnAttData(gameFileXML,"ReqItem")) != null){
                        areaReqItem=true;
                    }
                    String areaOption = gameFileXML.getElementText();
                    addToArray(possibleOptions,areaOption);
                    addToArray(newAreaOptions,areaOption);
                    gameFileXML.next(); 
                }
                else if (findEventLoc(gameFileXML,XMLStreamConstants.START_ELEMENT,"Option")){
                    if((neededItem = returnAttData(gameFileXML,"ReqItem")) != null){
                        descriptorReqItem = true;
                    }
                    String descriptorOption = gameFileXML.getElementText();
                    addToArray(possibleOptions,descriptorOption);
                    addToArray(descriptorOptions,descriptorOption);
                    gameFileXML.next();
                }
                else gameFileXML.next();            
            }
        }               
        gameFile.close();
        gameFileXML.close();
        gameFile = null;
        gameFileXML = null;

    }
    catch (IOException | XMLStreamException e)
    {
        System.out.println(e);
    }
} 

public void printOptions()

{
    for (int i = 0; i< possibleOptions.length -1;i++)
    {
        if (possibleOptions[i] != null)
        System.out.println(i + " " + possibleOptions[i]);
    }
} // end of printOptions() method

public String[] returnOptions()
{
    return possibleOptions;
}

public void selectedOptionSetUp(int userOptionSelected)
{
    System.out.println("");
    String textOfOption = possibleOptions[userOptionSelected];

    if (isItemInArray(newAreaOptions,textOfOption))
    {
        checkGainItem("Leave",textOfOption);



        if(areaReqItem){
            if (userHasItem(neededItem)){
                sectorID = 0;
                setAreaID(textOfOption);
                setAreaDescription();
                printArea();
            }
            else System.out.println("You do not have an item needed to continue\n");
        }
        else{
            sectorID = 0;
            setAreaID(textOfOption);
            setAreaDescription();
            printArea();
        }
        if(gainedItemText != null){
            System.out.println(gainedItemText);
        }

    }
    else if(isItemInArray(descriptorOptions,textOfOption))
    {
        checkGainItem("Option",textOfOption);

        if(descriptorReqItem){
            if(userHasItem(neededItem)){
                printDescriptorOption(textOfOption);
            }
            else System.out.println("You do not have an item needed to continue\t");
        }
        else printDescriptorOption(textOfOption);

        if(gainedItemText != null){
            System.out.println(gainedItemText);
        }
    }
    else
    {
        checkGainItem("Exit",textOfOption);



        if(optionRequiredItem){
            if(userHasItem(neededItem)){
                setSector(textOfOption);
            }
            else System.out.println("You do not have an item needed to continue\t");
        }
        else setSector(textOfOption);
        if(gainedItemText != null){
            System.out.println(gainedItemText);
        }
    }
    gainedItemText = null;
    optionRequiredItem = false;
    areaReqItem = false;
    descriptorReqItem = false;
    neededItem= null;
    getSectorDescription();
    printSectorDescription();
    Arrays.fill(possibleOptions, null);;
    Arrays.fill(newAreaOptions,null);
    Arrays.fill(descriptorOptions, null);
} 

private void setSector(String optionText)
{
    try
    {
        FileReader gameFile = new FileReader(gameFileName); 
        XMLStreamReader gameFileXML = inputFactory.createXMLStreamReader(gameFile);

        allLoops:

        if (findCorrectArea(gameFileXML) ){
            while (findEventLoc(gameFileXML,XMLStreamConstants.END_ELEMENT,"Sectors") == false){
                if(findEventLoc(gameFileXML,XMLStreamConstants.START_ELEMENT,"Sector")){ 
                    while(gameFileXML.hasNext()){
                        if(findEventLoc(gameFileXML,XMLStreamConstants.START_ELEMENT,"Exit")){
                            int tempSectorID = Integer.parseInt(returnAttData(gameFileXML,"ID"));
                            if(gameFileXML.getElementText().equals(optionText)){
                                sectorID= tempSectorID;
                                gameFileXML.close();
                                gameFile.close();
                                break allLoops;  
                            }
                            else gameFileXML.next();     
                        }
                        else gameFileXML.next();
                    }
                }       
                else gameFileXML.next();
            } //end of first while
        }            
        else gameFileXML.next();

        gameFile.close();
        gameFileXML.close();
        gameFile = null;
        gameFileXML = null;
    }

    catch (IOException | XMLStreamException e)
    {
        System.out.println(e);
    }
}

private void setAreaID(String exitText)
{
    try
    {
        FileReader gameFile = new FileReader(gameFileName); 
        XMLStreamReader gameFileXML = inputFactory.createXMLStreamReader(gameFile);


             if(findCorrectSector(gameFileXML))
             {  
                 boolean foundNewAreaID = false;
                 while(foundNewAreaID == false){
                     if (findEventLoc(gameFileXML,XMLStreamConstants.START_ELEMENT,"Leave")){
                         int tempAreaID = Integer.parseInt(returnAttData(gameFileXML,"ID")); // FLAG possible bug issue
                         if (gameFileXML.getElementText().equals(exitText)){ 
                             areaID = tempAreaID;
                             foundNewAreaID = true; 
                         }
                         else gameFileXML.next();
                     }
                     else gameFileXML.next();
                 }
             }
             else gameFileXML.next(); //possibly dangerous to have this with a while inside if?

        gameFile.close();
        gameFileXML.close();
        gameFile = null;
        gameFileXML = null;
    }

    catch (IOException | XMLStreamException e)
    {
        System.out.println(e);
    }
} // end of setArea() method

private void setAreaDescription()
{
    try
    {
        FileReader gameFile = new FileReader(gameFileName); 
        XMLStreamReader gameFileXML = inputFactory.createXMLStreamReader(gameFile);

        allLoops:
        while(gameFileXML.hasNext())
        {
            if(gameFileXML.getEventType() == XMLStreamConstants.START_ELEMENT && gameFileXML.getLocalName().equals("Area"))
            {
                if(Integer.parseInt(returnAttData(gameFileXML,"ID")) == areaID)
                {
                    while ((gameFileXML.getEventType() == XMLStreamConstants.END_ELEMENT && gameFileXML.getLocalName().equals("Description"))== false)
                    {
                        if (gameFileXML.getEventType() == XMLStreamConstants.START_ELEMENT && gameFileXML.getLocalName().equals("Description"))
                        {
                            areaDescription = gameFileXML.getElementText();
                            break allLoops;
                        }
                        else gameFileXML.next();
                    }
                }
                else gameFileXML.next();
            }
            else gameFileXML.next();
        }
        gameFileXML.close();
        gameFile.close();
        gameFileXML = null;
        gameFile = null;
    }

    catch (IOException | XMLStreamException e)
    {
        System.out.println(e);
    }
}

private void printDescriptorOption(String userOption)
{
    try
    {
        FileReader gameFile = new FileReader(gameFileName); 
        XMLStreamReader gameFileXML = inputFactory.createXMLStreamReader(gameFile);
        allLoops:
        while(gameFileXML.hasNext())
        {
            if(gameFileXML.getEventType() == XMLStreamConstants.START_ELEMENT && 
                    gameFileXML.getLocalName().equals("Area") && 
                    Integer.parseInt(returnAttData(gameFileXML,"ID")) == areaID)
            {
                while((gameFileXML.getEventType() == XMLStreamConstants.END_ELEMENT && gameFileXML.getLocalName().equals("Area"))== false)
                {
                    if(gameFileXML.getEventType() == XMLStreamConstants.START_ELEMENT &&
                            gameFileXML.getLocalName().equals("Sector") &&
                            Integer.parseInt(returnAttData(gameFileXML,"ID")) == sectorID)
                    {
                        while((gameFileXML.getEventType() == XMLStreamConstants.END_ELEMENT &&
                                gameFileXML.getLocalName().equals("Sector")) == false)
                        {
                            if(gameFileXML.getEventType() == XMLStreamConstants.START_ELEMENT &&
                                    gameFileXML.getLocalName().equals("Option"))
                            {
                                String tempOptionString = gameFileXML.getAttributeValue(0);
                                if(gameFileXML.getElementText().equals(userOption))
                                {
                                    System.out.println(tempOptionString + "\n");
                                    gameFileXML.close();
                                    gameFile.close();
                                    break allLoops;
                                }
                                else gameFileXML.next();
                            }
                            else gameFileXML.next();
                        }
                    }
                    else gameFileXML.next();    
                }
            }
            else gameFileXML.next();
        }
        gameFile.close();
        gameFileXML.close();
        gameFile = null;
        gameFileXML = null;
    }
    catch (IOException | XMLStreamException e){
        System.out.println(e);
    }
}

private void printArea(){
        if(testAreaID != areaID){
            System.out.println(areaDescription);
        }   
    } // end of printArea() method

private void checkGainItem(String optionType, String optionText){
    String gainedItem = null;
    try{

        FileReader gameFile = new FileReader(gameFileName); 
        XMLStreamReader gameFileXML = inputFactory.createXMLStreamReader(gameFile);

        if (findCorrectSector(gameFileXML)){
            while(gameFileXML.hasNext()){
                if(findEventLoc(gameFileXML,XMLStreamConstants.START_ELEMENT,optionType)){
                    String potentialItem = returnAttData(gameFileXML,"GainItem");
                    String potentialItemResponse = returnAttData(gameFileXML,"GainItemText");
                    if(gameFileXML.getElementText().equals(optionText)){
                        gainedItem = potentialItem;
                        gainedItemText = potentialItemResponse;
                        break;
                    }       
                }
                else gameFileXML.next();
            }
        }
        else gameFileXML.next();
        gameFile.close();
        gameFileXML.close();    

        if(gainedItem != null){
            if(isItemInArray(userGainedItems,gainedItem)){
                gainedItemText="an item was here";
            }
            else {
                addToArray(userGainedItems,gainedItem);
            }
        }
        gameFileXML.close();
        gameFile.close();
        gameFileXML = null;
        gameFile = null;
    }
    catch (IOException | XMLStreamException e){
        System.out.println(e);
    }
}

private String returnAttData(XMLStreamReader reader, String att){
    int count = reader.getAttributeCount();
    String attVal = null;
    for(int i = 0; i < count; i++){
        if(reader.getAttributeLocalName(i).equals(att)){
            attVal = reader.getAttributeValue(i);//this should ALWAYS happen, or code might break
            break;
        }
    }
    return attVal;
}

private boolean findEventLoc(XMLStreamReader reader, int ele, String eleName){
    boolean foundEvent = false;

    if(reader.getEventType() == ele && reader.getLocalName().equals(eleName)){
         foundEvent= true;;
    }
    else foundEvent = false;
    return foundEvent;
}

private boolean findIntro(XMLStreamReader reader) throws XMLStreamException{
    boolean foundIntro = false;

    while(foundIntro == false){
        if(findEventLoc(reader,XMLStreamConstants.START_ELEMENT,"Intro")){
            foundIntro = true;
        }
        else reader.next();
    }
    return foundIntro;
}
private boolean findCorrectArea(XMLStreamReader reader) throws XMLStreamException{
    boolean foundCorrectArea = false;
    //assumes foundCorrectArea will always become true
    while(foundCorrectArea == false) {
    if(findEventLoc(reader,XMLStreamConstants.START_ELEMENT,"Area")){
        if (Integer.parseInt(returnAttData(reader,"ID")) == areaID ) {
            foundCorrectArea = true;
        }
        else reader.next();
    }
    else reader.next();
    }
    return foundCorrectArea;
}

private boolean findCorrectSector(XMLStreamReader reader) throws XMLStreamException{
    boolean foundCorrectSector = false;

    while (foundCorrectSector == false){
        if(findCorrectArea(reader)){
            while(foundCorrectSector == false) {
                if(findEventLoc(reader,XMLStreamConstants.START_ELEMENT,"Sector")){
                    if(Integer.parseInt(returnAttData(reader,"ID"))== sectorID){
                        foundCorrectSector = true;
                    }
                    else reader.next();
                }
                else reader.next();
            }
        }
        else reader.next();
    }
    return foundCorrectSector;
}

private boolean userHasItem(String item){
    boolean hasItem = false;
    if (isItemInArray(userGainedItems,item)){
        hasItem = true;
    }
    else hasItem = false;
    return hasItem;
}

private void addToArray(String[] array, String item){
    for(int i = 0; i < array.length - 1;i++){
        if (array[i] == null){
            array[i]= item;
            break;
        }
    }
}

private boolean isItemInArray(String[] array, String item){
    boolean itemFound = false;
    for(int i=0; i < array.length -1;i++){
        if(array[i] == null) break;
        if (array[i].equals(item)){
            itemFound = true;
            break;
        }
    }
    return itemFound;
}
}

As a recap of what I want to know: what would be some better ways to do the same thing, without using any extra self made classes in staX (if that is what I'm using, albeit just the tip of the iceburg). Is there a leak somewhere maybe? Is using null values to FileReader and XMLStreamReader appropriate?

Thank you for your time.

1

There are 1 answers

4
PJ Fanning On BEST ANSWER

If the XML file is not very large, I would recommend using a DOM parser and applying XPath expressions. This code is much easier to write and maintain than streaming XML code like STaX. I do use STaX myself for large documents.

For XPath, I normally use http://jaxen.org/faq.html, as opposed to the javax.xml.xpath codebase.

If you have memory usage problems in your code, you should try an analysis tool like https://visualvm.github.io/ or a profiler like JProfiler.

I would recommend using InputStreams over FileReaders. The FileReader class makes assumptions about the character encoding of the XML file.