content: /*
   *  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
   *
   *      https://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.Iterator;
  import java.util.List;
  import java.util.NoSuchElementException;
  import java.util.function.Supplier;
  
  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 final List<Resource> cachedResources = new ArrayList<>();
  
      private Iterator<Resource> filteringIterator;
  
      private final Supplier<Iterator<Resource>> filteringIteratorSupplier =
          () -> new FilteringIterator(getResourceCollection().iterator());
  
      @Override
      protected Iterator<Resource> createIterator() {
          if (isCache()) {
              if (filteringIterator == null) {
                  // no worry of thread safety here, see function's contract
                  filteringIterator = filteringIteratorSupplier.get();
              }
              return new CachedIterator(filteringIterator);
          }
          return filteringIteratorSupplier.get();
      }
  
      @Override
      protected int getSize() {
          // to compute the size, just iterate: the iterator will take care of
          // caching
          final Iterator<Resource> 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 overridden in order to define the filtering algorithm
       *
       * @param r resource considered for filtration
       * @return whether the resource should be filtered or not
       */
      protected boolean filterResource(final Resource r) {
          return false;
      }
  
      private class FilteringIterator implements Iterator<Resource> {
  
          Resource next = null;
  
          boolean ended = false;
  
          protected final Iterator<Resource> it;
  
          FilteringIterator(final Iterator<Resource> it) {
              this.it = it;
          }
  
          @Override
          public boolean hasNext() {
              if (ended) {
                  return false;
              }
              while (next == null) {
                  if (!it.hasNext()) {
                      ended = true;
                      return false;
                  }
                  next = it.next();
                  if (filterResource(next)) {
                      next = null;
                  }
              }
              return true;
          }
  
          @Override
          public Resource next() {
              if (!hasNext()) {
                  throw new UnsupportedOperationException();
              }
              final Resource r = next;
              next = null;
              return r;
          }
  
      }
  
      /**
       * Iterator that will put in the shared cache array list the selected
       * resources
       */
      private class CachedIterator implements Iterator<Resource> {
  
          int cursor = 0;
  
          private final Iterator<Resource> it;
  
          /**
           * Default constructor
           *
           * @param it
           *            the iterator which will provide the resources to put in
           *            cache
           */
          public CachedIterator(final Iterator<Resource> it) {
              this.it = it;
          }
  
          @Override
          public boolean hasNext() {
              synchronized (cachedResources) {
                  // have we already cached the next entry ?
                  if (cachedResources.size() > cursor) {
                      return true;
                  }
                  // does the wrapped iterator any more resource ?
                  if (!it.hasNext()) {
                      return false;
                  }
                  // put in cache the next resource
                  final Resource r = it.next();
                  cachedResources.add(r);
              }
              return true;
          }
  
          @Override
          public Resource 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(cursor++);
              }
          }
  
          @Override
          public void remove() {
              throw new UnsupportedOperationException();
          }
      }
  }
  