| package org.apache.maven.index.treeview; |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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. |
| */ |
| |
| import java.io.IOException; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.apache.lucene.search.BooleanClause; |
| import org.apache.lucene.search.BooleanQuery; |
| import org.apache.lucene.search.Query; |
| import org.apache.maven.index.ArtifactInfo; |
| import org.apache.maven.index.Field; |
| import org.apache.maven.index.Indexer; |
| import org.apache.maven.index.IteratorSearchRequest; |
| import org.apache.maven.index.IteratorSearchResponse; |
| import org.apache.maven.index.MAVEN; |
| import org.apache.maven.index.expr.SourcedSearchExpression; |
| import org.apache.maven.index.treeview.TreeNode.Type; |
| import org.codehaus.plexus.component.annotations.Component; |
| import org.codehaus.plexus.component.annotations.Requirement; |
| import org.codehaus.plexus.logging.AbstractLogEnabled; |
| import org.codehaus.plexus.util.StringUtils; |
| |
| @Component( role = IndexTreeView.class ) |
| public class DefaultIndexTreeView |
| extends AbstractLogEnabled |
| implements IndexTreeView |
| { |
| @Requirement |
| private Indexer indexer; |
| |
| protected Indexer getIndexer() |
| { |
| return indexer; |
| } |
| |
| public TreeNode listNodes( TreeViewRequest request ) |
| throws IOException |
| { |
| // get the last path elem |
| String name = null; |
| |
| if ( !"/".equals( request.getPath() ) ) |
| { |
| |
| if ( request.getPath().endsWith( "/" ) ) |
| { |
| name = request.getPath().substring( 0, request.getPath().length() - 1 ); |
| } |
| else |
| { |
| name = request.getPath(); |
| } |
| |
| name = name.substring( name.lastIndexOf( '/' ) + 1, name.length() ); |
| |
| // root is "/" |
| if ( !name.equals( "/" ) && name.endsWith( "/" ) ) |
| { |
| name = name.substring( 0, name.length() - 1 ); |
| } |
| |
| } |
| else |
| { |
| name = "/"; |
| } |
| |
| // the root node depends on request we have, so let's see |
| TreeNode result = request.getFactory().createGNode( this, request, request.getPath(), name ); |
| |
| if ( request.hasFieldHints() ) |
| { |
| listChildren( result, request, null ); |
| } |
| else |
| { |
| // non hinted way, the "old" way |
| if ( "/".equals( request.getPath() ) ) |
| { |
| // get root groups and finish |
| Set<String> rootGroups = request.getIndexingContext().getRootGroups(); |
| |
| for ( String group : rootGroups ) |
| { |
| if ( group.length() > 0 ) |
| { |
| result.getChildren().add( |
| request.getFactory().createGNode( this, request, request.getPath() + group + "/", group ) ); |
| } |
| } |
| } |
| else |
| { |
| Set<String> allGroups = request.getIndexingContext().getAllGroups(); |
| |
| listChildren( result, request, allGroups ); |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * @param root |
| * @param factory |
| * @param allGroups |
| * @throws IOException |
| */ |
| protected void listChildren( TreeNode root, TreeViewRequest request, Set<String> allGroups ) |
| throws IOException |
| { |
| String path = root.getPath(); |
| |
| Map<String, TreeNode> folders = new HashMap<String, TreeNode>(); |
| |
| String rootPartialGroupId = StringUtils.strip( root.getPath().replaceAll( "/", "." ), "." ); |
| |
| folders.put( Type.G + ":" + rootPartialGroupId, root ); |
| |
| IteratorSearchResponse artifacts = getArtifacts( root, request ); |
| |
| try |
| { |
| for ( ArtifactInfo ai : artifacts ) |
| { |
| String versionKey = Type.V + ":" + ai.artifactId + ":" + ai.version; |
| |
| TreeNode versionResource = folders.get( versionKey ); |
| |
| if ( versionResource == null ) |
| { |
| String artifactKey = Type.A + ":" + ai.artifactId; |
| |
| TreeNode artifactResource = folders.get( artifactKey ); |
| |
| if ( artifactResource == null ) |
| { |
| TreeNode groupParentResource = root; |
| |
| TreeNode groupResource = root; |
| |
| // here comes the twist: we have to search for parent G node |
| String partialGroupId = null; |
| |
| String[] groupIdElems = ai.groupId.split( "\\." ); |
| |
| for ( String groupIdElem : groupIdElems ) |
| { |
| if ( partialGroupId == null ) |
| { |
| partialGroupId = groupIdElem; |
| } |
| else |
| { |
| partialGroupId = partialGroupId + "." + groupIdElem; |
| } |
| |
| String groupKey = Type.G + ":" + partialGroupId; |
| |
| groupResource = folders.get( groupKey ); |
| |
| // it needs to be created only if not found (is null) and is _below_ groupParentResource |
| if ( groupResource == null |
| && groupParentResource.getPath().length() < getPathForAi( ai, MAVEN.GROUP_ID ).length() ) |
| { |
| String gNodeName = |
| partialGroupId.lastIndexOf( '.' ) > -1 ? partialGroupId.substring( |
| partialGroupId.lastIndexOf( '.' ) + 1, partialGroupId.length() ) |
| : partialGroupId; |
| |
| groupResource = |
| request.getFactory().createGNode( this, request, |
| "/" + partialGroupId.replaceAll( "\\.", "/" ) + "/", gNodeName ); |
| |
| groupParentResource.getChildren().add( groupResource ); |
| |
| folders.put( groupKey, groupResource ); |
| |
| groupParentResource = groupResource; |
| } |
| else if ( groupResource != null ) |
| { |
| // we found it as already existing, break if this is the node we want |
| if ( groupResource.getPath().equals( getPathForAi( ai, MAVEN.GROUP_ID ) ) ) |
| { |
| break; |
| } |
| |
| groupParentResource = groupResource; |
| } |
| } |
| |
| artifactResource = |
| request.getFactory().createANode( this, request, ai, getPathForAi( ai, MAVEN.ARTIFACT_ID ) ); |
| |
| groupParentResource.getChildren().add( artifactResource ); |
| |
| folders.put( artifactKey, artifactResource ); |
| } |
| |
| versionResource = |
| request.getFactory().createVNode( this, request, ai, getPathForAi( ai, MAVEN.VERSION ) ); |
| |
| artifactResource.getChildren().add( versionResource ); |
| |
| folders.put( versionKey, versionResource ); |
| } |
| |
| String nodePath = getPathForAi( ai, null ); |
| |
| versionResource.getChildren().add( |
| request.getFactory().createArtifactNode( this, request, ai, nodePath ) ); |
| } |
| } |
| finally |
| { |
| artifacts.close(); |
| } |
| |
| if ( !request.hasFieldHints() ) |
| { |
| Set<String> groups = getGroups( path, allGroups ); |
| |
| for ( String group : groups ) |
| { |
| TreeNode groupResource = root.findChildByPath( path + group + "/", Type.G ); |
| |
| if ( groupResource == null ) |
| { |
| groupResource = request.getFactory().createGNode( this, request, path + group + "/", group ); |
| |
| root.getChildren().add( groupResource ); |
| } |
| else |
| { |
| // if the folder has been created as an artifact name, |
| // we need to check for possible nested groups as well |
| listChildren( groupResource, request, allGroups ); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Builds a path out from ArtifactInfo. The field parameter controls "how deep" the path goes. Possible values are |
| * MAVEN.GROUP_ID (builds a path from groupId only), MAVEN.ARTIFACT_ID (builds a path from groupId + artifactId), |
| * MAVEN.VERSION (builds a path up to version) or anything else (including null) will build "full" artifact path. |
| * |
| * @param ai |
| * @param field |
| * @return path |
| */ |
| protected String getPathForAi( ArtifactInfo ai, Field field ) |
| { |
| StringBuilder sb = new StringBuilder( "/" ); |
| |
| sb.append( ai.groupId.replaceAll( "\\.", "/" ) ); |
| |
| if ( MAVEN.GROUP_ID.equals( field ) ) |
| { |
| // stop here |
| return sb.append( "/" ).toString(); |
| } |
| |
| sb.append( "/" ).append( ai.artifactId ); |
| |
| if ( MAVEN.ARTIFACT_ID.equals( field ) ) |
| { |
| // stop here |
| return sb.append( "/" ).toString(); |
| } |
| |
| sb.append( "/" ).append( ai.version ); |
| |
| if ( MAVEN.VERSION.equals( field ) ) |
| { |
| // stop here |
| return sb.append( "/" ).toString(); |
| } |
| |
| sb.append( "/" ).append( ai.artifactId ).append( "-" ).append( ai.version ); |
| |
| if ( ai.classifier != null ) |
| { |
| sb.append( "-" ).append( ai.classifier ); |
| } |
| |
| sb.append( "." ).append( ai.fextension == null ? "jar" : ai.fextension ); |
| |
| return sb.toString(); |
| } |
| |
| protected Set<String> getGroups( String path, Set<String> allGroups ) |
| { |
| path = path.substring( 1 ).replace( '/', '.' ); |
| |
| int n = path.length(); |
| |
| Set<String> result = new HashSet<String>(); |
| |
| for ( String group : allGroups ) |
| { |
| if ( group.startsWith( path ) ) |
| { |
| group = group.substring( n ); |
| |
| int nextDot = group.indexOf( '.' ); |
| |
| if ( nextDot > -1 ) |
| { |
| group = group.substring( 0, nextDot ); |
| } |
| |
| if ( group.length() > 0 && !result.contains( group ) ) |
| { |
| result.add( group ); |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| protected IteratorSearchResponse getArtifacts( TreeNode root, TreeViewRequest request ) |
| throws IOException |
| { |
| if ( request.hasFieldHints() ) |
| { |
| return getHintedArtifacts( root, request ); |
| } |
| |
| String path = root.getPath(); |
| |
| IteratorSearchResponse result = null; |
| |
| String g = null; |
| |
| String a = null; |
| |
| String v = null; |
| |
| // "working copy" of path |
| String wp = null; |
| |
| // remove last / from path |
| if ( path.endsWith( "/" ) ) |
| { |
| path = path.substring( 0, path.length() - 1 ); |
| } |
| |
| // 1st try, let's consider path is a group |
| |
| // reset wp |
| wp = path; |
| |
| g = wp.substring( 1 ).replace( '/', '.' ); |
| |
| result = getArtifactsByG( g, request ); |
| |
| if ( result.getTotalHitsCount() > 0 ) |
| { |
| return result; |
| } |
| else |
| { |
| result.close(); |
| } |
| |
| // 2nd try, lets consider path a group + artifactId, we must ensure there is at least one / but not as root |
| |
| if ( path.lastIndexOf( '/' ) > 0 ) |
| { |
| // reset wp |
| wp = path; |
| |
| a = wp.substring( wp.lastIndexOf( '/' ) + 1, wp.length() ); |
| |
| g = wp.substring( 1, wp.lastIndexOf( '/' ) ).replace( '/', '.' ); |
| |
| result = getArtifactsByGA( g, a, request ); |
| |
| if ( result.getTotalHitsCount() > 0 ) |
| { |
| return result; |
| } |
| else |
| { |
| result.close(); |
| } |
| |
| // 3rd try, let's consider path a group + artifactId + version. There is no 100% way to detect this! |
| |
| try |
| { |
| // reset wp |
| wp = path; |
| |
| v = wp.substring( wp.lastIndexOf( '/' ) + 1, wp.length() ); |
| |
| wp = wp.substring( 0, wp.lastIndexOf( '/' ) ); |
| |
| a = wp.substring( wp.lastIndexOf( '/' ) + 1, wp.length() ); |
| |
| g = wp.substring( 1, wp.lastIndexOf( '/' ) ).replace( '/', '.' ); |
| |
| result = getArtifactsByGAV( g, a, v, request ); |
| |
| if ( result.getTotalHitsCount() > 0 ) |
| { |
| return result; |
| } |
| else |
| { |
| result.close(); |
| } |
| } |
| catch ( StringIndexOutOfBoundsException e ) |
| { |
| // nothing |
| } |
| } |
| |
| // if we are here, no hits found |
| return IteratorSearchResponse.empty( result.getQuery() ); |
| } |
| |
| protected IteratorSearchResponse getHintedArtifacts( TreeNode root, TreeViewRequest request ) |
| throws IOException |
| { |
| // we know that hints are there: G hint, GA hint or GAV hint |
| if ( request.hasFieldHint( MAVEN.GROUP_ID, MAVEN.ARTIFACT_ID, MAVEN.VERSION ) ) |
| { |
| return getArtifactsByGAV( request.getFieldHint( MAVEN.GROUP_ID ), |
| request.getFieldHint( MAVEN.ARTIFACT_ID ), request.getFieldHint( MAVEN.VERSION ), request ); |
| } |
| else if ( request.hasFieldHint( MAVEN.GROUP_ID, MAVEN.ARTIFACT_ID ) ) |
| { |
| return getArtifactsByGA( request.getFieldHint( MAVEN.GROUP_ID ), request.getFieldHint( MAVEN.ARTIFACT_ID ), |
| request ); |
| } |
| else if ( request.hasFieldHint( MAVEN.GROUP_ID ) ) |
| { |
| return getArtifactsByG( request.getFieldHint( MAVEN.GROUP_ID ), request ); |
| } |
| else |
| { |
| // if we are here, no hits found or something horribly went wrong? |
| return IteratorSearchResponse.empty( null ); |
| } |
| } |
| |
| protected IteratorSearchResponse getArtifactsByG( String g, TreeViewRequest request ) |
| throws IOException |
| { |
| return getArtifactsByGAVField( g, null, null, request ); |
| } |
| |
| protected IteratorSearchResponse getArtifactsByGA( String g, String a, TreeViewRequest request ) |
| throws IOException |
| { |
| return getArtifactsByGAVField( g, a, null, request ); |
| } |
| |
| protected IteratorSearchResponse getArtifactsByGAV( String g, String a, String v, TreeViewRequest request ) |
| throws IOException |
| { |
| return getArtifactsByGAVField( g, a, v, request ); |
| } |
| |
| protected IteratorSearchResponse getArtifactsByGAVField( String g, String a, String v, TreeViewRequest request ) |
| throws IOException |
| { |
| assert g != null; |
| |
| Query groupIdQ = null; |
| Query artifactIdQ = null; |
| Query versionQ = null; |
| |
| // minimum must have |
| groupIdQ = getIndexer().constructQuery( MAVEN.GROUP_ID, new SourcedSearchExpression( g ) ); |
| |
| if ( StringUtils.isNotBlank( a ) ) |
| { |
| artifactIdQ = getIndexer().constructQuery( MAVEN.ARTIFACT_ID, new SourcedSearchExpression( a ) ); |
| } |
| |
| if ( StringUtils.isNotBlank( v ) ) |
| { |
| versionQ = getIndexer().constructQuery( MAVEN.VERSION, new SourcedSearchExpression( v ) ); |
| } |
| |
| BooleanQuery q = new BooleanQuery(); |
| |
| q.add( new BooleanClause( groupIdQ, BooleanClause.Occur.MUST ) ); |
| |
| if ( artifactIdQ != null ) |
| { |
| q.add( new BooleanClause( artifactIdQ, BooleanClause.Occur.MUST ) ); |
| } |
| |
| if ( versionQ != null ) |
| { |
| q.add( new BooleanClause( versionQ, BooleanClause.Occur.MUST ) ); |
| } |
| |
| IteratorSearchRequest searchRequest = new IteratorSearchRequest( q, request.getArtifactInfoFilter() ); |
| |
| searchRequest.getContexts().add( request.getIndexingContext() ); |
| |
| IteratorSearchResponse result = getIndexer().searchIterator( searchRequest ); |
| |
| return result; |
| } |
| } |