001// Generated by delombok at Sat Feb 25 17:58:34 UTC 2023 002package io.cui.tools.formatting.template; 003 004import static io.cui.tools.base.Preconditions.checkState; 005import static java.util.Objects.requireNonNull; 006import java.util.ArrayList; 007import java.util.List; 008import io.cui.tools.formatting.template.lexer.Lexer; 009import io.cui.tools.formatting.template.lexer.LexerBuilder; 010import io.cui.tools.formatting.template.token.Token; 011 012/** 013 * Formatter which is able to replace parameter inside the template based on 014 * {@link FormatterSupport} information. See {@link io.cui.tools.formatting} for details. 015 * 016 * @param <T> at least {@link FormatterSupport} 017 * 018 * @author Eugen Fischer 019 */ 020public final class TemplateFormatterImpl<T extends FormatterSupport> implements TemplateFormatter<T> { 021 @java.lang.SuppressWarnings("all") 022 @lombok.Generated 023 private final java.lang.Object $lock = new java.lang.Object[0]; 024 private static final long serialVersionUID = -6297959581838201331L; 025 private final String template; 026 private ArrayList<Token> parsedTokens; 027 private Lexer<T> lexer; 028 private final boolean strict; 029 030 private TemplateFormatterImpl(final String template, final boolean strict) { 031 this.template = template; 032 this.strict = strict; 033 } 034 035 /** 036 * Lexer which should be used to scan the template 037 * 038 * @param lexerInstance must not be {@code null} 039 * 040 * @return reference to TemplateFormatter, fluent api style 041 */ 042 TemplateFormatterImpl<T> scanBy(final Lexer<T> lexerInstance) { 043 this.lexer = requireNonNull(lexerInstance, "Parser must not be null"); 044 this.parsedTokens = null; 045 return this; 046 } 047 048 /** 049 * replace attributes from template by attribute values from the map. 050 * missing template attributes will be ignored and doesn't add to result at 051 * all. 052 * 053 * @param reference must not be null 054 * 055 * @return completed template 056 */ 057 @Override 058 public String format(final T reference) { 059 requireNonNull(reference, "Reference must not be null"); 060 final var tokenList = getParsedTokens(); 061 final var buffer = new StringBuilder(0); 062 for (var index = 0; index < tokenList.size(); index++) { 063 final var token = tokenList.get(index); 064 if (token.isStringToken()) { 065 if (lookUpLastTokenHasValue(tokenList, reference, index) && lookUpNextTokenHasValue(tokenList, reference, index)) { 066 buffer.append(token.substituteAttribute(reference)); 067 } 068 } else { 069 buffer.append(token.substituteAttribute(reference)); 070 } 071 } 072 return buffer.toString(); 073 } 074 075 private boolean lookUpNextTokenHasValue(final List<Token> tokenList, final T reference, final int currentTokenIndex) { 076 var nextTokenIndex = currentTokenIndex + 1; 077 while (nextTokenIndex < tokenList.size()) { 078 final var token = tokenList.get(nextTokenIndex); 079 if (!token.isStringToken()) { 080 final var value = token.substituteAttribute(reference); 081 if (!value.isEmpty()) { 082 return true; 083 } 084 } 085 nextTokenIndex++; 086 } 087 return false; 088 } 089 090 private boolean lookUpLastTokenHasValue(final List<Token> tokenList, final T reference, final int currentTokenIndex) { 091 var result = false; 092 final var lastTokenIndex = currentTokenIndex - 1; 093 if (lastTokenIndex >= 0) { 094 final var value = tokenList.get(lastTokenIndex).substituteAttribute(reference); 095 result = !value.isEmpty(); 096 } 097 return result; 098 } 099 100 private List<Token> getParsedTokens() { 101 synchronized (this.$lock) { 102 if (null == this.parsedTokens) { 103 checkState(null != this.lexer, "Parser must be initialized before."); 104 this.parsedTokens = new ArrayList<>(this.lexer.scan(this.template)); 105 } 106 return this.parsedTokens; 107 } 108 } 109 110 /** 111 * The created TemplateFormatter provide only usage of simple expression 112 * language with squared brackets. 113 * 114 * @param template must not be null 115 * @param sourceType must not be null 116 * 117 * @return TemplateFormatter which using template and lexer which was 118 * forwarded 119 */ 120 public static <F extends FormatterSupport> TemplateFormatter<F> createFormatter(final String template, final Class<F> sourceType) { 121 return TemplateBuilder.useTemplate(template).forType(sourceType); 122 } 123 124 /** 125 * @param template must not be null 126 * @param source must not be null 127 * 128 * @return TemplateFormatter 129 */ 130 public static <F extends FormatterSupport> TemplateFormatter<F> createFormatter(final String template, final F source) { 131 return TemplateBuilder.useTemplate(template).forSource(source); 132 } 133 134 /** 135 * @param template must not be null 136 * @param lexer must not be null 137 * 138 * @return TemplateFormatter which using template and lexer which was 139 * forwarded 140 */ 141 public static <F extends FormatterSupport> TemplateFormatter<F> createFormatter(final String template, final Lexer<F> lexer) { 142 return TemplateBuilder.useTemplate(template).scanBy(lexer); 143 } 144 145 /** 146 * The created TemplateFormatter provide only usage of simple expression 147 * language with squared brackets. 148 * 149 * @param template must not be null 150 * @param sourceType must not be null 151 * @param strict use strict mode for pattern matching (only match exact name) instead of best 152 * fitting 153 * 154 * @return TemplateFormatter which using template and lexer which was 155 * forwarded 156 */ 157 public static <F extends FormatterSupport> TemplateFormatter<F> createFormatter(final String template, final Class<F> sourceType, final boolean strict) { 158 return TemplateBuilder.useTemplate(template).strict(strict).forType(sourceType); 159 } 160 161 /** 162 * @param template must not be null 163 * @param source must not be null 164 * @param strict use strict mode for pattern matching (only match exact name) instead of best 165 * fitting 166 * 167 * @return TemplateFormatter 168 */ 169 public static <F extends FormatterSupport> TemplateFormatter<F> createFormatter(final String template, final F source, final boolean strict) { 170 return TemplateBuilder.useTemplate(template).strict(strict).forSource(source); 171 } 172 173 /** 174 * @param template must not be null 175 * @param lexer must not be null 176 * @param strict use strict mode for pattern matching (only match exact name) instead of best 177 * fitting 178 * 179 * @return TemplateFormatter which using template and lexer which was 180 * forwarded 181 */ 182 public static <F extends FormatterSupport> TemplateFormatter<F> createFormatter(final String template, final Lexer<F> lexer, final boolean strict) { 183 return TemplateBuilder.useTemplate(template).strict(strict).scanBy(lexer); 184 } 185 186 // owolff: False positive 187 /** 188 * @return a newly created {@link TemplateBuilder} 189 */ 190 @SuppressWarnings("squid:S2440") 191 public static TemplateBuilder builder() { 192 return new TemplateBuilder(); 193 } 194 195 196 /** 197 * Template Builder 198 * 199 * @author Eugen Fischer 200 */ 201 public static final class TemplateBuilder { 202 private TemplateBuilder() { 203 } 204 205 static FormatterBuilder useTemplate(final String template) { 206 return new FormatterBuilder(template); 207 } 208 209 210 static final class FormatterBuilder { 211 private final String template; 212 private boolean strict; 213 214 FormatterBuilder(final String templateInput) { 215 template = templateInput; 216 } 217 218 /** 219 * @param strict use strict mode for pattern matching (only match exact name) instead of 220 * best fitting 221 * @return a {@link FormatterBuilder} with strict set to given parameter 222 */ 223 public FormatterBuilder strict(boolean strict) { 224 this.strict = strict; 225 return this; 226 } 227 228 public <F extends FormatterSupport> TemplateFormatter<F> scanBy(final Lexer<F> lexer) { 229 return new TemplateFormatterImpl<F>(template, strict).scanBy(lexer); 230 } 231 232 public <F extends FormatterSupport> TemplateFormatter<F> forSource(final F source) { 233 final Lexer<F> lexer = LexerBuilder.useSimpleElWithSquaredBrackets().strict(strict).build(source); 234 return this.scanBy(lexer); 235 } 236 237 public <F extends FormatterSupport> TemplateFormatter<F> forType(final Class<F> classType) { 238 final Lexer<F> lexer = LexerBuilder.useSimpleElWithSquaredBrackets().strict(strict).build(classType); 239 return scanBy(lexer); 240 } 241 } 242 } 243 244 @java.lang.Override 245 @java.lang.SuppressWarnings("all") 246 @lombok.Generated 247 public java.lang.String toString() { 248 return "TemplateFormatterImpl(template=" + this.template + ", parsedTokens=" + this.getParsedTokens() + ", lexer=" + this.lexer + ", strict=" + this.strict + ")"; 249 } 250 251 @java.lang.Override 252 @java.lang.SuppressWarnings("all") 253 @lombok.Generated 254 public boolean equals(final java.lang.Object o) { 255 if (o == this) return true; 256 if (!(o instanceof TemplateFormatterImpl)) return false; 257 final TemplateFormatterImpl<?> other = (TemplateFormatterImpl<?>) o; 258 if (this.strict != other.strict) return false; 259 final java.lang.Object this$template = this.template; 260 final java.lang.Object other$template = other.template; 261 if (this$template == null ? other$template != null : !this$template.equals(other$template)) return false; 262 final java.lang.Object this$parsedTokens = this.getParsedTokens(); 263 final java.lang.Object other$parsedTokens = other.getParsedTokens(); 264 if (this$parsedTokens == null ? other$parsedTokens != null : !this$parsedTokens.equals(other$parsedTokens)) return false; 265 final java.lang.Object this$lexer = this.lexer; 266 final java.lang.Object other$lexer = other.lexer; 267 if (this$lexer == null ? other$lexer != null : !this$lexer.equals(other$lexer)) return false; 268 return true; 269 } 270 271 @java.lang.Override 272 @java.lang.SuppressWarnings("all") 273 @lombok.Generated 274 public int hashCode() { 275 final int PRIME = 59; 276 int result = 1; 277 result = result * PRIME + (this.strict ? 79 : 97); 278 final java.lang.Object $template = this.template; 279 result = result * PRIME + ($template == null ? 43 : $template.hashCode()); 280 final java.lang.Object $parsedTokens = this.getParsedTokens(); 281 result = result * PRIME + ($parsedTokens == null ? 43 : $parsedTokens.hashCode()); 282 final java.lang.Object $lexer = this.lexer; 283 result = result * PRIME + ($lexer == null ? 43 : $lexer.hashCode()); 284 return result; 285 } 286}