/* * Copyright 2015-2017 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v1.0 which * accompanies this distribution and is available at * * http://www.eclipse.org/legal/epl-v10.html */ package org.junit.platform.commons.util; import static org.junit.platform.commons.meta.API.Usage.Internal; import java.io.File; import java.net.URL; import java.security.CodeSource; import java.util.Arrays; import java.util.Optional; import java.util.function.Function; import java.util.jar.Attributes; import java.util.jar.JarFile; import java.util.jar.Manifest; import java.util.regex.Pattern; import javax.lang.model.SourceVersion; import org.junit.platform.commons.meta.API; /** * Collection of utilities for working with {@linkplain Package packages}. * * <h3>DISCLAIMER</h3> * * <p>These utilities are intended solely for usage within the JUnit framework * itself. <strong>Any usage by external parties is not supported.</strong> * Use at your own risk! * * @since 1.0 */ @API(Internal) public final class PackageUtils { ///CLOVER:OFF private PackageUtils() { /* no-op */ } ///CLOVER:ON static final String DEFAULT_PACKAGE_NAME = ""; /** * Compiled {@code "\."} pattern used to split canonical package (and type) names. */ private static final Pattern DOT_PATTERN = Pattern.compile("\\."); /** * Assert that the supplied package name is valid in terms of Java syntax. * * <p>Note: this method does not actually verify if the named package exists in the classpath. * * <p>The default package is represented by an empty string ({@code ""}). * * @param packageName the package name to validate * @throws PreconditionViolationException if the supplied package name is * {@code null}, contains only whitespace, or contains parts that are not * valid in terms of Java syntax (e.g., containing keywords such as * {@code void}, {@code import}, etc.) * @see SourceVersion#isName(CharSequence) */ public static void assertPackageNameIsValid(String packageName) { Preconditions.notNull(packageName, "package name must not be null"); if (packageName.equals(DEFAULT_PACKAGE_NAME)) { return; } Preconditions.notBlank(packageName, "package name must not contain only whitespace"); boolean allValid = Arrays.stream(DOT_PATTERN.split(packageName, -1)).allMatch(SourceVersion::isName); Preconditions.condition(allValid, "invalid part(s) in package name: " + packageName); } /** * Get the package attribute for the supplied {@code type} using the * supplied {@code function}. * * <p>This method only returns a non-empty {@link Optional} value holder * if the class loader for the supplied type created a {@link Package} * object and the supplied function does not return {@code null} when * applied. * * @param type the type to get the package attribute for * @param function a function that computes the package attribute value * (e.g., {@code Package::getImplementationTitle}); never {@code null} * @return an {@code Optional} containing the attribute value; never * {@code null} but potentially empty * @throws PreconditionViolationException if the supplied type or function * is {@code null} * @see Class#getPackage() * @see Package#getImplementationTitle() * @see Package#getImplementationVersion() */ public static Optional<String> getAttribute(Class<?> type, Function<Package, String> function) { Preconditions.notNull(type, "type must not be null"); Preconditions.notNull(function, "function must not be null"); Package typePackage = type.getPackage(); if (typePackage != null) { return Optional.ofNullable(function.apply(typePackage)); } return Optional.empty(); } /** * Get the value of the specified attribute name, specified as a string, * or an empty {@link Optional} if the attribute was not found. The attribute * name is case-insensitive. * * <p>This method also returns an empty {@link Optional} value holder * if any exception is caught while loading the manifest file via the * JAR file of the specified type. * * @param type the type to get the attribute for * @param name the attribute name as a string * @return an {@code Optional} containing the attribute value; never * {@code null} but potentially empty * @throws PreconditionViolationException if the supplied type is * {@code null} or the specified name is blank * @see Manifest#getMainAttributes() */ public static Optional<String> getAttribute(Class<?> type, String name) { Preconditions.notNull(type, "type must not be null"); Preconditions.notBlank(name, "name must not be blank"); try { CodeSource codeSource = type.getProtectionDomain().getCodeSource(); URL jarUrl = codeSource.getLocation(); try (JarFile jarFile = new JarFile(new File(jarUrl.toURI()))) { Manifest manifest = jarFile.getManifest(); Attributes mainAttributes = manifest.getMainAttributes(); return Optional.ofNullable(mainAttributes.getValue(name)); } } catch (Exception e) { return Optional.empty(); } } }