001package fi.jyu.mit.ohj2;
002
003import java.io.BufferedReader;
004import java.io.File;
005import java.io.FileNotFoundException;
006import java.io.FileReader;
007import java.io.FileWriter;
008import java.io.IOException;
009import java.io.PrintWriter;
010import java.util.Map;
011import java.util.TreeMap;
012
013
014/**
015 * Luokka ini-tiedoston käsittelyyn.  Tiedoston muoto:
016 * <pre>
017 * [FormGUI]
018 * leveys=200
019 * korkeus=100
020 * cbEtsi=1
021 * [Positions]
022 * FormGUI=0,0
023 * </pre>
024 * 
025 * Usea ohjelman osa voi "luoda" tiedoston uudestaan, koska jo avattut
026 * tiedostot puskuroidaan, eikä niitä avata enää toista kertaa.
027 * Tämä ei kuitenkaan ole vielä thread-safe.
028 * 
029 * @author vesal
030 * @version 14.6.2013
031 * 
032 * @example
033 * <pre name="test">
034 * #import java.io.File;
035 *   File fini = new File("testini.ini");
036 *   File fini2 = new File("testini2.ini");
037 *   fini.delete();  fini2.delete();
038 *   
039 *   IniFile ini = IniFile.create("testini.ini");
040 *   ini.write("FormGUI","leveys",200);
041 *   ini.write("FormGUI","korkeus",100);
042 *   ini.write("FormGUI","cbEtsi",1);
043 *   ini.write("Positions","FormGUI","20,30");
044 *   
045 *   fini.renameTo(fini2) === true; // koska saman luominen ei loisi mitään uutta
046 *   
047 *   ini = IniFile.create("testini2.ini");
048 *   ini.read("FormGUI","leveys",0) === 200;
049 *   ini.read("FormGUI","korkeus",0) === 100;
050 *   ini.read("FormGUI","cbEtsi",0) === 1;
051 *   ini.read("FormGUI","cbEiOo",2) === 2;
052 *   ini.read("eioo","cbEiOo",2) === 2;
053 *   ini.read("Positions","FormGUI","1,1") === "20,30";
054 *   ini.write("FormGUI","cbEtsi",0);
055 *   ini.read("FormGUI","cbEtsi",9) === 0;
056 *   fini2.delete() === true;
057 * </pre>
058 * 
059 */
060public class IniFile  {
061
062    private static TreeMap<String,IniFile> iniFiles = new TreeMap<>();
063    
064    /**
065     * Luodaan uusi ini-file tai annetaan entinen jos on jo kerran luettu
066     * @param fileName minkä nimistä tiedostoa käsitellään
067     * @return luota tai puskurista otettu tiedosto
068     */
069    public static IniFile create(String fileName) {
070        File file = new File(fileName);
071        String fullname = file.getAbsolutePath();
072        IniFile result = iniFiles.get(fullname);
073        if ( result != null ) return result;
074        result = new IniFile(fullname);
075        iniFiles.put(fullname, result);
076        return result;
077    }
078    
079    private String fileName;
080    private TreeMap<String,TreeMap<String,String>> sections = new TreeMap<>(); 
081    
082    /**
083     * @param fileName luettavan tiedoston nimi
084     */
085    private IniFile(String fileName) {
086        this.fileName = fileName;
087        readFile();
088    }
089
090    
091    private String lastSectionName = null;
092    private TreeMap<String,String> lastSection = null; 
093    
094    
095    private TreeMap<String, String> getSectionValue(String section) {
096        if ( section.equals(lastSectionName) ) return lastSection;
097        lastSectionName = section;
098        lastSection = sections.get(section);
099        if ( lastSection != null ) return lastSection;
100        lastSection = new TreeMap<>();
101        sections.put(section, lastSection);
102        return lastSection;
103    }
104
105
106    private TreeMap<String, String> getSectionValueNoCreate(String section) {
107        if ( section.equals(lastSectionName) ) return lastSection;
108        TreeMap<String,String> last = sections.get(section);
109        if ( last == null ) return null;
110        lastSection = last;
111        lastSectionName = section;
112        return lastSection;
113    }
114
115
116    private void writeNoSave(String section, String item, String value) {
117        TreeMap<String,String> sectionValue = getSectionValue(section);
118        sectionValue.put(item, value.replaceAll("\n", "\\\\n"));
119    }
120    
121    
122    private void readFile() {
123        String sectionName = "";
124        try ( BufferedReader fi = new BufferedReader(new FileReader(fileName)) ) {
125            String rivi;
126            while ((rivi = fi.readLine()) != null) { 
127                rivi = rivi.trim();
128                if ( rivi.startsWith("[")) {
129                    sectionName = rivi.replace('[', ' ').replace(']', ' ').trim();
130                    continue;
131                }
132                int p = rivi.indexOf('=');
133                if ( p < 0 ) continue;
134                String itemName = rivi.substring(0,p);
135                String value = rivi.substring(p+1);
136                writeNoSave(sectionName, itemName, value);
137            }
138
139        } catch ( FileNotFoundException e ) {
140            // Mitäs tehtäis
141        } catch (IOException e) {
142            // Mitäs tehtäis
143        }
144    }
145    
146    
147    /**
148     * Tallenttaa koko rakenteen
149     */
150    public void saveFile()  {
151        File ftied = new File(fileName);
152
153        try ( PrintWriter fo = new PrintWriter(new FileWriter(ftied.getCanonicalPath())) ) {
154            for (Map.Entry<String,TreeMap<String,String>> entry : sections.entrySet()) {
155                fo.println("[" + entry.getKey() + "]");
156                for (Map.Entry<String,String> item : entry.getValue().entrySet()) {
157                    fo.println(item.getKey() + "=" + item.getValue());
158                }
159            }
160        } catch ( FileNotFoundException ex ) {
161            // Mitäs tehtäis
162        } catch ( IOException ex ) {
163            // Mitäs tehtäis
164        }
165
166    }
167
168
169    /**
170     * Kirjoitetaan uusi arvo 
171     * @param section mihin osioon kirjoitetaan
172     * @param item mikä on nimi
173     * @param value mikä on uusi arvo
174     */
175    public void write(String section, String item, String value) {
176        writeNoSave(section, item, value);
177        saveFile();
178    }
179    
180    
181    /**
182     * Kirjoitetaan uusi arvo 
183     * @param section mihin osioon kirjoitetaan
184     * @param item mikä on nimi
185     * @param value mikä on uusi arvo
186     */
187    public void write(String section, String item, int value) {
188        write(section,item,""+value);
189    }
190    
191    
192    /**
193     * Luetaan arvo
194     * @param section mistä osioista luetaan
195     * @param item mistä kohdasta luetaan
196     * @param defValue mikä arvo palautetaan jos itemia ei löydy
197     * @return löydetty arvo tai oletus
198     */
199    public String read(String section, String item, String defValue) {
200        TreeMap<String,String> sectionValue = getSectionValueNoCreate(section);
201        if ( sectionValue == null ) return defValue;
202        String result = sectionValue.get(item);
203        if ( result == null ) return defValue;
204        result = result.replaceAll("\\\\n", "\n");
205        return result;
206    }
207    
208    
209    /**
210     * Luetaan arvo
211     * @param section mistä osioista luetaan
212     * @param item mistä kohdasta luetaan
213     * @return löydetty arvo tai null jos ei löydy
214     */
215    public String read(String section, String item) {
216        return read(section,item,(String)null);
217    }
218    
219    
220    /**
221     * Luetaan arvo
222     * @param section mistä osioista luetaan
223     * @param item mistä kohdasta luetaan
224     * @param defValue mikä arvo palautetaan jos itemia ei löydy
225     * @return löydetty arvo tai oletus
226     */
227    public int read(String section, String item, int defValue) {
228        String result = read(section,item);
229        if ( result == null ) return defValue;
230        return Mjonot.erotaInt(result,defValue);
231    }
232
233
234}