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

org.sonar.server.computation.component.VisitorsCrawler Maven / Gradle / Ivy

There is a newer version: 7.0
Show newest version
/*
 * SonarQube
 * Copyright (C) 2009-2016 SonarSource SA
 * mailto:contact AT sonarsource DOT com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package org.sonar.server.computation.component;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import java.util.List;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.sonar.api.utils.log.Loggers;
import org.sonar.core.util.logs.Profiler;

import static com.google.common.collect.FluentIterable.from;
import static com.google.common.collect.Iterables.concat;
import static java.util.Objects.requireNonNull;

/**
 * This crawler make any number of {@link TypeAwareVisitor} or {@link PathAwareVisitor} defined in a list visit a component tree, component per component, in the order of the list
 */
public class VisitorsCrawler implements ComponentCrawler {

  private final Map visitorCumulativeDurations;
  private final List preOrderVisitorWrappers;
  private final List postOrderVisitorWrappers;

  public VisitorsCrawler(Iterable visitors) {
    List visitorWrappers = from(visitors).transform(ToVisitorWrapper.INSTANCE).toList();
    this.preOrderVisitorWrappers = from(visitorWrappers).filter(MathPreOrderVisitor.INSTANCE).toList();
    this.postOrderVisitorWrappers = from(visitorWrappers).filter(MatchPostOrderVisitor.INSTANCE).toList();
    this.visitorCumulativeDurations = from(visitors).toMap(VisitorWrapperToInitialDuration.INSTANCE);
  }

  public Map getCumulativeDurations() {
    return ImmutableMap.copyOf(
      Maps.transformValues(this.visitorCumulativeDurations, VisitorDurationToDuration.INSTANCE)
      );
  }

  @Override
  public void visit(final Component component) {
    try {
      visitImpl(component);
    } catch (RuntimeException e) {
      VisitException.rethrowOrWrap(
        e,
        "Visit of Component {key=%s,type=%s} failed",
        component.getKey(), component.getType());
    }
  }

  private void visitImpl(Component component) {
    MatchVisitorMaxDepth visitorMaxDepth = MatchVisitorMaxDepth.forComponent(component);
    List preOrderVisitorWrappersToExecute = from(preOrderVisitorWrappers).filter(visitorMaxDepth).toList();
    List postOrderVisitorWrappersToExecute = from(postOrderVisitorWrappers).filter(visitorMaxDepth).toList();
    if (preOrderVisitorWrappersToExecute.isEmpty() && postOrderVisitorWrappersToExecute.isEmpty()) {
      return;
    }

    for (VisitorWrapper visitorWrapper : concat(preOrderVisitorWrappers, postOrderVisitorWrappers)) {
      visitorWrapper.beforeComponent(component);
    }

    for (VisitorWrapper visitorWrapper : preOrderVisitorWrappersToExecute) {
      visitNode(component, visitorWrapper);
    }

    visitChildren(component);

    for (VisitorWrapper visitorWrapper : postOrderVisitorWrappersToExecute) {
      visitNode(component, visitorWrapper);
    }

    for (VisitorWrapper visitorWrapper : concat(preOrderVisitorWrappersToExecute, postOrderVisitorWrappersToExecute)) {
      visitorWrapper.afterComponent(component);
    }
  }

  private void visitChildren(Component component) {
    for (Component child : component.getChildren()) {
      visit(child);
    }
  }

  private void visitNode(Component component, VisitorWrapper visitor) {
    Profiler profiler = Profiler.create(Loggers.get(visitor.getWrappedVisitor().getClass()))
      .startTrace("Visiting component {}", component.getKey());
    visitor.visitAny(component);
    switch (component.getType()) {
      case PROJECT:
        visitor.visitProject(component);
        break;
      case MODULE:
        visitor.visitModule(component);
        break;
      case DIRECTORY:
        visitor.visitDirectory(component);
        break;
      case FILE:
        visitor.visitFile(component);
        break;
      case VIEW:
        visitor.visitView(component);
        break;
      case SUBVIEW:
        visitor.visitSubView(component);
        break;
      case PROJECT_VIEW:
        visitor.visitProjectView(component);
        break;
      default:
        throw new IllegalStateException(String.format("Unknown type %s", component.getType().name()));
    }
    long duration = profiler.stopTrace();
    incrementDuration(visitor, duration);
  }

  private void incrementDuration(VisitorWrapper visitorWrapper, long duration) {
    visitorCumulativeDurations.get(visitorWrapper.getWrappedVisitor()).increment(duration);
  }

  private enum ToVisitorWrapper implements Function {
    INSTANCE;

    @Override
    public VisitorWrapper apply(@Nonnull ComponentVisitor componentVisitor) {
      if (componentVisitor instanceof TypeAwareVisitor) {
        return new TypeAwareVisitorWrapper((TypeAwareVisitor) componentVisitor);
      } else if (componentVisitor instanceof PathAwareVisitor) {
        return new PathAwareVisitorWrapper((PathAwareVisitor) componentVisitor);
      } else {
        throw new IllegalArgumentException("Only TypeAwareVisitor and PathAwareVisitor can be used");
      }
    }
  }

  private static class MatchVisitorMaxDepth implements Predicate {
    private static final Map INSTANCES = buildInstances();

    private static Map buildInstances() {
      ImmutableMap.Builder builder = ImmutableMap.builder();
      for (Component.Type type : Component.Type.values()) {
        builder.put(type, new MatchVisitorMaxDepth(type));
      }
      return builder.build();
    }

    private final Component.Type type;

    private MatchVisitorMaxDepth(Component.Type type) {
      this.type = requireNonNull(type);
    }

    public static MatchVisitorMaxDepth forComponent(Component component) {
      return INSTANCES.get(component.getType());
    }

    @Override
    public boolean apply(@Nonnull VisitorWrapper visitorWrapper) {
      CrawlerDepthLimit maxDepth = visitorWrapper.getMaxDepth();
      return maxDepth.isSameAs(type) || maxDepth.isDeeperThan(type);
    }
  }

  private enum MathPreOrderVisitor implements Predicate {
    INSTANCE;

    @Override
    public boolean apply(@Nonnull VisitorWrapper visitorWrapper) {
      return visitorWrapper.getOrder() == ComponentVisitor.Order.PRE_ORDER;
    }
  }

  private enum MatchPostOrderVisitor implements Predicate {
    INSTANCE;

    @Override
    public boolean apply(@Nonnull VisitorWrapper visitorWrapper) {
      return visitorWrapper.getOrder() == ComponentVisitor.Order.POST_ORDER;
    }
  }

  private static final class VisitorDuration {
    private long duration = 0;

    public void increment(long duration) {
      this.duration += duration;
    }

    public long getDuration() {
      return duration;
    }
  }

  private enum VisitorWrapperToInitialDuration implements Function {
    INSTANCE;

    @Override
    @Nonnull
    public VisitorDuration apply(@Nonnull ComponentVisitor visitorWrapper) {
      return new VisitorDuration();
    }
  }

  private enum VisitorDurationToDuration implements Function {
    INSTANCE;

    @Nullable
    @Override
    public Long apply(VisitorDuration input) {
      return input.getDuration();
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy