// Copyright 2016 The Bazel Authors. All Rights Reserved.
//
// 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 com.google.testing.coverage;

import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.jacoco.core.analysis.Analyzer;
import org.jacoco.core.analysis.IClassCoverage;
import org.jacoco.core.analysis.ICoverageVisitor;
import org.jacoco.core.data.ExecutionData;
import org.jacoco.core.data.ExecutionDataStore;
import org.jacoco.core.internal.data.CRC64;
import org.jacoco.core.internal.flow.ClassProbesAdapter;
import org.objectweb.asm.ClassReader;

/**
 * Analyzer that process the branch coverage detail information.
 *
 * <p>Reuse the Analyzer class from Jacoco to avoid duplicating the content detection logic.
 * Override the main {@code Analyzer.analyzeClass} method which does the main work.
 */
public class BranchDetailAnalyzer extends Analyzer {

  private final ExecutionDataStore executionData;
  private final Map<String, BranchCoverageDetail> branchDetails;

  public BranchDetailAnalyzer(final ExecutionDataStore executionData) {
    super(executionData, new ICoverageVisitor() {
      @Override
      public void visitCoverage(IClassCoverage coverage) {
      }
    });

    this.executionData = executionData;
    this.branchDetails = new TreeMap<String, BranchCoverageDetail>();
  }

  @Override
  public void analyzeClass(final ClassReader reader) {
    final Map<Integer, BranchExp> lineToBranchExp = mapProbes(reader);

    long classid = CRC64.classId(reader.b);
    ExecutionData classData = executionData.get(classid);
    if (classData == null) {
      return;
    }
    boolean[] probes = classData.getProbes();

    BranchCoverageDetail detail = new BranchCoverageDetail();

    for (Map.Entry<Integer, BranchExp> entry : lineToBranchExp.entrySet()) {
      int line = entry.getKey();
      BranchExp branchExp = entry.getValue();
      List<CovExp> branches = branchExp.getBranches();

      detail.setBranches(line, branches.size());
      for (int branchIdx = 0; branchIdx < branches.size(); branchIdx++) {
        if (branches.get(branchIdx).eval(probes)) {
          detail.setTakenBit(line, branchIdx);
        }
      }
    }
    if (detail.linesWithBranches().size() > 0) {
      branchDetails.put(reader.getClassName(), detail);
    }
  }

  // Generate the line to probeExp map so that we can evaluate the coverage.
  private Map<Integer, BranchExp> mapProbes(final ClassReader reader) {
    final ClassProbesMapper mapper = new ClassProbesMapper();
    final ClassProbesAdapter adapter = new ClassProbesAdapter(mapper, false);
    reader.accept(adapter, 0);

    return mapper.result();
  }

  public Map<String, BranchCoverageDetail> getBranchDetails() {
    return branchDetails;
  }
}

@@ -647,16 +647,72 @@
 erage;%0A%0A
+import java.io.IOException;%0Aimport java.io.InputStream;%0A
 import j
@@ -1005,16 +1005,62 @@
 aStore;%0A
+import org.jacoco.core.internal.InputStreams;%0A
 import o
@@ -1696,16 +1696,25 @@
   super(
+%0A        
 executio
@@ -1719,16 +1719,24 @@
 ionData,
+%0A       
  new ICo
@@ -1759,16 +1759,20 @@
 %7B%0A      
+   
+ 
 @Overrid
@@ -1773,16 +1773,20 @@
 verride%0A
+    
       pu
@@ -1839,25 +1839,22 @@
 e) %7B
+%7D
 %0A    
-  %7D%0A
     %7D);%0A
 %0A   
@@ -1849,17 +1849,16 @@
     %7D);%0A
-%0A
     this
@@ -1964,16 +1964,610 @@
 );%0A  %7D%0A%0A
+  // Override all analyzeClass methods.%0A  @Override%0A  public void analyzeClass(final InputStream input, final String location) throws IOException %7B%0A    final byte%5B%5D buffer;%0A    try %7B%0A      buffer = InputStreams.readFully(input);%0A    %7D catch (final IOException e) %7B%0A      throw analyzerError(location, e);%0A    %7D%0A    analyzeClass(buffer, location);%0A  %7D%0A%0A  @Override%0A  public void analyzeClass(final byte%5B%5D buffer, final String location) throws IOException %7B%0A    try %7B%0A      analyzeClass(buffer);%0A    %7D catch (final RuntimeException cause) %7B%0A      throw analyzerError(location, cause);%0A    %7D%0A  %7D%0A%0A
   @Overr
@@ -3528,24 +3528,389 @@
 %0A    %7D%0A  %7D%0A%0A
+  private void analyzeClass(final byte%5B%5D source) %7B%0A    final ClassReader reader = new ClassReader(source);%0A    analyzeClass(reader);%0A  %7D%0A%0A  private IOException analyzerError(final String location, final Exception cause) %7B%0A    final IOException ex = new IOException(String.format(%22Error while analyzing %25s.%22, location));%0A    ex.initCause(cause);%0A    return ex;%0A  %7D%0A%0A
   // Generat

Update BranchDetailAnalyzer.