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