Merge pull request #4647 from craigburke/image-banner
* image-banner: Add image banner documentation Rework ImageBanner Support Add ImageBanner color distance calculation Support image based banners
This commit is contained in:
		
						commit
						d3f1d126f3
					
				| 
						 | 
				
			
			@ -31,6 +31,11 @@ content into your application; rather pick only the properties that you need.
 | 
			
		|||
	# BANNER
 | 
			
		||||
	banner.charset=UTF-8 # Banner file encoding.
 | 
			
		||||
	banner.location=classpath:banner.txt # Banner file location.
 | 
			
		||||
	banner.image.location=classpath:banner.gif # (Banner image file location, jpg/png can also be used).
 | 
			
		||||
	banner.image.width= # Width of the banner image in chars (default 76)
 | 
			
		||||
	banner.image.hight= # Height of the banner image in chars (default based on image height)
 | 
			
		||||
	banner.image.margin= # Left hand image margin in chars (default 2)
 | 
			
		||||
	banner.image.invert= # If images should be inverted for dark terminal themes (default false)
 | 
			
		||||
 | 
			
		||||
	# LOGGING
 | 
			
		||||
	logging.config= # Location of the logging configuration file. For instance `classpath:logback.xml` for Logback
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -212,8 +212,8 @@ might have.
 | 
			
		|||
 | 
			
		||||
[source,properties,indent=0,subs="verbatim,quotes,attributes"]
 | 
			
		||||
----
 | 
			
		||||
	spring.main.web_environment=false
 | 
			
		||||
	spring.main.banner_mode=off
 | 
			
		||||
	spring.main.web-environment=false
 | 
			
		||||
	spring.main.banner-mode=off
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
and then the Spring Boot banner will not be printed on startup, and the application will
 | 
			
		||||
| 
						 | 
				
			
			@ -239,7 +239,7 @@ used with the following configuration:
 | 
			
		|||
[source,properties,indent=0,subs="verbatim,quotes,attributes"]
 | 
			
		||||
----
 | 
			
		||||
	spring.main.sources=com.acme.Config,com.acme.ExtraConfig
 | 
			
		||||
	spring.main.banner_mode=console
 | 
			
		||||
	spring.main.banner-mode=console
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
The actual application will _now_ show the banner (as overridden by configuration) and use
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,8 +52,11 @@ such as the user that launched the application.
 | 
			
		|||
The banner that is printed on start up can be changed by adding a `banner.txt` file
 | 
			
		||||
to your classpath, or by setting `banner.location` to the location of such a file.
 | 
			
		||||
If the file has an unusual encoding you can set `banner.charset` (default is `UTF-8`).
 | 
			
		||||
In addition a text file, you can also add a `banner.gif`, `banner.jpg` or `banner.png`
 | 
			
		||||
image file to your classpath, or set a `banner.image.location` property. Images will be
 | 
			
		||||
converted into an ASCII art representation and printed above any text banner.
 | 
			
		||||
 | 
			
		||||
You can use the following variables inside your `banner.txt` file:
 | 
			
		||||
Inside your `banner.txt` file you can use any of the following placeholders:
 | 
			
		||||
 | 
			
		||||
.Banner variables
 | 
			
		||||
|===
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 62 KiB  | 
| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
${Ansi.GREEN} :: Sample application build with Spring Boot${spring-boot.formatted-version} ::${Ansi.DEFAULT}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,180 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2015 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 org.springframework.boot;
 | 
			
		||||
 | 
			
		||||
import java.awt.Color;
 | 
			
		||||
import java.awt.Image;
 | 
			
		||||
import java.awt.image.BufferedImage;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.io.PrintStream;
 | 
			
		||||
 | 
			
		||||
import javax.imageio.ImageIO;
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.logging.Log;
 | 
			
		||||
import org.apache.commons.logging.LogFactory;
 | 
			
		||||
 | 
			
		||||
import org.springframework.boot.ansi.AnsiBackground;
 | 
			
		||||
import org.springframework.boot.ansi.AnsiColor;
 | 
			
		||||
import org.springframework.boot.ansi.AnsiColors;
 | 
			
		||||
import org.springframework.boot.ansi.AnsiElement;
 | 
			
		||||
import org.springframework.boot.ansi.AnsiOutput;
 | 
			
		||||
import org.springframework.boot.bind.RelaxedPropertyResolver;
 | 
			
		||||
import org.springframework.core.env.Environment;
 | 
			
		||||
import org.springframework.core.env.PropertyResolver;
 | 
			
		||||
import org.springframework.core.io.Resource;
 | 
			
		||||
import org.springframework.util.Assert;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Banner implementation that prints ASCII art generated from an image resource
 | 
			
		||||
 * {@link Resource}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Craig Burke
 | 
			
		||||
 * @author Phillip Webb
 | 
			
		||||
 * @since 1.4.0
 | 
			
		||||
 */
 | 
			
		||||
public class ImageBanner implements Banner {
 | 
			
		||||
 | 
			
		||||
	private static final Log log = LogFactory.getLog(ImageBanner.class);
 | 
			
		||||
 | 
			
		||||
	private static final double[] RGB_WEIGHT = { 0.2126d, 0.7152d, 0.0722d };
 | 
			
		||||
 | 
			
		||||
	private static final char[] PIXEL = { ' ', '.', '*', ':', 'o', '&', '8', '#', '@' };
 | 
			
		||||
 | 
			
		||||
	private static final int LUMINANCE_INCREMENT = 10;
 | 
			
		||||
 | 
			
		||||
	private static final int LUMINANCE_START = LUMINANCE_INCREMENT * PIXEL.length;
 | 
			
		||||
 | 
			
		||||
	private final Resource image;
 | 
			
		||||
 | 
			
		||||
	public ImageBanner(Resource image) {
 | 
			
		||||
		Assert.notNull(image, "Image must not be null");
 | 
			
		||||
		Assert.isTrue(image.exists(), "Image must exist");
 | 
			
		||||
		this.image = image;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void printBanner(Environment environment, Class<?> sourceClass,
 | 
			
		||||
			PrintStream out) {
 | 
			
		||||
		String headless = System.getProperty("java.awt.headless");
 | 
			
		||||
		try {
 | 
			
		||||
			System.setProperty("java.awt.headless", "true");
 | 
			
		||||
			printBanner(environment, out);
 | 
			
		||||
		}
 | 
			
		||||
		catch (Exception ex) {
 | 
			
		||||
			log.warn("Image banner not printable: " + this.image + " (" + ex.getClass()
 | 
			
		||||
					+ ": '" + ex.getMessage() + "')", ex);
 | 
			
		||||
		}
 | 
			
		||||
		finally {
 | 
			
		||||
			if (headless == null) {
 | 
			
		||||
				System.clearProperty("java.awt.headless");
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				System.setProperty("java.awt.headless", headless);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void printBanner(Environment environment, PrintStream out)
 | 
			
		||||
			throws IOException {
 | 
			
		||||
		PropertyResolver properties = new RelaxedPropertyResolver(environment,
 | 
			
		||||
				"banner.image.");
 | 
			
		||||
		int width = properties.getProperty("width", Integer.class, 76);
 | 
			
		||||
		int heigth = properties.getProperty("height", Integer.class, 0);
 | 
			
		||||
		int margin = properties.getProperty("margin", Integer.class, 2);
 | 
			
		||||
		boolean invert = properties.getProperty("invert", Boolean.class, false);
 | 
			
		||||
		BufferedImage image = readImage(width, heigth);
 | 
			
		||||
		printBanner(image, margin, invert, out);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private BufferedImage readImage(int width, int heigth) throws IOException {
 | 
			
		||||
		InputStream inputStream = this.image.getInputStream();
 | 
			
		||||
		try {
 | 
			
		||||
			BufferedImage image = ImageIO.read(inputStream);
 | 
			
		||||
			return resizeImage(image, width, heigth);
 | 
			
		||||
		}
 | 
			
		||||
		finally {
 | 
			
		||||
			inputStream.close();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private BufferedImage resizeImage(BufferedImage image, int width, int height) {
 | 
			
		||||
		if (width < 1) {
 | 
			
		||||
			width = 1;
 | 
			
		||||
		}
 | 
			
		||||
		if (height <= 0) {
 | 
			
		||||
			double aspectRatio = (double) width / image.getWidth() * 0.5;
 | 
			
		||||
			height = (int) Math.ceil(image.getHeight() * aspectRatio);
 | 
			
		||||
		}
 | 
			
		||||
		BufferedImage resized = new BufferedImage(width, height,
 | 
			
		||||
				BufferedImage.TYPE_INT_RGB);
 | 
			
		||||
		Image scaled = image.getScaledInstance(width, height, Image.SCALE_DEFAULT);
 | 
			
		||||
		resized.getGraphics().drawImage(scaled, 0, 0, null);
 | 
			
		||||
		return resized;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void printBanner(BufferedImage image, int margin, boolean invert,
 | 
			
		||||
			PrintStream out) {
 | 
			
		||||
		AnsiElement background = (invert ? AnsiBackground.BLACK : AnsiBackground.DEFAULT);
 | 
			
		||||
		out.print(AnsiOutput.encode(AnsiColor.DEFAULT));
 | 
			
		||||
		out.print(AnsiOutput.encode(background));
 | 
			
		||||
		out.println();
 | 
			
		||||
		out.println();
 | 
			
		||||
		AnsiColor lastColor = AnsiColor.DEFAULT;
 | 
			
		||||
		for (int y = 0; y < image.getHeight(); y++) {
 | 
			
		||||
			for (int i = 0; i < margin; i++) {
 | 
			
		||||
				out.print(" ");
 | 
			
		||||
			}
 | 
			
		||||
			for (int x = 0; x < image.getWidth(); x++) {
 | 
			
		||||
				Color color = new Color(image.getRGB(x, y), false);
 | 
			
		||||
				AnsiColor ansiColor = AnsiColors.getClosest(color);
 | 
			
		||||
				if (ansiColor != lastColor) {
 | 
			
		||||
					out.print(AnsiOutput.encode(ansiColor));
 | 
			
		||||
					lastColor = ansiColor;
 | 
			
		||||
				}
 | 
			
		||||
				out.print(getAsciiPixel(color, invert));
 | 
			
		||||
			}
 | 
			
		||||
			out.println();
 | 
			
		||||
		}
 | 
			
		||||
		out.print(AnsiOutput.encode(AnsiColor.DEFAULT));
 | 
			
		||||
		out.print(AnsiOutput.encode(AnsiBackground.DEFAULT));
 | 
			
		||||
		out.println();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private char getAsciiPixel(Color color, boolean dark) {
 | 
			
		||||
		double luminance = getLuminance(color, dark);
 | 
			
		||||
		for (int i = 0; i < PIXEL.length; i++) {
 | 
			
		||||
			if (luminance >= (LUMINANCE_START - (i * LUMINANCE_INCREMENT))) {
 | 
			
		||||
				return PIXEL[i];
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return PIXEL[PIXEL.length - 1];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private int getLuminance(Color color, boolean inverse) {
 | 
			
		||||
		double luminance = 0.0;
 | 
			
		||||
		luminance += getLuminance(color.getRed(), inverse, RGB_WEIGHT[0]);
 | 
			
		||||
		luminance += getLuminance(color.getGreen(), inverse, RGB_WEIGHT[1]);
 | 
			
		||||
		luminance += getLuminance(color.getBlue(), inverse, RGB_WEIGHT[2]);
 | 
			
		||||
		return (int) Math.ceil((luminance / 0xFF) * 100);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private double getLuminance(int component, boolean inverse, double weight) {
 | 
			
		||||
		return (inverse ? 0xFF - component : component) * weight;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -38,7 +38,7 @@ import org.springframework.util.Assert;
 | 
			
		|||
import org.springframework.util.StreamUtils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Banner implementation that prints from a source {@link Resource}.
 | 
			
		||||
 * Banner implementation that prints from a source text {@link Resource}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Phillip Webb
 | 
			
		||||
 * @author Vedran Pavic
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,9 +16,6 @@
 | 
			
		|||
 | 
			
		||||
package org.springframework.boot;
 | 
			
		||||
 | 
			
		||||
import java.io.ByteArrayOutputStream;
 | 
			
		||||
import java.io.PrintStream;
 | 
			
		||||
import java.io.UnsupportedEncodingException;
 | 
			
		||||
import java.lang.reflect.Constructor;
 | 
			
		||||
import java.security.AccessControlException;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
| 
						 | 
				
			
			@ -41,6 +38,7 @@ import org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader;
 | 
			
		|||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
 | 
			
		||||
import org.springframework.beans.factory.support.BeanNameGenerator;
 | 
			
		||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
 | 
			
		||||
import org.springframework.boot.Banner.Mode;
 | 
			
		||||
import org.springframework.boot.diagnostics.FailureAnalyzers;
 | 
			
		||||
import org.springframework.context.ApplicationContext;
 | 
			
		||||
import org.springframework.context.ApplicationContextInitializer;
 | 
			
		||||
| 
						 | 
				
			
			@ -140,6 +138,7 @@ import org.springframework.web.context.support.StandardServletEnvironment;
 | 
			
		|||
 * @author Christian Dupuis
 | 
			
		||||
 * @author Stephane Nicoll
 | 
			
		||||
 * @author Jeremy Rickard
 | 
			
		||||
 * @author Craig Burke
 | 
			
		||||
 * @see #run(Object, String[])
 | 
			
		||||
 * @see #run(Object[], String[])
 | 
			
		||||
 * @see #SpringApplication(Object...)
 | 
			
		||||
| 
						 | 
				
			
			@ -166,19 +165,17 @@ public class SpringApplication {
 | 
			
		|||
	/**
 | 
			
		||||
	 * Default banner location.
 | 
			
		||||
	 */
 | 
			
		||||
	public static final String BANNER_LOCATION_PROPERTY_VALUE = "banner.txt";
 | 
			
		||||
	public static final String BANNER_LOCATION_PROPERTY_VALUE = SpringApplicationBannerPrinter.DEFAULT_BANNER_LOCATION;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Banner location property key.
 | 
			
		||||
	 */
 | 
			
		||||
	public static final String BANNER_LOCATION_PROPERTY = "banner.location";
 | 
			
		||||
	public static final String BANNER_LOCATION_PROPERTY = SpringApplicationBannerPrinter.BANNER_LOCATION_PROPERTY;
 | 
			
		||||
 | 
			
		||||
	private static final String CONFIGURABLE_WEB_ENVIRONMENT_CLASS = "org.springframework.web.context.ConfigurableWebEnvironment";
 | 
			
		||||
 | 
			
		||||
	private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";
 | 
			
		||||
 | 
			
		||||
	private static final Banner DEFAULT_BANNER = new SpringBootBanner();
 | 
			
		||||
 | 
			
		||||
	private static final Set<String> SERVLET_ENVIRONMENT_SOURCE_NAMES;
 | 
			
		||||
 | 
			
		||||
	static {
 | 
			
		||||
| 
						 | 
				
			
			@ -542,42 +539,16 @@ public class SpringApplication {
 | 
			
		|||
	 * @see #setBannerMode
 | 
			
		||||
	 */
 | 
			
		||||
	protected void printBanner(Environment environment) {
 | 
			
		||||
		Banner selectedBanner = selectBanner(environment);
 | 
			
		||||
		if (this.bannerMode == Banner.Mode.LOG) {
 | 
			
		||||
			try {
 | 
			
		||||
				logger.info(createStringFromBanner(selectedBanner, environment));
 | 
			
		||||
			}
 | 
			
		||||
			catch (UnsupportedEncodingException ex) {
 | 
			
		||||
				logger.warn("Failed to create String for banner", ex);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			selectedBanner.printBanner(environment, this.mainApplicationClass,
 | 
			
		||||
					System.out);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private Banner selectBanner(Environment environment) {
 | 
			
		||||
		String location = environment.getProperty(BANNER_LOCATION_PROPERTY,
 | 
			
		||||
				BANNER_LOCATION_PROPERTY_VALUE);
 | 
			
		||||
		ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader
 | 
			
		||||
				: new DefaultResourceLoader(getClassLoader());
 | 
			
		||||
		Resource resource = resourceLoader.getResource(location);
 | 
			
		||||
		if (resource.exists()) {
 | 
			
		||||
			return new ResourceBanner(resource);
 | 
			
		||||
		SpringApplicationBannerPrinter banner = new SpringApplicationBannerPrinter(resourceLoader,
 | 
			
		||||
				this.banner);
 | 
			
		||||
		if (this.bannerMode == Mode.LOG) {
 | 
			
		||||
			banner.print(environment, this.mainApplicationClass, logger);
 | 
			
		||||
		}
 | 
			
		||||
		if (this.banner != null) {
 | 
			
		||||
			return this.banner;
 | 
			
		||||
		else {
 | 
			
		||||
			banner.print(environment, this.mainApplicationClass, System.out);
 | 
			
		||||
		}
 | 
			
		||||
		return DEFAULT_BANNER;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private String createStringFromBanner(Banner banner, Environment environment)
 | 
			
		||||
			throws UnsupportedEncodingException {
 | 
			
		||||
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
 | 
			
		||||
		banner.printBanner(environment, this.mainApplicationClass, new PrintStream(baos));
 | 
			
		||||
		String charset = environment.getProperty("banner.charset", "UTF-8");
 | 
			
		||||
		return baos.toString(charset);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,146 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2016 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 org.springframework.boot;
 | 
			
		||||
 | 
			
		||||
import java.io.ByteArrayOutputStream;
 | 
			
		||||
import java.io.PrintStream;
 | 
			
		||||
import java.io.UnsupportedEncodingException;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import org.apache.commons.logging.Log;
 | 
			
		||||
 | 
			
		||||
import org.springframework.core.env.Environment;
 | 
			
		||||
import org.springframework.core.io.Resource;
 | 
			
		||||
import org.springframework.core.io.ResourceLoader;
 | 
			
		||||
import org.springframework.util.StringUtils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class used by {@link SpringApplication} to print the application banner.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Phillip Webb
 | 
			
		||||
 */
 | 
			
		||||
class SpringApplicationBannerPrinter {
 | 
			
		||||
 | 
			
		||||
	static final String BANNER_LOCATION_PROPERTY = "banner.location";
 | 
			
		||||
 | 
			
		||||
	static final String BANNER_IMAGE_LOCATION_PROPERTY = "banner.image.location";
 | 
			
		||||
 | 
			
		||||
	static final String DEFAULT_BANNER_LOCATION = "banner.txt";
 | 
			
		||||
 | 
			
		||||
	static final String[] IMAGE_EXTENSION = { "gif", "jpg", "png" };
 | 
			
		||||
 | 
			
		||||
	private static final Banner DEFAULT_BANNER = new SpringBootBanner();
 | 
			
		||||
 | 
			
		||||
	private final ResourceLoader resourceLoader;
 | 
			
		||||
 | 
			
		||||
	private final Banner fallbackBanner;
 | 
			
		||||
 | 
			
		||||
	SpringApplicationBannerPrinter(ResourceLoader resourceLoader, Banner fallbackBanner) {
 | 
			
		||||
		this.resourceLoader = resourceLoader;
 | 
			
		||||
		this.fallbackBanner = fallbackBanner;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void print(Environment environment, Class<?> sourceClass, Log logger) {
 | 
			
		||||
		Banner banner = getBanner(environment, this.fallbackBanner);
 | 
			
		||||
		try {
 | 
			
		||||
			logger.info(createStringFromBanner(banner, environment, sourceClass));
 | 
			
		||||
		}
 | 
			
		||||
		catch (UnsupportedEncodingException ex) {
 | 
			
		||||
			logger.warn("Failed to create String for banner", ex);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void print(Environment environment, Class<?> sourceClass, PrintStream out) {
 | 
			
		||||
		Banner banner = getBanner(environment, this.fallbackBanner);
 | 
			
		||||
		banner.printBanner(environment, sourceClass, out);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private Banner getBanner(Environment environment, Banner definedBanner) {
 | 
			
		||||
		Banners banners = new Banners();
 | 
			
		||||
		banners.addIfNotNull(getImageBanner(environment));
 | 
			
		||||
		banners.addIfNotNull(getTextBanner(environment));
 | 
			
		||||
		if (banners.hasAtLeastOneBanner()) {
 | 
			
		||||
			return banners;
 | 
			
		||||
		}
 | 
			
		||||
		if (this.fallbackBanner != null) {
 | 
			
		||||
			return this.fallbackBanner;
 | 
			
		||||
		}
 | 
			
		||||
		return DEFAULT_BANNER;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private Banner getTextBanner(Environment environment) {
 | 
			
		||||
		String location = environment.getProperty(BANNER_LOCATION_PROPERTY,
 | 
			
		||||
				DEFAULT_BANNER_LOCATION);
 | 
			
		||||
		Resource resource = this.resourceLoader.getResource(location);
 | 
			
		||||
		if (resource.exists()) {
 | 
			
		||||
			return new ResourceBanner(resource);
 | 
			
		||||
		}
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private Banner getImageBanner(Environment environment) {
 | 
			
		||||
		String location = environment.getProperty(BANNER_IMAGE_LOCATION_PROPERTY);
 | 
			
		||||
		if (StringUtils.hasLength(location)) {
 | 
			
		||||
			Resource resource = this.resourceLoader.getResource(location);
 | 
			
		||||
			return (resource.exists() ? new ImageBanner(resource) : null);
 | 
			
		||||
		}
 | 
			
		||||
		for (String ext : IMAGE_EXTENSION) {
 | 
			
		||||
			Resource resource = this.resourceLoader.getResource("banner." + ext);
 | 
			
		||||
			if (resource.exists()) {
 | 
			
		||||
				return new ImageBanner(resource);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private String createStringFromBanner(Banner banner, Environment environment,
 | 
			
		||||
			Class<?> mainApplicationClass) throws UnsupportedEncodingException {
 | 
			
		||||
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
 | 
			
		||||
		banner.printBanner(environment, mainApplicationClass, new PrintStream(baos));
 | 
			
		||||
		String charset = environment.getProperty("banner.charset", "UTF-8");
 | 
			
		||||
		return baos.toString(charset);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * {@link Banner} comprised of other {@link Banner Banners}.
 | 
			
		||||
	 */
 | 
			
		||||
	private static class Banners implements Banner {
 | 
			
		||||
 | 
			
		||||
		private final List<Banner> banners = new ArrayList<Banner>();
 | 
			
		||||
 | 
			
		||||
		public void addIfNotNull(Banner banner) {
 | 
			
		||||
			if (banner != null) {
 | 
			
		||||
				this.banners.add(banner);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public boolean hasAtLeastOneBanner() {
 | 
			
		||||
			return !this.banners.isEmpty();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public void printBanner(Environment environment, Class<?> sourceClass,
 | 
			
		||||
				PrintStream out) {
 | 
			
		||||
			for (Banner banner : this.banners) {
 | 
			
		||||
				banner.printBanner(environment, sourceClass, out);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,140 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2016 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 org.springframework.boot.ansi;
 | 
			
		||||
 | 
			
		||||
import java.awt.Color;
 | 
			
		||||
import java.awt.color.ColorSpace;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.LinkedHashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Map.Entry;
 | 
			
		||||
 | 
			
		||||
import org.springframework.util.Assert;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Utility for working with {@link AnsiColor} in the context of {@link Color AWT Colors}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Craig Burke
 | 
			
		||||
 * @author Ruben Dijkstra
 | 
			
		||||
 * @author Phillip Webb
 | 
			
		||||
 * @author Michael Simons
 | 
			
		||||
 * @since 1.4.0
 | 
			
		||||
 */
 | 
			
		||||
public final class AnsiColors {
 | 
			
		||||
 | 
			
		||||
	private static final Map<AnsiColor, LabColor> ANSI_COLOR_MAP;
 | 
			
		||||
 | 
			
		||||
	static {
 | 
			
		||||
		Map<AnsiColor, LabColor> colorMap = new LinkedHashMap<AnsiColor, LabColor>();
 | 
			
		||||
		colorMap.put(AnsiColor.BLACK, new LabColor(0x000000));
 | 
			
		||||
		colorMap.put(AnsiColor.RED, new LabColor(0xAA0000));
 | 
			
		||||
		colorMap.put(AnsiColor.GREEN, new LabColor(0x00AA00));
 | 
			
		||||
		colorMap.put(AnsiColor.YELLOW, new LabColor(0xAA5500));
 | 
			
		||||
		colorMap.put(AnsiColor.BLUE, new LabColor(0x0000AA));
 | 
			
		||||
		colorMap.put(AnsiColor.MAGENTA, new LabColor(0xAA00AA));
 | 
			
		||||
		colorMap.put(AnsiColor.CYAN, new LabColor(0x00AAAA));
 | 
			
		||||
		colorMap.put(AnsiColor.WHITE, new LabColor(0xAAAAAA));
 | 
			
		||||
		colorMap.put(AnsiColor.BRIGHT_BLACK, new LabColor(0x555555));
 | 
			
		||||
		colorMap.put(AnsiColor.BRIGHT_RED, new LabColor(0xFF5555));
 | 
			
		||||
		colorMap.put(AnsiColor.BRIGHT_GREEN, new LabColor(0x55FF00));
 | 
			
		||||
		colorMap.put(AnsiColor.BRIGHT_YELLOW, new LabColor(0xFFFF55));
 | 
			
		||||
		colorMap.put(AnsiColor.BRIGHT_BLUE, new LabColor(0x5555FF));
 | 
			
		||||
		colorMap.put(AnsiColor.BRIGHT_MAGENTA, new LabColor(0xFF55FF));
 | 
			
		||||
		colorMap.put(AnsiColor.BRIGHT_CYAN, new LabColor(0x55FFFF));
 | 
			
		||||
		colorMap.put(AnsiColor.BRIGHT_WHITE, new LabColor(0xFFFFFF));
 | 
			
		||||
		ANSI_COLOR_MAP = Collections.unmodifiableMap(colorMap);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private AnsiColors() {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static AnsiColor getClosest(Color color) {
 | 
			
		||||
		return getClosest(new LabColor(color));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static AnsiColor getClosest(LabColor color) {
 | 
			
		||||
		AnsiColor result = null;
 | 
			
		||||
		double resultDistance = Float.MAX_VALUE;
 | 
			
		||||
		for (Entry<AnsiColor, LabColor> entry : ANSI_COLOR_MAP.entrySet()) {
 | 
			
		||||
			double distance = color.getDistance(entry.getValue());
 | 
			
		||||
			if (result == null || distance < resultDistance) {
 | 
			
		||||
				resultDistance = distance;
 | 
			
		||||
				result = entry.getKey();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return result;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Represents a color stored in LAB form.
 | 
			
		||||
	 */
 | 
			
		||||
	private static final class LabColor {
 | 
			
		||||
 | 
			
		||||
		private static final ColorSpace XYZ_COLOR_SPACE = ColorSpace
 | 
			
		||||
				.getInstance(ColorSpace.CS_CIEXYZ);
 | 
			
		||||
 | 
			
		||||
		private final double l;
 | 
			
		||||
 | 
			
		||||
		private final double a;
 | 
			
		||||
 | 
			
		||||
		private final double b;
 | 
			
		||||
 | 
			
		||||
		LabColor(Integer rgb) {
 | 
			
		||||
			this(rgb == null ? (Color) null : new Color(rgb));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		LabColor(Color color) {
 | 
			
		||||
			Assert.notNull(color, "Color must not be null");
 | 
			
		||||
			float[] lab = fromXyz(color.getColorComponents(XYZ_COLOR_SPACE, null));
 | 
			
		||||
			this.l = lab[0];
 | 
			
		||||
			this.a = lab[1];
 | 
			
		||||
			this.b = lab[2];
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private float[] fromXyz(float[] xyz) {
 | 
			
		||||
			return fromXyz(xyz[0], xyz[1], xyz[2]);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private float[] fromXyz(float x, float y, float z) {
 | 
			
		||||
			double l = (f(y) - 16.0) * 116.0;
 | 
			
		||||
			double a = (f(x) - f(y)) * 500.0;
 | 
			
		||||
			double b = (f(y) - f(z)) * 200.0;
 | 
			
		||||
			return new float[] { (float) l, (float) a, (float) b };
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private double f(double t) {
 | 
			
		||||
			return (t > (216.0 / 24389.0) ? Math.cbrt(t)
 | 
			
		||||
					: (1.0 / 3.0) * Math.pow(29.0 / 6.0, 2) * t + (4.0 / 29.0));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// See http://en.wikipedia.org/wiki/Color_difference#CIE94
 | 
			
		||||
		public double getDistance(LabColor other) {
 | 
			
		||||
			double c1 = Math.sqrt(this.a * this.a + this.b * this.b);
 | 
			
		||||
			double deltaC = c1 - Math.sqrt(other.a * other.a + other.b * other.b);
 | 
			
		||||
			double deltaA = this.a - other.a;
 | 
			
		||||
			double deltaB = this.b - other.b;
 | 
			
		||||
			double deltaH = Math.sqrt(
 | 
			
		||||
					Math.max(0.0, deltaA * deltaA + deltaB * deltaB - deltaC * deltaC));
 | 
			
		||||
			return Math.sqrt(Math.max(0.0,
 | 
			
		||||
					Math.pow((this.l - other.l) / (1.0), 2)
 | 
			
		||||
							+ Math.pow(deltaC / (1 + 0.045 * c1), 2)
 | 
			
		||||
							+ Math.pow(deltaH / (1 + 0.015 * c1), 2.0)));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,76 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2016 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 org.springframework.boot.ansi;
 | 
			
		||||
 | 
			
		||||
import java.awt.Color;
 | 
			
		||||
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests for {@link AnsiColors}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Phillip Webb
 | 
			
		||||
 */
 | 
			
		||||
public class AnsiColorsTests {
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void getClosestWhenExactMatchShouldReturnAnsiColor() throws Exception {
 | 
			
		||||
		assertThat(getClosest(0x000000)).isEqualTo(AnsiColor.BLACK);
 | 
			
		||||
		assertThat(getClosest(0xAA0000)).isEqualTo(AnsiColor.RED);
 | 
			
		||||
		assertThat(getClosest(0x00AA00)).isEqualTo(AnsiColor.GREEN);
 | 
			
		||||
		assertThat(getClosest(0xAA5500)).isEqualTo(AnsiColor.YELLOW);
 | 
			
		||||
		assertThat(getClosest(0x0000AA)).isEqualTo(AnsiColor.BLUE);
 | 
			
		||||
		assertThat(getClosest(0xAA00AA)).isEqualTo(AnsiColor.MAGENTA);
 | 
			
		||||
		assertThat(getClosest(0x00AAAA)).isEqualTo(AnsiColor.CYAN);
 | 
			
		||||
		assertThat(getClosest(0xAAAAAA)).isEqualTo(AnsiColor.WHITE);
 | 
			
		||||
		assertThat(getClosest(0x555555)).isEqualTo(AnsiColor.BRIGHT_BLACK);
 | 
			
		||||
		assertThat(getClosest(0xFF5555)).isEqualTo(AnsiColor.BRIGHT_RED);
 | 
			
		||||
		assertThat(getClosest(0x55FF00)).isEqualTo(AnsiColor.BRIGHT_GREEN);
 | 
			
		||||
		assertThat(getClosest(0xFFFF55)).isEqualTo(AnsiColor.BRIGHT_YELLOW);
 | 
			
		||||
		assertThat(getClosest(0x5555FF)).isEqualTo(AnsiColor.BRIGHT_BLUE);
 | 
			
		||||
		assertThat(getClosest(0xFF55FF)).isEqualTo(AnsiColor.BRIGHT_MAGENTA);
 | 
			
		||||
		assertThat(getClosest(0x55FFFF)).isEqualTo(AnsiColor.BRIGHT_CYAN);
 | 
			
		||||
		assertThat(getClosest(0xFFFFFF)).isEqualTo(AnsiColor.BRIGHT_WHITE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void getClosestWhenCloseShouldReturnAnsiColor() throws Exception {
 | 
			
		||||
		assertThat(getClosest(0x292424)).isEqualTo(AnsiColor.BLACK);
 | 
			
		||||
		assertThat(getClosest(0x8C1919)).isEqualTo(AnsiColor.RED);
 | 
			
		||||
		assertThat(getClosest(0x0BA10B)).isEqualTo(AnsiColor.GREEN);
 | 
			
		||||
		assertThat(getClosest(0xB55F09)).isEqualTo(AnsiColor.YELLOW);
 | 
			
		||||
		assertThat(getClosest(0x0B0BA1)).isEqualTo(AnsiColor.BLUE);
 | 
			
		||||
		assertThat(getClosest(0xA312A3)).isEqualTo(AnsiColor.MAGENTA);
 | 
			
		||||
		assertThat(getClosest(0x0BB5B5)).isEqualTo(AnsiColor.CYAN);
 | 
			
		||||
		assertThat(getClosest(0xBAB6B6)).isEqualTo(AnsiColor.WHITE);
 | 
			
		||||
		assertThat(getClosest(0x615A5A)).isEqualTo(AnsiColor.BRIGHT_BLACK);
 | 
			
		||||
		assertThat(getClosest(0xF23333)).isEqualTo(AnsiColor.BRIGHT_RED);
 | 
			
		||||
		assertThat(getClosest(0x55E80C)).isEqualTo(AnsiColor.BRIGHT_GREEN);
 | 
			
		||||
		assertThat(getClosest(0xF5F54C)).isEqualTo(AnsiColor.BRIGHT_YELLOW);
 | 
			
		||||
		assertThat(getClosest(0x5656F0)).isEqualTo(AnsiColor.BRIGHT_BLUE);
 | 
			
		||||
		assertThat(getClosest(0xFA50FA)).isEqualTo(AnsiColor.BRIGHT_MAGENTA);
 | 
			
		||||
		assertThat(getClosest(0x56F5F5)).isEqualTo(AnsiColor.BRIGHT_CYAN);
 | 
			
		||||
		assertThat(getClosest(0xEDF5F5)).isEqualTo(AnsiColor.BRIGHT_WHITE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private AnsiColor getClosest(int rgb) {
 | 
			
		||||
		return AnsiColors.getClosest(new Color(rgb));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -13,9 +13,36 @@
 | 
			
		|||
  {
 | 
			
		||||
    "name": "banner.location",
 | 
			
		||||
    "type": "org.springframework.core.io.Resource",
 | 
			
		||||
    "description": "Banner file location.",
 | 
			
		||||
    "description": "Banner text resource location.",
 | 
			
		||||
    "defaultValue": "classpath:banner.txt"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "banner.image.location",
 | 
			
		||||
    "type": "org.springframework.core.io.Resource",
 | 
			
		||||
    "description": "Banner image file location.",
 | 
			
		||||
    "defaultValue": "banner.gif"
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "banner.image.width",
 | 
			
		||||
    "type": "java.lang.Integer",
 | 
			
		||||
    "description": "Banner image width (in chars)."
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "banner.image.height",
 | 
			
		||||
    "type": "java.lang.Integer",
 | 
			
		||||
    "description": "Banner image height (in chars)."
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "banner.image.margin",
 | 
			
		||||
    "type": "java.lang.Integer",
 | 
			
		||||
    "description": "Left hand image height (in chars)."
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "banner.image.invert",
 | 
			
		||||
    "type": "java.lang.Boolean",
 | 
			
		||||
    "description": "Invert images for dark console themes.",
 | 
			
		||||
    "defaultValue": false
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "name": "debug",
 | 
			
		||||
    "type": "java.lang.Boolean",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,191 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright 2012-2014 the original author or authors.
 | 
			
		||||
 *
 | 
			
		||||
 * 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 org.springframework.boot;
 | 
			
		||||
 | 
			
		||||
import java.io.ByteArrayOutputStream;
 | 
			
		||||
import java.io.PrintStream;
 | 
			
		||||
 | 
			
		||||
import org.junit.After;
 | 
			
		||||
import org.junit.Before;
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
 | 
			
		||||
import org.springframework.boot.ansi.AnsiBackground;
 | 
			
		||||
import org.springframework.boot.ansi.AnsiColor;
 | 
			
		||||
import org.springframework.boot.ansi.AnsiOutput;
 | 
			
		||||
import org.springframework.boot.ansi.AnsiOutput.Enabled;
 | 
			
		||||
import org.springframework.core.env.ConfigurableEnvironment;
 | 
			
		||||
import org.springframework.core.io.ClassPathResource;
 | 
			
		||||
import org.springframework.mock.env.MockEnvironment;
 | 
			
		||||
import org.springframework.test.context.support.TestPropertySourceUtils;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests for {@link ImageBanner}.
 | 
			
		||||
 *
 | 
			
		||||
 * @author Craig Burke
 | 
			
		||||
 * @author Phillip Webb
 | 
			
		||||
 */
 | 
			
		||||
public class ImageBannerTests {
 | 
			
		||||
 | 
			
		||||
	private static final String NEW_LINE = System.getProperty("line.separator");
 | 
			
		||||
 | 
			
		||||
	private static final char HIGH_LUMINANCE_CHARACTER = ' ';
 | 
			
		||||
 | 
			
		||||
	private static final char LOW_LUMINANCE_CHARACTER = '@';
 | 
			
		||||
 | 
			
		||||
	private static final String INVERT_TRUE = "banner.image.invert=true";
 | 
			
		||||
 | 
			
		||||
	@Before
 | 
			
		||||
	public void setup() {
 | 
			
		||||
		AnsiOutput.setEnabled(AnsiOutput.Enabled.ALWAYS);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@After
 | 
			
		||||
	public void cleanup() {
 | 
			
		||||
		AnsiOutput.setEnabled(Enabled.DETECT);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void printBannerShouldResetForegroundAndBackground() {
 | 
			
		||||
		String banner = printBanner("black-and-white.gif");
 | 
			
		||||
		String expected = AnsiOutput.encode(AnsiColor.DEFAULT)
 | 
			
		||||
				+ AnsiOutput.encode(AnsiBackground.DEFAULT);
 | 
			
		||||
		assertThat(banner).startsWith(expected);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void printBannerWhenInvertedShouldResetForegroundAndBackground() {
 | 
			
		||||
		String banner = printBanner("black-and-white.gif", INVERT_TRUE);
 | 
			
		||||
		String expected = AnsiOutput.encode(AnsiColor.DEFAULT)
 | 
			
		||||
				+ AnsiOutput.encode(AnsiBackground.BLACK);
 | 
			
		||||
		assertThat(banner).startsWith(expected);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void printBannerShouldPrintWhiteAsBrightWhiteHighLuminance() {
 | 
			
		||||
		String banner = printBanner("black-and-white.gif");
 | 
			
		||||
		String expected = AnsiOutput.encode(AnsiColor.BRIGHT_WHITE)
 | 
			
		||||
				+ HIGH_LUMINANCE_CHARACTER;
 | 
			
		||||
		assertThat(banner).contains(expected);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void printBannerWhenInvertedShouldPrintWhiteAsBrightWhiteLowLuminance() {
 | 
			
		||||
		String banner = printBanner("black-and-white.gif", INVERT_TRUE);
 | 
			
		||||
		String expected = AnsiOutput.encode(AnsiColor.BRIGHT_WHITE)
 | 
			
		||||
				+ LOW_LUMINANCE_CHARACTER;
 | 
			
		||||
		assertThat(banner).contains(expected);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void printBannerShouldPrintBlackAsBlackLowLuminance() {
 | 
			
		||||
		String banner = printBanner("black-and-white.gif");
 | 
			
		||||
		String expected = AnsiOutput.encode(AnsiColor.BLACK) + LOW_LUMINANCE_CHARACTER;
 | 
			
		||||
		assertThat(banner).contains(expected);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void printBannerWhenInvertedShouldPrintBlackAsBlackHighLuminance() {
 | 
			
		||||
		String banner = printBanner("black-and-white.gif", INVERT_TRUE);
 | 
			
		||||
		String expected = AnsiOutput.encode(AnsiColor.BLACK) + HIGH_LUMINANCE_CHARACTER;
 | 
			
		||||
		assertThat(banner).contains(expected);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void printBannerWhenShouldPrintAllColors() {
 | 
			
		||||
		String banner = printBanner("colors.gif");
 | 
			
		||||
		for (AnsiColor color : AnsiColor.values()) {
 | 
			
		||||
			if (color != AnsiColor.DEFAULT) {
 | 
			
		||||
				assertThat(banner).contains(AnsiOutput.encode(color));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void printBannerShouldRenderGradient() throws Exception {
 | 
			
		||||
		AnsiOutput.setEnabled(AnsiOutput.Enabled.NEVER);
 | 
			
		||||
		String banner = printBanner("gradient.gif", "banner.image.width=10",
 | 
			
		||||
				"banner.image.margin=0");
 | 
			
		||||
		System.out.println(banner);
 | 
			
		||||
		assertThat(banner).contains("@#8&o:*.  ");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void printBannerShouldCalculateHeight() throws Exception {
 | 
			
		||||
		String banner = printBanner("large.gif", "banner.image.width=20");
 | 
			
		||||
		assertThat(getBannerHeight(banner)).isEqualTo(10);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void printBannerWhenHasHeightPropertyShouldSetHeight() throws Exception {
 | 
			
		||||
		String banner = printBanner("large.gif", "banner.image.width=20",
 | 
			
		||||
				"banner.image.height=30");
 | 
			
		||||
		assertThat(getBannerHeight(banner)).isEqualTo(30);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void printBannerShouldCapWidthAndCalculateHeight() throws Exception {
 | 
			
		||||
		AnsiOutput.setEnabled(AnsiOutput.Enabled.NEVER);
 | 
			
		||||
		String banner = printBanner("large.gif", "banner.image.margin=0");
 | 
			
		||||
		assertThat(getBannerWidth(banner)).isEqualTo(76);
 | 
			
		||||
		assertThat(getBannerHeight(banner)).isEqualTo(37);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void printBannerShouldPrintMargin() throws Exception {
 | 
			
		||||
		AnsiOutput.setEnabled(AnsiOutput.Enabled.NEVER);
 | 
			
		||||
		String banner = printBanner("large.gif");
 | 
			
		||||
		String[] lines = banner.split(NEW_LINE);
 | 
			
		||||
		for (int i = 2; i < lines.length - 1; i++) {
 | 
			
		||||
			assertThat(lines[i]).startsWith("  @");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void printBannerWhenHasMarginPropertShouldPrintSizedMargin() throws Exception {
 | 
			
		||||
		AnsiOutput.setEnabled(AnsiOutput.Enabled.NEVER);
 | 
			
		||||
		String banner = printBanner("large.gif", "banner.image.margin=4");
 | 
			
		||||
		String[] lines = banner.split(NEW_LINE);
 | 
			
		||||
		for (int i = 2; i < lines.length - 1; i++) {
 | 
			
		||||
			assertThat(lines[i]).startsWith("    @");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private int getBannerHeight(String banner) {
 | 
			
		||||
		return banner.split(NEW_LINE).length - 3;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private int getBannerWidth(String banner) {
 | 
			
		||||
		int width = 0;
 | 
			
		||||
		for (String line : banner.split(NEW_LINE)) {
 | 
			
		||||
			width = Math.max(width, line.length());
 | 
			
		||||
		}
 | 
			
		||||
		return width;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private String printBanner(String path, String... properties) {
 | 
			
		||||
		ImageBanner banner = new ImageBanner(new ClassPathResource(path, getClass()));
 | 
			
		||||
		ConfigurableEnvironment environment = new MockEnvironment();
 | 
			
		||||
		TestPropertySourceUtils.addInlinedPropertiesToEnvironment(environment,
 | 
			
		||||
				properties);
 | 
			
		||||
		ByteArrayOutputStream out = new ByteArrayOutputStream();
 | 
			
		||||
		banner.printBanner(environment, getClass(), new PrintStream(out));
 | 
			
		||||
		return out.toString();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -19,8 +19,10 @@ package org.springframework.boot;
 | 
			
		|||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.LinkedHashSet;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.concurrent.atomic.AtomicReference;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -65,7 +67,9 @@ import org.springframework.core.env.Environment;
 | 
			
		|||
import org.springframework.core.env.MapPropertySource;
 | 
			
		||||
import org.springframework.core.env.PropertySource;
 | 
			
		||||
import org.springframework.core.env.StandardEnvironment;
 | 
			
		||||
import org.springframework.core.io.ClassPathResource;
 | 
			
		||||
import org.springframework.core.io.DefaultResourceLoader;
 | 
			
		||||
import org.springframework.core.io.Resource;
 | 
			
		||||
import org.springframework.core.io.ResourceLoader;
 | 
			
		||||
import org.springframework.test.context.support.TestPropertySourceUtils;
 | 
			
		||||
import org.springframework.util.StringUtils;
 | 
			
		||||
| 
						 | 
				
			
			@ -90,6 +94,7 @@ import static org.mockito.Mockito.verify;
 | 
			
		|||
 * @author Christian Dupuis
 | 
			
		||||
 * @author Stephane Nicoll
 | 
			
		||||
 * @author Jeremy Rickard
 | 
			
		||||
 * @author Craig Burke
 | 
			
		||||
 */
 | 
			
		||||
public class SpringApplicationTests {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -191,6 +196,29 @@ public class SpringApplicationTests {
 | 
			
		|||
				.startsWith(String.format("Running a Test!%n%n123456"));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void imageBannerAndTextBanner() throws Exception {
 | 
			
		||||
		SpringApplication application = new SpringApplication(ExampleConfig.class);
 | 
			
		||||
		MockResourceLoader resourceLoader = new MockResourceLoader();
 | 
			
		||||
		resourceLoader.addResource("banner.gif", "black-and-white.gif");
 | 
			
		||||
		resourceLoader.addResource("banner.txt", "foobar.txt");
 | 
			
		||||
		application.setWebEnvironment(false);
 | 
			
		||||
		application.setResourceLoader(resourceLoader);
 | 
			
		||||
		application.run();
 | 
			
		||||
		assertThat(this.output.toString()).contains("@@@@").contains("Foo Bar");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void imageBannerLoads() throws Exception {
 | 
			
		||||
		SpringApplication application = new SpringApplication(ExampleConfig.class);
 | 
			
		||||
		MockResourceLoader resourceLoader = new MockResourceLoader();
 | 
			
		||||
		resourceLoader.addResource("banner.gif", "black-and-white.gif");
 | 
			
		||||
		application.setWebEnvironment(false);
 | 
			
		||||
		application.setResourceLoader(resourceLoader);
 | 
			
		||||
		application.run();
 | 
			
		||||
		assertThat(this.output.toString()).contains("@@@@@@");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Test
 | 
			
		||||
	public void logsNoActiveProfiles() throws Exception {
 | 
			
		||||
		SpringApplication application = new SpringApplication(ExampleConfig.class);
 | 
			
		||||
| 
						 | 
				
			
			@ -1089,4 +1117,26 @@ public class SpringApplicationTests {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static class MockResourceLoader implements ResourceLoader {
 | 
			
		||||
 | 
			
		||||
		private final Map<String, Resource> resources = new HashMap<String, Resource>();
 | 
			
		||||
 | 
			
		||||
		public void addResource(String source, String path) {
 | 
			
		||||
			this.resources.put(source, new ClassPathResource(path, getClass()));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public Resource getResource(String path) {
 | 
			
		||||
			Resource resource = this.resources.get(path);
 | 
			
		||||
			return (resource == null ? new ClassPathResource("doesnotexit") : resource);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public ClassLoader getClassLoader() {
 | 
			
		||||
			return getClass().getClassLoader();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 44 B  | 
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 95 B  | 
| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
Foo Bar
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 817 B  | 
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 142 B  | 
		Loading…
	
		Reference in New Issue