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

org.boon.datarepo.Collections Maven / Gradle / Ivy

/*
 * Copyright 2013-2014 Richard M. Hightower
 * 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.
 *
 * __________                              _____          __   .__
 * \______   \ ____   ____   ____   /\    /     \ _____  |  | _|__| ____    ____
 *  |    |  _//  _ \ /  _ \ /    \  \/   /  \ /  \\__  \ |  |/ /  |/    \  / ___\
 *  |    |   (  <_> |  <_> )   |  \ /\  /    Y    \/ __ \|    <|  |   |  \/ /_/  >
 *  |______  /\____/ \____/|___|  / \/  \____|__  (____  /__|_ \__|___|  /\___  /
 *         \/                   \/              \/     \/     \/       \//_____/
 *      ____.                     ___________   _____    ______________.___.
 *     |    |____ ___  _______    \_   _____/  /  _  \  /   _____/\__  |   |
 *     |    \__  \\  \/ /\__  \    |    __)_  /  /_\  \ \_____  \  /   |   |
 * /\__|    |/ __ \\   /  / __ \_  |        \/    |    \/        \ \____   |
 * \________(____  /\_/  (____  / /_______  /\____|__  /_______  / / ______|
 *               \/           \/          \/         \/        \/  \/
 */
package org.boon.datarepo;

import org.boon.core.Typ;
import org.boon.core.reflection.BeanUtils;
import org.boon.core.reflection.Fields;
import org.boon.core.reflection.fields.FieldAccess;
import org.boon.criteria.internal.Criteria;
import org.boon.datarepo.impl.decorators.FilterWithSimpleCache;
import org.boon.datarepo.spi.SPIFactory;
import org.boon.datarepo.spi.SearchIndex;
import org.boon.datarepo.spi.SearchableCollectionComposer;
import org.boon.core.Function;

import java.util.*;

import static org.boon.Exceptions.requireNonNull;


/**
 * Wraps regular collections in data repo searchable collections.
 */
public class Collections {


    /**
     * $q turns a listStream into a querying listStream.
     *
     * @param list    the listStream you want to convert
     * @param classes classes you want to be able to criteria.
     * @param      The type this criteria listStream will return
     * @return generic listStream decorated with criteria features.
     */
    public static  List $q( final List list, Class... classes ) {
        return listQuery( list, true, true, classes );
    }

    /**
     * $c turns a listStream back into a regular listStream.
     * This is the reverse of $q.
     *
     * @param list the listStream
     * @param   the type of the listStream.
     * @return the new decorated listStream.
     * @see Collections#plainList(java.util.List)
     */
    public static  List $c( final List list ) {
        return plainList( list );
    }

    /**
     * $c turns a listStream back into a regular listStream.
     * This is the reverse of $q.
     *
     * @param list the listStream
     * @param   the type of the listStream.
     * @return the new decorated listStream.
     * @see Collections#$c(java.util.List)
     */
    private static  List plainList( List list ) {
        if ( list instanceof QList ) {
            return ( ( QList ) list ).list;
        } else {
            return list;
        }
    }


    /**
     * listQuery turns a listStream into a querying listStream.
     *
     * @param list the listStream you want to convert
     * @param   The type this criteria listStream will return
     * @return generic listStream decorated with criteria features.
     * @see Collections#$q(java.util.List, Class[])
     */
    public static  List listQuery( final List list ) {
        return listQuery( list, true, true );
    }

    /**
     * listQuery turns a listStream into a querying listStream.
     *
     * @param list      the listStream you want to convert
     * @param classes   classes you want to be able to criteria.
     * @param        The type this criteria listStream will return
     * @param useField  use the field instead of the property
     * @param useUnSafe use unsafe
     * @param classes   listStream of classes that we can criteria against, these can be component classes
     * @return generic listStream decorated with criteria features.
     */
    public static  List listQuery( final List list, boolean useField, boolean useUnSafe, Class... classes ) {
        if ( list == null || list.size() == 0 ) {
            return list;
        }

        SearchableCollectionComposer query = null;

        if ( classes == null || classes.length == 0 ) {
            Class clazz = list.get( 0 ).getClass();

            query = getSearchableCollectionComposer( list, useField, useUnSafe, clazz );

        } else {
            query = getSearchableCollectionComposer( list, useField, useUnSafe, classes );

        }

        return new QList( list, ( SearchableCollection ) query );
    }

    /**
     * Decorates a set with additional criteria capabilities.
     *
     * @param set set to decorate
     * @param  generic type
     * @return new decorated listStream
     */
    public static  Set $q( final Set set ) {
        return setQuery( set, true, true );
    }

    /**
     * Un-decorates a set with additional criteria capabilities.
     *
     * @param set set to un-decorate
     * @param  generic type
     * @return new decorated listStream
     */
    public static  Set $c( final Set set ) {
        return plainSet( set );
    }


    /**
     * Un-decorates a set with additional criteria capabilities.
     *
     * @param set set to un-decorate
     * @param  generic type
     * @return new decorated listStream
     */
    private static  Set plainSet( Set set ) {
        if ( set instanceof QSet ) {
            return ( ( QSet ) set ).set;
        } else {
            return set;
        }
    }

    /**
     * Decorates a set with additional criteria capabilities.
     *
     * @param set set to un-decorate
     * @param  generic type
     * @return new decorated listStream
     */
    public static  Set setQuery( final Set set ) {
        return setQuery( set, true, true );
    }

    /**
     * Decorates a set with all sorts of chocolaty richness
     *
     * @param set
     * @param useField
     * @param useUnSafe
     * @param 
     * @return
     */
    public static  Set setQuery( final Set set, boolean useField, boolean useUnSafe ) {
        if ( set == null || set.size() == 0 ) {
            return set;
        }

        Class clazz = set.iterator().next().getClass();

        SearchableCollectionComposer query = getSearchableCollectionComposer( set, useField, useUnSafe, clazz );

        return new QSet( set, ( SearchableCollection ) query );
    }

    /**
     * This is the internal method that does it all. :)
     *
     * @param set
     * @param useField
     * @param useUnSafe
     * @param classes
     * @param 
     * @return
     */
    private static  SearchableCollectionComposer getSearchableCollectionComposer( Collection set, boolean useField, boolean useUnSafe, Class... classes ) {
        SearchableCollectionComposer query = SPIFactory.getSearchableCollectionFactory().get();


        Map fields = new LinkedHashMap<>();

        for ( Class cls : classes ) {

            Map fieldsSubType
                    = BeanUtils.getFieldsFromObject( cls );

            for ( String sKey : fieldsSubType.keySet() ) {
                if ( !fields.containsKey( sKey ) ) {
                    fields.put( sKey, fieldsSubType.get( sKey ) );
                }
            }


        }

        String primaryKey = findPrimaryKey( fields );
        FieldAccess field = fields.get( primaryKey );
        Function keyGetter = createKeyGetter( field );

        query.setFields( fields );
        query.setPrimaryKeyGetter( keyGetter );
        query.setPrimaryKeyName( primaryKey );
        Filter filter = SPIFactory.getFilterFactory().get();
        query.setFilter( filter );


        LookupIndex index = SPIFactory.getUniqueLookupIndexFactory().apply( fields.get( primaryKey ).type() );
        index.setKeyGetter( keyGetter );
        ( ( SearchableCollection ) query ).addLookupIndex( primaryKey, index );


        for ( FieldAccess f : fields.values() ) {
            if ( f.name().equals( primaryKey ) ) {
                continue;
            }
            if ( Typ.isBasicType( f.type() ) ) {
                configIndexes( ( SearchableCollection ) query, f.name(), fields );
            }
        }

        query.init();

        query.setFilter( new FilterWithSimpleCache( filter ) );

        ( ( SearchableCollection ) query ).addAll( set );
        return query;
    }


    /**
     * Allow you to criteria a criteria-able listStream.
     *
     * @param list        the listStream you want to criteria
     * @param expressions array of expressions
     * @param          the type of the listStream
     * @return the criteria results or an empty listStream if the listStream was not a criteria-able listStream.
     */
    public static  List query( final List list, Criteria... expressions ) {
        if ( list instanceof QList ) {
            QList qlist = ( QList ) list;
            return qlist.searchCollection().query( expressions );
        } else {
            throw new DataRepoException( "Not a criteria-able listStream." );
        }
    }

    /**
     * Allow you to criteria a criteria-able listStream.
     *
     * @param list        the listStream you want to criteria
     * @param expressions array of expressions
     * @param          the type of the listStream
     * @return the criteria results or an empty listStream if the listStream was not a criteria-able listStream.
     */
    public static  List sortedQuery( final List list, String sortBy, Criteria... expressions ) {
        if ( list instanceof QList ) {
            QList qlist = ( QList ) list;
            return qlist.searchCollection().sortedQuery( sortBy, expressions );
        } else {
            throw new DataRepoException( "Not a criteria-able listStream." );
        }
    }


    /**
     * Allow you to criteria a criteria-able listStream.
     *
     * @param set         the set you want to criteria
     * @param expressions array of expressions
     * @param          the type of the listStream
     * @return the criteria results or an empty listStream if the listStream was not a criteria-able listStream.
     */
    public static  List query( final Set set, Criteria... expressions ) {
        if ( set instanceof QSet ) {
            QSet qset = ( QSet ) set;
            return qset.searchCollection().query( expressions );
        }
        return null;
    }

    /**
     * Allow you to criteria a criteria-able listStream.
     *
     * @param set         the set you want to criteria
     * @param expressions array of expressions
     * @param          the type of the listStream
     * @return the criteria results or an empty listStream if the listStream was not a criteria-able listStream.
     */
    public static  List sortedQuery( final Set set, String sortBy, Criteria... expressions ) {
        if ( set instanceof QSet ) {
            QSet qset = ( QSet ) set;
            return qset.searchCollection().sortedQuery( sortBy, expressions );
        }
        return null;
    }

    /**
     * placeholder for a generic way to discover a primary key.
     * Right now the primarykey must be called id.
     *
     * @param fields fields we are going to search for the primary key
     * @return
     */
    private static String findPrimaryKey( Map fields ) {
        return "id";
    }


    /**
     * Create key getter.
     *
     * @param field
     * @return
     */
    private static Function createKeyGetter( final FieldAccess field ) {

        requireNonNull( field, "field cannot be null" );
        return new Function() {
            @Override
            public Object apply( Object o ) {

                if ( Fields.hasField( o.getClass(), field.name() ) ) {
                    return field.getValue( o );
                } else {
                    return null;
                }
            }
        };
    }


    /**
     * Helper class that holds an inner set and a searchable collection.
     * TODO we need a navigable version of this.
     *
     * @param 
     */
    static class QSet extends AbstractSet implements CollectionDecorator {
        final Set set;
        final SearchableCollection searchCollection;

        QSet( Set set, SearchableCollection searchCollection ) {
            this.set = set;
            this.searchCollection = searchCollection;
        }

        @Override
        public boolean add( T item ) {
            searchCollection.add( item );
            return set.add( item );
        }

        @Override
        public boolean remove( Object item ) {
            searchCollection.delete( ( T ) item );
            return set.remove( item );
        }


        @Override
        public Iterator iterator() {
            return set.iterator();
        }

        @Override
        public int size() {
            return set.size();
        }

        @Override
        public SearchableCollection searchCollection() {
            return searchCollection;
        }

        @Override
        public Collection collection() {
            return set;
        }
    }

    /**
     * @param 
     */
    static class QList extends AbstractList implements CollectionDecorator {
        List list;
        SearchableCollection query;

        QList( List list, SearchableCollection query ) {
            this.list = list;
            this.query = query;
        }

        @Override
        public boolean add( T item ) {
            query.add( item );
            return list.add( item );
        }

        @Override
        public boolean remove( Object item ) {
            query.delete( ( T ) item );
            return list.remove( item );
        }


        @Override
        public T get( int index ) {
            return list.get( index );
        }


        @Override
        public int size() {
            return list.size();
        }


        @Override
        public SearchableCollection searchCollection() {
            return query;
        }

        @Override
        public Collection collection() {
            return this.list;
        }
    }


    /**
     * Configures the indexes.
     *
     * @param query  the search criteria
     * @param prop   the prop
     * @param fields the reflected fields
     */
    private static void configIndexes( SearchableCollection query, String prop,
                                       Map fields ) {

        SearchIndex searchIndex = SPIFactory.getSearchIndexFactory().apply( fields.get( prop ).type() );
        searchIndex.init();
        Function kg = createKeyGetter( fields.get( prop ) );
        searchIndex.setKeyGetter( kg );
        query.addSearchIndex( prop, searchIndex );

        LookupIndex index = SPIFactory.getLookupIndexFactory().apply( fields.get( prop ).type() );
        index.setKeyGetter( kg );
        query.addLookupIndex( prop, index );

    }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy