203 lines
4.4 KiB
JavaScript
203 lines
4.4 KiB
JavaScript
'use strict';
|
|
|
|
const taulut = {
|
|
luokat: 'luokat',
|
|
opettajat: 'opettajat',
|
|
tilat: 'tilat',
|
|
};
|
|
|
|
class Transaktio {
|
|
peruttu = false;
|
|
|
|
muutokset = [];
|
|
|
|
constructor(tietokanta) {
|
|
this.tietokanta = tietokanta;
|
|
this.seuraavaId = tietokanta.seuraavaId;
|
|
this.taulut = new Map;
|
|
for (const taulu of tietokanta.taulut.keys()) {
|
|
this.taulut.set(taulu, new Map);
|
|
}
|
|
}
|
|
|
|
peru() {
|
|
this.peruttu = true;
|
|
}
|
|
|
|
hae(taulu, id) {
|
|
if (!this.taulut.has(taulu)) {
|
|
throw new Error(`ei taulua ${taulu}`);
|
|
}
|
|
if (this.taulut.get(taulu).has(id)) {
|
|
return this.taulut.get(taulu).get(id);
|
|
} else {
|
|
return this.tietokanta.taulut.get(taulu).get(id);
|
|
}
|
|
}
|
|
|
|
lisää(taulu, sisältö) {
|
|
if (this.peruttu) {
|
|
throw new Error(`yritys lisätä rivi perutussa transaktiossa`);
|
|
}
|
|
if (!this.taulut.has(taulu)) {
|
|
throw new Error(`ei taulua ${taulu}`);
|
|
}
|
|
const id = this.seuraavaId++;
|
|
this.muutokset.push({
|
|
taulu,
|
|
id,
|
|
uusi: sisältö,
|
|
});
|
|
this.taulut.get(taulu).set(id, sisältö);
|
|
return id;
|
|
}
|
|
|
|
poista(taulu, id) {
|
|
if (this.peruttu) {
|
|
throw new Error(`yritys poistaa rivi perutussa transaktiossa`);
|
|
}
|
|
const vanha = this.hae(taulu, id);
|
|
if (vanha === undefined) {
|
|
throw new Error(`ei riviä ${id} taulussa ${taulu}`);
|
|
}
|
|
this.muutokset.push({
|
|
taulu,
|
|
id,
|
|
vanha,
|
|
});
|
|
this.taulut.get(taulu).set(id, undefined);
|
|
}
|
|
}
|
|
|
|
class Tietokanta {
|
|
seuraavaId = 0;
|
|
taulut = new Map;
|
|
|
|
historia = [];
|
|
|
|
static serialisoidusta(serialisoitu) {
|
|
const parsittu = JSON.parse(serialisoitu);
|
|
const tietokanta = new this;
|
|
tietokanta.seuraavaId = parsittu.seuraavaId;
|
|
const muutokset = [];
|
|
for (const taulu in parsittu.taulut) {
|
|
for (let id in parsittu.taulut[taulu]) {
|
|
id = Number.parseInt(id);
|
|
const sisältö = parsittu.taulut[taulu][id];
|
|
tietokanta.taulut.get(taulu).set(id, sisältö);
|
|
muutokset.push({taulu, id, uusi: sisältö});
|
|
}
|
|
}
|
|
return [tietokanta, muutokset];
|
|
}
|
|
|
|
constructor() {
|
|
for (let taulu in taulut) {
|
|
this.taulut.set(taulu, new Map);
|
|
}
|
|
}
|
|
|
|
transaktio(funktio) {
|
|
const transaktio = new Transaktio(this);
|
|
funktio(transaktio);
|
|
return [this, this.suorita(transaktio)];
|
|
}
|
|
|
|
suorita(transaktio) {
|
|
if (transaktio.peruttu || transaktio.muutokset.length === 0) {
|
|
return [];
|
|
}
|
|
|
|
// Varmista, että invariantit ovat yhä totta
|
|
for (const {taulu, id, vanha, uusi} of transaktio.muutokset) {
|
|
}
|
|
|
|
// Suorita muutokset
|
|
for (const {taulu, id, uusi} of transaktio.muutokset) {
|
|
if (uusi !== undefined) {
|
|
this.taulut.get(taulu).set(id, uusi);
|
|
} else {
|
|
this.taulut.get(taulu).delete(id);
|
|
}
|
|
}
|
|
|
|
this.historia.push({
|
|
muutokset: transaktio.muutokset,
|
|
idMuutos: transaktio.seuraavaId - this.seuraavaId,
|
|
});
|
|
this.seuraavaId = transaktio.seuraavaId;
|
|
return transaktio.muutokset;
|
|
}
|
|
|
|
kumoa() {
|
|
if (this.historia.length === 0) {
|
|
return [this, []];
|
|
}
|
|
const {muutokset, idMuutos} = this.historia.pop();
|
|
this.seuraavaId -= idMuutos;
|
|
const kumotut = [];
|
|
for (const {taulu, id, vanha, uusi} of muutokset) {
|
|
if (vanha !== undefined) {
|
|
this.taulut.get(taulu).set(id, vanha);
|
|
} else {
|
|
this.taulut.get(taulu).delete(id);
|
|
}
|
|
kumotut.push({
|
|
taulu,
|
|
id,
|
|
vanha: uusi,
|
|
uusi: vanha,
|
|
});
|
|
}
|
|
return [this, kumotut];
|
|
}
|
|
|
|
hae(taulu, id) {
|
|
if (!this.taulut.has(taulu)) {
|
|
throw new Error(`ei taulua ${taulu}`);
|
|
}
|
|
return this.taulut.get(taulu).get(id);
|
|
}
|
|
|
|
järjestyksessä(taulu, järjestys) {
|
|
if (!this.taulut.has(taulu)) {
|
|
throw new Error(`ei taulua ${taulu}`);
|
|
}
|
|
const taulukko = Array.from(this.taulut.get(taulu).entries());
|
|
taulukko.sort(([xId, x], [yId, y]) => {
|
|
const vertaus = järjestys(x, y);
|
|
if (vertaus < 0 || vertaus > 0) {
|
|
return vertaus;
|
|
} else {
|
|
return xId - yId;
|
|
}
|
|
});
|
|
return taulukko.map(([id, _]) => id);
|
|
}
|
|
|
|
serialisoi() {
|
|
return JSON.stringify(this, (avain, arvo) => {
|
|
if (avain === 'historia') {
|
|
return undefined;
|
|
}
|
|
if (arvo instanceof Map) {
|
|
return Object.fromEntries(arvo.entries());
|
|
}
|
|
return arvo;
|
|
});
|
|
}
|
|
}
|
|
|
|
function tallennaTietokanta(tietokanta) {
|
|
window.localStorage.setItem('tietokanta', tietokanta.serialisoi());
|
|
}
|
|
|
|
function lataaTietokanta() {
|
|
const serialisoitu = window.localStorage.getItem('tietokanta');
|
|
if (serialisoitu === null) {
|
|
return;
|
|
}
|
|
let [tietokanta, muutokset] = Tietokanta.serialisoidusta(serialisoitu);
|
|
_tietokanta = tietokanta;
|
|
suorita([tietokanta, muutokset]);
|
|
}
|