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 }