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

de.flapdoodle.embed.mongo.packageresolver.ExplainRules Maven / Gradle / Ivy

There is a newer version: 4.18.2
Show newest version
/*
 * Copyright (C) 2011
 *   Michael Mosmann 
 *   Martin Jöhren 
 *
 * with contributions from
 * 	konstantin-ba@github,Archimedes Trajano	(trajano@github)
 *
 * 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.
 */
package de.flapdoodle.embed.mongo.packageresolver;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

public final class ExplainRules {
	private ExplainRules() {
		// no instance
	}

	public interface Context {
		Context oneDeeper();

		void label(String label);

		void matching(String explainMatch);

		void finder(String finderExplained);
	}

	private static class Output {
		private final StringBuilder sb=new StringBuilder();
		private final String NEW_LINE=System.lineSeparator();

		public String asString() {
			return sb.toString();
		}

		public ContextImpl root() {
			return new ContextImpl(0);
		}

		class ContextImpl implements Context {

			private final int level;

			public ContextImpl(int level) {
				this.level = level;
			}

			public ContextImpl oneDeeper() {
				return new ContextImpl(level+1);
			}

			public void label(String label) {
				sb.append(indent(level)).append("'").append(label).append("'").append(NEW_LINE);
			}

			public void matching(String explainMatch) {
				sb.append(indent(level)).append(explainMatch).append(NEW_LINE);
			}

			public void finder(String finderExplained) {
				sb.append(indent(level+1)).append(finderExplained).append(NEW_LINE);
			}
		}
	}

	static String explain(PackageFinderRules rules) {
		Output output = new Output();
		explain(output.root(), rules);
		return output.asString();
	}

	static void explain(Context context, PackageFinderRules rules) {
		rules.rules().forEach(rule -> {
			context.matching(explainMatch(rule.match()));
			PackageFinder finder = rule.finder();
			explain(context, finder);
		});
	}

	public static void explain(Context context, PackageFinder finder) {
		if (finder instanceof HasLabel) {
			context.label(((HasLabel) finder).label());
		}
		if (finder instanceof HasPlatformMatchRules) {
			explain(context.oneDeeper(), ((HasPlatformMatchRules) finder).rules());
		} else {
			context.finder(finder(finder));
		}
	}

	public static String finder(PackageFinder finder) {
		return finder instanceof HasExplanation
			? ((HasExplanation) finder).explain()
			: finder.getClass().getSimpleName();
	}

	public static String finderLabel(PackageFinder finder) {
		return finder instanceof HasLabel
			? ((HasLabel) finder).label()
			: finder.getClass().getSimpleName();
	}

	static String explainMatch(DistributionMatch match) {
		return forType(DistributionMatch.class)
			.mapIfInstance(PlatformMatch.class, ExplainRules::explainPlatformMatch)
			.orMapIfInstance(DistributionMatch.AndThen.class, andThen -> "" + explainMatch(andThen.first()) + " and " + explainMatch(andThen.second()))
			.orMapIfInstance(DistributionMatch.Any.class, any -> any.matcher().stream().map(ExplainRules::explainMatch).collect(Collectors.joining(" or ","(",")")))
			.orMapIfInstance(VersionRange.class, ExplainRules::explainVersionRange)
			.orMapIfInstance(ToolVersionRange.class, ExplainRules::explainToolsVersionRange)
			.orMapIfInstance(DistributionMatch.class, it -> it.getClass().getSimpleName())
			.apply(match)
			.get();
	}

	static String explainPlatformMatch(PlatformMatch match) {
		List parts=new ArrayList<>();
		match.os().ifPresent(os -> parts.add("os="+os));
		match.bitSize().ifPresent(bitSize -> parts.add("bitSize="+bitSize));
		match.cpuType().ifPresent(cpuType -> parts.add("cpuType="+cpuType));

		if (!match.version().isEmpty()) {
			parts.add(match.version().stream().map(version -> ""+version)
				.collect(Collectors.joining(", ", "(version is any of ", ")")));
		}

		return !parts.isEmpty()
			? parts.stream().collect(Collectors.joining(" and ", "(", ")"))
			: "(any)";
	}

	private static String explainVersionRange(VersionRange versionRange) {
		if (versionRange.min().isEqual(versionRange.max())) {
			return asHumanReadable(versionRange.min());
		}
		return asHumanReadable(versionRange.min())+"-"+asHumanReadable(versionRange.max());
	}

	private static String explainToolsVersionRange(ToolVersionRange versionRange) {
		if (versionRange.min().isEqual(versionRange.max())) {
			return "tools.version "+asHumanReadable(versionRange.min());
		}
		return "tools.version "+asHumanReadable(versionRange.min())+"-"+asHumanReadable(versionRange.max());
	}

	private static String asHumanReadable(NumericVersion version) {
		return version.asString();
	}

	private static String indent(int level) {
		return repeat(' ', level * 2);
	}

	private static String repeat(char c, int level) {
		char[] s = new char[level];
		for (int i = 0; i < level; i++) {
			s[i] = c;
		}
		return String.valueOf(s);
	}

	public static  HasOptionalBuilder forType(Class sourceType) {
		return new HasOptionalBuilder<>();
	}

	static class HasOptionalBuilder {
		 HasOptionalResult mapIfInstance(Class type, Function mapIfTypeMatches) {
			return ExplainRules.mapIfInstance(type, mapIfTypeMatches);
		}
	}

	interface HasOptionalResult extends Function> {

		default HasOptionalResult or(HasOptionalResult other) {
			HasOptionalResult that = this;
			return s -> {
				Optional first = that.apply(s);
				return first.isPresent() ? first : other.apply(s);
			};
		}

		default  HasOptionalResult orMapIfInstance(Class type, Function mapIfTypeMatches) {
			return or(ExplainRules.mapIfInstance(type, mapIfTypeMatches));
		}
	}

	static  HasOptionalResult mapIfInstance(Class type, Function mapIfTypeMatches) {
		return s -> type.isInstance(s)
			? Optional.of(mapIfTypeMatches.apply(type.cast(s)))
			: Optional.empty();
	}
}