NumberField.java
/*
* 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
*
* http://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.basepom.mojo.propertyhelper.fields;
import static com.google.common.base.Preconditions.checkState;
import org.basepom.mojo.propertyhelper.Field;
import org.basepom.mojo.propertyhelper.FieldContext;
import org.basepom.mojo.propertyhelper.ValueProvider;
import org.basepom.mojo.propertyhelper.definitions.NumberDefinition;
import java.util.List;
import java.util.Optional;
import java.util.StringJoiner;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
public final class NumberField extends Field<String, NumberDefinition> {
private final ValueProvider valueProvider;
// internal state
private List<NumberElement> numberElements;
private List<NumberElement> numberIndex;
@VisibleForTesting
public static NumberField forTesting(NumberDefinition numberDefinition, ValueProvider valueProvider) {
return new NumberField(numberDefinition, valueProvider, FieldContext.forTesting());
}
public NumberField(final NumberDefinition fieldDefinition, final ValueProvider valueProvider,
FieldContext fieldContext) {
super(fieldDefinition, fieldContext);
this.valueProvider = valueProvider;
}
@Override
public String getFieldName() {
// This is not the property name (because many definitions can map onto one prop)
// but the actual id.
return fieldDefinition.getId();
}
@Override
@SuppressWarnings("PMD.LambdaCanBeMethodReference") // https://github.com/pmd/pmd/issues/5043
public String getValue() {
parse();
return formatResult(value().map(v -> Long.toString(v))
.orElse(valueProvider.getValue().orElse("")));
}
private void parse() {
final Optional<String> value = valueProvider.getValue();
ImmutableList.Builder<NumberElement> numberELementBuilder = ImmutableList.builder();
ImmutableList.Builder<NumberElement> numberIndexBuilder = ImmutableList.builder();
if (value.isPresent()) {
String numberValue = value.get();
if (!numberValue.isBlank()) {
StringBuilder sb = new StringBuilder();
int charIndex = 0;
boolean number = Character.isDigit(numberValue.charAt(charIndex));
while (charIndex < numberValue.length()) {
char currentChar = numberValue.charAt(charIndex);
if (number != Character.isDigit(currentChar)) {
var numberElement = new NumberElement(number, sb.toString());
numberELementBuilder.add(numberElement);
if (number) {
numberIndexBuilder.add(numberElement);
}
number = !number;
sb.setLength(0);
}
sb.append(currentChar);
charIndex++;
}
if (sb.length() > 0) {
var numberElement = new NumberElement(number, sb.toString());
numberELementBuilder.add(numberElement);
if (number) {
numberIndexBuilder.add(numberElement);
}
}
}
}
this.numberElements = numberELementBuilder.build();
this.numberIndex = numberIndexBuilder.build();
}
private String print() {
return Joiner.on("").join(numberElements.stream().map(NumberElement::getFieldValue).iterator());
}
private Optional<Long> value() {
return fieldDefinition.getFieldNumber()
.map(numberIndex::get)
.flatMap(NumberElement::getLongValue);
}
private void set(long value) {
Optional<NumberElement> numberElement = fieldDefinition.getFieldNumber()
.map(numberIndex::get);
if (numberElement.isPresent()) {
numberElement.get().setLongValue(value);
valueProvider.setValue(print());
} else {
valueProvider.setValue(Long.toString(value));
}
}
public void increment() {
parse();
fieldDefinition.getFieldNumber().ifPresent(fieldNumber -> checkState(numberIndex.size() > fieldNumber,
"Only %s fields in %s, field %s requested.", numberElements.size(), print(), fieldNumber));
value().ifPresent(value -> {
set(value + fieldDefinition.getIncrement());
});
}
public Optional<Long> getNumberValue() {
parse();
fieldDefinition.getFieldNumber().ifPresent(fieldNumber -> checkState(numberIndex.size() > fieldNumber,
"Only %s fields in %s, field %s requested.", numberElements.size(), print(), fieldNumber));
return value();
}
@Override
public String toString() {
return new StringJoiner(", ", NumberField.class.getSimpleName() + "[", "]")
.add("valueProvider=" + valueProvider)
.add("numberElements=" + numberElements)
.add("numberIndex=" + numberIndex)
.add("fieldDefinition=" + fieldDefinition)
.toString();
}
private static final class NumberElement {
private final boolean number;
private String value;
private NumberElement(boolean number, String value) {
this.number = number;
this.value = value;
}
String getFieldValue() {
return value;
}
void setLongValue(long value) {
this.value = Long.toString(value);
}
Optional<Long> getLongValue() {
return number ? Optional.of(Long.parseLong(value)) : Optional.empty();
}
@Override
public String toString() {
return new StringJoiner(", ", NumberElement.class.getSimpleName() + "[", "]")
.add("number=" + number)
.add("value='" + value + "'")
.toString();
}
}
}