Move articles out of java-strings part1

This commit is contained in:
catalin-burcea
2019-10-12 22:15:42 +03:00
parent 0b6f07b8ba
commit a97793396f
27 changed files with 309 additions and 181 deletions

View File

@@ -0,0 +1,17 @@
## Java String Conversions
This module contains articles about string conversions from/to another type.
### Relevant Articles:
- [Converting String to Stream of chars](https://www.baeldung.com/java-string-to-stream)
- [Converting Strings to Enums in Java](https://www.baeldung.com/java-string-to-enum)
- [Convert a String to Title Case](https://www.baeldung.com/java-string-title-case)
- [Convert java.util.Date to String](https://www.baeldung.com/java-util-date-to-string)
- [Converting a Stack Trace to a String in Java](https://www.baeldung.com/java-stacktrace-to-string)
- [Image to Base64 String Conversion](https://www.baeldung.com/java-base64-image-string)
- [Convert a Comma Separated String to a List in Java](https://www.baeldung.com/java-string-with-separator-to-list)
- [Converting Java String to Double](https://www.baeldung.com/java-string-to-double)
- [Convert Char to String in Java](https://www.baeldung.com/java-convert-char-to-string)
- [Convert String to int or Integer in Java](https://www.baeldung.com/java-convert-string-to-int-or-integer)
- More articles: [[next -->]](/core-java-string-conversions-2)
"Counting Words in a String

View File

@@ -0,0 +1,67 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>core-java-string-conversions</artifactId>
<version>0.1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>core-java-string-conversions</name>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-java</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../../parent-java</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>com.ibm.icu</groupId>
<artifactId>icu4j</artifactId>
<version>${icu4j.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<!-- test scoped -->
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>${assertj.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>${org.hamcrest.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>core-java-string-conversions</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
<properties>
<icu4j.version>61.1</icu4j.version>
<assertj.version>3.6.1</assertj.version>
</properties>
</project>

View File

@@ -0,0 +1,44 @@
package com.baeldung.stringtoenum;
public enum PizzaStatusEnum {
ORDERED(5) {
@Override
public boolean isOrdered() {
return true;
}
},
READY(2) {
@Override
public boolean isReady() {
return true;
}
},
DELIVERED(0) {
@Override
public boolean isDelivered() {
return true;
}
};
private int timeToDelivery;
public boolean isOrdered() {
return false;
}
public boolean isReady() {
return false;
}
public boolean isDelivered() {
return false;
}
public int getTimeToDelivery() {
return timeToDelivery;
}
PizzaStatusEnum(int timeToDelivery) {
this.timeToDelivery = timeToDelivery;
}
}

View File

@@ -0,0 +1,68 @@
package com.baeldung.titlecase;
import com.ibm.icu.lang.UCharacter;
import com.ibm.icu.text.BreakIterator;
import org.apache.commons.lang3.text.WordUtils;
import java.util.Arrays;
import java.util.stream.Collectors;
public class TitleCaseConverter {
private static final String WORD_SEPARATOR = " ";
public static String convertToTitleCaseIteratingChars(String text) {
if (text == null || text.isEmpty()) {
return text;
}
StringBuilder converted = new StringBuilder();
boolean convertNext = true;
for (char ch : text.toCharArray()) {
if (Character.isSpaceChar(ch)) {
convertNext = true;
} else if (convertNext) {
ch = Character.toTitleCase(ch);
convertNext = false;
} else {
ch = Character.toLowerCase(ch);
}
converted.append(ch);
}
return converted.toString();
}
public static String convertToTitleCaseSplitting(String text) {
if (text == null || text.isEmpty()) {
return text;
}
return Arrays
.stream(text.split(WORD_SEPARATOR))
.map(word -> word.isEmpty()
? word
: Character.toTitleCase(word.charAt(0)) + word
.substring(1)
.toLowerCase())
.collect(Collectors.joining(WORD_SEPARATOR));
}
public static String convertToTitleCaseIcu4j(String text) {
if (text == null || text.isEmpty()) {
return text;
}
return UCharacter.toTitleCase(text, BreakIterator.getTitleInstance());
}
public static String convertToTileCaseWordUtilsFull(String text) {
return WordUtils.capitalizeFully(text);
}
public static String convertToTileCaseWordUtils(String text) {
return WordUtils.capitalize(text);
}
}

View File

@@ -0,0 +1,53 @@
package com.baeldung.chartostring;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class CharToStringUnitTest {
@Test
public void givenChar_whenCallingStringValueOf_shouldConvertToString() {
final char givenChar = 'x';
final String result = String.valueOf(givenChar);
assertThat(result).isEqualTo("x");
}
@Test
public void givenChar_whenCallingToStringOnCharacter_shouldConvertToString() {
final char givenChar = 'x';
final String result = Character.toString(givenChar);
assertThat(result).isEqualTo("x");
}
@Test
public void givenChar_whenCallingCharacterConstructor_shouldConvertToString3() {
final char givenChar = 'x';
final String result = new Character(givenChar).toString();
assertThat(result).isEqualTo("x");
}
@Test
public void givenChar_whenConcatenated_shouldConvertToString4() {
final char givenChar = 'x';
final String result = givenChar + "";
assertThat(result).isEqualTo("x");
}
@Test
public void givenChar_whenFormated_shouldConvertToString5() {
final char givenChar = 'x';
final String result = String.format("%c", givenChar);
assertThat(result).isEqualTo("x");
}
}

View File

@@ -0,0 +1,68 @@
package com.baeldung.datetostring;
import org.junit.BeforeClass;
import org.junit.Test;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import static org.junit.Assert.assertEquals;
public class DateToStringFormatterUnitTest {
private static final String DATE_FORMAT = "MMM d, yyyy HH:mm a";
private static final String EXPECTED_STRING_DATE = "Aug 1, 2018 12:00 PM";
private static Date date;
@BeforeClass
public static void setUp() {
TimeZone.setDefault(TimeZone.getTimeZone("CET"));
Calendar calendar = Calendar.getInstance();
calendar.set(2018, Calendar.AUGUST, 1, 12, 0);
date = calendar.getTime();
}
@Test
public void whenDateConvertedUsingSimpleDateFormatToString_thenCorrect() {
DateFormat formatter = new SimpleDateFormat(DATE_FORMAT);
String formattedDate = formatter.format(date);
assertEquals(EXPECTED_STRING_DATE, formattedDate);
}
@Test
public void whenDateConvertedUsingDateFormatToString_thenCorrect() {
String formattedDate = DateFormat
.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT, Locale.US)
.format(date);
assertEquals(EXPECTED_STRING_DATE, formattedDate);
}
@Test
public void whenDateConvertedUsingFormatterToString_thenCorrect() {
String formattedDate = String.format("%1$tb %1$te, %1$tY %1$tI:%1$tM %1$Tp", date);
assertEquals(EXPECTED_STRING_DATE, formattedDate);
}
@Test
public void whenDateConvertedUsingDateTimeApiToString_thenCorrect() {
DateTimeFormatter fmt = DateTimeFormatter.ofPattern(DATE_FORMAT);
Instant instant = date.toInstant();
LocalDateTime ldt = instant
.atZone(ZoneId.of("CET"))
.toLocalDateTime();
String formattedDate = ldt.format(fmt);
assertEquals(EXPECTED_STRING_DATE, formattedDate);
}
}

View File

@@ -0,0 +1,43 @@
package com.baeldung.filetobase64conversion;
import org.apache.commons.io.FileUtils;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.util.Base64;
import static org.junit.Assert.assertTrue;
public class FileToBase64StringConversionUnitTest {
private String inputFilePath = "test_image.jpg";
private String outputFilePath = "test_image_copy.jpg";
@Test
public void fileToBase64StringConversion() throws IOException {
//load file from /src/test/resources
ClassLoader classLoader = getClass().getClassLoader();
File inputFile = new File(classLoader
.getResource(inputFilePath)
.getFile());
byte[] fileContent = FileUtils.readFileToByteArray(inputFile);
String encodedString = Base64
.getEncoder()
.encodeToString(fileContent);
//create output file
File outputFile = new File(inputFile
.getParentFile()
.getAbsolutePath() + File.pathSeparator + outputFilePath);
// decode the string and write to file
byte[] decodedBytes = Base64
.getDecoder()
.decode(encodedString);
FileUtils.writeByteArrayToFile(outputFile, decodedBytes);
assertTrue(FileUtils.contentEquals(inputFile, outputFile));
}
}

View File

@@ -0,0 +1,56 @@
package com.baeldung.stringtodouble;
import static org.junit.Assert.assertEquals;
import java.text.DecimalFormat;
import java.text.ParseException;
import org.junit.Test;
public class StringToDoubleConversionUnitTest {
@Test
public void givenValidString_WhenParseDouble_ThenResultIsPrimitiveDouble() {
assertEquals(1.23, Double.parseDouble("1.23"), 0.000001);
}
@Test(expected = NullPointerException.class)
public void givenNullString_WhenParseDouble_ThenNullPointerExceptionIsThrown() {
Double.parseDouble(null);
}
@Test(expected = NumberFormatException.class)
public void givenInalidString_WhenParseDouble_ThenNumberFormatExceptionIsThrown() {
Double.parseDouble("&");
}
@Test
public void givenValidString_WhenValueOf_ThenResultIsPrimitiveDouble() {
assertEquals(1.23, Double.valueOf("1.23"), 0.000001);
}
@Test(expected = NullPointerException.class)
public void givenNullString_WhenValueOf_ThenNullPointerExceptionIsThrown() {
Double.valueOf(null);
}
@Test(expected = NumberFormatException.class)
public void givenInalidString_WhenValueOf_ThenNumberFormatExceptionIsThrown() {
Double.valueOf("&");
}
@Test
public void givenValidString_WhenDecimalFormat_ThenResultIsValidDouble() throws ParseException {
assertEquals(1.23, new DecimalFormat("#").parse("1.23").doubleValue(), 0.000001);
}
@Test(expected = NullPointerException.class)
public void givenNullString_WhenDecimalFormat_ThenNullPointerExceptionIsThrown() throws ParseException {
new DecimalFormat("#").parse(null);
}
@Test(expected = ParseException.class)
public void givenInvalidString_WhenDecimalFormat_ThenParseExceptionIsThrown() throws ParseException {
new DecimalFormat("#").parse("&");
}
}

View File

@@ -0,0 +1,27 @@
package com.baeldung.stringtoenum;
import static junit.framework.TestCase.assertTrue;
import org.junit.Test;
public class StringToEnumUnitTest {
@Test
public void whenConvertedIntoEnum_thenGetsConvertedCorrectly() {
String pizzaEnumValue = "READY";
PizzaStatusEnum pizzaStatusEnum = PizzaStatusEnum.valueOf(pizzaEnumValue);
assertTrue(pizzaStatusEnum == PizzaStatusEnum.READY);
}
@Test(expected = IllegalArgumentException.class)
public void whenConvertedIntoEnum_thenThrowsException() {
String pizzaEnumValue = "rEAdY";
PizzaStatusEnum pizzaStatusEnum = PizzaStatusEnum.valueOf(pizzaEnumValue);
}
@Test(expected = IllegalArgumentException.class)
public void givenInvalidEnumValueContentWiseAsString_whenConvertedIntoEnum_thenThrowsException() {
String pizzaEnumValue = "invalid";
PizzaStatusEnum pizzaStatusEnum = PizzaStatusEnum.valueOf(pizzaEnumValue);
}
}

View File

@@ -0,0 +1,62 @@
package com.baeldung.stringtoint;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
import com.google.common.primitives.Ints;
public class StringToIntOrIntegerUnitTest {
@Test
public void givenString_whenParsingInt_shouldConvertToInt() {
String givenString = "42";
int result = Integer.parseInt(givenString);
assertThat(result).isEqualTo(42);
}
@Test
public void givenString_whenCallingIntegerValueOf_shouldConvertToInt() {
String givenString = "42";
Integer result = Integer.valueOf(givenString);
assertThat(result).isEqualTo(new Integer(42));
}
@Test
public void givenString_whenCallingIntegerConstructor_shouldConvertToInt() {
String givenString = "42";
Integer result = new Integer(givenString);
assertThat(result).isEqualTo(new Integer(42));
}
@Test
public void givenString_whenCallingIntegerDecode_shouldConvertToInt() {
String givenString = "42";
int result = Integer.decode(givenString);
assertThat(result).isEqualTo(42);
}
@Test
public void givenString_whenTryParse_shouldConvertToInt() {
String givenString = "42";
Integer result = Ints.tryParse(givenString);
assertThat(result).isEqualTo(42);
}
@Test(expected = NumberFormatException.class)
public void givenInvalidInput_whenParsingInt_shouldThrow() {
String givenString = "nan";
Integer.parseInt(givenString);
}
}

View File

@@ -0,0 +1,134 @@
package com.baeldung.stringtolist;
import com.google.common.base.Function;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.junit.Assert.assertEquals;
public class ConvertStringToListUnitTest {
private final String countries = "Russia,Germany,England,France,Italy";
private final String ranks = "1,2,3,4,5, 6,7";
private final String emptyStrings = ",,,,,";
private final List<String> expectedCountriesList = Arrays.asList("Russia", "Germany", "England", "France", "Italy");
private final List<Integer> expectedRanksList = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
private final List<String> expectedEmptyStringsList = Arrays.asList("", "", "", "", "", "");
@Test
public void givenString_thenGetListOfStringByJava() {
List<String> convertedCountriesList = Arrays.asList(countries.split(",", -1));
assertEquals(expectedCountriesList, convertedCountriesList);
}
@Test
public void givenString_thenGetListOfStringByApache() {
List<String> convertedCountriesList = Arrays.asList(StringUtils.splitPreserveAllTokens(countries, ","));
assertEquals(expectedCountriesList, convertedCountriesList);
}
@Test
public void givenString_thenGetListOfStringByGuava() {
List<String> convertedCountriesList = Splitter.on(",")
.trimResults()
.splitToList(countries);
assertEquals(expectedCountriesList, convertedCountriesList);
}
@Test
public void givenString_thenGetListOfStringByJava8() {
List<String> convertedCountriesList = Stream.of(countries.split(",", -1))
.collect(Collectors.toList());
assertEquals(expectedCountriesList, convertedCountriesList);
}
@Test
public void givenString_thenGetListOfIntegerByJava() {
String[] convertedRankArray = ranks.split(",");
List<Integer> convertedRankList = new ArrayList<Integer>();
for (String number : convertedRankArray) {
convertedRankList.add(Integer.parseInt(number.trim()));
}
assertEquals(expectedRanksList, convertedRankList);
}
@Test
public void givenString_thenGetListOfIntegerByGuava() {
List<Integer> convertedRankList = Lists.transform(Splitter.on(",")
.trimResults()
.splitToList(ranks), new Function<String, Integer>() {
@Override
public Integer apply(String input) {
return Integer.parseInt(input.trim());
}
});
assertEquals(expectedRanksList, convertedRankList);
}
@Test
public void givenString_thenGetListOfIntegerByJava8() {
List<Integer> convertedRankList = Stream.of(ranks.split(","))
.map(String::trim)
.map(Integer::parseInt)
.collect(Collectors.toList());
assertEquals(expectedRanksList, convertedRankList);
}
@Test
public void givenString_thenGetListOfIntegerByApache() {
String[] convertedRankArray = StringUtils.split(ranks, ",");
List<Integer> convertedRankList = new ArrayList<Integer>();
for (String number : convertedRankArray) {
convertedRankList.add(Integer.parseInt(number.trim()));
}
assertEquals(expectedRanksList, convertedRankList);
}
@Test
public void givenEmptyStrings_thenGetListOfStringByJava() {
List<String> convertedEmptyStringsList = Arrays.asList(emptyStrings.split(",", -1));
assertEquals(expectedEmptyStringsList, convertedEmptyStringsList);
}
@Test
public void givenEmptyStrings_thenGetListOfStringByApache() {
List<String> convertedEmptyStringsList = Arrays.asList(StringUtils.splitPreserveAllTokens(emptyStrings, ","));
assertEquals(expectedEmptyStringsList, convertedEmptyStringsList);
}
@Test
public void givenEmptyStrings_thenGetListOfStringByGuava() {
List<String> convertedEmptyStringsList = Splitter.on(",")
.trimResults()
.splitToList(emptyStrings);
assertEquals(expectedEmptyStringsList, convertedEmptyStringsList);
}
@Test
public void givenEmptyStrings_thenGetListOfStringByJava8() {
List<String> convertedEmptyStringsList = Stream.of(emptyStrings.split(",", -1))
.collect(Collectors.toList());
assertEquals(expectedEmptyStringsList, convertedEmptyStringsList);
}
}

View File

@@ -0,0 +1,59 @@
package com.baeldung.stringtostream;
import org.junit.Test;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
public class StringToCharStreamUnitTest {
private String testString = "Tests";
@Test
public void givenTestString_whenChars_thenReturnIntStream() {
assertThat(testString.chars(), instanceOf(IntStream.class));
}
@Test
public void givenTestString_whenCodePoints_thenReturnIntStream() {
assertThat(testString.codePoints(), instanceOf(IntStream.class));
}
@Test
public void givenTestString_whenCodePoints_thenShowOccurences() throws Exception {
Map<Character, Integer> map = testString.codePoints()
.mapToObj(c -> (char) c)
.filter(Character::isLetter)
.collect(Collectors.toMap(c -> c, c -> 1, Integer::sum));
System.out.println(map);
}
@Test
public void givenIntStream_whenMapToObj_thenReturnCharacterStream() {
Stream<Character> characterStream = testString.chars()
.mapToObj(c -> (char) c);
Stream<Character> characterStream1 = testString.codePoints()
.mapToObj(c -> (char) c);
assertNotNull("IntStream returned by chars() did not map to Stream<Character>", characterStream);
assertNotNull("IntStream returned by codePoints() did not map to Stream<Character>", characterStream1);
}
@Test
public void givenIntStream_whenMapToObj_thenReturnStringStream() {
List<String> strings = testString.codePoints()
.mapToObj(c -> String.valueOf((char) c))
.collect(Collectors.toList());
assertEquals(strings.size(), 5);
}
}

View File

@@ -0,0 +1,70 @@
package com.baeldung.titlecase;
import org.junit.Assert;
import org.junit.Test;
import static org.junit.Assert.*;
public class TitleCaseConverterUnitTest {
private static final String TEXT = "tHis IS a tiTLe";
private static final String TEXT_EXPECTED = "This Is A Title";
private static final String TEXT_EXPECTED_NOT_FULL = "THis IS A TiTLe";
private static final String TEXT_OTHER_DELIMITERS = "tHis, IS a tiTLe";
private static final String TEXT_EXPECTED_OTHER_DELIMITERS = "This, Is A Title";
private static final String TEXT_EXPECTED_OTHER_DELIMITERS_NOT_FULL = "THis, IS A TiTLe";
@Test
public void whenConvertingToTitleCaseIterating_thenStringConverted() {
assertEquals(TEXT_EXPECTED, TitleCaseConverter.convertToTitleCaseIteratingChars(TEXT));
}
@Test
public void whenConvertingToTitleCaseSplitting_thenStringConverted() {
assertEquals(TEXT_EXPECTED, TitleCaseConverter.convertToTitleCaseSplitting(TEXT));
}
@Test
public void whenConvertingToTitleCaseUsingWordUtilsFull_thenStringConverted() {
assertEquals(TEXT_EXPECTED, TitleCaseConverter.convertToTileCaseWordUtilsFull(TEXT));
}
@Test
public void whenConvertingToTitleCaseUsingWordUtils_thenStringConvertedOnlyFirstCharacter() {
assertEquals(TEXT_EXPECTED_NOT_FULL, TitleCaseConverter.convertToTileCaseWordUtils(TEXT));
}
@Test
public void whenConvertingToTitleCaseUsingIcu4j_thenStringConverted() {
assertEquals(TEXT_EXPECTED, TitleCaseConverter.convertToTitleCaseIcu4j(TEXT));
}
@Test
public void whenConvertingToTitleCaseWithDifferentDelimiters_thenDelimitersKept() {
assertEquals(TEXT_EXPECTED_OTHER_DELIMITERS, TitleCaseConverter.convertToTitleCaseIteratingChars(TEXT_OTHER_DELIMITERS));
assertEquals(TEXT_EXPECTED_OTHER_DELIMITERS, TitleCaseConverter.convertToTitleCaseSplitting(TEXT_OTHER_DELIMITERS));
assertEquals(TEXT_EXPECTED_OTHER_DELIMITERS, TitleCaseConverter.convertToTileCaseWordUtilsFull(TEXT_OTHER_DELIMITERS));
assertEquals(TEXT_EXPECTED_OTHER_DELIMITERS_NOT_FULL, TitleCaseConverter.convertToTileCaseWordUtils(TEXT_OTHER_DELIMITERS));
assertEquals(TEXT_EXPECTED_OTHER_DELIMITERS, TitleCaseConverter.convertToTitleCaseIcu4j(TEXT_OTHER_DELIMITERS));
}
@Test
public void givenNull_whenConvertingToTileCase_thenReturnNull() {
assertEquals(null, TitleCaseConverter.convertToTitleCaseIteratingChars(null));
assertEquals(null, TitleCaseConverter.convertToTitleCaseSplitting(null));
assertEquals(null, TitleCaseConverter.convertToTileCaseWordUtilsFull(null));
assertEquals(null, TitleCaseConverter.convertToTileCaseWordUtils(null));
assertEquals(null, TitleCaseConverter.convertToTitleCaseIcu4j(null));
}
@Test
public void givenEmptyString_whenConvertingToTileCase_thenReturnEmptyString() {
assertEquals("", TitleCaseConverter.convertToTitleCaseIteratingChars(""));
assertEquals("", TitleCaseConverter.convertToTitleCaseSplitting(""));
assertEquals("", TitleCaseConverter.convertToTileCaseWordUtilsFull(""));
assertEquals("", TitleCaseConverter.convertToTileCaseWordUtils(""));
assertEquals("", TitleCaseConverter.convertToTitleCaseIcu4j(""));
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB