1   package fi.jyu.mit.ohj2;
2   
3   import java.io.*;
4   import java.util.Scanner;
5   
6   /**
7    * Aliohjelma kahden tekstitiedoston vertaamiseksi.
8    * Käyttö lähinnä JUnit testeissä esim. seuraavasti:
9    * <pre>
10   *     VertaaTiedosto.kirjoitaTiedosto("hiljaa.txt", 
11   *         "33 hiljaa 1 hiipii\n"+
12   *         "hyvä 33 tulee\n"+
13   *         "36 1 3 5 55\n"+
14   *         "nyt 33 riittää\n");    
15   *     VertaaTiedosto.kirjoitaTiedosto("hiljaayli30.txt", 
16   *         "33 hiljaa 1 hiipii\n"+
17   *         "36 1 3 5 55\n");
18   *     VertaaTiedosto.tuhoaTiedosto("tulos.txt");
19   *     TulYli30.main(new String[]{"hiljaa.txt","tulos.txt"});
20   *     VertaaTiedosto.vertaaFileFile("tulos.txt","hiljaayli30.txt") === null;
21   *     VertaaTiedosto.tuhoaTiedosto("hiljaa.txt");
22   *     VertaaTiedosto.tuhoaTiedosto("hiljaayli30.txt");
23   * </pre>
24   * @author vesal
25   * @version 10.3.2007
26   */
27  public class VertaaTiedosto { // NOPMD Cyclomatic
28      /**
29       * Verrataan kahta tekstitiedostoa ja heti kun tulee ensimmäin poikkeava 
30       * rivi palautetaanvirhe.  Lopussa olevat pelkkä yksi rivi ei tee eroa.
31       * @param nimi1 1. verrattavan tiedoston nimi
32       * @param nimi2 2. verrattavan tiedoston nimi
33       * @return ensimmäinen eroavaisuus joka löytyy. Null jos ei eroja.
34       * @throws IOException jos lukemisessa tapahtuu virhe.
35       * @example
36       * <pre name="test">
37       * #THROWS IOException
38       * #import java.io.*;
39       * #STATICIMPORT
40       *     kirjoitaTiedosto("hiljaa1.txt", "33 hiljaa 1 hiipii\nhyvä 33 tulee\n");
41       *         
42       *     kirjoitaTiedosto("hiljaa2.txt", "33 hiljaa 1 hiipii\nhyvä 33 tulee\n"); 
43       *     vertaaFileFile("hiljaa1.txt","hiljaa2.txt") === null;
44       *
45       *     kirjoitaTiedosto("hiljaa2.txt", "33 hiljaa 1 hiipii\nhyvä 33 tulee"); 
46       *     vertaaFileFile("hiljaa1.txt","hiljaa2.txt") === null;
47       *
48       *     kirjoitaTiedosto("hiljaa2.txt", "33 hiljaa 1 hiipii\nhyvä 33 tulee\n\n\n");
49       *     vertaaFileFile("hiljaa1.txt","hiljaa2.txt") === "Rivi 3: hiljaa1.txt loppui ensin, hiljaa2.txt on ";
50       *
51       *     kirjoitaTiedosto("hiljaa2.txt", "33 hiljaa 1 hiipii\nhyvä 34 tulee\n");
52       *     vertaaFileFile("hiljaa1.txt","hiljaa2.txt") === "Ero riveissä 2: hyvä 33 tulee ja hyvä 34 tulee";
53       *     
54       *     kirjoitaTiedosto("hiljaa2.txt", "33 hiljaa 1 hiipii\n");
55       *     vertaaFileFile("hiljaa1.txt","hiljaa2.txt") === "Rivi 2: hiljaa2.txt loppui ensin, hiljaa1.txt on hyvä 33 tulee";
56       *
57       *     kirjoitaTiedosto("hiljaa2.txt", "33 hiljaa 1 hiipii\nhyvä 33 tulee\nja 34 tulee\n");
58       *     vertaaFileFile("hiljaa1.txt","hiljaa2.txt") === "Rivi 3: hiljaa1.txt loppui ensin, hiljaa2.txt on ja 34 tulee";
59       *     vertaaFileFile("hiljaa1.txt","hiljaa3.txt") === "Tiedosto ei aukea: hiljaa3.txt";
60       *     vertaaFileFile("hiljaa4.txt","hiljaa2.txt") === "Tiedosto ei aukea: hiljaa4.txt";
61       *     
62       *     tuhoaTiedosto("hiljaa1.txt");
63       *     tuhoaTiedosto("hiljaa2.txt");
64       * 
65       * </pre>
66       */
67      @SuppressWarnings({ "resource", "null" }) // finally hoitaa sulkemisen, s1 ei ole null s1.compare-lauseessa
68      public static String vertaaFileFile(String nimi1, String nimi2) throws IOException { // NOPMD
69          BufferedReader f1 = null;
70          BufferedReader f2 = null;
71          try {
72              f1 = Tiedosto.avaa_lukemista_varten(nimi1);
73              f2 = Tiedosto.avaa_lukemista_varten(nimi2);
74              if (f1 == null) return "Tiedosto ei aukea: " + nimi1;
75              if (f2 == null) return "Tiedosto ei aukea: " + nimi2;
76              int n = 1;
77  
78              while (true) {
79                  String s1 = f1.readLine();
80                  String s2 = f2.readLine();
81                  if (s1 == null && s2 == null) return null;
82                  if (s1 != null && s2 == null) return "Rivi " + n + ": " + nimi2 + " loppui ensin, " + nimi1 + " on " + s1; // NOPMD
83                  if (s1 == null && s2 != null) return "Rivi " + n + ": " + nimi1 + " loppui ensin, " + nimi2 + " on " + s2; // NOPMD
84                  if (s1.compareTo(s2) != 0) return "Ero riveissä " + n + ": " + s1 + " ja " + s2; // NOPMD
85                  n++;
86              }
87          } finally {
88              if (f1 != null) f1.close();
89              if (f2 != null) f2.close();
90          }
91      }
92  
93  
94      /**
95       * Verrataan tekstitiedostoa merkkijonoon ja heti kun tulee ensimmäinen poikkeava 
96       * rivi palautetaan virhe.  Lopussa olevat pelkkä yksi tyhkä rivi eo tee eroa.
97       * @param nimi1 1. verrattavan tiedoston nimi
98       * @param ss2 2. verrattava sisältö
99       * @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 }
342