cdm/src/main/java/ucar/nc2/iosp/grads/GradsDataDescriptorFile.java
private void parseDDF(int maxLines) throws IOException {

    //long start2 = System.currentTimeMillis();

    variableList = new ArrayList<>();
    dimList = new ArrayList<>();
    attrList = new ArrayList<>();

    // LOOK not using raf - opened file again
    int count = 0;
    try (BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(ddFile), CDM.utf8Charset))) {
      boolean inVarSection = false;
      boolean inEnsSection = false;
      String line;
      String original;
      GradsDimension curDim = null;

      while ((original = r.readLine()) != null) {
        count++;
        if (count > maxLines) {
          error = true;
          return;
        }

        original = original.trim();
        if (original.isEmpty()) {
          continue;
        }
        line = original.toLowerCase();

        if (line.startsWith("@ ")) {
          attrList.add(GradsAttribute.parseAttribute(original));
          continue;
        }

        // ignore attribute metadata and comments
        if (line.startsWith("*")) {
          continue;
        }

        if (inEnsSection) {
          if (line.startsWith(ENDEDEF.toLowerCase())) {
            inEnsSection = false;
            continue;  // done skipping ensemble definitions
          }
          // parse the ensemble info
        }
        if (inVarSection) {
          if (line.startsWith(ENDVARS.toLowerCase())) {
            inVarSection = false;
            continue;  // done parsing variables
          }
          GradsVariable var = new GradsVariable(original);
          int numLevels = var.getNumLevels();
          if (numLevels == 0) {
            numLevels = 1;
          }
          gridsPerTimeStep += numLevels;

          // parse a variable
          variableList.add(var);

        } else {
          // not in var section or edef section, look for general metadata
          StringTokenizer st = new StringTokenizer(original);

          String label = st.nextToken();

          // TODO: Handle other options
          if (label.equalsIgnoreCase(OPTIONS)) {
            curDim = null;
            while (st.hasMoreTokens()) {
              String token = st.nextToken();
              if (token.equalsIgnoreCase(BIG_ENDIAN)) {
                bigEndian = true;
              } else if (token.equalsIgnoreCase(LITTLE_ENDIAN)) {
                bigEndian = false;
              } else if (token.equalsIgnoreCase(BYTESWAPPED)) {
                swapByteOrder();
              } else if (token.equalsIgnoreCase(YREV)) {
                yReversed = true;
              } else if (token.equalsIgnoreCase(TEMPLATE)) {
                isTemplate = true;
              } else if (token.equalsIgnoreCase(SEQUENTIAL)) {
                isSequential = true;
              }
            }
          } else if (label.equalsIgnoreCase(CHSUB)) {
            int start = Integer.parseInt(st.nextToken());
            int end = Integer.parseInt(st.nextToken());
            String sub = st.nextToken();
            addChsub(new Chsub(start, end, sub));

          } else if (label.equalsIgnoreCase(DSET)) {
            curDim = null;
            dataFile = st.nextToken();

          } else if (label.equalsIgnoreCase(UNDEF)) {
            curDim = null;
            missingData = Double.parseDouble(st.nextToken());

          } else if (label.equalsIgnoreCase(XYHEADER)) {
            curDim = null;
            xyHeaderBytes = Integer.parseInt(st.nextToken());

          } else if (label.equalsIgnoreCase(FILEHEADER)) {
            curDim = null;
            fileHeaderBytes = Integer.parseInt(st.nextToken());

          } else if (label.equalsIgnoreCase(XDEF)) {
            int xSize = Integer.valueOf(st.nextToken());
            String xMapping = st.nextToken();
            xDim = new GradsDimension(label, xSize, xMapping);
            curDim = xDim;
            dimList.add(xDim);

          } else if (label.equalsIgnoreCase(YDEF)) {
            int ySize = Integer.valueOf(st.nextToken());
            String yMapping = st.nextToken();
            yDim = new GradsDimension(label, ySize, yMapping);
            curDim = yDim;
            dimList.add(yDim);

          } else if (label.equalsIgnoreCase(ZDEF)) {
            int zSize = Integer.valueOf(st.nextToken());
            String zMapping = st.nextToken();
            zDim = new GradsDimension(label, zSize, zMapping);
            curDim = zDim;
            dimList.add(zDim);

          } else if (label.equalsIgnoreCase(TDEF)) {
            int tSize = Integer.valueOf(st.nextToken());
            // we can read the following directly
            // since tdef never uses "levels"
            String tMapping = st.nextToken();
            tDim = new GradsTimeDimension(label, tSize, tMapping);
            curDim = tDim;
            dimList.add(tDim);

          } else if (label.equalsIgnoreCase(EDEF)) {
            int eSize = Integer.valueOf(st.nextToken());
            // Check if EDEF entry is the short or extended version
            if (st.nextToken().equalsIgnoreCase(GradsEnsembleDimension.NAMES)) {
              inEnsSection = false;
              String eMapping = GradsEnsembleDimension.NAMES;
              eDim = new GradsEnsembleDimension(label, eSize, eMapping);
              curDim = eDim;
              dimList.add(curDim);

            } else {
              // TODO: handle list of ensembles
              curDim = null;
              inEnsSection = true;
            }

          } else if (label.equalsIgnoreCase(PDEF)) {
            curDim = null;
            hasProjection = true;

          } else if (label.equalsIgnoreCase(VARS)) {
            curDim = null;
            inVarSection = true;

          } else if (label.equalsIgnoreCase(DTYPE)) {
            curDim = null;
            dataType = st.nextToken();

          } else if (label.equalsIgnoreCase(TITLE)) {
            curDim = null;
            title = original.substring(original.indexOf(" ")).trim();

          } else if (curDim != null) {
            curDim.addLevel(label);
          }
          // get the rest of the tokens
          if (curDim != null) {
            while (st.hasMoreTokens()) {
              curDim.addLevel(st.nextToken());
            }
          }
        }

      }  // end parsing loop
      //System.out.println("Time to parse file = "
      //                   + (System.currentTimeMillis() - start2));

      // update the units for the zDimension if they are specified as
      // an attribute
      if (zDim != null) {
        for (GradsAttribute attr : attrList) {
          if (attr.getVariable().equalsIgnoreCase(ZDEF) &&
                  attr.getType().equalsIgnoreCase(GradsAttribute.STRING) &&
                  attr.getName().equalsIgnoreCase("units")) {
            zDim.setUnit(attr.getValue());
            break;
          }
        }
      }

    } catch (IOException ioe) {
      log.error("Error parsing metadata for " + ddFile);
      throw new IOException("error parsing metadata for " + ddFile);
    }
  }
private void parseDDF(int maxLines) throws IOException {

    //long start2 = System.currentTimeMillis();

    variableList = new ArrayList<>();
    dimList = new ArrayList<>();
    attrList = new ArrayList<>();

    // LOOK not using raf - opened file again
    int count = 0;
    try (BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(ddFile), CDM.utf8Charset))) {
      boolean inVarSection = false;
      boolean inEnsSection = false;
      String line;
      String original;
      GradsDimension curDim = null;

      while ((original = r.readLine()) != null) {
        count++;
        if (count > maxLines) {
          error = true;
          return;
        }

        original = original.trim();
        if (original.isEmpty()) {
          continue;
        }
        line = original.toLowerCase();

        if (line.startsWith("@ ")) {
          attrList.add(GradsAttribute.parseAttribute(original));
          continue;
        }

        // ignore attribute metadata and comments
        if (line.startsWith("*")) {
          continue;
        }

        if (inEnsSection) {
          if (line.startsWith(ENDEDEF.toLowerCase())) {
            inEnsSection = false;
            continue;  // done skipping ensemble definitions
          }
          // parse the ensemble info
        }
        if (inVarSection) {
          if (line.startsWith(ENDVARS.toLowerCase())) {
            inVarSection = false;
            continue;  // done parsing variables
          }
          GradsVariable var = new GradsVariable(original);
          int numLevels = var.getNumLevels();
          if (numLevels == 0) {
            numLevels = 1;
          }
          gridsPerTimeStep += numLevels;

          // parse a variable
          variableList.add(var);

        } else {
          // not in var section or edef section, look for general metadata
          StringTokenizer st = new StringTokenizer(original);

          String label = st.nextToken();

          // TODO: Handle other options
          if (label.equalsIgnoreCase(OPTIONS)) {
            curDim = null;
            while (st.hasMoreTokens()) {
              String token = st.nextToken();
              if (token.equalsIgnoreCase(BIG_ENDIAN)) {
                bigEndian = true;
              } else if (token.equalsIgnoreCase(LITTLE_ENDIAN)) {
                bigEndian = false;
              } else if (token.equalsIgnoreCase(BYTESWAPPED)) {
                swapByteOrder();
              } else if (token.equalsIgnoreCase(YREV)) {
                yReversed = true;
              } else if (token.equalsIgnoreCase(TEMPLATE)) {
                isTemplate = true;
              } else if (token.equalsIgnoreCase(SEQUENTIAL)) {
                isSequential = true;
              }
            }
          } else if (label.equalsIgnoreCase(CHSUB)) {
            int start = Integer.parseInt(st.nextToken());
            int end = Integer.parseInt(st.nextToken());
            String sub = st.nextToken();
            addChsub(new Chsub(start, end, sub));

          } else if (label.equalsIgnoreCase(DSET)) {
            curDim = null;
            dataFile = st.nextToken();

          } else if (label.equalsIgnoreCase(UNDEF)) {
            curDim = null;
            missingData = Double.parseDouble(st.nextToken());

          } else if (label.equalsIgnoreCase(XYHEADER)) {
            curDim = null;
            xyHeaderBytes = Integer.parseInt(st.nextToken());

          } else if (label.equalsIgnoreCase(FILEHEADER)) {
            curDim = null;
            fileHeaderBytes = Integer.parseInt(st.nextToken());

          } else if (label.equalsIgnoreCase(XDEF)) {
            int xSize = Integer.valueOf(st.nextToken());
            String xMapping = st.nextToken();
            xDim = new GradsDimension(label, xSize, xMapping);
            curDim = xDim;
            dimList.add(xDim);

          } else if (label.equalsIgnoreCase(YDEF)) {
            int ySize = Integer.valueOf(st.nextToken());
            String yMapping = st.nextToken();
            yDim = new GradsDimension(label, ySize, yMapping);
            curDim = yDim;
            dimList.add(yDim);

          } else if (label.equalsIgnoreCase(ZDEF)) {
            int zSize = Integer.valueOf(st.nextToken());
            String zMapping = st.nextToken();
            zDim = new GradsDimension(label, zSize, zMapping);
            curDim = zDim;
            dimList.add(zDim);

          } else if (label.equalsIgnoreCase(TDEF)) {
            int tSize = Integer.valueOf(st.nextToken());
            // we can read the following directly
            // since tdef never uses "levels"
            String tMapping = st.nextToken();
            tDim = new GradsTimeDimension(label, tSize, tMapping);
            curDim = tDim;
            dimList.add(tDim);

          } else if (label.equalsIgnoreCase(EDEF)) {
            int eSize = Integer.valueOf(st.nextToken());
            // Check if EDEF entry is the short or extended version
            if (st.nextToken().equalsIgnoreCase(GradsEnsembleDimension.NAMES)) {
              inEnsSection = false;
              String eMapping = GradsEnsembleDimension.NAMES;
              eDim = new GradsEnsembleDimension(label, eSize, eMapping);
              curDim = eDim;
              dimList.add(curDim);

            } else {
              // TODO: handle list of ensembles
              curDim = null;
              inEnsSection = true;
            }

          } else if (label.equalsIgnoreCase(PDEF)) {
            curDim = null;
            hasProjection = true;

          } else if (label.equalsIgnoreCase(VARS)) {
            curDim = null;
            inVarSection = true;

          } else if (label.equalsIgnoreCase(DTYPE)) {
            curDim = null;
            dataType = st.nextToken();

          } else if (label.equalsIgnoreCase(TITLE)) {
            curDim = null;
            title = original.substring(original.indexOf(" ")).trim();

          } else if (curDim != null) {
            curDim.addLevel(label);
          }
          // get the rest of the tokens
          if (curDim != null) {
            while (st.hasMoreTokens()) {
              curDim.addLevel(st.nextToken());
            }
          }
        }

      }  // end parsing loop
      //System.out.println("Time to parse file = "
      //                   + (System.currentTimeMillis() - start2));

      // update the units for the zDimension if they are specified as
      // an attribute
      if (zDim != null) {
        for (GradsAttribute attr : attrList) {
          if (attr.getVariable().equalsIgnoreCase(ZDEF) &&
                  attr.getType().equalsIgnoreCase(GradsAttribute.STRING) &&
                  attr.getName().equalsIgnoreCase("units")) {
            zDim.setUnit(attr.getValue());
            break;
          }
        }
      }

    } catch (IOException ioe) {
      log.error("Error parsing metadata for " + ddFile);
      throw new IOException("error parsing metadata for " + ddFile);
    }
  }
Parse the ctl file

@throws IOException problem reading the file