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    }