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

net.avcompris.commons.query.impl.Tokenizer Maven / Gradle / Ivy

There is a newer version: 0.6.3
Show newest version
package net.avcompris.commons.query.impl;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.substringBefore;

import javax.annotation.Nullable;

import org.apache.commons.lang3.StringUtils;

import net.avcompris.commons.query.FilterSyntaxException;

final class Tokenizer {

	// private final String expression;

	private String current;

	public Tokenizer(final String expression) {

		// this.expression=
		checkNotNull(expression, "expression");

		current = expression;
	}

	@Override
	public String toString() {

		return "Tokenizer: " + current;
	}

	public String normalizeSpace() {

		checkState(current != null, "current should not be null");

		final String normalized = StringUtils.normalizeSpace(current);

		if (!normalized.contentEquals(current)) {

			setCurrent(normalized);
		}

		return current;
	}

	public String getCurrent() {

		return current;
	}

	public Tokenizer setCurrent(@Nullable final String current) {

		if (current == null) {

			if (this.current != null) {

				this.current = null;
			}

		} else if (this.current == null) {

			checkState(false, //
					"current != null (%s), while this.current == null", current);

		} else if (current.length() > this.current.length()) {

			checkState(false, //
					"current.length (%s) = %s, while this.current.length (%s) = %s", //
					current, current.length(), this.current, this.current.length());

		} else {

			this.current = current;
		}

		return this;
	}

	public boolean hasNext() {

		return !isBlank(current);
	}

	public Tokenizer substringAfter(final String s) {

		checkState(current != null, //
				"current should not be null (s: %s)", s);

		final String after = StringUtils.substringAfter(current, s);

		checkState(after != null, //
				"current (%s) doesn’t contain s: %s", current, s);

		return setCurrent(after);
	}

	public Tokenizer substringAfter(final int length) {

		checkState(current != null, //
				"current should not be null (length: %s)", length);

		checkState(current.length() >= length, //
				"current.length = %s should not be >= length: %s", current.length(), length);

		final String after = current.substring(length);

		return setCurrent(after);
	}

	public ParsedArg nextArg() throws FilterSyntaxException {

		return parseArgEscapeQuotes();
	}

	public static final class ParsedArg {

		public final String s;
		public final boolean hadQuotes;

		private ParsedArg(final String s, final boolean hadQuotes) {

			this.s = checkNotNull(s, "s");
			this.hadQuotes = hadQuotes;
		}

		public boolean isNull() {

			return "null".contentEquals(s) && !hadQuotes;
		}
	}

	private ParsedArg parseArgEscapeQuotes() throws FilterSyntaxException {

		final String s0 = getCurrent();

		checkArgument(!isBlank(s0), //
				"s: %s", s0);

		String next = null;

		final String trim = s0.trim();

		final char firstChar = trim.charAt(0);

		final boolean hadQuotes;

		String s = s0;

		if (firstChar == '(') {

			throw new FilterSyntaxException("Illegal opening parenthesis.");

		} else if (firstChar == ')') {

			throw new FilterSyntaxException("Illegal closing parenthesis.");

		} else if (firstChar == '"') {

			hadQuotes = true;

			s = parseQuotes(trim, '"');

			next = trim.substring(s.length() + 2).trim();

			s = s.replace("\\\"", "\"").replace("\\'", "'").replace("\\\\", "\\");

			if (isBlank(next)) {
				next = null;
			}

		} else if (firstChar == '\'') {

			hadQuotes = true;

			s = parseQuotes(trim, '\'');

			next = trim.substring(s.length() + 2).trim();

			s = s.replace("\\\"", "\"").replace("\\'", "'").replace("\\\\", "\\");

			if (isBlank(next)) {
				next = null;
			}

		} else if (s.contains(" ")) {

			hadQuotes = false;

			s = substringBefore(trim, " ");

			next = trim.substring(s.length()).trim();

		} else {

			hadQuotes = false;
		}

		// return parseArgDontEscapeQuotes(s, hadQuotes, next);

		setCurrent(next);

		return new ParsedArg(s, hadQuotes);
	}

	private static String parseQuotes(final String s, final char quoteChar) {

		final StringBuilder sb = new StringBuilder();

		boolean escape = false;
		boolean inQuotes = false;

		for (char c : s.toCharArray()) {

			if (c == '\\') {

				escape = !escape;

			} else if (c == quoteChar) {

				if (!escape) {

					if (inQuotes) {

						return sb.toString();

					} else {

						inQuotes = true;

						continue;
					}

				} else {

					escape = false;
				}

			} else {

				escape = false;
			}

			sb.append(c);
		}

		throw new IllegalArgumentException("Unclosed quotes in: " + s);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy