{
Manifests содержит классы для работы с метаданными о программах,
которые обычно хранятся в архивах с программами (/META-INF/MANIFEST.MF).
Copyright © 2016, 2019, 2022–2023 Малик Разработчик
Это свободная программа: вы можете перераспространять её и/или
изменять её на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
в каком она была опубликована Фондом свободного программного обеспечения;
либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.
Эта программа распространяется в надежде, что она может быть полезна,
но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЁННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
общественной лицензии GNU.
Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
вместе с этой программой. Если это не так, см.
<http://www.gnu.org/licenses/>.
}
unit Manifests;
{$MODE DELPHI}
interface
uses
Lang,
IOStreams,
FileIO;
{%region public }
type
ProgrammeManifestProperty = class;
ProgrammeManifest = class;
ProgrammeManifestProperty_Array1d = packed array of ProgrammeManifestProperty;
ProgrammeManifestProperty = class(_Object)
strict private
name: AnsiString;
value: AnsiString;
public
constructor create(); overload;
constructor create(const name: AnsiString); overload;
constructor create(const name, value: AnsiString); overload;
procedure assign(prop: ProgrammeManifestProperty); overload;
procedure assign(const name, value: AnsiString); overload;
procedure setValue(const value: AnsiString);
function getName(): AnsiString;
function getValue(): AnsiString;
function getValueComponents(): AnsiString_Array1d;
end;
ProgrammeManifest = class(_Object)
strict private
length: int;
properties: ProgrammeManifestProperty_Array1d;
procedure add(prop: ProgrammeManifestProperty);
procedure remove(index: int);
public
constructor create();
destructor destroy; override;
procedure loadFromStream(stream: Input); virtual;
procedure saveToStream(stream: Output); virtual;
procedure loadFromFile(const fileName: AnsiString); overload;
procedure loadFromFile(const fileName: UnicodeString); overload;
procedure saveToFile(const fileName: AnsiString); overload;
procedure saveToFile(const fileName: UnicodeString); overload;
procedure clear();
procedure setValue(const name, value: AnsiString);
procedure setProperty(index: int; prop: ProgrammeManifestProperty);
function getValue(const name: AnsiString): AnsiString;
function getProperty(index: int): ProgrammeManifestProperty;
function getLength(): int;
function indexOf(const name: AnsiString): int;
function find(const name: AnsiString): ProgrammeManifestProperty;
strict private const
MAX_LINE_LENGTH = int(100);
end;
{%endregion}
{%region routine }
function getComponents(const s: AnsiString): AnsiString_Array1d; overload;
function getComponents(const s: AnsiString; separator: Char): AnsiString_Array1d; overload;
function makeStringOf(const c: AnsiString_Array1d): AnsiString; overload;
function makeStringOf(const c: AnsiString_Array1d; separator: Char): AnsiString; overload;
function ProgrammeManifestProperty_Array1d_create(length: int): ProgrammeManifestProperty_Array1d;
procedure arraycopy(const src: ProgrammeManifestProperty_Array1d; srcOffset: int; const dst: ProgrammeManifestProperty_Array1d; dstOffset: int; length: int); overload;
{%endregion}
implementation
{%region routine }
function getComponents(const s: AnsiString): AnsiString_Array1d;
begin
result := getComponents(s, ',');
end;
function getComponents(const s: AnsiString; separator: Char): AnsiString_Array1d;
var
c: int;
i: int;
j: int;
len: int;
begin
if trim(s) = '' then begin
result := nil;
exit;
end;
c := 1;
i := 1;
len := length(s);
for i := 1 to len do begin
if s[i] = separator then begin
inc(c);
end;
end;
result := String_Array1d_create(c);
c := 0;
i := 1;
j := 1;
for i := 1 to len do begin
if s[i] = separator then begin
result[c] := trim(copy(s, j, i - j));
inc(c);
j := i + 1;
end;
end;
result[c] := trim(copy(s, j, len - j + 1));
end;
function makeStringOf(const c: AnsiString_Array1d): AnsiString;
begin
result := makeStringOf(c, ',');
end;
function makeStringOf(const c: AnsiString_Array1d; separator: Char): AnsiString;
var
i: int;
j: int;
begin
result := '';
j := length(c) - 1;
for i := 0 to j do begin
if i < j then begin
result := result + trim(c[i]) + separator;
end else begin
result := result + trim(c[i]);
end;
end;
end;
function ProgrammeManifestProperty_Array1d_create(length: int): ProgrammeManifestProperty_Array1d;
begin
setLength(result, length);
end;
procedure arraycopy(const src: ProgrammeManifestProperty_Array1d; srcOffset: int; const dst: ProgrammeManifestProperty_Array1d; dstOffset: int; length: int);
var
lim: int;
len: int;
begin
lim := srcOffset + length;
len := System.length(src);
if (lim > len) or (lim < srcOffset) or (srcOffset < 0) or (srcOffset > len) then begin
raise ArrayIndexOutOfBoundsException.create('arraycopy: индекс элемента массива выходит из диапазона.');
end;
lim := dstOffset + length;
len := System.length(dst);
if (lim > len) or (lim < dstOffset) or (dstOffset < 0) or (dstOffset > len) then begin
raise ArrayIndexOutOfBoundsException.create('arraycopy: индекс элемента массива выходит из диапазона.');
end;
move(src[srcOffset], dst[dstOffset], length * sizeof(_Object));
end;
{%endregion}
{%region ProgrammeManifestProperty }
constructor ProgrammeManifestProperty.create();
begin
inherited create();
self.name := '';
self.value := '';
end;
constructor ProgrammeManifestProperty.create(const name: AnsiString);
begin
inherited create();
self.name := name;
self.value := '';
end;
constructor ProgrammeManifestProperty.create(const name, value: AnsiString);
begin
inherited create();
self.name := name;
self.value := value;
end;
procedure ProgrammeManifestProperty.assign(prop: ProgrammeManifestProperty);
begin
self.name := prop.name;
self.value := prop.value;
end;
procedure ProgrammeManifestProperty.assign(const name, value: AnsiString);
begin
self.name := name;
self.value := value;
end;
procedure ProgrammeManifestProperty.setValue(const value: AnsiString);
begin
self.value := value;
end;
function ProgrammeManifestProperty.getName(): AnsiString;
begin
result := name;
end;
function ProgrammeManifestProperty.getValue(): AnsiString;
begin
result := value;
end;
function ProgrammeManifestProperty.getValueComponents(): AnsiString_Array1d;
begin
result := getComponents(value);
end;
{%endregion}
{%region ProgrammeManifest }
constructor ProgrammeManifest.create();
begin
inherited create();
length := 0;
properties := ProgrammeManifestProperty_Array1d_create(16);
end;
destructor ProgrammeManifest.destroy;
var
i: int;
p: ProgrammeManifestProperty_Array1d;
begin
p := properties;
for i := length - 1 downto 0 do begin
p[i].free();
end;
inherited destroy;
end;
procedure ProgrammeManifest.add(prop: ProgrammeManifestProperty);
var
p: ProgrammeManifestProperty_Array1d;
len: int;
begin
p := properties;
len := length;
if len = System.length(p) then begin
p := ProgrammeManifestProperty_Array1d_create(len shl 1);
arraycopy(properties, 0, p, 0, len);
properties := p;
end;
p[len] := prop;
length := len + 1;
end;
procedure ProgrammeManifest.remove(index: int);
var
p: ProgrammeManifestProperty_Array1d;
len: int;
c: int;
begin
p := properties;
len := length - 1;
p[index].free();
c := len - index;
if c > 0 then begin
arraycopy(p, index + 1, p, index, c);
end;
length := len;
p[len] := nil;
end;
procedure ProgrammeManifest.loadFromStream(stream: Input);
var
p: byte_Array1d;
l: int;
prev: int;
curr: int;
name: AnsiString;
value: AnsiString;
begin
l := int(stream.available());
p := byte_Array1d_create(l + 1);
stream.read(p, 0, l);
p[l] := byte($00);
if (p[0] = byte($ef)) and (p[1] = byte($bb)) and (p[2] = byte($bf)) then begin
curr := 3;
end else begin
curr := 0;
end;
clear();
while p[curr] <> byte($00) do begin
prev := curr;
if Char(p[curr]) in ['0'..'9', 'A'..'Z', 'a'..'z'] then begin
inc(curr)
end else begin
break;
end;
while Char(p[curr]) in ['0'..'9', 'A'..'Z', 'a'..'z', '-', '_'] do begin
inc(curr);
end;
name := extractString(p, prev, curr - prev);
if Char(p[curr]) = ':' then begin
inc(curr);
end;
value := '';
repeat
if p[curr] = byte($20) then begin
inc(curr);
end;
prev := curr;
while not(Char(p[curr]) in [#0, #10, #13]) do begin
inc(curr);
end;
value := value + extractString(p, prev, curr - prev);
while Char(p[curr]) in [#10, #13] do begin
inc(curr);
end;
until p[curr] <> byte($20);
setValue(name, value);
end;
end;
procedure ProgrammeManifest.saveToStream(stream: Output);
var
i: int;
j: int;
k: int;
c: int;
p: ProgrammeManifestProperty_Array1d;
stored: UnicodeString;
wideName: UnicodeString;
wideValue: UnicodeString;
begin
p := properties;
stored := '';
for i := 0 to length - 1 do begin
with p[i] do begin
wideName := toUTF16String(getName());
wideValue := toUTF16String(getValue());
end;
stored := stored + wideName + ':';
j := MAX_LINE_LENGTH - (System.length(wideName) + 2);
if j < 0 then begin
j := 0;
end;
k := 0;
repeat
stored := stored + #$0020 + copy(wideValue, k + 1, j - k);
c := int(stored[System.length(stored)]);
if (c >= $d800) and (c <= $dbff) then begin
inc(j);
stored := stored + wideValue[j] + LINE_ENDING;
end else begin
stored := stored + LINE_ENDING;
end;
k := j;
inc(j, MAX_LINE_LENGTH - 1);
until k > System.length(wideValue);
end;
stream.write(stringToByteArray(toUTF8String(stored)));
end;
procedure ProgrammeManifest.loadFromFile(const fileName: AnsiString);
var
f: FileInputStream;
begin
f := FileInputStream.create(fileName);
if f.isInvalidHandle() then begin
f.free();
raise FileNotOpenException.create(fileName);
end;
loadFromStream(f);
end;
procedure ProgrammeManifest.loadFromFile(const fileName: UnicodeString);
var
f: FileInputStream;
begin
f := FileInputStream.create(fileName);
if f.isInvalidHandle() then begin
f.free();
raise FileNotOpenException.create(fileName);
end;
loadFromStream(f);
end;
procedure ProgrammeManifest.saveToFile(const fileName: AnsiString);
var
f: FileOutputStream;
begin
f := FileOutputStream.create(fileName);
if f.isInvalidHandle() then begin
f.free();
raise FileNotOpenException.create(fileName);
end;
saveToStream(f);
end;
procedure ProgrammeManifest.saveToFile(const fileName: UnicodeString);
var
f: FileOutputStream;
begin
f := FileOutputStream.create(fileName);
if f.isInvalidHandle() then begin
f.free();
raise FileNotOpenException.create(fileName);
end;
saveToStream(f);
end;
procedure ProgrammeManifest.clear();
var
i: int;
p: ProgrammeManifestProperty_Array1d;
begin
p := properties;
for i := length - 1 downto 0 do begin
p[i].free();
p[i] := nil;
end;
length := 0;
end;
procedure ProgrammeManifest.setValue(const name, value: AnsiString);
var
i: int;
begin
i := indexOf(name);
if System.length(value) > 0 then begin
if i >= 0 then begin
properties[i].setValue(value);
end else begin
add(ProgrammeManifestProperty.create(name, value));
end;
end else begin
if i >= 0 then begin
remove(i);
end;
end;
end;
procedure ProgrammeManifest.setProperty(index: int; prop: ProgrammeManifestProperty);
begin
getProperty(index).assign(prop);
end;
function ProgrammeManifest.getValue(const name: AnsiString): AnsiString;
var
i: int;
begin
i := indexOf(name);
if i >= 0 then begin
result := properties[i].getValue();
end else begin
result := '';
end;
end;
function ProgrammeManifest.getProperty(index: int): ProgrammeManifestProperty;
begin
if (index < 0) or (index >= length) then begin
raise IndexOutOfBoundsException.create('ProgrammeManifest.getProperty: индекс выходит из диапазона.');
end;
result := properties[index];
end;
function ProgrammeManifest.getLength(): int;
begin
result := length;
end;
function ProgrammeManifest.indexOf(const name: AnsiString): int;
var
i: int;
p: ProgrammeManifestProperty_Array1d;
begin
p := properties;
for i := 0 to length - 1 do begin
if p[i].getName() = name then begin
result := i;
exit;
end;
end;
result := -1;
end;
function ProgrammeManifest.find(const name: AnsiString): ProgrammeManifestProperty;
var
i: int;
begin
i := indexOf(name);
if i >= 0 then begin
result := properties[i];
end else begin
result := nil;
end;
end;
{%endregion}
end.