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

aQute.bnd.osgi.MessageReporter Maven / Gradle / Ivy

The newest version!
package aQute.bnd.osgi;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

import aQute.bnd.exceptions.Exceptions;
import aQute.bnd.header.Attrs;
import aQute.bnd.header.Parameters;
import aQute.service.reporter.Report.Location;
import aQute.service.reporter.Reporter;
import aQute.service.reporter.Reporter.SetLocation;

/**
 * This is the error and warning messages collected. It implements a concurrent
 * message handling system.
 */
public class MessageReporter {

	final Processor								processor;
	final AtomicInteger							counter		= new AtomicInteger(1000);
	volatile ConcurrentHashMap	messages	= new ConcurrentHashMap<>();

	class Message extends Location implements SetLocation, Comparable {
		@Override
		public String toString() {
			return "Message [error=" + error + ", sequence=" + sequence + ", message=" + message + "]";
		}

		final boolean	error;
		final int		sequence;

		Message(int sequence, String message, boolean error) {
			this.sequence = sequence;
			this.message = message;
			this.error = error;
		}

		Message(int sequence, Message m, String actualPrefix) {
			m.to(this);
			this.sequence = sequence;
			this.message = actualPrefix.concat(m.message);
			this.error = m.error;
		}

		@Override
		public SetLocation file(String file) {
			this.file = file;
			return this;
		}

		@Override
		public SetLocation header(String header) {
			this.header = header;
			return this;
		}

		@Override
		public SetLocation context(String context) {
			this.context = context;
			return this;
		}

		@Override
		public SetLocation method(String methodName) {
			this.methodName = methodName;
			return this;
		}

		@Override
		public SetLocation line(int n) {
			this.line = n;
			return this;
		}

		@Override
		public SetLocation reference(String reference) {
			this.reference = reference;
			return this;
		}

		@Override
		public SetLocation details(Object details) {
			this.details = details;
			return null;
		}

		@Override
		public Location location() {
			return this;
		}

		@Override
		public SetLocation length(int length) {
			this.length = length;
			return this;
		}

		@Override
		public int compareTo(Message o) {
			return Integer.compare(sequence, o.sequence);
		}

		Message fixup(Instructions fixupInstrs, boolean failok) {

			boolean error = failok ? false : this.error;

			Instruction matcher = fixupInstrs.finder(message);
			String type = error ? Constants.FIXUPMESSAGES_IS_ERROR : Constants.FIXUPMESSAGES_IS_WARNING;
			String message = this.message;

			if (matcher != null && !matcher.isNegated()) {

				Attrs attrs = fixupInstrs.get(matcher);
				String restrict = attrs.get(Constants.FIXUPMESSAGES_RESTRICT_DIRECTIVE);
				String replace = attrs.get(Constants.FIXUPMESSAGES_REPLACE_DIRECTIVE);
				String is = attrs.get(Constants.FIXUPMESSAGES_IS_DIRECTIVE);

				if (restrict == null || restrict.equals(type)) {

					if (is != null && !is.equals(type)) {
						error = !error;
					}

					if (replace != null) {
						try (Processor replacer = new Processor(processor())) {
							replacer.setProperty("@", message);
							processor().getLogger()
								.debug("replacing {} with {}", message, replace);
							message = replacer.getReplacer()
								.process(replace);
						} catch (Exception e) {
							throw Exceptions.duck(e);
						}
					}

					if (attrs.isEmpty() || Constants.FIXUPMESSAGES_IS_IGNORE.equals(is)) {
						message = null;
					}
				}
			}
			if (message == null)
				return null;

			if (error == this.error && message.equals(this.message))
				return this;

			return new Message(sequence, message, error);
		}

	}

	class Cache {
		final List	errors		= new ArrayList<>();
		final List	warnings	= new ArrayList<>();

		Cache() {
			fixup(messages.values()).stream()
				.forEach(m -> {
					if (m.error)
						errors.add(m.message);
					else
						warnings.add(m.message);
				});
		}
	}

	MessageReporter(Processor processor) {
		this.processor = processor;
	}

	List getWarnings() {
		return fixup().warnings;
	}

	List getErrors() {
		return fixup().errors;
	}

	Cache fixup() {
		return new Cache();
	}

	Location getLocation(String msg) {
		return messages.get(msg);
	}

	public SetLocation error(String format, Object... args) {
		try {
			for (int i = 0; i < args.length; i++) {
				if (args[i] instanceof Throwable t) {
					args[i] = Exceptions.causes(t);
				}
			}
			String s = Processor.formatArrays(format, args);
			Message m = new Message(counter.getAndIncrement(), s, true);
			putMessage(s, m);
			return m;
		} finally {
			processor().signal();
		}
	}

	void putMessage(String s, Message m) {
		ConcurrentHashMap current;
		do {
			current = messages;
			current.putIfAbsent(s, m);
		} while (current != messages);
	}

	SetLocation warning(String format, Object... args) {
		String s = Processor.formatArrays(format, args);
		Message m = new Message(counter.getAndIncrement(), s, false);
		putMessage(s, m);
		return m;
	}

	void getInfo(Reporter from, String prefix) {
		String actualPrefix = prefix == null ? processor().getBase() + " :" : prefix;

		MessageReporter other;
		if (from instanceof Processor processor) {
			other = processor.reporter;
		} else if (from instanceof MessageReporter mr) {
			other = mr;
		} else {
			// backward compatible reporters
			addAll(true, actualPrefix, from);
			addAll(false, actualPrefix, from);
			from.getErrors()
				.clear();
			from.getWarnings()
				.clear();
			return;
		}

		ConcurrentHashMap older = other.clear();
		other.fixup(older.values())
			.stream()
			.map(m -> new Message(counter.getAndIncrement(), m, actualPrefix))
			.forEach(m -> {
				putMessage(m.message, m);
			});
	}

	/*
	 *
	 */
	private List fixup(Collection older) {
		boolean failOk = processor().isFailOk();
		Instructions fixupInstrs = new Instructions();
		Parameters fixup = processor().getMergedParameters(Constants.FIXUPMESSAGES);
		fixup.forEach((k, v) -> fixupInstrs.put(Instruction.legacy(k), v));

		return older.stream()
			.sorted()
			.map(m -> m.fixup(fixupInstrs, failOk))
			.filter(Objects::nonNull)
			.toList();

	}

	/*
	 * for backward compatible reporters
	 */
	void addAll(boolean error, String prefix, Reporter reporter) {
		for (String message : new ArrayList<>(error ? reporter.getErrors() : reporter.getWarnings())) {
			String newMessage = prefix.isEmpty() ? message : prefix.concat(message);
			Message m = new Message(counter.getAndIncrement(), newMessage, error);
			Location location = reporter.getLocation(message);
			if (location != null)
				location.to(m);
			putMessage(newMessage, m);
		}
	}

	ConcurrentHashMap clear() {
		ConcurrentHashMap previous = messages;
		messages = new ConcurrentHashMap<>();
		return previous;
	}

	Processor processor() {
		return this.processor.current();
	}

	Message remove(String message) {
		return messages.remove(message);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy