]> source.dussan.org Git - archiva.git/blob
827eda035aafe8e04fb1139779c8cdc6df4b0354
[archiva.git] /
1 package org.apache.archiva.indexer.search;
2
3 /*
4  * Licensed to the Apache Software Foundation (ASF) under one
5  * or more contributor license agreements.  See the NOTICE file
6  * distributed with this work for additional information
7  * regarding copyright ownership.  The ASF licenses this file
8  * to you under the Apache License, Version 2.0 (the
9  * "License"); you may not use this file except in compliance
10  * with the License.  You may obtain a copy of the License at
11  *
12  *  http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  * KIND, either express or implied.  See the License for the
18  * specific language governing permissions and limitations
19  * under the License.
20  */
21
22 import org.apache.archiva.admin.model.RepositoryAdminException;
23 import org.apache.archiva.admin.model.beans.ManagedRepository;
24 import org.apache.archiva.admin.model.beans.ProxyConnector;
25 import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin;
26 import org.apache.archiva.admin.model.proxyconnector.ProxyConnectorAdmin;
27 import org.apache.archiva.common.plexusbridge.PlexusSisuBridgeException;
28 import org.apache.archiva.indexer.util.SearchUtil;
29 import org.apache.commons.lang.StringUtils;
30 import org.apache.maven.index.ArtifactInfo;
31 import org.apache.maven.index.FlatSearchRequest;
32 import org.apache.maven.index.FlatSearchResponse;
33 import org.apache.maven.index.MAVEN;
34 import org.apache.maven.index.NexusIndexer;
35 import org.apache.maven.index.OSGI;
36 import org.apache.maven.index.QueryCreator;
37 import org.apache.maven.index.SearchType;
38 import org.apache.maven.index.context.IndexCreator;
39 import org.apache.maven.index.context.IndexingContext;
40 import org.apache.maven.index.expr.SearchExpression;
41 import org.apache.maven.index.expr.SearchTyped;
42 import org.apache.maven.index.expr.SourcedSearchExpression;
43 import org.apache.maven.index.expr.UserInputSearchExpression;
44 import org.apache.maven.index.shaded.lucene.search.BooleanClause;
45 import org.apache.maven.index.shaded.lucene.search.BooleanClause.Occur;
46 import org.apache.maven.index.shaded.lucene.search.BooleanQuery;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49 import org.springframework.stereotype.Service;
50
51 import javax.inject.Inject;
52 import java.io.IOException;
53 import java.util.ArrayList;
54 import java.util.Collection;
55 import java.util.Collections;
56 import java.util.HashSet;
57 import java.util.List;
58 import java.util.Map;
59 import java.util.Set;
60
61 /**
62  * RepositorySearch implementation which uses the Maven Indexer for searching.
63  */
64 @Service( "repositorySearch#maven" )
65 public class MavenRepositorySearch
66     implements RepositorySearch
67 {
68     private Logger log = LoggerFactory.getLogger( getClass() );
69
70     private NexusIndexer indexer;
71
72     private QueryCreator queryCreator;
73
74     private ManagedRepositoryAdmin managedRepositoryAdmin;
75
76     private ProxyConnectorAdmin proxyConnectorAdmin;
77
78     protected MavenRepositorySearch()
79     {
80         // for test purpose
81     }
82
83     @Inject
84     public MavenRepositorySearch( NexusIndexer nexusIndexer, ManagedRepositoryAdmin managedRepositoryAdmin,
85                                   ProxyConnectorAdmin proxyConnectorAdmin, QueryCreator queryCreator )
86         throws PlexusSisuBridgeException
87     {
88         this.indexer = nexusIndexer;
89         this.queryCreator = queryCreator;
90         this.managedRepositoryAdmin = managedRepositoryAdmin;
91         this.proxyConnectorAdmin = proxyConnectorAdmin;
92     }
93
94     /**
95      * @see RepositorySearch#search(String, List, String, SearchResultLimits, List)
96      */
97     @Override
98     public SearchResults search( String principal, List<String> selectedRepos, String term, SearchResultLimits limits,
99                                  List<String> previousSearchTerms )
100         throws RepositorySearchException
101     {
102         List<String> indexingContextIds = addIndexingContexts( selectedRepos );
103
104         // since upgrade to nexus 2.0.0, query has changed from g:[QUERIED TERM]* to g:*[QUERIED TERM]*
105         //      resulting to more wildcard searches so we need to increase max clause count
106         BooleanQuery.setMaxClauseCount( Integer.MAX_VALUE );
107         BooleanQuery q = new BooleanQuery();
108
109         if ( previousSearchTerms == null || previousSearchTerms.isEmpty() )
110         {
111             constructQuery( term, q );
112         }
113         else
114         {
115             for ( String previousTerm : previousSearchTerms )
116             {
117                 BooleanQuery iQuery = new BooleanQuery();
118                 constructQuery( previousTerm, iQuery );
119
120                 q.add( iQuery, BooleanClause.Occur.MUST );
121             }
122
123             BooleanQuery iQuery = new BooleanQuery();
124             constructQuery( term, iQuery );
125             q.add( iQuery, BooleanClause.Occur.MUST );
126         }
127
128         // we retun only artifacts without classifier in quick search, olamy cannot find a way to say with this field empty
129         // FIXME  cannot find a way currently to setup this in constructQuery !!!
130         return search( limits, q, indexingContextIds, NoClassifierArtifactInfoFilter.LIST, selectedRepos, true );
131
132     }
133
134     /**
135      * @see RepositorySearch#search(String, SearchFields, SearchResultLimits)
136      */
137     @Override
138     public SearchResults search( String principal, SearchFields searchFields, SearchResultLimits limits )
139         throws RepositorySearchException
140     {
141         if ( searchFields.getRepositories() == null )
142         {
143             throw new RepositorySearchException( "Repositories cannot be null." );
144         }
145
146         List<String> indexingContextIds = addIndexingContexts( searchFields.getRepositories() );
147
148         // if no index found in the specified ones return an empty search result instead of doing a search on all index
149         // olamy: IMHO doesn't make sense
150         if ( !searchFields.getRepositories().isEmpty() && ( indexingContextIds == null
151             || indexingContextIds.isEmpty() ) )
152         {
153             return new SearchResults();
154         }
155
156         BooleanQuery q = new BooleanQuery();
157         if ( StringUtils.isNotBlank( searchFields.getGroupId() ) )
158         {
159             q.add( indexer.constructQuery( MAVEN.GROUP_ID, searchFields.isExactSearch() ? new SourcedSearchExpression(
160                        searchFields.getGroupId() ) : new UserInputSearchExpression( searchFields.getGroupId() ) ),
161                    BooleanClause.Occur.MUST );
162         }
163
164         if ( StringUtils.isNotBlank( searchFields.getArtifactId() ) )
165         {
166             q.add( indexer.constructQuery( MAVEN.ARTIFACT_ID,
167                                            searchFields.isExactSearch()
168                                                ? new SourcedSearchExpression( searchFields.getArtifactId() )
169                                                : new UserInputSearchExpression( searchFields.getArtifactId() ) ),
170                    BooleanClause.Occur.MUST );
171         }
172
173         if ( StringUtils.isNotBlank( searchFields.getVersion() ) )
174         {
175             q.add( indexer.constructQuery( MAVEN.VERSION, searchFields.isExactSearch() ? new SourcedSearchExpression(
176                        searchFields.getVersion() ) : new SourcedSearchExpression( searchFields.getVersion() ) ),
177                    BooleanClause.Occur.MUST );
178         }
179
180         if ( StringUtils.isNotBlank( searchFields.getPackaging() ) )
181         {
182             q.add( indexer.constructQuery( MAVEN.PACKAGING, searchFields.isExactSearch() ? new SourcedSearchExpression(
183                        searchFields.getPackaging() ) : new UserInputSearchExpression( searchFields.getPackaging() ) ),
184                    BooleanClause.Occur.MUST );
185         }
186
187         if ( StringUtils.isNotBlank( searchFields.getClassName() ) )
188         {
189             q.add( indexer.constructQuery( MAVEN.CLASSNAMES,
190                                            new UserInputSearchExpression( searchFields.getClassName() ) ),
191                    BooleanClause.Occur.MUST );
192         }
193
194         if ( StringUtils.isNotBlank( searchFields.getBundleSymbolicName() ) )
195         {
196             q.add( indexer.constructQuery( OSGI.SYMBOLIC_NAME,
197                                            new UserInputSearchExpression( searchFields.getBundleSymbolicName() ) ),
198                    BooleanClause.Occur.MUST );
199         }
200
201         if ( StringUtils.isNotBlank( searchFields.getBundleVersion() ) )
202         {
203             q.add( indexer.constructQuery( OSGI.VERSION,
204                                            new UserInputSearchExpression( searchFields.getBundleVersion() ) ),
205                    BooleanClause.Occur.MUST );
206         }
207
208         if ( StringUtils.isNotBlank( searchFields.getBundleExportPackage() ) )
209         {
210             q.add( indexer.constructQuery( OSGI.EXPORT_PACKAGE,
211                                            new UserInputSearchExpression( searchFields.getBundleExportPackage() ) ),
212                    Occur.MUST );
213         }
214
215         if ( StringUtils.isNotBlank( searchFields.getBundleExportService() ) )
216         {
217             q.add( indexer.constructQuery( OSGI.EXPORT_SERVICE,
218                                            new UserInputSearchExpression( searchFields.getBundleExportService() ) ),
219                    Occur.MUST );
220         }
221
222         if ( StringUtils.isNotBlank( searchFields.getBundleImportPackage() ) )
223         {
224             q.add( indexer.constructQuery( OSGI.IMPORT_PACKAGE,
225                                            new UserInputSearchExpression( searchFields.getBundleImportPackage() ) ),
226                    Occur.MUST );
227         }
228
229         if ( StringUtils.isNotBlank( searchFields.getBundleName() ) )
230         {
231             q.add( indexer.constructQuery( OSGI.NAME, new UserInputSearchExpression( searchFields.getBundleName() ) ),
232                    Occur.MUST );
233         }
234
235         if ( StringUtils.isNotBlank( searchFields.getBundleImportPackage() ) )
236         {
237             q.add( indexer.constructQuery( OSGI.IMPORT_PACKAGE,
238                                            new UserInputSearchExpression( searchFields.getBundleImportPackage() ) ),
239                    Occur.MUST );
240         }
241
242         if ( StringUtils.isNotBlank( searchFields.getBundleRequireBundle() ) )
243         {
244             q.add( indexer.constructQuery( OSGI.REQUIRE_BUNDLE,
245                                            new UserInputSearchExpression( searchFields.getBundleRequireBundle() ) ),
246                    Occur.MUST );
247         }
248
249         if ( StringUtils.isNotBlank( searchFields.getClassifier() ) )
250         {
251             q.add( indexer.constructQuery( MAVEN.CLASSIFIER, searchFields.isExactSearch() ? new SourcedSearchExpression(
252                        searchFields.getClassifier() ) : new UserInputSearchExpression( searchFields.getClassifier() ) ),
253                    Occur.MUST );
254         }
255         else if ( searchFields.isExactSearch() )
256         {
257             //TODO improvement in case of exact search and no classifier we must query for classifier with null value
258             // currently it's done in DefaultSearchService with some filtering
259         }
260
261         if ( q.getClauses() == null || q.getClauses().length <= 0 )
262         {
263             throw new RepositorySearchException( "No search fields set." );
264         }
265
266         return search( limits, q, indexingContextIds, Collections.<ArtifactInfoFilter>emptyList(),
267                        searchFields.getRepositories(), searchFields.isIncludePomArtifacts() );
268     }
269
270     private static class NullSearch
271         implements SearchTyped, SearchExpression
272     {
273         private static final NullSearch INSTANCE = new NullSearch();
274
275         @Override
276         public String getStringValue()
277         {
278             return "[[NULL_VALUE]]";
279         }
280
281         @Override
282         public SearchType getSearchType()
283         {
284             return SearchType.EXACT;
285         }
286     }
287
288     private SearchResults search( SearchResultLimits limits, BooleanQuery q, List<String> indexingContextIds,
289                                   List<? extends ArtifactInfoFilter> filters, List<String> selectedRepos,
290                                   boolean includePoms )
291         throws RepositorySearchException
292     {
293
294         try
295         {
296             FlatSearchRequest request = new FlatSearchRequest( q );
297
298             request.setContexts( getIndexingContexts( indexingContextIds ) );
299             if ( limits != null )
300             {
301                 // we apply limits only when first page asked
302                 if ( limits.getSelectedPage() == 0 )
303                 {
304                     request.setCount( limits.getPageSize() * ( Math.max( 1, limits.getSelectedPage() ) ) );
305                 }
306             }
307
308             FlatSearchResponse response = indexer.searchFlat( request );
309
310             if ( response == null || response.getTotalHits() == 0 )
311             {
312                 SearchResults results = new SearchResults();
313                 results.setLimits( limits );
314                 return results;
315             }
316
317             return convertToSearchResults( response, limits, filters, selectedRepos, includePoms );
318         }
319         catch ( IOException e )
320         {
321             throw new RepositorySearchException( e.getMessage(), e );
322         }
323         catch ( RepositoryAdminException e )
324         {
325             throw new RepositorySearchException( e.getMessage(), e );
326         }
327
328     }
329
330     private List<IndexingContext> getIndexingContexts( List<String> ids )
331     {
332         List<IndexingContext> contexts = new ArrayList<>( ids.size() );
333
334         for ( String id : ids )
335         {
336             IndexingContext context = indexer.getIndexingContexts().get( id );
337             if ( context != null )
338             {
339                 contexts.add( context );
340             }
341             else
342             {
343                 log.warn( "context with id {} not exists", id );
344             }
345         }
346
347         return contexts;
348     }
349
350     private void constructQuery( String term, BooleanQuery q )
351     {
352         q.add( indexer.constructQuery( MAVEN.GROUP_ID, new UserInputSearchExpression( term ) ), Occur.SHOULD );
353         q.add( indexer.constructQuery( MAVEN.ARTIFACT_ID, new UserInputSearchExpression( term ) ), Occur.SHOULD );
354         q.add( indexer.constructQuery( MAVEN.VERSION, new UserInputSearchExpression( term ) ), Occur.SHOULD );
355         q.add( indexer.constructQuery( MAVEN.PACKAGING, new UserInputSearchExpression( term ) ), Occur.SHOULD );
356         q.add( indexer.constructQuery( MAVEN.CLASSNAMES, new UserInputSearchExpression( term ) ), Occur.SHOULD );
357
358         //Query query =
359         //    new WildcardQuery( new Term( MAVEN.CLASSNAMES.getFieldName(), "*" ) );
360         //q.add( query, Occur.MUST_NOT );
361         // olamy IMHO we could set this option as at least one must match
362         //q.setMinimumNumberShouldMatch( 1 );
363     }
364
365
366     /**
367      * @param selectedRepos
368      * @return indexing contextId used
369      */
370     private List<String> addIndexingContexts( List<String> selectedRepos )
371     {
372         Set<String> indexingContextIds = new HashSet<>();
373         for ( String repo : selectedRepos )
374         {
375             try
376             {
377                 ManagedRepository repoConfig = managedRepositoryAdmin.getManagedRepository( repo );
378
379                 if ( repoConfig != null )
380                 {
381
382                     IndexingContext context = managedRepositoryAdmin.createIndexContext( repoConfig );
383                     if ( context.isSearchable() )
384                     {
385                         indexingContextIds.addAll( getRemoteIndexingContextIds( repo ) );
386                         indexingContextIds.add( context.getId() );
387                     }
388                     else
389                     {
390                         log.warn( "indexingContext with id {} not searchable", repoConfig.getId() );
391                     }
392
393                 }
394                 else
395                 {
396                     log.warn( "Repository '{}' not found in configuration.", repo );
397                 }
398             }
399             catch ( RepositoryAdminException e )
400             {
401                 log.warn( "RepositoryAdminException occured while accessing index of repository '{}' : {}", repo,
402                           e.getMessage() );
403                 continue;
404             }
405         }
406
407         return new ArrayList<>( indexingContextIds );
408     }
409
410
411     @Override
412     public Set<String> getRemoteIndexingContextIds( String managedRepoId )
413         throws RepositoryAdminException
414     {
415         Set<String> ids = new HashSet<>();
416
417         List<ProxyConnector> proxyConnectors = proxyConnectorAdmin.getProxyConnectorAsMap().get( managedRepoId );
418
419         if ( proxyConnectors == null || proxyConnectors.isEmpty() )
420         {
421             return ids;
422         }
423
424         for ( ProxyConnector proxyConnector : proxyConnectors )
425         {
426             String remoteId = "remote-" + proxyConnector.getTargetRepoId();
427             IndexingContext context = indexer.getIndexingContexts().get( remoteId );
428             if ( context != null && context.isSearchable() )
429             {
430                 ids.add( remoteId );
431             }
432         }
433
434         return ids;
435     }
436
437     @Override
438     public Collection<String> getAllGroupIds( String principal, List<String> selectedRepos )
439         throws RepositorySearchException
440     {
441         List<IndexingContext> indexContexts = getIndexingContexts( selectedRepos );
442
443         if ( indexContexts == null || indexContexts.isEmpty() )
444         {
445             return Collections.emptyList();
446         }
447
448         try
449         {
450             Set<String> allGroupIds = new HashSet<>();
451             for ( IndexingContext indexingContext : indexContexts )
452             {
453                 allGroupIds.addAll( indexingContext.getAllGroups() );
454             }
455             return allGroupIds;
456         }
457         catch ( IOException e )
458         {
459             throw new RepositorySearchException( e.getMessage(), e );
460         }
461
462     }
463
464     private SearchResults convertToSearchResults( FlatSearchResponse response, SearchResultLimits limits,
465                                                   List<? extends ArtifactInfoFilter> artifactInfoFilters,
466                                                   List<String> selectedRepos, boolean includePoms )
467         throws RepositoryAdminException
468     {
469         SearchResults results = new SearchResults();
470         Set<ArtifactInfo> artifactInfos = response.getResults();
471
472         for ( ArtifactInfo artifactInfo : artifactInfos )
473         {
474             if ( StringUtils.equalsIgnoreCase( "pom", artifactInfo.getFileExtension() ) && !includePoms )
475             {
476                 continue;
477             }
478             String id = SearchUtil.getHitId( artifactInfo.getGroupId(), //
479                                              artifactInfo.getArtifactId(), //
480                                              artifactInfo.getClassifier(), //
481                                              artifactInfo.getPackaging() );
482             Map<String, SearchResultHit> hitsMap = results.getHitsMap();
483
484             if ( !applyArtifactInfoFilters( artifactInfo, artifactInfoFilters, hitsMap ) )
485             {
486                 continue;
487             }
488
489             SearchResultHit hit = hitsMap.get( id );
490             if ( hit != null )
491             {
492                 if ( !hit.getVersions().contains( artifactInfo.getVersion() ) )
493                 {
494                     hit.addVersion( artifactInfo.getVersion() );
495                 }
496             }
497             else
498             {
499                 hit = new SearchResultHit();
500                 hit.setArtifactId( artifactInfo.getArtifactId() );
501                 hit.setGroupId( artifactInfo.getGroupId() );
502                 hit.setRepositoryId( artifactInfo.getRepository() );
503                 hit.addVersion( artifactInfo.getVersion() );
504                 hit.setBundleExportPackage( artifactInfo.getBundleExportPackage() );
505                 hit.setBundleExportService( artifactInfo.getBundleExportService() );
506                 hit.setBundleSymbolicName( artifactInfo.getBundleSymbolicName() );
507                 hit.setBundleVersion( artifactInfo.getBundleVersion() );
508                 hit.setBundleDescription( artifactInfo.getBundleDescription() );
509                 hit.setBundleDocUrl( artifactInfo.getBundleDocUrl() );
510                 hit.setBundleRequireBundle( artifactInfo.getBundleRequireBundle() );
511                 hit.setBundleImportPackage( artifactInfo.getBundleImportPackage() );
512                 hit.setBundleLicense( artifactInfo.getBundleLicense() );
513                 hit.setBundleName( artifactInfo.getBundleName() );
514                 hit.setContext( artifactInfo.getContext() );
515                 hit.setGoals( artifactInfo.getGoals() );
516                 hit.setPrefix( artifactInfo.getPrefix() );
517                 hit.setPackaging( artifactInfo.getPackaging() );
518                 hit.setClassifier( artifactInfo.getClassifier() );
519                 hit.setFileExtension( artifactInfo.getFileExtension() );
520                 hit.setUrl( getBaseUrl( artifactInfo, selectedRepos ) );
521             }
522
523             results.addHit( id, hit );
524         }
525
526         results.setTotalHits( response.getTotalHitsCount() );
527         results.setTotalHitsMapSize( results.getHitsMap().values().size() );
528         results.setReturnedHitsCount( response.getReturnedHitsCount() );
529         results.setLimits( limits );
530
531         if ( limits == null || limits.getSelectedPage() == SearchResultLimits.ALL_PAGES )
532         {
533             return results;
534         }
535         else
536         {
537             return paginate( results );
538         }
539     }
540
541     /**
542      * calculate baseUrl without the context and base Archiva Url
543      *
544      * @param artifactInfo
545      * @return
546      */
547     protected String getBaseUrl( ArtifactInfo artifactInfo, List<String> selectedRepos )
548         throws RepositoryAdminException
549     {
550         StringBuilder sb = new StringBuilder();
551         if ( StringUtils.startsWith( artifactInfo.getContext(), "remote-" ) )
552         {
553             // it's a remote index result we search a managed which proxying this remote and on which
554             // current user has read karma
555             String managedRepoId =
556                 getManagedRepoId( StringUtils.substringAfter( artifactInfo.getContext(), "remote-" ), selectedRepos );
557             if ( managedRepoId != null )
558             {
559                 sb.append( '/' ).append( managedRepoId );
560                 artifactInfo.setContext( managedRepoId );
561             }
562         }
563         else
564         {
565             sb.append( '/' ).append( artifactInfo.getContext() );
566         }
567
568         sb.append( '/' ).append( StringUtils.replaceChars( artifactInfo.getGroupId(), '.', '/' ) );
569         sb.append( '/' ).append( artifactInfo.getArtifactId() );
570         sb.append( '/' ).append( artifactInfo.getVersion() );
571         sb.append( '/' ).append( artifactInfo.getArtifactId() );
572         sb.append( '-' ).append( artifactInfo.getVersion() );
573         if ( StringUtils.isNotBlank( artifactInfo.getClassifier() ) )
574         {
575             sb.append( '-' ).append( artifactInfo.getClassifier() );
576         }
577         // maven-plugin packaging is a jar
578         if ( StringUtils.equals( "maven-plugin", artifactInfo.getPackaging() ) )
579         {
580             sb.append( "jar" );
581         }
582         else
583         {
584             sb.append( '.' ).append( artifactInfo.getPackaging() );
585         }
586
587         return sb.toString();
588     }
589
590     /**
591      * return a managed repo for a remote result
592      *
593      * @param remoteRepo
594      * @param selectedRepos
595      * @return
596      * @throws RepositoryAdminException
597      */
598     private String getManagedRepoId( String remoteRepo, List<String> selectedRepos )
599         throws RepositoryAdminException
600     {
601         Map<String, List<ProxyConnector>> proxyConnectorMap = proxyConnectorAdmin.getProxyConnectorAsMap();
602         if ( proxyConnectorMap == null || proxyConnectorMap.isEmpty() )
603         {
604             return null;
605         }
606         if ( selectedRepos != null && !selectedRepos.isEmpty() )
607         {
608             for ( Map.Entry<String, List<ProxyConnector>> entry : proxyConnectorMap.entrySet() )
609             {
610                 if ( selectedRepos.contains( entry.getKey() ) )
611                 {
612                     for ( ProxyConnector proxyConnector : entry.getValue() )
613                     {
614                         if ( StringUtils.equals( remoteRepo, proxyConnector.getTargetRepoId() ) )
615                         {
616                             return proxyConnector.getSourceRepoId();
617                         }
618                     }
619                 }
620             }
621         }
622
623         // we don't find in search selected repos so return the first one
624         for ( Map.Entry<String, List<ProxyConnector>> entry : proxyConnectorMap.entrySet() )
625         {
626
627             for ( ProxyConnector proxyConnector : entry.getValue() )
628             {
629                 if ( StringUtils.equals( remoteRepo, proxyConnector.getTargetRepoId() ) )
630                 {
631                     return proxyConnector.getSourceRepoId();
632                 }
633             }
634
635         }
636         return null;
637     }
638
639     private boolean applyArtifactInfoFilters( ArtifactInfo artifactInfo,
640                                               List<? extends ArtifactInfoFilter> artifactInfoFilters,
641                                               Map<String, SearchResultHit> currentResult )
642     {
643         if ( artifactInfoFilters == null || artifactInfoFilters.isEmpty() )
644         {
645             return true;
646         }
647
648         for ( ArtifactInfoFilter filter : artifactInfoFilters )
649         {
650             if ( !filter.addArtifactInResult( artifactInfo, currentResult ) )
651             {
652                 return false;
653             }
654         }
655         return true;
656     }
657
658     protected SearchResults paginate( SearchResults results )
659     {
660         SearchResultLimits limits = results.getLimits();
661         SearchResults paginated = new SearchResults();
662
663         // ( limits.getPageSize() * ( Math.max( 1, limits.getSelectedPage() ) ) );
664
665         int fetchCount = limits.getPageSize();
666         int offset = ( limits.getSelectedPage() * limits.getPageSize() );
667
668         if ( fetchCount > results.getTotalHits() )
669         {
670             fetchCount = results.getTotalHits();
671         }
672
673         // Goto offset.
674         if ( offset < results.getTotalHits() )
675         {
676             // only process if the offset is within the hit count.
677             for ( int i = 0; i < fetchCount; i++ )
678             {
679                 // Stop fetching if we are past the total # of available hits.
680                 if ( offset + i >= results.getHits().size() )
681                 {
682                     break;
683                 }
684
685                 SearchResultHit hit = results.getHits().get( ( offset + i ) );
686                 if ( hit != null )
687                 {
688                     String id = SearchUtil.getHitId( hit.getGroupId(), hit.getArtifactId(), hit.getClassifier(),
689                                                      hit.getPackaging() );
690                     paginated.addHit( id, hit );
691                 }
692                 else
693                 {
694                     break;
695                 }
696             }
697         }
698         paginated.setTotalHits( results.getTotalHits() );
699         paginated.setReturnedHitsCount( paginated.getHits().size() );
700         paginated.setTotalHitsMapSize( results.getTotalHitsMapSize() );
701         paginated.setLimits( limits );
702
703         return paginated;
704     }
705
706
707 }