LinkProcessingImpl.java

/*
 * #%L
 * wcm.io
 * %%
 * Copyright (C) 2018 wcm.io
 * %%
 * 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.
 * #L%
 */
package io.wcm.caravan.hal.comparison.impl.links;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Sets;

import io.wcm.caravan.hal.comparison.HalComparisonStrategy;
import io.wcm.caravan.hal.comparison.HalDifference;
import io.wcm.caravan.hal.comparison.impl.PairWithRelation;
import io.wcm.caravan.hal.comparison.impl.ProcessingResult;
import io.wcm.caravan.hal.comparison.impl.context.HalComparisonContextImpl;
import io.wcm.caravan.hal.comparison.impl.links.steps.LinkAdditionRemovalReorderingDetection;
import io.wcm.caravan.hal.comparison.impl.links.steps.LinkRelationBlackList;
import io.wcm.caravan.hal.comparison.impl.links.steps.LinkTemplateProcessor;
import io.wcm.caravan.hal.resource.HalResource;
import io.wcm.caravan.hal.resource.Link;

/**
 * Implementation of {@link LinkProcessing} that delegates the actual logic to multiple
 * {@link LinkProcessingStep}s (and collects the results).
 */
public class LinkProcessingImpl implements LinkProcessing {

  private final Iterable<LinkProcessingStep> processingSteps;

  /**
   * Default constructor that defines the order of processing steps to be executed
   */
  public LinkProcessingImpl(HalComparisonStrategy strategy) {
    this.processingSteps = ImmutableList.of(
        new LinkRelationBlackList(strategy),
        new LinkAdditionRemovalReorderingDetection(),
        new LinkTemplateProcessor(strategy));
  }

  /**
   * Alternative constructor that allows mocking of processing steps
   * @param processingSteps to be applied *instead* of the default processing steps
   */
  public LinkProcessingImpl(Iterable<LinkProcessingStep> processingSteps) {
    this.processingSteps = ImmutableList.copyOf(processingSteps);
  }

  @Override
  public ProcessingResult<Link> process(HalComparisonContextImpl context, HalResource expected, HalResource actual) {

    List<PairWithRelation<Link>> pairsToCompare = new ArrayList<>();
    List<HalDifference> diffs = new ArrayList<>();

    ListMultimap<String, Link> allExpectedLinks = expected.getLinks();
    ListMultimap<String, Link> allActualLinks = actual.getLinks();

    Set<String> allRelations = Sets.union(allExpectedLinks.keys().elementSet(), allActualLinks.keys().elementSet());

    for (String relation : allRelations) {

      HalComparisonContextImpl newContext = context.withAppendedHalPath(relation, expected);

      List<Link> remainingExpectedLinks = new ArrayList<>(allExpectedLinks.get(relation));
      List<Link> remainingActualLinks = new ArrayList<>(allActualLinks.get(relation));

      for (LinkProcessingStep step : processingSteps) {

        List<HalDifference> stepDiffs = step.apply(newContext, remainingExpectedLinks, remainingActualLinks);
        diffs.addAll(stepDiffs);
      }

      for (int i = 0; i < remainingExpectedLinks.size() && i < remainingActualLinks.size(); i++) {
        pairsToCompare.add(new PairWithRelation<Link>(relation, remainingExpectedLinks.get(i), remainingActualLinks.get(i)));
      }
    }

    return new ProcessingResult<>(pairsToCompare, diffs);
  }

}