classdef (Sealed = true) Dictionary < handle & matlab.mixin.CustomDisplay
%CODER.DICTIONARY represents an embedded coder dictionary object
%
%   CODER.DICTIONARY object contains code specifications used to generate
%   code. This object allows you to perform operations to initialize the
%   dictionary and select package-based code specifications.
%
%   CODER.DICTIONARY(SOURCENAME) can be used to construct a new embedded
%   coder dictionary object.
%
%   Arguments: 
%     SOURCENAME:   Input. Name or handle of a model or the name of a
%                   Simulink data dictionary file.
%
%
% CODER.DICTIONARY methods:
%    get                         - gets the value of the specified property
%    set                         - sets the value of the specified property
%    resetToDefault              - resets dictionary to initial state.
%    loadPackage                 - loads the specified package.
%    unloadPackage               - unloads the specified package.
%    refreshPackage              - refreshes the loaded package in the
%                                  dictionary.
%    empty                       - returns true if the dictionary is empty.
%    valid                       - returns true if the dictionary is valid.
%    remove                      - deletes all dictionary content
%    getSection                  - returns a coder.dictionary.Section object
%    getSections                 - returns an array of
%                                  coder.dictionary.Section objects

%   Copyright 2017-2023 The MathWorks, Inc.
    methods
        
        function dictionaryObj = set(dictionaryObj, varargin)
            try
                coder.dictionary.internal.checkValidEntry(dictionaryObj.sourceDictionary);
                if(isequal(mod(length(varargin), 2), 1))
                    DAStudio.error('SimulinkCoderApp:data:IncorrectNumberOfInputArguments');
                end
                for i = 1:2:length(varargin)
                    name  = convertStringsToChars(varargin{i});
                    value = convertStringsToChars(varargin{i + 1});
                    coder.dictionary.internal.dispatcher('checkValidProperty', dictionaryObj.sourceDictionary, dictionaryObj.sourceDictionary, name, 'set');
                    coder.dictionary.internal.dispatcher('setProp', dictionaryObj.sourceDictionary, dictionaryObj.sourceDictionary, name, value);
                end
            catch e
                throwAsCaller(e);
            end
        end
        
        function out = get(dictionaryObj, property)
            narginchk(2,2);
            try
                coder.dictionary.internal.checkValidEntry(dictionaryObj.sourceDictionary);
                property = convertStringsToChars(property);
                coder.dictionary.internal.dispatcher('checkValidProperty', dictionaryObj.sourceDictionary, dictionaryObj.sourceDictionary, property, 'get');
                retVal = coder.dictionary.internal.dispatcher('getProp',dictionaryObj.sourceDictionary, dictionaryObj.sourceDictionary, property, false);
                if isa(retVal, 'coderdictionary.data.CoderDataEntry')
                    retVal = coder.dictionary.Entry(retVal);
                end
                out = retVal;
            catch e
                throwAsCaller(e);
            end
        end
        
        function resetToDefault(dictionaryObj)
            %RESETTODEFAULT resetToDefault restores an embedded
            %   coder dictionary to it's factory default state.
            %
            %   RESETTODEFAULT(DICTIONARYOBJ) restores an embedded
            %   coder dictionary to the state it was in when it was first
            %   created. This deletes any existing customized content so it
            %   should be used with care.
            %
            %   Arguments:
            %
            %     dictionaryObj          Input. A valid coder.Dictionary object.
            %
            %

            coder.dictionary.internal.checkValidDictionary(dictionaryObj.sourceDictionary, dictionaryObj.Name, false);
            if strcmp(dictionaryObj.getCodeInterfaceType, 'ServiceInterface')
                coder.internal.CoderDataStaticAPI.createServiceInterface(dictionaryObj.Name);
            else
                coder.internal.CoderDataStaticAPI.transactionify(dictionaryObj.sourceDictionary,...
                    @()coder.internal.CoderDataStaticAPI.Utils.initializeDict(dictionaryObj.sourceDictionary.CDefinitions, true, true), ...
                    false);
            end
        end
        
        function loadPackage(dictionaryObj, packageName)
            %LOADPACKAGE loadPackage allows for the use of package based
            %   code specifications by code dictionary features
            %
            %   LOADPACKAGE(DICTIONARYOBJ, PACKAGENAME) enables the use of 
            %   storage classes and memory sections defined in the package PACKAGENAME
            %   by users of the code dictionary. 
            %
            %   Arguments:
            %
            %     dictionaryObj          Input. A valid coder.Dictionary object.
            %     packageName            Input. String. A valid package identifier.
            %
            %   Examples:
            %     Load the package 'mpt' for use by the coder dictionary 'coderDictionaryObj' 
            %     
            %         coderDictionaryObj.loadPackage('mpt');
            %

            narginchk(2,2);
            coder.dictionary.internal.checkValidDictionary(dictionaryObj.sourceDictionary, dictionaryObj.Name);
            if isempty(packageName)
                DAStudio.error('Simulink:dialog:UnableToFindPKg', packageName);
            end
            
            try 
                coder.internal.CoderDataStaticAPI.loadStorageClassInPackage(dictionaryObj.sourceDictionary, convertStringsToChars(packageName), false);
            catch e
                throwAsCaller(e);
            end
        end
        
        function unloadPackage(dictionaryObj, packageName)
            %UNLOADPACKAGE unloadPackage disallows for the use of package based
            %   code specifications by code dictionary features
            %
            %   UNLOADPACKAGE(DICTIONARYOBJ, PACKAGENAME) disables the use of 
            %   storage classes and memory sections defined in the package PACKAGENAME
            %   by users of the code dictionary. 
            %
            %   Arguments:
            %
            %     dictionaryObj          Input. A valid coder.Dictionary object.
            %     packageName            Input. String. A valid package identifier.
            %
            %   Examples:
            %     Unload the package 'mpt' for use by the coder dictionary 'coderDictionaryObj' 
            %     
            %         coderDictionaryObj.unloadPackage('mpt');
            %

            narginchk(2,2);
            coder.dictionary.internal.checkValidDictionary(dictionaryObj.sourceDictionary, dictionaryObj.Name);
            packageName = convertStringsToChars(packageName);
            if strcmp(packageName, 'SimulinkBuiltin')
                DAStudio.error('SimulinkCoderApp:data:CannotUnloadBuiltinPackages');
            end
            try 
                coder.internal.CoderDataStaticAPI.removeLegacyPackage(dictionaryObj.sourceDictionary, packageName);
            catch e
                throwAsCaller(e);
            end            
        end
        
        function packages = getLoadedPackages(dictionaryObj)
            coder.dictionary.internal.checkValidDictionary(dictionaryObj.sourceDictionary, dictionaryObj.Name);
            packages = convertCharsToStrings(coderdictionary.data.api.getNonBuiltinPackageNames(dictionaryObj.sourceDictionary));           
        end

        function refreshPackage(dictionaryObj)
            %REFRESHPACKAGE refreshPackage reloads a package from disk into
            %   the dictionary
            %
            %   REFRESHPACKAGE(DICTIONARYOBJ) re-reads a code specification 
            %   package from disk and updates the contents of the embedded
            %   coder dictionary if necessary. This is useful when the package
            %   based code specifications have been changed and the
            %   embedded coder dictionary needs to be synced with those
            %   changes.
            %
            %   Arguments:
            %
            %     dictionaryObj          Input. A valid coder.Dictionary object.
            %
            %   Examples:
            %     
            %         coderDictionaryObj.refreshPackage();
            %

            
            coder.dictionary.internal.checkValidDictionary(dictionaryObj.sourceDictionary, dictionaryObj.Name);
            isRefreshPackageList = true;
            coder.dictionary.internal.refreshPackage(dictionaryObj, isRefreshPackageList);
        end
        
        function sectionObj = getSection(dictionaryObj, sectionID)
            %GETSECTION getSection returns the requested Section object
            %
            %   GETSECTION(DICTIONARYOBJ, SECTIONID) returns a coder.dictionary.Section
            %   for the section identified by sectionID. Valid sections
            %   are: 'StorageClasses', 'MemorySections', 'FunctionCustomizationTemplates'
            %
            %   Arguments:
            %
            %     sectionObj          Input. A valid coder.Dictionary object.
            %
            %   Examples:
            %     
            %         sectionObj = getSection(coderDictionaryObj, 'StorageClasses');
            %

            narginchk(2,2);
            coder.dictionary.internal.checkValidDictionary(dictionaryObj.sourceDictionary, dictionaryObj.Name);
            try
                sectionObj = coder.dictionary.internal.resolveSection(dictionaryObj, convertStringsToChars(sectionID));
            catch e
                throwAsCaller(e);
            end
        end

        function defaultEntry = getDictionaryDefault(dictionaryObj, varargin)
            narginchk(2,3);
            coder.dictionary.internal.checkValidDictionary(dictionaryObj.sourceDictionary, dictionaryObj.Name);
            try
                defaultEntry = coder.dictionary.internal.getDefault(dictionaryObj, varargin{:});
            catch e
                throwAsCaller(e);
            end
        end
        
        function setDictionaryDefault(dictionaryObj, varargin)
            coder.dictionary.internal.checkValidDictionary(dictionaryObj.sourceDictionary, dictionaryObj.Name);
            try
                coder.dictionary.internal.setDefault(dictionaryObj, varargin{:});
            catch e
                throwAsCaller(e);
            end
        end
        function sectionArray = getSections(dictionaryObj)
            coder.dictionary.internal.checkValidDictionary(dictionaryObj.sourceDictionary, dictionaryObj.Name);
            try
                sectionArray = coder.dictionary.internal.resolveSections(dictionaryObj.sourceDictionary);
            catch e
                throwAsCaller(e);
            end
        end        
        
        function isEmpty = empty(dictionaryObj)
            %EMPTY returns true if the dictionary is empty
            %
            %   EMPTY(DICTIONARYOBJ) returns true if the dictionary is
            %   empty
            %
            %   Arguments:
            %
            %     dictionaryObj   Input. A valid coder.Dictionary object.
            %     isEmpty         Output. Logical. True if dictionary is
            %                     empty
            %
            %   Examples:
            %     
            %       isDictionaryEmpty = empty(coderDictionaryObj);
            %

            coder.dictionary.internal.checkValidDictionary(dictionaryObj.sourceDictionary, dictionaryObj.Name, false);
            isEmpty = dictionaryObj.sourceDictionary.isEmpty();
        end
        
        function isvalid = valid(dictionaryObj)
            %VALID Check to see if dictionary is still valid
            %
            %   VALID(ENTRYOBJ) returns true if the dictionary is still valid.
            %   If the dictionary has been deleted, this returns false.
            %
            %   Arguments:
            %
            %     dictionaryObj Input. A coder.Dictionary object.
            %     isvalid       Output. Logical.
            %
            %   Examples:
            %
            %       isvalid = valid(dictionary);
            %
            
            isvalid = true;
            try 
                coder.dictionary.internal.checkValidDictionary(dictionaryObj.sourceDictionary, dictionaryObj.Name);
            catch
                isvalid = false;
            end
        end

        function configurationType = getCodeInterfaceType(dictionaryObj)
            %GETCODEINTERFACETYPE getCodeInterfaceType returns the
            %dictionary code interface type
            %
            %   GETCODEINTERFACETYPE(DICTIONARYOBJ) returns the dictionary
            %   code interface type. Valid code interface types are: 
            % 'DataInterface', 'ServiceInterface'
            %
            %
            %   Examples:
            %     
            %        configurationType = getCodeInterfaceType(dictionaryObj)
            %

            configurationType = dictionaryObj.sourceDictionary.getConfigurationType;
        end
        function view(dictionaryObj)
            %VIEW view opens coder dictionary editor
            %
            %   view(DICTIONARYOBJ) opens coder dictionary editor
            %
            
            coder.dictionary.internal.view(dictionaryObj.Name);
        end
    end
    methods (Access = protected, Hidden)
        function header = getHeader(obj)
            if obj.sourceDictionary.isvalid
                headerStr = matlab.mixin.CustomDisplay.getClassNameForHeader(obj);
                if strcmp(obj.getCodeInterfaceType, 'ServiceInterface')
                    msg = message('SimulinkCoderApp:data:ServiceInterfaceDictionaryHeader', ...
                                  headerStr).getString;
                else
                    msg = message('SimulinkCoderApp:data:DictionaryHeader', ...
                                  headerStr).getString;
                end
                header = sprintf('  %s\n', msg);
            else
                error('SimulinkCoderApp:data:InvalidCoderDataEntry', message('SimulinkCoderApp:data:InvalidCoderDataEntry').getString)
            end
        end
        function propgrp = getPropertyGroups(obj)
            if obj.sourceDictionary.isvalid
                propList = coder.dictionary.internal.getPropertyGroupsForDictionary(obj);
                propgrp = matlab.mixin.util.PropertyGroup(propList);
            end
        end
        function delete(obj)
            if isa(obj.ddConn, 'Simulink.data.Dictionary')
                obj.ddConn.close;
                obj.ddConn = [];
            end
        end
    end
    methods (Hidden)
        function configurationType = getConfigurationType(dictionaryObj)
            %GETCONFIGURATIONTYPE getConfigurationType returns the
            %dictionary code interface type
            %
            %   GETCONFIGURATIONTYPE(DICTIONARYOBJ) returns the dictionary
            %   code interface type. Valid code interface types are: 
            % 'DataInterface', 'ServiceInterface'
            %
            %
            %   Examples:
            %     
            %        configurationType = getConfigurationType(dictionaryObj)
            %

            configurationType = dictionaryObj.sourceDictionary.getConfigurationType;
        end
         function remove(dictionaryObj)
            %REMOVE deletes the embedded coder dictionary
            %
            %   REMOVE(DICTIONARYOBJ) removes the embedded coder dictionary
            %   by deleting all of its contents
            %
            %   Arguments:
            %
            %     dictionaryObj   Input. A valid coder.Dictionary object.
            %
            %   Examples:
            %     
            %       coderDictionaryObj.remove();
            %

            try
                sourceName = coder.dictionary.internal.resolveValidSourceName(dictionaryObj.Name);
                coder.dictionary.internal.performCleanup(sourceName);
                coder.dictionary.internal.removeCoderDictionary(sourceName);
            catch e
                throwAsCaller(e);
            end                
        end
        function out = getSourceDictionary(obj)
            out = obj.sourceDictionary;
        end
        function dictionaryObj = Dictionary(sourceName, varargin)
            %DICTIONARY Constructor for the coder.Dictionary object
            %
            %   DICTIONARY(sourceName) gets an embedded coder dictionary
            %   from the model or Simulink data dictionary specified by
            %   'sourceName'
            %
            %   Arguments:
            %
            %     sourceName      Input. String specifying the model or
            %                     Simulink data dictionary.
            %     dictionaryObj   Output. A valid coder.Dictionary object. It
            %                     may be empty.
            %
            %   Examples:
            %     
            %         coder.Dictionary('myModel');
            %

            p = inputParser;
            addRequired(p, 'sourceName');
            addParameter(p, 'addSourceDictionary', true, @islogical);
            if slfeature('CodeGenerationProject')
                addParameter(p, 'searchProjectClosure', false, @islogical);
            end
            parse(p, sourceName, varargin{:});
            dictionaryObj = coder.dictionary.internal.constructDictionaryObject(dictionaryObj, p, sourceName);
        end
    end
    
    properties (Transient = true, Hidden)
        sourceDictionary
        Name
        ddConn = []
    end
        
end

% LocalWords:  SOURCENAME RESETTODEFAULT DICTIONARYOBJ LOADPACKAGE PACKAGENAME PKg GETDEFAULT
% LocalWords:  UNLOADPACKAGE REFRESHPACKAGE GETSECTION SECTIONID ENTRYOBJ getCodeInterfaceType
% LocalWords:  rtwdemo GETSECTIONS SETDEFAULT GETDICTIONARYDEFAULT SETDICTIONARYDEFAULT
% LocalWords:  GETCONFIGURATIONTYPE coderdictionary


