gt:             dateStr = dateStr.substring(0, dateStr.length() - 3).trim();
context: /*
   * Copyright (C) 2013 by Array Systems Computing Inc. http://www.array.ca
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU 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 General Public License for
   * more details.
   *
   * You should have received a copy of the GNU General Public License along
   * with this program; if not, see http://www.gnu.org/licenses/
   */
  package org.esa.nest.dataio.netcdf;
  
  import org.esa.beam.framework.datamodel.*;
  import org.esa.beam.framework.dataop.maptransf.*;
  import org.esa.beam.util.logging.BeamLogManager;
  import org.esa.nest.datamodel.AbstractMetadata;
  import ucar.ma2.Array;
  import ucar.ma2.DataType;
  import ucar.ma2.Index;
  import ucar.nc2.Attribute;
  import ucar.nc2.Dimension;
  import ucar.nc2.Group;
  import ucar.nc2.Variable;
  
  import java.io.IOException;
  import java.text.ParseException;
  import java.util.*;
  
  /**
   * Provides some NetCDF related utility methods.
   */
  public class NetCDFUtils {
  
      public static Band createBand(final Variable variable, final int rasterWidth, final int rasterHeight) {
          final NcAttributeMap attMap = NcAttributeMap.create(variable);
          final Band band = new Band(variable.getShortName(),
                                     getRasterDataType(variable),
                                     rasterWidth,
                                     rasterHeight);
          band.setDescription(getDescription(variable, attMap));
          band.setUnit(getUnit(variable, attMap));
          band.setScalingFactor(getScalingFactor(attMap));
          band.setScalingOffset(getAddOffset(attMap));
  
          final Number noDataValue = getNoDataValue(attMap);
          if (noDataValue != null) {
              band.setNoDataValue(noDataValue.doubleValue());
              band.setNoDataValueUsed(true);
          }
          return band;
      }
  
      public static TiePointGrid createTiePointGrid(final Variable variable, final int gridWidth, final int gridHeight,
                                                    final int sceneWidth, final int sceneHeight) throws IOException {
          final NcAttributeMap attMap = NcAttributeMap.create(variable);
  
          final float subSamplingX = (float)sceneWidth / (float)(gridWidth - 1);
          final float subSamplingY = (float)sceneHeight / (float)(gridHeight - 1);
  
          final Array data = variable.read();
          final float[] dataArray = new float[(int)data.getSize()]; //(float[])data.copyTo1DJavaArray();
          for(int i = 0; i < data.getSize(); ++i) {
              dataArray[i] = data.getFloat(i);
          }
          
          final TiePointGrid tpg = new TiePointGrid(variable.getShortName(), gridWidth, gridHeight, 0, 0,
                  subSamplingX, subSamplingY, dataArray);
  
          tpg.setDescription(getDescription(variable, attMap));
          tpg.setUnit(getUnit(variable, attMap));
          tpg.setScalingFactor(getScalingFactor(attMap));
          tpg.setScalingOffset(getAddOffset(attMap));
  
          final Number noDataValue = getNoDataValue(attMap);
          if (noDataValue != null) {
              tpg.setNoDataValue(noDataValue.doubleValue());
              tpg.setNoDataValueUsed(true);
          }
          return tpg;
      }
  
      private static String getDescription(final Variable variable, final NcAttributeMap attMap) {
          String desc = variable.getDescription();
          if(desc == null || desc.isEmpty()) {
              desc = attMap.getStringValue(NetcdfConstants.DESCRIPTION);
          }
          return desc;
      }
  
      private static String getUnit(final Variable variable, final NcAttributeMap attMap) {
          String unit = variable.getUnitsString();
          if(unit == null || unit.isEmpty()) {
              unit = attMap.getStringValue(NetcdfConstants.UNIT);
          }
          return unit;
      }
  
      private static double getScalingFactor(final NcAttributeMap attMap) {
          Number numValue = attMap.getNumericValue(NetcdfConstants.SCALE_FACTOR_ATT_NAME);
          if (numValue == null) {
              numValue = attMap.getNumericValue(NetcdfConstants.SLOPE_ATT_NAME);
          }
          return numValue != null ? numValue.doubleValue() : 1.0;
      }
  
      private static double getAddOffset(final NcAttributeMap attMap) {
          Number numValue = attMap.getNumericValue(NetcdfConstants.ADD_OFFSET_ATT_NAME);
          if (numValue == null) {
              numValue = attMap.getNumericValue(NetcdfConstants.INTERCEPT_ATT_NAME);
          }
          return numValue != null ? numValue.doubleValue() : 0.0;
      }
  
      private static Number getNoDataValue(final NcAttributeMap attMap) {
          Number noDataValue = attMap.getNumericValue(NetcdfConstants.FILL_VALUE_ATT_NAME);
          if (noDataValue == null) {
              noDataValue = attMap.getNumericValue(NetcdfConstants.MISSING_VALUE_ATT_NAME);
          }
          return noDataValue;
      }
  
      public static MapInfoX createMapInfoX(final Variable lonVar,
                                            final Variable latVar,
                                            final int sceneRasterWidth,
                                            final int sceneRasterHeight) throws IOException {
          float pixelX;
          float pixelY;
          float easting;
          float northing;
          float pixelSizeX;
          float pixelSizeY;
  
          final NcAttributeMap lonAttrMap = NcAttributeMap.create(lonVar);
          final Number lonValidMin = lonAttrMap.getNumericValue(NetcdfConstants.VALID_MIN_ATT_NAME);
          final Number lonStep = lonAttrMap.getNumericValue(NetcdfConstants.STEP_ATT_NAME);
  
          final NcAttributeMap latAttrMap = NcAttributeMap.create(latVar);
          final Number latValidMin = latAttrMap.getNumericValue(NetcdfConstants.VALID_MIN_ATT_NAME);
          final Number latStep = latAttrMap.getNumericValue(NetcdfConstants.STEP_ATT_NAME);
  
          boolean yFlipped;
          if (lonValidMin != null && lonStep != null && latValidMin != null && latStep != null) {
              // COARDS convention uses 'valid_min' and 'step' attributes
              pixelX = 0.5f;
              pixelY = (sceneRasterHeight - 1.0f) + 0.5f;
              easting = lonValidMin.floatValue();
              northing = latValidMin.floatValue();
              pixelSizeX = lonStep.floatValue();
              pixelSizeY = latStep.floatValue();
              // must flip
              yFlipped = true; // todo - check
          } else {
              // CF convention
  
              final Array lonData = lonVar.read();
              final Array latData = latVar.read();
  
              final Index i0 = lonData.getIndex().set(0);
              final Index i1 = lonData.getIndex().set(1);
              pixelSizeX = lonData.getFloat(i1) - lonData.getFloat(i0);
              easting = lonData.getFloat(i0);
  
              final int latSize = (int) latVar.getSize();
              final Index j0 = latData.getIndex().set(0);
              final Index j1 = latData.getIndex().set(1);
              pixelSizeY = latData.getFloat(j1) - latData.getFloat(j0);
  
              pixelX = 0.5f;
              pixelY = 0.5f;
  
              // this should be the 'normal' case
              if (pixelSizeY < 0) {
                  pixelSizeY *= -1;
                  yFlipped = false;
                  northing = latData.getFloat(latData.getIndex().set(0));
              } else {
                  yFlipped = true;
                  northing = latData.getFloat(latData.getIndex().set(latSize - 1));
              }
          }
  
          if (pixelSizeX <= 0 || pixelSizeY <= 0) {
              return null;
          }
  
          final MapProjection projection = MapProjectionRegistry.getProjection(IdentityTransformDescriptor.NAME);
          final MapInfo mapInfo = new MapInfo(projection,
                                              pixelX, pixelY,
                                              easting, northing,
                                              pixelSizeX, pixelSizeY,
                                              Datum.WGS_84);
          mapInfo.setSceneWidth(sceneRasterWidth);
          mapInfo.setSceneHeight(sceneRasterHeight);
          return new MapInfoX(mapInfo, yFlipped);
      }
  
      private static int getRasterDataType(final Variable variable) {
          //NetcdfDataTypeWorkarounds workarounds = NetcdfDataTypeWorkarounds.getInstance();
          //if (workarounds.hasWorkaroud(variable.getName(), variable.getDataType())) {
          //    return workarounds.getRasterDataType(variable.getName(), variable.getDataType());
          //}
          return getRasterDataType(variable.getDataType(), variable.isUnsigned());
      }
  
      private static boolean isValidRasterDataType(final DataType dataType) {
          return getRasterDataType(dataType, false) != -1;
      }
  
      private static int getRasterDataType(final DataType dataType, final boolean unsigned) {
          return getProductDataType(dataType, unsigned, true);
      }
  
      private static int getProductDataType(final DataType dataType, final boolean unsigned, final boolean rasterDataOnly) {
          if (dataType == DataType.BYTE) {
              return unsigned ? ProductData.TYPE_UINT8 : ProductData.TYPE_INT8;
          } else if (dataType == DataType.SHORT) {
              return unsigned ? ProductData.TYPE_UINT16 : ProductData.TYPE_INT16;
          } else if (dataType == DataType.INT) {
              return unsigned ? ProductData.TYPE_UINT32 : ProductData.TYPE_INT32;
          } else if (dataType == DataType.FLOAT) {
              return ProductData.TYPE_FLOAT32;
          } else if (dataType == DataType.DOUBLE) {
              return ProductData.TYPE_FLOAT64;
          } else if (!rasterDataOnly) {
              if (dataType == DataType.CHAR) {
                  // return ProductData.TYPE_ASCII; todo - handle this case
              } else if (dataType == DataType.STRING) {
                  return ProductData.TYPE_ASCII;
              }
          }
          return -1;
      }
  
      public static void addGroups(final MetadataElement parentElem, final Group parentGroup) {
          final List<Group> groupList = parentGroup.getGroups();
          for(Group grp : groupList) {
              final MetadataElement newElem = new MetadataElement(grp.getShortName());
              parentElem.addElement(newElem);
              // recurse
              addGroups(newElem, grp);
          }
  
          addAttributes(parentElem, parentGroup);
      }
  
      public static MetadataElement addAttributes(final MetadataElement parentElem, final String elemName,
                                             final List<Attribute> attribList) {
          final MetadataElement globalElem = new MetadataElement(elemName);
          parentElem.addElement(globalElem);
          for(Attribute at : attribList) {
              createMetadataAttributes(globalElem, at, at.getName());
          }
          return globalElem;
      }
  
      private static void addAttributes(final MetadataElement parentElem, final Group parentGroup) {
          final List<Attribute> attribList = parentGroup.getAttributes();
          for(Attribute at : attribList) {
              createMetadataAttributes(parentElem, at, at.getName());
          }
      }
  
      private static void createMetadataAttributes(final MetadataElement parentElem, final Attribute attribute,
                                                  final String name) {
          // todo - note that we still do not support NetCDF data type 'char' here!
  
          final int i = name.indexOf('/');
          if(i > 0) {
              final String elemName = name.substring(0, i);
              final String attName = name.substring(i+1, name.length());
              MetadataElement newElem = parentElem.getElement(elemName);
              if(newElem == null) {
                  newElem = new MetadataElement(elemName);
                  parentElem.addElement(newElem);
              }
              createMetadataAttributes(newElem, attribute, attName);
          } else {
              final int productDataType = getProductDataType(attribute.getDataType(), false, false);
              if (productDataType != -1) {
                  ProductData productData;
                  if (attribute.isString()) {
                      String strValue = attribute.getStringValue();
                      if(strValue.startsWith(NetcdfConstants.UTC_TYPE)) {
                          strValue = strValue.substring(NetcdfConstants.UTC_TYPE.length(), strValue.length());
                          productData = AbstractMetadata.parseUTC(strValue);
                      } else {
                          productData = ProductData.createInstance(strValue);
                      }
                  } else if (attribute.isArray()) {
                      productData = ProductData.createInstance(productDataType, attribute.getLength());
                      productData.setElems(attribute.getValues().getStorage());
                  } else {
                      productData = ProductData.createInstance(productDataType, 1);
                      productData.setElems(attribute.getValues().getStorage());
                  }
                  final MetadataAttribute metadataAttribute = new MetadataAttribute(name, productData, true);
                  parentElem.addAttribute(metadataAttribute);
              }
          }
      }
  
      public static String getProductType(final NcAttributeMap attMap, final String defaultType) {
          String productType = attMap.getStringValue("Product Type");
          if (productType == null) {
              productType = attMap.getStringValue("Conventions");
              if (productType == null) {
                  productType = defaultType;
              }
          }
          return productType;
      }
  
      public static String getProductDescription(final NcAttributeMap attMap) {
          String description = attMap.getStringValue("description");
          if (description == null) {
              description = attMap.getStringValue("title");
              if (description == null) {
                  description = attMap.getStringValue("comment");
              }
          }
          return description;
      }
  
      public static ProductData.UTC getSceneRasterStartTime(NcAttributeMap globalAttributes) {
          return getSceneRasterTime(globalAttributes,
                                    NetcdfConstants.START_DATE_ATT_NAME,
                                    NetcdfConstants.START_TIME_ATT_NAME);
      }
  
      public static ProductData.UTC getSceneRasterStopTime(NcAttributeMap globalAttributes) {
          return getSceneRasterTime(globalAttributes,
                                    NetcdfConstants.STOP_DATE_ATT_NAME,
                                    NetcdfConstants.STOP_TIME_ATT_NAME);
      }
  
      private static ProductData.UTC getSceneRasterTime(NcAttributeMap globalAttributes,
                                                       final String dateAttName,
                                                       final String timeAttName) {
          final String dateStr = globalAttributes.getStringValue(dateAttName);
          final String timeStr = globalAttributes.getStringValue(timeAttName);
          final String dateTimeStr = getDateTimeString(dateStr, timeStr);
  
          if (dateTimeStr != null) {
              try {
                  return parseDateTime(dateTimeStr);
              } catch (ParseException e) {
                  BeamLogManager.getSystemLogger().warning(
                          "Failed to parse time string '" + dateTimeStr + '\'');
              }
          }
  
          return null;
      }
  
      private static String getDateTimeString(String dateStr, String timeStr) {
          if (dateStr != null && dateStr.endsWith("UTC")) {
  