<commit_before>/*
 * Copyright 2011 ZXing authors
 *
 * 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.zxing.client.j2se;

import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.LuminanceSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.NotFoundException;
import com.google.zxing.Result;
import com.google.zxing.ResultPoint;
import com.google.zxing.client.result.ParsedResult;
import com.google.zxing.client.result.ResultParser;
import com.google.zxing.common.BitArray;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.multi.GenericMultipleBarcodeReader;
import com.google.zxing.multi.MultipleBarcodeReader;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.Callable;

/**
 * One of a pool of threads which pulls images off the Inputs queue and decodes them in parallel.
 *
 * @see CommandLineRunner
 */
final class DecodeWorker implements Callable<Integer> {

  private static final int RED = 0xFFFF0000;
  private static final int BLACK = 0xFF000000;
  private static final int WHITE = 0xFFFFFFFF;

  private final DecoderConfig config;
  private final Queue<URI> inputs;
  private final Map<DecodeHintType,?> hints;

  DecodeWorker(DecoderConfig config, Queue<URI> inputs) {
    this.config = config;
    this.inputs = inputs;
    hints = config.buildHints();
  }

  @Override
  public Integer call() throws IOException {
    int successful = 0;
    for (URI input; (input = inputs.poll()) != null;) {
      Result[] results = decode(input, hints);
      if (results != null) {
        successful++;
        if (config.dumpResults) {
          dumpResult(input, results);
        }
      }
    }
    return successful;
  }

  private static Path buildOutputPath(URI input, String suffix) throws IOException {
    Path outDir;
    String inputFileName;
    if ("file".equals(input.getScheme())) {
      Path inputPath = Paths.get(input);
      outDir = inputPath.getParent();
      inputFileName = inputPath.getFileName().toString();
    } else {
      outDir = Paths.get(".").toRealPath();
      String[] pathElements = input.getPath().split("/");
      inputFileName = pathElements[pathElements.length - 1];
    }

    // Replace/add extension
    int pos = inputFileName.lastIndexOf('.');
    if (pos > 0) {
      inputFileName = inputFileName.substring(0, pos) + suffix;
    } else {
      inputFileName += suffix;
    }

    return outDir.resolve(inputFileName);
  }

  private static void dumpResult(URI input, Result... results) throws IOException {
    Collection<String> resultTexts = new ArrayList<>();
    for (Result result : results) {
      resultTexts.add(result.getText());
    }
    Files.write(buildOutputPath(input, ".txt"), resultTexts, StandardCharsets.UTF_8);
  }

  private Result[] decode(URI uri, Map<DecodeHintType,?> hints) throws IOException {
    BufferedImage image = ImageReader.readImage(uri);

    LuminanceSource source;
    if (config.crop == null) {
      source = new BufferedImageLuminanceSource(image);
    } else {
      List<Integer> crop = config.crop;
      source = new BufferedImageLuminanceSource(
          image, crop.get(0), crop.get(1), crop.get(2), crop.get(3));
    }

    BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
    if (config.dumpBlackPoint) {
      dumpBlackPoint(uri, image, bitmap);
    }

    MultiFormatReader multiFormatReader = new MultiFormatReader();
    Result[] results;
    try {
      if (config.multi) {
        MultipleBarcodeReader reader = new GenericMultipleBarcodeReader(multiFormatReader);
        results = reader.decodeMultiple(bitmap, hints);
      } else {
        results = new Result[]{multiFormatReader.decode(bitmap, hints)};
      }
    } catch (NotFoundException ignored) {
      System.out.println(uri + ": No barcode found");
      return null;
    }

    if (config.brief) {
      System.out.println(uri + ": Success");
    } else {
      for (Result result : results) {
        ParsedResult parsedResult = ResultParser.parseResult(result);
        System.out.println(uri +
            " (format: " + result.getBarcodeFormat() +
            ", type: " + parsedResult.getType() + "):\n" +
            "Raw result:\n" +
            result.getText() + "\n" +
            "Parsed result:\n" +
            parsedResult.getDisplayResult());
        System.out.println("Found " + result.getResultPoints().length + " result points.");
        for (int i = 0; i < result.getResultPoints().length; i++) {
          ResultPoint rp = result.getResultPoints()[i];
          System.out.println("  Point " + i + ": (" + rp.getX() + ',' + rp.getY() + ')');
        }
      }
    }

    return results;
  }

  /**
   * Writes out a single PNG which is three times the width of the input image, containing from left
   * to right: the original image, the row sampling monochrome version, and the 2D sampling<commit_msg>Use StringWriter to prevent race condition in println<commit_after>/*
 * Copyright 2011 ZXing authors
 *
 * 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.zxing.client.j2se;

import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.LuminanceSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.NotFoundException;
import com.google.zxing.Result;
import com.google.zxing.ResultPoint;
import com.google.zxing.client.result.ParsedResult;
import com.google.zxing.client.result.ResultParser;
import com.google.zxing.common.BitArray;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.multi.GenericMultipleBarcodeReader;
import com.google.zxing.multi.MultipleBarcodeReader;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.StringWriter;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.Callable;

/**
 * One of a pool of threads which pulls images off the Inputs queue and decodes them in parallel.
 *
 * @see CommandLineRunner
 */
final class DecodeWorker implements Callable<Integer> {

  private static final int RED = 0xFFFF0000;
  private static final int BLACK = 0xFF000000;
  private static final int WHITE = 0xFFFFFFFF;

  private final DecoderConfig config;
  private final Queue<URI> inputs;
  private final Map<DecodeHintType,?> hints;

  DecodeWorker(DecoderConfig config, Queue<URI> inputs) {
    this.config = config;
    this.inputs = inputs;
    hints = config.buildHints();
  }

  @Override
  public Integer call() throws IOException {
    int successful = 0;
    for (URI input; (input = inputs.poll()) != null;) {
      Result[] results = decode(input, hints);
      if (results != null) {
        successful++;
        if (config.dumpResults) {
          dumpResult(input, results);
        }
      }
    }
    return successful;
  }

  private static Path buildOutputPath(URI input, String suffix) throws IOException {
    Path outDir;
    String inputFileName;
    if ("file".equals(input.getScheme())) {
      Path inputPath = Paths.get(input);
      outDir = inputPath.getParent();
      inputFileName = inputPath.getFileName().toString();
    } else {
      outDir = Paths.get(".").toRealPath();
      String[] pathElements = input.getPath().split("/");
      inputFileName = pathElements[pathElements.length - 1];
    }

    // Replace/add extension
    int pos = inputFileName.lastIndexOf('.');
    if (pos > 0) {
      inputFileName = inputFileName.substring(0, pos) + suffix;
    } else {
      inputFileName += suffix;
    }

    return outDir.resolve(inputFileName);
  }

  private static void dumpResult(URI input, Result... results) throws IOException {
    Collection<String> resultTexts = new ArrayList<>();
    for (Result result : results) {
      resultTexts.add(result.getText());
    }
    Files.write(buildOutputPath(input, ".txt"), resultTexts, StandardCharsets.UTF_8);
  }

  private Result[] decode(URI uri, Map<DecodeHintType,?> hints) throws IOException {
    BufferedImage image = ImageReader.readImage(uri);

    LuminanceSource source;
    if (config.crop == null) {
      source = new BufferedImageLuminanceSource(image);
    } else {
      List<Integer> crop = config.crop;
      source = new BufferedImageLuminanceSource(
          image, crop.get(0), crop.get(1), crop.get(2), crop.get(3));
    }

    BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
    if (config.dumpBlackPoint) {
      dumpBlackPoint(uri, image, bitmap);
    }

    MultiFormatReader multiFormatReader = new MultiFormatReader();
    Result[] results;
    try {
      if (config.multi) {
        MultipleBarcodeReader reader = new GenericMultipleBarcodeReader(multiFormatReader);
        results = reader.decodeMultiple(bitmap, hints);
      } else {
        results = new Result[]{multiFormatReader.decode(bitmap, hints)};
      }
    } catch (NotFoundException ignored) {
      System.out.println(uri + ": No barcode found");
      return null;
    }

    if (config.brief) {
      System.out.println(uri + ": Success");
    } else {
      StringWriter output = new StringWriter();
      for (int resultIndex = 0; resultIndex < results.length; resultIndex++) {
        Result result = results[resultIndex];
        ParsedResult parsedResult = ResultParser.parseResult(result);
        output.write(uri +
            " (format: " + result.getBarcodeFormat() +
            ", type: " + parsedResult.getType() + "):\n" +
            "Raw result:\n" +
            result.getText() + "\n" +
            "Parsed result:\n" +
            parsedResult.getDisplayResult() + "\n");
        output.write("Found " + result.getResultPoints().length + " result points.\n");
        for (int pointIndex = 0; pointIndex < result.getResultPoints().length; pointIndex++) {
          ResultPoint rp = result.getResultPoints()[pointIndex];
          output.write("  Point " + pointIndex + ": (" + rp.getX() + ',' + rp.getY() + ')');
          if (pointIndex != result.getResultPoints().length - 1) {
            output.write('\n');
          }
        }
        if (resultIndex != results.length - 1) {
          output.write('\n');
        }
      }
      System.out.println(output.toString());
    }

    return results;
  }

  /**
   * Writes out a single PNG which is three times the width of the input image, containing from left
   * to right: the original image, the row sampling monochrome version, and the 2D sampling
{'fraction_non_alphanumeric': 0.11541929666366095, 'fraction_numerical': 0.005656201327977703, 'mean_word_length': 3.250871080139373, 'pattern_counts': {'":': 6, '<': 22, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5832031', 'n_tokens_mistral': 3600, 'n_tokens_neox': 3518, 'n_words': 942}