blob: 87552c91af98955c8dbdb2cdbdc0c2ea6e3daee2 [file] [log] [blame]
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;
}
}