001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.commons.io.ini;
020
021import java.io.BufferedReader;
022import java.io.IOException;
023import java.io.InputStream;
024import java.io.InputStreamReader;
025import java.io.OutputStream;
026import java.io.PrintWriter;
027import java.util.Iterator;
028import java.util.LinkedHashMap;
029import java.util.Map;
030
031import lucee.commons.io.IOUtil;
032import lucee.commons.io.res.Resource;
033import lucee.runtime.type.Struct;
034import lucee.runtime.type.StructImpl;
035
036/**
037* read a ini file and allow to modifie and read the data
038*/
039public final class IniFile {
040
041        private Map sections;
042        private final Resource file;
043 
044        private static Map newMap() {return new LinkedHashMap();}
045 
046 /**
047  * Constructor for the IniFile object
048  *
049  * @param file ini FIle
050 * @throws IOException
051  */
052         public IniFile(Resource file) throws IOException {
053             this.file=file;
054             sections = newMap();
055             InputStream is=null;
056             if(!file.exists())file.createFile(false);
057             try {
058                 load(is=file.getInputStream());
059             }
060             finally {
061                 IOUtil.closeEL(is);
062             }
063         }
064         
065         public IniFile(InputStream is) throws IOException {
066             sections = newMap();
067             load(is);
068             file=null;
069         }
070
071 /**
072  * Sets the KeyValue attribute of the IniFile object
073  *
074  * @param strSection  the section to set
075  * @param key the key of the new value
076  * @param value the value to set
077  */
078 public void setKeyValue(String strSection, String key, String value) {
079     Map section = getSectionEL(strSection);
080     if(section==null) {
081         section=newMap();
082         sections.put(strSection.toLowerCase(),section);
083     }
084     section.put(key.toLowerCase(), value);
085 }
086
087 /**
088  * Gets the Sections attribute of the IniFile object
089  *
090  * @return   The Sections value
091  */
092 public Map getSections() {
093   return sections;
094 }
095
096 /**
097  * Gets the Section attribute of the IniFile object
098  *
099  * @param strSection  section name to get
100  * @return         The Section value
101 * @throws IOException
102  */
103 public Map getSection(String strSection) throws IOException {
104     Object o=sections.get(strSection.toLowerCase());
105     if(o==null) throw new IOException("section with name "+strSection+" does not exist");
106     return (Map) o;
107   }
108 /**
109  * Gets the Section attribute of the IniFile object, return null if section not exist
110  *
111  * @param strSection  section name to get
112  * @return         The Section value
113  */
114 public Map getSectionEL(String strSection) {
115     Object o=sections.get(strSection.toLowerCase());
116     if(o==null) return null;
117     return (Map) o;
118   }
119
120 /**
121  * Gets the NullOrEmpty attribute of the IniFile object
122  *
123  * @param section  section to check
124  * @param key      key to check
125  * @return         is empty or not
126  */
127 public boolean isNullOrEmpty(String section, String key) {
128   String value = getKeyValueEL(section, key);
129   return (value == null || value.length() == 0);
130 }
131
132 /**
133  * Gets the KeyValue attribute of the IniFile object
134  *
135  * @param strSection  section to get
136  * @param key      key to get
137  * @return         matching alue
138 * @throws IOException
139  */
140 public String getKeyValue(String strSection, String key) throws IOException {
141     Object o= getSection(strSection).get(key.toLowerCase());
142     if(o==null) throw new IOException("key "+key+" doesn't exist in section "+strSection);
143     return (String)o;
144
145 }
146 /**
147  * Gets the KeyValue attribute of the IniFile object, if not exist return null
148  *
149  * @param strSection  section to get
150  * @param key      key to get
151  * @return         matching alue
152  */
153 public String getKeyValueEL(String strSection, String key) {
154     Map map=getSectionEL(strSection);
155     if(map==null) return null;
156     Object o=map.get(key.toLowerCase());
157     if(o==null) return null;
158     return (String) o;
159
160 }
161
162
163 /**
164  * loads the ini file
165  * @param in  inputstream to read
166 * @throws IOException
167  */
168 public void load(InputStream in) throws IOException {
169   
170     BufferedReader input = IOUtil.toBufferedReader(new InputStreamReader(in));
171     String read;
172     Map section = null;
173     String sectionName;
174     while ((read = input.readLine()) != null) {
175       if (read.startsWith(";") || read.startsWith("#")) {
176         continue;
177       }
178       else if (read.startsWith("[")) {
179         // new section
180         sectionName = read.substring(1, read.indexOf("]")).trim().toLowerCase();
181         section = getSectionEL(sectionName);
182         if (section == null) {
183           section = newMap();
184           sections.put(sectionName, section);
185         }
186       }
187       else if (read.indexOf("=") != -1 && section != null) {
188         // new key
189         String key = read.substring(0, read.indexOf("=")).trim().toLowerCase();
190         String value = read.substring(read.indexOf("=") + 1).trim();
191         section.put(key, value);
192       }
193     }
194
195 }
196
197 /**
198  * save back content to ini file
199  * @throws IOException
200  */
201 public void save() throws IOException {
202         if(!file.exists())file.createFile(true);
203     OutputStream out=IOUtil.toBufferedOutputStream(file.getOutputStream());
204     Iterator it = sections.keySet().iterator();
205     PrintWriter output = new PrintWriter(out);
206     try {
207     while(it.hasNext()) {
208         String strSection = (String) it.next();
209         output.println("[" + strSection + "]");
210         Map section = getSectionEL(strSection);
211         Iterator iit = section.keySet().iterator();
212         while(iit.hasNext()) {
213             String key = (String) iit.next();
214             output.println(key + "=" + section.get(key));
215        }
216       }
217     }
218     finally {
219         IOUtil.flushEL(output);
220         IOUtil.closeEL(output);
221         IOUtil.flushEL(out);
222         IOUtil.closeEL(out);
223     }
224 }
225
226 /**
227  * removes a selection
228  *
229  * @param strSection  section to remove
230  */
231 public void removeSection(String strSection) {
232     sections.remove(strSection);
233 }
234 
235 /**
236  * 
237 * @param file
238 * @return return a struct with all section an dkey list as value
239 * @throws IOException
240 */
241public static Struct getProfileSections(Resource file) throws IOException {
242     IniFile ini=new IniFile(file);
243     Struct rtn=new StructImpl(Struct.TYPE_SYNC);
244     Map sections = ini.getSections();
245     Iterator it = sections.keySet().iterator();
246     while(it.hasNext()) {
247         String strSection=(String) it.next();
248         Map section = ini.getSectionEL(strSection);
249         Iterator iit = section.keySet().iterator();
250         StringBuilder sb=new StringBuilder();
251         while(iit.hasNext()) {
252             if(sb.length()!=0)sb.append(',');
253             sb.append(iit.next());
254         }
255         rtn.setEL(strSection,sb.toString());
256     }
257     return rtn;
258 }
259}