HalComparisonImpl.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;

import org.osgi.service.component.annotations.Component;

import io.wcm.caravan.hal.comparison.HalComparison;
import io.wcm.caravan.hal.comparison.HalComparisonSource;
import io.wcm.caravan.hal.comparison.HalComparisonStrategy;
import io.wcm.caravan.hal.comparison.HalDifference;
import io.wcm.caravan.hal.comparison.impl.context.HalComparisonContextImpl;
import io.wcm.caravan.hal.comparison.impl.embedded.EmbeddedProcessing;
import io.wcm.caravan.hal.comparison.impl.embedded.EmbeddedProcessingImpl;
import io.wcm.caravan.hal.comparison.impl.links.LinkProcessing;
import io.wcm.caravan.hal.comparison.impl.links.LinkProcessingImpl;
import io.wcm.caravan.hal.comparison.impl.properties.PropertyDiffDetector;
import io.wcm.caravan.hal.comparison.impl.properties.PropertyProcessing;
import io.wcm.caravan.hal.resource.HalResource;
import rx.Observable;
import rx.Single;

/**
 * Implementation of the {@link HalComparison} OSGi service that glues the other implementation classes together,
 * loads the API entry points and starts the recursive comparison.
 * @see HalComparisonRecursionImpl
 * @see PropertyDiffDetector
 * @see EmbeddedProcessingImpl
 * @see LinkProcessingImpl
 */
@Component(service = HalComparison.class)
public class HalComparisonImpl implements HalComparison {

  @Override
  public Observable<HalDifference> compare(HalComparisonSource expected, HalComparisonSource actual, HalComparisonStrategy strategy) {

    HalComparisonRecursionImpl recursion = wireImplementationClasses(expected, actual, strategy);

    HalComparisonContextImpl context = createContextForEntryPoint(expected, actual, strategy);

    return loadEntryPointsAndStartRecursion(recursion, context, expected, actual);
  }

  private HalComparisonRecursionImpl wireImplementationClasses(HalComparisonSource expected, HalComparisonSource actual, HalComparisonStrategy strategy) {

    PropertyProcessing propertyProcessing = new PropertyDiffDetector();
    EmbeddedProcessing embeddedProcessing = new EmbeddedProcessingImpl(strategy);
    LinkProcessing linkProcessing = new LinkProcessingImpl(strategy);

    return new HalComparisonRecursionImpl(expected, actual, propertyProcessing, embeddedProcessing, linkProcessing);
  }

  private HalComparisonContextImpl createContextForEntryPoint(HalComparisonSource expected, HalComparisonSource actual, HalComparisonStrategy strategy) {

    String expectedUrl = expected.getEntryPointUrl();
    String actualUrl = actual.getEntryPointUrl();

    return new HalComparisonContextImpl(expectedUrl, actualUrl);
  }

  private Observable<HalDifference> loadEntryPointsAndStartRecursion(HalComparisonRecursionImpl recursion, HalComparisonContextImpl context,
      HalComparisonSource expected, HalComparisonSource actual) {

    Single<HalResource> expectedEntryPoint = expected.resolveLink(context.getExpectedUrl());
    Single<HalResource> actualEntryPoint = actual.resolveLink(context.getActualUrl());

    // wait until both entry points are loaded before starting the comparison
    return expectedEntryPoint.zipWith(actualEntryPoint, (e, a) -> recursion.compareRecursively(context, e, a))
        // flatten the Single<Observable<HalDifference>> returned by zipWith
        .flatMapObservable(r -> r);
  }
}