001package org.jadira.scanner.core.spi;
002
003import java.util.ArrayList;
004import java.util.Arrays;
005import java.util.Collections;
006import java.util.List;
007
008import org.jadira.scanner.core.api.Allocator;
009import org.jadira.scanner.core.api.Filter;
010import org.jadira.scanner.core.api.Locator;
011import org.jadira.scanner.core.api.Projector;
012import org.jadira.scanner.core.api.Resolver;
013import org.jadira.scanner.core.concurrent.AllocatorTask;
014import org.jadira.scanner.core.concurrent.FilterTask;
015import org.jadira.scanner.core.concurrent.ProjectorTask;
016
017public abstract class AbstractResolver<T, E, A> implements Resolver<T, E, A> {
018
019        private static final Integer ZERO = Integer.valueOf(0);
020
021        private static final int SEGMENT_SIZE = 500;
022
023        private final List<A> driverData;
024        
025        protected AbstractResolver() {
026                this.driverData = new ArrayList<A>();
027        }
028        
029        protected AbstractResolver(List<A> driverData) {
030                this.driverData = driverData;
031        }
032        
033        protected List<A> getDriverData() {
034                return driverData;
035        }
036        
037        protected List<A> locate(Locator<A> locator) {
038                
039                final List<A> result = new ArrayList<A>();
040                if (driverData != null) {
041                        result.addAll(driverData);
042                }
043                final List<A> located = locator == null ? null : locator.locate();
044                if (located != null) {
045                        result.addAll(located);
046                }
047                return result;
048        }
049        
050        protected List<E> allocate(List<A> driverData) {
051                
052                AllocatorTask<E, A> task = new AllocatorTask<E, A>(getAllocator(), driverData);
053                final List<E> result = task.compute();
054                
055                return result;
056        }
057        
058        protected List<E> project(Projector<E> projector, List<E> sourceList) {
059                
060                ProjectorTask<E> task = new ProjectorTask<E>(projector, sourceList);
061                List<E> result = task.compute();
062                
063                return result;
064        }
065        
066        protected List<T> assign(List<E> sourceList) {
067                
068                AllocatorTask<T, E> task = new AllocatorTask<T, E>(getAssigner(), sourceList);
069                List<T> result = task.compute();
070                
071                return result;
072        }
073        
074        private <S> List<S> filter(Class<?> sourceType, Integer limit, List<Filter<?>> myFilters, List<S> sourceList) {
075
076                if (ZERO.equals(limit)) {
077                        return Collections.emptyList();
078                }
079                
080                List<S> result = sourceList;
081                for (Filter<?> nextFilter : myFilters) {
082                        if (nextFilter.targetType().isAssignableFrom(sourceType)) {
083                                
084                                @SuppressWarnings("unchecked")
085                                Filter<S> theFilter = (Filter<S>) nextFilter;
086                                
087                                FilterTask<S> task = new FilterTask<S>(limit, theFilter, result);
088                                result = task.compute();
089                        }
090                }
091                return result;
092        }
093        
094        protected abstract Allocator<E,A> getAllocator();
095        
096        protected abstract Allocator<T,E> getAssigner();
097
098        @Override
099        public T resolveFirst(Locator<A> locator, Projector<E> projector, Filter<?>... filters) {
100                List<? extends T> result = resolve(Integer.valueOf(1), locator, projector, filters);
101                return result.isEmpty() ? null : result.get(0);
102        }
103
104        @Override
105        public List<? extends T> resolve(Integer limit, Locator<A> locator, Projector<E> projector, Filter<?>... filters) {
106
107                final List<Filter<?>> myFilters = Arrays.asList(filters);
108                
109                final List<A> locatedList = locate(locator);
110                List<E> sourceList = allocate(locatedList);
111                
112                // Chunk the source list to avoid resource starvation
113                final List<T> output = new ArrayList<T>();
114
115                sourceList = project(projector, sourceList);
116                
117                while (!sourceList.isEmpty() 
118                                && (limit == null || (output.size() < limit))) {
119                        
120                        List<E> nextSegmentList;
121                        if (sourceList.size() <= SEGMENT_SIZE) {
122                                nextSegmentList = sourceList;
123                                sourceList = Collections.emptyList();
124                        } else {
125                                nextSegmentList = sourceList.subList(0, SEGMENT_SIZE);
126                                sourceList = sourceList.subList(SEGMENT_SIZE, sourceList.size());
127                        }
128                        
129                        nextSegmentList = filter(getSourceType(), null, myFilters, nextSegmentList);
130                
131                        if (!nextSegmentList.isEmpty()) {
132                                List<T> targetList = assign(nextSegmentList);
133                                targetList = filter(getTargetType(), limit, myFilters, targetList);
134                                output.addAll(targetList);
135                        }
136                }
137                if (limit == null) {
138                        return output;
139                } else {
140                        return output.subList(0, output.size() < limit ? output.size() : limit);
141                }
142        }
143
144        @Override
145        public List<? extends T> resolveAll(Locator<A> locator, Projector<E> projector, Filter<?>... filters) {
146                return resolve(null, locator, projector, filters);
147        }
148        
149        protected Class<?> getSourceType() {
150                return TypeHelper.getTypeArguments(AbstractResolver.class, this.getClass()).get(1);
151        }
152
153        protected Class<?> getTargetType() {
154                return TypeHelper.getTypeArguments(AbstractResolver.class, this.getClass()).get(0);
155        }
156}