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    }