-1

I had a file to read and with this code I succeeded my JUnit tests. As you can see, I pass the String line as parameter to the readPrevisione(...) method.

package oroscopo.persistence;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.StringTokenizer;

import oroscopo.model.Previsione;
import oroscopo.model.SegnoZodiacale;

public class TextFileOroscopoRepository implements OroscopoRepository {

 private HashMap<String, List<Previsione>> mapSettore = new HashMap<>();


public TextFileOroscopoRepository(Reader baseReader) throws IOException, BadFileFormatException{
    if (baseReader == null)
        throw new IllegalArgumentException("baseReader is null");
    BufferedReader bufReader = new BufferedReader(baseReader);
    String line;
    while((line=bufReader.readLine()) != null){
        readPrevisione(line,bufReader);
    }



}

private void readPrevisione(String line, BufferedReader bufReader) throws IOException, BadFileFormatException{
    String nomeSettore = line.trim();
    if (!Character.isUpperCase(nomeSettore.charAt(0)))
        throw new BadFileFormatException();
    List<Previsione> listaPrev = new ArrayList<>();
    while (!(line = bufReader.readLine()).equalsIgnoreCase("FINE")){
        try{
        StringTokenizer st1 = new StringTokenizer(line, "\t");
        if(st1.countTokens() < 2)
            throw new BadFileFormatException();
        String prev = st1.nextToken("\t").trim();
        int val = Integer.parseInt(st1.nextToken("\t").trim());
        Set<SegnoZodiacale> segni = new HashSet<>();
        if (st1.hasMoreTokens()){
            while(st1.hasMoreTokens()){
                try{
                segni.add(SegnoZodiacale.valueOf(st1.nextToken(",").trim()));
                }
                catch (IllegalArgumentException e){
                throw new BadFileFormatException();
                }
            }
            Previsione p = new Previsione(prev,val,segni);
            listaPrev.add(p);   
        }
        else{
            Previsione p2 = new Previsione(prev,val);
            listaPrev.add(p2);  
        }
        }
        catch (NumberFormatException e){
            throw new BadFileFormatException();
        }
        catch (NoSuchElementException e){
            throw new BadFileFormatException();
        }   

    }
    mapSettore.put(nomeSettore, listaPrev); 
}

@Override
public Set<String> getSettori() {
    return mapSettore.keySet();
}

@Override
public List<Previsione> getPrevisioni(String settore) {
    return mapSettore.get(settore.toUpperCase());
    }
}

Here with the same code, instead passing the read line as parameter, I pass the StringTokenizer that already has read the line. It should work like above but my JUnit tests fail. What did I do wrong?

package oroscopo.persistence;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.StringTokenizer;

import oroscopo.model.Previsione;
import oroscopo.model.SegnoZodiacale;

public class TextFileOroscopoRepository implements OroscopoRepository {

 private HashMap<String, List<Previsione>> mapSettore = new HashMap<>();


public TextFileOroscopoRepository(Reader baseReader) throws IOException, BadFileFormatException{
    if (baseReader == null)
        throw new IllegalArgumentException("baseReader is null");
    BufferedReader bufReader = new BufferedReader(baseReader);
    String line;
    while((line=bufReader.readLine()) != null){
        StringTokenizer st = new StringTokenizer(line);
        readPrevisione(st,bufReader);
    }



}

private void readPrevisione(StringTokenizer st, BufferedReader bufReader) throws IOException, BadFileFormatException{
    String nomeSettore = st.nextToken().trim();
    if (!Character.isUpperCase(nomeSettore.charAt(0)))
        throw new BadFileFormatException();
    List<Previsione> listaPrev = new ArrayList<>();
    String line;
    while (!(line = bufReader.readLine()).equalsIgnoreCase("FINE")){
        try{
        StringTokenizer st1 = new StringTokenizer(line, "\t");
        if(st1.countTokens() < 2)
            throw new BadFileFormatException();
        String prev = st1.nextToken("\t").trim();
        int val = Integer.parseInt(st1.nextToken("\t").trim());
        Set<SegnoZodiacale> segni = new HashSet<>();
        if (st1.hasMoreTokens()){
            while(st1.hasMoreTokens()){
                try{
                segni.add(SegnoZodiacale.valueOf(st1.nextToken(",").trim()));
                }
                catch (IllegalArgumentException e){
                throw new BadFileFormatException();
                }
            }
            Previsione p = new Previsione(prev,val,segni);
            listaPrev.add(p);   
        }
        else{
            Previsione p2 = new Previsione(prev,val);
            listaPrev.add(p2);  
        }
        }
        catch (NumberFormatException e){
            throw new BadFileFormatException();
        }
        catch (NoSuchElementException e){
            throw new BadFileFormatException();
        }   

    }
    mapSettore.put(nomeSettore, listaPrev); 
}

@Override
public Set<String> getSettori() {
    return mapSettore.keySet();
}

@Override
public List<Previsione> getPrevisioni(String settore) {
    return mapSettore.get(settore.toUpperCase());
    }

}

EDIT: Here is the File.txt that I want to read.

And here is an example of one of my JUnit test:

@Test
public void testLetturaCorrettaPrevisioni1() throws IOException, BadFileFormatException {
    Reader mr = new StringReader(
            "NOMESEZIONE\navrai la testa un po' altrove\t\t4\tARIETE,TORO,GEMELLI\ngrande intimita'\t9\nFINE\n"
                    + "SEZIONE2\ntesto di prova\t\t\t\t\t66\t\nFINE");

    OroscopoRepository or = new TextFileOroscopoRepository(mr);

    assertEquals("avrai la testa un po' altrove", or.getPrevisioni("nomesezione").get(0).getPrevisione());
    assertEquals(4, or.getPrevisioni("nomesezione").get(0).getValore());
    Set<SegnoZodiacale> validi = new HashSet<SegnoZodiacale>() {
        private static final long serialVersionUID = 1L;

        {
            add(SegnoZodiacale.ARIETE);
            add(SegnoZodiacale.TORO);
            add(SegnoZodiacale.GEMELLI);
        }
    };
    for (SegnoZodiacale s : SegnoZodiacale.values()) {
        if (validi.contains(s))
            assertTrue(or.getPrevisioni("nomesezione").get(0).validaPerSegno(s));
        else
            assertFalse(or.getPrevisioni("nomesezione").get(0).validaPerSegno(s));
    }

    assertEquals("grande intimita'", or.getPrevisioni("nomesezione").get(1).getPrevisione());
    assertEquals(9, or.getPrevisioni("nomesezione").get(1).getValore());
    for (SegnoZodiacale s : SegnoZodiacale.values()) {
        assertTrue(or.getPrevisioni("nomesezione").get(1).validaPerSegno(s));
    }
}
Sharkbyte
  • 21
  • 9
  • Both versions contain a potential NPE if `readLine()` returns an unexpected null. – user207421 Jun 01 '17 at 10:33
  • If readLine() returns null it means that the file.txt is empty.. In the Controller section of the program (not showed here), if the HashMap is empty it throws an exception.. But anyway that's not the point.. The problem is between StringTokenizer and String passed as parameters. – Sharkbyte Jun 01 '17 at 11:25
  • It means the file is empty *or* doesn't contain a `"FINE"` line. I posted that as a comment, not an answer. – user207421 Jun 01 '17 at 11:40
  • You're right! I've forgotten about the "FINE" line! Thanks! – Sharkbyte Jun 01 '17 at 11:43

2 Answers2

0

You are creating StringTokenizer with default delimiter, that is, "the space character, the tab character, the newline character, the carriage-return character, and the form-feed character."

So in the first case you set as value of the "nomeSettore" variable the whole line but when you use StringTokenizer.nextToken() you are giving to "nomeSettore" just the value of the first token. So, "nomeSettore" can have different values if your String "line" contains whitespaces and you will have different key-value pairs inside you map.

You can take a look at this example:

public class TestSO {

public static void main(String[] args) {
    String line = "abcdfs faf afd fa";
    StringTokenizer st = new StringTokenizer(line);
    readPrevisione(st, null);
    readPrevisione(line, null);
}

private static void readPrevisione(StringTokenizer st, BufferedReader bufReader) {
    String nomeSettore = st.nextToken().trim();
    System.out.println(nomeSettore);
}

private static void readPrevisione(String st, BufferedReader bufReader) {
    String nomeSettore = st.trim();
    System.out.println(nomeSettore);
}

}

It prints as output:

abcdfs
abcdfs faf afd fa
Davis Molinari
  • 741
  • 1
  • 5
  • 20
  • Yes but.. The first line is "EXAMPLE\n" ... So I don't understand the error because it should work with both versions and the code is the same.. Just changes the String line and StringTokenizer parameters – Sharkbyte Jun 01 '17 at 11:16
  • What is your JUnit testing? Is it on the full content of the map? Is it on a single line? It's all about what are you actually testing. – Davis Molinari Jun 01 '17 at 11:20
  • I edited with the File.txt to read and relative format of String. – Sharkbyte Jun 01 '17 at 11:36
0

I've understood why it didn't work.. The String line was : "EXAMPLE\n" but after

while((line=bufReader.readLine()) != null){
...}

line = "EXAMPLE" because the readLine() eats the newline. So I passed to the readPrevisione() a StringTokenizer as parameter

while((line=bufReader.readLine()) != null){
    StringTokenizer st = new StringTokenizer(line);
    readPrevisione(st,bufReader);
}

private void readPrevisione(StringTokenizer st, BufferedReader bufReader) throws IOException, BadFileFormatException{
String nomeSettore = st.nextToken().trim();
...}

And st.nextToken() search for a \n that is not contained in "EXAMPLE". That's why it didn't work.

Sharkbyte
  • 21
  • 9