Skip to content

Report save to disk

Renders a report (reusing a cached render where possible) and writes the resulting bytes to a caller-specified destination file, refusing to replace an existing file unless the caller explicitly requests an overwrite.

Field Value
Operation ID OP-09
Trigger Save invoked
Inputs overwrite As Boolean = False, DestinationFileName (instance property)
Outputs Boolean — whether the destination file exists after the call
Primary components ReportBase.Save, Render, IO.FileStream
Entry point Public Function Save(Optional overwrite As Boolean = False) As Boolean

Steps

  1. Checks whether DestinationFileName already exists on disk (IO.File.Exists).
  2. Proceeds only if the file does not already exist, or overwrite is True — an existing destination file is not replaced unless the caller requests an overwrite (BR-PRT-014).
  3. If neither condition holds: execution skips straight to step 5's existence check without touching the file.
  4. Requires m_DestinationFileName.Length > 0 before actually writing — saving requires a destination file name (BR-PRT-014).
  5. If empty: no render and no write occurs, but execution still falls through to step 5.
  6. Calls Render(overwrite) to ensure m_reportFile is populated (respecting Render's own render-result caching), then opens DestinationFileName with FileMode.Create (which itself truncates/overwrites at the OS level) and writes all of m_reportFile's bytes.

Origin: This step is the full Report render lifecycle operation, including its render-result caching (BR-PRT-007). 5. Returns IO.File.Exists(DestinationFileName) — re-checked fresh, not a cached flag carried over from step 1 — as the final result.

Process flow

flowchart TD
    Start([Save called]) --> Check["Check file exists"]
    Check --> Guard{"exists AND<br>not overwrite?"}
    Guard -->|Yes| SkipWrite["skip write"]
    Guard -->|No| Named{"DestinationFileName<br>set?"}
    Named -->|No| SkipWrite
    Named -->|Yes| Render["Render()<br>(→OP-06)"]
    Render --> Write["FileStream.Create,<br>write bytes"]
    Write --> Result(["Return File.Exists<br>(re-checked)"])
    SkipWrite --> Result

Failure and edge-case behavior

  • There is no try/catch around the FileStream write: an IOException (locked file, invalid path, disk full) propagates unhandled — a contrast with Render's and Distribute's caught-and-recorded failure pattern.
  • If Render inside step 4 fails silently (BR-PRT-010 — returns False with an ErrorDetail but no exception) and leaves m_reportFile as Nothing, the subsequent fs.Write(m_reportFile, 0, m_reportFile.Length) would throw a NullReferenceException — a real, uncaught crash path.*
  • The final IO.File.Exists check means Save can return True even when nothing was written during this call, simply because a pre-existing file (from a prior run) is present and no overwrite was requested. This is the intended BR-PRT-014 semantics, not a bug.

Note: Statements marked with * were inferred from code structure, naming conventions, or contextual evidence where direct confirmation was not available in source artifacts. Statements marked with ** could not be confirmed; treat as unknown until verified.

Sources

  • SAIF.Services.Printing/Reporting/ReportBase.vb
  • SAIF.Services.Printing/Reporting/IReport.vb