Skip to content

Commit

Permalink
Make it possible to build a parser by tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
Morten Haraldsen committed Feb 3, 2024
1 parent 45003cb commit 5e98a2b
Show file tree
Hide file tree
Showing 15 changed files with 713 additions and 3 deletions.
3 changes: 2 additions & 1 deletion src/main/java/com/ethlo/time/Field.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ public enum Field
HOUR(13),
MINUTE(16),
SECOND(19),
NANO(20);
NANO(20),
ZONE_OFFSET(17);

private final int requiredLength;

Expand Down
10 changes: 10 additions & 0 deletions src/main/java/com/ethlo/time/TimezoneOffset.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
public class TimezoneOffset
{
public static final TimezoneOffset UTC = new TimezoneOffset(0, 0);
private static final int SECONDS_PER_HOUR = 3600;
private static final int SECONDS_PER_MINUTE = 60;
private static final int MINUTES_PER_HOUR = 60;
private final int hours;
private final int minutes;

Expand All @@ -43,6 +46,13 @@ public static TimezoneOffset ofHoursMinutes(int hours, int minutes)
return new TimezoneOffset(hours, minutes);
}

public static TimezoneOffset ofTotalSeconds(int seconds)
{
final int absHours = seconds / SECONDS_PER_HOUR;
int absMinutes = (seconds / SECONDS_PER_MINUTE) % MINUTES_PER_HOUR;
return ofHoursMinutes(absHours, absMinutes);
}

public static TimezoneOffset of(ZoneOffset offset)
{
final int seconds = offset.getTotalSeconds();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,14 @@ public static int parsePositiveInt(final String strNum, int startInclusive, int
int result = 0;
try
{

for (int i = startInclusive; i < endExclusive; i++)
{
final char c = strNum.charAt(i);
if (c < ZERO || c > DIGIT_9)
{
ErrorUtil.raiseUnexpectedCharacter(strNum, i);
}
result = result * 10 + (c - ZERO);
result = (result * 10) + (c - ZERO);
}
}
catch (StringIndexOutOfBoundsException exc)
Expand Down
102 changes: 102 additions & 0 deletions src/main/java/com/ethlo/time/token/ConfigurableDateTimeParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package com.ethlo.time.token;

/*-
* #%L
* Internet Time Utility
* %%
* Copyright (C) 2017 - 2024 Morten Haraldsen (ethlo)
* %%
* 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.
* #L%
*/

import static com.ethlo.time.Field.NANO;
import static com.ethlo.time.Field.YEAR;

import java.text.ParsePosition;

import com.ethlo.time.DateTime;
import com.ethlo.time.Field;
import com.ethlo.time.TimezoneOffset;

public class ConfigurableDateTimeParser implements DateTimeParser
{
private final DateTimeToken[] tokens;

public ConfigurableDateTimeParser(DateTimeToken... tokens)
{
this.tokens = tokens;
}

public ConfigurableDateTimeParser combine(DateTimeToken... tokens)
{
return new ConfigurableDateTimeParser(combine(this.tokens, tokens));
}

private DateTimeToken[] combine(DateTimeToken[] a, DateTimeToken[] b)
{
final DateTimeToken[] result = new DateTimeToken[a.length + b.length];
System.arraycopy(a, 0, result, 0, a.length);
System.arraycopy(b, 0, result, a.length, b.length);
return result;
}

@Override
public DateTime parse(String text, ParsePosition parsePosition)
{
int fractionsLength = 0;
int highestOrdinal = YEAR.ordinal();
final int[] values = new int[]{0, 1, 1, 0, 0, 0, 0, -1};
for (DateTimeToken token : tokens)
{
final int index = parsePosition.getIndex();
final int value = token.read(text, parsePosition);
final Field field = token.getField();
if (field != null)
{
final int ordinal = field.ordinal();
values[ordinal] = value;
highestOrdinal = Math.max(ordinal, highestOrdinal);
if (token instanceof FractionsToken)
{
fractionsLength = parsePosition.getIndex() - index;
values[ordinal] = scale(value, fractionsLength);
}
}
}

return new DateTime(
Field.values()[Math.min(highestOrdinal, NANO.ordinal())],
values[Field.YEAR.ordinal()],
values[Field.MONTH.ordinal()],
values[Field.DAY.ordinal()],
values[Field.HOUR.ordinal()],
values[Field.MINUTE.ordinal()],
values[Field.SECOND.ordinal()],
values[Field.NANO.ordinal()],
values[Field.ZONE_OFFSET.ordinal()] != -1 ? TimezoneOffset.ofTotalSeconds(values[Field.ZONE_OFFSET.ordinal()]) : null,
fractionsLength
);
}

private int scale(int value, int length)
{
int pos = length;
while (pos < 9)
{
value *= 10;
pos++;

Check warning on line 98 in src/main/java/com/ethlo/time/token/ConfigurableDateTimeParser.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/ethlo/time/token/ConfigurableDateTimeParser.java#L97-L98

Added lines #L97 - L98 were not covered by tests
}
return value;
}
}
30 changes: 30 additions & 0 deletions src/main/java/com/ethlo/time/token/DateTimeParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.ethlo.time.token;

/*-
* #%L
* Internet Time Utility
* %%
* Copyright (C) 2017 - 2024 Morten Haraldsen (ethlo)
* %%
* 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.
* #L%
*/

import java.text.ParsePosition;

import com.ethlo.time.DateTime;

public interface DateTimeParser
{
DateTime parse(String text, ParsePosition parsePosition);
}
32 changes: 32 additions & 0 deletions src/main/java/com/ethlo/time/token/DateTimeToken.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.ethlo.time.token;

/*-
* #%L
* Internet Time Utility
* %%
* Copyright (C) 2017 - 2024 Morten Haraldsen (ethlo)
* %%
* 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.
* #L%
*/

import java.text.ParsePosition;

import com.ethlo.time.Field;

public interface DateTimeToken
{
int read(String text, ParsePosition parsePosition);

Field getField();
}
53 changes: 53 additions & 0 deletions src/main/java/com/ethlo/time/token/DigitsToken.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.ethlo.time.token;

/*-
* #%L
* Internet Time Utility
* %%
* Copyright (C) 2017 - 2024 Morten Haraldsen (ethlo)
* %%
* 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.
* #L%
*/

import com.ethlo.time.Field;
import com.ethlo.time.internal.LimitedCharArrayIntegerUtil;

import java.text.ParsePosition;

public class DigitsToken implements DateTimeToken
{
private final Field field;
private final int length;

public DigitsToken(Field field, int length)
{
this.field = field;
this.length = length;
}

@Override
public int read(String text, ParsePosition parsePosition)
{
final int offset = parsePosition.getIndex();
final int end = offset + length;
final int value = LimitedCharArrayIntegerUtil.parsePositiveInt(text, offset, end);
parsePosition.setIndex(end);
return value;
}

public Field getField()
{
return field;
}
}
31 changes: 31 additions & 0 deletions src/main/java/com/ethlo/time/token/FourDigitToken.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.ethlo.time.token;

/*-
* #%L
* Internet Time Utility
* %%
* Copyright (C) 2017 - 2024 Morten Haraldsen (ethlo)
* %%
* 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.
* #L%
*/

import com.ethlo.time.Field;

public class FourDigitToken extends DigitsToken
{
public FourDigitToken(Field field)
{
super(field, 4);
}
}
60 changes: 60 additions & 0 deletions src/main/java/com/ethlo/time/token/FractionsToken.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.ethlo.time.token;

/*-
* #%L
* Internet Time Utility
* %%
* Copyright (C) 2017 - 2024 Morten Haraldsen (ethlo)
* %%
* 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.
* #L%
*/

import static com.ethlo.time.internal.LimitedCharArrayIntegerUtil.DIGIT_9;
import static com.ethlo.time.internal.LimitedCharArrayIntegerUtil.ZERO;

import java.text.ParsePosition;

import com.ethlo.time.Field;

public class FractionsToken implements DateTimeToken
{
@Override
public int read(final String text, final ParsePosition parsePosition)
{
int idx = parsePosition.getIndex();
final int length = text.length();
int value = 0;
while (idx < length)
{
final char c = text.charAt(idx);
if (c < ZERO || c > DIGIT_9)
{
break;

Check warning on line 43 in src/main/java/com/ethlo/time/token/FractionsToken.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/com/ethlo/time/token/FractionsToken.java#L43

Added line #L43 was not covered by tests
}
else
{
value = value * 10 + (c - ZERO);
idx++;
}
}
parsePosition.setIndex(idx);
return value;
}

@Override
public Field getField()
{
return Field.NANO;
}
}
Loading

0 comments on commit 5e98a2b

Please sign in to comment.