gt:                     "-t", tileIndex + "");
context: package org.esa.beam.dataio.s2;
  
  import com.bc.ceres.core.Assert;
  import com.bc.ceres.glevel.MultiLevelModel;
  import org.apache.commons.lang.SystemUtils;
  import org.esa.beam.jai.ResolutionLevel;
  import org.esa.beam.jai.SingleBandedOpImage;
  import org.esa.beam.util.ImageUtils;
  import org.esa.beam.util.io.FileUtils;
  import org.esa.beam.util.logging.BeamLogManager;
  
  import javax.imageio.stream.FileImageInputStream;
  import javax.imageio.stream.ImageInputStream;
  import javax.media.jai.*;
  import javax.media.jai.operator.BorderDescriptor;
  import javax.media.jai.operator.ConstantDescriptor;
  import javax.media.jai.operator.CropDescriptor;
  import javax.media.jai.operator.ScaleDescriptor;
  import java.awt.*;
  import java.awt.image.DataBufferUShort;
  import java.awt.image.SampleModel;
  import java.awt.image.WritableRaster;
  import java.io.File;
  import java.io.IOException;
  import java.util.Arrays;
  import java.util.HashMap;
  import java.util.Map;
  import java.util.logging.Logger;
  
  import static org.esa.beam.dataio.s2.S2Config.L1C_TILE_LAYOUTS;
  
  // todo - better log problems during read process, see {@report "Problem detected..."} code marks
  
  /**
   * @author Norman Fomferra
   */
  class L1cTileOpImage extends SingleBandedOpImage {
  
      private static class Jp2File {
          File file;
          String header;
          ImageInputStream stream;
          long dataPos;
          int width;
          int height;
      }
  
      protected final Logger logger;
      private final File imageFile;
      private final File cacheDir;
      private final L1cTileLayout l1cTileLayout;
      private Map<File, Jp2File> openFiles;
      private Map<File, Object> locks;
  
      static PlanarImage create(File imageFile,
                                File cacheDir,
                                Point imagePos,
                                L1cTileLayout l1cTileLayout,
                                MultiLevelModel imageModel,
                                S2SpatialResolution spatialResolution,
                                int level) {
  
          Assert.notNull(cacheDir, "cacheDir");
          Assert.notNull(l1cTileLayout, "imageLayout");
          Assert.notNull(imageModel, "imageModel");
          Assert.notNull(spatialResolution, "spatialResolution");
  
          if (imageFile != null) {
              PlanarImage opImage = new L1cTileOpImage(imageFile, cacheDir, imagePos, l1cTileLayout, imageModel, level);
  
              BeamLogManager.getSystemLogger().fine("The size before: " + opImage.getWidth() + ", " + opImage.getHeight());
  
              // todo skipping scale
  
              if (spatialResolution != S2SpatialResolution.R10M) {
                  PlanarImage scaled = createScaledImage(opImage, spatialResolution, level);
                  BeamLogManager.getSystemLogger().fine("The size after: " + scaled.getWidth() + ", " + scaled.getHeight());
  
                  return scaled;
              }
  
              return opImage;
          } else {
              int targetWidth = getSizeAtResolutionLevel(L1C_TILE_LAYOUTS[0].width, level);
              int targetHeight = getSizeAtResolutionLevel(L1C_TILE_LAYOUTS[0].height, level);
              Dimension targetTileDim = getTileDimAtResolutionLevel(L1C_TILE_LAYOUTS[0].tileWidth, L1C_TILE_LAYOUTS[0].tileHeight, level);
              SampleModel sampleModel = ImageUtils.createSingleBandedSampleModel(S2Config.SAMPLE_DATA_BUFFER_TYPE, targetWidth, targetHeight);
              ImageLayout imageLayout = new ImageLayout(0, 0, targetWidth, targetHeight, 0, 0, targetTileDim.width, targetTileDim.height, sampleModel, null);
              return ConstantDescriptor.create((float) imageLayout.getWidth(null),
                                               (float) imageLayout.getHeight(null),
                                               new Short[]{S2Config.FILL_CODE_NO_FILE},
                                               new RenderingHints(JAI.KEY_IMAGE_LAYOUT, imageLayout));
          }
      }
  
      static PlanarImage createScaledImage(PlanarImage sourceImage, S2SpatialResolution resolution, int level) {
          BeamLogManager.getSystemLogger().fine("Asking for scaled image: " + resolution.toString());
  
          int sourceWidth = sourceImage.getWidth();
          int sourceHeight = sourceImage.getHeight();
          int targetWidth = getSizeAtResolutionLevel(L1C_TILE_LAYOUTS[0].width, level);
          int targetHeight = getSizeAtResolutionLevel(L1C_TILE_LAYOUTS[0].height, level);
          float scaleX = resolution.resolution / (float) S2SpatialResolution.R10M.resolution;
          float scaleY = resolution.resolution / (float) S2SpatialResolution.R10M.resolution;
          final Dimension tileDim = getTileDim(targetWidth, targetHeight);
          ImageLayout imageLayout = new ImageLayout();
          imageLayout.setTileWidth(tileDim.width);
          imageLayout.setTileHeight(tileDim.height);
          BorderExtender borderExtender = BorderExtender.createInstance(BorderExtender.BORDER_ZERO);
          RenderingHints renderingHints = new RenderingHints(JAI.KEY_BORDER_EXTENDER,
                                                             borderExtender);
          renderingHints.put(JAI.KEY_IMAGE_LAYOUT, imageLayout);
          RenderedOp scaledImage = ScaleDescriptor.create(sourceImage,
                                                          scaleX,
                                                          scaleY,
                                                          sourceImage.getMinX() - sourceImage.getMinX() * scaleX,
                                                          sourceImage.getMinY() - sourceImage.getMinY() * scaleY,
                                                          Interpolation.getInstance(Interpolation.INTERP_NEAREST),
                                                          renderingHints);
          if (scaledImage.getWidth() == targetWidth && scaledImage.getHeight() == targetHeight) {
              return scaledImage;
          }
          else if (scaledImage.getWidth() >= targetWidth && scaledImage.getHeight() >= targetHeight) {
              return CropDescriptor.create(scaledImage,
                                           (float) sourceImage.getMinX(),
                                           (float) sourceImage.getMinY(),
                                           (float) targetWidth,
                                           (float) targetHeight,
                                           null);
          }
          else if (scaledImage.getWidth() <= targetWidth && scaledImage.getHeight() <= targetHeight) {
              int rightPad = targetWidth - scaledImage.getWidth();
              int bottomPad = targetHeight - scaledImage.getHeight();
              return BorderDescriptor.create(scaledImage, 0, rightPad, 0, bottomPad, borderExtender, null);
          } else {
              throw new IllegalStateException();
          }
      }
  
      L1cTileOpImage(File imageFile,
                     File cacheDir,
                     Point imagePos,
                     L1cTileLayout l1cTileLayout,
                     MultiLevelModel imageModel,
                     int level) {
          super(S2Config.SAMPLE_DATA_BUFFER_TYPE,
                imagePos,
                l1cTileLayout.width,
                l1cTileLayout.height,
                getTileDimAtResolutionLevel(l1cTileLayout.tileWidth, l1cTileLayout.tileHeight, level),
                null,
                ResolutionLevel.create(imageModel, level));
  
          Assert.notNull(imageFile, "imageFile");
          Assert.notNull(cacheDir, "cacheDir");
          Assert.notNull(l1cTileLayout, "l1cTileLayout");
          Assert.notNull(imageModel, "imageModel");
  
          this.imageFile = imageFile;
          this.cacheDir = cacheDir;
          this.l1cTileLayout = l1cTileLayout;
          this.openFiles = new HashMap<File, Jp2File>();
          this.locks = new HashMap<File, Object>();
          this.logger = BeamLogManager.getSystemLogger();
      }
  
      @Override
      protected synchronized void computeRect(PlanarImage[] sources, WritableRaster dest, Rectangle destRect) {
          final DataBufferUShort dataBuffer = (DataBufferUShort) dest.getDataBuffer();
          final short[] tileData = dataBuffer.getData();
  
          final int tileWidth = this.getTileWidth();
          final int tileHeight = this.getTileHeight();
          final int tileX = destRect.x / tileWidth;
          final int tileY = destRect.y / tileHeight;
  
          if (tileWidth * tileHeight != tileData.length) {
              throw new IllegalStateException(String.format("tileWidth (=%d) * tileHeight (=%d) != tileData.length (=%d)",
                                                            tileWidth, tileHeight, tileData.length));
          }
  
          final Dimension jp2TileDim = getDimAtResolutionLevel(l1cTileLayout.tileWidth, l1cTileLayout.tileHeight, getLevel());
  
          final int jp2TileWidth = jp2TileDim.width;
          final int jp2TileHeight = jp2TileDim.height;
          final int jp2TileX = destRect.x / jp2TileWidth;
          final int jp2TileY = destRect.y / jp2TileHeight;
  
          // Res - Img Size - Tile W
          //  0  -  10960   -  4096
          //  1  -   5480   -  2048
          //  2  -   2740   -  1024
          //  3  -   1370   -   512
          //  4  -    685   -   256
          //  5  -    343   -   128
  
          File outputFile = null;
  
          try {
              outputFile = new File(cacheDir,
                                    FileUtils.exchangeExtension(imageFile.getName(),
                                                                String.format("_R%d_TX%d_TY%d.pgx",
                                                                              getLevel(), jp2TileX, jp2TileY)));
          } catch (Exception e) {
              e.printStackTrace();
          }
          final File outputFile0 = getFirstComponentOutputFile(outputFile);
  
  
          // todo - outputFile0 may have already been created, although 'opj_decompress' has not finished execution.
          //        This may be the reason for party filled tiles, that sometimes occur
          if (!outputFile0.exists())
          {
              logger.fine(String.format("Jp2ExeImage.readTileData(): recomputing res=%d, tile=(%d,%d)\n", getLevel(), jp2TileX, jp2TileY));
  
              try {
                  decompressTile(outputFile, jp2TileX, jp2TileY);
              } catch (IOException e) {
                  logger.severe("opj_decompress process failed: " + e.getMessage());
                  if (outputFile0.exists() && !outputFile0.delete()) {
                      logger.severe("Failed to delete file: " + outputFile0.getAbsolutePath());
                  }
              }
              if (!outputFile0.exists()) {
                  logger.fine("No output file generated");
                  Arrays.fill(tileData, S2Config.FILL_CODE_NO_FILE);
                  return;
              }
          }
  
          try {
              logger.fine(String.format("Jp2ExeImage.readTileData(): reading res=%d, tile=(%d,%d)\n", getLevel(), jp2TileX, jp2TileY));
              readTileData(outputFile0, tileX, tileY, tileWidth, tileHeight, jp2TileX, jp2TileY, jp2TileWidth, jp2TileHeight, tileData, destRect);
          } catch (IOException e) {
              logger.severe("Failed to read uncompressed tile data");
          }
      }
  
      private File getFirstComponentOutputFile(File outputFile) {
          return FileUtils.exchangeExtension(outputFile, "_0.pgx");
      }
  
      private void decompressTile(final File outputFile, int jp2TileX, int jp2TileY) throws IOException {
          final int tileIndex = l1cTileLayout.numXTiles * jp2TileY + jp2TileX;
  
  
          ProcessBuilder builder = null;
          if(SystemUtils.IS_OS_WINDOWS)
          {
              String inputFileName = Utils.GetIterativeShortPathName(imageFile.getPath());
              String outputFileName = outputFile.getPath();
  
              if(inputFileName.length() == 0)
              {
                  inputFileName = imageFile.getPath();
              }
  
              builder = new ProcessBuilder(S2Config.OPJ_DECOMPRESSOR_EXE,
                      "-i", inputFileName,
                      "-o", outputFileName,
                      "-r", getLevel() + "",
                      "-t", tileIndex + "");
          }
          else
          {
              builder = new ProcessBuilder(S2Config.OPJ_DECOMPRESSOR_EXE,
                      "-i", imageFile.getPath(),
                      "-o", outputFile.getPath(),
                      "-r", getLevel() + "",
  