class MappingOptions {
    Condition<?, ?> condition;
    Converter<?, ?> converter;
    Provider<?> provider;
    int skipType;
    boolean mapFromSource;

    void reset() {
      condition = null;
      converter = null;
      provider = null;
      skipType = 0;
      mapFromSource = false;
    }
  }

  static <S, D> Collection<MappingImpl> build(Class<S> sourceType, Class<D> destinationType,
      InheritingConfiguration configuration, PropertyMap<S, D> propertyMap) {
    return new ExplicitMappingBuilder<S, D>(sourceType, destinationType, configuration)
        .build(propertyMap);
  }

  ExplicitMappingBuilder(Class<S> sourceType, Class<D> destinationType,
      InheritingConfiguration configuration) {
    this.sourceType = sourceType;
    this.destinationType = destinationType;
    this.configuration = configuration;
  }

  public D skip() {
    map();
    options.skipType = 1;
    return destination;
  }

  public void skip(Object destination) {
    map(destination);
    options.skipType = 2;
  }

  public void skip(Object source, Object destination) {
    map(source, destination);
    options.skipType = 3;
  }

  public D map() {
    saveLastMapping();
    getNextMapping();
    return destination;
  }

  public D map(Object subject) {
    saveLastMapping();
    getNextMapping();
    recordSourceValue(subject);
    return destination;
  }

  public void map(Object source, Object destination) {
    saveLastMapping();
    getNextMapping();
    recordSourceValue(source);
  }

  public <T> T source(String sourcePropertyPath) {
    if (sourcePropertyPath == null)
      errors.errorNullArgument("sourcePropertyPath");
    if (sourceAccessors != null)
      saveLastMapping();

    String[] propertyNames = DOT_PATTERN.split(sourcePropertyPath);
    sourceAccessors = new ArrayList<Accessor>(propertyNames.length);
    ValueReader<?> valueReader = configuration.valueAccessStore.getFirstSupportedReader(sourceType);
    if (valueReader != null)
      for (String propertyName : propertyNames)
        sourceAccessors.add(ValueReaderPropertyInfo.create(valueReader, propertyName));
    else {
      Accessor accessor = null;
      for (String propertyName : propertyNames) {
        Class<?> propertyType = accessor == null ? sourceType : accessor.getType();
        accessor = PropertyInfoRegistry.accessorFor(propertyType, propertyName, configuration);
        if (accessor == null) {
          errors.errorInvalidSourcePath(sourcePropertyPath, propertyType, propertyName);
          return null;
        }

        sourceAccessors.add(accessor);
      }
    }

    return null;
  }

  public Object destination(String destPropertyPath) {
    if (destPropertyPath == null)
      errors.errorNullArgument("destPropertyPath");

    String[] propertyNames = DOT_PATTERN.split(destPropertyPath);
    destinationMutators = new ArrayList<Mutator>(propertyNames.length);
    ValueWriter<?> valueWriter = configuration.valueMutateStore.getFirstSupportedWriter(destinationType);
    if (valueWriter != null)
      for (String propertyName : propertyNames)
        destinationMutators.add(ValueWriterPropertyInfo.create(valueWriter, propertyName));
    else {
      Mutator mutator = null;
      for (String propertyName : propertyNames) {
        Class<?> propertyType = mutator == null ? destinationType : mutator.getType();
        mutator = PropertyInfoRegistry.mutatorFor(propertyType, propertyName, configuration);
        if (mutator == null) {
          errors.errorInvalidDestinationPath(destPropertyPath, propertyType, propertyName);
          return null;
        }
        destinationMutators.add(mutator);
      }
    }
    return null;
  }

  public ConditionExpression<S, D> using(Converter<?, ?> converter) {
    saveLastMapping();
    if (converter == null)
      errors.errorNullArgument("converter");
    Assert.state(options.converter == null, "using() can only be called once per mapping.");
    options.converter = converter;
    return this;
  }

  public ConditionExpression<S, D> when(Condition<?, ?> condition) {
    saveLastMapping();
    if (condition == null)
      errors.errorNullArgument("condition");
    Assert.state(options.condition == null, "when() can only be called once per mapping.");
    options.condition = condition;
    return this;
  }

  public ConditionExpression<S, D> with(Provider<?> provider) {
    saveLastMapping();
    if (provider == null)
      errors.errorNullArgument("provider");
    Assert.state(options.provider == null, "withProvider() can only be called once per mapping.");
    options.provider = provider;
    return this;
  }

  /**
   * Builds and returns all property mappings defined in the {@code propertyMap}.
   */
  Collection<MappingImpl> build(PropertyMap<S, D> propertyMap) {
    try {
      PROPERTY_MAP_CONFIGURE.invoke(propertyMap, this);
      saveLastMapping();
    } catch (IllegalAccessException e) {
      errors.errorAccessingConfigure(e);
    } catch (InvocationTargetException e) {
      Throwable cause = e.getCause();
      if (cause instanceof ConfigurationException)
        throw (ConfigurationException) cause;
      else
        errors.addMessage(cause, "Failed to configure mappings");
    } catch (NullPointerException e) {
      if (proxyErrors.hasErrors()) {
        throw proxyErrors.toException();
      }
      throw e;
    }

    errors.throwConfigurationExceptionIfErrorsExist();
    return propertyMappings;
  }

  /**
   * Visits the {@code propertyMap} and captures and validates mappings.
   */
  public void visitPropertyMap(PropertyMap<S, D> propertyMap) {<FILL_FUNCTION_BODY>

    String propertyMapClassName = propertyMap.getClass().getName();

    try {
      ClassReader cr = new ClassReader(propertyMap.getClass().getClassLoader().getResourceAsStream(
          propertyMapClassName.replace('.', '/') + ".class"));
      ExplicitMappingVisitor visitor = new ExplicitMappingVisitor(errors, configuration,
          propertyMapClassName, destinationType.getName(), propertyMap.getClass().getClassLoader());
      cr.accept(visitor, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG);
      visitedMappings = visitor.mappings;
    } catch (IOException e) {
      errors.errorReadingClass(e, propertyMapClassName);
    }

    errors.throwConfigurationExceptionIfErrorsExist();
    createProxies();
  
<no_super_class>