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