git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@958669 13f79535-47bb-0310-9956-ffa450edef68master
@@ -81,6 +81,11 @@ Fixed bugs: | |||
case-insensitive file system. | |||
Bugzilla Report 49041. | |||
* The <restrict> resource collection was checking every resource even if | |||
we actually just want the first one, like in the exemple of use of | |||
resourcelist in the documentation (getting the first available resource | |||
from a mirror list). | |||
Other changes: | |||
-------------- | |||
@@ -0,0 +1,210 @@ | |||
/* | |||
* 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.tools.ant.types.resources; | |||
import java.io.File; | |||
import java.util.Iterator; | |||
import java.util.Stack; | |||
import org.apache.tools.ant.BuildException; | |||
import org.apache.tools.ant.Project; | |||
import org.apache.tools.ant.types.DataType; | |||
import org.apache.tools.ant.types.Resource; | |||
import org.apache.tools.ant.types.ResourceCollection; | |||
/** | |||
* Base class for a ResourceCollection that wraps a single nested | |||
* ResourceCollection. | |||
* @since Ant 1.8.2 | |||
*/ | |||
public abstract class AbstractResourceCollectionWrapper | |||
extends DataType implements ResourceCollection, Cloneable { | |||
private static final String ONE_NESTED_MESSAGE | |||
= " expects exactly one nested resource collection."; | |||
private ResourceCollection rc; | |||
private boolean cache = true; | |||
/** | |||
* Set whether to cache collections. | |||
* @param b boolean cache flag. | |||
*/ | |||
public synchronized void setCache(boolean b) { | |||
cache = b; | |||
} | |||
/** | |||
* Learn whether to cache collections. Default is <code>true</code>. | |||
* @return boolean cache flag. | |||
*/ | |||
public synchronized boolean isCache() { | |||
return cache; | |||
} | |||
/** | |||
* Add a ResourceCollection to the container. | |||
* @param c the ResourceCollection to add. | |||
* @throws BuildException on error. | |||
*/ | |||
public synchronized void add(ResourceCollection c) throws BuildException { | |||
if (isReference()) { | |||
throw noChildrenAllowed(); | |||
} | |||
if (c == null) { | |||
return; | |||
} | |||
if (rc != null) { | |||
throw oneNested(); | |||
} | |||
rc = c; | |||
if (Project.getProject(rc) == null) { | |||
Project p = getProject(); | |||
if (p != null) { | |||
p.setProjectReference(rc); | |||
} | |||
} | |||
setChecked(false); | |||
} | |||
/** | |||
* Fulfill the ResourceCollection contract. | |||
* @return an Iterator of Resources. | |||
*/ | |||
public final synchronized Iterator iterator() { | |||
if (isReference()) { | |||
return ((AbstractResourceCollectionWrapper) getCheckedRef()).iterator(); | |||
} | |||
dieOnCircularReference(); | |||
return new FailFast(this, createIterator()); | |||
} | |||
/** | |||
* Do create an iterator on the resource collection. The creation | |||
* of the iterator is allowed to not be thread safe whereas the iterator | |||
* itself should. The returned iterator will be wrapped into the FailFast | |||
* one. | |||
* | |||
* @return the iterator on the resource collection | |||
*/ | |||
protected abstract Iterator createIterator(); | |||
/** | |||
* Fulfill the ResourceCollection contract. | |||
* @return number of elements as int. | |||
*/ | |||
public synchronized int size() { | |||
if (isReference()) { | |||
return ((AbstractResourceCollectionWrapper) getCheckedRef()).size(); | |||
} | |||
dieOnCircularReference(); | |||
return getSize(); | |||
} | |||
/** | |||
* Do compute the size of the resource collection. The implementation of | |||
* this function is allowed to be not thread safe. | |||
* | |||
* @return | |||
*/ | |||
protected abstract int getSize(); | |||
/** | |||
* Fulfill the ResourceCollection contract. | |||
* @return whether this is a filesystem-only resource collection. | |||
*/ | |||
public synchronized boolean isFilesystemOnly() { | |||
if (isReference()) { | |||
return ((BaseResourceCollectionContainer) getCheckedRef()).isFilesystemOnly(); | |||
} | |||
dieOnCircularReference(); | |||
if (rc == null || rc.isFilesystemOnly()) { | |||
return true; | |||
} | |||
/* now check each Resource in case the child only | |||
lets through files from any children IT may have: */ | |||
for (Iterator i = createIterator(); i.hasNext();) { | |||
Resource r = (Resource) i.next(); | |||
if (r.as(FileProvider.class) == null) { | |||
return false; | |||
} | |||
} | |||
return true; | |||
} | |||
/** | |||
* Overrides the version of DataType to recurse on all DataType | |||
* child elements that may have been added. | |||
* @param stk the stack of data types to use (recursively). | |||
* @param p the project to use to dereference the references. | |||
* @throws BuildException on error. | |||
*/ | |||
protected synchronized void dieOnCircularReference(Stack stk, Project p) | |||
throws BuildException { | |||
if (isChecked()) { | |||
return; | |||
} | |||
if (isReference()) { | |||
super.dieOnCircularReference(stk, p); | |||
} else { | |||
if (rc instanceof DataType) { | |||
pushAndInvokeCircularReferenceCheck((DataType) rc, stk, p); | |||
} | |||
setChecked(true); | |||
} | |||
} | |||
/** | |||
* Get the nested ResourceCollection. | |||
* @return a ResourceCollection. | |||
* @throws BuildException if no nested ResourceCollection has been provided. | |||
*/ | |||
protected final synchronized ResourceCollection getResourceCollection() { | |||
dieOnCircularReference(); | |||
if (rc == null) { | |||
throw oneNested(); | |||
} | |||
return rc; | |||
} | |||
/** | |||
* Format this BaseResourceCollectionWrapper as a String. | |||
* @return a descriptive <code>String</code>. | |||
*/ | |||
public synchronized String toString() { | |||
if (isReference()) { | |||
return getCheckedRef().toString(); | |||
} | |||
if (getSize() == 0) { | |||
return ""; | |||
} | |||
StringBuffer sb = new StringBuffer(); | |||
for (Iterator i = createIterator(); i.hasNext();) { | |||
if (sb.length() > 0) { | |||
sb.append(File.pathSeparatorChar); | |||
} | |||
sb.append(i.next()); | |||
} | |||
return sb.toString(); | |||
} | |||
private BuildException oneNested() { | |||
return new BuildException(super.toString() + ONE_NESTED_MESSAGE); | |||
} | |||
} |
@@ -17,16 +17,8 @@ | |||
*/ | |||
package org.apache.tools.ant.types.resources; | |||
import java.io.File; | |||
import java.util.Stack; | |||
import java.util.Iterator; | |||
import java.util.Collection; | |||
import org.apache.tools.ant.Project; | |||
import org.apache.tools.ant.BuildException; | |||
import org.apache.tools.ant.types.DataType; | |||
import org.apache.tools.ant.types.Resource; | |||
import org.apache.tools.ant.types.ResourceCollection; | |||
import java.util.Iterator; | |||
/** | |||
* Base class for a ResourceCollection that wraps a single nested | |||
@@ -34,165 +26,24 @@ import org.apache.tools.ant.types.ResourceCollection; | |||
* @since Ant 1.7 | |||
*/ | |||
public abstract class BaseResourceCollectionWrapper | |||
extends DataType implements ResourceCollection, Cloneable { | |||
private static final String ONE_NESTED_MESSAGE | |||
= " expects exactly one nested resource collection."; | |||
extends AbstractResourceCollectionWrapper { | |||
private ResourceCollection rc; | |||
private Collection coll = null; | |||
private boolean cache = true; | |||
/** | |||
* Set whether to cache collections. | |||
* @param b boolean cache flag. | |||
*/ | |||
public synchronized void setCache(boolean b) { | |||
cache = b; | |||
} | |||
/** | |||
* Learn whether to cache collections. Default is <code>true</code>. | |||
* @return boolean cache flag. | |||
*/ | |||
public synchronized boolean isCache() { | |||
return cache; | |||
protected Iterator createIterator() { | |||
return cacheCollection().iterator(); | |||
} | |||
/** | |||
* Add a ResourceCollection to the container. | |||
* @param c the ResourceCollection to add. | |||
* @throws BuildException on error. | |||
*/ | |||
public synchronized void add(ResourceCollection c) throws BuildException { | |||
if (isReference()) { | |||
throw noChildrenAllowed(); | |||
} | |||
if (c == null) { | |||
return; | |||
} | |||
if (rc != null) { | |||
throw oneNested(); | |||
} | |||
rc = c; | |||
if (Project.getProject(rc) == null) { | |||
Project p = getProject(); | |||
if (p != null) { | |||
p.setProjectReference(rc); | |||
} | |||
} | |||
setChecked(false); | |||
} | |||
/** | |||
* Fulfill the ResourceCollection contract. | |||
* @return an Iterator of Resources. | |||
*/ | |||
public final synchronized Iterator iterator() { | |||
if (isReference()) { | |||
return ((BaseResourceCollectionWrapper) getCheckedRef()).iterator(); | |||
} | |||
dieOnCircularReference(); | |||
return new FailFast(this, cacheCollection().iterator()); | |||
} | |||
/** | |||
* Fulfill the ResourceCollection contract. | |||
* @return number of elements as int. | |||
*/ | |||
public synchronized int size() { | |||
if (isReference()) { | |||
return ((BaseResourceCollectionWrapper) getCheckedRef()).size(); | |||
} | |||
dieOnCircularReference(); | |||
protected int getSize() { | |||
return cacheCollection().size(); | |||
} | |||
/** | |||
* Fulfill the ResourceCollection contract. | |||
* @return whether this is a filesystem-only resource collection. | |||
*/ | |||
public synchronized boolean isFilesystemOnly() { | |||
if (isReference()) { | |||
return ((BaseResourceCollectionContainer) getCheckedRef()).isFilesystemOnly(); | |||
} | |||
dieOnCircularReference(); | |||
if (rc == null || rc.isFilesystemOnly()) { | |||
return true; | |||
} | |||
/* now check each Resource in case the child only | |||
lets through files from any children IT may have: */ | |||
for (Iterator i = cacheCollection().iterator(); i.hasNext();) { | |||
Resource r = (Resource) i.next(); | |||
if (r.as(FileProvider.class) == null) { | |||
return false; | |||
} | |||
} | |||
return true; | |||
} | |||
/** | |||
* Overrides the version of DataType to recurse on all DataType | |||
* child elements that may have been added. | |||
* @param stk the stack of data types to use (recursively). | |||
* @param p the project to use to dereference the references. | |||
* @throws BuildException on error. | |||
*/ | |||
protected synchronized void dieOnCircularReference(Stack stk, Project p) | |||
throws BuildException { | |||
if (isChecked()) { | |||
return; | |||
} | |||
if (isReference()) { | |||
super.dieOnCircularReference(stk, p); | |||
} else { | |||
if (rc instanceof DataType) { | |||
pushAndInvokeCircularReferenceCheck((DataType) rc, stk, p); | |||
} | |||
setChecked(true); | |||
} | |||
} | |||
/** | |||
* Get the nested ResourceCollection. | |||
* @return a ResourceCollection. | |||
* @throws BuildException if no nested ResourceCollection has been provided. | |||
*/ | |||
protected final synchronized ResourceCollection getResourceCollection() { | |||
dieOnCircularReference(); | |||
if (rc == null) { | |||
throw oneNested(); | |||
} | |||
return rc; | |||
} | |||
/** | |||
* Template method for subclasses to return a Collection of Resources. | |||
* @return Collection. | |||
*/ | |||
protected abstract Collection getCollection(); | |||
/** | |||
* Format this BaseResourceCollectionWrapper as a String. | |||
* @return a descriptive <code>String</code>. | |||
*/ | |||
public synchronized String toString() { | |||
if (isReference()) { | |||
return getCheckedRef().toString(); | |||
} | |||
if (cacheCollection().size() == 0) { | |||
return ""; | |||
} | |||
StringBuffer sb = new StringBuffer(); | |||
for (Iterator i = coll.iterator(); i.hasNext();) { | |||
if (sb.length() > 0) { | |||
sb.append(File.pathSeparatorChar); | |||
} | |||
sb.append(i.next()); | |||
} | |||
return sb.toString(); | |||
} | |||
private synchronized Collection cacheCollection() { | |||
if (coll == null || !isCache()) { | |||
coll = getCollection(); | |||
@@ -200,8 +51,4 @@ public abstract class BaseResourceCollectionWrapper | |||
return coll; | |||
} | |||
private BuildException oneNested() { | |||
return new BuildException(super.toString() + ONE_NESTED_MESSAGE); | |||
} | |||
} |
@@ -0,0 +1,157 @@ | |||
package org.apache.tools.ant.types.resources; | |||
import java.util.ArrayList; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.NoSuchElementException; | |||
import org.apache.tools.ant.types.Resource; | |||
/** | |||
* Resource collection which load underlying resource collection only on demand | |||
* with support for caching | |||
*/ | |||
public class LazyResourceCollectionWrapper extends | |||
AbstractResourceCollectionWrapper { | |||
/** List of cached resources */ | |||
private List cachedResources = new ArrayList(); | |||
private FilteringIterator filteringIterator; | |||
protected Iterator createIterator() { | |||
Iterator iterator; | |||
if (isCache()) { | |||
if (filteringIterator == null) { | |||
// no worry of thread safety here, see function's contract | |||
filteringIterator = new FilteringIterator( | |||
getResourceCollection().iterator()); | |||
} | |||
iterator = new CachedIterator(filteringIterator); | |||
} else { | |||
iterator = new FilteringIterator(getResourceCollection().iterator()); | |||
} | |||
return iterator; | |||
} | |||
protected int getSize() { | |||
// to compute the size, just iterate: the iterator will take care of | |||
// caching | |||
Iterator it = createIterator(); | |||
int size = 0; | |||
while (it.hasNext()) { | |||
it.next(); | |||
size++; | |||
} | |||
return size; | |||
} | |||
/** | |||
* Specify if the resource should be filtered or not. This function should | |||
* be overrided in order to define the filtering algorithm | |||
* | |||
* @param r | |||
* @return | |||
*/ | |||
protected boolean filterResource(Resource r) { | |||
return false; | |||
} | |||
private class FilteringIterator implements Iterator { | |||
Resource next = null; | |||
boolean ended = false; | |||
protected final Iterator it; | |||
public FilteringIterator(Iterator it) { | |||
this.it = it; | |||
} | |||
public boolean hasNext() { | |||
if (ended) { | |||
return false; | |||
} | |||
while (next == null) { | |||
if (!it.hasNext()) { | |||
ended = true; | |||
return false; | |||
} | |||
next = (Resource) it.next(); | |||
if (filterResource(next)) { | |||
next = null; | |||
} | |||
} | |||
return true; | |||
} | |||
public Object next() { | |||
if (!hasNext()) { | |||
throw new UnsupportedOperationException(); | |||
} | |||
Resource r = next; | |||
next = null; | |||
return r; | |||
} | |||
public void remove() { | |||
throw new UnsupportedOperationException(); | |||
} | |||
} | |||
/** | |||
* Iterator that will put in the shared cache array list the selected | |||
* resources | |||
*/ | |||
private class CachedIterator implements Iterator { | |||
int cusrsor = 0; | |||
private final Iterator it; | |||
/** | |||
* Default constructor | |||
* | |||
* @param it | |||
* the iterator which will provide the resources to put in | |||
* cache | |||
*/ | |||
public CachedIterator(Iterator it) { | |||
this.it = it; | |||
} | |||
public boolean hasNext() { | |||
synchronized (cachedResources) { | |||
// have we already cached the next entry ? | |||
if (cachedResources.size() > cusrsor) { | |||
return true; | |||
} | |||
// does the wrapped iterator any more resource ? | |||
if (!it.hasNext()) { | |||
return false; | |||
} | |||
// put in cache the next resource | |||
Resource r = (Resource) it.next(); | |||
cachedResources.add(r); | |||
} | |||
return true; | |||
} | |||
public Object next() { | |||
// first check that we have some to deliver | |||
if (!hasNext()) { | |||
throw new NoSuchElementException(); | |||
} | |||
synchronized (cachedResources) { | |||
// return the cached entry as hasNext should have put one for | |||
// this iterator | |||
return cachedResources.get(cusrsor++); | |||
} | |||
} | |||
public void remove() { | |||
throw new UnsupportedOperationException(); | |||
} | |||
} | |||
} |
@@ -17,8 +17,6 @@ | |||
*/ | |||
package org.apache.tools.ant.types.resources; | |||
import java.util.ArrayList; | |||
import java.util.Collection; | |||
import java.util.Iterator; | |||
import java.util.Stack; | |||
@@ -37,23 +35,17 @@ import org.apache.tools.ant.types.resources.selectors.ResourceSelectorContainer; | |||
public class Restrict | |||
extends ResourceSelectorContainer implements ResourceCollection { | |||
private BaseResourceCollectionWrapper w = new BaseResourceCollectionWrapper() { | |||
private LazyResourceCollectionWrapper w = new LazyResourceCollectionWrapper() { | |||
/** | |||
* Restrict the nested ResourceCollection based on the nested selectors. | |||
* @return a Collection of Resources. | |||
*/ | |||
protected Collection getCollection() { | |||
ArrayList result = new ArrayList(); | |||
outer: for (Iterator ri = w.getResourceCollection().iterator(); ri.hasNext();) { | |||
Resource r = (Resource) ri.next(); | |||
for (Iterator i = getSelectors(); i.hasNext();) { | |||
if (!((ResourceSelector) (i.next())).isSelected(r)) { | |||
continue outer; | |||
} | |||
protected boolean filterResource(Resource r) { | |||
for (Iterator i = getSelectors(); i.hasNext();) { | |||
if (!((ResourceSelector) (i.next())).isSelected(r)) { | |||
return true; | |||
} | |||
result.add(r); | |||
} | |||
return result; | |||
return false; | |||
} | |||
}; | |||
@@ -0,0 +1,180 @@ | |||
/* | |||
* 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.tools.ant.types.resources; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.NoSuchElementException; | |||
import junit.framework.TestCase; | |||
import org.apache.tools.ant.types.Resource; | |||
import org.apache.tools.ant.types.ResourceCollection; | |||
public class LazyResourceCollectionTest extends TestCase { | |||
private class StringResourceCollection implements ResourceCollection { | |||
List resources = Arrays.asList(new Resource[] {}); | |||
List createdIterators = new ArrayList(); | |||
public int size() { | |||
return resources.size(); | |||
} | |||
public Iterator iterator() { | |||
StringResourceIterator it = new StringResourceIterator(); | |||
createdIterators.add(it); | |||
return it; | |||
} | |||
public boolean isFilesystemOnly() { | |||
return false; | |||
} | |||
} | |||
private class StringResourceIterator implements Iterator { | |||
int cursor = 0; | |||
public void remove() { | |||
throw new UnsupportedOperationException(); | |||
} | |||
public Object next() { | |||
if (cursor < 3) { | |||
cursor++; | |||
return new StringResource("r" + cursor); | |||
} | |||
return null; | |||
} | |||
public boolean hasNext() { | |||
return cursor < 3; | |||
} | |||
} | |||
public void testLazyLoading() throws Exception { | |||
StringResourceCollection collectionTest = new StringResourceCollection(); | |||
LazyResourceCollectionWrapper lazyCollection = new LazyResourceCollectionWrapper(); | |||
lazyCollection.add(collectionTest); | |||
Iterator it = lazyCollection.iterator(); | |||
assertOneCreatedIterator(collectionTest); | |||
StringResourceIterator stringResourceIterator = (StringResourceIterator) collectionTest.createdIterators | |||
.get(0); | |||
assertEquals("A resource was loaded without iterating", 1, | |||
stringResourceIterator.cursor); | |||
StringResource r = (StringResource) it.next(); | |||
assertOneCreatedIterator(collectionTest); | |||
assertEquals("r1", r.getValue()); | |||
assertEquals("Iterating once load more than 1 resource", 2, | |||
stringResourceIterator.cursor); | |||
r = (StringResource) it.next(); | |||
assertOneCreatedIterator(collectionTest); | |||
assertEquals("r2", r.getValue()); | |||
assertEquals("Iterating twice load more than 2 resources", 3, | |||
stringResourceIterator.cursor); | |||
r = (StringResource) it.next(); | |||
assertOneCreatedIterator(collectionTest); | |||
assertEquals("r3", r.getValue()); | |||
assertEquals("Iterating 3 times load more than 3 resources", 3, | |||
stringResourceIterator.cursor); | |||
try { | |||
it.next(); | |||
fail("NoSuchElementException shoudl have been raised"); | |||
} catch (NoSuchElementException e) { | |||
// ok | |||
} | |||
} | |||
private void assertOneCreatedIterator( | |||
StringResourceCollection testCollection) { | |||
assertEquals("More than one iterator has been created", 1, | |||
testCollection.createdIterators.size()); | |||
} | |||
public void testCaching() throws Exception { | |||
StringResourceCollection collectionTest = new StringResourceCollection(); | |||
LazyResourceCollectionWrapper lazyCollection = new LazyResourceCollectionWrapper(); | |||
lazyCollection.add(collectionTest); | |||
assertTrue(lazyCollection.isCache()); | |||
Iterator it1 = lazyCollection.iterator(); | |||
assertOneCreatedIterator(collectionTest); | |||
Iterator it2 = lazyCollection.iterator(); | |||
assertOneCreatedIterator(collectionTest); | |||
StringResourceIterator stringResourceIterator = (StringResourceIterator) collectionTest.createdIterators | |||
.get(0); | |||
assertEquals("A resource was loaded without iterating", 1, | |||
stringResourceIterator.cursor); | |||
StringResource r = (StringResource) it1.next(); | |||
assertEquals("r1", r.getValue()); | |||
assertEquals("Iterating once load more than 1 resource", 2, | |||
stringResourceIterator.cursor); | |||
r = (StringResource) it2.next(); | |||
assertEquals("r1", r.getValue()); | |||
assertEquals( | |||
"The second iterator did not lookup in the cache for a resource", | |||
2, stringResourceIterator.cursor); | |||
r = (StringResource) it2.next(); | |||
assertEquals("r2", r.getValue()); | |||
assertEquals("Iterating twice load more than 2 resources", 3, | |||
stringResourceIterator.cursor); | |||
r = (StringResource) it1.next(); | |||
assertEquals("r2", r.getValue()); | |||
assertEquals( | |||
"The first iterator did not lookup in the cache for a resource", | |||
3, stringResourceIterator.cursor); | |||
r = (StringResource) it2.next(); | |||
assertEquals("r3", r.getValue()); | |||
assertEquals("Iterating 3 times load more than 3 resources", 3, | |||
stringResourceIterator.cursor); | |||
r = (StringResource) it1.next(); | |||
assertEquals("r3", r.getValue()); | |||
assertEquals( | |||
"The first iterator did not lookup in the cache for a resource", | |||
3, stringResourceIterator.cursor); | |||
try { | |||
it1.next(); | |||
fail("NoSuchElementException should have been raised"); | |||
} catch (NoSuchElementException e) { | |||
// ok | |||
} | |||
try { | |||
it2.next(); | |||
fail("NoSuchElementException should have been raised"); | |||
} catch (NoSuchElementException e) { | |||
// ok | |||
} | |||
} | |||
} |