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

org.bndly.common.app.PackageHeaderParser Maven / Gradle / Ivy

package org.bndly.common.app;

/*-
 * #%L
 * App Main
 * %%
 * Copyright (C) 2013 - 2020 Cybercon GmbH
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.IntConsumer;

class PackageHeaderParser {

	interface PackageDescription {

		String getName();
	}

	STATE state;

	enum STATE {
		NEW_PACKAGE,
		MD_NAME,
		MD_VALUE_SWITCH,
		MD_VALUE_QUOTED,
		MD_VALUE_QUOTED_END,
		MD_VALUE_UNQUOTED,
	}

	int TOKEN_PACKAGE_SEP = ',';
	int TOKEN_META_DATA_SEP = ';';
	int TOKEN_VALUE_ASSIGN = '=';
	int TOKEN_VALUE_QUOTE = '"';

	void parse(String header, Consumer consumer) {
		if (header == null) {
			return;
		}
		state = STATE.NEW_PACKAGE;
		PackageParserImpl packageParserImpl = new PackageParserImpl(consumer);
		header.codePoints().forEach(packageParserImpl);
		packageParserImpl.complete();
	}

	private class PackageParserImpl implements IntConsumer {

		private final Consumer consumer;

		public PackageParserImpl(Consumer consumer) {
			this.consumer = consumer;
		}
		StringBuilder packageNameBuilder = new StringBuilder();
		StringBuilder metaDataBuilder = new StringBuilder();
		List metaData = new ArrayList<>();

		private void notifyConsumer() {
			String name = packageNameBuilder.toString().trim();
			consumer.accept(new PackageDescription() {
				@Override
				public String getName() {
					return name;
				}
			});
			reset();
		}
		
		private void reset() {
			packageNameBuilder = new StringBuilder();
			metaData = new ArrayList<>();
		}

		private void flushMetaData() {
			metaData.add(metaDataBuilder.toString());
			metaDataBuilder = new StringBuilder();
		}

		@Override
		public void accept(int cp) {
			switch (state) {
				case NEW_PACKAGE:
					if (cp == TOKEN_PACKAGE_SEP) {
						// package is complete
						notifyConsumer();
					} else if (cp == TOKEN_META_DATA_SEP) {
						// package is complete, but meta data follows
						state = STATE.MD_NAME;
					} else {
						// character of package name
						packageNameBuilder.appendCodePoint(cp);
					}
					break;
				case MD_NAME:
					if (cp == TOKEN_META_DATA_SEP) {
						// empty meta data can be ignored...
					} else if (cp == TOKEN_VALUE_ASSIGN) {
						// name of meta data is complete
						flushMetaData();
						state = STATE.MD_VALUE_SWITCH;
					} else {
						metaDataBuilder.appendCodePoint(cp);
					}
					break;
				case MD_VALUE_SWITCH:
					if (cp == TOKEN_VALUE_QUOTE) {
						// our value is supposed to be closed by a quote
						state = STATE.MD_VALUE_QUOTED;
					} else {
						// our value is not wrapped in quotes
						metaDataBuilder.appendCodePoint(cp);
						state = STATE.MD_VALUE_UNQUOTED;
					}
					break;
				case MD_VALUE_QUOTED:
					if (cp == TOKEN_VALUE_QUOTE) {
						// our value is complete
						flushMetaData();
						state = STATE.MD_VALUE_QUOTED_END;
					} else {
						// our value is not wrapped in quotes
						metaDataBuilder.appendCodePoint(cp);
					}
					break;
				case MD_VALUE_QUOTED_END:
					if (cp == TOKEN_META_DATA_SEP) {
						// our value is complete
						state = STATE.MD_NAME;
					} else if (cp == TOKEN_PACKAGE_SEP) {
						notifyConsumer();
						state = STATE.NEW_PACKAGE;
					} else {
						// any other character will be ignored
					}
					break;
				case MD_VALUE_UNQUOTED:
					if (cp == TOKEN_META_DATA_SEP) {
						// our value is complete
						flushMetaData();
						state = STATE.MD_NAME;
					} else if (cp == TOKEN_PACKAGE_SEP) {
						flushMetaData();
						notifyConsumer();
						state = STATE.NEW_PACKAGE;
					} else {
						// any other character will be appended as part of the value
						metaDataBuilder.appendCodePoint(cp);
					}
					break;
				default:
					throw new IllegalStateException(state.toString());
			}
		}

		private void complete() {
			switch (state) {
				case NEW_PACKAGE:
					if (packageNameBuilder.length() > 0) {
						// package is complete
						notifyConsumer();
					}
					break;
				case MD_NAME:
					if (metaDataBuilder.length() > 0) {
						// metadata is complete
						flushMetaData();
					}
					notifyConsumer();
					break;
				case MD_VALUE_SWITCH:
					if (metaDataBuilder.length() > 0) {
						// metadata is complete
						flushMetaData();
					}
					notifyConsumer();
					break;
				case MD_VALUE_QUOTED:
					if (metaDataBuilder.length() > 0) {
						// metadata is complete
						flushMetaData();
					}
					notifyConsumer();
					break;
				case MD_VALUE_QUOTED_END:
					if (metaDataBuilder.length() > 0) {
						// metadata is complete
						flushMetaData();
					}
					notifyConsumer();
					break;
				case MD_VALUE_UNQUOTED:
					if (metaDataBuilder.length() > 0) {
						// metadata is complete
						flushMetaData();
					}
					notifyConsumer();
					break;
				default:
					throw new IllegalStateException(state.toString());
			}
		}
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy