diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ResourceBanner.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ResourceBanner.java index 650003bc73..11d9991ed0 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ResourceBanner.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ResourceBanner.java @@ -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); diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ansi/Ansi256Color.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ansi/Ansi256Color.java new file mode 100644 index 0000000000..e93f166602 --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ansi/Ansi256Color.java @@ -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. + *

+ * 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; + } + + } + +} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ansi/Ansi256PropertySource.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ansi/Ansi256PropertySource.java new file mode 100644 index 0000000000..0420d2de6e --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/ansi/Ansi256PropertySource.java @@ -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 { + + 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; + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ResourceBannerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ResourceBannerTests.java index d75d3474bd..39234e5bd7 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ResourceBannerTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ResourceBannerTests.java @@ -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()); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ansi/Ansi256ColorTest.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ansi/Ansi256ColorTest.java new file mode 100644 index 0000000000..bc32ea9c6f --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ansi/Ansi256ColorTest.java @@ -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."); + } + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ansi/Ansi256PropertySourceTest.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ansi/Ansi256PropertySourceTest.java new file mode 100644 index 0000000000..5fcad0d36f --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/ansi/Ansi256PropertySourceTest.java @@ -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(); + } + +}