docs: void readFile(final Reference ref, final URI parseFile) throws DITAOTException {
          currentFile = ref.filename;
          assert currentFile.isAbsolute();
          final URI src = parseFile != null ? parseFile : currentFile;
          assert src.isAbsolute();
          final URI rel = tempFileNameScheme.generateTempFileName(currentFile);
          outputFile = new File(job.tempDirURI.resolve(rel));
          final File outputDir = outputFile.getParentFile();
          if (!outputDir.exists() && !outputDir.mkdirs()) {
              logger.error("Failed to create output directory " + outputDir.getAbsolutePath());
              return;
          }
          validateMap = Collections.emptyMap();
          defaultValueMap = Collections.emptyMap();
          logger.info("Processing " + currentFile + " to " + outputFile.toURI());
          final String[] params = { currentFile.toString() };
  
          // Verify stub for current file is in Job
          final FileInfo fi = job.getFileInfo(currentFile);
          if (fi == null) {
              final FileInfo stub = new FileInfo.Builder()
                      .src(currentFile)
                      .uri(rel)
                      .result(currentFile)
                      .isInput(currentFile.equals(rootFile))
                      .build();
              job.add(stub);
          }
  
  //        InputSource in = null;
          Result out = null;
          try {
              final TransformerFactory tf = TransformerFactory.newInstance();
              final SAXTransformerFactory stf = (SAXTransformerFactory) tf;
              final TransformerHandler serializer = stf.newTransformerHandler();
  
              XMLReader parser = getXmlReader(ref.format);
              XMLReader xmlSource = parser;
              for (final XMLFilter f: getProcessingPipe(currentFile)) {
                  f.setParent(xmlSource);
                  f.setEntityResolver(CatalogUtils.getCatalogResolver());
                  xmlSource = f;
              }
  
              try {
                  final LexicalHandler lexicalHandler = new DTDForwardHandler(xmlSource);
                  parser.setProperty("http://xml.org/sax/properties/lexical-handler", lexicalHandler);
                  parser.setFeature("http://xml.org/sax/features/lexical-handler", true);
              } catch (final SAXNotRecognizedException e) {}
  
  //            in = new InputSource(src.toString());
              out = new StreamResult(new FileOutputStream(outputFile));
              serializer.setResult(out);
              xmlSource.setContentHandler(serializer);
              xmlSource.parse(src.toString());
  
              if (listFilter.isValidInput()) {
                  processParseResult(currentFile);
                  categorizeCurrentFile(ref);
              } else if (!currentFile.equals(rootFile)) {
                  logger.warn(MessageUtils.getMessage("DOTJ021W", params).toString());
                  failureList.add(currentFile);
              }
          } catch (final RuntimeException e) {
              throw e;
          } catch (final SAXParseException sax) {
              final Exception inner = sax.getException();
              if (inner != null && inner instanceof DITAOTException) {
                  throw (DITAOTException) inner;
              }
              if (currentFile.equals(rootFile)) {
                  throw new DITAOTException(MessageUtils.getMessage("DOTJ012F", params).toString() + ": " + sax.getMessage(), sax);
              } else if (processingMode == Mode.STRICT) {
                  throw new DITAOTException(MessageUtils.getMessage("DOTJ013E", params).toString() + ": " + sax.getMessage(), sax);
              } else {
                  logger.error(MessageUtils.getMessage("DOTJ013E", params).toString() + ": " + sax.getMessage(), sax);
              }
              failureList.add(currentFile);
          } catch (final FileNotFoundException e) {
              if (!exists(currentFile)) {
                  if (currentFile.equals(rootFile)) {
                      throw new DITAOTException(MessageUtils.getMessage("DOTA069F", params).toString(), e);
                  } else if (processingMode == Mode.STRICT) {
                      throw new DITAOTException(MessageUtils.getMessage("DOTX008E", params).toString(), e);
                  } else {
                      logger.error(MessageUtils.getMessage("DOTX008E", params).toString());
                  }
              } else if (currentFile.equals(rootFile)) {
                  throw new DITAOTException(MessageUtils.getMessage("DOTJ078F", params).toString() + " Cannot load file: " + e.getMessage(), e);
              } else if (processingMode == Mode.STRICT) {
                  throw new DITAOTException(MessageUtils.getMessage("DOTJ079E", params).toString() + " Cannot load file: " + e.getMessage(), e);
              } else {
                  logger.error(MessageUtils.getMessage("DOTJ079E", params).toString() + " Cannot load file: " + e.getMessage());
              }
              failureList.add(currentFile);
          } catch (final Exception e) {
              if (currentFile.equals(rootFile)) {
                  throw new DITAOTException(MessageUtils.getMessage("DOTJ012F", params).toString() + ": " + e.getMessage(),  e);
              } else if (processingMode == Mode.STRICT) {
                  throw new DITAOTException(MessageUtils.getMessage("DOTJ013E", params).toString() + ": " + e.getMessage(), e);
              } else {
                  logger.error(MessageUtils.getMessage("DOTJ013E", params).toString() + ": " + e.getMessage(), e);
              }
              failureList.add(currentFile);
          } finally {
              if (out != null) {
                  try {
                      close(out);
                  } catch (final IOException e) {
                      logger.error(e.getMessage(), e) ;
                  }
              }
              if (failureList.contains(currentFile)) {
                  FileUtils.deleteQuietly(outputFile);
              }
          }
  
          if (!listFilter.isValidInput() && currentFile.equals(rootFile)) {
              if (validate) {
                  // stop the build if all content in the input file was filtered out.
                  throw new DITAOTException(MessageUtils.getMessage("DOTJ022F", params).toString());
              } else {
                  // stop the build if the content of the file is not valid.
                  throw new DITAOTException(MessageUtils.getMessage("DOTJ034F", params).toString());
              }
          }
  
          doneList.add(currentFile);
          listFilter.reset();
          keydefFilter.reset();
  
      }