001 /*
002 * Copyright 2005,2009 Ivan SZKIBA
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 package org.ini4j;
017
018 import org.ini4j.spi.ServiceFinder;
019
020 import org.xml.sax.Attributes;
021 import org.xml.sax.InputSource;
022 import org.xml.sax.SAXException;
023 import org.xml.sax.helpers.DefaultHandler;
024
025 import java.io.IOException;
026 import java.io.InputStream;
027 import java.io.InputStreamReader;
028 import java.io.Reader;
029
030 import java.net.URL;
031
032 import java.util.Locale;
033
034 import javax.xml.parsers.SAXParserFactory;
035
036 public class IniParser extends AbstractParser
037 {
038 private static final String COMMENTS = ";#";
039 private static final String OPERATORS = ":=";
040
041 public static final char SECTION_BEGIN = '[';
042 public static final char SECTION_END = ']';
043
044 public IniParser()
045 {
046 super(OPERATORS, COMMENTS);
047 }
048
049 public static IniParser newInstance()
050 {
051 return ServiceFinder.findService(IniParser.class);
052 }
053
054 public static IniParser newInstance(Config config)
055 {
056 IniParser instance = newInstance();
057
058 instance.setConfig(config);
059
060 return instance;
061 }
062
063 public void parse(InputStream input, IniHandler handler) throws IOException, InvalidIniFormatException
064 {
065 parse(newIniSource(input), handler);
066 }
067
068 public void parse(Reader input, IniHandler handler) throws IOException, InvalidIniFormatException
069 {
070 parse(newIniSource(input), handler);
071 }
072
073 public void parse(URL input, IniHandler handler) throws IOException, InvalidIniFormatException
074 {
075 parse(newIniSource(input), handler);
076 }
077
078 public void parseXML(InputStream input, IniHandler handler) throws IOException, InvalidIniFormatException
079 {
080 parseXML(new InputStreamReader(input), handler);
081 }
082
083 public void parseXML(Reader input, final IniHandler handler) throws IOException, InvalidIniFormatException
084 {
085 class XmlToIni extends DefaultHandler
086 {
087 private static final String TAG_SECTION = "section";
088 private static final String TAG_OPTION = "option";
089 private static final String TAG_INI = "ini";
090 private static final String ATTR_KEY = "key";
091 private static final String ATTR_VALUE = "value";
092 private static final String ATTR_VERSION = "version";
093 private static final String CURRENT_VERSION = "1.0";
094
095 @Override public void startElement(String uri, String localName, String qname, Attributes attrs) throws SAXException
096 {
097 String key = attrs.getValue(ATTR_KEY);
098
099 if (qname.equals(TAG_INI))
100 {
101 String ver = attrs.getValue(ATTR_VERSION);
102
103 if ((ver == null) || !ver.equals(CURRENT_VERSION))
104 {
105 throw new SAXException("Missing or invalid 'version' attribute");
106 }
107
108 handler.startIni();
109 }
110 else
111 {
112 if (key == null)
113 {
114 throw new SAXException("missing '" + ATTR_KEY + "' attribute");
115 }
116
117 if (qname.equals(TAG_SECTION))
118 {
119 handler.startSection(key);
120 }
121 else if (qname.equals(TAG_OPTION))
122 {
123 handler.handleOption(key, attrs.getValue(ATTR_VALUE));
124 }
125 else
126 {
127 throw new SAXException("Invalid element: " + qname);
128 }
129 }
130 }
131
132 @Override public void endElement(String uri, String localName, String qname) throws SAXException
133 {
134 if (qname.equals(TAG_SECTION))
135 {
136 handler.endSection();
137 }
138 else if (qname.equals(TAG_INI))
139 {
140 handler.endIni();
141 }
142 }
143 }
144
145 XmlToIni xmlToini = new XmlToIni();
146
147 try
148 {
149 SAXParserFactory.newInstance().newSAXParser().parse(new InputSource(input), xmlToini);
150 }
151 catch (Exception x)
152 {
153 throw new InvalidIniFormatException(x);
154 }
155 }
156
157 public void parseXML(URL input, IniHandler handler) throws IOException, InvalidIniFormatException
158 {
159 parseXML(input.openStream(), handler);
160 }
161
162 private String parseSectionLine(String line, IniSource source, IniHandler handler) throws InvalidIniFormatException
163 {
164 String sectionName;
165
166 if (line.charAt(line.length() - 1) != SECTION_END)
167 {
168 parseError(line, source.getLineNumber());
169 }
170
171 sectionName = unescape(line.substring(1, line.length() - 1).trim());
172 if ((sectionName.length() == 0) && !getConfig().isUnnamedSection())
173 {
174 parseError(line, source.getLineNumber());
175 }
176
177 if (getConfig().isLowerCaseSection())
178 {
179 sectionName = sectionName.toLowerCase(Locale.getDefault());
180 }
181
182 handler.startSection(sectionName);
183
184 return sectionName;
185 }
186
187 private void parse(IniSource source, IniHandler handler) throws IOException, InvalidIniFormatException
188 {
189 handler.startIni();
190 String sectionName = null;
191
192 for (String line = source.readLine(); line != null; line = source.readLine())
193 {
194
195 if (line.charAt(0) == SECTION_BEGIN)
196 {
197 if (sectionName != null)
198 {
199 handler.endSection();
200 }
201
202 sectionName = parseSectionLine(line, source, handler);
203
204 }
205 else
206 {
207 if (sectionName == null)
208 {
209 if (getConfig().isGlobalSection())
210 {
211 sectionName = getConfig().getGlobalSectionName();
212 handler.startSection(sectionName);
213 }
214 else
215 {
216 parseError(line, source.getLineNumber());
217 }
218 }
219
220 parseOptionLine(line, handler, source.getLineNumber());
221
222 }
223 }
224
225 if (sectionName != null)
226 {
227 handler.endSection();
228 }
229
230 handler.endIni();
231 }
232 }