osindepended.fileformats.text.plain.pas

Переключить прокрутку окна
Загрузить этот исходный код

{
    osindepended.fileformats.text.plain — модуль для реализации простого текстового файлового формата.

    Copyright © 2021 Малик Разработчик

    Это свободная программа: вы можете перераспространять её и/или
    изменять её на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
    в каком она была опубликована Фондом свободного программного обеспечения;
    либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

    Эта программа распространяется в надежде, что она может быть полезна,
    но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
    или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЁННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
    общественной лицензии GNU.

    Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
    вместе с этой программой. Если это не так, см.
    <http://www.gnu.org/licenses/>.
}

unit osindepended.fileformats.text.plain;

{$MODE DELPHI}

interface

uses
    pascalx.lang,
    pascalx.io,
    osindepended.fileformats,
    osindepended.fileformats.text;

{$ASMMODE INTEL,CALLING REGISTER,TYPEINFO ON}

{%region public }
type
    PlainTextCodec = class;

    PlainTextCodec = class(DynamicalyAllocatedObject, DataHolder, DataDecoder, DataEncoder, TextDecoder, TextEncoder)
    private
        count: int;
        strings: AnsiString_Array1d;
        lineEnding: AnsiString;
    public
        constructor create(); override;
        function toString(): AnsiString; override;
        function getLineEnding(): AnsiString;
        function getAnsiStrings(): AnsiString_Array1d;
        function getUnicodeStrings(): UnicodeString_Array1d;
        function isEmpty(): boolean;
        procedure clear();
        procedure setLineEnding(const lineEnding: AnsiString);
        procedure setAnsiStrings(const strings: AnsiString_Array1d);
        procedure setUnicodeStrings(const strings: UnicodeString_Array1d);
        procedure loadFromInputStream(stream: Input);
        procedure loadFromDataInputStream(stream: DataInput);
        procedure saveToOutputStream(stream: Output);
        procedure saveToDataOutputStream(stream: DataOutput);
    end;
{%endregion}

implementation

{%region PlainTextCodec }
    constructor PlainTextCodec.create();
    begin
        inherited create();
        self.lineEnding := LINE_ENDING;
    end;

    function PlainTextCodec.toString(): AnsiString;
    var
        i: int;
        count: int;
        text: AnsiString;
        lineEnding: AnsiString;
        strings: AnsiString_Array1d;
    begin
        count := self.count;
        if count <= 0 then begin
            result := '';
            exit;
        end;
        lineEnding := self.lineEnding;
        strings := self.strings;
        text := strings[0];
        for i := 1 to count - 1 do begin
            text := text + lineEnding + strings[i];
        end;
        result := text;
    end;

    function PlainTextCodec.getLineEnding(): AnsiString;
    begin
        result := lineEnding;
    end;

    function PlainTextCodec.getAnsiStrings(): AnsiString_Array1d;
    var
        count: int;
        astrings: AnsiString_Array1d;
    begin
        count := self.count;
        astrings := AnsiString_Array1d_create(count);
        arraycopyAnsiStrings(strings, 0, astrings, 0, count);
        result := astrings;
    end;

    function PlainTextCodec.getUnicodeStrings(): UnicodeString_Array1d;
    var
        i: int;
        count: int;
        astrings: AnsiString_Array1d;
        ustrings: UnicodeString_Array1d;
    begin
        count := self.count;
        astrings := self.strings;
        ustrings := UnicodeString_Array1d_create(count);
        for i := count - 1 downto 0 do begin
            ustrings[i] := stringToUTF16(astrings[i]);
        end;
        result := ustrings;
    end;

    function PlainTextCodec.isEmpty(): boolean;
    begin
        result := false;
    end;

    procedure PlainTextCodec.clear();
    begin
        count := 0;
        strings := nil;
        lineEnding := LINE_ENDING;
    end;

    procedure PlainTextCodec.setLineEnding(const lineEnding: AnsiString);
    begin
        if (lineEnding <> #$0d#$0a) and (lineEnding <> #$0d) and (lineEnding <> #$0a) then begin
            raise InvalidDataFormatException.create('PlainTextCodec.setLineEnding: ' + msgInvalidDataFormat);
        end;
        self.lineEnding := lineEnding;
    end;

    procedure PlainTextCodec.setAnsiStrings(const strings: AnsiString_Array1d);
    begin
        self.count := length(strings);
        self.strings := strings;
    end;

    procedure PlainTextCodec.setUnicodeStrings(const strings: UnicodeString_Array1d);
    var
        i: int;
        count: int;
        astrings: AnsiString_Array1d;
    begin
        count := length(strings);
        astrings := AnsiString_Array1d_create(count);
        for i := count - 1 downto 0 do begin
            astrings[i] := stringToUTF8(strings[i]);
        end;
        self.count := count;
        self.strings := astrings;
    end;

    procedure PlainTextCodec.loadFromInputStream(stream: Input);
    var
        i1: int;
        i2: int;
        len: int;
        count: int;
        data: byte_Array1d;
        lineEnding: AnsiString;
        strings: AnsiString_Array1d;
    begin
        len := int(longMin(stream.available(), INT_MAX_VALUE));
        if len <= 0 then begin
            self.count := 0;
            self.strings := nil;
            self.lineEnding := LINE_ENDING;
            exit;
        end;
        data := byte_Array1d_create(len);
        len := stream.read(data, 0, len);
        if len <= 0 then begin
            self.count := 0;
            self.strings := nil;
            self.lineEnding := LINE_ENDING;
            exit;
        end;
        count := 1;
        for i2 := 0 to len - 1 do begin
            case int(data[i2]) of
            $0d: begin
                inc(count);
            end;
            $0a: begin
                if (i2 <= 0) or (data[i2 - 1] <> $0d) then begin
                    inc(count);
                end;
            end;
            end;
        end;
        lineEnding := LINE_ENDING;
        strings := AnsiString_Array1d_create(count);
        count := 0;
        i1 := 0;
        for i2 := 0 to len - 1 do begin
            case int(data[i2]) of
            $0d: begin
                if count = 0 then begin
                    lineEnding := #$0d;
                end;
                strings[count] := AnsiString_create(data, i1, i2 - i1);
                inc(count);
                i1 := i2 + 1;
            end;
            $0a: begin
                if (i2 <= 0) or (data[i2 - 1] <> $0d) then begin
                    if count = 0 then begin
                        lineEnding := #$0a;
                    end;
                    strings[count] := AnsiString_create(data, i1, i2 - i1);
                    inc(count);
                end else begin
                    if count = 1 then begin
                        lineEnding := #$0d#$0a;
                    end;
                end;
                i1 := i2 + 1;
            end;
            end;
        end;
        strings[count] := AnsiString_create(data, i1, len - i1);
        self.count := count + 1;
        self.strings := strings;
        self.lineEnding := lineEnding;
    end;

    procedure PlainTextCodec.loadFromDataInputStream(stream: DataInput);
    begin
        loadFromInputStream(stream);
    end;

    procedure PlainTextCodec.saveToOutputStream(stream: Output);
    begin
        saveToDataOutputStream(DataOutputStream.create(stream));
    end;

    procedure PlainTextCodec.saveToDataOutputStream(stream: DataOutput);
    var
        i: int;
        count: int;
        lineEnding: AnsiString;
        strings: AnsiString_Array1d;
    begin
        count := self.count;
        if count <= 0 then exit;
        strings := self.strings;
        lineEnding := self.lineEnding;
        stream.writeAnsiChars(strings[0]);
        for i := 1 to count - 1 do begin
            stream.writeAnsiChars(lineEnding);
            stream.writeAnsiChars(strings[i]);
        end;
    end;
{%endregion}

initialization
    classInfoAdd([
        typeInfo(PlainTextCodec)
    ]);

end.