001 /*
002 * Copyright 2005 [ini4j] Development Team
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.ini4j.addon;
018
019 import java.io.IOException;
020 import java.io.LineNumberReader;
021 import java.io.Reader;
022 import java.io.InputStreamReader;
023 import java.net.URL;
024 import java.util.*;
025 import org.ini4j.*;
026
027 public class FancyIniParser extends IniParser
028 {
029 public static final char INCLUDE_BEGIN = '<';
030 public static final char INCLUDE_END = '>';
031
032 private boolean _allowEmptyOption = true;
033 private boolean _allowUnnamedSection = true;
034 private boolean _allowMissingSection = true;
035 private String _missingSectionName = "";
036 private boolean _allowSectionCaseConversion;
037 private boolean _allowOptionCaseConversion;
038 private boolean _allowInclude = true;
039
040 public synchronized void setAllowEmptyOption(boolean flag)
041 {
042 _allowEmptyOption = flag;
043 }
044
045 public synchronized boolean isAllowEmptyOption()
046 {
047 return _allowEmptyOption;
048 }
049
050 public synchronized void setAllowUnnamedSection(boolean flag)
051 {
052 _allowUnnamedSection = flag;
053 }
054
055 public synchronized boolean isAllowUnnamedSection()
056 {
057 return _allowUnnamedSection;
058 }
059
060 public synchronized void setAllowMissingSection(boolean flag)
061 {
062 _allowMissingSection = flag;
063 }
064
065 public synchronized boolean isAllowMissingSection()
066 {
067 return _allowMissingSection;
068 }
069
070 public synchronized void setMissingSectionName(String name)
071 {
072 _missingSectionName = name;
073 }
074
075 public synchronized String getMissingSectionName()
076 {
077 return _missingSectionName;
078 }
079
080 public synchronized void setAllowSectionCaseConversion(boolean flag)
081 {
082 _allowSectionCaseConversion = flag;
083 }
084
085 public synchronized boolean isAllowSectionCaseConversion()
086 {
087 return _allowSectionCaseConversion;
088 }
089
090 public synchronized void setAllowOptionCaseConversion(boolean flag)
091 {
092 _allowOptionCaseConversion = flag;
093 }
094
095 public synchronized boolean isAllowOptionCaseConversion()
096 {
097 return _allowOptionCaseConversion;
098 }
099
100 public synchronized boolean isAllowInclude()
101 {
102 return _allowInclude;
103 }
104
105 public synchronized void setAllowInclude(boolean flag)
106 {
107 _allowInclude = flag;
108 }
109
110 protected static class IniSource
111 {
112 protected URL base;
113 protected Stack<URL> bases;
114
115 protected LineNumberReader reader;
116 protected Stack<LineNumberReader> readers;
117
118 protected IniSource(Reader input)
119 {
120 reader = new LineNumberReader(input);
121 }
122
123 protected IniSource(URL base) throws IOException
124 {
125 this.base = base;
126 reader = new LineNumberReader(new InputStreamReader(base.openStream()));
127 }
128
129 protected void include(LineNumberReader input, URL location)
130 {
131 if ( readers == null )
132 {
133 readers = new Stack<LineNumberReader>();
134 bases = new Stack<URL>();
135 }
136
137 readers.push(reader);
138 bases.push(base);
139
140 reader = input;
141 base = location;
142 }
143
144 protected int getLineNumber()
145 {
146 return reader.getLineNumber();
147 }
148
149 protected String readLine() throws IOException
150 {
151 String line = reader.readLine();
152
153 if ( line == null )
154 {
155 if ( (readers != null) && ! readers.empty() )
156 {
157 reader = readers.pop();
158 base = bases.pop();
159 line = readLine();
160 }
161 }
162 else
163 {
164 String buff = line.trim();
165
166 if ( (buff.length() > 2) && (buff.charAt(0) == INCLUDE_BEGIN) && (buff.charAt(buff.length() - 1) == INCLUDE_END) )
167 {
168 buff = buff.substring(1, buff.length()-1).trim();
169
170 URL loc = base == null ? new URL(buff) : new URL(base, buff);
171
172 LineNumberReader inc = new LineNumberReader(new InputStreamReader(loc.openStream()));
173 include(inc, loc);
174 line = readLine();
175 }
176 }
177
178 return line;
179 }
180 }
181
182 public void parse(Reader input, IniHandler handler) throws IOException, InvalidIniFormatException
183 {
184 parse(new IniSource(input), handler);
185 }
186
187 public void parse(URL input, IniHandler handler) throws IOException, InvalidIniFormatException
188 {
189 parse(new IniSource(input), handler);
190 }
191
192 protected void parse(IniSource source, IniHandler handler) throws IOException, InvalidIniFormatException
193 {
194 handler.startIni();
195
196 String sectionName = null;
197
198 for (String line = source.readLine(); line != null; line = source.readLine())
199 {
200 line = line.trim();
201
202 if ( (line.length() == 0) || (COMMENTS.indexOf(line.charAt(0)) >= 0))
203 {
204 continue;
205 }
206
207 if ( line.charAt(0) == SECTION_BEGIN )
208 {
209 if ( sectionName != null )
210 {
211 handler.endSection();
212 }
213
214 if ( line.charAt(line.length()-1) != SECTION_END )
215 {
216 parseError(line, source.getLineNumber());
217 }
218
219 sectionName = unescape(line.substring(1, line.length()-1).trim());
220
221 if ( (sectionName.length() == 0) && ! isAllowUnnamedSection() )
222 {
223 parseError(line, source.getLineNumber());
224 }
225
226 if ( isAllowSectionCaseConversion() )
227 {
228 sectionName = sectionName.toLowerCase(Locale.getDefault());
229 }
230
231 handler.startSection(sectionName);
232 }
233 else
234 {
235 if ( sectionName == null )
236 {
237 if ( isAllowMissingSection() )
238 {
239 sectionName = getMissingSectionName();
240 handler.startSection(sectionName);
241 }
242 else
243 {
244 parseError(line, source.getLineNumber());
245 }
246 }
247
248 int idx = line.indexOf(OPERATOR);
249
250 String name = null;
251 String value = null;
252
253 if ( idx < 0 )
254 {
255 if ( isAllowEmptyOption() )
256 {
257 name = line;
258 }
259 else
260 {
261 parseError(line, source.getLineNumber());
262 }
263 }
264 else
265 {
266 name = unescape(line.substring(0, idx)).trim();
267 value = unescape(line.substring(idx+1)).trim();
268 }
269
270 if ( name.length() == 0)
271 {
272 parseError(line, source.getLineNumber());
273 }
274
275 if ( isAllowOptionCaseConversion() )
276 {
277 name = name.toLowerCase(Locale.getDefault());
278 }
279
280 handler.handleOption(name, value);
281 }
282 }
283
284 if ( sectionName != null )
285 {
286 handler.endSection();
287 }
288
289 handler.endIni();
290 }
291 }