classdef (Hidden = true) CodeWriter < handle
%CODEWRITER The basic interface for all code writing utility classes.
%
%   This class provides automatic indentation for generated code blocks and
%   language-specific comments.
%
%   This is an undocumented class. Its methods and properties are likely to
%   change without warning from one release to the next.

%   Copyright 2011-2014 The MathWorks, Inc.

    properties(Access = protected, Abstract)
        BlockStartString        % Starts a block. In C it is the opening curly bracket: '{'. This string is printed after the head (i.e.: 'if (headCondition) {').
        BlockEndString          % Ends a block. In C it is the closing curly bracket: '}'.
        CommentStartString      % The string that starts a comment. In C this is '/*'.
        CommentEndString        % The string that ends a comment. In C this is '*/'.
        IsLineComment           % Indicates whether the comment is a line comment. This means that no code can be written in the same line after the comment start string.
    end
    
    

    properties(Access = protected)
        IndentString = ' '      % The string used for indenting lines.
        IndentPerLevel = 4      % The number of times to repeat the indentation string per indentation level.
        IndentLevel = 0         % The current indentation level.
        IndentCache = {}        % Cache of pre-calculated indent strings
        SpaceString = ' '       % This string is used to separate statements on the same line.
        BlockEndTagDelim = ': ' % The string that separates the end tag and the additional comment in the end of the block.
        NewLineString           % The string used to break lines. This is set to '\n' in the constructor.
    end

    
    
    properties(Access = protected)
        IsFreshLine = true      % Indicates whether there has been anything written yet to the current line.
        BlockDataStack = {}     % Keeps a stack of comments for every block (used to print the 'end tags' at the end of blocks).
        BlockCount = 0          % The number of started (and yet unterminated) blocks. This counter is also used as an index of the top of the block comment stack.
    end



    properties(GetAccess = protected, SetAccess = private)
        OutWriter               % The output writer where all the contents will be written to. It must implement the methods specified in 'rtw.connectivity.Writer'.
    end
    
    
    
    properties(Constant, Access = private)
        CodeWriters = containers.Map(... % This map contains all the known code writers (for specific languages)
            {...
                'C', ...
                'MATLAB'...
            },...
            {...
                'rtw.connectivity.CCodeWriter', ...
                'rtw.connectivity.MATLABCodeWriter'...
            }, 'UniformValues', false)
    end

    
    
    methods(Access = protected) % Constructors
        
        function this = CodeWriter(outWriter)
        % CODEWRITER Creates a new instance of the code writer.
        %
        %   outWriter - The output writer sink to which the code will be
        %   printed. This output writer must implement the methods of the
        %   `rtw.connectivity.Writer` interface.
            this.OutWriter = outWriter;
            this.NewLineString = sprintf('\n');
        end
        
    end
    
    
    
    methods(Static) % The factory method (a convenience method that creates code writers for specific languages).
        
        function codeWriter = create(varargin)
        %CREATE The main method for creating instances of code writers for
        %a chosen programming language.
        %
        %   CODEWRITER = CREATE('language', LANGUAGE, 'writerObject', OUTPUT) 
        %      Creates a code writer with the given name that will write to
        %   the given output stream.
        %
        %       LANGUAGE - the name of the code writer to create (e.g.: 'C' or
        %       'MATLAB'). For a list of supported code writers, see
        %       `rtw.connectivity.CodeWriter.getCodeWritersList`.
        %
        %       OUTPUT - the writer to which the code writer should output the
        %       code (this object must implement the methods specified in
        %       `rtw.connectivity.Writer`).
        %
        %   CODEWRITER = CREATE('language', LANGUAGE, 'filename', FILENAME, 'append', APPEND) 
        %     Creates a code writer with the given name that will write to
        %   the given file.
        %
        %       LANGUAGE - the name of the code writer to create (e.g.: 'C' or
        %       'MATLAB'). For a list of supported code writers, see
        %       `rtw.connectivity.CodeWriter.getCodeWritersList`.
        %
        %       FILENAME - the name of the file to which to write the code.
        %
        %       APPEND - [optional] indicates whether we should append to the
        %       file or not. Default is false.
        %
        %   CODEWRITER = CREATE('filename', FILENAME, 'append', APPEND) 
        %        Creates a 'C' code writer that will write to the given file.
        %
        %       FILENAME - the name of the file to which to write the code.
        %
        %       APPEND - [optional] indicates whether we should append to the
        %       file or not. Default is false.
        
            % parse inputs
            p = inputParser;
            p.addParameter('language', 'C');
            p.addParameter('fileName', '');
            p.addParameter('append', false);
            p.addParameter('callCBeautifier', true);
            p.addParameter('obfuscateCode', false);
            p.addParameter('writerObject', []);
            p.addParameter('machineFormat', 'n');
            p.addParameter('encoding', 'UTF-8'); % Default to UTF-8 when not specified
            p.parse(varargin{:});
            
            % extract inputs from parser
            language = p.Results.language;
            fileName = p.Results.fileName;
            append = p.Results.append;
            callCBeautifier = p.Results.callCBeautifier;
            obfuscateCode = p.Results.obfuscateCode;
            writerObject = p.Results.writerObject;
            machineFormat = p.Results.machineFormat;
            encoding = p.Results.encoding;
            
            % validate inputs
            assert(ischar(language), 'The code writer''s language must be a string.');
            assert(islogical(append), 'The code writer''s append must be a logical.');
            assert(islogical(callCBeautifier), 'The code writer''s callCBeautifier must be a logical.');
            assert(islogical(obfuscateCode), 'The code writer''s obfuscateCode must be a logical.');
            assert(ischar(fileName), 'The code writer''s fileName must be a string.');
            assert(ischar(machineFormat), 'The code writer''s machineFormat must be a string.');
            assert(ischar(encoding), 'The code writer''s encoding must be a string.');
            
            % By default, only apply the c beautifier on C/C++ files
            if any(strcmp('callCBeautifier', p.UsingDefaults))
                [~,~,fileExt] = fileparts(fileName);
                isCFile = any(strcmpi(fileExt, {'.c','.h','.cpp','.hpp'}));
                callCBeautifier = callCBeautifier && isCFile;
            end
            
            % Code obfuscation is only supported for C/C++ files
            if any(strcmp('obfuscateCode', p.UsingDefaults))
                [~,~,fileExt] = fileparts(fileName);
                isCFile = any(strcmpi(fileExt, {'.c','.h','.cpp','.hpp'}));
                obfuscateCode = obfuscateCode && isCFile;
            end
            
            % create writerObject unless one is passed in
            if isempty(writerObject)
                writerObject = rtw.connectivity.FileWriter(fileName,...
                    append, callCBeautifier, obfuscateCode, machineFormat, encoding);
            end
            
            % Is the code writer in the map of known ones at all?
            assert(isKey(rtw.connectivity.CodeWriter.CodeWriters, language), ...
                   'Could not create the unknown code writer "%s".', ...
                   language);

            % Now finally create the code writer and initialize it with the
            % provided output sink.
            name = rtw.connectivity.CodeWriter.CodeWriters(language);
            codeWriter = feval(name, writerObject);
        end
        
    end
    
    
    methods % The high-level interface
        
        function wBlockStart(this, headFormat, varargin)
        %WBLOCKSTART Starts a new code block by printing the block's
        %header and increasing indentation level by one.
        %
        % This method is equivalent to `WBLOCKSTARTCOMMENT(headFormat, '', varargin{:})`.
            this.wBlockStartComment(headFormat, '', varargin{:});
        end
        
        
        function wBlockStartComment(this, headFormat, comment, varargin)
        % WBLOCKSTARTCOMMENT Starts a new code block by printing the block's
        % header and increasing indentation level by one. This also prints
        % a comment after the head of the block (if the comment is not
        % empty).
        %
        % Note that this method ends the line and starts a new one. For the
        % equivalent method that does not break the line see the method
        % `emitBlockStart`. This method is equivalent to
        % `emitBlockStart(headFormat, true, comment)`.
        %
        %
        % Usage:
        %
        % WBLOCKSTARTCOMMENT(THIS, HEADFORMAT, COMMENT, V1, ..., VN)
        %
        %   headFormat - [optional] the format string that is printed just
        %   before the start block delimiter.
        %
        %   comment - [optional] the comment that should be printed after
        %   the start of the block.
        %
        %   v1, ..., vN - [optional] these are the `SPRINTF` parameters to
        %   the `HEADFORMAT` string.
        %
        %
        % Examples (for the C programming language):
        %
        % 1.)
        %
        %       wBlockStartComment('do');
        %
        %       > do {
        %       >
        %
        %   Equivalent to:
        %
        %       emitBlockStart('do', true, '');
        %
        % 2.)
        %
        %       wBlockStartComment();
        %
        %       > {
        %       >
        %
        %   Equivalent to:
        %
        %       emitBlockStart('', true, '');
        %
        % 3.)
        %
        %       wBlockStartComment('while (a >= %d)', 'Optimize the parameters...', 89);
        %       wBlockEnd();
        %
        %       > while (a >= 89) { /* Optimize the parameters */
        %       > } /* while */
        %       >
        %
        %   Equivalent to:
        %
        %       emitBlockStart('do', true, 'Repeat this...');
        %       emitBlockEnd('', true, true, false);
        %
        % 4.)
        %
        %       wBlockStartComment('', 'Do this once.');
        %
        %       > { /* Do this once. */
        %       >
        %
        %   Equivalent to:
        %
        %       emitBlockStart('', true, 'Do this once.');
            switch nargin
                case 0
                    assert(false, 'Wrong number of parameters.');
                case 1
                    this.emitBlockStart('', true, '');
                case 2
                    this.emitBlockStart(headFormat, true, '');
                case 3
                    this.emitBlockStart(headFormat, true, comment);
                otherwise
                    this.emitBlockStart(sprintf(headFormat, varargin{:}), true, comment);
            end
        end
        
        
        function wBlockMiddle(this, headFormat, varargin)
        % WBLOCKMIDDLE Prints the middle part of the block. This is useful
        % for writing 'else' and 'else if' blocks.
        %
        % Note: this method prints the line with an indent of one less and
        % starts a new line.
        %
        % Examples (for the C programming language):
        %
        % 1.)
        %
        %       wBlockMiddle('else if (b > 0)')
        %
        %       > } else if (b > 0) {
        %       >
        %
        %   Equivalent to:
        %
        %       emitBlockMiddle('else if (b > 0)', true);
            if nargin < 2
                assert(false, 'Wrong number of parameters.');
            elseif nargin == 2
                this.emitBlockMiddle(headFormat, true);
            else
                this.emitBlockMiddle(sprintf(headFormat, varargin{:}), true);
            end
        end
        
        
        function wBlockEnd(this, comment, endTag, includeSemicolon)
        % WBLOCKEND Ends a code block by printing the block's footer and
        % decreasing indentation level by one. This function also prints an
        % 'end-tag' into the trailing comment (which is the first word of
        % the matching block head.
        %
        % Note that this method ends the line and starts a new one. For the
        % equivalent method that does not break the line see the method
        % `emitBlockEnd`. This method is equivalent to
        % `emitBlockEnd(comment, true, endTag, includeSemicolon)`.
        %
        %   comment - [optional; default = ''] Printed after the end of the
        %   block (on the same line).
        %
        %   endTag - [optional; default = false] Indicates whether the
        %   'end-tag' should be printed in the trailing comment just behind
        %   the terminating block delimiter. The end tag is the first word
        %   of the head in the matching `wBlockStart`.
        %
        %   includeSemicolon - [optional; default = false] Indicates
        %   whether a semicolon ';' should be printed after the terminating
        %   block delimiter.
        %
        % Examples (for the C programming language):
        %
        % 1.)
        %
        %       wBlockEnd();
        %
        %       > }
        %       >
        %
        %   Equivalent to:
        %
        %       emitBlockEnd('', true, true);
        %
        % 2.)
        %
        %       wBlockEnd('What was that?');
        %
        %       > } /* What was that? */
        %       >
        %
        %   Equivalent to:
        %
        %       emitBlockEnd('What was that?', true, true);
        %
        % Examples (for C/C++ programming language):
        % 3.) class/struct definition block end with semicolon
        %
        %       wBlockEnd('', false, true);
        %
        %       > };
        %       >
        %
        %   Equivalent to:
        %
        %       emitBlockEnd('', true, false, true);
            switch nargin
                case 1
                    this.emitBlockEnd('', true, true);
                case 2
                    this.emitBlockEnd(comment, true, true);
                case 3
                    this.emitBlockEnd(comment, true, endTag);
                case 4
                    this.emitBlockEnd(comment, true, endTag, includeSemicolon);
                otherwise
                    assert(false, 'Wrong number of parameters.');
            end
        end
        
        
        function wLine(this, content, varargin)
        % WLINE Prints the content with an indent and breaks the line.
        %
        %   content - the content of the statement.
        %
        % Equivalent to:
        %
        %       emitLine(sprintf(content, varargin{:}), true, true)
        %
        % Examples (for the C programming language):
        %
        % 1.)
        %
        %       wLine('a = b + 5;');
        %
        %       > a = b + 5;
        %       >
            if nargin > 2
                this.emitLine(sprintf(content, varargin{:}), true, true);
            else
                this.emitLine(content, true, true);
            end
        end
        
        
        function wComment(this, comment)
        % WCOMMENT Prints a comment with an indent, ends the line and
        % starts a fresh one.
        %
        %   comment - The contents of the comment.
        %
        % Equivalent to:
        %
        %   emitComment(comment, true)
        %
        % Examples (for the C programming language):
        %
        %       wComment('A description of...')
        %
        %       > /* A description of... */
        %       >
            this.emitComment(comment, true);
        end
        
        
        function wNewLine(this)
        % WNEWLINE Prints the newline character and starts a fresh line.
        %
        % NOTE: This method switches the 'IsFreshLine' flag to true.
            this.raw(this.NewLineString);
            % We have started a new line, which means it is fresh (requires
            % indent). But we have not necessarily terminated the
            % wLine.
            this.IsFreshLine = true;
        end
        
        
        function incIndent(this)
        % INCINDENT Increases the indent level by one. This function is
        % automatically called by `wBlockStart`.
            this.IndentLevel = this.IndentLevel + 1;
        end
        
        
        function decIndent(this)
        % DECINDENT Decreases the indent level by one. This function is
        % automatically called by `wBlockEnd`.
            if this.IndentLevel > 0
                this.IndentLevel = this.IndentLevel - 1;
            end
        end
        
        
        % clean up on destruction
        function delete(this)
           this.close;
        end
        
        
        % flush the buffer to the file and close the file
        function close(this)
            this.OutWriter.close;
        end

        % set the number of spaces for an indent (IndentPerLevel):
        function setIndentPerLevel(this, indent)
        % SETINDENTPERLEVEL Sets the number of spaces for Code Indentation.
            this.IndentPerLevel = indent;
        end

        % set the end of line character (NewLineString):
        function setNewLineString(this, newLineString)
        % SETNEWLINESTRING Sets the string to use for end-of-line.
            this.NewLineString = newLineString;
        end
        
    end
    
    
    
    methods(Access = protected) % Methods that specific implementations could meaningfully override.
        
        function endTag = getEndTag(~, startBlockComment)
        % GETENDTAG Extracts the tag from the comment at the start of the
        % block. This tag will be used as a comment in the end of the
        % block. This is useful to annotate nicely what a block does and
        % where a particular block ends.
            endTag = regexprep(startBlockComment, '^([\w]*).*$', '$1');
        end
        
    end
    
    
    
    methods(Access = public, Hidden) % More flexible 'emit' methods.
        
        function writeBinary(this, content)
            this.OutWriter.writeBinary(content);
        end
        
        function emitLine(this, content, endLine, indent)
        % EMITLINE Prints the contents into the current line.
        %
        %   content - the text contents of the line.
        %
        %   endLine - indicates whether the line should be ended and a
        %   fresh one started.
        %
        %   indent - indicates whether the indent should be printed before
        %   the contents.
        %
        % Examples:
        %
        %   1.)
        %
        %       emitLine('#define <iostream>', true, false);
        %
        %       > #define <iostream>
        %       >
        %
        %   2.)
        %
        %       emitLine('#define <iostream>', false, false);
        %
        %       > #define <iostream>
        %
        %   3.)
        %
        %       emitLine('#define <iostream>', false, true);
        %
        %       >     #define <iostream>
        %
        %   4.) Calling `newLine` does the same thing and it is faster than
        %   this:
        %
        %       emitLine('', true, false);
        %
        %       >
        %       >
            if indent
                this.indentOrSpace();
            end
            
            if ~isempty(content)
                this.raw(content);
                this.IsFreshLine = false;
            end
            
            if endLine
                this.wNewLine();
            end
        end
        
        
        function emitBlockStart(this, headStr, endLine, comment)
        % EMITBLOCKSTART Starts a new code block by printing the block's
        % header and increasing indentation level by one.
        %
        % NOTE: This method terminates the current line but does not end it
        % if it is not fresh and not terminated.
        %
        % NOTE: if the language does not support block-comments, the line
        % will be forcefully terminated.
        %
        % Parameters:
        %
        %   headStr - printed before the start of the block. In
        %   C `this.wBlockStart('while (true)')` would print something like
        %   `while(true) {`.
        %
        %   endLine - if set to `true` this method will break
        %   the line (in C it would simply put a newline delimiter).
        %
        %   comment - the comment that should be printed after
        %   the start of the block. In C `this.wBlockStart('', 'Do something...')`
        %   would print something like `{ /* Do something */`.
        %
        % Examples (for the C programming language):
        %
        %   1.)
        %
        %   emitBlockStart('if (a > b)', true, '');
        %
        %       > if (a > b) {
        %       >
        %
        %   2.)
        %
        %   emitBlockStart('', true, '');
        %
        %       > {
        %       >
        %
        %   3.)
        %
        %   emitBlockStart('', false, '');
        %
        %       > {
        %
        %   4.)
        %
        %   emitBlockStart('if (a > b)', false, '');
        %
        %       > if (a > b) {
        %
        %   5.)
        %
        %   emitBlockStart('if (a > b)', false, '');
        %
        %       > if (a > b) {
        %
        %   6.)
        %
        %   emitBlockStart('if (a > b)', false, 'if a greater than b, do something');
        %
        %       > if (a > b) { /* if a greater than b, do something */
        %
        %   7.)
        %
        %   emitBlockStart('if (a > b)', true, 'if a greater than b, do something');
        %
        %       > if (a > b) { /* if a greater than b, do something */
        %       >
            
            % Indent if necessary, otherwise print a space (so that we are
            % not printing exactly behind a previously finished statement).
            this.indentOrSpace();
            
            % Increase the indent and push some data onto the stack.
            this.pushBlock({headStr});
            
            % Print the head, but only if it is provided and non-empty.
            if isempty(headStr)
                % We have a 'headless' block. But the target language must
                % support headless blocks. Verify this!
                assert(~isempty(this.BlockStartString), ...
                        'This code writer does not support plain code blocks (e.g.: blocks without "for", "if", "while", etc.).');
                this.raw(this.BlockStartString);
            else
                % The head is not empty. Print it.
                if isempty(this.BlockStartString)
                    this.raw(headStr);
                else
                    this.raw([ headStr this.SpaceString this.BlockStartString ]);
                end
            end
            
            % Now print the comment (if any) and optionally end the line.
            if ~isempty(comment)
                this.emitComment(comment, endLine);
            elseif endLine
                this.wNewLine();
            end
        end
        
        
        function emitBlockMiddle(this, headStr, endLine)
        % EMITBLOCKMIDDLE Prints the middle part of the block. This is useful
        % for writing 'else' and 'else if' blocks.
        %
        % Examples (for the C programming language):
        %
        % 1.)
        %
        %       emitBlockMiddle('else if (b > 0)', false)
        %
        %       > } else if (b > 0) {
        %
        % 2.)
        %
        %       emitBlockMiddle('else if (b > 0)', true)
        %
        %       > } else if (b > 0) {
        %       >
        
            this.decIndent();
            
            this.indentOrSpace();
            
            % First end the block:
            if ~isempty(this.BlockEndString)
                this.raw(this.BlockEndString);
            end
            
            % Now print the head (if any)
            if ~isempty(headStr)
                this.raw(this.SpaceString);
                this.raw(headStr);
            end
            
            % Now also print the start block string:
            if ~isempty(this.BlockStartString)
                this.raw(this.SpaceString);
                this.raw(this.BlockStartString);
            end
            
            if endLine
                this.wNewLine();
            end
            
            this.incIndent();
            
        end
        
        
        function emitBlockEnd(this, comment, endLine, endTag, includeSemicolon)
        % EMITBLOCKEND Ends a code block by printing the block's footer and
        % decreasing indentation level by one.
        %
        % Note: This method terminates the current line but does not end it
        % if it is not fresh and not terminated.
        %
        %   comment - Printed after the end of the block (on the
        %   same line).
        %
        %   endLine - Ends the line after printing the end of
        %   the block.
        %
        %   endTag - Indicates whether the first word of the
        %   matching wBlockStart comment should be printed at the end of
        %   the block (see example 6).
        %
        %   includeSemicolon - Indicates whether a semicolon should be printed
        %   immediately after the block terminating delimiter.
        %
        % Examples (for the C programming language):
        %
        %   1.)
        %
        %       emitBlockEnd('', false, false);
        %
        %       > }
        %
        %   2.)
        %
        %       emitBlockEnd('', true, false);
        %
        %       > }
        %       >
        %
        %   3.)
        %
        %       emitBlockEnd('This did something', false, false);
        %
        %       > } /* This did something */
        %
        %   4.)
        %
        %       emitBlockEnd('This did something', true, false);
        %
        %       > } /* This did something */
        %       >
        %
        %
        %   5.)
        %
        %       emitBlockStart('if (a > b)', true, 'Check if we have an error.');
        %       emitBlockEnd('We have thrown an exception here.', true, true);
        %
        %       > if (a > b) { /* Check if we have an error. */
        %       > } /* Check: We have thrown an exception here. */
        %       >
        %
        %   6.) class/struct definition block end with semicolon
        %
        %       emitBlockEnd('', false, false, true);
        %
        %       > };
        
            narginchk(4, 5);
            if nargin == 5 && includeSemicolon
                blockDelimiter = [ this.BlockEndString ';' ];
            else
                blockDelimiter = this.BlockEndString;
            end

            % We are ending a block, so decrease the indent and get the
            % block's data:
            startBlockData = this.popBlock();
            
            % Indent if necessary, otherwise print a space (so that we are
            % not printing exactly behind a previously finished statement).
            this.indentOrSpace();
            
            % Print the block delimiter
            this.raw(blockDelimiter);
            
            % Now we have to print the comment at the end. Will the comment
            % contain the end-tag?
            if endTag && ~isempty(startBlockData{1})
                extractedTag = this.getEndTag(startBlockData{1});
                if ~isempty(extractedTag)
                    if isempty(comment)
                        comment = extractedTag;
                    else
                        comment = [ extractedTag this.BlockEndTagDelim comment ];
                    end
                end
            end
            
            if ~isempty(comment)
                this.emitComment(comment, endLine);
            elseif endLine
                % There is no comment. Just go to the next line.
                this.wNewLine();
            end
        end
        
        
        function emitComment(this, comment, endLine)
        % EMITCOMMENT Prints a comment.
        %
        %   comment - The contents of the comment.
        %
        %   endLine - Indicates whether this method should end the line.
        %
        % Note: this method will end the line and start a fresh one if the
        % language does not support multi-line comments.
        %
        % Examples (for the C programming language):
        %
        %   emitComment('A description of...', true)
        %
        %       > /* A description of... */
        %       >
        %
        %
        %   emitComment('A description of...', false)
        %
        %       > /* A description of... */
            this.indentOrSpace();
            this.rawComment(comment);
            if endLine || this.IsLineComment
                this.wNewLine();
            end
        end
        
    end
    
    
    
    methods(Access = protected, Hidden) % Raw printing methods
        
        function rawIndent(this)
        % RAWINDENT Prints the indentation (regardless of whether the line
        % is fresh or not).
        %
        % NOTE: This method switches the 'IsFreshLine' flag to false.
            indentLevel = this.IndentLevel;
            if indentLevel > 0
                indentCacheSize = length(this.IndentCache);
                if indentCacheSize >= indentLevel
                    indentString = this.IndentCache{indentLevel};
                else
                    indentString = repmat(this.IndentString, 1, this.IndentPerLevel * indentLevel);
                    this.IndentCache{indentLevel} = indentString;
                end                                                
                this.raw(indentString);
            end            
            this.IsFreshLine = false;
        end
        
        
        function rawComment(this, comment)
        % RAWCOMMENT Prints the comment (and nothing else).
            if isempty(this.CommentEndString)
                this.raw([ this.CommentStartString this.SpaceString comment ]);
            else
                this.raw([ this.CommentStartString this.SpaceString comment this.SpaceString this.CommentEndString ]);
            end
        end
        
        
        function raw(this, contents)
        % RAW Prints the contents as is and does not end the line.
        %
        %   contents - the string to print.
           this.OutWriter.write(contents);
        end
        
    end
    
    
    methods(Access = protected, Hidden) % Utility methods.
        
        function pushBlock(this, data)
        % PUSHBLOCK Increases the block counter and puts some data on the
        % stack.
            this.incIndent;
            this.BlockCount = this.BlockCount + 1;
            this.BlockDataStack{this.BlockCount} = data;
        end
        
        
        function data = popBlock(this)
        % POPBLOCK Decreases the block counter and pops the head from the
        % stack of memorized block data.
            this.decIndent;
            if this.BlockCount > 0
                data = this.BlockDataStack{this.BlockCount};
                this.BlockCount = this.BlockCount - 1;
            else
                data = '';
            end
        end
        
        
        function indent(this)
        % INDENT Prints the indent if needed (i.e.: if we are in a fresh
        % line).
        %
        % NOTE: Calling this method for the second time in a row will do
        % nothing. Use the `rawIndent` function, if you want to print
        % multiple indentations.
        %
        % NOTE: This method switches the 'IsFreshLine' flag to false.
            if this.IsFreshLine
                this.rawIndent();
            end
        end
        
        
        function indentOrSpace(this)
        % INDENTORSPACE Prints the indent if needed (if we are in a fresh line),
        % or just a space (if we are not in a fresh line).
        %
        % NOTE: Calling this method for the second time in a row will print
        % just a space character. Use the `rawIndent` function, if you want
        % to print multiple indentations.
        %
        % NOTE: This method switches the 'IsFreshLine' flag to false.
            if this.IsFreshLine
                this.rawIndent();
            else
                this.raw(this.SpaceString);
            end
        end
        
    end
    
    
    methods(Hidden) % Legacy methods for conformance with the old BufferedWriter
        
        % write string to buffer, indenting according to the current
        % indentation level if at the start of a new line, and terminating
        % with a new line.
        function writeLine(this, string, varargin)
            this.wLine(string, varargin{:});
        end

        
        % write each string of the cell array to the buffer, 
        % indenting according to the current indentation level if at the
        % start of a new line, and terminating with a new line.
        function writeCellLines(this, cellLines)
            for i=1:length(cellLines)
                this.wLine(cellLines{i});                                
            end
        end
        
        
        function newLine(this)
        % NEWLINE Prints the newline character and starts a fresh line.
        %
        % NOTE: This method switches the `ISFRESHLINE` flag to true.
            this.wNewLine();
        end
        
    end
    
end
