%CVDATA  Coverage data
%
%   CLASS_ID = cvdata( ID ) Create a test data object for the data contained
%   in the coverage tool internal test-data object ID.
%
%   CLASS_ID = cvdata( LHS, RHS, METRICS ) creates a test data object with the
%   supplied data derived from two other cvdata objects, LHS and RHS.
%
%   See also CVLOAD, CVREPORT, CVTEST, CVSIM, CVSAVE.

%   Bill Aldrich
%   Copyright 1990-2019 The MathWorks, Inc.

classdef cvdata < SlCov.CovEngineProxy
        
    properties (GetAccess=public, SetAccess=private, Dependent=true)
        test
        rootID        
        checksum
        modelinfo
        startTime
        stopTime
        simulationStartTime
        simulationStopTime
        intervalStartTime
        intervalStopTime
        metrics
    end
    
    properties (GetAccess=public, SetAccess=public, Dependent=true, Hidden=true)
        sfcnCovData
        codeCovData
        filter
        filterApplied
        description
        simMode
        tag
        aggregatedIds
        uniqueId
        ownerModel
        ownerBlock
        harnessModel
        trace
        traceOn
        filterData
        testRunInfo
        aggregatedTestInfo
        reqTestMapInfo
        scopeDataToReqs
    end
    properties (GetAccess=public, SetAccess=private, Dependent=true, Hidden=true)
        % Many function use this version of rootID then create
        % another dependent property to forward the access to rootID! Need
        % to remove it later.
        rootId
        type
        startTimeEnum
        stopTimeEnum
        isExternalMATLABFile
        isSubsystem
        isSimulinkCustomCode
        isSharedUtility
        isCustomCode
        covSFcnEnable
    end
    
    properties (Hidden=true)
        localData = {}
        fileRef = []
        isLoaded = true
        variantChecksum
    end
    
    properties (Hidden=true, Transient=true)
        cachedSFcnCovInfoStruct        
        traceMap  = []
        aggInfoMap = []
        traceMask = []
    end
    methods
        %% Public method: cvdata ------------------------------------------
        %  Abstract:
        %    constructor
        function this = cvdata(varargin)
            
            if nargin==0
                this.localData.type = 'DERIVED_DATA';
                this.localData.tag = '';
                this.localData.description = '';
            elseif nargin >= 1
                cvd = varargin{1};
                if isa(cvd, class(this))
                    cvd.load();
                    this = cvd;
                elseif isa(cvd, 'double')
                    if ~cv('ishandle', cvd)
                        error(message('Slvnv:simcoverage:cvdata:CvObjNotExists', cvd));
                    end
                    if cv('get',varargin{1},'.isa')~=cv('get','default','testdata.isa')
                        error(message('Slvnv:simcoverage:cvdata:CvObjNotTestdata', cvd));
                    end
                    % Create a new object
                    this.id = cvd;
                elseif ischar(cvd) || isstring(cvd)
                    uuid = '';
                    if nargin > 1
                        srcCvd = varargin{2};
                        uuid = srcCvd.uniqueId;
                    end
                    fileName = convertStringsToChars(cvd);
                    this = cvdata.setupFileRef(this, fileName, uuid);
                else
                    error(message('Slvnv:simcoverage:cvdata:BadInput'))
                end
            else
                error(message('Slvnv:simcoverage:cvdata:BadSyntax'));
            end
            if nargin ~=0 && this.isLoaded && isempty(this.uniqueId)
                this.setUniqueId;            
            end
            this.buildTraceMask();
        end
        
        %% Public methods defined in separate file ------------------------
        %
        display(cvdata)
        r = minus(p,q)
        r = mtimes(p,q)
        r = plus(p,q)
        B = saveobj(A)
        r = times(p,q)
        res = extract(cvdata, subsysH)
        out = valid(cvdata)
        out = validRoot(cvdata)
        out = isCompatible(this, cvd)
        %==================
        function setUniqueId(this)
            checkId(this);
            if isempty(this.uniqueId)
                guidStr = net.jini.id.UuidFactory.generate();
                guidStr = char(guidStr.toString());
                this.uniqueId = guidStr;
            end
        end
        %==================
        function clearUniqueId(this)
                this.uniqueId = '';
        end
        %==================
        %% Public setter/getter for the dependent properties --------------
        %
        function value = get.type(this)
            checkId(this);
            if isDerived(this)
                value = 'DERIVED_DATA';
            else
                value = 'TEST_DATA';
            end
        end
        
        function value = get.test(this)
            checkId(this);
            if this.id==0
                value = [];
            else
                value = cvtest(this.id);
            end
        end
        
        function value = get.rootID(this)
            checkId(this);
            value = [];
            if this.id==0
                value = this.localData.rootId;
            else
                rId = getRootId(this.id);
                if rId
                    value = rId;
                end
            end
        end
        
        function value = get.rootId(this)
            value = this.rootID;
        end
            
        function value = get.checksum(this)
            checkId(this);
            
            value = [];
            if this.id==0
                value = this.localData.variantChecksum;
                if isempty(value)
                    value = this.localData.checksum;
                end
            else
                chks = cv('get', this.id,'.variantChecksum');
                if ~any(chks)
                  rId = getRootId(this.id);
                  if rId
                    chks  = cv('get',rId,'.checksum');
                  end
                end
                value.u1 = chks(1);
                value.u2 = chks(2);
                value.u3 = chks(3);
                value.u4 = chks(4);
            end
        end
        
        function value = get.startTimeEnum(this)
            checkId(this);
            if this.id==0
                value = this.localData.startTimeEnum;
            else
                value = cv('get',this.id,'testdata.startTime');
            end
        end
        
        function value = get.stopTimeEnum(this)
            checkId(this);
            if this.id==0
                value = this.localData.stopTimeEnum;
            else
                value = cv('get',this.id,'testdata.stopTime');
            end
        end
        
        function value = get.startTime(this)
            value = datestr(this.startTimeEnum);
        end
        
        function value = get.stopTime(this)
            value = datestr(this.stopTimeEnum);
        end
%=============================                
        function value = get.simulationStartTime(this)
            checkId(this);
            if this.id==0
                value = this.localData.simulationStartTime;
            else
                value = cv('get',this.id,'testdata.simStartTime');
            end
        end
%=============================        
        function value = get.simulationStopTime(this)
            checkId(this);
            if this.id==0
                value = this.localData.simulationStopTime;
            else
                value = cv('get',this.id,'testdata.simStopTime');
            end
        end
%=============================              
        function value = get.intervalStartTime(this)
            checkId(this);
            if this.id==0
                value = this.localData.intervalStartTime;
            else
                cvt = cvtest(this.id);
                value = cvt.getSlcovSettings.intervalStartTime;
            end
        end
%=============================              
        function value = get.intervalStopTime(this)
            checkId(this);
            if this.id==0
                value = this.localData.intervalStopTime;
            else
                cvt = cvtest(this.id);
                value = cvt.getSlcovSettings.intervalStopTime;
            end
        end
        %=============================              
        function value = get.metrics(this)
            checkId(this);
            
            id = this.id;
            if id==0
                value = this.localData.metrics;
            else
                [allMetricNames, allTOMetricNames] = cvi.MetricRegistry.getAllMetricNames();
                value = [];
                
                for idx = 1:numel(allMetricNames)
                    metric = allMetricNames{idx};
                    value.(metric) = getMetricData(id, metric);
                end
                
                if ~isempty(allTOMetricNames)
                    value.testobjectives  = getToMetricData(id, allTOMetricNames);
                end
            end
        end
        %=============================              
        function value = get.traceOn(this)
            checkId(this);
            
            id = this.id;
            if id==0
                value = this.localData.traceOn;
            else
                value = cv('get', id, '.traceOn');
            end
        end

        %=============================              
        function set.traceOn(this, value)
            checkId(this);
            id = this.id;
            if id==0
                this.localData.traceOn = value;
            else
                cv('set', id, '.traceOn', value);
            end
        end

        %=============================              
        function value = get.trace(this)
            checkId(this);
            
            id = this.id;
            value = [];
            %only derived data can produce trace for now
            
            if id == 0
                if isfield(this.localData, 'trace')
                    value = this.localData.trace;
                end
            else
                if any(cv ('get', id, '.traceData'))
                    [allMetricNames, allTOMetricNames] = cvi.MetricRegistry.getAllMetricNames();
                    allMetricNames = setdiff(allMetricNames, {'sigrange', 'sigsize'});
                    for idx = 1:numel(allMetricNames)
                        metric = allMetricNames{idx};
                        value.(metric) = getTraceData(id, metric);
                    end
                end
                value.testobjectives  = getTOTraceData(id);
            end
        end
        %=============================              
        function set.testRunInfo(this, value)
            id = this.id;
            if id == 0
                this.localData.testRunInfo = value;
            else
                cv('set', id, '.testRunInfo', value);
            end
        end
        %=============================              
        function value = get.testRunInfo(this)
            id = this.id;
            value = [];
            if id == 0
                if isfield(this.localData, 'testRunInfo')
                    value = this.localData.testRunInfo;
                end
            else
                value = cv('get', id, '.testRunInfo');
            end
            
            if isempty(value)
                % Return structure with default fields.
                value = struct('runId',0, 'runName','', 'testId',[]);
            end
        end
        
        %=============================              
        function set.aggregatedTestInfo(this, value)
            id = this.id;
            if id == 0
                this.localData.aggregatedTestInfo = value;
            else
                cv('set', id, '.aggregatedTestInfo', value);
            end
        end
        %=============================              
        function value = get.aggregatedTestInfo(this)
            id = this.id;
            value = [];
            if id == 0
                if isfield(this.localData, 'aggregatedTestInfo')
                    value = this.localData.aggregatedTestInfo;
                end
            else
                value = cv('get', id, '.aggregatedTestInfo');
            end
        end
        
        %=============================              
        function set.reqTestMapInfo(this, value)
            id = this.id;
            if id == 0
                this.localData.reqTestMapInfo = value;
            else
                cv('set', id, '.reqTestMapInfo', value);
            end
            
            if this.scopeDataToReqs
                this.applyReqMask(true);
            end
        end
        %=============================              
        function value = get.reqTestMapInfo(this)
            id = this.id;
            value = [];
            if id == 0
                if isfield(this.localData, 'reqTestMapInfo')
                    value = this.localData.reqTestMapInfo;
                end
            else
                value = cv('get', id, '.reqTestMapInfo');
            end
        end

        %=============================              
        function set.scopeDataToReqs(this, value)
            value = logical(value);
            isChanged = (value ~= this.scopeDataToReqs);
            id = this.id;
            if id == 0
                this.localData.scopeDataToReqs = value;
            else
                cv('set', id, '.scopeDataToReqs', value);
            end
            
            if isChanged
                this.applyReqMask(value);
            end
        end
        %=============================              
        function value = get.scopeDataToReqs(this)
            id = this.id;
            value = false;
            if id == 0
                if isfield(this.localData, 'scopeDataToReqs')
                    value = this.localData.scopeDataToReqs;
                end
            else
                value = cv('get', id, '.scopeDataToReqs');
            end
        end
        
        %=============================              
        function set.filterData(this, fd)
            checkId(this);
            
            id = this.id;
            if id==0
                this.localData.filterData = fd;
            else
                cv('set', id, '.filterData', fd);
            end
        end
        %=============================              
        function value = get.filterData(this)
            checkId(this);
            
            id = this.id;
            if id==0
                value = this.localData.filterData;
            else
                value = cv('get', id, '.filterData');
            end
        end
        %=============================              
        function value = get.modelinfo(this)
            checkId(this);
            
            id = this.id;
            if id==0
                value = this.localData.modelinfo;
            else
                fieldNames = {'modelVersion','creator','lastModifiedDate','defaultParameterBehavior','blockReductionStatus', ...
                    'conditionallyExecuteInputs','mcdcMode','analyzedModel','reducedBlocks','ownerModel','ownerBlock','harnessModel'};
                value = [];
                for idx = 1:numel(fieldNames)
                    fn = fieldNames{idx};
                    value.(fn) = getModelInfoField(id, fn);
                end
                
                cvt = cvtest(id);
                value.logicBlkShortcircuit = cvt.getSlcovSettings.logicBlkShortcircuit;
            end
        end

        function set.harnessModel(this, value)
            if this.id==0
                this.localData.modelinfo.harnessModel =  value;
            else
                cv('set', this.id, 'testdata.harnessModel', value);
            end
        end         
        
        function value = get.harnessModel(this)
            if this.id==0
                value = this.localData.modelinfo.harnessModel;
            else
                value = cv('get', this.id, '.harnessModel');
            end
        end
        
        function set.ownerModel(this, value)
            if this.id==0
                this.localData.modelinfo.ownerModel =  value;
            else
                cv('set', this.id, 'testdata.ownerModel', value);
            end
        end         
        
        function value = get.ownerModel(this)
            if this.id==0
                value = this.localData.modelinfo.ownerModel;
            else
                value = cv('get', this.id, '.ownerModel');
            end
        end
        
        function set.filter(this, value)
            checkId(this);
            value = convertStringsToChars(value);
            if this.id==0
                this.localData.covFilter =  value;
            else
                cv('set', this.id, 'testdata.covFilter', value);
            end
            applyFilter(this);
        end         
        
        function value = get.filter(this)
            checkId(this);
            if this.id==0
                value = this.localData.covFilter;
            else
                value = cv('get', this.id, '.covFilter');
            end
            if isempty(value)
                value = '';
            end
        end
    
        
       function set.filterApplied(this, value)
            if this.id==0
                this.localData.filterApplied =  value;
            else
                cv('set', this.id, 'testdata.filterApplied', value);
            end
        end         
        
        function value = get.filterApplied(this)
            if this.id==0
                value = this.localData.filterApplied;
            else
                value = cv('get', this.id, '.filterApplied');
            end
        end

        function value = get.sfcnCovData(this)
            checkId(this);
            if this.id==0
                value = this.localData.sfcnCovData;
            else
                value = cv('get', this.id, '.data.sfcnCovData');
            end
        end
        
        function set.sfcnCovData(this, obj)
            checkId(this);
            obj = convertStringsToChars(obj);
            if this.id==0
                this.localData.sfcnCovData = obj;
            else
                if isempty(obj)
                    obj = SlCov.results.CodeCovDataGroup.empty();
                else
                    if ~ischar(obj)
                        validateattributes(obj, {'SlCov.results.CodeCovDataGroup'}, {'scalar'}, 'cvdata.set.sfcnCovData', '', 1);
                    end
                end
                
                cv('set', this.id, '.data.sfcnCovData', obj);
            end
            if ~isempty(obj)
                obj.setCovData(this);
            end
        end

        function value = get.codeCovData(this)
            checkId(this);
            if this.id==0
                value = this.localData.codeCovData;
            else
                value = cv('get', this.id, '.data.codeCovData');
            end
        end
        
        function set.codeCovData(this, obj)
            checkId(this);
            obj = convertStringsToChars(obj);
            if this.id==0
                this.localData.codeCovData = obj;
            else
                if isempty(obj)
                    obj = SlCov.results.CodeCovData.empty();
                else
                    if ~ischar(obj)
                        validateattributes(obj, {'SlCov.results.CodeCovData'}, {'scalar'}, 'cvdata.set.codeCovData', '', 1);
                    end
                end
                
                cv('set', this.id, '.data.codeCovData', obj);
            end
            if ~isempty(obj)
                obj.setCovData(this);
            end
        end 

        function value = get.simMode(this)
            checkId(this);
            if this.id==0
                value = this.localData.simMode;
            else
                value = SlCov.CovMode(cv('get', cv('get', this.rootId, '.modelcov'), '.simMode'));
            end
        end

        function val = get.isExternalMATLABFile(this)
            checkId(this);
            if this.id==0
                val = logical(this.localData.isExternalMATLABFile);
            else
                rId = this.rootId;
                val = cv('get', cv('get', rId, '.modelcov'), '.isScript')==1 && ...
                    cv('get', cv('get', rId, '.topSlsf'), '.origin')==3;
            end            
        end

        function val = get.isSubsystem(this)
            checkId(this);
            if this.id==0
                val = logical(this.localData.isSubsystem);
            else
                rId = this.rootId;
                val = ~isempty( cv('get', rId, '.path'));
            end            
        end

        function val = get.isSimulinkCustomCode(this)
            if ~SlCov.isSLCustomCodeCovFeatureOn()
                val = false;
                return
            end
            checkId(this);
            if this.id==0
                val = logical(this.localData.isSimulinkCustomCode);
            else
                rId = this.rootId;
                modelcovId = cv('get', rId, '.modelcov');
                val = cv('get', modelcovId, '.isScript')==1 && ...
                      cv('get', cv('get', rId, '.topSlsf'), '.origin')==4 && ...
                      ~SlCov.CovMode.isGeneratedCode(cv('get', modelcovId, '.simMode'));
                if val
                    codeGrp = this.sfcnCovData;
                    if ~isempty(codeGrp)
                        allRes = codeGrp.getAll();
                        allModes = [allRes.Mode];
                        val = val && all(allModes==SlCov.CovMode.SLCustomCode);
                    end
                end
            end
        end

        function val = get.isSharedUtility(this)
            checkId(this);
            if this.id==0
                val = logical(this.localData.isSharedUtility);
            else
                rId = this.rootId;
                modelcovId = cv('get', rId, '.modelcov');
                val = cv('get', modelcovId, '.isScript')==1 && ...
                      SlCov.CovMode.isGeneratedCode(cv('get', modelcovId, '.simMode')) && ...
                      cv('get', cv('get', rId, '.topSlsf'), '.origin')==4 && ...
                      cv('get', cv('get', cv('get', rId, '.topSlsf'), '.treeNode.child'), '.origin')==4;
            end
        end

        function val = get.isCustomCode(this)
            checkId(this);
            if this.id==0
                val = logical(this.localData.isCustomCode);
            else
                rId = this.rootId;
                modelcovId = cv('get', rId, '.modelcov');
                val = cv('get', modelcovId, '.isScript')==1 && ...
                      SlCov.CovMode.isGeneratedCode(cv('get', modelcovId, '.simMode')) && ...
                      cv('get', cv('get', rId, '.topSlsf'), '.origin')==4 && ...
                      cv('get', cv('get', cv('get', rId, '.topSlsf'), '.treeNode.child'), '.origin')~=4;
            end
        end
        
        function val = get.covSFcnEnable(this)
            checkId(this);
            if this.id==0
                val = logical(this.localData.covSFcnEnable);
            else
                val = cv('get',this.id,'.covSFcnEnable');
            end
            
        end
        
        function set.tag(this, str)
            str = convertStringsToChars(str);
            if this.id==0
                this.localData.tag = str;
            else
               cv('SetTag', this.id, str);
            end
        end
        function descr = get.tag(this)
            if this.id==0
                descr = this.localData.tag ;
            else
               descr = cv('GetTag', this.id);
            end
        end
        function set.description(this, str)
            str = convertStringsToChars(str);
            if this.id==0
                this.localData.description = str;
            else
               cv('SetDescription', this.id, str);
            end
        end
        function descr = get.description(this)
            if this.id==0
                descr = this.localData.description ;
            else
               descr = cv('GetDescription', this.id);
            end
        end
        function set.aggregatedIds(this, fN)
            if isempty(fN)
                return;
            end
            if ~iscell(fN)
                fN = {fN};
            end
            str = strjoin(fN, ',');
            if this.id==0
                this.localData.aggregatedIds = str;
            else
               cv('set', this.id, '.aggregatedIds', str);
            end
        end
        function aggregatedIds = get.aggregatedIds(this)
            aggregatedIds = [];
            if this.id==0
               if isfield(this.localData, 'aggregatedIds')
                   str = this.localData.aggregatedIds;
               else
                   str = '';
               end
            else
               str = cv('get', this.id, '.aggregatedIds');
            end
            if ~isempty(str)
                aggregatedIds = strsplit(str,',');
            end
        end
        function set.uniqueId(this, id)
            checkId(this);
            if this.id==0
                this.localData.uniqueId = id;
            else
               cv('set', this.id, '.uniqueId', id);
            end
        end
        function id = get.uniqueId(this)
            checkId(this);
            id = [];
            if this.id==0
               if isfield(this.localData, 'uniqueId')
                   id = this.localData.uniqueId;
               end
            else
               id  = cv('get', this.id, '.uniqueId');
            end
        end
        
    end
    
    methods (Hidden)
        %% Public methods defined in separate file ------------------------
        %
        newCvd = linkSubsystemData(this, cvd)
        filter = setLinkedSubsystemFilter(this, filter, toJustifySubsystems, linkedSubsysCvd)
        cvdata = applyFilter(cvdata, fileName)
        ddObj = commitdd(mObj)
        [enabled, enabledTO] = getEnabledMetricNames(cvdata)
        result = isDerived(Obj)
        [metricStruct, traceStruct] = perform_operation(lhs_cvdata, rhs_cvdata, opFcn, opChar, joinedAggregatedTestInfo)
        foundCvIds = findCvIdsInScope(this, scope, invert)

        %% Public method: isExternalFile ----------------------------------
        function res = isExternalFile(this)
            res = this.isExternalMATLABFile || this.isSimulinkCustomCode || ...
                this.isCustomCode || this.isSharedUtility;
        end

        %% Public method: isEmpty------------------------------------------
        function res = isEmpty(this)
            res = ~any(this.metrics.testobjectives.cvmetric_Structural_block) && ...
                    (isempty(this.metrics.decision) || ~any(this.metrics.decision)) && ...
                    isempty(this.sfcnCovData) && ...
                    isempty(this.codeCovData);
        end
        %% Public method: fillCachedSFcnCovInfoStruct ---------------------
        %         
        function fillCachedSFcnCovInfoStruct(this)
            this.cachedSFcnCovInfoStruct = cvi.SFunctionCov.extractResultstInfo({this});
        end

        %% Public method: refreshSimMode ----------------------------------
        %  Abstract:
        %     Replace ModelRefTopSIL (or ModelRefTopPIL) by SIL (or PIL)
        function refreshSimMode(this)
            checkId(this);
            if this.id==0
                val = SlCov.CovMode(this.localData.simMode);
            else
                val = SlCov.CovMode(cv('get', cv('get', this.rootId, '.modelcov'), '.simMode'));
            end

            newVal = SlCov.CovMode.fixTopMode(val);

            if newVal~=val
                if this.id==0
                    this.localData.simMode = newVal;
                else
                    cv('set', cv('get', this.rootId, '.modelcov'), '.simMode', newVal);
                end
            end
        end
        
        function load(this)           
            if this.isLoaded 
                return;
            end
            cvd = cvdata.loadFileRef(this);
            ri = getRootId(cvd.id);
            if ri == 0
               throwAsCaller(MException(message('Slvnv:simcoverage:cvdata:InvalidCvDataNoObj')));
            end
            this.isLoaded = true;
            this.id = cvd.id;
        end         
        
    end
    
    methods (Static, Hidden)
        metricIndexMap = getMetricIndices(data, descendantCvIds, allMetricNames)
       
        u = processMetric(rootId, metric, collectedMetricData, opFcn, opChar)
        
        [metricStruct, traceStruct] = processSubsystemMetric(targetRootId, targetCvd, targetIndices, ...
                                               sourceCvd, sourceIndices, ...
                                               joinedAggregatedTestInfo, ...
                                               metricNames, toMetricNames, op, isSameSubsys)
        
        this_Trace = getTraceContributionForSide(this_cvdata, this_TraceData, ...
                                                 this_OutcomeExecuted, other_OutcomeExecuted,...
                                                 joinedAggregatedTestInfo, needTraceIdRemap)

        [traceMcdcTrue, traceMcdcFalse] = processMcdcTrace(rootId, ...
                                            lhs_cvdata, rhs_cvdata, ...
                                            lhsMetricDataAll, rhsMetricDataAll, ...
                                            joinedAggregatedTestInfo)
                                        
        function cvd = findCvdataByUniqueId(uniqueId)
            cvd = [];
            if isempty(uniqueId)
                return;
            end
            
            testdataIds = cv('find', 'all', '.isa', cv('get','default','testdata.isa'));
            allCvds = [];
            if ~iscell(uniqueId)
                uniqueId = {uniqueId};
            end
            for idx = 1:numel(uniqueId)
                foundId = cv('find', testdataIds, '.uniqueId', uniqueId{idx});
                cvdTemp = [];
                if (numel(foundId) == 1)
                    cvdTemp = cvdata(foundId);
                    if cvdTemp.valid()
                        allCvds = [allCvds cvdTemp]; %#ok<AGROW>
                    end
                end
                if isempty(cvdTemp)
                    %something wrong
                    return
                end
            end
            if isempty(allCvds)
               %something wrong
               return
            end
            if numel(allCvds) == 1
                cvd = allCvds;
            else
                cvd = cv.cvdatagroup(allCvds(:));
            end
        end
                             
       function this = setupFileRef(this, fileName, uuid)        
            [path, fileName, ext] = cvi.ReportUtils.getFilePartsWithReadChecks(fileName, '.cvt');
            fullFileName = fullfile(path, [fileName ext]);
            dirInfo = dir(fullFileName);
            this.fileRef.name = fullFileName;            
            this.fileRef.datenum = dirInfo.datenum;
            this.fileRef.uuid = uuid;
            this.isLoaded = false; 
       end
       function checkFileRef(this)       
            [path, fileName, ext] = cvi.ReportUtils.getFilePartsWithReadChecks(this.fileRef.name, '.cvt');
            fullFileName = fullfile(path, [fileName ext]);
            dirInfo = dir(fullFileName);
            if ~isequal(this.fileRef.datenum, dirInfo.datenum)
                throwAsCaller(MException(message('Slvnv:simcoverage:cvdata:InvalidCvDataFileRef', fullFileName, datestr(this.fileRef.datenum))));
            end
       end
       function cvd = loadFileRef(this)
            cvd = [];
            if ~this.isLoaded
                %prevent .valid checking it
                this.isLoaded = true; 
                cvd = cvdata.findCvdataByUniqueId(this.fileRef.uuid);
                if ~isempty(cvd)
                    return;
                end
                cvdata.checkFileRef(this);
                [~, d] = cvload(this.fileRef.name);
                if isempty(d)
                   throwAsCaller(MException(message('Slvnv:simcoverage:cvdata:InvalidCvDataNoObj')));
                end
                cvd = d{1};
            end
       end
        
        %% Static Method: findModel ---------------------------------------
        %        
        function [modelName, modelcovId] = findModel(testObj)
            
            validateattributes(testObj, {'cvdata'}, {'scalar'}, 'cvdata.findModel', '', 1);
            
            modelcovId = cv('get',testObj.rootId,'.modelcov');
            if SlCov.CovMode.isGeneratedCode(cv('get', modelcovId, '.simMode'))
                modelName = cv('get', modelcovId, '.unmangledName');
            else
                modelName = cv('GetModelcovName',modelcovId);
            end
        end
        
        %% Static Method: findTopModel ------------------------------------
        %
        function [topModelName, topModelcovId] = findTopModel(testObj)
            
            validateattributes(testObj, {'cvdata'}, {'scalar'}, 'cvdata.findTopModel', '', 1);
            
            modelCovId = cv('get',testObj.rootId,'.modelcov');
            
            topModelcovId = cv('get', modelCovId, '.topModelcovId');

            topModelName = '';
            if ~isempty(topModelcovId) && topModelcovId ~=0
                if SlCov.CovMode.isGeneratedCode(cv('get', topModelcovId, '.simMode'))
                    topModelName = cv('get', topModelcovId, '.unmangledName');
                else
                    topModelName  = cv('GetModelcovName',topModelcovId);
                end
            end
        end
        
        %% Static Method: findTopModelHandle ------------------------------
        %
        function [topModelHandle, topModelcovId] = findTopModelHandle(testObj)
            
            validateattributes(testObj, {'cvdata'}, {'scalar'}, 'cvdata.findTopModelHandle', '', 1);
            
            [~, topModelcovId] = cvdata.findTopModel(testObj);
            topModelHandle = cv('get', topModelcovId, '.handle');
            if SlCov.CovMode.isGeneratedCode(cv('get', topModelcovId, '.simMode'))
                topModelName = cv('get', topModelcovId, '.unmangledName');
                try
                    topModelHandle = get_param(topModelName, 'Handle');
                catch
                end
            end
        end        
        
        %% Static Method: getHTMLOptions ----------------------------------
        %
        function options = getHTMLOptions(testObj)
            
            validateattributes(testObj, {'cvdata'}, {'scalar'}, 'cvdata.getHTMLOptions', '', 1);
            
            topModelName = cvdata.findTopModel(testObj);
            if ~isempty(topModelName)
                options = cvi.CvhtmlSettings(topModelName);
                options.topModelName = topModelName; 
            else
                modelName = cvdata.findModel(testObj);
                options = cvi.CvhtmlSettings(modelName);
            end
        end       
       aggregateDescription(this, srcCvd1, srcCvd2)        
       aggregateUniqueIds(this, srcCvd1, srcCvd2)
       nai = joinAggregatedTestInfo(lhsCvd, rhsCvd)
       ai = createAggregatedTestInfo(cvd)
       [ati, traceStruct] = removeDuplicateTestTraces(ati, traceStruct)
       
       function traceId = getInternalTraceId(uniqueId, aggregatedTestInfo)
           traceId =  find({aggregatedTestInfo.uniqueId} == string(uniqueId));
       end
       
       function uniqueId = getUniqueIdFromTraceId(aggregatedTestInfo, traceId)
           uniqueId =  aggregatedTestInfo(traceId).uniqueId;
       end
       
    end
    
    
    methods (Hidden)
        newCvd = addSubsystem(this, targetSubsys, cvd)
        newCvd = cutSubsystem(this, targetSubsys)
        
        newCvd = applyMask(this, mask)
        newCvd = applyDataMask(this, targetCvIds)
        applyHierarchyMask(this, targetCvIds, mask)
        cvIds = findMaskCvIds(this, mask)
        setAggregatedTestInfo(this, lhsCvd, rhsCvd)
        processAgregatedInfo(this)
        
        traceLabel = getTrace(this, metricName, idx, toString)
        res = getTraceLabel(this, traceId)
        traceInfo = getTraceInfo(this, metricName, idx)
        
        applyReqMask(this, isMasked)
        buildTraceMask(this)
        reaggregateFromTrace(this, metricName, isTOMetric, traceMask)
        
        
        createDerivedData(this, lhs, rhs, metrics, trace)                                
        setMetricData(this, metricData)
        processMetrics(this)
        
        function addFilterData(this, fd)
            oldFD = this.filterData;
            if isempty(oldFD)
                newFD = fd; 
            else
                newFD = [oldFD fd];
            end
            this.filterData = newFD;
        end

        
        function setRootId(this, id)
            if this.id == 0
                this.localData.rootId = id;
            end
        end
        
        function variantStates = getRootVariantStates(this)
            if this.id == 0
                variantStates = this.localData.variantStates;
            else
               variantStates = cv('get', this.id, '.variantStates');
            end
        end
        
        function setRootVariantStates(this, variantStates)
            if this.id == 0
                this.localData.variantStates = variantStates;
            else
               cv('set', this.id, '.variantStates', variantStates);
            end
        end
        function storeRootVariants(this)
            variantStates = cvi.RootVariant.getRootVariantStates(this.rootID);
            if ~isempty(variantStates)
                this.setRootVariantStates(variantStates);
                chks = cv('get', this.rootID, '.checksum'); 
                if this.id == 0
                    tvchks.u1 = chks(1);
                    tvchks.u2 = chks(2);
                    tvchks.u3 = chks(3);
                    tvchks.u4 = chks(4);
                    this.localData.variantChecksum = tvchks;
                else
                   cv('set', this.id, '.variantChecksum', chks); 
                end
            end
        end
        %=============================              
        function setAnalyzedModel(this, value)
            id = this.id;
            if id == 0
                this.localData.modelinfo.analyzedModel = value;
            end
        end

        %% Private Method: checkId ----------------------------------------
        %
        function checkId(this)
            
            if ~this.isLoaded
                this.load();
            end

            id = this.id;
            
            if id==0 
                if ~isstruct(this.localData) ||...
                    ~isfield(this.localData, 'rootId') || ...
                    ~cv('ishandle', this.localData.rootId)
                
                    throwAsCaller(MException(message('Slvnv:simcoverage:cvdata:InvalidCvDataNoObj')));
                end
            else
                if ~cv('ishandle', id) || isempty(cv('get', id, '.isa')) || ...
                        cv('get', id, '.isa') ~= cv('get', 'default', 'testdata.isa')
                    
                    throwAsCaller(MException(message('Slvnv:simcoverage:cvdata:InvalidCvData', 1)));
                end
            end
            
        end
        %=========
        function value = canHarnessMapBackToOwner(this)
            value = cv('get', cv('get', this.rootId, '.modelcov'), '.canHarnessMapBackToOwner');   
        end
    end
end

%% ------------------------------------------------------------------------
function invalid_subscript
throwAsCaller(MException(message('Slvnv:simcoverage:subsref:InvalidSubscript')));
end

%% ------------------------------------------------------------------------
function rootId = getRootId(id)

rootId = cv('get',id,'.linkNode.parent');
if ~cv('ishandle',rootId) || cv('get','default','root.isa') ~= cv('get',rootId,'.isa')
    %warning(message('Slvnv:simcoverage:subsref:UnresolvedRootBlock'));
    rootId = 0;
end

end

%% ------------------------------------------------------------------------
function value = getModelInfoField(id, fn)

if strcmpi(fn, 'analyzedModel')
    value = cv('get',id, '.analyzedModel');
    if ~isempty(value)
        return;
    end
        
    rootId = getRootId(id);
    modelcovId = cv('get', rootId, '.modelcov');
    if isempty(modelcovId) || ~cv('ishandle', modelcovId)
        return;
    end
    th = cv('get', cv('get', rootId, '.topSlsf'), '.handle');
    % The model can have been closed then fallback to the specified
    % model's name if not a valid handle
    if is_simulink_handle(th)
        value = getfullname(th);
    else
        if SlCov.CovMode.isGeneratedCode(cv('get', modelcovId, '.simMode'))
            value = cv('get', modelcovId, '.unmangledName');
        else
            value = cv('GetModelcovName', modelcovId);
        end
        path = cv('get', rootId, '.path');
        if ~isempty(path)
            value = [value '/' path];
        end
    end
else
    value = cv('get',id,['.' fn]);
end

end

%% ------------------------------------------------------------------------
function value = getToMetricData(id, allTOMetricNames)

value = [];
for idx = 1:numel(allTOMetricNames)
    value.(allTOMetricNames{idx}) = zeros(0,1);
end

metricdataIds = cv('get',id, 'testdata.testobjectives');
metricdataIds(metricdataIds == 0) = [];
if ~isempty(metricdataIds)
    for idx = 1:numel(metricdataIds)
        if metricdataIds(idx) ~= 0
            data = cv('get', metricdataIds(idx), '.data.rawdata');
            metricName = cv('get', metricdataIds(idx), '.metricName');
            value.(metricName) = data;
        end
    end
end

end

%% ------------------------------------------------------------------------
function value = getTOTraceData(id)

value = [];

metricdataIds = cv('get',id, 'testdata.testobjectives');
metricdataIds(metricdataIds == 0) = [];
if ~isempty(metricdataIds)
    for idx = 1:numel(metricdataIds)
        if metricdataIds(idx) ~= 0
            data = cv('get', metricdataIds(idx), '.trace.rawdata');
            if ~isempty(data)
                metricName = cv('get', metricdataIds(idx), '.metricName');
                value.(metricName) = data;
            end
        end
    end
end

end

%% ------------------------------------------------------------------------
function value = getMetricData(id, metricName)

enumVal = cvi.MetricRegistry.getEnum(metricName);
if enumVal<0
    invalid_subscript;
end
value = cv('get',id,['testdata.data.' metricName]);

end
%% ------------------------------------------------------------------------
function value = getTraceData(id, metricName)

enumVal = cvi.MetricRegistry.getEnum(metricName);
if enumVal<0
    invalid_subscript;
end
value = cv('get',id,['testdata.traceData.' metricName]);

end

