classdef Annotations < handle
%Code coverage annotations for the HTML code generation report
%
% See also
%
% This is an undocumented class. Its methods and properties are likely to
% change without warning from one release to the next.

% Copyright 2009-2012 The MathWorks, Inc.

properties (Access=private, Constant=true)
XmlEncoding='UTF-8';
end

properties (Access=private)
AnnotationsAreAvailable;
end

methods (Access = protected)

function setAnnotationsAvailable(this, available)
this.AnnotationsAreAvailable = available;
end

end

methods (Access = public)

function available = hasAnnotations(this)
available = this.AnnotationsAreAvailable;
end

end

methods (Access=public, Abstract=true)

[lineNos, colNos, textStr1, textStr1Status, textStr2, textStr2Status,...
toolTips] = getAnnotations(this)

summary = getSummaryHtml(this)

setSrcFile(this, srcFile, componentBu)

end

methods (Access=public, Static=true)

function [label, bar] = getHtmlSummaryBar(cov, total ,msgIdLabel, msgIdTooltip)
%GETHTMLSUMMARYBAR returns an html percentage complete bar

% Create the file summary annotation
space = '&nbsp;';
covBarEmpty = sprintf('<a CLASS="VNB">%s</a>',...
repmat(space,1,10));

covStr = sprintf('%d',cov);
totalStr = sprintf('%d',total);

if total==0
covBar=covBarEmpty;
covText = sprintf...
('<A>%s %s</A>', ...
DAStudio.message(msgIdLabel),...
DAStudio.message...
('CoderCoverage:AllTools:NoCoverageToMeasureShort')...
);
covTitle= DAStudio.message('CoderCoverage:AllTools:NoCoverageToMeasure');
else
cov_percent=floor(100*cov/total);
if cov_percent>50
% Round down for visual indicator
cov_perdec=floor(10*cov/total);
else
% Round up for visual indicator
cov_perdec=ceil(10*cov/total);
end
covTitle=DAStudio.message(msgIdTooltip, ...
covStr, totalStr);
covBar = sprintf(['<a CLASS="VTB">%s'...
'</a><a CLASS="VFB">%s</a>'],...
repmat(space,1,cov_perdec),...
repmat(space,1,10-cov_perdec));
covText = sprintf('<A>%s %d%s</A>',...
DAStudio.message(msgIdLabel), ...
cov_percent, '%');
end
label = sprintf('<SPAN TITLE="%s">%s</SPAN>', ...
covTitle, covText);
bar = sprintf('<SPAN TITLE="%s">%s</SPAN>', ...
covTitle, covBar);
end

end

methods (Access=public)

function [xmlTxt, encoding] = getXml(this)

encoding = coder.coverage.Annotations.XmlEncoding;

[lineNos, colNos, textStr1, textStr1Status, textStr2, textStr2Status,...
toolTips] = getAnnotations(this);

assert(all(...
strcmp(textStr1Status, 'T') | ...
strcmp(textStr1Status, 'F') | ...
strcmp(textStr1Status, 'N') | ...
strcmp(textStr1, '') ...
) ...
&& all( ...
strcmp(textStr2Status, 'T') | ...
strcmp(textStr2Status, 'F') | ...
strcmp(textStr2Status, 'N') | ...
strcmp(textStr2, '') ...
), ...
['Status must be one of T, F or N for covered, ' ...
'not covered or no coverage to measure']);

nl = sprintf('\n');
spaces = repmat('&nbsp;',1,7); % filler for line number area
xmlbegin = sprintf('<?xml version="1.0" encoding="%s"?>',...
coder.coverage.Annotations.XmlEncoding);
css = [ ...
' <style><![CDATA[.VF { font-style: italic; color: #FF0000 }]]></style>' nl ...
' <style><![CDATA[.VT { font-style: bold; color: #000080 }]]></style>' nl ...
' <style><![CDATA[.VN { font-style: italic; color: #888888 }]]></style>' nl ...
' <style><![CDATA[.VTB { background-color: #000080 }]]></style>' nl ...
' <style><![CDATA[.VFB { background-color: #FF0000 }]]></style>' nl ...
' <style><![CDATA[.VNB { background-color: #888888 }]]></style>'];
root = ModelAdvisor.Element;
root.setTag('root');
summary = ModelAdvisor.Element;
summary.setTag('summary');
annotation = ModelAdvisor.Element;
annotation.setTag('annotation');
annotation.setContent(['<![CDATA[' this.getSummaryHtml ']]>']);
summary.setContent(annotation.emitHTML);
linedefault = ModelAdvisor.Element;
linedefault.setTag('line');
linedefault.setAttribute('id','default');
annotation.setContent('<![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;]]>');
linedefault.setContent(annotation.emitHTML);

[lineNosUnique, ~, in] = unique(lineNos);
numLines = length(lineNosUnique);
lines = cell(numLines,1);

spaceChar='&nbsp;';
lineChar='.';

emptyAnnotation = '&nbsp;&nbsp;&nbsp;&nbsp;';

for k=1:numLines
k_idx = in==k;
lineNo_k = lineNosUnique(k);
colNos_k = colNos(k_idx);
textStr1_k = textStr1(k_idx);
textStr1Status_k = textStr1Status(k_idx);
textStr2_k = textStr2(k_idx);
textStr2Status_k = textStr2Status(k_idx);
toolTips_k = toolTips(k_idx);

[colNos_k, sort_k_idx] = sort(colNos_k);
textStr1_k = textStr1_k(sort_k_idx);
textStr1Status_k = textStr1Status_k(sort_k_idx);
textStr2_k = textStr2_k(sort_k_idx);
textStr2Status_k = textStr2Status_k(sort_k_idx);
toolTips_k = toolTips_k(sort_k_idx);

line = ModelAdvisor.Element;
line.setTag('line');
line.setAttribute('id',sprintf('%d',lineNo_k));

if colNos_k(1) ~=0
% We need to insert a dummy annotation for column number zero
colNos_k = [0; colNos_k]; %#ok<AGROW>
textStr1_k = [{emptyAnnotation}; textStr1_k(:)];
textStr1Status_k = [{'T'}; textStr1Status_k(:)];
textStr2_k = [{''}; textStr2_k(:)];
textStr2Status_k = [{''}; textStr2Status_k(:)];
toolTips_k = [{''}; toolTips_k(:)];
end

inner = cell(length(colNos_k),1);
for n=1:length(colNos_k)
a = ModelAdvisor.Element;
a.setTag('annotation');
if (colNos_k(n)~=0)
assert(n>1, ...
'Only the first annotation for a line is allowed not to have a column pointer')
pointerShaft = char(zeros(1,colNos_k(n)));
if mod(n,2)==0
pointerShaft(1:end)=lineChar;
else
pointerShaft(1:2:end)=lineChar;
pointerShaft(2:2:end)='-';
pointerShaft = strrep(pointerShaft, '-',spaceChar);
end
columnPointer = [spaces pointerShaft '^'];
else
columnPointer = '';
end

% Build html for the annotation
str1=textStr1_k{n};
str2=textStr2_k{n};
part1 = '';
part2 = '';
if ~isempty(str1)
part1 = sprintf('<A CLASS="V%s">%s</A>',...
textStr1Status_k{n}, str1);
end
if ~isempty(str2)
part2 = sprintf('<A CLASS="V%s">%s</A>',...
textStr2Status_k{n}, str2);
end
if ~isempty(toolTips_k{n})
titleHtml = sprintf(' TITLE="%s"', toolTips_k{n});
else
titleHtml = '';
end
annotationHtml = sprintf....
('<SPAN%s>%s%s</SPAN>', titleHtml, ...
part1, part2);

a.setContent(['<![CDATA[' annotationHtml ...
columnPointer ']]>']);
inner{n} = a.emitHTML;
end
line.setContent(sprintf('%s\n',inner{:}));
lines{k} = line.emitHTML;
end
root.setContent([css nl summary.emitHTML nl linedefault.emitHTML nl sprintf('%s',lines{:})]);
xmlTxt = [xmlbegin root.emitHTML];
end

function covData = getData(this)
% Similar to getXML, but we only get the coverage data,
% do not generate XML file. By doing this, we separate the
% data and presentation.
%
% The covData will be read by Code View,
% and dynamically render on the view
%
% output: structured coverage data for one source file
% summary: summary data
% probes: array of probes
% Each probe is a struct with following fields:
% line: line number
% col: column number
% str1:
% status1: 'T', 'F', 'N'
% str2:
% status2: 'T', 'F', 'N'
% tooltip: some description text

% summary for the whole file
covData.summary = this.getSummaryHtml();

% individual line data
[lineNos, colNos, textStr1, textStr1Status, textStr2, textStr2Status,...
toolTips] = getAnnotations(this);

n = length(lineNos);
probes = cell(n, 1);
for i = 1:n
s = [];
s.line = lineNos(i);
s.col = colNos(i);
s.str1 = textStr1{i};
s.status1 = textStr1Status{i};
s.str2 = textStr2{i};
s.status2 = textStr2Status{i};
s.tooltip = toolTips{i};

probes{i} = s;
end
covData.probes = probes;
end
end
end