/**
   * Creates an iterator that returns an {@link Options} containing each possible parameter set
   * defined by these {@link
   * com.biomatters.plugins.barcoding.validator.research.options.BatchOptions}. Note that the {@link
   * Options} object returned is always the same to save on initializing multiple copies. Each time
   * {@link java.util.Iterator#next()} is called the next parameter set is set on the {@link
   * Options}.
   *
   * @return An iterator that will iterate over all possible parameter sets specified by this {@link
   *     com.biomatters.plugins.barcoding.validator.research.options.BatchOptions}
   * @throws DocumentOperationException if there is a problem creating the {@link Options} object
   */
  public Iterator<T> iterator() throws DocumentOperationException {
    List<Set<OptionToSet>> possibleValues = new ArrayList<Set<OptionToSet>>();

    Map<String, MultiValueOption<?>> multiValueOptions = getMultiValueOptions("", options);
    for (Map.Entry<String, MultiValueOption<?>> entry : multiValueOptions.entrySet()) {
      possibleValues.add(getOptionsToSet(entry.getKey(), entry.getValue()));
    }
    Set<List<OptionToSet>> lists = Sets.cartesianProduct(possibleValues);
    final Iterator<List<OptionToSet>> possibilityIterator = lists.iterator();

    final T template;
    try {
      //noinspection unchecked
      template = (T) options.getClass().newInstance(); // getClass() doesn't return Class<T> :(
      template.valuesFromXML(options.valuesToXML(XMLSerializable.ROOT_ELEMENT_NAME));
    } catch (InstantiationException e) {
      throw new DocumentOperationException("Failed to create Options: " + e.getMessage(), e);
    } catch (IllegalAccessException e) {
      throw new DocumentOperationException("Failed to create Options: " + e.getMessage(), e);
    }

    return new Iterator<T>() {
      @Override
      public boolean hasNext() {
        return possibilityIterator.hasNext();
      }

      @Override
      public T next() {
        List<OptionToSet> possibility = possibilityIterator.next();
        for (OptionToSet optionToSet : possibility) {
          template.setValue(optionToSet.name, optionToSet.value);
        }
        return template;
      }

      @Override
      public void remove() {
        new UnsupportedOperationException();
      }
    };
  }