/*
* Copyright (c) 2013-2014 the original author or authors
*
* 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 io.werval.runtime.routes;
import java.net.MalformedURLException;
import java.time.DateTimeException;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.List;
import io.werval.api.Application;
import io.werval.api.exceptions.ParameterBinderException;
import io.werval.api.exceptions.ParameterBindingException;
import io.werval.api.exceptions.ParameterUnbindingException;
import io.werval.api.routes.ParameterBinder;
import io.werval.api.routes.ParameterBinders;
import io.werval.runtime.util.TypeResolver;
import io.werval.util.Hashids;
/**
* Parameter Binders instance.
*
* Include standard binders.
*/
public final class ParameterBindersInstance
implements ParameterBinders
{
private abstract static class StrictTypingParameterBinder<T>
implements ParameterBinder<T>
{
@Override
public final boolean accept( java.lang.Class<?> type )
{
return type.equals( TypeResolver.resolveArgument( getClass(), ParameterBinder.class ) );
}
}
/**
* {@link java.lang.String} Parameter Binder.
*/
public static final class String
extends StrictTypingParameterBinder<java.lang.String>
{
@Override
public java.lang.String bind( java.lang.String name, java.lang.String value )
{
return value;
}
@Override
public java.lang.String unbind( java.lang.String name, java.lang.String value )
{
return value;
}
}
/**
* {@link java.lang.Boolean} Parameter Binder.
*/
public static final class Boolean
extends StrictTypingParameterBinder<java.lang.Boolean>
{
@Override
public java.lang.Boolean bind( java.lang.String name, java.lang.String value )
{
return java.lang.Boolean.valueOf( value );
}
@Override
public java.lang.String unbind( java.lang.String name, java.lang.Boolean value )
{
return java.lang.String.valueOf( value );
}
}
/**
* {@link java.lang.Short} Parameter Binder.
*/
public static final class Short
extends StrictTypingParameterBinder<java.lang.Short>
{
@Override
public java.lang.Short bind( java.lang.String name, java.lang.String value )
{
try
{
return java.lang.Short.valueOf( value );
}
catch( NumberFormatException ex )
{
throw new ParameterBinderException( "Invalid parameter '" + name + "' format: '" + value + "'", ex );
}
}
@Override
public java.lang.String unbind( java.lang.String name, java.lang.Short value )
{
return java.lang.String.valueOf( value );
}
}
/**
* {@link java.lang.Integer} Parameter Binder.
*/
public static final class Integer
extends StrictTypingParameterBinder<java.lang.Integer>
{
@Override
public java.lang.Integer bind( java.lang.String name, java.lang.String value )
{
try
{
return java.lang.Integer.valueOf( value );
}
catch( NumberFormatException ex )
{
throw new ParameterBinderException( "Invalid parameter '" + name + "' format: '" + value + "'", ex );
}
}
@Override
public java.lang.String unbind( java.lang.String name, java.lang.Integer value )
{
return java.lang.String.valueOf( value );
}
}
/**
* {@link java.lang.Long} Parameter Binder.
*/
public static final class Long
extends StrictTypingParameterBinder<java.lang.Long>
{
@Override
public java.lang.Long bind( java.lang.String name, java.lang.String value )
{
try
{
return java.lang.Long.valueOf( value );
}
catch( NumberFormatException ex )
{
throw new ParameterBinderException( "Invalid parameter '" + name + "' format: '" + value + "'", ex );
}
}
@Override
public java.lang.String unbind( java.lang.String name, java.lang.Long value )
{
return java.lang.String.valueOf( value );
}
}
/**
* {@link java.lang.Double} Parameter Binder.
*/
public static final class Double
extends StrictTypingParameterBinder<java.lang.Double>
{
@Override
public java.lang.Double bind( java.lang.String name, java.lang.String value )
{
try
{
return java.lang.Double.valueOf( value );
}
catch( NumberFormatException ex )
{
throw new ParameterBinderException( "Invalid parameter '" + name + "' format: '" + value + "'", ex );
}
}
@Override
public java.lang.String unbind( java.lang.String name, java.lang.Double value )
{
return java.lang.String.valueOf( value );
}
}
/**
* {@link java.lang.Float} Parameter Binder.
*/
public static final class Float
extends StrictTypingParameterBinder<java.lang.Float>
{
@Override
public java.lang.Float bind( java.lang.String name, java.lang.String value )
{
try
{
return java.lang.Float.valueOf( value );
}
catch( NumberFormatException ex )
{
throw new ParameterBinderException( "Invalid parameter '" + name + "' format: '" + value + "'", ex );
}
}
@Override
public java.lang.String unbind( java.lang.String name, java.lang.Float value )
{
return java.lang.String.valueOf( value );
}
}
/**
* {@link java.math.BigInteger} Parameter Binder.
*/
public static final class BigInteger
extends StrictTypingParameterBinder<java.math.BigInteger>
{
@Override
public java.math.BigInteger bind( java.lang.String name, java.lang.String value )
{
try
{
return new java.math.BigInteger( value );
}
catch( NumberFormatException ex )
{
throw new ParameterBinderException( "Invalid parameter '" + name + "' format: '" + value + "'", ex );
}
}
@Override
public java.lang.String unbind( java.lang.String name, java.math.BigInteger value )
{
return value.toString();
}
}
/**
* {@link java.math.BigDecimal} Parameter Binder.
*/
public static final class BigDecimal
extends StrictTypingParameterBinder<java.math.BigDecimal>
{
@Override
public java.math.BigDecimal bind( java.lang.String name, java.lang.String value )
{
try
{
return new java.math.BigDecimal( value );
}
catch( NumberFormatException ex )
{
throw new ParameterBinderException( "Invalid parameter '" + name + "' format: '" + value + "'", ex );
}
}
@Override
public java.lang.String unbind( java.lang.String name, java.math.BigDecimal value )
{
return value.toString();
}
}
/**
* {@link java.util.UUID} Parameter Binder.
*/
public static final class UUID
extends StrictTypingParameterBinder<java.util.UUID>
{
@Override
public java.util.UUID bind( java.lang.String name, java.lang.String value )
{
try
{
return java.util.UUID.fromString( value );
}
catch( IllegalArgumentException ex )
{
throw new ParameterBinderException( "Invalid parameter '" + name + "' format: '" + value + "'", ex );
}
}
@Override
public java.lang.String unbind( java.lang.String name, java.util.UUID value )
{
return value.toString();
}
}
/**
* {@link java.net.URL} Parameter Binder.
*/
public static final class URL
extends StrictTypingParameterBinder<java.net.URL>
{
@Override
public java.net.URL bind( java.lang.String name, java.lang.String value )
{
try
{
return new java.net.URL( value );
}
catch( MalformedURLException ex )
{
throw new ParameterBinderException( "Invalid parameter '" + name + "' format: '" + value + "'", ex );
}
}
@Override
public java.lang.String unbind( java.lang.String name, java.net.URL value )
{
return value.toString();
}
}
/**
* {@link java.lang.Class} Parameter Binder.
*/
public static final class Class
extends StrictTypingParameterBinder<java.lang.Class<?>>
{
@Override
public java.lang.Class<?> bind( java.lang.String name, java.lang.String value )
{
try
{
return java.lang.Class.forName( value );
}
catch( ClassNotFoundException ex )
{
throw new ParameterBinderException( "Invalid parameter '" + name + "' format: '" + value + "'", ex );
}
}
@Override
public java.lang.String unbind( java.lang.String name, java.lang.Class<?> value )
{
return value.getCanonicalName();
}
}
/**
* {@link java.time.Duration} Parameter Binder.
*/
public static final class Duration
extends StrictTypingParameterBinder<java.time.Duration>
{
@Override
public java.time.Duration bind( java.lang.String name, java.lang.String value )
{
try
{
return java.time.Duration.parse( value );
}
catch( DateTimeParseException ex )
{
throw new ParameterBinderException( "Invalid parameter '" + name + "' format: '" + value + "'", ex );
}
}
@Override
public java.lang.String unbind( java.lang.String name, java.time.Duration value )
{
return value.toString();
}
}
/**
* {@link java.time.Period} Parameter Binder.
*/
public static final class Period
extends StrictTypingParameterBinder<java.time.Period>
{
@Override
public java.time.Period bind( java.lang.String name, java.lang.String value )
{
try
{
return java.time.Period.parse( value );
}
catch( DateTimeParseException ex )
{
throw new ParameterBinderException( "Invalid parameter '" + name + "' format: '" + value + "'", ex );
}
}
@Override
public java.lang.String unbind( java.lang.String name, java.time.Period value )
{
return value.toString();
}
}
/**
* {@link java.time.Year} Parameter Binder.
*/
public static final class Year
extends StrictTypingParameterBinder<java.time.Year>
{
@Override
public java.time.Year bind( java.lang.String name, java.lang.String value )
{
try
{
return java.time.Year.parse( value );
}
catch( DateTimeParseException ex )
{
throw new ParameterBinderException( "Invalid parameter '" + name + "' format: '" + value + "'", ex );
}
}
@Override
public java.lang.String unbind( java.lang.String name, java.time.Year value )
{
return value.toString();
}
}
/**
* {@link java.time.Month} Parameter Binder.
*/
public static final class Month
extends StrictTypingParameterBinder<java.time.Month>
{
@Override
public java.time.Month bind( java.lang.String name, java.lang.String value )
{
try
{
return java.time.Month.of( java.lang.Integer.valueOf( value ) );
}
catch( NumberFormatException | DateTimeException ex )
{
throw new ParameterBinderException( "Invalid parameter '" + name + "' format: '" + value + "'", ex );
}
}
@Override
public java.lang.String unbind( java.lang.String name, java.time.Month value )
{
return java.lang.String.valueOf( value.getValue() );
}
}
/**
* {@link java.time.DayOfWeek} Parameter Binder.
*/
public static final class DayOfWeek
extends StrictTypingParameterBinder<java.time.DayOfWeek>
{
@Override
public java.time.DayOfWeek bind( java.lang.String name, java.lang.String value )
{
try
{
return java.time.DayOfWeek.of( java.lang.Integer.valueOf( value ) );
}
catch( NumberFormatException | DateTimeException ex )
{
throw new ParameterBinderException( "Invalid parameter '" + name + "' format: '" + value + "'", ex );
}
}
@Override
public java.lang.String unbind( java.lang.String name, java.time.DayOfWeek value )
{
return java.lang.String.valueOf( value.getValue() );
}
}
/**
* {@link java.time.YearMonth} Parameter Binder.
*/
public static final class YearMonth
extends StrictTypingParameterBinder<java.time.YearMonth>
{
@Override
public java.time.YearMonth bind( java.lang.String name, java.lang.String value )
{
return java.time.YearMonth.parse( value );
}
@Override
public java.lang.String unbind( java.lang.String name, java.time.YearMonth value )
{
return value.toString();
}
}
/**
* {@link java.time.MonthDay} Parameter Binder.
*/
public static final class MonthDay
extends StrictTypingParameterBinder<java.time.MonthDay>
{
@Override
public java.time.MonthDay bind( java.lang.String name, java.lang.String value )
{
return java.time.MonthDay.parse( value );
}
@Override
public java.lang.String unbind( java.lang.String name, java.time.MonthDay value )
{
return value.toString();
}
}
/**
* {@link java.time.LocalDate} Parameter Binder.
*/
public static final class LocalDate
extends StrictTypingParameterBinder<java.time.LocalDate>
{
@Override
public java.time.LocalDate bind( java.lang.String name, java.lang.String value )
{
return java.time.LocalDate.parse( value );
}
@Override
public java.lang.String unbind( java.lang.String name, java.time.LocalDate value )
{
return value.toString();
}
}
/**
* {@link java.time.LocalTime} Parameter Binder.
*/
public static final class LocalTime
extends StrictTypingParameterBinder<java.time.LocalTime>
{
@Override
public java.time.LocalTime bind( java.lang.String name, java.lang.String value )
{
return java.time.LocalTime.parse( value );
}
@Override
public java.lang.String unbind( java.lang.String name, java.time.LocalTime value )
{
return value.toString();
}
}
/**
* {@link java.time.LocalDateTime} Parameter Binder.
*/
public static final class LocalDateTime
extends StrictTypingParameterBinder<java.time.LocalDateTime>
{
@Override
public java.time.LocalDateTime bind( java.lang.String name, java.lang.String value )
{
return java.time.LocalDateTime.parse( value );
}
@Override
public java.lang.String unbind( java.lang.String name, java.time.LocalDateTime value )
{
return value.toString();
}
}
/**
* {@link java.time.OffsetTime} Parameter Binder.
*/
public static final class OffsetTime
extends StrictTypingParameterBinder<java.time.OffsetTime>
{
@Override
public java.time.OffsetTime bind( java.lang.String name, java.lang.String value )
{
return java.time.OffsetTime.parse( value );
}
@Override
public java.lang.String unbind( java.lang.String name, java.time.OffsetTime value )
{
return value.toString();
}
}
/**
* {@link java.time.OffsetDateTime} Parameter Binder.
*/
public static final class OffsetDateTime
extends StrictTypingParameterBinder<java.time.OffsetDateTime>
{
@Override
public java.time.OffsetDateTime bind( java.lang.String name, java.lang.String value )
{
return java.time.OffsetDateTime.parse( value );
}
@Override
public java.lang.String unbind( java.lang.String name, java.time.OffsetDateTime value )
{
return value.toString();
}
}
/**
* {@link java.time.ZonedDateTime} Parameter Binder.
*/
public static final class ZonedDateTime
extends StrictTypingParameterBinder<java.time.ZonedDateTime>
{
@Override
public java.time.ZonedDateTime bind( java.lang.String name, java.lang.String value )
{
return java.time.ZonedDateTime.parse( value );
}
@Override
public java.lang.String unbind( java.lang.String name, java.time.ZonedDateTime value )
{
return value.toString();
}
}
/**
* {@link io.werval.util.Hashid} Parameter Binder.
* <p>
* Use the application's secret as salt.
*/
public static final class Hashid
extends StrictTypingParameterBinder<io.werval.util.Hashid>
{
private Hashids hashids;
@Override
public void init( Application application )
{
hashids = application.crypto().hashids();
}
@Override
public io.werval.util.Hashid bind( java.lang.String name, java.lang.String value )
{
return hashids.decode( value );
}
@Override
public java.lang.String unbind( java.lang.String name, io.werval.util.Hashid value )
{
return value.toString();
}
}
private final List<ParameterBinder<?>> parameterBinders = new ArrayList<>();
public ParameterBindersInstance( List<ParameterBinder<?>> parameterBinders )
{
this.parameterBinders.addAll( parameterBinders );
}
@Override
@SuppressWarnings( "unchecked" )
public <T> T bind( java.lang.Class<T> type, java.lang.String paramName, java.lang.String paramValue )
throws ParameterBindingException
{
List<Exception> errors = new ArrayList<>();
for( ParameterBinder<?> parameterBinder : parameterBinders )
{
if( parameterBinder.accept( type ) )
{
try
{
return (T) parameterBinder.bind( paramName, paramValue );
}
catch( Exception ex )
{
errors.add( ex );
}
}
}
if( errors.isEmpty() )
{
throw new ParameterBindingException( "No ParameterBinder found for type: " + type );
}
ParameterBindingException ex = new ParameterBindingException(
"Unable to bind parameter " + paramName + " valued to " + paramValue
);
errors.forEach( e -> ex.addSuppressed( e ) );
throw ex;
}
@Override
@SuppressWarnings( "unchecked" )
public <T> java.lang.String unbind( java.lang.Class<T> type, java.lang.String paramName, T paramValue )
throws ParameterUnbindingException
{
List<Exception> errors = new ArrayList<>();
for( ParameterBinder<?> parameterBinder : parameterBinders )
{
if( parameterBinder.accept( type ) )
{
return ( (ParameterBinder<T>) parameterBinder ).unbind( paramName, paramValue );
}
}
if( errors.isEmpty() )
{
throw new ParameterUnbindingException( "No ParameterBinder found for type: " + type );
}
ParameterUnbindingException ex = new ParameterUnbindingException(
"Unable to unbind parameter " + paramName + " valued to " + paramValue
);
errors.forEach( e -> ex.addSuppressed( e ) );
throw ex;
}
}