Files
Rozpisky/Rozpisky/Data/ProjektStore.cs
T
marekandClaude Opus 5 a6377ab583 Baseline: současný stav projektu Rozpisky
První commit — WPF aplikace pro generování výkresových rohových razítek
(.NET 10, SkiaSharp, ACadSharp, ClosedXML) včetně xUnit testů, DXF šablony,
log a dokumentace. Build výstupy (bin/, obj/, .vs/) jsou ignorovány.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 09:45:49 +02:00

116 lines
4.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.IO;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using Rozpisky.Models;
namespace Rozpisky.Data;
/// <summary>
/// Společné konstanty a JSON nastavení pro `.rzp` soubory. Vlastní čtení/zápis se zámkem
/// řeší <see cref="ProjektFileHandle"/>. Žádné NuGet závislosti (System.Text.Json je součást .NET).
/// </summary>
public static class ProjektStore
{
public const string Pripona = ".rzp";
public const string DialogFilter = "Projekt Rozpisky (*.rzp)|*.rzp|Všechny soubory (*.*)|*.*";
internal static readonly JsonSerializerOptions Opts = new()
{
WriteIndented = true,
// Aby se diakritika v souboru psala čitelně (ě š č…), ne jako \uXXXX.
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
};
}
/// <summary>
/// Otevřený `.rzp` soubor s exkluzivním zámkem drženým po celou dobu, co je projekt
/// otevřený. Díky <see cref="FileShare.Read"/> ho ostatní procesy smí jen číst/kopírovat,
/// ale nejde smazat, přejmenovat ani přepsat Windows ho hlásí jako „používaný". Ukládání
/// jde přes držený handle (zámek se neuvolní). Zavře se přes <see cref="Dispose"/> (zavření
/// projektu/appky); při pádu uvolní zámek operační systém sám.
/// </summary>
public sealed class ProjektFileHandle : IDisposable
{
private readonly FileStream _stream;
/// <summary>Cesta k zamčenému souboru.</summary>
public string Cesta { get; }
private ProjektFileHandle(string cesta, FileStream stream)
{
Cesta = cesta;
_stream = stream;
}
/// <summary>Otevře existující soubor s exkluzivním zámkem a vrátí načtený dokument.
/// Vyhodí <see cref="IOException"/>, když je soubor otevřený jinde (sdílecí porušení).</summary>
public static ProjektFileHandle Otevri(string cesta, out ProjektDocument doc)
{
var fs = new FileStream(cesta, FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
try
{
string text = Cti(fs);
doc = JsonSerializer.Deserialize<ProjektDocument>(text, ProjektStore.Opts)
?? throw new InvalidDataException("Soubor projektu je prázdný nebo poškozený.");
return new ProjektFileHandle(cesta, fs);
}
catch
{
fs.Dispose(); // při chybě zámek nedržíme
throw;
}
}
/// <summary>Vytvoří/přepíše soubor na nové cestě, zapíše dokument a podrží zámek.
/// Vyhodí <see cref="IOException"/>, když je cílový soubor otevřený jinde.</summary>
public static ProjektFileHandle Vytvor(string cesta, ProjektDocument doc)
{
var fs = new FileStream(cesta, FileMode.Create, FileAccess.ReadWrite, FileShare.Read);
try
{
Zapis(fs, doc);
return new ProjektFileHandle(cesta, fs);
}
catch
{
fs.Dispose();
throw;
}
}
/// <summary>Přečte soubor jen pro čtení (bez zámku) i když ho jiný proces drží otevřený.
/// Žádá pouze <see cref="FileAccess.Read"/> a povoluje <see cref="FileShare.ReadWrite"/>, takže
/// nekoliduje s cizím zámkem. Handle se nedrží vrací jen načtený dokument.</summary>
public static ProjektDocument CtiJenProCteni(string cesta)
{
using var fs = new FileStream(cesta, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
return JsonSerializer.Deserialize<ProjektDocument>(Cti(fs), ProjektStore.Opts)
?? throw new InvalidDataException("Soubor projektu je prázdný nebo poškozený.");
}
/// <summary>Uloží dokument přes držený zámek (soubor zůstává zamčený).</summary>
public void Uloz(ProjektDocument doc) => Zapis(_stream, doc);
private static void Zapis(FileStream fs, ProjektDocument doc)
{
// Serializace musí proběhnout PŘED useknutím souboru kdyby vyhodila výjimku
// až po SetLength(0), zůstal by soubor prázdný a projekt by byl nenávratně pryč.
string json = JsonSerializer.Serialize(doc, ProjektStore.Opts);
fs.Seek(0, SeekOrigin.Begin);
fs.SetLength(0);
using (var w = new StreamWriter(fs, new UTF8Encoding(false), 1024, leaveOpen: true))
w.Write(json);
fs.Flush(flushToDisk: true);
}
private static string Cti(FileStream fs)
{
fs.Seek(0, SeekOrigin.Begin);
using var r = new StreamReader(fs, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, 1024, leaveOpen: true);
return r.ReadToEnd();
}
public void Dispose() => _stream.Dispose();
}