Skip to content

Commit

Permalink
#56 : API: Custom converter ByteSize
Browse files Browse the repository at this point in the history
  • Loading branch information
Gmugra committed May 13, 2021
1 parent e3e9774 commit 2b8bc45
Show file tree
Hide file tree
Showing 10 changed files with 706 additions and 8 deletions.
48 changes: 41 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,20 @@ To access properties you need to define a convenient Java interface, e.g. :
```java
package my.superapp;

import static net.cactusthorn.config.core.Disable.Feature.*
import net.cactusthorn.config.core.*
import static net.cactusthorn.config.core.Disable.Feature.*;
import net.cactusthorn.config.core.*;
import net.cactusthorn.config.core.converter.*;

import java.util.concurrent.TimeUnit;
import java.util.Set;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.time.LocalDate;

@Config
@Prefix("app")
interface MyConfig {
public interface MyConfig {

@Default("unknown") String val();

Expand All @@ -78,6 +80,8 @@ interface MyConfig {
@Disable(PREFIX) Optional<List<UUID>> ids();

@Split("[:;]") @Default("DAYS:HOURS") Set<TimeUnit> units();

@ConverterLocalDate({"dd.MM.yyyy", "yyyy-MM-dd"}) LocalDate date();
}
```
- An interface must be annotated with `@Config`.
Expand All @@ -100,6 +104,7 @@ app.val=ABC
app.number=10
ids=f8c3de3d-1fea-4d7c-a8b0-29f63c4c3454,123e4567-e89b-12d3-a456-556642440000
app.units=DAYS:HOURS;MICROSECONDS
app.date=12.11.2005
```

### Annotations
Expand Down Expand Up @@ -179,7 +184,7 @@ The return type of the interface methods must either:
1. e.g. [Integer.valueOf](https://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html#valueOf-java.lang.String-)
1. e.g. [UUID.fromString](https://docs.oracle.com/javase/8/docs/api/java/util/UUID.html#fromString-java.lang.String-)
1. If both methods are present then `valueOf` used unless the type is an `enum` in which case `fromString` used.
1. Be `java.net.URL`, `java.net.URI`, `java.time.Instant`, `java.time.Duration`, `java.time.Period`, `java.nio.file.Path`
1. Be `java.net.URL`, `java.net.URI`, `java.time.Instant`, `java.time.Duration`, `java.time.Period`, `java.nio.file.Path`, `net.cactusthorn.config.core.converter.bytesize.ByteSize`
1. Be `List<T>`, `Set<T>` or `SortedSet<T>`, where T satisfies 2, 3 or 4 above. The resulting collection is read-only.
1. Be `Optional<T>`, where T satisfies 2, 3, 4 or 5 above

Expand Down Expand Up @@ -212,6 +217,35 @@ e.g. `2011-12-03T10:15:30Z`
- `m`, `mo`, `month`, `months`
- `y`, `year`, `years`

### `net.cactusthorn.config.core.converter.bytesize.ByteSize` format
It based on [OWNER](http://owner.aeonbits.org/docs/type-conversion/) classes to represent data sizes.

usage:
```java
@Config public interface MyByteSize {
@Default("10 megabytes")
ByteSize size();
}
```
The supported unit strings for `ByteSize` are case sensitive and must be lowercase. Exactly these strings are supported:
- `byte`, `bytes`, `b`
- `kilobyte`, `kilobytes`, `k`, `ki`, `kib`
- `kibibyte`, `kibibytes`, `kb`
- `megabyte`, `megabytes`, `m`, `mi`, `mib`
- `mebibyte`, `mebibytes`, `mb`
- `gigabyte`, `gigabytes`, `g`, `gi`, `gib`
- `gibibyte`, `gibibytes`, `gb`
- `terabyte`, `terabytes`, `t`, `ti`, `tib`
- `tebibyte`, `tebibytes`, `tb`
- `petabyte`, `petabytes`, `p`, `pi`, `pib`
- `pebibyte`, `pebibytes`, `pb`
- `exabyte`, `exabytes`, `e`, `ei`, `eib`
- `exbibyte`, `exbibytes`, `eb`
- `zettabyte`, `zettabytes`, `z`, `zi`, `zib`
- `zebibyte`, `zebibytes`, `zb`
- `yottabyte`, `yottabytes`, `y`, `yi`, `yib`
- `yobibyte`, `yobibytes`, `yb`

### Custom converters
If it's need to deal with class which is not supported "by default" (see *Supported method return types*), a custom converter can be implemented and used.
```java
Expand Down Expand Up @@ -266,9 +300,9 @@ public interface MyConfig {
```

Several such annotation shipped with the library:
`net.cactusthorn.config.core.converter.ConverterLocalDate`
`net.cactusthorn.config.core.converter.ConverterLocalDateTime`
`net.cactusthorn.config.core.converter.ConverterZonedDateTime`
* `net.cactusthorn.config.core.converter.ConverterLocalDate`
* `net.cactusthorn.config.core.converter.ConverterLocalDateTime`
* `net.cactusthorn.config.core.converter.ConverterZonedDateTime`

## Loaders

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@

import net.cactusthorn.config.compiler.ProcessorException;
import net.cactusthorn.config.core.converter.Converter;
import net.cactusthorn.config.core.converter.bytesize.ByteSize;
import net.cactusthorn.config.core.converter.standard.ByteSizeConverter;
import net.cactusthorn.config.core.converter.standard.DurationConverter;
import net.cactusthorn.config.core.converter.standard.InstantConverter;
import net.cactusthorn.config.core.converter.standard.PathConverter;
Expand All @@ -57,6 +59,7 @@ public class DefaultConvertorValidator extends MethodValidatorAncestor {
CONVERTERS.put(Path.class, PathConverter.class.getName());
CONVERTERS.put(Duration.class, DurationConverter.class.getName());
CONVERTERS.put(Period.class, PeriodConverter.class.getName());
CONVERTERS.put(ByteSize.class, ByteSizeConverter.class.getName());
}

private final Map<TypeMirror, Type> classTypes = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/*
* Copyright (c) 2012-2016, Luigi R. Viggiano
* All rights reserved.
*
* This software is distributed under the BSD license.
* See the terms of the BSD license in the documentation provided with this software.
*/
package net.cactusthorn.config.core.converter.bytesize;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.Objects;

/**
* A unit of byte size, such as "512 kilobytes".
*
* This class models a two part byte count size, one part being a value and the
* other part being a {@link ByteSizeUnit}.
*
* This class supports converting to another {@link ByteSizeUnit}.
*
* @author Stefan Freyr Stefansson
*/
public class ByteSize {
private final BigDecimal value;
private final ByteSizeUnit unit;

/**
* Creates a byte size value from two parts, a value and a {@link ByteSizeUnit}.
*
* @param value the value part of this byte size.
* @param unit the unit part of this byte size.
*/
public ByteSize(BigDecimal value, ByteSizeUnit unit) {
this.value = value;
this.unit = unit;
}

/**
* Creates a byte size value from a <code>long</code> value representing the
* number of bytes.
*
* The unit part of this byte size will be {@link ByteSizeUnit#BYTES}.
*
* @param bytes the number of bytes this {@link ByteSize} instance should
* represent
*/
public ByteSize(long bytes) {
this(bytes, ByteSizeUnit.BYTES);
}

/**
* Creates a byte size value from a <code>String</code> value and a
* {@link ByteSizeUnit}.
*
* @param value the value part of this byte size
* @param unit the unit part of this byte size
*/
public ByteSize(String value, ByteSizeUnit unit) {
this(new BigDecimal(value), unit);
}

/**
* Creates a byte size value from a <code>long</code> value and a
* {@link ByteSizeUnit}.
*
* @param value the value part of this byte size
* @param unit the unit part of this byte size
*/
public ByteSize(long value, ByteSizeUnit unit) {
this(BigDecimal.valueOf(value), unit);
}

/**
* Creates a byte size value from a <code>double</code> value and a
* {@link ByteSizeUnit}.
*
* @param value the value part of this byte size
* @param unit the unit part of this byte size
*/
public ByteSize(double value, ByteSizeUnit unit) {
this(BigDecimal.valueOf(value), unit);
}

/**
* Returns the number of bytes that this byte size represents after multiplying
* the unit factor with the value.
*
* Since the value part can be a represented by a decimal, there is some
* possibility of a rounding error. Therefore, the result of multiplying the
* value and the unit factor are always rounded towards positive infinity to the
* nearest integer value (see {@link RoundingMode#CEILING}) to make sure that
* this method never gives values that are too small.
*
* @return number of bytes this byte size represents after factoring in the
* unit.
*/
public BigInteger getBytes() {
return value.multiply(unit.getFactor()).setScale(0, RoundingMode.CEILING).toBigIntegerExact();
}

/**
* Returns the number of bytes that this byte size represents as a
* <code>long</code> after multiplying the unit factor with the value, throwing
* an exception if the result overflows a <code>long</code>.
*
* @throws ArithmeticException if the result overflows a <code>long</code>
*
* @return the number of bytes that this byte size represents after factoring in
* the unit.
*/
public long getBytesAsLong() {
return getBytes().longValueExact();
}

/**
* Returns the number of bytes that this byte size represents as an
* <code>int</code> after multiplying the unit factor with the value, throwing
* an exception if the result overflows an <code>int</code>.
*
* @throws ArithmeticException if the result overflows an <code>int</code>
*
* @return the number of bytes that this byte size represents after factoring in
* the unit.
*/
public int getBytesAsInt() {
return getBytes().intValueExact();
}

/**
* Creates a new {@link ByteSize} representing the same byte size but in a
* different unit.
*
* Scale of the value (number of decimal points) is handled automatically but if
* a non-terminating decimal expansion occurs, an {@link ArithmeticException} is
* thrown.
*
* @param unit the unit for the new {@link ByteSize}.
*
* @throws ArithmeticException if a non-terminating decimal expansion occurs
* during calculation.
*
* @return a new {@link ByteSize} instance representing the same byte size as
* this but using the specified unit.
*/
public ByteSize convertTo(ByteSizeUnit byteSizeUnit) {
BigDecimal bytes = this.value.multiply(this.unit.getFactor()).setScale(0, RoundingMode.CEILING);
return new ByteSize(bytes.divide(byteSizeUnit.getFactor()), byteSizeUnit);
}

@Override public String toString() {
return value.toString() + " " + unit.toStringShortForm();
}

@Override public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}

ByteSize byteSize = (ByteSize) o;

return getBytes().equals(byteSize.getBytes());
}

@Override public int hashCode() {
return Objects.hash(value, unit);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2012-2016, Luigi R. Viggiano
* All rights reserved.
*
* This software is distributed under the BSD license.
* See the terms of the BSD license in the documentation provided with this software.
*/
package net.cactusthorn.config.core.converter.bytesize;

/**
* Represents the possible standards that a {@link ByteSizeUnit} can have.
* Different standards represent different "power of" values for which byte
* sizes are defined in.
*
* @see <a href=
* "https://en.wikipedia.org/wiki/Binary_prefix">https://en.wikipedia.org/wiki/Binary_prefix</a>
*
* @author Stefan Freyr Stefansson
*/
public enum ByteSizeStandard {

/**
* The International System of Units (SI) standard. Base of 1000.
*/
SI(1000),

/**
* The International Electrotechnical Commission (IEC) standard. Base of 1024.
*/
IEC(1024);

private final int powerOf;

ByteSizeStandard(int powerOf) {
this.powerOf = powerOf;
}

public int powerOf() {
return powerOf;
}
}
Loading

0 comments on commit 2b8bc45

Please sign in to comment.