{
SettingFiles содержит классы для работы с файлами инициализации (.ini).
Copyright © 2016, 2017 Малик Разработчик
Это свободная программа: вы можете перераспространять её и/или
изменять её на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
в каком она была опубликована Фондом свободного программного обеспечения;
либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.
Эта программа распространяется в надежде, что она может быть полезна,
но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЁННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
общественной лицензии GNU.
Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
вместе с этой программой. Если это не так, см.
<http://www.gnu.org/licenses/>.
}
unit SettingFiles;
{$MODE DELPHI,EXTENDEDSYNTAX ON}
interface
uses
Lang, IOStream, FileIO, TextFiles;
{$ASMMODE INTEL,CALLING REGISTER,INLINE ON,GOTO ON}
{$H+,I-,J-,M-,Q-,R-,T-}
type
InvalidSectionIdentException = class;
AbstractInitializationFile = class;
NormalInitializationFile = class;
InvalidSectionIdentException = class(Exception)
public
constructor create(const message: String);
end;
AbstractInitializationFile = class(_Object)
public
function readSections(): String_Array1d; virtual; abstract;
function readSection(const section: String): String_Array1d; virtual; abstract;
function readString(const section, ident, default: String): String; virtual; abstract;
procedure writeString(const section, ident, value: String); virtual; abstract;
procedure eraseSection(const section: String); virtual; abstract;
procedure deleteIdent(const section, ident: String); virtual; abstract;
procedure loadFromStream(stream: Input); virtual; abstract;
procedure saveToStream(stream: Output); virtual; abstract;
procedure updateFile(); virtual; abstract;
function isSectionExists(const section: String): boolean; virtual;
function isIdentExists(const section, ident: String): boolean; virtual;
function readBoolean(const section, ident: String; default: boolean): boolean; virtual;
function readInt(const section, ident: String; default: int): int; virtual;
function readLong(const section, ident: String; default: long): long; virtual;
function readFloat(const section, ident: String; default: float): float; virtual;
function readDouble(const section, ident: String; default: double): double; virtual;
function readReal(const section, ident: String; default: real): real; virtual;
procedure writeBoolean(const section, ident: String; value: boolean); virtual;
procedure writeInt(const section, ident: String; value: int); virtual;
procedure writeLong(const section, ident: String; value: long); virtual;
procedure writeFloat(const section, ident: String; value: float); virtual;
procedure writeDouble(const section, ident: String; value: double); virtual;
procedure writeReal(const section, ident: String; value: real); virtual;
end;
NormalInitializationFile = class(AbstractInitializationFile)
private
fileName: String;
sections: int_Array1d;
strings: String_Array1d;
sectionsCount: int;
stringsCount: int;
function getSectionAt(index: int): String;
function indexOfSection(const section: String): int;
function indexOfIdent(const section, ident: String): int;
procedure insertString(index: int; const str: String);
procedure deleteString(index: int);
public
constructor create(); overload;
constructor create(const fileName: String); overload;
function isSectionExists(const section: String): boolean; override;
function isIdentExists(const section, ident: String): boolean; override;
function readSections(): String_Array1d; override;
function readSection(const section: String): String_Array1d; override;
function readString(const section, ident, default: String): String; override;
procedure writeString(const section, ident, value: String); override;
procedure eraseSection(const section: String); override;
procedure deleteIdent(const section, ident: String); override;
procedure loadFromStream(stream: Input); override;
procedure saveToStream(stream: Output); override;
procedure updateFile(); override;
function getFileName(): String; virtual;
strict private
const BRACKET_LEFT = '[';
const BRACKET_RIGHT = ']';
const IDENT_VALUE_SEPARATOR = '=';
const LINE_ESCAPE = '\';
const COMMENT_START = ';';
end;
resourcestring
msgSectionNameCannotBeEmpty = 'Название секции не может быть пустой строкой.';
msgIdentNameCannotBeEmpty = 'Название параметра не может быть пустой строкой.';
msgIdentCannotContainSeparator = 'Название параметра не может содержать символ «=».';
msgFileNameNotSpecified = 'Имя файла не было задано при создании этого экземпляра класса ';
msgFileCannotBeWritten = 'Не удалось записать данные в файл: ';
implementation
{ unit private members }
function booleanToString(value: boolean): String;
begin
if value = false then begin
result := '0';
end else begin
result := '1';
end;
end;
{ InvalidSectionIdentException }
constructor InvalidSectionIdentException.create(const message: String);
begin
inherited create(message);
end;
{ AbstractInitializationFile }
function AbstractInitializationFile.isSectionExists(const section: String): boolean;
var
str: String;
s: String_Array1d;
i: int;
begin
str := stringTrim(section);
s := readSections();
for i := length(s) - 1 downto 0 do begin
if stringTrim(s[i]) = str then begin
result := true;
exit;
end;
end;
result := false;
end;
function AbstractInitializationFile.isIdentExists(const section, ident: String): boolean;
var
str: String;
s: String_Array1d;
i: int;
begin
str := stringTrim(ident);
s := readSection(stringTrim(section));
for i := length(s) - 1 downto 0 do begin
if stringTrim(s[i]) = str then begin
result := true;
exit;
end;
end;
result := false;
end;
function AbstractInitializationFile.readBoolean(const section, ident: String;
default: boolean): boolean;
var
s: String;
begin
s := stringTrim(readString(section, ident, booleanToString(default)));
if length(s) <> 1 then begin
result := default;
exit;
end;
case s[1] of
'0': result := false;
'1': result := true;
else result := default;
end;
end;
function AbstractInitializationFile.readInt(const section, ident: String;
default: int): int;
var
s: String;
begin
s := stringTrim(readString(section, ident, intToString(default)));
if stringStartsWith('0x', s) or stringStartsWith('0X', s) then begin
result := stringParseInt(copy(s, 3, length(s) - 2), 16, default);
exit;
end;
if stringStartsWith('$', s) then begin
result := stringParseInt(copy(s, 2, length(s) - 1), 16, default);
exit;
end;
result := stringParseInt(s, 10, default);
end;
function AbstractInitializationFile.readLong(const section, ident: String;
default: long): long;
var
s: String;
begin
s := stringTrim(readString(section, ident, longToString(default)));
if stringStartsWith('0x', s) or stringStartsWith('0X', s) then begin
result := stringParseLong(copy(s, 3, length(s) - 2), 16, default);
exit;
end;
if stringStartsWith('$', s) then begin
result := stringParseLong(copy(s, 2, length(s) - 1), 16, default);
exit;
end;
result := stringParseLong(s, 10, default);
end;
function AbstractInitializationFile.readFloat(const section, ident: String;
default: float): float;
begin
result := realToFloat(stringParseReal(stringTrim(readString(
section, ident, realToString(default))), default));
end;
function AbstractInitializationFile.readDouble(const section, ident: String;
default: double): double;
begin
result := realToDouble(stringParseReal(stringTrim(readString(
section, ident, realToString(default))), default));
end;
function AbstractInitializationFile.readReal(const section, ident: String;
default: real): real;
begin
result := stringParseReal(stringTrim(readString(
section, ident, realToString(default))), default);
end;
procedure AbstractInitializationFile.writeBoolean(const section, ident: String; value: boolean);
begin
writeString(section, ident, booleanToString(value));
end;
procedure AbstractInitializationFile.writeInt(const section, ident: String; value: int);
begin
writeString(section, ident, intToString(value));
end;
procedure AbstractInitializationFile.writeLong(const section, ident: String; value: long);
begin
writeString(section, ident, longToString(value));
end;
procedure AbstractInitializationFile.writeFloat(const section, ident: String; value: float);
begin
writeString(section, ident, realToString(value));
end;
procedure AbstractInitializationFile.writeDouble(const section, ident: String; value: double);
begin
writeString(section, ident, realToString(value));
end;
procedure AbstractInitializationFile.writeReal(const section, ident: String; value: real);
begin
writeString(section, ident, realToString(value));
end;
{ NormalInitializationFile }
constructor NormalInitializationFile.create();
begin
create('');
end;
constructor NormalInitializationFile.create(const fileName: String);
var
stream: FileInputStream;
begin
inherited create();
self.fileName := fileName;
if length(fileName) = 0 then begin
exit;
end;
stream := FileInputStream.create(stringToUTF16(fileName));
if stream.hasOpenError() then begin
stream.free();
exit;
end;
loadFromStream(stream);
end;
function NormalInitializationFile.getSectionAt(index: int): String;
begin
if (index < 0) or (index >= sectionsCount) then begin
result := '';
exit;
end;
index := sections[index];
result := stringTrim(strings[index]);
result := stringTrim(copy(result, 2, length(result) - 2));
end;
function NormalInitializationFile.indexOfSection(const section: String): int;
var
i: int;
s: String;
begin
s := stringTrim(section);
for i := 0 to sectionsCount - 1 do begin
if getSectionAt(i) = s then begin
result := i;
exit;
end;
end;
result := -1;
end;
function NormalInitializationFile.indexOfIdent(const section, ident: String): int;
var
e: int;
i: int;
j: int;
l: int;
s: String_Array1d;
t: String;
d: String;
begin
j := indexOfSection(section);
if j < 0 then begin
result := -1;
exit;
end;
s := strings;
d := stringTrim(ident);
for i := sections[j] + 1 to stringsCount - 1 do begin
t := stringTrim(s[i]);
l := length(t);
if (l <= 0) or (t[1] = COMMENT_START) then begin
continue;
end;
if (t[1] = BRACKET_LEFT) and (t[l] = BRACKET_RIGHT) then begin
break;
end;
e := pos(IDENT_VALUE_SEPARATOR, t);
if e <= 0 then begin
continue;
end;
if stringTrim(copy(t, 1, e - 1)) = d then begin
result := i;
exit;
end;
end;
result := -1;
end;
procedure NormalInitializationFile.insertString(index: int; const str: String);
var
s: String_Array1d;
t: int_Array1d;
c: int;
i: int;
j: int;
ts: String;
begin
s := strings;
c := stringsCount;
if c = length(s) then begin
s := String_Array1d_create((c shl 1) + 1);
arraycopyStrings(strings, 0, s, 0, c);
strings := s;
end;
arraycopyStrings(s, index, s, index + 1, c - index);
s[index] := str;
stringsCount := c + 1;
t := sections;
c := sectionsCount;
for i := c - 1 downto 0 do begin
j := t[i];
if j < index then begin
break;
end;
t[i] := j + 1;
end;
ts := stringTrim(str);
if stringStartsWith(BRACKET_LEFT, ts) and stringEndsWith(BRACKET_RIGHT, ts) then begin
j := c;
for i := 0 to c - 1 do begin
if t[i] > index then begin
j := i;
break;
end;
end;
if c = length(t) then begin
t := int_Array1d_create((c shl 1) + 1);
arraycopyPrimitives(sections, 0, t, 0, c);
sections := t;
end;
arraycopyPrimitives(t, j, t, j + 1, c - j);
t[j] := index;
sectionsCount := c + 1;
end;
end;
procedure NormalInitializationFile.deleteString(index: int);
var
s: String_Array1d;
t: int_Array1d;
c: int;
i: int;
j: int;
begin
s := strings;
c := stringsCount;
arraycopyStrings(s, index + 1, s, index, c - index - 1);
s[c - 1] := '';
stringsCount := c - 1;
t := sections;
c := sectionsCount;
for i := c - 1 downto 0 do begin
j := t[i];
if j = index then begin
arraycopyPrimitives(t, i + 1, t, i, c - i - 1);
sectionsCount := c - 1;
break;
end;
if j < index then begin
break;
end;
t[i] := j - 1;
end;
end;
function NormalInitializationFile.isSectionExists(const section: String): boolean;
begin
result := indexOfSection(section) >= 0;
end;
function NormalInitializationFile.isIdentExists(const section, ident: String): boolean;
begin
result := indexOfIdent(section, ident) >= 0;
end;
function NormalInitializationFile.readSections(): String_Array1d;
var
i: int;
begin
result := String_Array1d_create(sectionsCount);
for i := sectionsCount - 1 downto 0 do begin
result[i] := getSectionAt(i);
end;
end;
function NormalInitializationFile.readSection(const section: String): String_Array1d;
var
e: int;
i: int;
j: int;
l: int;
z: int;
s: String_Array1d;
x: String_Array1d;
t: String;
begin
j := indexOfSection(section);
if j < 0 then begin
result := nil;
exit;
end;
z := 0;
s := strings;
x := String_Array1d_create(8);
for i := sections[j] + 1 to stringsCount - 1 do begin
t := stringTrim(s[i]);
l := length(t);
if (l <= 0) or (t[1] = COMMENT_START) then begin
continue;
end;
if (t[1] = BRACKET_LEFT) and (t[l] = BRACKET_RIGHT) then begin
break;
end;
e := pos(IDENT_VALUE_SEPARATOR, t);
if e <= 0 then begin
continue;
end;
if length(x) = z then begin
result := x;
x := String_Array1d_create((z shl 1) + 1);
arraycopyStrings(result, 0, x, 0, z);
result := nil;
end;
x[z] := stringTrim(copy(t, 1, e - 1));
inc(z);
end;
result := String_Array1d_create(z);
arraycopyStrings(x, 0, result, 0, z);
end;
function NormalInitializationFile.readString(const section, ident, default: String): String;
var
i: int;
j: int;
s: String;
begin
i := indexOfIdent(section, ident);
if i < 0 then begin
result := default;
exit;
end;
s := strings[i];
j := pos(IDENT_VALUE_SEPARATOR, s);
result := stringTrim(copy(s, j + 1, length(s) - j));
end;
procedure NormalInitializationFile.writeString(const section, ident, value: String);
var
i: int;
begin
if length(section) = 0 then begin
raise InvalidSectionIdentException.create(msgSectionNameCannotBeEmpty);
end;
if length(ident) = 0 then begin
raise InvalidSectionIdentException.create(msgIdentNameCannotBeEmpty);
end;
if pos(IDENT_VALUE_SEPARATOR, ident) > 0 then begin
raise InvalidSectionIdentException.create(msgIdentCannotContainSeparator);
end;
i := indexOfIdent(section, ident);
if i < 0 then begin
i := indexOfSection(section);
if i < 0 then begin
insertString(stringsCount, '');
insertString(stringsCount, BRACKET_LEFT + stringTrim(section) + BRACKET_RIGHT);
insertString(stringsCount, stringTrim(ident) +
IDENT_VALUE_SEPARATOR + stringTrim(value));
exit;
end;
insertString(sections[i] + 1, stringTrim(ident) +
IDENT_VALUE_SEPARATOR + stringTrim(value));
exit;
end;
strings[i] := stringTrim(ident) + IDENT_VALUE_SEPARATOR + stringTrim(value);
end;
procedure NormalInitializationFile.eraseSection(const section: String);
var
i: int;
j: int;
k: int;
t: int_Array1d;
s: String_Array1d;
begin
k := indexOfSection(section);
if k < 0 then begin
exit;
end;
t := sections;
i := t[k];
if k < sectionsCount - 1 then begin
j := t[k + 1];
end else begin
j := stringsCount;
end;
s := strings;
k := stringsCount;
arraycopyStrings(s, j, s, i, k - j);
stringsCount := k - (j - i);
for i := stringsCount to k - 1 do begin
s[i] := '';
end;
end;
procedure NormalInitializationFile.deleteIdent(const section, ident: String);
var
i: int;
begin
i := indexOfIdent(section, ident);
if i >= 0 then begin
deleteString(i);
end;
end;
procedure NormalInitializationFile.loadFromStream(stream: Input);
var
i: int;
j: int;
k: int;
t: int_Array1d;
s: String_Array1d;
str: String;
begin
s := loadStringsFromStream(stream);
k := length(s);
for i := k - 1 downto 1 do begin
str := s[i - 1];
if stringEndsWith(LINE_ESCAPE, str) then begin
s[i - 1] := copy(str, 1, length(str) - 1) + stringTrim(s[i]);
arraycopyStrings(s, i + 1, s, i, k - i - 1);
dec(k);
s[k] := '';
end;
end;
j := 0;
for i := 0 to k - 1 do begin
str := stringTrim(s[i]);
if stringStartsWith(BRACKET_LEFT, str) and stringEndsWith(BRACKET_RIGHT, str) then begin
inc(j);
end;
end;
t := int_Array1d_create(j);
j := 0;
for i := 0 to k - 1 do begin
str := stringTrim(s[i]);
if stringStartsWith(BRACKET_LEFT, str) and stringEndsWith(BRACKET_RIGHT, str) then begin
t[j] := i;
inc(j);
end;
end;
sections := t;
strings := s;
sectionsCount := j;
stringsCount := k;
end;
procedure NormalInitializationFile.saveToStream(stream: Output);
begin
saveStringsToStream(stream, strings, stringsCount);
end;
procedure NormalInitializationFile.updateFile();
var
f: FileOutputStream;
fn: String;
begin
fn := fileName;
if length(fn) = 0 then begin
raise IOException.create(msgFileNameNotSpecified + self.getClass().getCanonicalName());
end;
f := FileOutputStream.create(stringToUTF16(fn), false);
if f.hasOpenError() then begin
f.free();
raise IOException.create(msgFileCannotBeWritten + fn);
end;
saveToStream(f);
end;
function NormalInitializationFile.getFileName(): String;
begin
result := fileName;
end;
end.