Getting data from .csv file into array list of objects - java

Here is the format of the .csv file I am working with -
Hostname,IP Address,Patched?,OS Version,Notes
A.example.COM,1.1.1.1,NO,11,Faulty fans
b.example.com,1.1.1.2,no,13,Behind the other routers so no one sees it
C.EXAMPLE.COM,1.1.1.3,no,12.1
d.example.com,1.1.1.4,yes,14
c.example.com,1.1.1.5,no,12,Case a bit loose
e.example.com,1.1.1.6,no,12.3
f.example.com,1.1.1.7,No,15,Guarded by sharks with lasers on their heads
I currently have this program which reads in all data from the above .csv file into an array list before then outputting the results to the console as you can see in the included output text. Ideally I eventually need to be able to compare each different field in each different row with one another, perform calculations etc. so would like to save each row as an object inside an array list instead. I have tried doing this but so far with no success. Is there a simple way of modifying my program to do this? Also, if possible, I would like for the headings and the notes not to be included. Here is my code so far -
package crunchify;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
public class CrunchifyCSVtoArrayList {
public static void main(String[] args) {
BufferedReader crunchifyBuffer = null;
try {
String crunchifyLine;
crunchifyBuffer = new BufferedReader(new FileReader("Crunchify-CSV-to-ArrayList.csv"));
while ((crunchifyLine = crunchifyBuffer.readLine()) != null) {
System.out.println("ArrayList data: " + crunchifyCSVtoArrayList(crunchifyLine) + "\n");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (crunchifyBuffer != null) crunchifyBuffer.close();
} catch (IOException crunchifyException) {
crunchifyException.printStackTrace();
}
}
}
// Utility which converts CSV to ArrayList using Split Operation
public static ArrayList<String> crunchifyCSVtoArrayList(String crunchifyCSV) {
ArrayList<String> crunchifyResult = new ArrayList<String>();
if (crunchifyCSV != null) {
String[] splitData = crunchifyCSV.split("\\s*,\\s*");
for (int i = 0; i < splitData.length; i++) {
if (!(splitData[i] == null) || !(splitData[i].length() == 0)) {
crunchifyResult.add(splitData[i].trim());
}
}
}
return crunchifyResult;
}
}
Current output:
ArrayList data: [Hostname, IP Address, Patched?, OS Version, Notes]
ArrayList data: [A.example.COM, 1.1.1.1, NO, 11, Faulty fans]
ArrayList data: [b.example.com, 1.1.1.2, no, 13, Behind the other routers so no one sees it]
ArrayList data: [C.EXAMPLE.COM, 1.1.1.3, no, 12.1]
ArrayList data: [d.example.com, 1.1.1.4, yes, 14]
ArrayList data: [c.example.com, 1.1.1.5, no, 12, Case a bit loose]
ArrayList data: [e.example.com, 1.1.1.6, no, 12.3]
ArrayList data: [f.example.com, 1.1.1.7, No, 12.2]
ArrayList data: [g.example.com, 1.1.1.6, no, 15, Guarded by sharks with lasers on their heads]
To give some more detail on the kind of thing I will eventually need to do, it needs to print out which routers need updating e.g. if any of the routers are below version 12 then print that those ones need updating.

You should create a custom class to hold the data. Objects of this class represent a single row from the csv file.

Related

Move from string to array but after that select by the first character (Record Type 1, 2, 5)

I need your help, I am new in Java
I need to read a flat file with 5 different of records
the way to differentiate each record is the first characters, after that I have the idea to move to an 5 different array to play with with the data inside.
example
120220502Name Last Name1298843984 $1.50
120220501other client 8989899889 $23.89
2Toronto372 Yorkland drive 1 year Ontario
512345678Transfer Stove Pay
522457839Pending Microwave Interactive
any help will quite appreciated
Break the problem into chunks. The first problem is reading the file:
try (BufferedReader reader = new BufferedReader(new FileReader("path/to/file"))) {
parseData(reader); //method to do the work.
} catch (IOException e) {
e.printStackTrace();
}
Then you need to decide what kind of record it is:
public void parseData(BufferedReader input) throws IOException {
for (String line = input.readLine(); line != null; line = input.readLine()) {
if (line.startsWith("1")) {
parseType1(line);
} else if (line.startsWith("2")) {
parseType2(line);
} else if (line.startsWith("5")) {
parseType5(line);
} else {
throw new Exception("Unknown record type: " + line.charAt(0));
}
}
}
Then you'll need to create the various parseTypeX method to handle turning the text into usable chunks and then into classes.
public Type1Record parseType1(String data) {
//create a Type1Record
Type1Record record = new Type1Record();
//split the string something like
String [] fields = data.split("\\s+");
//Assign those chunks to the record
record.setId(fields[0]);
record.setFirstName(fields[1]);
record.setLastName(fields[2]);
record.setTotal(fields[3]); //if you want this to be a real number, you'll need to remove the $
}
Repeat the process with the other record types. You'll likely need to group records together, but that should be easy enough.

Saving data to a file doesn't behave as expected

I have a simple project where I created a Store with customers, products and employees. Each is represented by a Class of course and I also have a CSV file for each one of them to be able to load data from and save data to it.
I'm facing issues where the file reading/writing is working, but not really. For example, I have the ability to save each file individually so if for instance I want to create a new customer, I'd save it to the list and then to the file. Issue is, once I do it for another Class (i.e if I create a new employee) and then save it again, the customer file object I saw in the CSV earlier is deleted. BUT, once I add a new object again, that same object reappears again. Hope you can somehow understand, but here is a more detailed view:
customer.csv is empty:
Me creating a new customer:
Created and saved to CSV:
Now, if I go to the other menu, and click on "Save all data" that jon snow customer object will be gone. Then if I create a new customer, then it will be added to the CSV file, along with the jon snow I added earlier. So why is it gone in the first place?
So here is the whole file reader/writer code I'm using:
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.util.ArrayList;
import java.io.IOException;
import java.util.List;
import java.util.Scanner;
class CSV {
static void CreateFile(String filename) { //Create new file
try {
File fileToCreate = new File(filename);
if (fileToCreate.createNewFile()) {
System.out.println("File created sucessfully: " + fileToCreate.getName());
}
} catch (IOException e) {
System.out.println("Cannot create file!");
}
}
static void ReadFile(String path_and_filename){
try {
File fileToRead = new File(path_and_filename);
Scanner myReader = new Scanner(fileToRead);
System.out.println("Reading file "+path_and_filename+" :");
while (myReader.hasNextLine()) {
String data = myReader.nextLine();
System.out.println(data);
}
myReader.close();
System.out.println();
} catch (FileNotFoundException e) {
System.out.println("There is no such file "+"\"path_and_filename\""+".\n");
}
}
// The StringBuilder in Java represents a mutable sequence of characters.
// Java's built in String class is not mutable.
static void saveArrayListToFile(List<Output> listToSave, String fileName, String sep) throws Exception {
StringBuilder ans = new StringBuilder();
for (Output record : listToSave) {
ans.append(record.createOutput());
ans.append(sep);
}
saveStringToFile(ans.toString(), fileName);
System.out.println("\nData saved to "+ fileName);
}
static void saveArrayListToFile1(ArrayList<String> listToSave, String fileName, String sep){
StringBuilder ans = new StringBuilder();
for (Object record : listToSave) {
ans.append(record.toString());
ans.append(sep);
}
saveStringToFile(ans.toString(), fileName);
System.out.println("\nList was saved to file "+fileName+"\n");
}
static void saveStringToFile(String data, String fileName){
BufferedWriter bufferedWriter=null;
try {
bufferedWriter = new BufferedWriter(
new FileWriter(fileName,false));
bufferedWriter.write(data);
} catch (IOException e) {
System.out.println("Cannot write to file");
} finally {
try {
bufferedWriter.close();
} catch (IOException e) {
System.out.println("Cannot write to file");
}
}
}
}
When I'm creating a new customer, I call it from a menu and it looks like this:
switch (selection) {
case 1:
try {
System.out.println("You're registering as a new customer");
String custID = ObjectIDs.generateID();
System.out.println("Enter first name:");
String firstName = sc.next();
System.out.println("Enter last name:");
String lastName = sc.next();
st.newCustomer(custID, firstName, lastName);
st.saveCustomersList();
} catch (Exception e) {
e.printStackTrace();
}
break;
the saveCustomerList() function is this:
#SuppressWarnings("unchecked")
void saveCustomersList() throws Exception {
CSV.saveArrayListToFile((List<Output>)(List<?>) customers, CUSTOMERS_FILE_PATH,"\n");
}
And then the functions calls saveArrayListToFile() to save it.
The behavior is the same with Product and Employee projects, so I randomly chose to show how it acts when creating a new Product.
I hope I added enough information. If needed, I can paste more code in but I already feel it's very cluttered. Hopefully it's ok.
Thank you very much :)
At the moment it's hard to say, as one can only hypothesise as to what happens when you click on "Save all data". There are some weird things (what is saveArrayListToFile and saveArrayListToFile11? Why does one declare an exception? When are these called?).
Having said that, look at the actual file writing method saveStringToFile, it says:
bufferedWriter = new BufferedWriter(new FileWriter(fileName,false));
This false there means 'do not append to file, rewrite it from scratch'. So each time you call it, file contents are discarded and replaced from what you provide to the method call. So my somewhat educated guess would be:
You save customer one to file (gets cleared, customer 1 written) and
append the customer to a list of customers (that's my guess)
You
save customer two to file (file gets cleared, so only customer 2 is
saved), you add to list to customers (do you?)
Then you choose 'save all' which gets list of customers, and save them in one go, a single call to the method. The file is cleared, all customers are saved.
But it's all guessing. Try creating a minimal, reproducible example
In addition to pafau k. I would like to add some things at least I would do differently...
First of all:
Things that can cause errors or unexpected behaviour:
Everything below is in saveStringToFile
Like already pointed out the Initialisation of the BufferedWriter: It should be initialized like this:
bufferedWriter = new BufferedWriter(new FileWriter(filename, true));
This puts the File into appending mode (if you want to append to a file you can also get rid of the boolean (second argument) entirely because appending is standard: new FileWriter(filename))
If for some case the Creation of the BufferedWriter failed you will still have a null-pointing object as bufferedWriter. This however means that you will be surprised with a NullPointerException in your finally block. To prevent this first of all do a check in your finally block:
if (bufferedWriter != null) {
// Close your bufferedWriter in here
}
Also, if you run into an error you will likely be presented with the same error message twice.
Now cosmetics:
Things that I would write differently for aesthetic reasons:
Java methods (and static "methods") are always starting with a small letter :)
This means it should be public static void createFile() for example or static void readFile()
variables and parameters of methods do not contain seperators like _ but instead if you want to make it more readable you start with a small letter and for each seperation you use a capital letter for that: e.g. String thisIsAVeryLongVariableWithALotOfSeperations = "Foo";
The generic types in saveArrayListToFile1() work like a placeholder. So you declare ArrayList<String> listToSave so you don't need a cast in the following for-loop: You can simply write:
for (String record : listToSave) {
ans.append(record);
ans.append(sep);
}
I hope this fixes all errors or complications. :)

How can I parse a file and map input into several subrecords?

I have to start of with saying that I'm a super noob when it comes to Java and programming in general.
My issue:
I have a problem with parsing a textfile and putting the data of the file into subrecords. The key whether to put the data into a new subrecord or not is in my case based on the name of the tag in the file.
Here's an example of a file that I want to parse/split into subrecords.
SHP
DATA 1
DATA 2
DATA 3
ITEM A
DATA B
DATA C
ITEM A
DATA B
DATA C
SHP
So basically when I encounter the first occurrence of SHP I want to create a new SHP Class and then map the following tags (DATA in this example) into fields of the new SHP Object. However if the next tag is ITEM, I then need to create a new ITEM Object in SHP and then map the following DATA tags until a new ITEM tag is found... What makes it worse is that number of SHP tags can also be multiple in the file that I'm parsing.
What I've done so far is to put all the contents of the file into a ArrayList and then I iterate over this list and depending of the value of the current "record" I then create a new Object or map the value of the record in the list.
However I get totally lost with all my loops and I need some help! :)
Is there a good way of doing this? How can I easily fetch chunks of data from an ArrayList and extract and map this into new objects depending on the value of the current record?
I'm thinking that the same questions would appear if you would to create a parser from scratch that maps the data from an XML file and puts this into Objects, right?
Extended version of issue with real examples
Maybe it's easier if I provide some real examples and real data to illustrate my issue. Initially I just thought that it would make it harder to understand but hopefully this will be easier.
The file and the content:
Example file
So what I have done so far is to create some classes that should represent the data in the file. I even names the fields/variables of the class so that it would be easier for me to map.
Here are the classes:
public class REQUEST {
public SHIPMENT[] SHIPMENTS;
}
public class SHIPMENT {
public String IVNO;
public String CNNAME;
public String CNADDRESS1;
public String CNPC;
public String CNCITY;
public PACKAGE[] PACKAGES;
public ITEM[] ITEMS;
}
public class PACKAGE {
public String GOODSLINE;
public String GOODSDESCR;
public String GRWEIGHT;
}
public class ITEM {
public String ITEMLINE;
public String ARTNO;
public String GRWEIGHT;
}
And below is the code that I've done that doesn't work properly. I manage to create multiple SHIPMENT's and also create the ITEM's but for some reason the data isn't mapped. I also before calling this mapMethod put the entire content of the file into an ArrayList. One fiel row per record in the ArrayList.
Ok, so here it is in it's total and please be gentle with me :)
public REQUEST mapRequest(ArrayList<String> inputfile)
throws IllegalArgumentException, IllegalAccessException {
REQUEST request = new REQUEST();
ArrayList<SHIPMENT> shipments = new ArrayList<>();
for (int i = 0; i < inputfile.size(); i++) {
String recordIdentifier = getRecordIdentifier(inputfile.get(i));
if (StringUtils.equals(recordIdentifier, "IVNO")) {
SHIPMENT shipment = new SHIPMENT();
ArrayList<ITEM> items = new ArrayList<>();
int j = 0;
for (j = i; j < inputfile.size(); j++) {
String currShpRecIdentifier = getRecordIdentifier(inputfile.get(j));
String nextShpRecIdentifier = StringUtils.EMPTY;
if (j + 1 < inputfile.size()) {
nextShpRecIdentifier = getRecordIdentifier(inputfile.get(j + 1));
}
try {
Field field = shipment.getClass().getField(currShpRecIdentifier);
String shipmentRecordValue = getRecordValue(inputfile.get(j));
field.set(shipment, shipmentRecordValue);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
if (StringUtils.equals(nextShpRecIdentifier, "ITEMLINE")) {
ITEM item = new ITEM();
int k = 0;
for (k = j; k < inputfile.size(); k++) {
String currItemRecIdentifier = getRecordIdentifier(inputfile.get(k));
String nextItemRecIdentifier = StringUtils.EMPTY;
if (k + 1 < inputfile.size()) {
nextItemRecIdentifier = getRecordIdentifier(inputfile
.get(k + 1));
}
try {
Field field = item.getClass().getField(currItemRecIdentifier);
String itemRecordValue = getRecordValue(inputfile.get(k));
field.set(item, itemRecordValue);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
if (StringUtils.equals(nextItemRecIdentifier, "ITEMLINE")) {
break;
}
}
items.add(item);
j = k;
}
if (StringUtils.equals(nextShpRecIdentifier, "IVNO")) {
break;
}
}
shipment.ITEMS = items.toArray(new ITEM[items.size()]);
shipments.add(shipment);
i = j;
}
}
request.SHIPMENTS = shipments.toArray(new SHIPMENT[shipments.size()]);
return request;
}
private String getRecordIdentifier(String in) {
return StringUtils.left(in, 15).trim();
}
private String getRecordValue(String in) {
return StringUtils.substring(in, 16).trim();
}
I don't really get where you're having trouble with that.
FileReader input = new FileReader(filePathToYourFile);
BufferedReader bufRead = new BufferedReader(input);
String readLine = null;
String shp = "SHP"; //or define them as "constants" with static final
String item = "ITEM";
String data = "DATA";
ArrayList<SHPType> listOfSHPObjects = new ArrayList<SHPType>();
SHPType lastSHP;
ItemType lastItem;
while ( (readLine = bufRead.readLine()) != null)
{
String[] splittedLine = readLine.split(" ");
if(splittedLine[0].equals(shp))
{ //if it's an SHP, make an SHP object, and add it to a list.
lastSHP = new SHPType();
listOfSHPObjects.add(lastshp);
}
if(splittedLine[0].equals(data) && lastSHP != null)
{ //if it's a data object, either add it to the property/field/list/collection of the latest SHP, or if you have found an item, add it to the item.
//of course if you haven't found any SHP objects, discard it, as it doesn't belong anywhere
if(lastItem == null)
lastSHP.CollectionThatHoldsData.add(new DataType());
else
lastItem.CollectionThatHoldsData.add(new DataType());
}
if(splittedLine[0].equals(item) && lastSHP != null)
{
//create a new last item and add it to the item list of the last SHP found. New data will be added to this item
lastItem = new ItemType();
lastSHP.CollectionThatHoldsItems.add(lastItem);
}
}
//close the file etc...
This assumes that if you find the following:
DATA
ITEM
SHP
etc
The first DATA and ITEM are meaningless. If you want to assign them to the first SHP that you have found, store them in a waitingForSHP arrayList, and as soon as you find the first SHP, add them there.
Also, if you want to create an SHP/Item/Data based on the second value (fe in DATA 5, meaning 5), pass the 5 (splittedLine[1] variable in my example) as an argument to the constructor of the SHPType/ItemType/DataType object. It will be a string, so if you want to pass it as an integer, you will have to convert it ( Integer.parseInt(splittedLine[1]) ).

Java looping through array - Optimization

I've got some Java code that runs quite the expected way, but it's taking some amount of time -some seconds- even if the job is just looping through an array.
The input file is a Fasta file as shown in the image below. The file I'm using is 2.9Mo, and there are some other Fasta file that can take up to 20Mo.
And in the code im trying to loop through it by bunches of threes, e.g: AGC TTT TCA ... etc The code has no functional sens for now but what I want is to append each Amino Acid to it's equivalent bunch of Bases. Example :
AGC - Ser / CUG Leu / ... etc
So what's wrong with the code ? and Is there any way to do it better ? Any optimization ? Looping through the whole String is taking some time, maybe just seconds, but need to find a better way to do it.
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class fasta {
public static void main(String[] args) throws IOException {
File fastaFile;
FileReader fastaReader;
BufferedReader fastaBuffer = null;
StringBuilder fastaString = new StringBuilder();
try {
fastaFile = new File("res/NC_017108.fna");
fastaReader = new FileReader(fastaFile);
fastaBuffer = new BufferedReader(fastaReader);
String fastaDescription = fastaBuffer.readLine();
String line = fastaBuffer.readLine();
while (line != null) {
fastaString.append(line);
line = fastaBuffer.readLine();
}
System.out.println(fastaDescription);
System.out.println();
String currentFastaAcid;
for (int i = 0; i < fastaString.length(); i+=3) {
currentFastaAcid = fastaString.toString().substring(i, i + 3);
System.out.println(currentFastaAcid);
}
} catch (NullPointerException e) {
System.out.println(e.getMessage());
} catch (FileNotFoundException e) {
System.out.println(e.getMessage());
} catch (IOException e) {
System.out.println(e.getMessage());
} finally {
fastaBuffer.close();
}
}
}
currentFastaAcid = fastaString.toString().substring(i, i + 3);
Please replace with
currentFastaAcid = fastaString.substring(i, i + 3);
toString method of StringBuilder create new instance of String object every time you call it. It still contain a copy of all your large string. If you call substring directly from StringBuilder it will return a small copy of substring.
Also remove System.out.println if you don't really need it.
The big factor here is you are doing the call to substring over a new String each time.
Instead, use substring directly over the stringbuilder
for (int i = 0; i < fastaString.length(); i+=3){
currentFastaAcid = fastaString.substring(i, i + 3);
System.out.println(currentFastaAcid);
}
Also, instead of print the currentFastaAcid each time, save it into a list and print this list at the end
List<String> acids = new LinkedList<String>();
for (int i = 0; i < fastaString.length(); i+=3){
currentFastaAcid = fastaString.substring(i, i + 3);
acids.add(currentFastaAcid);
}
System.out.println(acids.toString());
Your main problem besides the debug output surely is, that you are creating a new String with your completely read data from the file in each iteration of your loop:
currentFastaAcid = fastaString.toString().substring(i, i + 3);
fastaString.toString() will give the same result in each iteration and therefore is redundant. Get it outside the loop and you will surely save some seconds runtime.
Apart from suggested optimization in the serial code, I will go for parallel processing to reduce time further. If you have really big file, you can divide the work of reading file and processing read-lines, in separate threads. That way, when one thread is busy reading nextline from large file, other thread can process read-lines and print them on console.
If you remove the
System.out.println(currentFastaAcid);
line in the for loop, you will gain quite decent time.

Java pairs of symbols program

Using stack data structure(s): If the input file is not balanced, the un-balance cause and the in-file localization details will be supplied. For flexibility reasons, read the balancing pairs of symbols from a text file. Test your program by considering the following pairs of symbols: ( ), { }, [ ], /* */
I'm having trouble with the last requirement: /* */
I also can't seem to grasp how to print the in-file localization details? i.e which line number of the text file the error has occured on?
The text file looks like this:
(()(()
{}}}{}{
[[[]][][]
((}})){{]
()
[]
{}
[]{}
()()()[]
*/ /*
(a+b) = c
The code:
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class P1 {
private boolean match = true;
// The stack
private java.util.Stack<Character> matchStack = new java.util.Stack<Character>();
// What to do with a match
public boolean ismatch() {
return match && matchStack.isEmpty();
}
// Finding a match
public void add(char c) {
Character k = leftSide(c);
if (k == null)
;
else if (k.charValue() == c)
matchStack.push(k);
else {
if (matchStack.isEmpty() || !matchStack.pop().equals(k))
match = false;
}
}
// Add string values
public void add(String s) {
for (int i = 0; i < s.length(); i++)
add(s.charAt(i));
}
// The various symbol pairs
protected static Character leftSide(char c) {
switch (c) {
case '(':
case ')':
return new Character('(');
case '[':
case ']':
return new Character('[');
case '{':
case '}':
return new Character('{');
default:
return null;
}
}
// Main method. Welcome message, read the test file, build the array, print
// results.
public static void main(String args[]) {
List<String[]> arrays = new ArrayList<String[]>();
// Welcome message
System.out
.println("Project #1\n"
+ "Welcome! The following program ensures both elements of various paired symbols are present.\n"
+ "Test Data will appear below: \n"
+ "-------------------------------");
// Read the file
try {
BufferedReader in = new BufferedReader(new FileReader(
"testfile.txt"));
String str;
// Keep reading while there is still more data
while ((str = in.readLine()) != null) {
// Line by line read & add to array
String arr[] = str.split(" ");
if (arr.length > 0)
arrays.add(arr);
// Let the user know the match status (i.e. print the results)
P1 mp = new P1();
mp.add(str);
System.out.print(mp.ismatch() ? "\nSuccessful Match:\n"
: "\nThis match is not complete:\n");
System.out.println(str);
}
in.close();
// Catch exceptions
} catch (FileNotFoundException e) {
System.out
.println("We're sorry, we are unable to find that file: \n"
+ e.getMessage());
} catch (IOException e) {
System.out
.println("We're sorry, we are unable to read that file: \n"
+ e.getMessage());
}
}
}
An easy way to implement this would be using a map of stacks such as Map<String, Stack<Location>>, where Location is a class you create that holds two ints (a line number and a character number). That can be your location info. The key (String) to this map would be your left side (opener) part of your pairs. Every time you have an opener you look up the appropriate Stack in the map and push a new Location on it for that opener. Each time you encounter a closer you look up its opener, use the opener to look up the correct Stack in the map and then pop it once. The reason I say use String for your key is because not all your openers can be represented by Character namely your /* opener, so a String will have to do. Since you can't switch on Strings for your leftSide(char) (which will now be leftSide(String)) function you'll either have to use if-else or use a map (Map<String, String>) to create the closer to opener mappings.
When the end of the file is reached the only Location objects remaining in the Stack objects should be unclosed openers.

Categories

Resources