001// Generated by delombok at Sat Feb 25 17:58:34 UTC 2023 002package io.cui.tools.string; 003 004import static io.cui.tools.collect.CollectionLiterals.mutableList; 005import static io.cui.tools.collect.MoreCollections.isEmpty; 006import static io.cui.tools.string.MoreStrings.isBlank; 007import static java.util.Objects.requireNonNull; 008import java.util.ArrayList; 009import java.util.Iterator; 010import java.util.stream.Collectors; 011import io.cui.tools.logging.CuiLogger; 012import lombok.NonNull; 013 014/** 015 * Inspired by Googles Joiner. 016 * <p> 017 * It uses internally the {@link String#join(CharSequence, Iterable)} implementation 018 * of java and provides a guava like wrapper. It focuses on the simplified Joining 019 * and omits the Map based variants. 020 * </p> 021 * <h2>Usage</h2> 022 * 023 * <pre> 024 * assertEquals("key=value", Joiner.on('=').join("key", "value")); 025 * assertEquals("key=no value", Joiner.on('=').useForNull("no value").join("key", null)); 026 * assertEquals("key", Joiner.on('=').skipNulls().join("key", null)); 027 * assertEquals("key", Joiner.on('=').skipEmptyStrings().join("key", "")); 028 * assertEquals("key", Joiner.on('=').skipBlankStrings().join("key", " ")); 029 * </pre> 030 * 031 * <h2>Migrating from Guava</h2> 032 * <p> 033 * In order to migrate for most case you only need to replace the package name on the import. 034 * </p> 035 * <h2>Changes to Guavas-Joiner</h2> 036 * <p> 037 * In case of content to be joined containing {@code null}-values and not set to skip nulls, 038 * {@link #skipNulls()} it does not throw an 039 * {@link NullPointerException} but writes "null" for each {@code null} element. You can define a 040 * different String by calling {@link #useForNull(String)} 041 * </p> 042 * <p> 043 * In addition to {@link #skipEmptyStrings()} it provides a variant {@link #skipBlankStrings()} 044 * </p> 045 * 046 * @author Oliver Wolff 047 */ 048public final class Joiner { 049 private static final CuiLogger log = new CuiLogger(Joiner.class); 050 @NonNull 051 private final JoinerConfig joinerConfig; 052 053 /** 054 * Returns a Joiner that uses the given fixed string as a separator. For example, {@code 055 * Joiner.on("-").join("foo", "bar")} returns a String "foo-bar" 056 * 057 * @param separator the literal, nonempty string to recognize as a separator 058 * 059 * @return a {@link Joiner}, with default settings, that uses that separator 060 */ 061 public static Joiner on(final String separator) { 062 requireNonNull(separator); 063 return new Joiner(JoinerConfig.builder().separator(separator).build()); 064 } 065 066 /** 067 * Returns a Joiner that uses the given fixed string as a separator. For example, {@code 068 * Joiner.on('-').join("foo", "bar")} returns a String "foo-bar" 069 * 070 * @param separator the literal, nonempty string to recognize as a separator 071 * 072 * @return a {@link Joiner}, with default settings, that uses that separator 073 */ 074 public static Joiner on(final char separator) { 075 requireNonNull(separator); 076 return on(String.valueOf(separator)); 077 } 078 079 /** 080 * @param nullText to be used as substitution for {@code null} elements 081 * @return a joiner with the same behavior as this one, except automatically substituting {@code 082 * nullText} for any provided null elements. 083 */ 084 public Joiner useForNull(final String nullText) { 085 return new Joiner(joinerConfig.copy().useForNull(nullText).build()); 086 } 087 088 /** 089 * @return a joiner with the same behavior as this one, except automatically skipping 090 * null-values 091 */ 092 public Joiner skipNulls() { 093 return new Joiner(joinerConfig.copy().skipNulls(true).build()); 094 } 095 096 /** 097 * @return a joiner with the same behavior as this one, except automatically skipping 098 * String-values that evaluate to an empty String 099 */ 100 public Joiner skipEmptyStrings() { 101 return new Joiner(joinerConfig.copy().skipEmpty(true).build()); 102 } 103 104 /** 105 * @return a joiner with the same behavior as this one, except automatically skipping 106 * String-values that evaluate to a blank String as defined within 107 * {@link MoreStrings#isBlank(CharSequence)} 108 */ 109 public Joiner skipBlankStrings() { 110 return new Joiner(joinerConfig.copy().skipBlank(true).build()); 111 } 112 113 /** 114 * @param parts to be joined 115 * 116 * @return a string containing the string representation of each of {@code parts}, using the 117 * previously configured separator between each. 118 */ 119 public String join(Iterable<?> parts) { 120 return doJoin(parts); 121 } 122 123 /** 124 * @param parts to be joined 125 * 126 * @return a string containing the string representation of each of {@code parts}, using the 127 * previously configured separator between each. 128 */ 129 public String join(Iterator<?> parts) { 130 return doJoin(mutableList(parts)); 131 } 132 133 /** 134 * @param parts to be joined 135 * @return a string containing the string representation of each of {@code parts}, using the 136 * previously configured separator between each. 137 */ 138 public String join(Object... parts) { 139 return doJoin(mutableList(parts)); 140 } 141 142 private String doJoin(Iterable<?> parts) { 143 log.trace("Joining elements with configuration {}", joinerConfig); 144 if (isEmpty(parts)) { 145 return ""; 146 } 147 var builder = new ArrayList<CharSequence>(); 148 for (Object element : parts) { 149 if (null == element) { 150 if (!joinerConfig.isSkipNulls()) { 151 builder.add(joinerConfig.getUseForNull()); 152 } 153 } else if (element instanceof CharSequence) { 154 builder.add((CharSequence) element); 155 } else { 156 builder.add(MoreStrings.lenientToString(element)); 157 } 158 } 159 if (joinerConfig.isSkipEmpty()) { 160 builder = builder.stream().filter(element -> !MoreStrings.isEmpty(element)).collect(Collectors.toCollection(ArrayList::new)); 161 } 162 if (joinerConfig.isSkipBlank()) { 163 builder = builder.stream().filter(element -> !isBlank(element)).collect(Collectors.toCollection(ArrayList::new)); 164 } 165 return String.join(joinerConfig.getSeparator(), builder); 166 } 167 168 @java.lang.SuppressWarnings("all") 169 @lombok.Generated 170 Joiner(@NonNull final JoinerConfig joinerConfig) { 171 if (joinerConfig == null) { 172 throw new java.lang.NullPointerException("joinerConfig is marked non-null but is null"); 173 } 174 this.joinerConfig = joinerConfig; 175 } 176}