1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.basepom.mojo.propertyhelper.fields;
16
17 import static com.google.common.base.Preconditions.checkState;
18
19 import org.basepom.mojo.propertyhelper.Field;
20 import org.basepom.mojo.propertyhelper.FieldContext;
21 import org.basepom.mojo.propertyhelper.ValueProvider;
22 import org.basepom.mojo.propertyhelper.definitions.NumberDefinition;
23
24 import java.util.List;
25 import java.util.Optional;
26 import java.util.StringJoiner;
27
28 import com.google.common.annotations.VisibleForTesting;
29 import com.google.common.base.Joiner;
30 import com.google.common.collect.ImmutableList;
31
32 public final class NumberField extends Field<String, NumberDefinition> {
33
34 private final ValueProvider valueProvider;
35
36
37 private List<NumberElement> numberElements;
38 private List<NumberElement> numberIndex;
39
40 @VisibleForTesting
41 public static NumberField forTesting(NumberDefinition numberDefinition, ValueProvider valueProvider) {
42 return new NumberField(numberDefinition, valueProvider, FieldContext.forTesting());
43 }
44
45 public NumberField(final NumberDefinition fieldDefinition, final ValueProvider valueProvider,
46 FieldContext fieldContext) {
47 super(fieldDefinition, fieldContext);
48
49 this.valueProvider = valueProvider;
50 }
51
52 @Override
53 public String getFieldName() {
54
55
56 return fieldDefinition.getId();
57 }
58
59 @Override
60 public String getValue() {
61 parse();
62
63 return formatResult(value().map(v -> Long.toString(v))
64 .orElse(valueProvider.getValue().orElse("")));
65 }
66
67 private void parse() {
68 final Optional<String> value = valueProvider.getValue();
69
70 ImmutableList.Builder<NumberElement> numberELementBuilder = ImmutableList.builder();
71 ImmutableList.Builder<NumberElement> numberIndexBuilder = ImmutableList.builder();
72
73 if (value.isPresent()) {
74 String numberValue = value.get();
75 if (!numberValue.isBlank()) {
76 StringBuilder sb = new StringBuilder();
77 int charIndex = 0;
78 boolean number = Character.isDigit(numberValue.charAt(charIndex));
79
80 while (charIndex < numberValue.length()) {
81 char currentChar = numberValue.charAt(charIndex);
82 if (number != Character.isDigit(currentChar)) {
83 var numberElement = new NumberElement(number, sb.toString());
84
85 numberELementBuilder.add(numberElement);
86 if (number) {
87 numberIndexBuilder.add(numberElement);
88 }
89
90 number = !number;
91 sb.setLength(0);
92 }
93 sb.append(currentChar);
94 charIndex++;
95 }
96 if (sb.length() > 0) {
97 var numberElement = new NumberElement(number, sb.toString());
98
99 numberELementBuilder.add(numberElement);
100 if (number) {
101 numberIndexBuilder.add(numberElement);
102 }
103 }
104 }
105 }
106
107 this.numberElements = numberELementBuilder.build();
108 this.numberIndex = numberIndexBuilder.build();
109 }
110
111 private String print() {
112 return Joiner.on("").join(numberElements.stream().map(NumberElement::getFieldValue).iterator());
113 }
114
115 private Optional<Long> value() {
116 return fieldDefinition.getFieldNumber()
117 .map(fieldNumber -> numberIndex.get(fieldNumber))
118 .flatMap(NumberElement::getLongValue);
119 }
120
121 private void set(long value) {
122 Optional<NumberElement> numberElement = fieldDefinition.getFieldNumber()
123 .map(fieldNumber -> numberIndex.get(fieldNumber));
124
125 if (numberElement.isPresent()) {
126 numberElement.get().setLongValue(value);
127 valueProvider.setValue(print());
128 } else {
129 valueProvider.setValue(Long.toString(value));
130 }
131 }
132
133 public void increment() {
134 parse();
135
136 fieldDefinition.getFieldNumber().ifPresent(fieldNumber -> checkState(numberIndex.size() > fieldNumber,
137 "Only %s fields in %s, field %s requested.", numberElements.size(), print(), fieldNumber));
138
139 value().ifPresent(value -> {
140 set(value + fieldDefinition.getIncrement());
141 });
142 }
143
144 public Optional<Long> getNumberValue() {
145 parse();
146
147 fieldDefinition.getFieldNumber().ifPresent(fieldNumber -> checkState(numberIndex.size() > fieldNumber,
148 "Only %s fields in %s, field %s requested.", numberElements.size(), print(), fieldNumber));
149
150 return value();
151 }
152
153 @Override
154 public String toString() {
155 return new StringJoiner(", ", NumberField.class.getSimpleName() + "[", "]")
156 .add("valueProvider=" + valueProvider)
157 .add("numberElements=" + numberElements)
158 .add("numberIndex=" + numberIndex)
159 .add("fieldDefinition=" + fieldDefinition)
160 .toString();
161 }
162
163 private static final class NumberElement {
164
165 private final boolean number;
166 private String value;
167
168 private NumberElement(boolean number, String value) {
169 this.number = number;
170 this.value = value;
171 }
172
173 String getFieldValue() {
174 return value;
175 }
176
177 void setLongValue(long value) {
178 this.value = Long.toString(value);
179 }
180
181 Optional<Long> getLongValue() {
182 return number ? Optional.of(Long.parseLong(value)) : Optional.empty();
183 }
184
185 @Override
186 public String toString() {
187 return new StringJoiner(", ", NumberElement.class.getSimpleName() + "[", "]")
188 .add("number=" + number)
189 .add("value='" + value + "'")
190 .toString();
191 }
192 }
193 }