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

jasima.core.util.ArgListParser Maven / Gradle / Ivy

/*******************************************************************************
 * This file is part of jasima, v1.3, the Java simulator for manufacturing and 
 * logistics.
 *  
 * Copyright (c) 2015 		jasima solutions UG
 * Copyright (c) 2010-2015 Torsten Hildebrandt and jasima contributors
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see .
 *******************************************************************************/
package jasima.core.util;

import jasima.core.util.ArgListTokenizer.ParseException;
import jasima.core.util.ArgListTokenizer.TokenType;

import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;

public class ArgListParser {

	public static class ParseTree {
		private String classOrXmlName;
		private Map params;

		public ParseTree(String classOrXmlName, Map params) {
			super();
			this.classOrXmlName = classOrXmlName;
			this.params = params;
		}

		public String getClassOrXmlName() {
			return classOrXmlName;
		}

		public void setClassOrXmlName(String classOrXmlName) {
			this.classOrXmlName = classOrXmlName;
		}

		public Map getParams() {
			return params;
		}

		public void setParams(Map params) {
			this.params = params;
		}

		@Override
		public String toString() {
			StringBuilder sb = new StringBuilder();
			toString(this, sb);
			return sb.toString();
		}

		public static StringBuilder toString(ParseTree t, StringBuilder sb) {
			sb.append(t.getClassOrXmlName());

			if (t.getParams() != null) {
				sb.append('(');
				for (Entry e : t.getParams().entrySet()) {
					sb.append(e.getKey()).append('=');
					toString(e.getValue(), sb);
					sb.append(';');
				}
				if (t.getParams().entrySet().size() != 0) {
					sb.setCharAt(sb.length() - 1, ')');
				} else {
					sb.append(')');
				}
			}
			return sb;
		}

	}

	private final ArgListTokenizer tk;

	public ArgListParser(ArgListTokenizer tk) {
		super();
		this.tk = tk;
	}

	/**
	 * Parses class/property definitions in a form similar to:
	 * {@code "a.b.c.ATC(prop1=abc;prop2=1.23;prop3=Test(abc=xyz;def=123))"} All
	 * names and values are returned as Strings.
	 */
	public ParseTree parseClassAndPropDef() {
		// required class name
		TokenType token = tk.nextTokenNoWhitespace();
		assureTokenTypes(token, TokenType.STRING);
		String className = tk.currTokenText();
		Map params = null;

		// optional parameter list in round parenthesis
		token = tk.nextTokenNoWhitespace();
		if (token == TokenType.PARENS_OPEN) {
			params = new LinkedHashMap<>();
			while (true) {
				// name
				token = tk.nextTokenNoWhitespace();
				assureTokenTypes(token, TokenType.STRING,
						TokenType.PARENS_CLOSE);
				if (token == TokenType.PARENS_CLOSE)
					break; // end of parameter list
				String paramName = tk.currTokenText();

				// equals
				assureTokenTypes(tk.nextTokenNoWhitespace(), TokenType.EQUALS);

				// value: create sub-parser
				ParseTree paramValue = new ArgListParser(tk)
						.parseClassAndPropDef();
				assert paramValue != null;

				// save parsed parameter
				params.put(paramName, paramValue);

				// more parameters?
				token = tk.nextTokenNoWhitespace();
				assureTokenTypes(token, TokenType.SEMICOLON,
						TokenType.PARENS_CLOSE);
				if (token == TokenType.SEMICOLON) {
					// nothing special, start next iteration
				} else if (token == TokenType.PARENS_CLOSE) {
					break; // found end of list
				}
			}
		} else {
			// let parent handle it
			tk.pushBackToken();
		}

		return new ParseTree(className, params);
	}

	private void assureTokenTypes(TokenType actual, TokenType... expected) {
		for (TokenType e : expected) {
			if (actual == e)
				return;
		}

		String msg = "expected one of: %s, but found: %s, '%s'";
		if (expected.length == 1)
			msg = "expected %s, but found: %s, '%s'";
		throw new ParseException(tk.currTokenStart(), msg,
				Arrays.deepToString(expected), actual, tk.currTokenText());
	}

	// ************* static methods below ******************************

	/**
	 * Constructs a new ListTokenizer around {@code input} and then calls
	 * {@link #parseClassAndPropDef()}. This class assumes it can read and parse
	 * the whole string, otherwise it throws a {@link ParseException}.
	 * 
	 * @param input
	 *            The input string to parse.
	 * @return The
	 */
	public static ParseTree parseClassAndPropDef(String input)
			throws ParseException {
		ArgListTokenizer tk = new ArgListTokenizer(input);
		ParseTree res = new ArgListParser(tk).parseClassAndPropDef();

		// full input read?
		if (tk.nextToken() != null) {
			throw new ParseException(tk.currTokenStart(), String.format(
					Util.DEF_LOCALE,
					"There is data after the last token: '%s'.",
					input.substring(tk.currTokenStart())));
		}

		return res;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy