All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.pitest.dependency.DependencyExtractor Maven / Gradle / Ivy

/*
 * Copyright 2010 Henry Coles
 *
 * 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 org.pitest.dependency;

import static org.pitest.functional.prelude.Prelude.and;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.logging.Logger;

import org.objectweb.asm.ClassReader;
import org.pitest.bytecode.NullVisitor;
import org.pitest.classinfo.ClassByteArraySource;
import org.pitest.functional.FCollection;
import java.util.Optional;
import org.pitest.util.Functions;
import org.pitest.util.Log;

public class DependencyExtractor {
  private static final Logger        LOG = Log.getLogger();
  private final int                  depth;
  private final ClassByteArraySource classToBytes;

  public DependencyExtractor(final ClassByteArraySource classToBytes,
      final int depth) {
    this.depth = depth;
    this.classToBytes = classToBytes;
  }

  public Collection extractCallDependenciesForPackages(
      final String clazz, final Predicate targetPackages)
          throws IOException {
    final Set allDependencies = extractCallDependencies(clazz,
        new IgnoreCoreClasses());
    return FCollection.filter(allDependencies,
        and(asJVMNamePredicate(targetPackages), notSuppliedClass(clazz)));
  }

  private static Predicate notSuppliedClass(final String clazz) {
    return a -> !Functions.jvmClassToClassName().apply(a).equals(clazz);
  }

  private static Predicate asJVMNamePredicate(
      final Predicate predicate) {
    return a -> predicate.test(Functions.jvmClassToClassName().apply(a));
  }

  public Collection extractCallDependenciesForPackages(
      final String clazz, final Predicate targetPackages,
      final Predicate doNotTraverse) throws IOException {
    final Set allDependencies = extractCallDependencies(clazz,
        doNotTraverse);
    return FCollection.filter(allDependencies, targetPackages);
  }

  Set extractCallDependencies(final String clazz,
      final Predicate filter) throws IOException {

    return this
        .extractCallDependencies(clazz, new TreeSet(), filter, 0);
  }

  public int getMaxDistance() {
    return this.depth;
  }

  private Set extractCallDependencies(final String clazz,
      final TreeSet visited, final Predicate filter,
      final int currentDepth) throws IOException {

    final Map> classesToAccesses = groupDependenciesByClass(extractRelevantDependencies(
        clazz, filter));
    final Set dependencies = new HashSet<>(
        classesToAccesses.keySet());

    dependencies.removeAll(visited);
    visited.addAll(dependencies);

    if ((currentDepth < (this.depth - 1)) || (this.depth == 0)) {

      dependencies.addAll(examineChildDependencies(currentDepth, dependencies,
          visited, filter));

    }

    return dependencies;

  }

  private Set examineChildDependencies(final int currentDepth,
      final Set classes, final TreeSet visited,
      final Predicate filter) throws IOException {

    final Set deps = new HashSet<>();
    for (final String each : classes) {
      final Set childDependencies = extractCallDependencies(each,
          visited, filter, currentDepth + 1);
      deps.addAll(childDependencies);
    }
    return deps;
  }

  private Set extractRelevantDependencies(final String clazz,
      final Predicate filter) throws IOException {
    final List dependencies = extract(clazz, filter);
    final Set relevantDependencies = new TreeSet<>(
        equalDestinationComparator());
    FCollection.filter(dependencies, filter, relevantDependencies);
    return relevantDependencies;
  }

  private static Comparator equalDestinationComparator() {
    return (o1, o2) -> o1.getDest().compareTo(o2.getDest());
  }

  private List extract(final String clazz,
      final Predicate filter) throws IOException {
    final Optional bytes = this.classToBytes.getBytes(clazz);
    if (!bytes.isPresent()) {
      LOG.warning("No bytes found for " + clazz);
      return Collections.emptyList();
    }
    final ClassReader reader = new ClassReader(bytes.get());
    final List dependencies = new ArrayList<>();

    final Consumer se = constructCollectingSideEffectForVisitor(
        dependencies, and(nameIsEqual(clazz).negate(), filter));
    final DependencyClassVisitor dcv = new DependencyClassVisitor(
        new NullVisitor(), se);
    reader.accept(dcv, ClassReader.EXPAND_FRAMES);
    return dependencies;
  }

  private Map> groupDependenciesByClass(
      final Set relevantDependencies) {
    final List sortedByClass = new ArrayList<>(
        relevantDependencies.size());
    Collections.sort(sortedByClass, classNameComparator());

    return FCollection.fold(addDependenciesToMap(),
        new HashMap>(), relevantDependencies);

  }

  private static BiFunction>, DependencyAccess, Map>> addDependenciesToMap() {

    return (map, access) -> {

    List list = map.get(access.getDest().getOwner());
    if (list == null) {
    list = new ArrayList<>();
    }
    list.add(access);
    map.put(access.getDest().getOwner(), list);
    return map;
   };
  }

  private static Comparator classNameComparator() {
    return (lhs, rhs) -> lhs.getDest().getOwner().compareTo(rhs.getDest().getOwner());
  }

  private static Predicate nameIsEqual(final String clazz) {
    return a -> a.getDest().getOwner().equals(clazz);
  }

  private static Consumer constructCollectingSideEffectForVisitor(
      final List dependencies,
      final Predicate predicate) {
    final Consumer se = a -> {
      if (predicate.test(a)) {
        dependencies.add(a);
      }
    };
    return se;
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy