Add ANSI 8-bit color support

Update ANSI property support to include an 8-bit (256 color) option.

See gh-18264
pull/18321/head
Toshiaki Maki 5 years ago committed by Phillip Webb
parent 5ca5ec8395
commit 65a27ef6d6

@ -28,6 +28,7 @@ import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.ansi.Ansi256PropertySource;
import org.springframework.boot.ansi.AnsiPropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
@ -43,6 +44,7 @@ import org.springframework.util.StreamUtils;
*
* @author Phillip Webb
* @author Vedran Pavic
* @author Toshiaki Maki
* @since 1.2.0
*/
public class ResourceBanner implements Banner {
@ -80,6 +82,7 @@ public class ResourceBanner implements Banner {
resolvers.add(environment);
resolvers.add(getVersionResolver(sourceClass));
resolvers.add(getAnsiResolver());
resolvers.add(getAnsi256Resolver());
resolvers.add(getTitleResolver(sourceClass));
return resolvers;
}
@ -123,6 +126,12 @@ public class ResourceBanner implements Banner {
return new PropertySourcesPropertyResolver(sources);
}
private PropertyResolver getAnsi256Resolver() {
MutablePropertySources sources = new MutablePropertySources();
sources.addFirst(new Ansi256PropertySource("ansi256"));
return new PropertySourcesPropertyResolver(sources);
}
private PropertyResolver getTitleResolver(Class<?> sourceClass) {
MutablePropertySources sources = new MutablePropertySources();
String applicationTitle = getApplicationTitle(sourceClass);

@ -0,0 +1,84 @@
/*
* Copyright 2012-2019 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
*
* https://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;
/**
* {@link AnsiElement} implementation for Ansi 256 colors.
* <p>
* use {@link Ansi256Color.Foreground} or {@link Ansi256Color.Background} as a concrete
* class.
*
* @author Toshiaki Maki
* @since 2.2.0
*/
public abstract class Ansi256Color implements AnsiElement {
/**
* color code
*/
final int colorCode;
/**
* @param colorCode color code (must be 0-255)
* @throws IllegalArgumentException if color code is not between 0 and 255.
*/
Ansi256Color(int colorCode) {
if (colorCode < 0 || colorCode > 255) {
throw new IllegalArgumentException("'colorCode' must be between 0 and 255.");
}
this.colorCode = colorCode;
}
/**
* {@link Ansi256Color} foreground colors.
*
* @author Toshiaki Maki
* @since 2.2.0
*/
public static class Foreground extends Ansi256Color {
public Foreground(int colorCode) {
super(colorCode);
}
@Override
public String toString() {
return "38;5;" + super.colorCode;
}
}
/**
* {@link Ansi256Color} background colors.
*
* @author Toshiaki Maki
* @since 2.2.0
*/
public static class Background extends Ansi256Color {
public Background(int colorCode) {
super(colorCode);
}
@Override
public String toString() {
return "48;5;" + super.colorCode;
}
}
}

@ -0,0 +1,63 @@
/*
* Copyright 2012-2019 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
*
* https://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 org.springframework.core.env.PropertyResolver;
import org.springframework.core.env.PropertySource;
import org.springframework.util.StringUtils;
/**
* {@link PropertyResolver} for {@link Ansi256Color.Background} and
* {@link Ansi256Color.Foreground} elements. Supports properties of the form
* {@code Ansi256Color.Foreground_N} and {@code Ansi256Color.Background_N} ({@code N} must
* be between 0 and 255).
*
* @author Toshiaki Maki
* @since 2.2.0
*/
public class Ansi256PropertySource extends PropertySource<AnsiElement> {
private static final String PREFIX = "Ansi256Color.";
private static final String FOREGROUND_PREFIX = PREFIX + "Foreground_";
private static final String BACKGROUND_PREFIX = PREFIX + "Background_";
/**
* Create a new {@link Ansi256PropertySource} instance.
* @param name the name of the property source
*/
public Ansi256PropertySource(String name) {
super(name);
}
@Override
public Object getProperty(String name) {
if (StringUtils.hasLength(name)) {
if (name.startsWith(FOREGROUND_PREFIX)) {
final int colorCode = Integer.parseInt(name.substring(FOREGROUND_PREFIX.length()));
return AnsiOutput.encode(new Ansi256Color.Foreground(colorCode));
}
else if (name.startsWith(BACKGROUND_PREFIX)) {
final int colorCode = Integer.parseInt(name.substring(BACKGROUND_PREFIX.length()));
return AnsiOutput.encode(new Ansi256Color.Background(colorCode));
}
}
return null;
}
}

@ -39,6 +39,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @author Phillip Webb
* @author Vedran Pavic
* @author Toshiaki Maki
*/
class ResourceBannerTests {
@ -95,6 +96,24 @@ class ResourceBannerTests {
assertThat(banner).startsWith("This is red.");
}
@Test
void renderWith256Colors() {
Resource resource = new ByteArrayResource(
"${Ansi256Color.Foreground_208}This is orange.${Ansi.NORMAL}".getBytes());
AnsiOutput.setEnabled(AnsiOutput.Enabled.ALWAYS);
String banner = printBanner(resource, null, null, null);
assertThat(banner).startsWith("\033[38;5;208mThis is orange.\u001B[0m");
}
@Test
void renderWith256ColorsButDisabled() {
Resource resource = new ByteArrayResource(
"${Ansi256Color.Foreground_208}This is orange.${Ansi.NORMAL}".getBytes());
AnsiOutput.setEnabled(AnsiOutput.Enabled.NEVER);
String banner = printBanner(resource, null, null, null);
assertThat(banner).startsWith("This is orange.");
}
@Test
void renderWithTitle() {
Resource resource = new ByteArrayResource("banner ${application.title} ${a}".getBytes());

@ -0,0 +1,54 @@
/*
* Copyright 2012-2019 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
*
* https://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 org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown;
/**
* Tests for {@link Ansi256Color}.
*
* @author Toshiaki Maki
*/
class Ansi256ColorTest {
@Test
void testForeground() {
final Ansi256Color ansi256Color = new Ansi256Color.Foreground(208);
assertThat(ansi256Color.toString()).isEqualTo("38;5;208");
}
@Test
void testBackground() {
final Ansi256Color ansi256Color = new Ansi256Color.Background(208);
assertThat(ansi256Color.toString()).isEqualTo("48;5;208");
}
@Test
void testIllegalColorCode() {
try {
new Ansi256Color.Foreground(256);
failBecauseExceptionWasNotThrown(IllegalArgumentException.class);
}
catch (IllegalArgumentException ex) {
assertThat(ex.getMessage()).isEqualTo("'colorCode' must be between 0 and 255.");
}
}
}

@ -0,0 +1,59 @@
/*
* Copyright 2012-2019 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
*
* https://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 org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link Ansi256PropertySource}.
*
* @author Toshiaki Maki
*/
class Ansi256PropertySourceTest {
private Ansi256PropertySource source = new Ansi256PropertySource("ansi256");
@AfterEach
void reset() {
AnsiOutput.setEnabled(AnsiOutput.Enabled.DETECT);
}
@Test
void getPropertyShouldConvertAnsi256ColorForeground() {
AnsiOutput.setEnabled(AnsiOutput.Enabled.ALWAYS);
final Object property = this.source.getProperty("Ansi256Color.Foreground_100");
assertThat(property).isEqualTo("\033[38;5;100m");
}
@Test
void getPropertyShouldConvertAnsi256ColorBackground() {
AnsiOutput.setEnabled(AnsiOutput.Enabled.ALWAYS);
final Object property = this.source.getProperty("Ansi256Color.Background_100");
assertThat(property).isEqualTo("\033[48;5;100m");
}
@Test
void getMissingPropertyShouldReturnNull() {
AnsiOutput.setEnabled(AnsiOutput.Enabled.ALWAYS);
final Object property = this.source.getProperty("Ansi256Color.ForeGround_100");
assertThat(property).isNull();
}
}
Loading…
Cancel
Save