Skip to content
This repository has been archived by the owner on Sep 13, 2024. It is now read-only.

Add temporal converter to support Java 8 time/date classes #46

Merged
merged 1 commit into from
Aug 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public Object fromConfiguration( final ConverterLookup lookup, final PlexusConfi
{
try
{
result = fromString( (String) result );
result = fromString( (String) result, type );
}
catch ( final ComponentConfigurationException e )
{
Expand All @@ -58,15 +58,24 @@ public Object fromConfiguration( final ConverterLookup lookup, final PlexusConfi
// Customizable methods
// ----------------------------------------------------------------------

protected abstract Object fromString( final String str )
throws ComponentConfigurationException;
protected Object fromString( final String str, final Class<?> type )
throws ComponentConfigurationException
{
return fromString( str );
}

protected Object fromString( final String str )
throws ComponentConfigurationException
{
throw new UnsupportedOperationException("The class " + this.getClass().getName() + " must implement one of the fromString(...) methods, but it doesn't");
}

// ----------------------------------------------------------------------
// Shared methods
// ----------------------------------------------------------------------

@Override
protected final Object fromExpression( final PlexusConfiguration configuration, final ExpressionEvaluator evaluator,
protected Object fromExpression( final PlexusConfiguration configuration, final ExpressionEvaluator evaluator,
final Class<?> type )
throws ComponentConfigurationException
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*******************************************************************************
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.codehaus.plexus.component.configurator.converters.basic;

import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.util.Locale;

import org.codehaus.plexus.component.configurator.ComponentConfigurationException;

/**
* Supports type conversion into {@link java.time} classes.
* The supported patterns of the used {@link DateTimeFormatter} is either
* <ul>
* <li>ISO-8601 extended offset date-time-format ({@link DateTimeFormatter#ISO_OFFSET_DATE_TIME}) or</li>
* <li>{@code yyyy-MM-dd HH:mm:ss[[a][.S [a]]}</li>
* </ul>
*
*/
public class TemporalConverter
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mcculls Any reason why PlexusDateTypeConverter was implemented as Guice TypeConverter and DateConverter is just an adapter?

extends AbstractBasicConverter
{
/**
* Supports all formats of {@link org.eclipse.sisu.plexus.PlexusDateTypeConverter}
*/
private static final DateTimeFormatter PLEXUS_DATE_TIME_FORMATTER = DateTimeFormatter
.ofPattern( "yyyy-MM-dd HH:mm:ss[[a][.S [a]]", Locale.US )
.withZone( ZoneId.systemDefault() );

public boolean canConvert( final Class<?> type )
{
return Temporal.class.isAssignableFrom( type );
}

@Override
protected final Object fromString( final String str, final Class<?> type ) throws ComponentConfigurationException
{
return createTemporalFromString( str, type );
}

private Temporal createTemporalFromString( String value, final Class<?> type )
{
TemporalAccessor temporalAccessor;
try
{
temporalAccessor = DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse( value );
}
catch ( DateTimeParseException e )
{
temporalAccessor = PLEXUS_DATE_TIME_FORMATTER.parse( value );
}
final Temporal temporal;
if ( type.equals( LocalDate.class ) )
{
temporal = LocalDate.from( temporalAccessor );
}
else if ( type.equals( LocalDateTime.class ) )
{
temporal = LocalDateTime.from( temporalAccessor );
}
else if ( type.equals( LocalTime.class ) )
{
temporal = LocalTime.from( temporalAccessor );
}
else if ( type.equals( Instant.class ) )
{
temporal = Instant.from( temporalAccessor );
}
else if ( type.equals( OffsetDateTime.class ) )
{
temporal = ZonedDateTime.from( temporalAccessor ).toOffsetDateTime();
}
else if ( type.equals( OffsetTime.class ) )
{
temporal = ZonedDateTime.from( temporalAccessor ).toOffsetDateTime().toOffsetTime();
}
else if ( type.equals(ZonedDateTime.class ) )
{
temporal = ZonedDateTime.from( temporalAccessor );
}
else
{
throw new IllegalArgumentException( "Unsupported temporal type " + type );
}
return temporal;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.codehaus.plexus.component.configurator.converters.basic.StringBufferConverter;
import org.codehaus.plexus.component.configurator.converters.basic.StringBuilderConverter;
import org.codehaus.plexus.component.configurator.converters.basic.StringConverter;
import org.codehaus.plexus.component.configurator.converters.basic.TemporalConverter;
import org.codehaus.plexus.component.configurator.converters.basic.UriConverter;
import org.codehaus.plexus.component.configurator.converters.basic.UrlConverter;
import org.codehaus.plexus.component.configurator.converters.composite.ArrayConverter;
Expand Down Expand Up @@ -73,6 +74,7 @@ public final class DefaultConverterLookup
new ClassRealmConverter(), //
new StringBufferConverter(), //
new StringBuilderConverter(), //
new TemporalConverter(), //
new ObjectWithFieldsConverter() };

private final Map<Class<?>, ConfigurationConverter> lookupCache = //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;

import org.codehaus.plexus.component.configurator.BasicComponentConfigurator;
import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
Expand All @@ -25,6 +34,7 @@
import org.junit.rules.TemporaryFolder;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;

public class BasicComponentConfiguratorTest
{
Expand Down Expand Up @@ -64,6 +74,68 @@ public void testTypeWithoutConverterButConstructorAcceptingString()
assertEquals( "hello world", component.custom.toString() );
}

@Test
public void testTemporalConvertersWithoutMillisecondsAndOffset() throws ComponentConfigurationException
{
TemporalComponent component = new TemporalComponent();
String dateString = "2023-01-02 03:04:05";
configure( component,
"localDateTime", dateString,
"localDate", dateString,
"localTime", dateString,
"instant", dateString,
"offsetDateTime", dateString,
"offsetTime", dateString,
"zonedDateTime", dateString );
assertEquals( LocalDateTime.of(2023, 1, 2, 3, 4, 5, 0), component.localDateTime );
assertEquals( LocalDate.of(2023, 1, 2), component.localDate );
assertEquals( LocalTime.of(3, 4, 5, 0), component.localTime );
ZoneOffset systemOffset = ZoneId.systemDefault().getRules().getOffset( component.localDateTime );
assertEquals( OffsetDateTime.of( component.localDateTime, systemOffset).toInstant(), component.instant );
assertEquals( OffsetDateTime.of( component.localDateTime, systemOffset), component.offsetDateTime );
assertEquals( OffsetTime.of( component.localTime, systemOffset ), component.offsetTime );
assertEquals( ZonedDateTime.of( component.localDateTime, ZoneId.systemDefault() ), component.zonedDateTime );
}

@Test
public void testTemporalConvertersWithISO8601StringWithOffset() throws ComponentConfigurationException
{
TemporalComponent component = new TemporalComponent();
String dateString = "2023-01-02T03:04:05.000000900+02:30";
configure( component,
"localDateTime", dateString,
"localDate", dateString,
"localTime", dateString,
"instant", dateString,
"offsetDateTime", dateString,
"offsetTime", dateString,
"zonedDateTime", dateString );
assertEquals( LocalDateTime.of(2023, 1, 2, 3, 4, 5, 900), component.localDateTime );
assertEquals( LocalDate.of(2023, 1, 2), component.localDate );
assertEquals( LocalTime.of(3, 4, 5, 900), component.localTime );
ZoneOffset offset = ZoneOffset.ofHoursMinutes( 2, 30 );
assertEquals( OffsetDateTime.of( component.localDateTime, offset).toInstant(), component.instant );
assertEquals( OffsetDateTime.of( component.localDateTime, offset), component.offsetDateTime );
assertEquals( OffsetTime.of( component.localTime, offset ), component.offsetTime );
assertEquals( ZonedDateTime.of( component.localDateTime, offset ), component.zonedDateTime );
}

@Test
public void testTemporalConvertersWithInvalidString() throws ComponentConfigurationException
{
TemporalComponent component = new TemporalComponent();
String dateString = "invalid";
assertThrows( ComponentConfigurationException.class,
() -> configure( component,
"localDateTime", dateString,
"localDate", dateString,
"localTime", dateString,
"instant", dateString,
"offsetDateTime", dateString,
"offsetTime", dateString,
"zonedDateTime", dateString ) );
}

private void configure( Object component, String... keysAndValues )
throws ComponentConfigurationException
{
Expand Down Expand Up @@ -136,4 +208,21 @@ static final class CustomTypeComponent
{
CustomType custom;
}

static final class TemporalComponent
{
LocalDateTime localDateTime;

LocalDate localDate;

LocalTime localTime;

Instant instant;

OffsetDateTime offsetDateTime;

OffsetTime offsetTime;

ZonedDateTime zonedDateTime;
}
}
Loading