All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.asteriskjava.config.ConfigFileReader Maven / Gradle / Ivy

There is a newer version: 3.41.0
Show newest version
package org.asteriskjava.config;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

/**
 * Reads and parses Asterisk configuration files. 
* See Asterisk's main/config.c.
* Client code is not supposed to use this class. * * @author srt * @version $Id$ */ public class ConfigFileReader { private static final int MAX_LINE_LENGTH = 8192; private static char COMMENT_META = ';'; private static char COMMENT_TAG = '-'; private static final Set> WARNING_CLASSES; static { WARNING_CLASSES = new HashSet<>(); WARNING_CLASSES.add(MissingEqualSignException.class); WARNING_CLASSES.add(UnknownDirectiveException.class); WARNING_CLASSES.add(MissingDirectiveParameterException.class); } private StringBuilder commentBlock; protected final Map categories; private final List warnings; protected Category currentCategory; private int currentCommentLevel = 0; public ConfigFileReader() { this.commentBlock = new StringBuilder(); this.categories = new LinkedHashMap<>(); this.warnings = new ArrayList<>(); } public ConfigFile readFile(String configfile) { final ConfigFile result; try (BufferedReader reader = new BufferedReader( new InputStreamReader(new FileInputStream(configfile), StandardCharsets.UTF_8))) { readFile(configfile, reader); } catch (Exception e) { // ignored } result = new ConfigFileImpl(configfile, new TreeMap<>(categories)); return result; } void reset() { commentBlock = new StringBuilder(); categories.clear(); warnings.clear(); currentCategory = null; currentCommentLevel = 0; } Collection getCategories() { return categories.values(); } public Collection getWarnings() { return new ArrayList<>(warnings); } void readFile(String configfile, BufferedReader reader) throws IOException, ConfigParseException { String line; int lineno = 0; CharBuffer buffer = CharBuffer.allocate(MAX_LINE_LENGTH); reset(); while ((line = reader.readLine()) != null) { lineno++; buffer.clear(); buffer.put(line); buffer.put("\n"); buffer.flip(); processLine(configfile, lineno, buffer); } } ConfigElement processLine(String configfile, int lineno, CharBuffer buffer) throws ConfigParseException { char c; final StringBuilder processLineBuilder; final StringBuilder lineCommentBuilder; processLineBuilder = new StringBuilder(MAX_LINE_LENGTH); lineCommentBuilder = new StringBuilder(MAX_LINE_LENGTH); buffer.mark(); while (buffer.hasRemaining()) { final int position; position = buffer.position(); c = buffer.get(); // System.out.println(position + ": " + c); if (c == COMMENT_META) { if (position >= 1 && buffer.get(position - 1) == '\\') { /* Escaped semicolons aren't comments. */ } // NOPMD else if (buffer.remaining() >= 3 && buffer.get(position + 1) == COMMENT_TAG && buffer.get(position + 2) == COMMENT_TAG && buffer.get(position + 3) != COMMENT_TAG) { /* Meta-Comment start detected ";--" */ currentCommentLevel++; // System.out.println("Comment start, new level: " + // currentCommentLevel); if (!inComment()) { commentBlock.append(";--"); buffer.position(position + 3); buffer.mark(); continue; } } else if (inComment() && position >= 2 && buffer.get(position - 1) == COMMENT_TAG && buffer.get(position - 2) == COMMENT_TAG) { /* Meta-Comment end detected */ currentCommentLevel--; if (!inComment()) { buffer.reset(); // int commentLength = (position + 1) - // buffer.position(); // buffer.reset(); // for (int i = 0; i < commentLength; i++) // { // commentBlock.append(buffer.get()); // } commentBlock.append(c); // System.out.println("Comment end at " + position + ": // '" + commentBlock.toString() + "'"); buffer.position(position + 1); buffer.compact(); buffer.flip(); // System.out.println("Buffer compacted"); continue; } } else { if (!inComment()) { /* * If ; is found, and we are not nested in a comment, we * immediately stop all comment processing */ // System.out.println("Found ; while not in comment"); while (buffer.hasRemaining()) { lineCommentBuilder.append(buffer.get()); } break; } /* Found ';' while in comment */ // NOPMD } } if (inComment()) { commentBlock.append(c); } else { // System.out.println("Added '" + c + "' to processLine"); processLineBuilder.append(c); } } String processLineString; String lineCommentString; ConfigElement configElement; processLineString = processLineBuilder.toString().trim(); lineCommentString = lineCommentBuilder.toString().trim(); // System.out.println("process line: '" + processLineString + "'"); if (processLineString.length() == 0) { if (lineCommentString.length() != 0) { commentBlock.append(";"); commentBlock.append(lineCommentString); } if (!inComment()) { commentBlock.append("\n"); } return null; } try { configElement = processTextLine(configfile, lineno, processLineString); } catch (ConfigParseException e) { // some parsing exceptions are treated as warnings by Asterisk, we // mirror this behavior. if (WARNING_CLASSES.contains(e.getClass())) { warnings.add(e); return null; } throw e; } if (lineCommentString.length() != 0) { configElement.setComment(lineCommentString); } if (commentBlock.length() != 0) { configElement.setPreComment(commentBlock.toString()); commentBlock.delete(0, commentBlock.length()); } return configElement; } boolean inComment() { return currentCommentLevel != 0; } protected ConfigElement processTextLine(String configfile, int lineno, String line) throws ConfigParseException { ConfigElement configElement; if (line.charAt(0) == '[') // A category header { configElement = parseCategoryHeader(configfile, lineno, line); } else if (line.charAt(0) == '#') // a directive - #include or #exec { configElement = parseDirective(configfile, lineno, line); } else // just a line { if (currentCategory == null) { throw new ConfigParseException(configfile, lineno, "parse error: No category context for line %d of %s", lineno, configfile); } configElement = parseVariable(configfile, lineno, line); currentCategory.addElement(configElement); } return configElement; } protected Category parseCategoryHeader(String configfile, int lineno, String line) throws ConfigParseException { final Category category; final String name; final int nameEndPos; /* * format is one of the following: [foo] define a new category named * 'foo' [foo](!) define a new template category named 'foo' [foo](+) * append to category 'foo', error if foo does not exist. [foo](a) * define a new category and inherit from template a. You can put a * comma-separated list of templates and '!' and '+' between * parentheses, with obvious meaning. */ nameEndPos = line.indexOf(']'); if (nameEndPos == -1) { throw new ConfigParseException(configfile, lineno, "parse error: no closing ']', line %d of %s", lineno, configfile); } name = line.substring(1, nameEndPos); category = new Category(configfile, lineno, name); /* Handle options or categories to inherit from if available */ if (line.length() > nameEndPos + 1 && line.charAt(nameEndPos + 1) == '(') { final String[] options; final String optionsString; final int optionsEndPos; optionsString = line.substring(nameEndPos + 1); optionsEndPos = optionsString.indexOf(')'); if (optionsEndPos == -1) { throw new ConfigParseException(configfile, lineno, "parse error: no closing ')', line %d of %s", lineno, configfile); } options = optionsString.substring(1, optionsEndPos).split(","); for (String cur : options) { if ("!".equals(cur)) // category template { category.markAsTemplate(); } else if ("+".equals(cur)) // category addition { final Category categoryToAddTo; categoryToAddTo = categories.get(name); if (categoryToAddTo == null) { throw new ConfigParseException(configfile, lineno, "Category addition requested, but category '%s' does not exist, line %d of %s", name, lineno, configfile); } // todo implement category addition // category = categoryToAddTo; } else { final Category baseCategory; baseCategory = categories.get(cur); if (baseCategory == null) { throw new ConfigParseException(configfile, lineno, "Inheritance requested, but category '%s' does not exist, line %d of %s", cur, lineno, configfile); } inheritCategory(category, baseCategory); } } } appendCategory(category); return category; } ConfigDirective parseDirective(String configfile, int lineno, String line) throws ConfigParseException { ConfigDirective directive; final StringBuilder nameBuilder; final StringBuilder paramBuilder; String name = null; String param; nameBuilder = new StringBuilder(); paramBuilder = new StringBuilder(); for (int i = 1; i < line.length(); i++) { final char c; c = line.charAt(i); if (name == null) { nameBuilder.append(c); if (Character.isWhitespace(c) || i + 1 == line.length()) { name = nameBuilder.toString().trim(); } } else { paramBuilder.append(c); } } param = paramBuilder.toString().trim(); if (param.length() != 0 && (param.charAt(0) == '"' || param.charAt(0) == '<' || param.charAt(0) == '>')) { param = param.substring(1); } int endPos = param.length() - 1; if (param.length() != 0 && (param.charAt(endPos) == '"' || param.charAt(endPos) == '<' || param.charAt(endPos) == '>')) { param = param.substring(0, endPos); } if ("exec".equalsIgnoreCase(name)) { directive = new ExecDirective(configfile, lineno, param); } else if ("include".equalsIgnoreCase(name)) { directive = new IncludeDirective(configfile, lineno, param); } else { throw new UnknownDirectiveException(configfile, lineno, "Unknown directive '%s' at line %d of %s", name, lineno, configfile); } if (param.length() == 0) { if (name == null) { name = ""; } throw new MissingDirectiveParameterException(configfile, lineno, "Directive '#%s' needs an argument (%s) at line %d of %s", name.toLowerCase(Locale.US), "include".equalsIgnoreCase(name) ? "filename" : "/path/to/executable", lineno, configfile); } return directive; } protected ConfigVariable parseVariable(String configfile, int lineno, String line) throws ConfigParseException { int pos; String name; String value; pos = line.indexOf('='); if (pos == -1) { throw new MissingEqualSignException(configfile, lineno, "No '=' (equal sign) in line %d of %s", lineno, configfile); } name = line.substring(0, pos).trim(); // Ignore > in => if (line.length() > pos + 1 && line.charAt(pos + 1) == '>') { pos++; } value = (line.length() > pos + 1) ? line.substring(pos + 1).trim() : ""; return new ConfigVariable(configfile, lineno, name, value); } void inheritCategory(Category category, Category baseCategory) { category.addBaseCategory(baseCategory); } void appendCategory(Category category) { categories.put(category.getName(), category); currentCategory = category; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy