class ReferenceMapExpressionImpl<S, D> implements ReferenceMapExpression<S, D> {
  private final TypeMapImpl<S, D> typeMap;
  private final MappingOptions options;
  private final PropertyReferenceCollector collector;
  private final S source;
  private final D destination;

  ReferenceMapExpressionImpl(TypeMapImpl<S, D> typeMap) {
    this(typeMap, new MappingOptions());
  }

  ReferenceMapExpressionImpl(TypeMapImpl<S, D> typeMap, MappingOptions options) {
    this.typeMap = typeMap;
    this.options = options;
    this.collector = new PropertyReferenceCollector(typeMap.configuration, options);
    try {
      this.source = ProxyFactory.proxyFor(typeMap.getSourceType(), collector.newSourceInterceptor(),
          collector.getProxyErrors());
      this.destination = ProxyFactory
          .proxyFor(typeMap.getDestinationType(), collector.newDestinationInterceptor(),
              collector.getProxyErrors());
    } catch (ErrorsException e) {
      throw e.getErrors().toConfigurationException();
    }
  }

  @Override
  public <V> void map(SourceGetter<S> sourceGetter, DestinationSetter<D, V> destinationSetter) {
    visitSource(sourceGetter);
    visitDestination(destinationSetter);
    skipMapping(collector.collect());
    collector.reset();
  }

  @Override
  public <V> void skip(DestinationSetter<D, V> destinationSetter) {
    options.skipType = 1;
    visitDestination(destinationSetter);
    skipMapping(collector.collect());
    collector.reset();
  }

  private void skipMapping(MappingImpl skipMapping) {
    String prefix = skipMapping.getPath();
    List<String> conflictPaths = typeMap.getMappings().stream()
        .map(Mapping::getPath)
        .filter(path -> path.startsWith(prefix) && !path.equals(prefix))
        .collect(toList());
    if (conflictPaths.isEmpty()) {
      typeMap.addMapping(skipMapping);
    } else {
      collector.getErrors().skipConflict(skipMapping.getPath(), conflictPaths);
      collector.getErrors().throwConfigurationExceptionIfErrorsExist();
    }
  }

  @Override
  public <V> void skip(SourceGetter<S> sourceGetter, DestinationSetter<D, V> destinationSetter) {
    options.skipType = 1;
    visitSource(sourceGetter);
    visitDestination(destinationSetter);
    skipMapping(collector.collect());
    collector.reset();
  }

  private void visitSource(SourceGetter<S> sourceGetter) {
    notNull(sourceGetter, "sourceGetter");
    try {
      Object sourceProperty = sourceGetter.get(source);
      if (source == sourceProperty)
        collector.mapFromSource(typeMap.getSourceType());
      if (collector.isNoSourceGetter())
        collector.mapFromConstant(sourceProperty);
    } catch (NullPointerException e) {
      if (collector.getProxyErrors().hasErrors())
        throw collector.getProxyErrors().toException();
      throw e;
    } catch (ErrorsException e) {
      throw e.getErrors().toConfigurationException();
    }
  }

  private <V> void visitDestination(DestinationSetter<D, V> destinationSetter) {<FILL_FUNCTION_BODY>}

  private <V> V destinationValue(DestinationSetter<D, V> destinationSetter) {
    Class<?>[] typeArguments = TypeResolver.resolveRawArguments(DestinationSetter.class, destinationSetter.getClass());
    if (typeArguments != null) {
      Class<?> valueClass = typeArguments[1];
      if (Primitives.isPrimitive(valueClass)) {
        return Primitives.defaultValue(Primitives.primitiveFor(valueClass));
      }
    }
    return null;
  }
}

    notNull(destinationSetter, "destinationSetter");
    try {
      destinationSetter.accept(destination, destinationValue(destinationSetter));
    } catch (NullPointerException e) {
      if (collector.getProxyErrors().hasErrors())
        throw collector.getProxyErrors().toException();
      throw e;
    } catch (ErrorsException e) {
      throw e.getErrors().toConfigurationException();
    }
  
<no_super_class>