001package fi.jyu.mit.ohj2;
002
003import java.io.*;
004import java.util.Scanner;
005
006/**
007 * Aliohjelma kahden tekstitiedoston vertaamiseksi.
008 * Käyttö lähinnä JUnit testeissä esim. seuraavasti:
009 * <pre>
010 *     VertaaTiedosto.kirjoitaTiedosto("hiljaa.txt", 
011 *         "33 hiljaa 1 hiipii\n"+
012 *         "hyvä 33 tulee\n"+
013 *         "36 1 3 5 55\n"+
014 *         "nyt 33 riittää\n");    
015 *     VertaaTiedosto.kirjoitaTiedosto("hiljaayli30.txt", 
016 *         "33 hiljaa 1 hiipii\n"+
017 *         "36 1 3 5 55\n");
018 *     VertaaTiedosto.tuhoaTiedosto("tulos.txt");
019 *     TulYli30.main(new String[]{"hiljaa.txt","tulos.txt"});
020 *     VertaaTiedosto.vertaaFileFile("tulos.txt","hiljaayli30.txt") === null;
021 *     VertaaTiedosto.tuhoaTiedosto("hiljaa.txt");
022 *     VertaaTiedosto.tuhoaTiedosto("hiljaayli30.txt");
023 * </pre>
024 * @author vesal
025 * @version 10.3.2007
026 */
027public class VertaaTiedosto { // NOPMD Cyclomatic
028    /**
029     * Verrataan kahta tekstitiedostoa ja heti kun tulee ensimmäin poikkeava 
030     * rivi palautetaanvirhe.  Lopussa olevat pelkkä yksi rivi ei tee eroa.
031     * @param nimi1 1. verrattavan tiedoston nimi
032     * @param nimi2 2. verrattavan tiedoston nimi
033     * @return ensimmäinen eroavaisuus joka löytyy. Null jos ei eroja.
034     * @throws IOException jos lukemisessa tapahtuu virhe.
035     * @example
036     * <pre name="test">
037     * #THROWS IOException
038     * #import java.io.*;
039     * #STATICIMPORT
040     *     kirjoitaTiedosto("hiljaa1.txt", "33 hiljaa 1 hiipii\nhyvä 33 tulee\n");
041     *         
042     *     kirjoitaTiedosto("hiljaa2.txt", "33 hiljaa 1 hiipii\nhyvä 33 tulee\n"); 
043     *     vertaaFileFile("hiljaa1.txt","hiljaa2.txt") === null;
044     *
045     *     kirjoitaTiedosto("hiljaa2.txt", "33 hiljaa 1 hiipii\nhyvä 33 tulee"); 
046     *     vertaaFileFile("hiljaa1.txt","hiljaa2.txt") === null;
047     *
048     *     kirjoitaTiedosto("hiljaa2.txt", "33 hiljaa 1 hiipii\nhyvä 33 tulee\n\n\n");
049     *     vertaaFileFile("hiljaa1.txt","hiljaa2.txt") === "Rivi 3: hiljaa1.txt loppui ensin, hiljaa2.txt on ";
050     *
051     *     kirjoitaTiedosto("hiljaa2.txt", "33 hiljaa 1 hiipii\nhyvä 34 tulee\n");
052     *     vertaaFileFile("hiljaa1.txt","hiljaa2.txt") === "Ero riveissä 2: hyvä 33 tulee ja hyvä 34 tulee";
053     *     
054     *     kirjoitaTiedosto("hiljaa2.txt", "33 hiljaa 1 hiipii\n");
055     *     vertaaFileFile("hiljaa1.txt","hiljaa2.txt") === "Rivi 2: hiljaa2.txt loppui ensin, hiljaa1.txt on hyvä 33 tulee";
056     *
057     *     kirjoitaTiedosto("hiljaa2.txt", "33 hiljaa 1 hiipii\nhyvä 33 tulee\nja 34 tulee\n");
058     *     vertaaFileFile("hiljaa1.txt","hiljaa2.txt") === "Rivi 3: hiljaa1.txt loppui ensin, hiljaa2.txt on ja 34 tulee";
059     *     vertaaFileFile("hiljaa1.txt","hiljaa3.txt") === "Tiedosto ei aukea: hiljaa3.txt";
060     *     vertaaFileFile("hiljaa4.txt","hiljaa2.txt") === "Tiedosto ei aukea: hiljaa4.txt";
061     *     
062     *     tuhoaTiedosto("hiljaa1.txt");
063     *     tuhoaTiedosto("hiljaa2.txt");
064     * 
065     * </pre>
066     */
067    @SuppressWarnings({ "resource", "null" }) // finally hoitaa sulkemisen, s1 ei ole null s1.compare-lauseessa
068    public static String vertaaFileFile(String nimi1, String nimi2) throws IOException { // NOPMD
069        BufferedReader f1 = null;
070        BufferedReader f2 = null;
071        try {
072            f1 = Tiedosto.avaa_lukemista_varten(nimi1);
073            f2 = Tiedosto.avaa_lukemista_varten(nimi2);
074            if (f1 == null) return "Tiedosto ei aukea: " + nimi1;
075            if (f2 == null) return "Tiedosto ei aukea: " + nimi2;
076            int n = 1;
077
078            while (true) {
079                String s1 = f1.readLine();
080                String s2 = f2.readLine();
081                if (s1 == null && s2 == null) return null;
082                if (s1 != null && s2 == null) return "Rivi " + n + ": " + nimi2 + " loppui ensin, " + nimi1 + " on " + s1; // NOPMD
083                if (s1 == null && s2 != null) return "Rivi " + n + ": " + nimi1 + " loppui ensin, " + nimi2 + " on " + s2; // NOPMD
084                if (s1.compareTo(s2) != 0) return "Ero riveissä " + n + ": " + s1 + " ja " + s2; // NOPMD
085                n++;
086            }
087        } finally {
088            if (f1 != null) f1.close();
089            if (f2 != null) f2.close();
090        }
091    }
092
093
094    /**
095     * Verrataan tekstitiedostoa merkkijonoon ja heti kun tulee ensimmäinen poikkeava 
096     * rivi palautetaan virhe.  Lopussa olevat pelkkä yksi tyhkä rivi eo tee eroa.
097     * @param nimi1 1. verrattavan tiedoston nimi
098     * @param ss2 2. verrattava sisältö
099     * @return ensimmäinen eroavaisuus joka löytyy. Null jos ei eroja.
100     * @throws IOException jos lukemisessa tapahtuu virhe.
101     * @example
102     * <pre name="test">
103     * #THROWS IOException
104     * #import java.io.*;
105     * #STATICIMPORT
106     *     kirjoitaTiedosto("hiljaa1.txt", "33 hiljaa 1 hiipii\nhyvä 33 tulee\n");
107     *         
108     *     vertaaFileString("hiljaa1.txt","33 hiljaa 1 hiipii\nhyvä 33 tulee\n") === null;
109     *
110     *     vertaaFileString("hiljaa1.txt","33 hiljaa 1 hiipii\nhyvä 33 tulee") === null;
111     *
112     *     vertaaFileString("hiljaa1.txt","33 hiljaa 1 hiipii\nhyvä 33 tulee\n\n") === "Rivi 3: hiljaa1.txt loppui ensin, jono on ";
113     *
114     *     vertaaFileString("hiljaa1.txt","33 hiljaa 1 hiipii\nhyvä 33 tulee\n\n\n") === "Rivi 3: hiljaa1.txt loppui ensin, jono on ";
115     *
116     *     vertaaFileString("hiljaa1.txt","33 hiljaa 1 hiipii\nhyvä 34 tulee\n") === "Ero riveissä 2: hyvä 33 tulee ja hyvä 34 tulee";
117     *     
118     *     vertaaFileString("hiljaa1.txt","33 hiljaa 1 hiipii\n") === "Rivi 2: Jono loppui ensin, hiljaa1.txt on hyvä 33 tulee";
119     *
120     *     vertaaFileString("hiljaa1.txt","33 hiljaa 1 hiipii\nhyvä 33 tulee\nja 34 tulee\n") === "Rivi 3: hiljaa1.txt loppui ensin, jono on ja 34 tulee";
121     *     vertaaFileString("hiljaa1.txt",null) === "Jono on null"; 
122     *     vertaaFileString("hiljaa4.txt","hiljaa2.txt") === "Tiedosto ei aukea: hiljaa4.txt";
123     *     
124     *     tuhoaTiedosto("hiljaa1.txt");
125     * 
126     * </pre>
127     */
128    @SuppressWarnings({ "null", "resource" }) // finally sulkee, s1 ei ole null s1.compare-lauseessa
129    public static String vertaaFileString(String nimi1, String ss2) throws IOException { // NOPMD
130        if (ss2 == null) return "Jono on null";
131        BufferedReader f1 = null;
132        @SuppressWarnings("resource")
133                Scanner f2 = new Scanner(ss2);
134        int n = 1;
135        try {
136            f1 = Tiedosto.avaa_lukemista_varten(nimi1);
137            if (f1 == null) return "Tiedosto ei aukea: " + nimi1;
138
139            while (true) {
140                String s1 = f1.readLine();
141                boolean b2 = f2.hasNextLine();
142                String s2 = null;
143                if (b2) s2 = f2.nextLine();
144                if (s1 == null && !b2) return null;
145                if (s1 != null && !b2) return "Rivi " + n + ": " + "Jono loppui ensin, " + nimi1 + " on " + s1;
146                if (s1 == null && b2) return "Rivi " + n + ": " + nimi1 + " loppui ensin, " + "jono on " + s2;
147                if (s1.compareTo(s2) != 0) return "Ero riveissä " + n + ": " + s1 + " ja " + s2;
148                n++;
149            }
150        } finally {
151            if (f1 != null) f1.close();
152            if (f2 != null) f2.close();
153        }
154    }
155
156
157    /**
158     * Verrataan kahta tekstitiedoston kaltaista merkkijonoa ja heti kun tulee ensimmäinen poikkeava 
159     * rivi palautetaan virhe.  Lopussa oleva yksi tyhjä rivi ei tee eroa.
160     * @param ss1 1. verrattava sisältö
161     * @param ss2 2. verrattava sisältö
162     * @return ensimmäinen eroavaisuus joka löytyy. Null jos ei eroja.
163     * @example
164     * <pre name="test">
165     * #THROWS IOException
166     * #import java.io.*;
167     * #STATICIMPORT
168     *     vertaaString2("","\n") === "Rivi 1: 1. loppui ensin, 2. on ";
169     *     vertaaString2("kissa\n","kissa") === null;
170     *     vertaaString2("\n\n","\n") === "Rivi 2: 2. loppui ensin, 1. on ";
171     *     vertaaString2("\r\n","\n") === null;
172     *     vertaaString2("\r\n","\n\n") === "Rivi 2: 1. loppui ensin, 2. on ";
173     *     vertaaString2("33 hiljaa 1 hiipii\r\nhyvä 33 tulee\r\n","33 hiljaa 1 hiipii\nhyvä 33 tulee\n") === null;
174     *     vertaaString2("a b","a c") === "Ero riveissä 1: a b ja a c"
175     *     vertaaString2(null,null) === null;
176     *     vertaaString2(null," ") === "1. on null";
177     *     vertaaString2(" ",null) === "2. on null";
178     * </pre>
179     */
180    @SuppressWarnings({ "null", "resource" })
181    public static String vertaaString2(String ss1, String ss2) { // NOPMD
182        if (ss1 == null & ss2 == null) return null;
183        if (ss1 == null) return "1. on null";
184        if (ss2 == null) return "2. on null";
185        Scanner f1 = new Scanner(ss1);
186        Scanner f2 = new Scanner(ss2);
187        int n = 1;
188        try {
189        while (true) {
190            String s1 = null;
191            String s2 = null;
192            boolean b1 = f1.hasNextLine();
193            boolean b2 = f2.hasNextLine();
194            if (b1) s1 = f1.nextLine();
195            if (b2) s2 = f2.nextLine();
196            if (!b1 && !b2) return null;
197            if (b1 && !b2) return "Rivi " + n + ": " + "2. loppui ensin, 1. on " + s1;
198            if (!b1 && b2) return "Rivi " + n + ": " + "1. loppui ensin, 2. on " + s2;
199            if (s1.compareTo(s2) != 0) return "Ero riveissä " + n + ": " + s1 + " ja " + s2;
200            n++;
201        }
202        } finally {
203                f1.close();
204                f2.close();
205        }
206    }
207
208
209    /**
210     * Verrataan kahta tekstitiedoston kaltaista merkkijonoa ja heti kun tulee ensimmäinen poikkeava 
211     * rivi palautetaan virhe.  Lopussa olevat pelkät tyhjät rivit merkitsevät.
212     * @param ss1 1. verrattava sisältö
213     * @param ss2 2. verrattava sisältö
214     * @return ensimmäinen eroavaisuus joka löytyy. Null jos ei eroja.
215     * @example
216     * <pre name="test">
217     * #THROWS IOException
218     * #import java.io.*;
219     * #STATICIMPORT
220     *     vertaaString("kissa\n","kissa") === "Rivi 2: 2. loppui ensin, 1. on ";
221     *     vertaaString("","\n") === "Rivi 1: 1. loppui ensin, 2. on ";
222     *     vertaaString("\n\n","\n") === "Rivi 3: 2. loppui ensin, 1. on ";
223     *     vertaaString("\r\n","\n") === null;
224     *     vertaaString("\r","\n")   === null;
225     *     vertaaString("\r\n","\n\n") === "Rivi 3: 1. loppui ensin, 2. on ";
226     *     vertaaString("33 hiljaa 1 hiipii\r\nhyvä 33 tulee\r\n","33 hiljaa 1 hiipii\nhyvä 33 tulee\n") === null;
227     *     vertaaString("a b","a c") === "Ero riveissä 1: a b ja a c"
228     *     vertaaString((String)null,null) === null;
229     *     vertaaString((String)null," ") === "1. on null";
230     *     vertaaString(" ",null) === "2. on null";
231     * </pre>
232     */
233    @SuppressWarnings("null")
234    public static String vertaaString(String ss1, String ss2) { // NOPMD
235        String st1 = ss1;
236        String st2 = ss2;
237        if (st1 == null & st2 == null) return null;
238        if (st1 == null) return "1. on null";
239        if (st2 == null) return "2. on null";
240        st1 = st1.replaceAll("\\r\\n", "\n");
241        st2 = st2.replaceAll("\\r\\n", "\n");
242        st1 = st1.replaceAll("\\r", "\n");
243        st2 = st2.replaceAll("\\r", "\n"); // Toki nyt voitaisiin verrata
244                                           // pelkkiä jonoja sellaisenaan
245        Erottelija f1 = new Erottelija(st1, "\n");
246        Erottelija f2 = new Erottelija(st2, "\n");
247        int n = 1;
248        while (true) {
249            String s1 = null;
250            String s2 = null;
251            boolean b1 = f1.hasMoreTokens();
252            boolean b2 = f2.hasMoreTokens();
253            if (b1) s1 = f1.nextToken();
254            if (b2) s2 = f2.nextToken();
255            if (!b1 && !b2) return null;
256            if (b1 && !b2) return "Rivi " + n + ": " + "2. loppui ensin, 1. on " + s1;
257            if (!b1 && b2) return "Rivi " + n + ": " + "1. loppui ensin, 2. on " + s2;
258            if (s1.compareTo(s2) != 0) return "Ero riveissä " + n + ": " + s1 + " ja " + s2;
259            n++;
260        }
261    }
262
263
264    /**
265     * Verrataan keskenään merkkijonotietovirtaa ja merkkijon sisältöä.
266     * Kakkki rivit (myös tyhjät) pitää olla samalla tavalla.
267     * bs tyhjennetään vertailun jälkeen.
268     * @param bs verrattava merkkijonotietovirta
269     * @param ss2 merkkijono johon verrataan
270     * @return null jos samat, muuten eroava rivi
271     * @example
272     * <pre name="test">
273     * #import java.io.ByteArrayOutputStream;
274     *   ByteArrayOutputStream bs  = new ByteArrayOutputStream();
275     *   PrintStream out = new PrintStream(bs);
276     *   out.println("kissa\n");
277     *   vertaaString(bs,"kissa") === "Rivi 2: 2. loppui ensin, 1. on ";
278     *   vertaaString(bs,"\n") === "Rivi 1: 1. loppui ensin, 2. on ";
279     *   out.println("\n\n");
280     *   vertaaString(bs,"\n") === "Rivi 3: 2. loppui ensin, 1. on ";
281     *   out.println("\r");  vertaaString(bs,"\n\n") === null;
282     *   out.println("33 hiljaa 1 hiipii\r\nhyvä 33 tulee");
283     *   vertaaString(bs,"33 hiljaa 1 hiipii\nhyvä 33 tulee\n") === null;
284     *   out.print("a b");
285     *   vertaaString(bs,"a c") === "Ero riveissä 1: a b ja a c"
286     *   vertaaString(bs,null) === "2. on null";
287     * </pre>
288     */
289    public static String vertaaString(ByteArrayOutputStream bs, String ss2) { // NOPMD
290       String ero = vertaaString(bs.toString(), ss2);
291       bs.reset();
292       return ero;
293    }
294
295
296    /**
297     * Kirjoitetaan tiedostoon sisältö
298     * @param nimi tiedoston nimi johon kirjoitetaan
299     * @param sisalto merkkijono joka kirjoitetaan tiedostoon
300     * @throws IOException jos tiedosto ei aukea.
301     * @example
302     * <pre name="test">
303     * #THROWS IOException
304     *     kirjoitaTiedosto("hiljaa1.txt", "33 hiljaa 1 hiipii\nhyvä 33 tulee\n");
305     *     tuhoaTiedosto("hiljaa1.txt");
306     *     kirjoitaTiedosto("ö:\\ö.ö", "33 hiljaa 1 hiipii\nhyvä 33 tulee\n"); #THROWS IOException
307     * </pre>
308     */
309    public static void kirjoitaTiedosto(String nimi, String sisalto) throws IOException {
310        @SuppressWarnings("resource") // suljetaan finalyssä
311        PrintWriter out = null;
312        try {
313            out = new PrintWriter(new FileWriter(nimi));
314            out.write(sisalto);
315            out.close();
316        } finally {
317            if (out != null) out.close();
318        }
319    }
320
321
322    /**
323     * Tuhotaan tiedosto levyltä
324     * @param nimi tuhottavan tiedoston nimi
325     */
326    public static void tuhoaTiedosto(String nimi) {
327        File f = new File(nimi);
328        f.delete();
329    }
330
331    /**
332     * Testataan tiedostojen vertaamista
333     * @param args ei käytössä
334     * @throws IOException jos tulee virhe
335     */
336    /*
337     * public static void main(String[] args) throws IOException { String koe =
338     * vertaaFileFile("fi/jyu/mit/ohj2/IO.java","fi/jyu/mit/ohj2/Syotto.java");
339     * System.out.println(koe); }
340     */
341}