001package fi.jyu.mit.ohj2;
002
003import java.io.ByteArrayInputStream;
004import java.io.ByteArrayOutputStream;
005import java.io.FileInputStream;
006import java.io.FileNotFoundException;
007import java.io.FileOutputStream;
008import java.io.IOException;
009import java.io.InputStream;
010import java.io.OutputStream;
011import java.io.PrintStream;
012
013/**
014 * Luokka tietovirtojen uudelleen suuntaamiseksi
015 * @author vesal
016 * 
017 */
018public class Suuntaaja { // NOPMD -luokkakirjasto
019// #STATICIMPORT
020// #import fi.jyu.mit.ohj2.*;
021    
022    /**
023     * Rajapinta suuntajalle
024     * @author vesal
025     *
026     */
027    public interface ISuuntaaja {
028        
029        /** 
030         * Palauttaa suuntauksen alkuperäiseen tilaan. 
031         */
032        void palauta();
033    }
034    
035    /**
036     * Käytössä olevan systeemin rivinvahdon merkkkijono
037     */
038    private static String NL = getNL(); // NOPMD - tarkoituksella lyhyt nimi
039    
040    
041    /**
042     * Palauttaa systeemin käytössä olevan newline jonon
043     * @return rivinvaihdon merkkiyhdistelmä käytetyssä järjestelmässä
044     */
045    public static String getNL() {
046        if ( NL != null ) return NL;
047        NL = "";
048        StringOutput so = new StringOutput();
049        System.out.println();
050        NL = so.toString();
051        so.palauta();
052        return NL;
053    }
054    
055    
056    /**
057     * Suuntaa inputin uudelleen ja kertoo Syotto-luokalle
058     * @param is uusi tietovirta syötölle
059     *  
060     */
061    protected static void setIn(InputStream is) {
062       System.setIn(is);
063       Syotto.alusta();
064       Readkey.init();
065    }
066    
067    /**
068     * Luokka jolla System.in otetaan tiedostosta
069     * @author vesal
070     * @version 11.3.2007
071     */
072    public static class Input implements ISuuntaaja {
073      private  static final InputStream origIn  = System.in; // NOPMD - ei ole vakio
074      private InputStream stdin = null;
075      
076      /**
077       * Asetetaan peruslukuvirta eri tiedostoon.
078       * Jos nimi on null, niin sitä virtaa ei suunnata uudelleen
079       * @param inNimi mistä tiedostosta System.in luetaan
080       * @throws FileNotFoundException jos tiedostoa ei saada käyttöön
081       * @example
082       * <pre name="test">
083       * #THROWS IOException
084       * #import java.io.*;
085       * #import java.util.*;
086       * #import static fi.jyu.mit.ohj2.VertaaTiedosto.*;
087       *   kirjoitaTiedosto("hiljaa1.txt", "33 hiljaa 1 hiipii\nhyvä 33 tulee\n");
088       *   Input in = new Input("hiljaa1.txt");
089       *   try ( Scanner sc = new Scanner(System.in) ) {
090       *   sc.nextLine() === "33 hiljaa 1 hiipii"; 
091       *   sc.nextLine() === "hyvä 33 tulee";
092       *   sc.hasNextLine() === false;
093       *   in.palauta();
094       *   tuhoaTiedosto("hiljaa1.txt"); 
095       *   in = new Input(null);
096       *   in.palauta();
097       *   }
098       * </pre>
099       */
100      public Input(String inNimi) throws FileNotFoundException {
101        // getNL();
102        if ( inNimi != null ) {
103          stdin = new FileInputStream(inNimi);
104          setIn(stdin);
105        }
106      }
107      
108      /**
109       * Palautetaan tietovirta takaisin alkuperäiseen tilaan
110       */
111      @Override
112      public void palauta() {
113        if ( stdin != null ) {
114          try {
115            stdin.close();
116          } catch (IOException e) { // NOPMD
117          }
118          setIn(origIn);
119        }
120      }
121      
122    }
123
124    /**
125     * Luokka jolla System.out suunnataan toiseen tiedostoon
126     * @author vesal
127     * @version 11.3.2007
128     */
129    public static class Output  implements ISuuntaaja  {
130      private PrintStream origOut = System.out; 
131      private boolean syserr = false;
132      private PrintStream stdout = null;
133      
134      /**
135       * Asetetaan perustulostusvirta eri tiedostoon.
136       * Jos nimi on null, niin sitä virtaa ei suunnata uudelleen
137       * @param outNimi mihin System.out kirjoitetaan
138       * @throws FileNotFoundException jos tiedostoa ei saada käyttöön
139       * @example
140       * <pre name="test">
141       * #THROWS IOException
142       * #import java.io.*;
143       * #import java.util.*;
144       * #import static fi.jyu.mit.ohj2.VertaaTiedosto.*;
145       *   Output out = new Output("hiljaa1.txt");
146       *   System.out.println("eka");
147       *   System.out.println("toka");
148       *   out.palauta();
149       *   vertaaFileString("hiljaa1.txt","eka\ntoka\n") === null;
150       *   tuhoaTiedosto("hiljaa1.txt"); 
151       *   
152       *   out = new Output(null);
153       *   out.palauta();
154       * </pre>
155       */
156      public Output(String outNimi) throws FileNotFoundException {
157          this(outNimi,false);
158      }
159      
160      /**
161       * @param outNimi mihin suunnataan
162       * @param syserr suunnataanko error-virta 
163       * @throws FileNotFoundException jos tiedostoa ei saada auki
164       * <pre name="test">
165       * #THROWS IOException
166       * #import java.io.*;
167       * #import java.util.*;
168       * #import static fi.jyu.mit.ohj2.VertaaTiedosto.*;
169       *   Output out = new Output("koeOut.txt",false);
170       *   Output err = new Output("koeErr.txt",true);
171       *   System.out.println("eka");
172       *   System.err.println("toka");
173       *   out.palauta();
174       *   err.palauta();
175       *   vertaaFileString("koeOut.txt","eka\n") === null;
176       *   vertaaFileString("koeErr.txt","toka\n") === null;
177       *   tuhoaTiedosto("koeOut.txt"); 
178       *   tuhoaTiedosto("koeErr.txt"); 
179       * </pre>
180       */
181      public Output(String outNimi,boolean syserr) throws FileNotFoundException {
182          // getNL();
183          if ( outNimi != null ) {
184            stdout = new PrintStream(new FileOutputStream(outNimi));
185            if ( syserr ) { origOut = System.err; System.setErr(stdout); this.syserr = true; }
186            else { origOut = System.out; System.setOut(stdout); }
187          }
188        }
189        
190      /**
191       * Palautetaan tietovirta takaisin alkuperäiseen tilaan
192       */
193      @Override
194      public void palauta() {
195        if (stdout != null ) {
196          stdout.close();
197          if ( syserr ) System.setErr(origOut);
198          else System.setOut(origOut);
199        }
200      }
201      
202    }
203    
204    /**
205     * Luokka syötön lukemiseksi merkkijonosta
206     * @author vesal
207     * @version 2.2.2008
208     * @example
209     * <pre name="test">
210     *  StringInput si = new StringInput("kissa\nkoira");
211     *  StringOutput so = new StringOutput();
212     *  Syotto.kysy("Mikä") === "kissa";
213     *  Syotto.kysy("Mikä") === "koira";
214     *  Syotto.kysy("Mikä") === "";
215     *  si = new StringInput("12\n13");
216     *  Syotto.kysy("Luku",0) === 12;
217     *  Syotto.kysy("Luku",0) === 13;
218     *  Syotto.kysy("Luku",0) === 0;
219     *  si.palauta();
220     *  so.palauta();
221     * </pre>
222     *
223     */
224    public static class StringInput implements ISuuntaaja {
225        private static final InputStream origIn  = System.in; // NOPMD ei ole vakio
226        private ByteArrayInputStream byteinput;
227        
228
229        /**
230         * Alustetataan lukutietovirta 
231         * @param inputString merkkijonojosta input otetaan
232         */
233        public StringInput(String inputString) {
234            byteinput = new ByteArrayInputStream(inputString.getBytes());
235            setIn(byteinput);
236        }
237
238        /**
239         * Palautetaan tietovirta takaisin alkuperäiseen tilaan
240         */
241        @Override
242        public void palauta() {
243            setIn(origIn);
244        }
245        
246        /**
247         * Laitetaan syöttöön uusi merkkijono jota luetaan-
248         * @param inputString  merkkijonojosta input otetaan
249         * @example
250         * <pre name="test">
251         *  StringInput si = new StringInput("kissa\nkoira");
252         *  StringOutput so = new StringOutput();
253         *  Syotto.kysy("Mikä") === "kissa";
254         *  Syotto.kysy("Mikä") === "koira";
255         *  Syotto.kysy("Mikä") === "";
256         *  si.input("12\n13");
257         *  Syotto.kysy("Luku",0) === 12;
258         *  Syotto.kysy("Luku",0) === 13;
259         *  Syotto.kysy("Luku",0) === 0;
260         *  si.palauta();
261         *  so.palauta();
262         * </pre>
263             */
264        public void input(String inputString) {
265            byteinput = new ByteArrayInputStream(inputString.getBytes());
266            setIn(byteinput);
267        }
268    }
269    
270    
271    /**
272     * Luokka tulostuksen siirtämiseksi merkkijonoon
273     * @author vesal
274     * @version 2.2.2008
275     *
276     */
277    public static class StringOutput implements ISuuntaaja {
278        private PrintStream origOut = System.out; // NOPMD - ei ole vakio
279        private boolean syserr = false; 
280        private final ByteArrayOutputStream byteoutput;
281        
282        
283        /**
284         * Alustetataan kirjoitustietovirta 
285         */
286        public StringOutput() {
287            this(false);
288        }
289
290        /**
291         * Alustetataan kirjoitustietovirta 
292         * @param syserr suunnattaanko System.err
293         * @example
294         * <pre name="test">
295         *   StringOutput se = new StringOutput(true);
296         *   StringOutput so = new StringOutput(false);
297         *   System.out.println("eka"); 
298         *   System.err.println("toka");
299         *   so.ero("eka\n") === null;
300         *   se.ero("toka\n") === null;
301         *   so.palauta();
302         *   se.palauta();
303         * </pre>
304         */
305        public StringOutput(boolean syserr) {
306            // if ( NL != null ) getNL();
307            byteoutput = new ByteArrayOutputStream();
308            PrintStream ps = new PrintStream(byteoutput); 
309            if ( syserr ) { origOut = System.err; System.setErr(ps); this.syserr = true; } 
310            else { origOut = System.out; System.setOut(ps); }
311        }
312
313        /**
314         * Palautetaan tietovirta takaisin alkuperäiseen tilaan
315         */
316        @Override
317        public void palauta() {
318            if ( syserr ) System.setErr(origOut);
319            else System.setOut(origOut);
320        }
321        
322        /**
323         * Palautetaan toistaiseksi tulostettu tieto merkkijonona
324         * @return tulostettu tieto
325         * @example
326         * <pre name="test">
327         *   String NL = getNL();
328         *   StringOutput so = new StringOutput();
329         *   System.out.println("eka"); 
330         *   System.out.println("toka");
331         *   so.toString() === "eka"+NL+"toka"+NL; 
332         *   System.out.println("kolmas");
333         *   so.toStringReset() === "eka"+NL+"toka"+NL+"kolmas"+NL;
334         *   so.toString() === "";
335         *   System.out.println("neljäs");
336         *   so.toStringReset() === "neljäs"+NL;
337         *   System.out.print("viides\nkuudes");
338         *   so.toStringReset() === "viides\nkuudes";
339         *   System.out.printf("viides%nkuudes");
340         *   so.toStringReset() === "viides"+NL+"kuudes";
341         *   so.palauta();
342         * </pre>
343         */
344        @Override
345        public String toString() {
346            return byteoutput.toString();
347        }
348        
349        /**
350         * Palautetaan toistaiseksi tulostettu tieto merkkijonona
351         * ja tyhjennetään tietovirta
352         * @return tulostettu tieto
353         */
354        public String toStringReset() {
355            String result = byteoutput.toString();
356            reset();
357            return result;
358        }
359        
360        /**
361         * Tyhjentää toistaiseksi tulostetun osan
362         */
363        public void reset() {
364            byteoutput.reset();
365        }
366        
367        /**
368         * Kirjoittaa sisällön tietovirtaan 
369         * @param out virta johon kirjoitetaan
370         * @throws IOException jos joku menee pieleen
371         * @example
372         * <pre name="test">
373         * #THROWS IOException
374         * StringOutput so = new StringOutput();
375         * try ( PrintStream fs = Tiedosto.avaa_kirjoittamista_varten_stream("hiljaa1.txt") ) {
376         * System.out.println("eka"); 
377         * System.out.println("toka");
378         * so.writeTo(fs);
379         * }
380         * so.palauta();
381         * vertaaFileString("hiljaa1.txt","eka\ntoka\n") === null;
382         * tuhoaTiedosto("hiljaa1.txt"); 
383         * </pre>
384         */
385        public void writeTo(OutputStream out) throws IOException {
386           byteoutput.writeTo(out); 
387        }
388        
389        /**
390         * Palauttaa alkuperäisen tietovirran
391         * @return alkuperäinen tietovirta
392         */
393        @SuppressWarnings("static-method")
394        public PrintStream getOrigOut() {
395            return origOut;
396        }
397        
398        /**
399         * Vertaa tuloksen sisältöä jonoon ja palauttaa eron
400         * tai null jos samat.  Tyhjentää tulosteen.
401         * @param verrattava jono johon output-jonon sisältöä verrataan
402         * @return null jos samat, muuten 1. ero
403         * @example
404         * <pre name="test">
405         *   StringOutput so = new StringOutput();
406         *   System.out.println("eka"); 
407         *   System.out.println("toka");
408         *   so.ero("eka\ntoka\n")         === null; 
409         *   System.out.println("kolmas");
410         *   so.ero("eka\ntoka\nkolmas\n") === "Ero riveissä 1: kolmas ja eka";
411         *   so.toString()                 === "";
412         *   System.out.println("neljäs");
413         *   so.ero("neljäs\n")            === null;
414         *   System.out.print("viides\nkuudes");
415         *   so.ero("viides\nkuudes")      === null;
416         *   System.out.printf("viides%nkuudes");
417         *   so.ero("viides\nkuudes")      === null;
418         *   so.palauta();
419         *   so.getOrigOut() == System.out === true;
420         * </pre>
421         */
422        public String ero(String verrattava) {
423            return VertaaTiedosto.vertaaString(toStringReset(), verrattava);
424        }
425        
426    }
427    
428    
429    
430    /**
431     * Luokka jolla System.in ja System.out suunnataan toiseen tiedostoon
432     * @author vesal
433     * @version 11.3.2007
434     */
435    public static class InOut implements ISuuntaaja {
436      private final Input in;
437      private final Output out;
438      
439      /**
440       * Asetetaan perusluku- ja tulostusvirta eri tiedostoon.
441       * Jos jompikumpi nimi on null, niin sitä virtaa ei suunnata uudelleen
442       * @param inNimi mistä tiedostosta System.in luetaan
443       * @param outNimi mihin System.out kirjoitetaan
444       * @throws FileNotFoundException jos tiedostoa ei saada käyttöön
445       * @example
446       * <pre name="test">
447       * #THROWS IOException
448       *   kirjoitaTiedosto("hiljaa1.txt", "eka\ntoka\n");
449       *   InOut io = new InOut("hiljaa1.txt","hiljaa2.txt");
450       *   Syotto.kysy("1.") === "eka";
451       *   Syotto.kysy("2.") === "toka";
452       *   Syotto.kysy("3.") === "";
453       *   io.palauta();
454       *   tuhoaTiedosto("hiljaa1.txt"); 
455       *   vertaaFileString("hiljaa2.txt","1. >2. >3. >") === null;
456       *   tuhoaTiedosto("hiljaa2.txt"); 
457       * </pre>
458       */
459      public InOut(String inNimi, String outNimi) throws FileNotFoundException {
460          in = new Input(inNimi);
461          out = new Output(outNimi);
462      }
463      
464      /**
465       * Palautetaan tietovirrat takaisin alkuperäiseen tilaan
466       */
467      @Override
468      public void palauta() {
469          in.palauta();
470          out.palauta();
471      }
472      
473    }
474    
475    /** 
476     * Testataan suuntaamista
477     * @param args ei käytössä
478     */
479/*    
480    public static void main(String[] args) {
481        StringInput si = new StringInput("kissa\nkoira\nkana");
482        StringOutput so = new StringOutput();
483        String s1 = Syotto.kysy("Mikä");
484        String so1 = so.toString();
485        so.getOrigOut().println("so1=" + so1);
486        si = new StringInput("12\n13\n15\n16\n17");
487        String s2 = Syotto.kysy("Kuka");
488        //si.palauta();
489        String s3 = Syotto.kysy("Mikä");
490        so.reset();
491        so.getOrigOut().println(s1 + "|" + s2 + "|" + s3);
492        String s4 = Syotto.kysy("Kuis");
493        System.out.println(s4);
494        si = new StringInput("12\n13\n15\n16\n17");
495        Scanner sc = new Scanner(System.in);
496        String s5 = sc.nextLine();
497        String s6 = Syotto.kysy("No");
498        si.palauta();
499        String so2 = so.toString();
500        so.palauta();
501        System.out.println(so1 + "|" + so2 + "|" + s5 + "|" + s6);
502    }
503*/    
504}