@@ -1,3 +1,5 @@ | |||
package org.apache.archiva.repository.storage.util; | |||
/* | |||
* Licensed to the Apache Software Foundation (ASF) under one | |||
* or more contributor license agreements. See the NOTICE file | |||
@@ -16,7 +18,7 @@ | |||
* under the License. | |||
*/ | |||
package org.apache.archiva.repository.storage; | |||
import org.apache.archiva.repository.storage.StorageAsset; | |||
import java.io.Closeable; | |||
import java.util.Collections; | |||
@@ -29,8 +31,6 @@ import java.util.Spliterator; | |||
import java.util.function.Consumer; | |||
import java.util.stream.Collectors; | |||
import java.util.stream.IntStream; | |||
import java.util.stream.Stream; | |||
import java.util.stream.StreamSupport; | |||
/** | |||
* | |||
@@ -39,7 +39,13 @@ import java.util.stream.StreamSupport; | |||
* parents. If the spliterator is used in a parallel stream, there is no guarantee for | |||
* the order of returned assets. | |||
* | |||
* The estimated size is not accurate, because the tree paths are scanned on demand. | |||
* The estimated size is not accurate, because the tree paths are scanned on demand (lazy loaded) | |||
* | |||
* The spliterator returns the status of the assets at the time of retrieval. If modifications occur | |||
* during traversal the returned assets may not represent the latest state. | |||
* There is no check for modifications during traversal and no <code>{@link java.util.ConcurrentModificationException}</code> are thrown. | |||
* | |||
* | |||
* | |||
* @since 3.0 | |||
* @author Martin Stockhammer <martin_s@apache.org> | |||
@@ -53,35 +59,45 @@ public class AssetSpliterator implements Spliterator<StorageAsset>, Closeable | |||
private LinkedHashSet<StorageAsset> visitedContainers = new LinkedHashSet<>( ); | |||
private long visited = 0; | |||
private final int splitThreshold; | |||
private static final int CHARACTERISTICS = Spliterator.DISTINCT|Spliterator.NONNULL; | |||
private static final int CHARACTERISTICS = Spliterator.DISTINCT|Spliterator.NONNULL|Spliterator.CONCURRENT; | |||
AssetSpliterator( int splitThreshold, StorageAsset... assets) { | |||
public AssetSpliterator( int splitThreshold, StorageAsset... assets) { | |||
this.splitThreshold = splitThreshold; | |||
init( assets ); | |||
} | |||
private void init( StorageAsset[] assets ) | |||
{ | |||
if (assets.length==0 || assets[0] == null) { | |||
throw new IllegalArgumentException( "There must be at least one non-null asset" ); | |||
} | |||
Collections.addAll( this.workList, assets ); | |||
retrieveNextPath( this.workList.get( 0 ) ); | |||
} | |||
AssetSpliterator( StorageAsset... assets) { | |||
public AssetSpliterator( StorageAsset... assets) { | |||
this.splitThreshold = DEFAULT_SPLIT_THRESHOLD; | |||
Collections.addAll( this.workList, assets ); | |||
init( assets ); | |||
} | |||
AssetSpliterator() { | |||
protected AssetSpliterator() { | |||
this.splitThreshold = DEFAULT_SPLIT_THRESHOLD; | |||
} | |||
AssetSpliterator( int splitThreshold) { | |||
protected AssetSpliterator( int splitThreshold) { | |||
this.splitThreshold = splitThreshold; | |||
} | |||
AssetSpliterator( int splitThreshold, Set<StorageAsset> visitedContainers) { | |||
protected AssetSpliterator( int splitThreshold, Set<StorageAsset> visitedContainers) { | |||
this.visitedContainers.addAll( visitedContainers ); | |||
this.splitThreshold = splitThreshold; | |||
} | |||
AssetSpliterator( List<StorageAsset> baseList, Set<StorageAsset> visitedContainers) { | |||
protected AssetSpliterator( List<StorageAsset> baseList, Set<StorageAsset> visitedContainers) { | |||
this.workList.addAll(baseList); | |||
retrieveNextPath( this.workList.get( 0 ) ); | |||
this.visitedContainers.addAll( visitedContainers ); | |||
this.splitThreshold = DEFAULT_SPLIT_THRESHOLD; | |||
} | |||
@@ -150,7 +166,7 @@ public class AssetSpliterator implements Spliterator<StorageAsset>, Closeable | |||
} | |||
} | |||
// In reverse order | |||
// Assets are returned in reverse order | |||
List<StorageAsset> getChildContainers( StorageAsset parent) { | |||
final List<StorageAsset> children = parent.list( ); | |||
final int len = children.size( ); | |||
@@ -158,7 +174,7 @@ public class AssetSpliterator implements Spliterator<StorageAsset>, Closeable | |||
children.get(len - i - 1)).filter( StorageAsset::isContainer ).collect( Collectors.toList( ) ); | |||
} | |||
// In reverse order | |||
// Assets are returned in reverse order | |||
List<StorageAsset> getChildFiles(StorageAsset parent) { | |||
final List<StorageAsset> children = parent.list( ); | |||
final int len = children.size( ); | |||
@@ -187,8 +203,8 @@ public class AssetSpliterator implements Spliterator<StorageAsset>, Closeable | |||
//noinspection InfiniteLoopStatement | |||
while (true) | |||
{ | |||
newWorkList.add( workList.getFirst( ) ); | |||
newSpliterator.add( workList.getFirst( ) ); | |||
newWorkList.add( workList.removeFirst( ) ); | |||
newSpliterator.add( workList.removeFirst( ) ); | |||
} | |||
} catch (NoSuchElementException e) { | |||
// |
@@ -0,0 +1,93 @@ | |||
package org.apache.archiva.repository.storage.util; | |||
/* | |||
* 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 org.apache.archiva.repository.storage.StorageAsset; | |||
import java.util.function.Consumer; | |||
import java.util.function.Function; | |||
import java.util.stream.Stream; | |||
import java.util.stream.StreamSupport; | |||
/** | |||
* | |||
* Utility class for traversing the asset tree recursively and stream based access to the assets. | |||
* | |||
* @since 3.0 | |||
* @author Martin Stockhammer <martin_s@apache.org> | |||
*/ | |||
public class StorageUtil | |||
{ | |||
/** | |||
* Walk the tree starting at the given asset. The consumer is called for each asset found. | |||
* It runs a depth-first search where children are consumed before their parents. | |||
* | |||
* @param start the starting asset | |||
* @param consumer the consumer that is applied to each asset | |||
*/ | |||
public static void walk( StorageAsset start, Consumer<StorageAsset> consumer ) { | |||
try(Stream<StorageAsset> assetStream = newAssetStream( start, false )) { | |||
assetStream.forEach( consumer::accept ); | |||
} | |||
} | |||
/** | |||
* Walk the tree starting at the given asset. The consumer function is called for each asset found | |||
* as long as it returns <code>true</code> as result. If the function returns <code>false</code> the | |||
* processing stops. | |||
* It runs a depth-first search where children are consumed before their parents. | |||
* | |||
* @param start the starting asset | |||
* @param consumer the consumer function that is applied to each asset and that has to return <code>true</code>, | |||
* if the walk should continue. | |||
*/ | |||
public static void walk( StorageAsset start, Function<StorageAsset, Boolean> consumer ) { | |||
try(Stream<StorageAsset> assetStream = newAssetStream( start, false )) { | |||
assetStream.anyMatch( a -> !consumer.apply( a ) ); | |||
} | |||
} | |||
/** | |||
* Returns a stream of assets starting at the given start node. The returned stream returns a closable | |||
* stream and should always be used in a try-with-resources statement. | |||
* | |||
* @param start the starting asset | |||
* @param parallel <code>true</code>, if a parallel stream should be created, otherwise <code>false</code> | |||
* @return the newly created stream | |||
*/ | |||
public static Stream<StorageAsset> newAssetStream( StorageAsset start, boolean parallel ) | |||
{ | |||
return StreamSupport.stream( new AssetSpliterator( start ), parallel ); | |||
} | |||
/** | |||
* Returns a non-parallel stream. | |||
* Calls {@link #newAssetStream(StorageAsset, boolean)} with <code>parallel=false</code>. | |||
* | |||
* @param start the starting asset | |||
* @return the returned stream object | |||
*/ | |||
public static Stream<StorageAsset> newAssetStream( StorageAsset start) { | |||
return newAssetStream( start, false ); | |||
} | |||
} |
@@ -1,100 +0,0 @@ | |||
package org.apache.archiva.repository.storage; | |||
/* | |||
* 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 org.apache.archiva.repository.storage.mock.MockAsset; | |||
import org.junit.jupiter.api.Test; | |||
import java.util.ArrayList; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import static org.junit.jupiter.api.Assertions.*; | |||
/** | |||
* Test the AssetSpliterator class | |||
* | |||
* @author Martin Stockhammer <martin_s@apache.org> | |||
*/ | |||
class AssetSpliteratorTest | |||
{ | |||
private StorageAsset createTree() { | |||
MockAsset root = new MockAsset( "" ); | |||
for (int i=0; i<10; i++) { | |||
String name1 = "a" + String.format("%03d",i); | |||
MockAsset parent1 = new MockAsset( root, name1 ); | |||
for (int k=0; k<15; k++) { | |||
String name2 = name1 + String.format("%03d", k); | |||
MockAsset parent2 = new MockAsset( parent1, name2 ); | |||
for (int u=0; u<5; u++) { | |||
String name3 = name2 + String.format("%03d", u); | |||
MockAsset parent3 = new MockAsset( parent2, name3 ); | |||
} | |||
} | |||
} | |||
return root; | |||
} | |||
private class Status { | |||
LinkedList<StorageAsset> visited = new LinkedList<>( ); | |||
Status() { | |||
} | |||
public void add(StorageAsset asset) { | |||
visited.addLast( asset ); | |||
} | |||
public StorageAsset getLast() { | |||
return visited.getLast( ); | |||
} | |||
public List<StorageAsset> getVisited() { | |||
return visited; | |||
} | |||
public int size() { | |||
return visited.size( ); | |||
} | |||
} | |||
@Test | |||
void tryAdvance( ) | |||
{ | |||
StorageAsset root = createTree( ); | |||
AssetSpliterator spliterator = new AssetSpliterator( root ); | |||
final StorageAsset expectedTarget = root.list( ).get( 0 ).list( ).get( 0 ).list( ).get( 0 ); | |||
final Status status = new Status( ); | |||
spliterator.tryAdvance( a -> status.add( a ) ); | |||
assertEquals( expectedTarget, status.getLast( ) ); | |||
} | |||
@Test | |||
void forEachRemaining( ) | |||
{ | |||
} | |||
@Test | |||
void trySplit( ) | |||
{ | |||
} | |||
} |
@@ -45,12 +45,12 @@ public class MockAsset implements StorageAsset | |||
public MockAsset( String name ) { | |||
this.name = name; | |||
this.path = ""; | |||
this.path = "/"; | |||
} | |||
public MockAsset( MockAsset parent, String name ) { | |||
this.parent = parent; | |||
this.path = parent.getPath( ) + "/" + name; | |||
this.path = (parent.hasParent()?parent.getPath( ):"") + "/" + name; | |||
this.name = name; | |||
parent.registerChild( this ); | |||
} | |||
@@ -189,4 +189,21 @@ public class MockAsset implements StorageAsset | |||
{ | |||
return getPath(); | |||
} | |||
@Override | |||
public boolean equals( Object o ) | |||
{ | |||
if ( this == o ) return true; | |||
if ( o == null || getClass( ) != o.getClass( ) ) return false; | |||
MockAsset mockAsset = (MockAsset) o; | |||
return path.equals( mockAsset.path ); | |||
} | |||
@Override | |||
public int hashCode( ) | |||
{ | |||
return path.hashCode( ); | |||
} | |||
} |
@@ -0,0 +1,161 @@ | |||
/* | |||
* 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. | |||
*/ | |||
package org.apache.archiva.repository.storage.util; | |||
import org.apache.archiva.repository.storage.StorageAsset; | |||
import org.apache.archiva.repository.storage.mock.MockAsset; | |||
import org.junit.jupiter.api.Test; | |||
import java.util.Spliterator; | |||
import static org.junit.jupiter.api.Assertions.assertEquals; | |||
import static org.junit.jupiter.api.Assertions.assertNotNull; | |||
/** | |||
* Test the AssetSpliterator class | |||
* | |||
* @author Martin Stockhammer <martin_s@apache.org> | |||
*/ | |||
class AssetSpliteratorTest | |||
{ | |||
private static int LEVEL1 = 10; | |||
private static int LEVEL2 = 15; | |||
private static int LEVEL3 = 5; | |||
private StorageAsset createTree() { | |||
return createTree( LEVEL1, LEVEL2, LEVEL3 ); | |||
} | |||
private StorageAsset createTree(int... levelElements) { | |||
MockAsset root = new MockAsset( "" ); | |||
recurseSubTree( root, 0, levelElements ); | |||
return root; | |||
} | |||
private void recurseSubTree(MockAsset parent, int level, int[] levelElements) { | |||
if (level < levelElements.length) | |||
{ | |||
for ( int k = 0; k < levelElements[level]; k++ ) | |||
{ | |||
String name = parent.getName( ) + String.format( "%03d", k ); | |||
MockAsset asset = new MockAsset( parent, name ); | |||
recurseSubTree( asset, level + 1, levelElements ); | |||
} | |||
} | |||
} | |||
@Test | |||
void tryAdvance( ) | |||
{ | |||
StorageAsset root = createTree( ); | |||
AssetSpliterator spliterator = new AssetSpliterator( root ); | |||
final ConsumeVisitStatus status = new ConsumeVisitStatus( ); | |||
StorageAsset expectedTarget = root.list( ).get( 0 ).list( ).get( 0 ).list( ).get( 0 ); | |||
spliterator.tryAdvance( status ); | |||
assertEquals( 1, status.size( ) ); | |||
assertEquals( expectedTarget, status.getLast( ) ); | |||
spliterator.tryAdvance( status ); | |||
assertEquals( 2, status.size( ) ); | |||
expectedTarget = root.list( ).get( 0 ).list( ).get( 0 ).list( ).get( 1 ); | |||
assertEquals( expectedTarget, status.getLast( ) ); | |||
} | |||
@Test | |||
void forEachRemaining( ) | |||
{ | |||
StorageAsset root = createTree( ); | |||
AssetSpliterator spliterator = new AssetSpliterator( root ); | |||
final ConsumeVisitStatus status = new ConsumeVisitStatus( ); | |||
spliterator.forEachRemaining( status ); | |||
// 10 * 15 * 5 + 10 * 15 + 10 + 1 | |||
assertEquals( LEVEL1*LEVEL2*LEVEL3+LEVEL1*LEVEL2+LEVEL1+1 | |||
, status.size( ) ); | |||
assertEquals( root, status.getLast( ) ); | |||
} | |||
@Test | |||
void forEachRemaining2( ) | |||
{ | |||
StorageAsset root = createTree( ); | |||
AssetSpliterator spliterator = new AssetSpliterator( root ); | |||
final ConsumeVisitStatus status = new ConsumeVisitStatus( ); | |||
spliterator.tryAdvance( a -> {} ); | |||
spliterator.tryAdvance( a -> {} ); | |||
spliterator.tryAdvance( a -> {} ); | |||
spliterator.tryAdvance( a -> {} ); | |||
spliterator.forEachRemaining( status ); | |||
int expected = LEVEL1 * LEVEL2 * LEVEL3 + LEVEL1 * LEVEL2 + LEVEL1 + 1; | |||
expected = expected - 4; | |||
assertEquals( expected | |||
, status.size( ) ); | |||
assertEquals( root, status.getLast( ) ); | |||
} | |||
@Test | |||
void forEachRemaining3( ) | |||
{ | |||
StorageAsset root = createTree( ); | |||
StorageAsset testRoot = root.list( ).get( 1 ); | |||
AssetSpliterator spliterator = new AssetSpliterator( testRoot ); | |||
final ConsumeVisitStatus status = new ConsumeVisitStatus( ); | |||
spliterator.forEachRemaining( status ); | |||
int expected = LEVEL2 * LEVEL3 + LEVEL2 + 1; | |||
assertEquals( expected | |||
, status.size( ) ); | |||
assertEquals( testRoot, status.getLast( ) ); | |||
} | |||
@Test | |||
void trySplit( ) | |||
{ | |||
StorageAsset root = createTree( ); | |||
AssetSpliterator spliterator = new AssetSpliterator( root ); | |||
final ConsumeVisitStatus status1 = new ConsumeVisitStatus( ); | |||
final ConsumeVisitStatus status2 = new ConsumeVisitStatus( ); | |||
Spliterator<StorageAsset> newSpliterator = spliterator.trySplit( ); | |||
assertNotNull( newSpliterator ); | |||
newSpliterator.forEachRemaining( status1 ); | |||
spliterator.forEachRemaining( status2 ); | |||
int sum = LEVEL1 * LEVEL2 * LEVEL3 + LEVEL1 * LEVEL2 + LEVEL1 + 1; | |||
int expected1 = sum / 2; | |||
int expected2 = sum / 2 + 1 ; | |||
assertEquals( expected1, status1.size( ) ); | |||
assertEquals( expected2, status2.size( ) ); | |||
} | |||
@Test | |||
void checkCharacteristics() { | |||
StorageAsset root = createTree( ); | |||
AssetSpliterator spliterator = new AssetSpliterator( root ); | |||
assertEquals( Spliterator.NONNULL, spliterator.characteristics( ) & Spliterator.NONNULL ); | |||
assertEquals( Spliterator.CONCURRENT, spliterator.characteristics( ) & Spliterator.CONCURRENT ); | |||
assertEquals( Spliterator.DISTINCT, spliterator.characteristics( ) & Spliterator.DISTINCT ); | |||
} | |||
} |
@@ -0,0 +1,35 @@ | |||
package org.apache.archiva.repository.storage.util; | |||
/* | |||
* 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 org.apache.archiva.repository.storage.StorageAsset; | |||
import java.util.function.Consumer; | |||
/** | |||
* @author Martin Stockhammer <martin_s@apache.org> | |||
*/ | |||
public class ConsumeVisitStatus extends VisitStatus implements Consumer<StorageAsset> | |||
{ | |||
@Override | |||
public void accept( StorageAsset asset ) | |||
{ | |||
add( asset ); | |||
} | |||
} |
@@ -0,0 +1,45 @@ | |||
package org.apache.archiva.repository.storage.util; | |||
/* | |||
* 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 org.apache.archiva.repository.storage.StorageAsset; | |||
import java.util.function.Function; | |||
import java.util.function.Predicate; | |||
/** | |||
* @author Martin Stockhammer <martin_s@apache.org> | |||
*/ | |||
public class StopVisitStatus extends VisitStatus implements Function<StorageAsset, Boolean> | |||
{ | |||
private Predicate<StorageAsset> stopCondition; | |||
public void setStopCondition( Predicate<StorageAsset> predicate ) | |||
{ | |||
this.stopCondition = predicate; | |||
} | |||
@Override | |||
public Boolean apply( StorageAsset asset ) | |||
{ | |||
add( asset ); | |||
return !stopCondition.test( asset ); | |||
} | |||
} |
@@ -0,0 +1,134 @@ | |||
package org.apache.archiva.repository.storage.util; | |||
/* | |||
* 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 org.apache.archiva.repository.storage.StorageAsset; | |||
import org.apache.archiva.repository.storage.mock.MockAsset; | |||
import org.junit.jupiter.api.Test; | |||
import java.util.List; | |||
import java.util.stream.Collectors; | |||
import java.util.stream.Stream; | |||
import static org.junit.jupiter.api.Assertions.*; | |||
/** | |||
* @author Martin Stockhammer <martin_s@apache.org> | |||
*/ | |||
class StorageUtilTest | |||
{ | |||
private static int LEVEL1 = 12; | |||
private static int LEVEL2 = 13; | |||
private static int LEVEL3 = 6; | |||
private StorageAsset createTree() { | |||
return createTree( LEVEL1, LEVEL2, LEVEL3 ); | |||
} | |||
private StorageAsset createTree(int... levelElements) { | |||
MockAsset root = new MockAsset( "" ); | |||
recurseSubTree( root, 0, levelElements ); | |||
return root; | |||
} | |||
private void recurseSubTree(MockAsset parent, int level, int[] levelElements) { | |||
if (level < levelElements.length) | |||
{ | |||
for ( int k = 0; k < levelElements[level]; k++ ) | |||
{ | |||
String name = parent.getName( ) + String.format( "%03d", k ); | |||
MockAsset asset = new MockAsset( parent, name ); | |||
recurseSubTree( asset, level + 1, levelElements ); | |||
} | |||
} | |||
} | |||
@Test | |||
void testWalkFromRoot() { | |||
StorageAsset root = createTree( ); | |||
ConsumeVisitStatus status = new ConsumeVisitStatus( ); | |||
StorageUtil.walk( root, status ); | |||
int expected = LEVEL1 * LEVEL2 * LEVEL3 + LEVEL1 * LEVEL2 + LEVEL1 + 1; | |||
assertEquals( expected, status.size() ); | |||
StorageAsset first = root.list( ).get( 0 ).list( ).get( 0 ).list().get(0); | |||
assertEquals( first, status.getFirst( ) ); | |||
assertEquals( root, status.getLast( ) ); | |||
} | |||
@Test | |||
void testWalkFromChild() { | |||
StorageAsset root = createTree( ); | |||
ConsumeVisitStatus status = new ConsumeVisitStatus( ); | |||
StorageAsset testRoot = root.list( ).get( 3 ); | |||
StorageUtil.walk( testRoot, status ); | |||
int expected = LEVEL2 * LEVEL3 + LEVEL2 + 1; | |||
assertEquals( expected, status.size() ); | |||
StorageAsset first = root.list( ).get( 3 ).list( ).get( 0 ).list().get(0); | |||
assertEquals( first, status.getFirst( ) ); | |||
assertEquals( testRoot, status.getLast( ) ); | |||
} | |||
@Test | |||
void testWalkFromRootWithCondition() { | |||
StorageAsset root = createTree( ); | |||
StopVisitStatus status = new StopVisitStatus( ); | |||
status.setStopCondition( a -> a.getName().equals("001002003") ); | |||
StorageUtil.walk( root, status ); | |||
assertEquals( "001002003", status.getLast( ).getName() ); | |||
int expected = LEVEL2 * LEVEL3 + LEVEL2 + 2 * LEVEL3 + 1 + 1 + 1 + 4; | |||
assertEquals( expected, status.size() ); | |||
} | |||
@Test | |||
void testStream() { | |||
StorageAsset root = createTree( ); | |||
ConsumeVisitStatus status = new ConsumeVisitStatus( ); | |||
List<StorageAsset> result; | |||
try ( Stream<StorageAsset> stream = StorageUtil.newAssetStream( root, false ) ) | |||
{ | |||
result = stream.filter( a -> a.getName( ).startsWith( "001" ) ).collect( Collectors.toList()); | |||
} | |||
int expected = LEVEL2 * LEVEL3 + LEVEL2 + 1; | |||
assertEquals( expected, result.size( ) ); | |||
assertEquals( "001", result.get( result.size( ) - 1 ).getName() ); | |||
assertEquals( "001012", result.get( result.size( ) - 2 ).getName() ); | |||
} | |||
@Test | |||
void testStreamParallel() { | |||
StorageAsset root = createTree( ); | |||
ConsumeVisitStatus status = new ConsumeVisitStatus( ); | |||
List<StorageAsset> result; | |||
try ( Stream<StorageAsset> stream = StorageUtil.newAssetStream( root, true ) ) | |||
{ | |||
result = stream.filter( a -> a.getName( ).startsWith( "001" ) ).collect( Collectors.toList()); | |||
} | |||
int expected = LEVEL2 * LEVEL3 + LEVEL2 + 1; | |||
assertEquals( expected, result.size( ) ); | |||
} | |||
} |
@@ -0,0 +1,67 @@ | |||
package org.apache.archiva.repository.storage.util; | |||
/* | |||
* 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 org.apache.archiva.repository.storage.StorageAsset; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import java.util.function.Consumer; | |||
import java.util.function.Function; | |||
import java.util.function.Predicate; | |||
/** | |||
* @author Martin Stockhammer <martin_s@apache.org> | |||
*/ | |||
class VisitStatus | |||
{ | |||
LinkedList<StorageAsset> visited = new LinkedList<>( ); | |||
VisitStatus( ) | |||
{ | |||
} | |||
public void add( StorageAsset asset ) | |||
{ | |||
// System.out.println( "Adding " + asset.getPath( ) ); | |||
visited.addLast( asset ); | |||
} | |||
public StorageAsset getLast( ) | |||
{ | |||
return visited.getLast( ); | |||
} | |||
public StorageAsset getFirst() { | |||
return visited.getFirst( ); | |||
} | |||
public List<StorageAsset> getVisited( ) | |||
{ | |||
return visited; | |||
} | |||
public int size( ) | |||
{ | |||
return visited.size( ); | |||
} | |||
} |