From 9e69b64f1baeda026f94cd059c2b9a8b44dacaad Mon Sep 17 00:00:00 2001 From: Vadim Lopatin Date: Tue, 16 Dec 2014 17:01:22 +0300 Subject: [PATCH] improve documentation --- src/dlangui/core/i18n.d | 79 +++++++++++----- src/dlangui/core/linestream.d | 113 +++++++++++++++-------- src/dlangui/core/logger.d | 167 +++++++++++++++++++++------------- 3 files changed, 234 insertions(+), 125 deletions(-) diff --git a/src/dlangui/core/i18n.d b/src/dlangui/core/i18n.d index 4f7a3cbe..c50b22dc 100644 --- a/src/dlangui/core/i18n.d +++ b/src/dlangui/core/i18n.d @@ -1,14 +1,32 @@ // Written in the D programming language. /** -This module contains internationalization support implementation. +This module contains UI internationalization support implementation. -Translation files contain of simple key=value pair lines. +UIString struct provides string container which can be either plain unicode string or id of string resource. -STRING_RESOURCE_ID=Translation text. +Translation strings are being stored in translation files, consisting of simple key=value pair lines: +--- +STRING_RESOURCE_ID=Translation text 1 +ANOTHER_STRING_RESOURCE_ID=Translation text 2 +--- Supports fallback to another translation file (e.g. default language). +If string resource is not found neither in main nor fallback translation files, UNTRANSLATED: RESOURCE_ID will be returned. + +String resources must be placed in i18n subdirectory inside one or more resource directories (set using Platform.instance.resourceDirs +property on application initialization). + +File names must be language code with extension .ini (e.g. en.ini, fr.ini, es.ini) + +If several files for the same language are found in (different directories) their content will be merged. It's useful to merge string resources +from DLangUI framework with resources of application. + +Set interface language using Platform.instance.uiLanguage in UIAppMain during initialization of application settings: +--- +Platform.instance.uiLanguage = "en"; +--- Synopsis: @@ -44,7 +62,12 @@ private import dlangui.core.linestream; private import std.utf; private import std.algorithm; -/** container for UI string - either raw value or string resource ID */ +/** + Container for UI string - either raw value or string resource ID + + Set resource id (string) or plain unicode text (dstring) to it, and get dstring. + +*/ struct UIString { /** if not null, use it, otherwise lookup by id */ private dstring _value; @@ -61,13 +84,14 @@ struct UIString { } - + /// Returns string resource id @property string id() const { return _id; } + /// Sets string resource id @property void id(string ID) { _id = ID; _value = null; } - /** get value (either raw or translated by id) */ + /** Get value (either raw or translated by id) */ @property dstring value() const { if (_value !is null) return _value; @@ -76,55 +100,59 @@ struct UIString { // translate ID to dstring return i18n.get(_id); } - /** set raw value */ + /** Set raw value using property */ @property void value(dstring newValue) { _value = newValue; } - /** assign raw value */ + /** Assign raw value */ ref UIString opAssign(dstring rawValue) { _value = rawValue; _id = null; return this; } - /** assign ID */ + /** Assign string resource id */ ref UIString opAssign(string ID) { _id = ID; _value = null; return this; } - /** default conversion to dstring */ + /** Default conversion to dstring */ alias value this; } -/** UIString item collection. */ +/** + UIString item collection + + Based on array. +*/ struct UIStringCollection { private UIString[] _items; private int _length; - /** returns number of items */ + /** Returns number of items */ @property int length() { return _length; } - /** slice */ + /** Slice */ UIString[] opIndex() { return _items[0 .. _length]; } - /** slice */ + /** Slice */ UIString[] opSlice() { return _items[0 .. _length]; } - /** slice */ + /** Slice */ UIString[] opSlice(size_t start, size_t end) { return _items[start .. end]; } - /** read item by index */ + /** Read item by index */ UIString opIndex(size_t index) { return _items[index]; } - /** modify item by index */ + /** Modify item by index */ UIString opIndexAssign(UIString value, size_t index) { _items[index] = value; return _items[index]; } - /** return unicode string for item by index */ + /** Return unicode string for item by index */ dstring get(size_t index) { return _items[index].value; } @@ -161,7 +189,7 @@ struct UIStringCollection { add(item); } } - /** remove all items */ + /** Remove all items */ void clear() { _items.length = 0; _length = 0; @@ -226,14 +254,14 @@ struct UIStringCollection { } } -/** UI Strings internationalization translator. */ +/** UI Strings internationalization translator */ synchronized class UIStringTranslator { private UIStringList _main; private UIStringList _fallback; private string[] _resourceDirs; - /** looks for i18n directory inside one of passed dirs, and uses first found as directory to read i18n files from */ + /** Looks for i18n directory inside one of passed dirs, and uses first found as directory to read i18n files from */ void findTranslationsDir(string[] dirs ...) { _resourceDirs.length = 0; import std.file; @@ -246,7 +274,7 @@ synchronized class UIStringTranslator { } } - /** convert resource path - append resource dir if necessary */ + /** Convert resource path - append resource dir if necessary */ string[] convertResourcePaths(string filename) { if (filename is null) return null; @@ -264,12 +292,13 @@ synchronized class UIStringTranslator { return res; } + /// create empty translator this() { _main = new shared UIStringList(); _fallback = new shared UIStringList(); } - /** load translation file(s) */ + /** Load translation file(s) */ bool load(string mainFilename, string fallbackFilename = null) { _main.clear(); _fallback.clear(); @@ -280,7 +309,7 @@ synchronized class UIStringTranslator { return res; } - /** translate string ID to string (returns "UNTRANSLATED: id" for missing values) */ + /** Translate string ID to string (returns "UNTRANSLATED: id" for missing values) */ dstring get(string id) { if (id is null) return null; @@ -365,7 +394,7 @@ private shared class UIStringList { } } -/** Global translator object. */ +/** Global UI translator object */ shared UIStringTranslator i18n; shared static this() { i18n = new shared UIStringTranslator(); diff --git a/src/dlangui/core/linestream.d b/src/dlangui/core/linestream.d index 1e7e0d63..8edc6892 100644 --- a/src/dlangui/core/linestream.d +++ b/src/dlangui/core/linestream.d @@ -1,7 +1,10 @@ // Written in the D programming language. /** -This module contains text file reader implementation. + +This module contains text stream reader implementation + +Implements class LineStream for reading of unicode text from stream and returning it by lines. Support utf8, utf16, utf32 be and le encodings, and line endings - according to D language source file specification. @@ -51,44 +54,74 @@ import std.stream; import std.stdio; import std.conv; +/** + Support reading of file (or string in memory) by lines + + Support utf8, utf16, utf32 be and le encodings, and line endings - according to D language source file specification. + + Low resource consuming. Doesn't flood with GC allocations. Dup line if you want to store it somewhere. + + Tracks line number. +*/ class LineStream { + /// File encoding public enum EncodingType { + /// plaing ASCII (character codes must be <= 127) ASCII, + /// utf-8 unicode UTF8, + /// utf-16 unicode big endian UTF16BE, + /// utf-16 unicode little endian UTF16LE, + /// utf-32 unicode big endian UTF32BE, + /// utf-32 unicode little endian UTF32LE }; - InputStream _stream; - string _filename; - ubyte[] _buf; // stream reading buffer - uint _pos; // reading position of stream buffer - uint _len; // number of bytes in stream buffer - bool _streamEof; // true if input stream is in EOF state - uint _line; // current line number - - uint _textPos; // start of text line in text buffer - uint _textLen; // position of last filled char in text buffer + 1 - dchar[] _textBuf; // text buffer - bool _eof; // end of file, no more lines + /// Error codes + public enum ErrorCodes { + /// invalid character for current encoding + INVALID_CHARACTER + }; + + private InputStream _stream; + private string _filename; + private ubyte[] _buf; // stream reading buffer + private uint _pos; // reading position of stream buffer + private uint _len; // number of bytes in stream buffer + private bool _streamEof; // true if input stream is in EOF state + private uint _line; // current line number + private uint _textPos; // start of text line in text buffer + private uint _textLen; // position of last filled char in text buffer + 1 + private dchar[] _textBuf; // text buffer + private bool _eof; // end of file, no more lines + + /// Returns file name @property string filename() { return _filename; } + /// Returns current line number @property uint line() { return _line; } + /// Returns file encoding EncodingType @property EncodingType encoding() { return _encoding; } + /// Returns error code @property int errorCode() { return _errorCode; } + /// Returns error message @property string errorMessage() { return _errorMessage; } + /// Returns line where error is found @property int errorLine() { return _errorLine; } + /// Returns line position (number of character in line) where error is found @property int errorPos() { return _errorPos; } - immutable EncodingType _encoding; + private immutable EncodingType _encoding; - int _errorCode; - string _errorMessage; - uint _errorLine; - uint _errorPos; + private int _errorCode; + private string _errorMessage; + private uint _errorLine; + private uint _errorPos; + /// Open file with known encoding protected this(InputStream stream, string filename, EncodingType encoding, ubyte[] buf, uint offset, uint len) { _filename = filename; _stream = stream; @@ -99,8 +132,8 @@ class LineStream { _streamEof = _stream.eof; } - // returns slice of bytes available in buffer - uint readBytes() { + /// returns slice of bytes available in buffer + protected uint readBytes() { uint bytesLeft = _len - _pos; if (_streamEof || bytesLeft > QUARTER_BYTE_BUFFER_SIZE) return bytesLeft; @@ -117,12 +150,12 @@ class LineStream { } // when bytes consumed from byte buffer, call this method to update position - void consumedBytes(uint count) { + protected void consumedBytes(uint count) { _pos += count; } // reserve text buffer for specified number of characters, and return pointer to first free character in buffer - dchar * reserveTextBuf(uint len) { + protected dchar * reserveTextBuf(uint len) { // create new text buffer if necessary if (_textBuf == null) { if (len < TEXT_BUFFER_SIZE) @@ -153,12 +186,12 @@ class LineStream { return _textBuf.ptr + _textLen; } - void appendedText(uint len) { + protected void appendedText(uint len) { //writeln("appended ", len, " chars of text"); //:", _textBuf[_textLen .. _textLen + len]); _textLen += len; } - void setError(int code, string message, uint errorLine, uint errorPos) { + protected void setError(int code, string message, uint errorLine, uint errorPos) { _errorCode = code; _errorMessage = message; _errorLine = errorLine; @@ -166,9 +199,12 @@ class LineStream { } // override to decode text - abstract uint decodeText(); - + protected abstract uint decodeText(); + + /// Unknown line position immutable static uint LINE_POSITION_UNDEFINED = uint.max; + + /// Read line from stream public dchar[] readLine() { if (_errorCode != 0) { //writeln("error ", _errorCode, ": ", _errorMessage, " in line ", _errorLine); @@ -250,11 +286,11 @@ class LineStream { return _textBuf[lineStart .. lineEnd]; } - immutable static int TEXT_BUFFER_SIZE = 1024; - immutable static int BYTE_BUFFER_SIZE = 512; - immutable static int QUARTER_BYTE_BUFFER_SIZE = BYTE_BUFFER_SIZE / 4; + protected immutable static int TEXT_BUFFER_SIZE = 1024; + protected immutable static int BYTE_BUFFER_SIZE = 512; + protected immutable static int QUARTER_BYTE_BUFFER_SIZE = BYTE_BUFFER_SIZE / 4; - // factory for string parser + /// Factory method for string parser public static LineStream create(string code, string filename = "") { uint len = cast(uint)code.length; ubyte[] data = new ubyte[len + 3]; @@ -268,7 +304,7 @@ class LineStream { return create(stream, filename); } - // factory + /// Factory for InputStream parser public static LineStream create(InputStream stream, string filename) { ubyte[] buf = new ubyte[BYTE_BUFFER_SIZE]; buf[0] = buf[1] = buf[2] = buf[3] = 0; @@ -293,13 +329,12 @@ class LineStream { protected bool invalidCharFlag; protected void invalidCharError() { uint pos = _textLen - _textPos + 1; - setError(1, "Invalid character in line " ~ to!string(_line) ~ ":" ~ to!string(pos), _line, pos); + setError(ErrorCodes.INVALID_CHARACTER, "Invalid character in line " ~ to!string(_line) ~ ":" ~ to!string(pos), _line, pos); } } - -class AsciiLineStream : LineStream { +private class AsciiLineStream : LineStream { this(InputStream stream, string filename, ubyte[] buf, uint len) { super(stream, filename, EncodingType.ASCII, buf, 0, len); } @@ -332,7 +367,7 @@ class AsciiLineStream : LineStream { } -class Utf8LineStream : LineStream { +private class Utf8LineStream : LineStream { this(InputStream stream, string filename, ubyte[] buf, uint len) { super(stream, filename, EncodingType.UTF8, buf, 3, len); } @@ -448,7 +483,7 @@ class Utf8LineStream : LineStream { } } -class Utf16beLineStream : LineStream { +private class Utf16beLineStream : LineStream { this(InputStream stream, string filename, ubyte[] buf, uint len) { super(stream, filename, EncodingType.UTF16BE, buf, 2, len); } @@ -482,7 +517,7 @@ class Utf16beLineStream : LineStream { } } -class Utf16leLineStream : LineStream { +private class Utf16leLineStream : LineStream { this(InputStream stream, string filename, ubyte[] buf, uint len) { super(stream, filename, EncodingType.UTF16LE, buf, 2, len); } @@ -516,7 +551,7 @@ class Utf16leLineStream : LineStream { } } -class Utf32beLineStream : LineStream { +private class Utf32beLineStream : LineStream { this(InputStream stream, string filename, ubyte[] buf, uint len) { super(stream, filename, EncodingType.UTF32BE, buf, 4, len); } @@ -555,7 +590,7 @@ class Utf32beLineStream : LineStream { } } -class Utf32leLineStream : LineStream { +private class Utf32leLineStream : LineStream { this(InputStream stream, string filename, ubyte[] buf, uint len) { super(stream, filename, EncodingType.UTF32LE, buf, 4, len); } diff --git a/src/dlangui/core/logger.d b/src/dlangui/core/logger.d index 465ee95b..ac70519e 100644 --- a/src/dlangui/core/logger.d +++ b/src/dlangui/core/logger.d @@ -1,24 +1,28 @@ // Written in the D programming language. /** -This module contains logger implementation. - +This module provides logging utilities. +Use Log class static methods. Synopsis: ---- import dlangui.core.logger; +// setup: + // use stderror for logging setStderrLogger(); // set log level setLogLevel(LogLeve.Debug); + +// usage: + // log debug message Log.d("mouse clicked at ", x, ",", y); // log error message -Log.d("exception while reading file", e); - +Log.e("exception while reading file", e); ---- Copyright: Vadim Lopatin, 2014 @@ -30,82 +34,123 @@ module dlangui.core.logger; import std.stdio; import std.datetime; +/// Log levels enum LogLevel : int { + /// Fatal error, cannot resume Fatal, + /// Error Error, + /// Warning Warn, + /// Informational message Info, + /// Debug message Debug, + /// Tracing message Trace } +/// Returns timestamp in milliseconds since 1970 UTC similar to Java System.currentTimeMillis() long currentTimeMillis() { return std.datetime.Clock.currStdTime / 10000; } +/** + + Logging utilities + +Setup example: +---- +// setup: +// use stderror for logging +setStderrLogger(); +// set log level +setLogLevel(LogLeve.Debug); +---- + +Logging example: +---- +// log debug message +Log.d("mouse clicked at ", x, ",", y); +// log error message +Log.e("exception while reading file", e); +---- + +*/ synchronized class Log { - static { - private LogLevel logLevel = LogLevel.Info; - private std.stdio.File logFile; + static: + private LogLevel logLevel = LogLevel.Info; + private std.stdio.File logFile; - void setStdoutLogger() { - logFile = stdout; - } + /// Redirects output to stdout + void setStdoutLogger() { + logFile = stdout; + } - void setStderrLogger() { - logFile = stderr; - } + /// Redirects output to stderr + void setStderrLogger() { + logFile = stderr; + } - void setFileLogger(File file) { - logFile = file; - } + /// Redirects output to file + void setFileLogger(File file) { + logFile = file; + } - void setLogLevel(LogLevel level) { - logLevel = level; - } + /// Sets log level (one of LogLevel) + void setLogLevel(LogLevel level) { + logLevel = level; + } - string logLevelName(LogLevel level) { - switch (level) { - case LogLevel.Fatal: return "F"; - case LogLevel.Error: return "E"; - case LogLevel.Warn: return "W"; - case LogLevel.Info: return "I"; - case LogLevel.Debug: return "D"; - case LogLevel.Trace: return "V"; - default: return "?"; - } - } - void log(S...)(LogLevel level, S args) { - if (logLevel >= level && logFile.isOpen) { - SysTime ts = Clock.currTime(); - logFile.writef("%04d-%02d-%02d %02d:%02d:%02d.%03d %s ", ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.second, ts.fracSec.msecs, logLevelName(level)); - logFile.writeln(args); - logFile.flush(); - } - } - void v(S...)(S args) { - if (logLevel >= LogLevel.Trace && logFile.isOpen) - log(LogLevel.Trace, args); - } - void d(S...)(S args) { - if (logLevel >= LogLevel.Debug && logFile.isOpen) - log(LogLevel.Debug, args); - } - void i(S...)(S args) { - if (logLevel >= LogLevel.Info && logFile.isOpen) - log(LogLevel.Info, args); - } - void w(S...)(S args) { - if (logLevel >= LogLevel.Warn && logFile.isOpen) - log(LogLevel.Warn, args); - } - void e(S...)(S args) { - if (logLevel >= LogLevel.Error && logFile.isOpen) - log(LogLevel.Error, args); - } - void f(S...)(S args) { - if (logLevel >= LogLevel.Fatal && logFile.isOpen) - log(LogLevel.Fatal, args); + /// Log level to name helper function + string logLevelName(LogLevel level) { + switch (level) { + case LogLevel.Fatal: return "F"; + case LogLevel.Error: return "E"; + case LogLevel.Warn: return "W"; + case LogLevel.Info: return "I"; + case LogLevel.Debug: return "D"; + case LogLevel.Trace: return "V"; + default: return "?"; } } + /// Log message with arbitrary log level + void log(S...)(LogLevel level, S args) { + if (logLevel >= level && logFile.isOpen) { + SysTime ts = Clock.currTime(); + logFile.writef("%04d-%02d-%02d %02d:%02d:%02d.%03d %s ", ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.second, ts.fracSec.msecs, logLevelName(level)); + logFile.writeln(args); + logFile.flush(); + } + } + /// Log verbose / trace message + void v(S...)(S args) { + if (logLevel >= LogLevel.Trace && logFile.isOpen) + log(LogLevel.Trace, args); + } + /// Log debug message + void d(S...)(S args) { + if (logLevel >= LogLevel.Debug && logFile.isOpen) + log(LogLevel.Debug, args); + } + /// Log info message + void i(S...)(S args) { + if (logLevel >= LogLevel.Info && logFile.isOpen) + log(LogLevel.Info, args); + } + /// Log warn message + void w(S...)(S args) { + if (logLevel >= LogLevel.Warn && logFile.isOpen) + log(LogLevel.Warn, args); + } + /// Log error message + void e(S...)(S args) { + if (logLevel >= LogLevel.Error && logFile.isOpen) + log(LogLevel.Error, args); + } + /// Log fatal error message + void f(S...)(S args) { + if (logLevel >= LogLevel.Fatal && logFile.isOpen) + log(LogLevel.Fatal, args); + } }