code: package org.gridkit.jvmtool.event;
  
  import java.util.Iterator;
  import java.util.NoSuchElementException;
  import java.util.concurrent.Callable;
  
  public abstract class ChainedEventReader<T extends Event> implements EventReader<T> {
  
      public static <T extends Event> EventReader<T> chain(final EventReader<T>... readers) {
          return new ChainedEventReader<T>() {
              int next;
  
              @Override
              protected EventReader<T> produceNext() {
                  return next < readers.length ? readers[next++] : null;
              }
          };
      }
  
      public static <T extends Event> EventReader<T> chain(final EventReader<T> reader, final Callable<T> producer) {
          return new ChainedEventReader<T>() {
  
              boolean first;
              boolean done;
  
              @SuppressWarnings({ "unchecked", "rawtypes" })
              @Override
              protected EventReader<T> produceNext() {
                  if (!first) {
                      first = true;
                      return reader;
                  }
                  if (done) {
                      return null;
                  }
                  else {
                      done = true;
                      try {
                          return (EventReader<T>) producer.call();
                      } catch (Exception e) {
                          return new SingleEventReader(new SimpleErrorEvent(e));
                      }
                  }
              }
          };
      }
  
      private boolean init;
      private EventReader<T> current;
  
      public ChainedEventReader() {
      }
  
      @Override
      public <M extends Event> EventReader<M> morph(EventMorpher<T, M> morpher) {
          return MorphingEventReader.morph(this, morpher);
      }
  
      @Override
      public Iterator<T> iterator() {
          return this;
      }
  
      @Override
      public boolean hasNext() {
          if (!init) {
              init = true;
              current = produceNext();
          }
          if (current == null) {
              return false;
          }
          while(true) {
              if (current.hasNext()) {
                  return true;
              }
              else {
                  current.dispose();
                  current = produceNext();
                  if (current == null) {
                      return false;
                  }
              }
          }
      }
  
      @Override
      public T next() {
          if (!hasNext()) {
              throw new NoSuchElementException();
          }
          return current.next();
      }
  
      @Override
      public T peekNext() {
          if (!hasNext()) {
              throw new NoSuchElementException();
          }
          return current.peekNext();
      }
  
      @Override
      public void remove() {
          throw new UnsupportedOperationException();
      }
  
  //    @Override
  //    @SuppressWarnings({ "unchecked", "rawtypes" })
  //    public <X extends Event> EventReader<X> filterByClass(Class<X> typeFilter) {
  //        this.typeFilter = (Class)typeFilter;
  //        if (current != null) {
  //            current = (EventReader)current.filterByClass(typeFilter);
  //        }
  //        return (EventReader)this;
  //    }
  //
      @Override
      public void dispose() {
          if (current != null) {
              current.dispose();
          }
      }
  
      protected abstract EventReader<T> produceNext();
  }
  