Package de.danielbechler.diff.differ

Source Code of de.danielbechler.diff.differ.DifferDispatcher

/*
* Copyright 2014 Daniel Bechler
*
* Licensed 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
*
* http://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 de.danielbechler.diff.differ;

import de.danielbechler.diff.access.Accessor;
import de.danielbechler.diff.access.Instances;
import de.danielbechler.diff.access.PropertyAwareAccessor;
import de.danielbechler.diff.introspection.PropertyReadException;
import de.danielbechler.diff.circular.CircularReferenceDetector;
import de.danielbechler.diff.circular.CircularReferenceDetectorFactory;
import de.danielbechler.diff.circular.CircularReferenceExceptionHandler;
import de.danielbechler.diff.filtering.IsReturnableResolver;
import de.danielbechler.diff.inclusion.IsIgnoredResolver;
import de.danielbechler.diff.introspection.PropertyAccessExceptionHandler;
import de.danielbechler.diff.introspection.PropertyAccessExceptionHandlerResolver;
import de.danielbechler.diff.node.DiffNode;
import de.danielbechler.diff.path.NodePath;
import de.danielbechler.diff.selector.ElementSelector;
import de.danielbechler.util.Assert;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static de.danielbechler.diff.circular.CircularReferenceDetector.CircularReferenceException;

/**
* @author Daniel Bechler
*/
public class DifferDispatcher
{
  private static final Logger logger = LoggerFactory.getLogger(DifferDispatcher.class);
  private final DifferProvider differProvider;
  private final CircularReferenceDetectorFactory circularReferenceDetectorFactory;
  private final CircularReferenceExceptionHandler circularReferenceExceptionHandler;
  private final IsIgnoredResolver isIgnoredResolver;
  private final IsReturnableResolver isReturnableResolver;
  private final PropertyAccessExceptionHandlerResolver propertyAccessExceptionHandlerResolver;
  CircularReferenceDetector workingCircularReferenceDetector;
  CircularReferenceDetector baseCircularReferenceDetector;

  public DifferDispatcher(final DifferProvider differProvider,
              final CircularReferenceDetectorFactory circularReferenceDetectorFactory,
              final CircularReferenceExceptionHandler circularReferenceExceptionHandler,
              final IsIgnoredResolver ignoredResolver,
              final IsReturnableResolver returnableResolver,
              final PropertyAccessExceptionHandlerResolver propertyAccessExceptionHandlerResolver)
  {
    Assert.notNull(differProvider, "differFactory");
    this.differProvider = differProvider;

    Assert.notNull(ignoredResolver, "ignoredResolver");
    this.isIgnoredResolver = ignoredResolver;

    this.circularReferenceDetectorFactory = circularReferenceDetectorFactory;
    this.circularReferenceExceptionHandler = circularReferenceExceptionHandler;
    this.isReturnableResolver = returnableResolver;
    this.propertyAccessExceptionHandlerResolver = propertyAccessExceptionHandlerResolver;

    resetInstanceMemory();
  }

  protected final void resetInstanceMemory()
  {
    workingCircularReferenceDetector = circularReferenceDetectorFactory.createCircularReferenceDetector();
    baseCircularReferenceDetector = circularReferenceDetectorFactory.createCircularReferenceDetector();
  }

  /**
   * Delegates the call to an appropriate {@link Differ}.
   *
   * @return A node representing the difference between the given {@link Instances}.
   */
  public DiffNode dispatch(final DiffNode parentNode,
               final Instances parentInstances,
               final Accessor accessor)
  {
    Assert.notNull(parentInstances, "parentInstances");
    Assert.notNull(accessor, "accessor");

    final DiffNode node = compare(parentNode, parentInstances, accessor);
    if (parentNode != null && isReturnableResolver.isReturnable(node))
    {
      parentNode.addChild(node);
    }
    return node;
  }

  private DiffNode compare(final DiffNode parentNode,
               final Instances parentInstances,
               final Accessor accessor)
  {
    final DiffNode node = new DiffNode(parentNode, accessor, null);
    if (isIgnoredResolver.isIgnored(node))
    {
      node.setState(DiffNode.State.IGNORED);
      return node;
    }

    final Instances accessedInstances;
    if (accessor instanceof PropertyAwareAccessor)
    {
      final PropertyAwareAccessor propertyAwareAccessor = (PropertyAwareAccessor) accessor;
      try
      {
        accessedInstances = parentInstances.access(accessor);
      }
      catch (final PropertyReadException e)
      {
        node.setState(DiffNode.State.INACCESSIBLE);
        final Class<?> parentType = parentInstances.getType();
        final String propertyName = propertyAwareAccessor.getPropertyName();
        final PropertyAccessExceptionHandler exceptionHandler = propertyAccessExceptionHandlerResolver
            .resolvePropertyAccessExceptionHandler(parentType, propertyName);
        if (exceptionHandler != null)
        {
          exceptionHandler.onPropertyReadException(e, node);
        }
        return node;
      }
    }
    else
    {
      accessedInstances = parentInstances.access(accessor);
    }

    if (accessedInstances.areNull())
    {
      return new DiffNode(parentNode, accessedInstances.getSourceAccessor(), accessedInstances.getType());
    }
    else
    {
      return compareWithCircularReferenceTracking(parentNode, accessedInstances);
    }
  }

  private DiffNode compareWithCircularReferenceTracking(final DiffNode parentNode,
                              final Instances instances)
  {
    DiffNode node = null;
    try
    {
      rememberInstances(parentNode, instances);
      try
      {
        node = compare(parentNode, instances);
      }
      finally
      {
        if (node != null)
        {
          forgetInstances(parentNode, instances);
        }
      }
    }
    catch (final CircularReferenceException e)
    {
      node = newCircularNode(parentNode, instances, e.getNodePath());
      circularReferenceExceptionHandler.onCircularReferenceException(node);
    }
    if (parentNode == null)
    {
      resetInstanceMemory();
    }
    return node;
  }

  private DiffNode compare(final DiffNode parentNode, final Instances instances)
  {
    final Differ differ = differProvider.retrieveDifferForType(instances.getType());
    if (differ == null)
    {
      throw new IllegalStateException("Couldn't create Differ for type '" + instances.getType() +
          "'. This mustn't happen, as there should always be a fallback differ.");
    }
    return differ.compare(parentNode, instances);
  }

  protected void forgetInstances(final DiffNode parentNode, final Instances instances)
  {
    final NodePath nodePath;
    if (parentNode != null)
    {
      final NodePath parentPath = parentNode.getPath();
      final ElementSelector elementSelector = instances.getSourceAccessor().getElementSelector();
      nodePath = NodePath.startBuildingFrom(parentPath).element(elementSelector).build();
    }
    else
    {
      nodePath = NodePath.withRoot();
    }
    logger.debug("[ {} ] Forgetting --- WORKING: {} <=> BASE: {}", nodePath, instances.getWorking(), instances.getBase());
    workingCircularReferenceDetector.remove(instances.getWorking());
    baseCircularReferenceDetector.remove(instances.getBase());
  }

  protected void rememberInstances(final DiffNode parentNode, final Instances instances)
  {
    final NodePath nodePath;
    if (parentNode != null)
    {
      final NodePath parentPath = parentNode.getPath();
      final ElementSelector elementSelector = instances.getSourceAccessor().getElementSelector();
      nodePath = NodePath.startBuildingFrom(parentPath).element(elementSelector).build();
    }
    else
    {
      nodePath = NodePath.withRoot();
    }
    logger.debug("[ {} ] Remembering --- WORKING: {} <=> BASE: {}", nodePath, instances.getWorking(), instances.getBase());

    transactionalPushToCircularReferenceDetectors(nodePath, instances);
  }

  private void transactionalPushToCircularReferenceDetectors(final NodePath nodePath, final Instances instances)
  {
    workingCircularReferenceDetector.push(instances.getWorking(), nodePath);

    // TODO This needs to be solved more elegantly. If the push for one of these detectors fails,
    // we need to make sure to revert the push to the other one, if it already happened.
    try
    {
      baseCircularReferenceDetector.push(instances.getBase(), nodePath);
    }
    catch (final CircularReferenceException e)
    {
      workingCircularReferenceDetector.remove(instances.getWorking()); // rollback
      throw e;
    }
  }

  private static DiffNode findNodeMatchingPropertyPath(final DiffNode node, final NodePath nodePath)
  {
    if (node == null)
    {
      return null;
    }
    if (node.matches(nodePath))
    {
      return node;
    }
    return findNodeMatchingPropertyPath(node.getParentNode(), nodePath);
  }

  private static DiffNode newCircularNode(final DiffNode parentNode,
                      final Instances instances,
                      final NodePath circleStartPath)
  {
    final DiffNode node = new DiffNode(parentNode, instances.getSourceAccessor(), instances.getType());
    node.setState(DiffNode.State.CIRCULAR);
    node.setCircleStartPath(circleStartPath);
    node.setCircleStartNode(findNodeMatchingPropertyPath(parentNode, circleStartPath));
    return node;
  }
}
TOP

Related Classes of de.danielbechler.diff.differ.DifferDispatcher

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.