Skip to content

Commit 2b8bc45

Browse files
committedMay 13, 2021
#56 : API: Custom converter ByteSize
1 parent e3e9774 commit 2b8bc45

File tree

10 files changed

+706
-8
lines changed

10 files changed

+706
-8
lines changed
 

‎README.md

+41-7
Original file line numberDiff line numberDiff line change
@@ -58,18 +58,20 @@ To access properties you need to define a convenient Java interface, e.g. :
5858
```java
5959
package my.superapp;
6060

61-
import static net.cactusthorn.config.core.Disable.Feature.*
62-
import net.cactusthorn.config.core.*
61+
import static net.cactusthorn.config.core.Disable.Feature.*;
62+
import net.cactusthorn.config.core.*;
63+
import net.cactusthorn.config.core.converter.*;
6364

6465
import java.util.concurrent.TimeUnit;
6566
import java.util.Set;
6667
import java.util.List;
6768
import java.util.Optional;
6869
import java.util.UUID;
70+
import java.time.LocalDate;
6971

7072
@Config
7173
@Prefix("app")
72-
interface MyConfig {
74+
public interface MyConfig {
7375

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

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

8082
@Split("[:;]") @Default("DAYS:HOURS") Set<TimeUnit> units();
83+
84+
@ConverterLocalDate({"dd.MM.yyyy", "yyyy-MM-dd"}) LocalDate date();
8185
}
8286
```
8387
- An interface must be annotated with `@Config`.
@@ -100,6 +104,7 @@ app.val=ABC
100104
app.number=10
101105
ids=f8c3de3d-1fea-4d7c-a8b0-29f63c4c3454,123e4567-e89b-12d3-a456-556642440000
102106
app.units=DAYS:HOURS;MICROSECONDS
107+
app.date=12.11.2005
103108
```
104109

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

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

220+
### `net.cactusthorn.config.core.converter.bytesize.ByteSize` format
221+
It based on [OWNER](http://owner.aeonbits.org/docs/type-conversion/) classes to represent data sizes.
222+
223+
usage:
224+
```java
225+
@Config public interface MyByteSize {
226+
@Default("10 megabytes")
227+
ByteSize size();
228+
}
229+
```
230+
The supported unit strings for `ByteSize` are case sensitive and must be lowercase. Exactly these strings are supported:
231+
- `byte`, `bytes`, `b`
232+
- `kilobyte`, `kilobytes`, `k`, `ki`, `kib`
233+
- `kibibyte`, `kibibytes`, `kb`
234+
- `megabyte`, `megabytes`, `m`, `mi`, `mib`
235+
- `mebibyte`, `mebibytes`, `mb`
236+
- `gigabyte`, `gigabytes`, `g`, `gi`, `gib`
237+
- `gibibyte`, `gibibytes`, `gb`
238+
- `terabyte`, `terabytes`, `t`, `ti`, `tib`
239+
- `tebibyte`, `tebibytes`, `tb`
240+
- `petabyte`, `petabytes`, `p`, `pi`, `pib`
241+
- `pebibyte`, `pebibytes`, `pb`
242+
- `exabyte`, `exabytes`, `e`, `ei`, `eib`
243+
- `exbibyte`, `exbibytes`, `eb`
244+
- `zettabyte`, `zettabytes`, `z`, `zi`, `zib`
245+
- `zebibyte`, `zebibytes`, `zb`
246+
- `yottabyte`, `yottabytes`, `y`, `yi`, `yib`
247+
- `yobibyte`, `yobibytes`, `yb`
248+
215249
### Custom converters
216250
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.
217251
```java
@@ -266,9 +300,9 @@ public interface MyConfig {
266300
```
267301

268302
Several such annotation shipped with the library:
269-
`net.cactusthorn.config.core.converter.ConverterLocalDate`
270-
`net.cactusthorn.config.core.converter.ConverterLocalDateTime`
271-
`net.cactusthorn.config.core.converter.ConverterZonedDateTime`
303+
* `net.cactusthorn.config.core.converter.ConverterLocalDate`
304+
* `net.cactusthorn.config.core.converter.ConverterLocalDateTime`
305+
* `net.cactusthorn.config.core.converter.ConverterZonedDateTime`
272306

273307
## Loaders
274308

‎compiler/src/main/java/net/cactusthorn/config/compiler/methodvalidator/DefaultConvertorValidator.java

+3
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939

4040
import net.cactusthorn.config.compiler.ProcessorException;
4141
import net.cactusthorn.config.core.converter.Converter;
42+
import net.cactusthorn.config.core.converter.bytesize.ByteSize;
43+
import net.cactusthorn.config.core.converter.standard.ByteSizeConverter;
4244
import net.cactusthorn.config.core.converter.standard.DurationConverter;
4345
import net.cactusthorn.config.core.converter.standard.InstantConverter;
4446
import net.cactusthorn.config.core.converter.standard.PathConverter;
@@ -57,6 +59,7 @@ public class DefaultConvertorValidator extends MethodValidatorAncestor {
5759
CONVERTERS.put(Path.class, PathConverter.class.getName());
5860
CONVERTERS.put(Duration.class, DurationConverter.class.getName());
5961
CONVERTERS.put(Period.class, PeriodConverter.class.getName());
62+
CONVERTERS.put(ByteSize.class, ByteSizeConverter.class.getName());
6063
}
6164

6265
private final Map<TypeMirror, Type> classTypes = new HashMap<>();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
/*
2+
* Copyright (c) 2012-2016, Luigi R. Viggiano
3+
* All rights reserved.
4+
*
5+
* This software is distributed under the BSD license.
6+
* See the terms of the BSD license in the documentation provided with this software.
7+
*/
8+
package net.cactusthorn.config.core.converter.bytesize;
9+
10+
import java.math.BigDecimal;
11+
import java.math.BigInteger;
12+
import java.math.RoundingMode;
13+
import java.util.Objects;
14+
15+
/**
16+
* A unit of byte size, such as "512 kilobytes".
17+
*
18+
* This class models a two part byte count size, one part being a value and the
19+
* other part being a {@link ByteSizeUnit}.
20+
*
21+
* This class supports converting to another {@link ByteSizeUnit}.
22+
*
23+
* @author Stefan Freyr Stefansson
24+
*/
25+
public class ByteSize {
26+
private final BigDecimal value;
27+
private final ByteSizeUnit unit;
28+
29+
/**
30+
* Creates a byte size value from two parts, a value and a {@link ByteSizeUnit}.
31+
*
32+
* @param value the value part of this byte size.
33+
* @param unit the unit part of this byte size.
34+
*/
35+
public ByteSize(BigDecimal value, ByteSizeUnit unit) {
36+
this.value = value;
37+
this.unit = unit;
38+
}
39+
40+
/**
41+
* Creates a byte size value from a <code>long</code> value representing the
42+
* number of bytes.
43+
*
44+
* The unit part of this byte size will be {@link ByteSizeUnit#BYTES}.
45+
*
46+
* @param bytes the number of bytes this {@link ByteSize} instance should
47+
* represent
48+
*/
49+
public ByteSize(long bytes) {
50+
this(bytes, ByteSizeUnit.BYTES);
51+
}
52+
53+
/**
54+
* Creates a byte size value from a <code>String</code> value and a
55+
* {@link ByteSizeUnit}.
56+
*
57+
* @param value the value part of this byte size
58+
* @param unit the unit part of this byte size
59+
*/
60+
public ByteSize(String value, ByteSizeUnit unit) {
61+
this(new BigDecimal(value), unit);
62+
}
63+
64+
/**
65+
* Creates a byte size value from a <code>long</code> value and a
66+
* {@link ByteSizeUnit}.
67+
*
68+
* @param value the value part of this byte size
69+
* @param unit the unit part of this byte size
70+
*/
71+
public ByteSize(long value, ByteSizeUnit unit) {
72+
this(BigDecimal.valueOf(value), unit);
73+
}
74+
75+
/**
76+
* Creates a byte size value from a <code>double</code> value and a
77+
* {@link ByteSizeUnit}.
78+
*
79+
* @param value the value part of this byte size
80+
* @param unit the unit part of this byte size
81+
*/
82+
public ByteSize(double value, ByteSizeUnit unit) {
83+
this(BigDecimal.valueOf(value), unit);
84+
}
85+
86+
/**
87+
* Returns the number of bytes that this byte size represents after multiplying
88+
* the unit factor with the value.
89+
*
90+
* Since the value part can be a represented by a decimal, there is some
91+
* possibility of a rounding error. Therefore, the result of multiplying the
92+
* value and the unit factor are always rounded towards positive infinity to the
93+
* nearest integer value (see {@link RoundingMode#CEILING}) to make sure that
94+
* this method never gives values that are too small.
95+
*
96+
* @return number of bytes this byte size represents after factoring in the
97+
* unit.
98+
*/
99+
public BigInteger getBytes() {
100+
return value.multiply(unit.getFactor()).setScale(0, RoundingMode.CEILING).toBigIntegerExact();
101+
}
102+
103+
/**
104+
* Returns the number of bytes that this byte size represents as a
105+
* <code>long</code> after multiplying the unit factor with the value, throwing
106+
* an exception if the result overflows a <code>long</code>.
107+
*
108+
* @throws ArithmeticException if the result overflows a <code>long</code>
109+
*
110+
* @return the number of bytes that this byte size represents after factoring in
111+
* the unit.
112+
*/
113+
public long getBytesAsLong() {
114+
return getBytes().longValueExact();
115+
}
116+
117+
/**
118+
* Returns the number of bytes that this byte size represents as an
119+
* <code>int</code> after multiplying the unit factor with the value, throwing
120+
* an exception if the result overflows an <code>int</code>.
121+
*
122+
* @throws ArithmeticException if the result overflows an <code>int</code>
123+
*
124+
* @return the number of bytes that this byte size represents after factoring in
125+
* the unit.
126+
*/
127+
public int getBytesAsInt() {
128+
return getBytes().intValueExact();
129+
}
130+
131+
/**
132+
* Creates a new {@link ByteSize} representing the same byte size but in a
133+
* different unit.
134+
*
135+
* Scale of the value (number of decimal points) is handled automatically but if
136+
* a non-terminating decimal expansion occurs, an {@link ArithmeticException} is
137+
* thrown.
138+
*
139+
* @param unit the unit for the new {@link ByteSize}.
140+
*
141+
* @throws ArithmeticException if a non-terminating decimal expansion occurs
142+
* during calculation.
143+
*
144+
* @return a new {@link ByteSize} instance representing the same byte size as
145+
* this but using the specified unit.
146+
*/
147+
public ByteSize convertTo(ByteSizeUnit byteSizeUnit) {
148+
BigDecimal bytes = this.value.multiply(this.unit.getFactor()).setScale(0, RoundingMode.CEILING);
149+
return new ByteSize(bytes.divide(byteSizeUnit.getFactor()), byteSizeUnit);
150+
}
151+
152+
@Override public String toString() {
153+
return value.toString() + " " + unit.toStringShortForm();
154+
}
155+
156+
@Override public boolean equals(Object o) {
157+
if (this == o) {
158+
return true;
159+
}
160+
if (o == null || getClass() != o.getClass()) {
161+
return false;
162+
}
163+
164+
ByteSize byteSize = (ByteSize) o;
165+
166+
return getBytes().equals(byteSize.getBytes());
167+
}
168+
169+
@Override public int hashCode() {
170+
return Objects.hash(value, unit);
171+
}
172+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright (c) 2012-2016, Luigi R. Viggiano
3+
* All rights reserved.
4+
*
5+
* This software is distributed under the BSD license.
6+
* See the terms of the BSD license in the documentation provided with this software.
7+
*/
8+
package net.cactusthorn.config.core.converter.bytesize;
9+
10+
/**
11+
* Represents the possible standards that a {@link ByteSizeUnit} can have.
12+
* Different standards represent different "power of" values for which byte
13+
* sizes are defined in.
14+
*
15+
* @see <a href=
16+
* "https://en.wikipedia.org/wiki/Binary_prefix">https://en.wikipedia.org/wiki/Binary_prefix</a>
17+
*
18+
* @author Stefan Freyr Stefansson
19+
*/
20+
public enum ByteSizeStandard {
21+
22+
/**
23+
* The International System of Units (SI) standard. Base of 1000.
24+
*/
25+
SI(1000),
26+
27+
/**
28+
* The International Electrotechnical Commission (IEC) standard. Base of 1024.
29+
*/
30+
IEC(1024);
31+
32+
private final int powerOf;
33+
34+
ByteSizeStandard(int powerOf) {
35+
this.powerOf = powerOf;
36+
}
37+
38+
public int powerOf() {
39+
return powerOf;
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
/*
2+
* Copyright (c) 2012-2016, Luigi R. Viggiano
3+
* All rights reserved.
4+
*
5+
* This software is distributed under the BSD license.
6+
* See the terms of the BSD license in the documentation provided with this software.
7+
*/
8+
9+
package net.cactusthorn.config.core.converter.bytesize;
10+
11+
import java.math.BigDecimal;
12+
import java.util.HashMap;
13+
import java.util.Map;
14+
15+
import static net.cactusthorn.config.core.converter.bytesize.ByteSizeStandard.*;
16+
17+
/**
18+
* Specifies the available byte size units that a {@link ByteSize} can have.
19+
*
20+
* A byte size unit has a {@link ByteSizeStandard} that dictates the base value
21+
* to be raised to a given power as well as the power value that dictates the
22+
* magnitude of the unit. For example, a unit having the SI standard with a
23+
* power value of 4 is known as a terabyte while a unit having the IEC standard
24+
* with a power value of 4 is known as a tebibyte. The former has a factor of
25+
* 1000^4 while the latter has a factor of 1024^4.
26+
*
27+
* @author Stefan Freyr Stefansson
28+
*/
29+
public enum ByteSizeUnit {
30+
/**
31+
* The base unit. Has a factor of 1.
32+
*/
33+
BYTES("", "B", SI, 0),
34+
35+
/**
36+
* The SI kilobyte. Has a factor of 1000^1.
37+
*/
38+
KILOBYTES("kilo", "KB", SI, 1),
39+
/**
40+
* The IEC kibibyte. Has a factor of 1024^1.
41+
*/
42+
KIBIBYTES("kibi", "KiB", IEC, 1),
43+
/**
44+
* The SI megabyte. Has a factor of 1000^2.
45+
*/
46+
MEGABYTES("mega", "MB", SI, 2),
47+
/**
48+
* The IEC mebibyte. Has a factor of 1024^2.
49+
*/
50+
MEBIBYTES("mebi", "MiB", IEC, 2),
51+
/**
52+
* The SI gigabyte. Has a factor of 1000^3.
53+
*/
54+
GIGABYTES("giga", "GB", SI, 3),
55+
/**
56+
* The IEC gibibyte. Has a factor of 1024^3.
57+
*/
58+
GIBIBYTES("gibi", "GiB", IEC, 3),
59+
/**
60+
* The SI terabyte. Has a factor of 1000^4.
61+
*/
62+
TERABYTES("tera", "TB", SI, 4),
63+
/**
64+
* The IEC tebibyte. Has a factor of 1024^4.
65+
*/
66+
TEBIBYTES("tebi", "TiB", IEC, 4),
67+
/**
68+
* The SI petabyte. Has a factor of 1000^5.
69+
*/
70+
PETABYTES("peta", "PB", SI, 5),
71+
/**
72+
* The IEC pebibyte. Has a factor of 1024^5.
73+
*/
74+
PEBIBYTES("pebi", "PiB", IEC, 5),
75+
/**
76+
* The SI exabyte. Has a factor of 1000^6.
77+
*/
78+
EXABYTES("exa", "EB", SI, 6),
79+
/**
80+
* The IEC exibyte. Has a factor of 1024^6.
81+
*/
82+
EXBIBYTES("exbi", "EiB", IEC, 6),
83+
/**
84+
* The SI zettabyte. Has a factor of 1000^7.
85+
*/
86+
ZETTABYTES("zetta", "ZB", SI, 7),
87+
/**
88+
* The IEC zebibyte. Has a factor of 1024^7.
89+
*/
90+
ZEBIBYTES("zebi", "ZiB", IEC, 7),
91+
/**
92+
* The SI yottabyte. Has a factor of 1000^8.
93+
*/
94+
YOTTABYTES("yotta", "YB", SI, 8),
95+
/**
96+
* The IEC yobibyte. Has a factor of 1024^8.
97+
*/
98+
YOBIBYTES("yobi", "YiB", IEC, 8);
99+
100+
private final String prefix;
101+
private final String shortLabel;
102+
private final ByteSizeStandard standard;
103+
private final int power;
104+
105+
ByteSizeUnit(String prefix, String shortLabel, ByteSizeStandard standard, int power) {
106+
this.prefix = prefix;
107+
this.shortLabel = shortLabel;
108+
this.standard = standard;
109+
this.power = power;
110+
}
111+
112+
private static Map<String, ByteSizeUnit> makeUnitsMap() {
113+
Map<String, ByteSizeUnit> map = new HashMap<String, ByteSizeUnit>();
114+
for (ByteSizeUnit unit : ByteSizeUnit.values()) {
115+
map.put(unit.prefix + "byte", unit);
116+
map.put(unit.prefix + "bytes", unit);
117+
if (unit.prefix.length() == 0) {
118+
map.put("b", unit);
119+
map.put("", unit); // no unit specified means bytes
120+
} else {
121+
String first = unit.prefix.substring(0, 1);
122+
if (unit.standard == IEC) {
123+
map.put(first, unit); // 512m
124+
map.put(first + "i", unit); // 512mi
125+
map.put(first + "ib", unit); // 512mib
126+
} else { //unit.standard == SI
127+
map.put(first + "b", unit); // 512kb
128+
}
129+
}
130+
}
131+
return map;
132+
}
133+
134+
private static Map<String, ByteSizeUnit> unitsMap = makeUnitsMap();
135+
136+
/**
137+
* Parses a string representation of a byte size unit and returns the
138+
* corresponding {@link ByteSizeUnit}.
139+
*
140+
* There is support for various formats. Below is a list describing them where
141+
* [prefix] represents the long form prefix of the unit (such as "kilo", "mega",
142+
* "tera", etc) and [first] represents the first letter in the prefix (such as
143+
* "k", "m", "t", etc):
144+
* <ul>
145+
* <li>"" (empty string) - refers to the {@link ByteSizeUnit#BYTES}</li>
146+
* <li>"b" - refers to the {@link ByteSizeUnit#BYTES}.</li>
147+
* <li>"[prefix]byte" - the prefix determines what {@link ByteSizeStandard} is
148+
* being used.</li>
149+
* <li>"[prefix]bytes" - the prefix determines what {@link ByteSizeStandard} is
150+
* being used.</li>
151+
* <li>"[first]" - refers to the {@link ByteSizeStandard#IEC} standard for the
152+
* corresponding prefix, eg. "k" is equal to "kibibyte"</li>
153+
* <li>"[first]i" - refers to the {@link ByteSizeStandard#IEC} standard for the
154+
* corresponding prefix, eg. "ki" is equal to "kibibyte"</li>
155+
* <li>"[first]ib" - refers to the {@link ByteSizeStandard#IEC} standard for the
156+
* corresponding prefix, eg. "kib" is equal to "kibibyte"</li>
157+
* <li>"[first]b" - refers to the {@link ByteSizeStandard#SI} standard for the
158+
* corresponding prefix, eg. "kb" is equal to "kilobyte"</li>
159+
* </ul>
160+
*
161+
* The parsing is case insensitive, meaning that the following strings are
162+
* equivalent: "KiB", "kIb", "KIB", etc.
163+
*
164+
* This method will return <code>null</code> if the specified string is not a
165+
* valid {@link ByteSizeUnit} string as described above.
166+
*
167+
* @param unit the string representation of the desired {@link ByteSizeUnit}.
168+
*
169+
* @return the {@link ByteSizeUnit} represented by the specified string or
170+
* <code>null</code> if the string could not be translated into a known
171+
* unit.
172+
*/
173+
public static ByteSizeUnit parse(String unit) {
174+
return unitsMap.get(unit.toLowerCase());
175+
}
176+
177+
/**
178+
* Returns whether this {@link ByteSizeUnit} is an SI unit.
179+
*
180+
* @return true iff this unit is an SI unit.
181+
*/
182+
public boolean isSI() {
183+
return this.standard == SI;
184+
}
185+
186+
/**
187+
* Returns whether this {@link ByteSizeUnit} is an IEC unit.
188+
*
189+
* @return true iff this unit is an IEC unit.
190+
*/
191+
public boolean isIEC() {
192+
return this.standard == IEC;
193+
}
194+
195+
/**
196+
* Gets the multiplication factor for this {@link ByteSizeUnit}.
197+
*
198+
* Returns the result of raising the poweOf value of this units standard to the
199+
* power specified in this unit.
200+
*
201+
* @return the factor by which to multiply for this unit.
202+
*/
203+
public BigDecimal getFactor() {
204+
return BigDecimal.valueOf(standard.powerOf()).pow(power);
205+
}
206+
207+
/**
208+
* Returns the long string representation of this unit, such as "kilobytes",
209+
* "megabytes" or "bytes".
210+
*
211+
* Note that this method always returns the plural form.
212+
*
213+
* @return the plural long string representation of this unit.
214+
*/
215+
public String toStringLongForm() {
216+
return prefix + "bytes";
217+
}
218+
219+
/**
220+
* Returns the short string representation of this unit, such as "KiB", "B" or
221+
* "MB".
222+
*
223+
* @return the short string representation of this unit.
224+
*/
225+
public String toStringShortForm() {
226+
return shortLabel;
227+
}
228+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright (C) 2021, Alexei Khatskevich
3+
*
4+
* Licensed under the BSD 3-Clause license.
5+
* You may obtain a copy of the License at
6+
*
7+
* https://github.com/Gmugra/net.cactusthorn.config/blob/main/LICENSE
8+
*
9+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
10+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
11+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
12+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
13+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
14+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
15+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
16+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
17+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
18+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
19+
*/
20+
package net.cactusthorn.config.core.converter.standard;
21+
22+
import static net.cactusthorn.config.core.util.ApiMessages.*;
23+
import static net.cactusthorn.config.core.util.ApiMessages.Key.*;
24+
25+
import java.math.BigDecimal;
26+
27+
import net.cactusthorn.config.core.converter.Converter;
28+
import net.cactusthorn.config.core.converter.bytesize.ByteSize;
29+
import net.cactusthorn.config.core.converter.bytesize.ByteSizeUnit;
30+
import net.cactusthorn.config.core.util.NumericAndCharSplitter;
31+
32+
/**
33+
* @author Stefan Freyr Stefansson, Alexei Khatskevich
34+
*/
35+
public class ByteSizeConverter implements Converter<ByteSize> {
36+
37+
private static final NumericAndCharSplitter SPLITTER = new NumericAndCharSplitter();
38+
39+
@Override public ByteSize convert(String input, String[] parameters) {
40+
String[] parts = SPLITTER.split(input);
41+
String value = parts[0];
42+
String unit = parts[1];
43+
44+
BigDecimal bdValue = new BigDecimal(value);
45+
ByteSizeUnit bsuUnit = ByteSizeUnit.parse(unit);
46+
47+
if (bsuUnit == null) {
48+
throw new IllegalArgumentException(msg(INVALID_UNIT_STRING, unit));
49+
}
50+
51+
return new ByteSize(bdValue, bsuUnit);
52+
}
53+
}

‎core/src/main/java/net/cactusthorn/config/core/util/ApiMessages.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public final class ApiMessages {
3131
public enum Key {
3232
IS_NULL, IS_EMPTY, CANT_LOAD_RESOURCE, VALUE_NOT_FOUND, LOADER_NOT_FOUND, CANT_INVOKE_CONFIGBUILDER, CANT_FIND_CONFIGBUILDER,
3333
WRONG_SOURCE_PARAM, DURATION_NO_NUMBER, DURATION_WRONG_TIME_UNIT, PERIOD_NO_NUMBER, PERIOD_WRONG_TIME_UNIT, MANIFEST_NOT_FOUND_1,
34-
MANIFEST_NOT_FOUND_2
34+
MANIFEST_NOT_FOUND_2, INVALID_UNIT_STRING
3535
}
3636

3737
private ApiMessages() {

‎core/src/main/resources/net/cactusthorn/config/core/util/ApiMessages.properties

+1
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ PERIOD_NO_NUMBER=No number in duration value '{0}'
1212
PERIOD_WRONG_TIME_UNIT=Could not parse time unit '{0}' (try d, w, mo, y)
1313
MANIFEST_NOT_FOUND_1=Manifest for ''{0}={1}'' NOT found
1414
MANIFEST_NOT_FOUND_2="Manifest for ''{0}'' NOT found."
15+
INVALID_UNIT_STRING="Invalid unit string: ''{0}''
1516
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Copyright (C) 2021, Alexei Khatskevich
3+
*
4+
* Licensed under the BSD 3-Clause license.
5+
* You may obtain a copy of the License at
6+
*
7+
* https://github.com/Gmugra/net.cactusthorn.config/blob/main/LICENSE
8+
*
9+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
10+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
11+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
12+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
13+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
14+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
15+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
16+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
17+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
18+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
19+
*/
20+
package net.cactusthorn.config.core.converter.bytesize;
21+
22+
import static org.junit.jupiter.api.Assertions.*;
23+
24+
import java.math.BigInteger;
25+
26+
import org.junit.jupiter.api.Test;
27+
28+
public class ByteSizeTest {
29+
30+
@Test public void basics() {
31+
assertEquals(1, new ByteSize(1, ByteSizeUnit.BYTES).getBytesAsLong());
32+
33+
BigInteger siBytes = BigInteger.valueOf(1000);
34+
BigInteger iecBytes = BigInteger.valueOf(1024);
35+
36+
for (ByteSizeUnit bsu : ByteSizeUnit.values()) {
37+
if (bsu == ByteSizeUnit.BYTES) {
38+
assertEquals(1, new ByteSize(1, bsu).getBytesAsLong());
39+
} else if (bsu.isIEC()) {
40+
assertEquals(iecBytes, new ByteSize(1, bsu).getBytes());
41+
iecBytes = iecBytes.multiply(BigInteger.valueOf(1024));
42+
} else if (bsu.isSI()) {
43+
assertEquals(siBytes, new ByteSize(1, bsu).getBytes());
44+
siBytes = siBytes.multiply(BigInteger.valueOf(1000));
45+
}
46+
}
47+
}
48+
49+
@Test public void conversion() {
50+
assertEquals(new ByteSize(0.5, ByteSizeUnit.GIGABYTES),
51+
new ByteSize(500, ByteSizeUnit.MEGABYTES).convertTo(ByteSizeUnit.GIGABYTES));
52+
assertEquals(new ByteSize(9.765625, ByteSizeUnit.KIBIBYTES),
53+
new ByteSize(10, ByteSizeUnit.KILOBYTES).convertTo(ByteSizeUnit.KIBIBYTES));
54+
assertEquals(new ByteSize(10, ByteSizeUnit.MEGABYTES), new ByteSize(10, ByteSizeUnit.MEGABYTES).convertTo(ByteSizeUnit.MEGABYTES));
55+
ByteSize bs = new ByteSize(1, ByteSizeUnit.BYTES).convertTo(ByteSizeUnit.ZETTABYTES);
56+
assertEquals(1, bs.getBytesAsLong());
57+
assertEquals(new ByteSize(1, ByteSizeUnit.BYTES), bs.convertTo(ByteSizeUnit.BYTES));
58+
}
59+
60+
@Test public void equality() {
61+
assertEquals(new ByteSize(500, ByteSizeUnit.MEGABYTES), new ByteSize(0.5, ByteSizeUnit.GIGABYTES));
62+
assertEquals(new ByteSize(500, ByteSizeUnit.MEBIBYTES), new ByteSize("0.48828125", ByteSizeUnit.GIBIBYTES));
63+
}
64+
65+
@Test public void toStr() {
66+
assertEquals("0.5 GB", new ByteSize(0.5, ByteSizeUnit.GIGABYTES).toString());
67+
}
68+
69+
@Test public void getBytesAsInt() {
70+
assertEquals(500000000, new ByteSize(0.5, ByteSizeUnit.GIGABYTES).getBytesAsInt());
71+
}
72+
73+
@Test public void hash() {
74+
assertEquals(new ByteSize(0.5, ByteSizeUnit.GIGABYTES).hashCode(), new ByteSize(0.5, ByteSizeUnit.GIGABYTES).hashCode());
75+
}
76+
77+
@Test public void bytes() {
78+
assertEquals("1024 B", new ByteSize(1024).toString());
79+
}
80+
81+
@Test public void eqSame() {
82+
ByteSize bs = new ByteSize(1024);
83+
assertTrue(bs.equals(bs));
84+
}
85+
86+
@Test public void eqNull() {
87+
ByteSize bs = new ByteSize(1024);
88+
assertFalse(bs.equals(null));
89+
}
90+
91+
@Test public void eqWrongObject() {
92+
ByteSize bs = new ByteSize(1024);
93+
assertFalse(bs.equals("wrong"));
94+
}
95+
96+
@Test public void parse() {
97+
ByteSizeUnit unit = ByteSizeUnit.parse("k");
98+
assertEquals(ByteSizeUnit.KIBIBYTES, unit);
99+
}
100+
101+
@Test public void toStringLongForm() {
102+
ByteSizeUnit unit = ByteSizeUnit.parse("k");
103+
assertEquals("kibibytes", unit.toStringLongForm());
104+
}
105+
106+
@Test public void isSI() {
107+
ByteSizeUnit unit = ByteSizeUnit.parse("k");
108+
assertFalse(unit.isSI());
109+
}
110+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright (C) 2021, Alexei Khatskevich
3+
*
4+
* Licensed under the BSD 3-Clause license.
5+
* You may obtain a copy of the License at
6+
*
7+
* https://github.com/Gmugra/net.cactusthorn.config/blob/main/LICENSE
8+
*
9+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
10+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
11+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
12+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
13+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
14+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
15+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
16+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
17+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
18+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
19+
*/
20+
package net.cactusthorn.config.core.converter.standard;
21+
22+
import static org.junit.jupiter.api.Assertions.assertEquals;
23+
import static org.junit.jupiter.api.Assertions.assertThrows;
24+
25+
import org.junit.jupiter.api.Test;
26+
import org.junit.jupiter.params.ParameterizedTest;
27+
import org.junit.jupiter.params.provider.ValueSource;
28+
29+
import net.cactusthorn.config.core.converter.Converter;
30+
import net.cactusthorn.config.core.converter.bytesize.ByteSize;
31+
import net.cactusthorn.config.core.converter.bytesize.ByteSizeUnit;
32+
33+
public class ByteSizeConverterTest {
34+
35+
static Converter<ByteSize> converter = new ByteSizeConverter();
36+
37+
@ParameterizedTest //
38+
@ValueSource(strings = { "10 bytes", "10byte", "10 byte" }) //
39+
public void bytes(String value) {
40+
assertEquals(new ByteSize(10, ByteSizeUnit.BYTES), converter.convert(value));
41+
}
42+
43+
@ParameterizedTest //
44+
@ValueSource(strings = { "10m", "10mi", "10mib" }) //
45+
public void mebibytes(String value) {
46+
assertEquals(new ByteSize(10, ByteSizeUnit.MEBIBYTES), converter.convert(value));
47+
}
48+
49+
@Test public void invalidUnit() {
50+
assertThrows(IllegalArgumentException.class, () -> converter.convert("10 sillybyte"));
51+
}
52+
53+
@Test public void invalidNumber() {
54+
assertThrows(IllegalArgumentException.class, () -> converter.convert("megabyte"));
55+
}
56+
}

0 commit comments

Comments
 (0)
Please sign in to comment.