java/alt2xml-in-ini/src/cz/frantovo/alt2xml/in/ini/Reader.java
author František Kučera <franta-hg@frantovo.cz>
Sat, 06 Sep 2014 20:31:05 +0200
changeset 85 4f7028b5f2fe
parent 84 1a09966c072c
child 86 1d31d9cd28c8
permissions -rw-r--r--
in-ini: correct indentation for entries outside groups
     1 /**
     2  * Alt2XML
     3  * Copyright © 2014 František Kučera (frantovo.cz)
     4  *
     5  * This program is free software: you can redistribute it and/or modify
     6  * it under the terms of the GNU General Public License as published by
     7  * the Free Software Foundation, either version 3 of the License, or
     8  * (at your option) any later version.
     9  *
    10  * This program is distributed in the hope that it will be useful,
    11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  * GNU General Public License for more details.
    14  *
    15  * You should have received a copy of the GNU General Public License
    16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
    17  */
    18 package cz.frantovo.alt2xml.in.ini;
    19 
    20 import cz.frantovo.alt2xml.AbstractAlt2XmlReader;
    21 import cz.frantovo.alt2xml.in.Alt2ContentHandler;
    22 import java.io.BufferedReader;
    23 import java.io.IOException;
    24 import java.io.InputStreamReader;
    25 import java.util.ArrayList;
    26 import java.util.List;
    27 import java.util.logging.Level;
    28 import java.util.logging.Logger;
    29 import java.util.regex.Matcher;
    30 import java.util.regex.Pattern;
    31 import org.xml.sax.InputSource;
    32 import org.xml.sax.SAXException;
    33 
    34 /**
    35  *
    36  * @author Ing. František Kučera (frantovo.cz)
    37  */
    38 public class Reader extends AbstractAlt2XmlReader {
    39 
    40 	public static final String ROOT_ELEMENT = "ini";
    41 	private static final Logger log = Logger.getLogger(Reader.class.getName());
    42 
    43 	@Override
    44 	public void parse(InputSource input) throws IOException, SAXException {
    45 		outputStart();
    46 
    47 		try (BufferedReader br = new BufferedReader(new InputStreamReader(input.getByteStream()))) {
    48 			FileContext fc = new FileContext(contentHandler);
    49 			for (String currentLine = br.readLine(); currentLine != null; currentLine = br.readLine()) {
    50 				for (LINE_TYPE lineType : LINE_TYPE.values()) {
    51 					boolean lineProcessed = lineType.processLine(currentLine, fc);
    52 					if (lineProcessed) {
    53 						break;
    54 					}
    55 				}
    56 			}
    57 			fc.outputEndSection(fc.lastSection);
    58 
    59 		}
    60 
    61 		outputEnd();
    62 	}
    63 
    64 	private void outputStart() throws SAXException {
    65 		contentHandler.startDocument();
    66 		contentHandler.lineBreak();
    67 		contentHandler.startElement(null, null, ROOT_ELEMENT, null);
    68 		contentHandler.lineBreak();
    69 	}
    70 
    71 	private void outputEnd() throws SAXException {
    72 		contentHandler.endElement(null, null, "ini");
    73 		contentHandler.lineBreak();
    74 		contentHandler.endDocument();
    75 	}
    76 
    77 	private static class FileContext {
    78 
    79 		private final Alt2ContentHandler contentHandler;
    80 		private String lastSection;
    81 
    82 		public FileContext(Alt2ContentHandler contentHandler) {
    83 			this.contentHandler = contentHandler;
    84 		}
    85 
    86 		protected void outputStartSection(String name) throws SAXException {
    87 			contentHandler.indentation(1);
    88 			contentHandler.startElement(null, null, name, null);
    89 			contentHandler.lineBreak();
    90 		}
    91 
    92 		protected void outputEndSection(String name) throws SAXException {
    93 			if (name != null) {
    94 				contentHandler.indentation(1);
    95 				contentHandler.endElement(null, null, name);
    96 				contentHandler.lineBreak();
    97 			}
    98 		}
    99 	}
   100 
   101 	private static class LineContext {
   102 
   103 		private final Matcher matcher;
   104 
   105 		public LineContext(Matcher matcher) {
   106 			this.matcher = matcher;
   107 		}
   108 	}
   109 
   110 	private enum LINE_TYPE {
   111 
   112 		COMMENT("\\s*(;|#)\\s*(?<comment>.*)") {
   113 					@Override
   114 					public void processLine(LineContext lc, FileContext fc) throws SAXException {
   115 						// TODO: comment → LexicalHandler
   116 						log.log(Level.FINER, "Comment: {0}", lc.matcher.group("comment"));
   117 					}
   118 
   119 				},
   120 		SECTION("\\s*\\[\\s*(?<name>[^\\s\\]]+)\\s*\\]\\s*") {
   121 					@Override
   122 					public void processLine(LineContext lc, FileContext fc) throws SAXException {
   123 						String name = lc.matcher.group("name");
   124 						fc.outputEndSection(fc.lastSection);
   125 						fc.outputStartSection(name);
   126 						fc.lastSection = name;
   127 					}
   128 
   129 				},
   130 		ENTRY(
   131 				"\\s*(?<key>[^=\\s]+)\\s*=\\s*\"(?<value>[^']+)\"\\s*((;|#)\\s*(?<comment>.*)){0,1}", // quoted value → include spaces + might have comment
   132 				"\\s*(?<key>[^=\\s]+)\\s*=\\s*'(?<value>[^']+)'\\s*((;|#)\\s*(?<comment>.*)){0,1}", // apostrophed value → include spaces + might have comment
   133 				"\\s*(?<key>[^=\\s]+)\\s*=\\s*(?<value>.+)" // unquoted value → strip spaces + no comments
   134 		) {
   135 					@Override
   136 					public void processLine(LineContext lc, FileContext fc) throws SAXException {
   137 						String key = lc.matcher.group("key");
   138 						String value = lc.matcher.group("value");
   139 
   140 						if (lc.matcher.groupCount() > 2) {
   141 							String comment = lc.matcher.group("comment");
   142 							// TODO: comment → LexicalHandler
   143 							log.log(Level.FINER, "Comment for entry „{0}“ is: {1}", new Object[]{key, comment});
   144 
   145 						}
   146 
   147 						fc.contentHandler.indentation(fc.lastSection == null ? 1 : 2);
   148 						fc.contentHandler.textElement(value, null, null, key, null);
   149 						fc.contentHandler.lineBreak();
   150 
   151 					}
   152 
   153 				},;
   154 
   155 		private LINE_TYPE(String... patterns) {
   156 			for (String pattern : patterns) {
   157 				this.patterns.add(Pattern.compile(pattern));
   158 			}
   159 		}
   160 
   161 		private final List<Pattern> patterns = new ArrayList<>();
   162 
   163 		protected boolean processLine(String currentLine, FileContext fileContext) throws SAXException {
   164 			for (Pattern pattern : patterns) {
   165 				Matcher m = pattern.matcher(currentLine);
   166 				if (m.matches()) {
   167 					log.log(Level.FINEST, "Line „{0}“ matches pattern „{1}“", new Object[]{currentLine, pattern});
   168 					processLine(new LineContext(m), fileContext);
   169 					return true;
   170 				}
   171 			}
   172 			return false;
   173 		}
   174 
   175 		public abstract void processLine(LineContext lc, FileContext fc) throws SAXException;
   176 	}
   177 
   178 }