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 }