Dima Ciun
Dima Ciun

Reputation: 87

Parse a text file, split it into lines, return a list and map into objects

So I have have 2 types of files, one for level definition and other for blocks structure definition.

I need to parse both of them into list of strings which contains the lines, and go over the list and over each line, split and parse and map them into java objects.

I have no lead how to do it, I have read about java io reader but I do get confused here where and how to use it.

Understanding the content of the level specification of a single level: this will go over the strings, split and parse them, and map them to java objects, resulting in a LevelInformation object.

P.S I already wrote and built the LevelInformation interface(which works if I manually write a level that implements this interface), which contains every thing that is in the text file (level name, velocities,background and etc..) So basically I just need to parse those texts file and map them into this interface.

    public interface LevelInformation {
       int numberOfBalls();
       // The initial velocity of each ball
       // Note that initialBallVelocities().size() == numberOfBalls()
// velocities created by (a,s) format. a = angle, s = speed.
       List<Velocity> initialBallVelocities();
       int paddleSpeed();
       int paddleWidth();
       // the level name will be displayed at the top of the screen.
       String levelName();
       // Returns a sprite with the background of the level
       Sprite getBackground();
       // The Blocks that make up this level, each block contains
       // its size, color and location.
       List<Block> blocks();
       // Number of blocks that should be removed
       // before the level is considered to be "cleared".
       // This number should be <= blocks.size();
       int numberOfBlocksToRemove();
    }

An example of the files:

level_definition.txt

START_LEVEL
level_name:Square Moon
ball_velocities:45,500
background:image(background_images/night.jpg)
paddle_speed:650
paddle_width:160
block_definitions:definitions/moon_block_definitions.txt
blocks_start_x:25
blocks_start_y:80
row_height:100
num_blocks:4
START_BLOCKS
--ll--
--ll--
END_BLOCKS
END_LEVEL

block_definitions.txt

    #block definitions
bdef symbol:l width:100 height:100 fill:color(RGB(154,157,84))

#spacers definitions
sdef symbol:- width:30

So I need to create a list and get a reader and somehow parse it. I'll be glad to get some tips, ideas and help for doing this. Thanks.

public class LevelSpecificationReader {

   public List<LevelInformation> fromReader(java.io.Reader reader) {
      // ...
   }
}

I think I need to:

This is my failed attempt:

public List<LevelInformation> fromReader(java.io.Reader reader) throws IOException {
    ArrayList<String> listOfLines = new ArrayList<>();
    BufferedReader bufReader = new BufferedReader(new FileReader("file.txt"));
    String line = bufReader.readLine();
    while (line != null) {
        listOfLines.add(line);
        line = bufReader.readLine();
    }
    return listOfLines;
}

This code brings an error bcause I return a list of string but I need a list of LevelInformation.

Upvotes: 0

Views: 1514

Answers (2)

Abra
Abra

Reputation: 20914

This is not a complete solution, since the code in your question is not a reproducible example since it is not complete. Where are the definitions of classes Block and Sprite and Velocity? Well I guessed those and made up minimal definitions for them.

My hope is that the below code will be enough to help you complete your project. It is based on the details you posted including the sample file: level_definition.txt

Class Sprite

import java.awt.Image;

public class Sprite {
    private Image  image;

    public Sprite(Image image) {
        this.image = image;
    }
}

class Velocity

public class Velocity {
    private int  speed;

    public Velocity(int speed) {
        this.speed = speed;
    }
}

Class LevelDtl which implements your interface: LevelInformation.
Personally, I don't see the need for an interface. I think it should be a class.

public class LevelDtl implements LevelInformation {
    private String  levelName;
    private List<Velocity>  ballVelocities;
    private Sprite  background;
    private int  paddleSpeed;
    private int  paddleWidth;
    private List<Block>  blocks;
    private int  numBlocks;

    @Override
    public int numberOfBalls() {
        return ballVelocities == null ? 0 : ballVelocities.size();
    }

    @Override
    public List<Velocity> initialBallVelocities() {
        return ballVelocities;
    }

    @Override
    public int paddleSpeed() {
        return paddleSpeed;
    }

    @Override
    public int paddleWidth() {
        return paddleWidth;
    }

    @Override
    public String levelName() {
        return levelName;
    }

    @Override
    public Sprite getBackground() {
        return background;
    }

    @Override
    public List<Block> blocks() {
        return blocks;
    }

    @Override
    public int numberOfBlocksToRemove() {
        return numBlocks;
    }

    public void setBackground(Sprite bg) {
        background = bg;
    }

    public void setBallVelocities(List<Velocity> velocities) {
        ballVelocities = velocities;
    }

    public void setLevelName(String name) {
        levelName = name;
    }

    public void setPaddleSpeed(int speed) {
        paddleSpeed = speed;
    }

    public void setPaddleWidth(int width) {
        paddleWidth = width;
    }

    public String toString() {
        return String.format("Level: %s , Paddle: [speed = %d , width = %d]",
                             levelName,
                             paddleSpeed,
                             paddleWidth);
    }
}

All the methods with @Override annotation are implementations of methods in LevelInformation interface. Also, note that method toString() is only for debugging purposes since I use it in the final class which is the one you named: LevelSpecificationReader. It reads the file level_definition.txt line by line, assuming the format shown in your question and builds and configures an instance of class LevelDtl which it then adds to a List. Finally, the below code prints the contents of the List. Of-course, using the sample data you provided in your question, the List contains only one element.

import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import javax.imageio.ImageIO;

public class LevelSpecificationReader {
    private static final String  BACKGROUND = "background:";
    private static final String  BALL_VELOCITIES = "ball_velocities:";
    private static final String  END_BLOCKS = "END_BLOCKS";
    private static final String  END_LEVEL = "END_LEVEL";
    private static final String  IMAGE = "image(";
    private static final String  LEVEL_NAME = "level_name:";
    private static final String  PADDLE_SPEED = "paddle_speed:";
    private static final String  PADDLE_WIDTH = "paddle_width:";
    private static final String  START_BLOCKS = "START_BLOCKS";
    private static final String  START_LEVEL = "START_LEVEL";

    private static void setBackground(LevelDtl level, String data) throws IOException {
        Objects.requireNonNull(level, "Null level.");
        if (data != null  &&  !data.isEmpty()) {
            // image(background_images/night.jpg)
            if (data.startsWith(IMAGE)) {
                String path = data.substring(IMAGE.length(), data.length() - 1);
                BufferedImage image = ImageIO.read(new File(path));
                level.setBackground(new Sprite(image));
            }
        }
    }

    private static void setInitialBallVelocities(LevelDtl level, String data) {
        Objects.requireNonNull(level, "Null level.");
        if (data != null  &&  !data.isEmpty()) {
            String[] numbers = data.split(",");
            if (numbers.length > 0) {
                List<Velocity> velocities = new ArrayList<>();
                for (String number : numbers) {
                    try {
                        int speed = Integer.parseInt(number);
                        Velocity velocity = new Velocity(speed);
                        velocities.add(velocity);
                    }
                    catch (NumberFormatException xNUmberFormat) {
                        // Ignore.
                    }
                }
                level.setBallVelocities(velocities);
            }
        }
    }

    private static void setPaddleSpeed(LevelDtl level, String data) {
        Objects.requireNonNull(level, "Null level.");
        if (data != null  &&  !data.isEmpty()) {
            int speed;
            try {
                speed = Integer.parseInt(data);
            }
            catch (NumberFormatException xNumberFormat) {
                speed = 0;
            }
            level.setPaddleSpeed(speed);
        }
    }

    private static void setPaddleWidth(LevelDtl level, String data) {
        Objects.requireNonNull(level, "Null level.");
        if (data != null  &&  !data.isEmpty()) {
            int width;
            try {
                width = Integer.parseInt(data);
            }
            catch (NumberFormatException xNumberFormat) {
                width = 0;
            }
            level.setPaddleWidth(width);
        }
    }

    /**
     * Start here.
     */
    public static void main(String[] args) {
        try (FileReader fr = new FileReader("level_definition.txt");
             BufferedReader br = new BufferedReader(fr)) {
               List<LevelInformation> levels = new ArrayList<>();
               LevelDtl level = null;
               String line = br.readLine();
               while (line != null) {
                   if (START_LEVEL.equals(line)) {
                       // End current level.
                       if (level != null) {
                           levels.add(level);
                       }
                       // Start next level.
                       level = new LevelDtl();
                   }
                   else if (line.startsWith(LEVEL_NAME)) {
                       level.setLevelName(line.substring(LEVEL_NAME.length()));
                   }
                   else if (line.startsWith(BALL_VELOCITIES)) {
                       setInitialBallVelocities(level, line.substring(BALL_VELOCITIES.length()));
                   }
                   else if (line.startsWith(BACKGROUND)) {
                       setBackground(level, line.substring(BACKGROUND.length()));
                   }
                   else if (line.startsWith(PADDLE_SPEED)) {
                       setPaddleSpeed(level, line.substring(PADDLE_SPEED.length()));
                   }
                   else if (line.startsWith(PADDLE_WIDTH)) {
                       setPaddleWidth(level, line.substring(PADDLE_WIDTH.length()));
                   }
                   line = br.readLine();
               }
               if (level != null) {
                   levels.add(level);
               }
               System.out.println(levels);
        }
        catch (IOException xIo) {
            xIo.printStackTrace();
        }
    }
}

The above code only handles lines in file level_definition.txt up to and including this line:

paddle_width:160

Good luck with adding code to handle the rest of the contents of the file.

Upvotes: 1

doppelgunner
doppelgunner

Reputation: 649

Maybe what you want is to break it into pieces first so that you don't need to worry about all the things at once and focus on parsing the strings. All you need to do is make a constructor that accepts string and then parse it in the constructor

public List<LevelInformation> fromReader(java.io.Reader reader) throws IOException {
ArrayList<LevelInformation> listOfLines = new ArrayList<>();
BufferedReader bufReader = new BufferedReader(new FileReader("file.txt"));
String line = bufReader.readLine();
while (line != null) {
    listOfLines.add(new LevelInformation(line));
    line = bufReader.readLine();
}
return listOfLines;

}

public class LevelInformation {

       LevelInformation (String text) {
           parseText(text);
       }

       private void parseText(String text) {

           //do something here
       }
}

Upvotes: 0

Related Questions