All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.neo4j.helpers.Settings Maven / Gradle / Ivy

/**
 * Copyright (c) 2002-2013 "Neo Technology,"
 * Network Engine for Objects in Lund AB [http://neotechnology.com]
 *
 * This file is part of Neo4j.
 *
 * Neo4j is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */
package org.neo4j.helpers;

import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.regex.Pattern;

import org.neo4j.graphdb.config.Setting;
import org.neo4j.helpers.collection.Iterables;
import org.neo4j.kernel.impl.util.FileUtils;

/**
 * Create settings for configurations in Neo4j. See {@link org.neo4j.graphdb.factory.GraphDatabaseSettings} for example.
 *
 * Each setting has a name, a parser that converts a string to the type of the setting, a default value,
 * an optional inherited setting, and optional value converters.
 *
 * A parser is a function that takes a string and converts to some type T. The parser may throw IllegalArgumentException
 * if it fails.
 *
 * The default value is the string representation of what you want as default. Special values are the constants NO_DEFAULT,
 * which means that you don't want any default value at all, and MANDATORY, which means that the user has to specify a value
 * for this setting. Not providing a mandatory value for a setting leads to an IllegalArgumentException.
 *
 * If a setting does not have a provided value, and no default, then
 */
public final class Settings
{
    private interface SettingHelper
            extends Setting
    {
        String lookup( Function settings );

        String defaultLookup( Function settings );
    }

    // Set default value to this if user HAS to set a value
    @SuppressWarnings("RedundantStringConstructorCall")
    // It's an explicitly allocated string so identity equality checks work
    public static final String MANDATORY = new String( "mandatory" );
    public static final String NO_DEFAULT = null;

    public static final String TRUE = "true";
    public static final String FALSE = "false";

    public static final String DURATION_FORMAT = "\\d+(ms|s|m)";
    public static final String SIZE_FORMAT = "\\d+[kmgKMG]?";

    public static final String ANY = ".+";

    @SuppressWarnings("unchecked")
    public static  Setting setting( final String name, final Function parser,
                                          final String defaultValue )
    {
        return setting( name, parser, defaultValue, (Setting) null );
    }

    public static  Setting setting( final String name, final Function parser,
                                          final String defaultValue,
                                          final Function2, T>... valueConverters )
    {
        return setting( name, parser, defaultValue, null, valueConverters );
    }

    @SuppressWarnings("unchecked")
    public static  Setting setting( final String name, final Function parser,
                                          final Setting inheritedSetting )
    {
        return setting( name, parser, null, inheritedSetting );
    }

    public static  Setting setting( final String name, final Function parser,
                                          final String defaultValue,
                                          final Setting inheritedSetting, final Function2, T>... valueConverters )
    {
        Function, String> valueLookup = named( name );

        Function, String> defaultLookup;
        if ( defaultValue != null )
        {
            //noinspection StringEquality
            if ( defaultValue.equals( MANDATORY ) )
            {
                defaultLookup = mandatory( valueLookup );
            }
            else
            {
                defaultLookup = withDefault( defaultValue, valueLookup );
            }
        }
        else
        {
            defaultLookup = Functions.nullFunction();
        }

        if ( inheritedSetting != null )
        {
            valueLookup = inheritedValue( valueLookup, inheritedSetting );
            defaultLookup = inheritedDefault( defaultLookup, inheritedSetting );
        }

        return new DefaultSetting( name, parser, valueLookup, defaultLookup, valueConverters );
    }

    private static  Function, String> inheritedValue( final Function, String> lookup, final Setting inheritedSetting )
    {
        return new Function, String>()
        {
            @Override
            public String apply( Function settings )
            {
                String value = lookup.apply( settings );
                if ( value == null )
                {
                    value = ((SettingHelper) inheritedSetting).lookup( settings );
                }
                return value;
            }
        };
    }

    private static  Function, String> inheritedDefault( final Function, String> lookup, final Setting inheritedSetting )
    {
        return new Function, String>()
        {
            @Override
            public String apply( Function settings )
            {
                String value = lookup.apply( settings );
                if ( value == null )
                {
                    value = ((SettingHelper) inheritedSetting).defaultLookup( settings );
                }
                return value;
            }
        };
    }

    public static final Function INTEGER = new Function()
    {
        @Override
        public Integer apply( String value )
        {
            try
            {
                return Integer.parseInt( value );
            }
            catch ( NumberFormatException e )
            {
                throw new IllegalArgumentException( "not a valid integer value" );
            }
        }

        @Override
        public String toString()
        {
            return "an integer";
        }
    };

    public static final Function LONG = new Function()
    {
        @Override
        public Long apply( String value )
        {
            try
            {
                return Long.parseLong( value );
            }
            catch ( NumberFormatException e )
            {
                throw new IllegalArgumentException( "not a valid long value" );
            }
        }

        @Override
        public String toString()
        {
            return "a long";
        }
    };

    public static final Function BOOLEAN = new Function()
    {
        @Override
        public Boolean apply( String value )
        {
            if ( value.equalsIgnoreCase( "true" ) )
            {
                return true;
            }
            else if ( value.equalsIgnoreCase( "false" ) )
            {
                return false;
            }
            else
            {
                throw new IllegalArgumentException( "must be 'true' or 'false'" );
            }
        }

        @Override
        public String toString()
        {
            return "a boolean";
        }
    };

    public static final Function FLOAT = new Function()
    {
        @Override
        public Float apply( String value )
        {
            try
            {
                return Float.parseFloat( value );
            }
            catch ( NumberFormatException e )
            {
                throw new IllegalArgumentException( "not a valid float value" );
            }
        }

        @Override
        public String toString()
        {
            return "a float";
        }
    };

    public static final Function DOUBLE = new Function()
    {
        @Override
        public Double apply( String value )
        {
            try
            {
                return Double.parseDouble( value );
            }
            catch ( NumberFormatException e )
            {
                throw new IllegalArgumentException( "not a valid double value" );
            }
        }

        @Override
        public String toString()
        {
            return "a double";
        }
    };

    public static final Function STRING = new Function()
    {
        @Override
        public String apply( String value )
        {
            return value.trim();
        }

        @Override
        public String toString()
        {
            return "a string";
        }
    };

    public static final Function HOSTNAME_PORT = new Function()
    {
        @Override
        public HostnamePort apply( String value )
        {
            return new HostnamePort( value );
        }

        @Override
        public String toString()
        {
            return "a hostname and port";
        }
    };

    public static final Function DURATION = new Function()
    {
        @Override
        public Long apply( String value )
        {
            return TimeUtil.parseTimeMillis.apply( value );
        }

        @Override
        public String toString()
        {
            return "a duration";
        }
    };

    public static final Function BYTES = new Function()
    {
        @Override
        public Long apply( String value )
        {
            try
            {
                String mem = value.toLowerCase();
                long multiplier = 1;
                if ( mem.endsWith( "k" ) )
                {
                    multiplier = 1024;
                    mem = mem.substring( 0, mem.length() - 1 );
                }
                else if ( mem.endsWith( "m" ) )
                {
                    multiplier = 1024 * 1024;
                    mem = mem.substring( 0, mem.length() - 1 );
                }
                else if ( mem.endsWith( "g" ) )
                {
                    multiplier = 1024 * 1024 * 1024;
                    mem = mem.substring( 0, mem.length() - 1 );
                }

                return Long.parseLong( mem.trim() ) * multiplier;
            }
            catch ( NumberFormatException e )
            {
                throw new IllegalArgumentException( String.format( "%s is not a valid size, must be e.g. 10, 5K, 1M, " +
                        "11G", value ) );
            }
        }

        @Override
        public String toString()
        {
            return "a byte size";
        }
    };

    public static final Function URI =
            new Function()
            {
                @Override
                public URI apply( String value )
                {
                    try
                    {
                        return new URI( value );
                    }
                    catch ( URISyntaxException e )
                    {
                        throw new IllegalArgumentException( "not a valid URI" );
                    }
                }

                @Override
                public String toString()
                {
                    return "a URI";
                }
            };

    public static final Function PATH = new Function()
    {
        @Override
        public File apply( String setting )
        {
            setting = FileUtils.fixSeparatorsInPath( setting );

            return new File( setting );
        }

        @Override
        public String toString()
        {
            return "a path";
        }
    };


    public static  Function options( final Class enumClass )
    {
        return options( EnumSet.allOf( enumClass ) );
    }

    public static  Function options( T... optionValues )
    {
        return Settings.options( Iterables.iterable( optionValues ) );
    }

    public static  Function options( final Iterable optionValues )
    {
        return new Function()
        {
            @Override
            public T apply( String value )
            {
                for ( T optionValue : optionValues )
                {
                    if ( optionValue.toString().equals( value ) )
                    {
                        return optionValue;
                    }
                }
                throw new IllegalArgumentException( "must be one of " + Iterables.toList( optionValues ).toString() );
            }

            @Override
            public String toString()
            {
                StringBuilder builder = new StringBuilder(  );
                builder.append( "one of " );
                String comma = "";
                for ( T optionValue : optionValues )
                {
                    builder.append( comma ).append( optionValue.toString() );
                    comma = ", ";
                }
                return builder.toString();
            }
        };
    }

    public static  Function> list( final String separator, final Function itemParser )
    {
        return new Function>()
        {
            @Override
            public List apply( String value )
            {
                String[] parts = value.split( separator );
                List list = new ArrayList();
                for ( String part : parts )
                {
                    list.add( itemParser.apply( part ) );
                }
                return list;
            }

            @Override
            public String toString()
            {
                return "a list separated by '"+separator+"' where items are "+itemParser.toString();
            }
        };
    }

    // Modifiers
    public static Function2, String> matches( final String regex )
    {
        final Pattern pattern = Pattern.compile( regex );

        return new Function2, String>()
        {
            @Override
            public String apply( String value, Function settings )
            {
                if ( !pattern.matcher( value ).matches() )
                {
                    throw new IllegalArgumentException( "value does not match expression:" + regex );
                }

                return value;
            }
        };
    }

    public static > Function2, T> min( final T min )
    {
        return new Function2, T>()
        {
            @Override
            public T apply( T value, Function settings )
            {
                if ( value != null && value.compareTo( min ) < 0 )
                {
                    throw new IllegalArgumentException( String.format( "minimum allowed value is: %s", min ) );
                }
                return value;
            }
        };
    }

    public static > Function2, T> max( final T max )
    {
        return new Function2, T>()
        {
            @Override
            public T apply( T value, Function settings )
            {
                if ( value != null && value.compareTo( max ) > 0 )
                {
                    throw new IllegalArgumentException( String.format( "maximum allowed value is: %s", max ) );
                }
                return value;
            }
        };
    }

    public static > Function2, T> range( final T min, final T max )
    {
        return Functions.>compose2().apply( min( min ), max( max ) );
    }

    public static final Function2, Integer> port =
            illegalValueMessage( "must be a valid port number(1-65535)", range( 1, 65535 ) );

    public static  Function2, T> illegalValueMessage( final String message,
                                                                                     final Function2,
                                                                                             T> valueFunction )
    {
        return new Function2, T>()
        {
            @Override
            public T apply( T from1, Function from2 )
            {
                try
                {
                    return valueFunction.apply( from1, from2 );
                }
                catch ( IllegalArgumentException e )
                {
                    throw new IllegalArgumentException( message );
                }
            }
        };
    }

    public static Function2, String> toLowerCase =
            new Function2, String>()
            {
                @Override
                public String apply( String value, Function settings )
                {
                    return value.toLowerCase();
                }
            };

    public static Function2, URI> normalize =
            new Function2, URI>()
            {
                @Override
                public URI apply( URI value, Function settings )
                {
                    String resultStr = value.normalize().toString();
                    if ( resultStr.endsWith( "/" ) )
                    {
                        value = java.net.URI.create( resultStr.substring( 0, resultStr.length() - 1 ) );
                    }
                    return value;
                }
            };

    // Setting converters and constraints
    public static Function2, File> basePath( final Setting baseSetting )
    {
        return new Function2, File>()
        {
            @Override
            public File apply( File path, Function settings )
            {
                File parent = baseSetting.apply( settings );

                return path.isAbsolute() ? path : new File( parent, path.getPath() );
            }

            @Override
            public String toString()
            {
                return "relative to '"+baseSetting.name()+"'";
            }
        };
    }

    public static Function2, File> isFile =
            new Function2, File>()
            {
                @Override
                public File apply( File path, Function settings )
                {
                    if ( path.exists() && !path.isFile() )
                    {
                        throw new IllegalArgumentException( String.format( "%s must point to a file, not a directory",
                                path.toString() ) );
                    }

                    return path;
                }
            };

    public static Function2, File> isDirectory =
            new Function2, File>()
            {
                @Override
                public File apply( File path, Function settings )
                {
                    if ( path.exists() && !path.isDirectory() )
                    {
                        throw new IllegalArgumentException( String.format( "%s must point to a file, not a directory",
                                path.toString() ) );
                    }

                    return path;
                }
            };

    // Setting helpers
    private static Function, String> named( final String name )
    {
        return new Function, String>()
        {
            @Override
            public String apply( Function settings )
            {
                return settings.apply( name );
            }
        };
    }

    private static Function, String> withDefault( final String defaultValue,
                                                                           final Function,
                                                                                   String> lookup )
    {
        return new Function, String>()
        {
            @Override
            public String apply( Function settings )
            {
                String value = lookup.apply( settings );
                if ( value == null )
                {
                    return defaultValue;
                }
                else
                {
                    return value;
                }
            }
        };
    }

    private static Function, String> mandatory( final Function,
            String> lookup )
    {
        return new Function, String>()
        {
            @Override
            public String apply( Function settings )
            {
                String value = lookup.apply( settings );
                if ( value == null )
                {
                    throw new IllegalArgumentException( "mandatory setting is missing" );
                }
                return value;
            }
        };
    }

    public static boolean osIsWindows()
    {
        String nameOs = System.getProperty( "os.name" );
        return nameOs.startsWith( "Windows" );
    }

    public static boolean osIsMacOS()
    {
        String nameOs = System.getProperty( "os.name" );
        return nameOs.equalsIgnoreCase( "Mac OS X" );
    }

    private Settings()
    {
    }

    public static class DefaultSetting implements SettingHelper
    {
        private final String name;
        private final Function parser;
        private final Function, String> valueLookup;
        private final Function, String> defaultLookup;
        private Function2, T>[] valueConverters;

        public DefaultSetting( String name, Function parser,
                               Function, String> valueLookup, Function, String> defaultLookup,
                               Function2, T>... valueConverters )
        {
            this.name = name;
            this.parser = parser;
            this.valueLookup = valueLookup;
            this.defaultLookup = defaultLookup;
            this.valueConverters = valueConverters;
        }

        @Override
        public String name()
        {
            return name;
        }

        @Override
        public String getDefaultValue()
        {
            return defaultLookup( Functions.nullFunction() );
        }

        public String lookup( Function settings )
        {
            return valueLookup.apply( settings );
        }

        public String defaultLookup( Function settings )
        {
            return defaultLookup.apply( settings );
        }

        @Override
        public T apply( Function settings )
        {
            // Lookup value as string
            String value = lookup( settings );

            // Try defaults
            if ( value == null )
            {
                try
                {
                    value = defaultLookup( settings );
                }
                catch ( Exception e )
                {
                    throw new IllegalArgumentException( String.format( "Missing mandatory setting '%s'", name() ) );
                }
            }

            // If still null, return null
            if ( value == null )
            {
                return null;
            }

            // Parse value
            T result;
            try
            {
                result = parser.apply( value );
                // Apply converters and constraints
                for ( Function2, T> valueConverter : valueConverters )
                {
                    result = valueConverter.apply( result, settings );
                }
            }
            catch ( IllegalArgumentException e )
            {
                throw new IllegalArgumentException( String.format( "Bad value '%s' for setting '%s': %s", value, name(), e.getMessage() ) );
            }


            return result;
        }

        @Override
        public String toString()
        {
            StringBuilder builder = new StringBuilder(  );
            builder.append( name ).append( " is " ).append( parser.toString() );

            if (valueConverters.length > 0)
            {
                builder.append( " which " );
                for ( int i = 0; i < valueConverters.length; i++ )
                {
                    Function2, T> valueConverter = valueConverters[i];
                    if (i > 0)
                        builder.append( ", and " );
                    builder.append( valueConverter );
                }
            }


            return builder.toString();
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy