classdef (Hidden) TargetRegistryFileTargetFrameworkUpgrade < target.internal.UpgradePlugin
    %TARGETREGISTRYFILETARGETFRAMEWORKUPGRADE Plugin for the Target
    %Framework target.upgrade tool to upgrade RTW.TargetRegistry
    %registrations from files that register function handle callbacks to
    %the target registry.

%   Copyright 2019-2022 The MathWorks, Inc.
    
    properties (Access=protected)        
        LegacyRegstrationFilename;
        UpgradedObjects target.internal.Object;
        UpgradeCleanupStack onCleanup;
    end
    
    methods
        
        % Upgrade implementation called by the Target Framework
        % infrastructure. Performs the upgrade from RTW.TargetRegistry
        % function to the Target Framework function.
        function upgradedObjects = upgrade(this, varargin)

            % The export process may have added logic to the cleanup stack
            % When the upgrade is finished unwind and cleanup the stack.
            cleanUpgradeStack = onCleanup(@() this.cleanupUpgradeStack()); %#ok<NASGU> 

            this.LegacyRegstrationFilename = varargin{1};
            
            % Run the specified legacy scripts and create the Target
            % Framework objects we need to export to a MATLAB function.
            % These are cached in UpgradedObjects
            this.populateUpgradedObjects();
                        
            upgradedObjects = this.UpgradedObjects;
        end
    end
    
    methods (Access=protected)       
        % Protected method for derived classes to validate the function
        % passed to the upgrade and inject the function handle which will
        % pickup the target registry registrations.
        functionArguments = setupRegistrationInjection(this, functionName, registerTargetFunctionHandle);
    end
    
    methods (Access=private)
        function populateUpgradedObjects(this)
            
            % Clear any existing state
            this.UpgradedObjects = target.internal.Object.empty();
            
            % We only care about the legacy file given to us, but there are
            % probably lots of shadowing functions littered on the MATLAB
            % path. To ensure we call the correct function we need to cd to
            % its location.
            [filepath, functionName] = fileparts(this.LegacyRegstrationFilename);            
            if ~isequal(filepath, '')
                oldPath = cd(filepath);
                pathCleanup = onCleanup(@() cd(oldPath)); %#ok<NASGU>
            end
            
            % Create registrationObjects cell to accept the legacy
            % registrations and define a nested function to inject to the
            % legacy registration 
            registrationObjects = {};
            function addToRegistrationObjectCell(regObject)
                registrationObjects{end+1} = regObject;
            end
            
            functionArguments = this.setupRegistrationInjection(functionName, ...
                @(registrationHandle) addToRegistrationObjectCell(registrationHandle()));
            
            % Turn off deprecation error and make sure to turn it back on
            % after file has been read. Since the error is replaced by a
            % warning, we also disable and re-enable the warning.
            throw = RTW.HWDeviceRegistry.throwDeprecationError(false);
            scopeGuardError = onCleanup(@() RTW.HWDeviceRegistry.throwDeprecationError(throw)); %#ok<NASGU>
            warn = warning('off', 'RTW:targetRegistry:ClassToBeRemoved');
            scopeGuardWarning = onCleanup(@() warning(warn)); %#ok<NASGU>
            
            % We need to call into the legacy registration file. With the
            % arguments set by the derived class.
            feval(functionName, functionArguments{:});

            % Now we have all the legacy registration objects. But some may
            % not be supported for upgrade so we need to process them all.
            cellfun(@(r) this.processRegistration(r), registrationObjects);
                
            % If we don't have any target objects to export at this point
            % there were no upgradable definitions in the legacy
            % registration file.
            if (length(this.UpgradedObjects) < 1)
                error(message('RTW:targetRegistry:noUpgradedObjects'));
            end
        end
        
        function processRegistration(this, registrationObjects)
                                    
            % Currently upgrade only supports RTW.HWDeviceRegistry objects
            if isa(registrationObjects, 'RTW.HWDeviceRegistry')
                
                % Use our existing helper to convert the HWDeviceObject to
                % the Target Framework.
                for i=1:length(registrationObjects)
                    [targetObj, aliasObjs] = targetrepository.createFromHWDeviceRegistryObject(registrationObjects(i));
                    this.UpgradedObjects = [this.UpgradedObjects targetObj aliasObjs];
                end
            elseif isa(registrationObjects, 'coder.make.ToolchainInfoRegistry')
                if coder.make.internal.featureOn('TargetFrameworkToolchainUpgrade')
                    % Convert the ToolchainInfoRegistry objects to
                    % ToolchainBuildMethods
                    for i = 1:numel(registrationObjects)
                        % Some toolchain registration files have empty
                        % configs which should be ignored
                        if ~isempty(registrationObjects(i).Name)
                            targetObj = targetrepository.createFromToolchainInfoRegistryObject(registrationObjects(i));
                            this.UpgradedObjects = [this.UpgradedObjects targetObj];
                        end
                    end
                end
            else
                % Don't currently support the object we
                % attempted to upgrade.
            end
        end
        
        function cleanupUpgradeStack(this)
            arrayfun(@delete, this.UpgradeCleanupStack);
            this.UpgradeCleanupStack = onCleanup.empty();
        end
    end
end

