www.gusucode.com > appdesigner工具箱matlab源码程序 > appdesigner/+appdesigner/+internal/+application/AppCodeTool.m

    classdef AppCodeTool < handle
    %APPCODETOOL Processes goto and debug events by ensuring that the app
    % is opened and ready before passing along the event to the client.
    %   
    %    Copyright 2015-2016 The MathWorks, Inc.
    
    properties (Access = private)
        % AppDesignEnvironment instance
        AppDesignEnvironment
        
        % Listener to AppModel InstanceCreated class event
        AppModelCreatedListener
        
        % Listener to CodeData InstanceCreated class event
        CodeDataCreatedListener
        
        % Queue of debug events that occur while app is loading
        DebugEventQueue = {};
    end
    
    methods
        function obj = AppCodeTool(appDesignEnvironment)
            obj.AppDesignEnvironment = appDesignEnvironment;
        end
        
        function processGoToLineColumn(obj, file, line, column)
        %  PROCESSGOTOLINECOLUMN Go to line/column in the app's Code
        %  View
            
            scrollToView = true;
            obj.doProcessGoToLineColumn(file, line, column, scrollToView,...
                @obj.processGoToAppReadyCallback);
        end
        
        function processDebugInfo(obj, currentMlappFilename, currentMlappLineNumber, mlappsInStack)
        %  PROCESSDEBUGMLAPP Process debug event involving MLAPPs
        %     
        %   processDebugInfo() will
        %       1) Set debug state of all apps opened in App Designer that
        %       are found in the debug call stack (mlappsInStack)
        %
        %       2) Open/Bring to front the app, if any, that is the current
        %       frame on the call stack (currentMlappFilename). 
        %       It uses doProcessGoToLineColumn to bring code view to front
        %       and place the cursor on the line where execution is stopped
        %       for debugging
        %
        %       3) Queues calls to processDebugInfo that occur while the 
        %       debugging app is loading. Once the app is done loading, the 
        %       queue is flushed and processed.
        
            if isempty(obj.AppModelCreatedListener)
                % No app is loading and so proceed with processing the data
                
                % Ensure the path file seperators are consistent
                currentMlappFilename  = fullfile(currentMlappFilename); 

                % Process mlappsInstack 
                appDesignerModel = obj.AppDesignEnvironment.AppDesignerModel;
                for idx = 1 : length(appDesignerModel.Children)
                    appModel = appDesignerModel.Children(idx);
                    if any(strcmpi(appModel.FullFileName, mlappsInStack))
                        % App is in the stack and opened

                        % Only set app's debugging state to true only if it
                        % is not already true
                        if ~appModel.IsDebugging
                            appModel.IsDebugging = true;
                        end
                    else
                        % App is not in the stack

                        % If it was debugging, it is not now and
                        % so set debugging state to false.
                        if appModel.IsDebugging
                            appModel.IsDebugging = false;
                            
                            aws = appdesigner.internal.service.AppManagementService.instance();
                            if aws.isAppRunInAppDesigner(appModel.FullFileName) && ...
                                isempty(aws.getRunningApp(appModel.FullFileName))
                                % dbquit has occured during app construction before
                                % the running app instance could be registered with
                                % the AppManagementService. This can result in the
                                % app's figure being left open which puts the app in
                                % a bad state and so close the figure (g1353572).
                                appFig = obj.findAppFigure(appModel.FullFileName);
                                delete(appFig);
                            end
                        end
                    end
                end

                % Process currentMlappFilename
                if ~isempty(currentMlappFilename)
                    % Since currentMlappFilename is not empty, execution is
                    % stopped in an MLAPP file for debugging. Execute
                    % goToLineColumn to open/bring to front the app in App
                    % Designer and place cursor on the line execution is
                    % stopped on.
                    column = 1;
                    scrollToView = false; % RTC handles scrolling when debugging
                    obj.doProcessGoToLineColumn(currentMlappFilename, currentMlappLineNumber, column, scrollToView,...
                        @obj.processDebugAppReadyCallback);
                end
            else
                % An app is loading and so queue the event to be processed
                % once the app is ready to handle it.
                obj.DebugEventQueue{end+1} = {currentMlappFilename, currentMlappLineNumber, mlappsInStack};
            end
        end
        
        function delete(obj)
            if ~isempty(obj.AppModelCreatedListener)
                % In case of App failed to open, to clean 'InstanceCreated'
                % class event of the AppModel
                delete(obj.AppModelCreatedListener);
                obj.AppModelCreatedListener = [];
            end
            
            if ~isempty(obj.CodeDataCreatedListener)
                % In case of App failed to open, to clean 'InstanceCreated'
                % class event of the CodeData
                delete(obj.CodeDataCreatedListener);
                obj.CodeDataCreatedListener = [];
            end
        end
    end
    
    methods (Access = private)
        function doProcessGoToLineColumn(obj, file, line, column, scrollToView, appReadyCallback)     
        %   doProcessGoToLineColumn() will send 'goToLineColumn' event to
        %   client side through CodeData peer node to ask to locate
        %   the code
        
            % Check if the app opened or not
            appModel = [];
            appDesignerModel = obj.AppDesignEnvironment.AppDesignerModel;
            for idx = 1 : length(appDesignerModel.Children)
                if strcmp(file, appDesignerModel.Children(idx).FullFileName) || ...
                        (ispc && strcmpi(file, appDesignerModel.Children(idx).FullFileName))
                    appModel = appDesignerModel.Children(idx);
                    break;
                end
            end
            
            if isempty(appModel)
                % App not opened and so listen to AppModel InstanceCreated
                % class event to handle it
                obj.AppModelCreatedListener = addlistener(?appdesigner.internal.model.AppModel,...
                    'InstanceCreated', ...
                    @(o,e)obj.handleAppModelCreated(e.Instance, file, line, column, scrollToView, appReadyCallback));

                % Load the app
                appdesigner(file);
            else
                % App is opened, Request client to perform goToLineColumn.
                appModel.CodeData.sendGoToLineColumnEventToClient(line, column, scrollToView);
            end
        end
        
        function handleAppModelCreated(obj, appModel, file, line, column, scrollToView, appReadyCallback)
            % Handler for AppModel 'InstanceCreated' event. 

            % If the AppModel is the one to go to line/column, send the
            % event to the client
            if strcmp(file, appModel.FullFileName)
                delete(obj.AppModelCreatedListener);
                obj.AppModelCreatedListener = [];
                
                if isempty(appModel.CodeData)
                    % App is opened but CodeData is not ready yet and so 
                    % listen to CodeData InstanceCreated class event to handle it
                    obj.CodeDataCreatedListener = addlistener(?appdesigner.internal.codegeneration.model.CodeData,...
                        'InstanceCreated', ...
                        @(o,e)obj.handleCodeDataCreated(appModel, line, column, scrollToView, appReadyCallback));
                else
                    appReadyCallback(appModel, line, column, scrollToView);
                end
            end
        end
        
        function handleCodeDataCreated(obj, appModel, line, column, scrollToView, appReadyCallback)
            % Handler for CodeData 'InstanceCreated' event. 
            
            delete(obj.CodeDataCreatedListener);
            obj.CodeDataCreatedListener = [];
            
            appReadyCallback(appModel, line, column, scrollToView);
        end
        
        function processGoToAppReadyCallback(obj, appModel, line, column, scrollToView)
            % Callback to be executed once app has been loaded due to a
            % goToLineColumn event and is ready to be used
            
            % App is loaded and so request client to perform goToLineColumn
            appModel.CodeData.sendGoToLineColumnEventToClient(line, column, scrollToView);
        end
        
        function processDebugAppReadyCallback(obj, appModel, line, column, scrollToView)
            % Callback to be executed once app has been loaded due to a
            % debug event and is ready to be used
            
            % Set the app to be debugging
            appModel.IsDebugging = true;
            
            % Request client to perform goToLineColumn
            appModel.CodeData.sendGoToLineColumnEventToClient(line, column, scrollToView);
            
            % Process and flush DebugEventQueue
            %
            % Use a local copy of the queue and clear out the instance's
            % queue. This is necessary because one of the queued
            % events might cause another app to load which could then
            % potentially cause events to queue again and execute this
            % function again.
            %
            % Ex: App1's startup function instantiates App2 and each app
            % has breakpoint in each constructor. From command line, user
            % instantiates App1 and execution hits breakpoint. While App1
            % is loading, user executes dbcont which causes the breakpoint
            % in App2 to be hit. The user then hit dbcont again before App1
            % has finished loading. These two dbcont events are queued and
            % aren't processed until App1 finishes loading. When the first
            % dbcont event is processed it needs to load App2 and so the
            % second dbcont event will be queued again to wait until App2
            % has finished loading. Need a local copy of the queue to 
            % handle this properly.
            localQueue = obj.DebugEventQueue;
            obj.DebugEventQueue = {};
            for i=1:length(localQueue)
                obj.processDebugInfo(localQueue{i}{:});
            end
        end
        
        % Get the running app figure using its full file name
        function appFig = findAppFigure(~, fullFileName)
            appFig = [];
            
            runningAppFigures = findall(0, 'Type', 'figure',...
                '-property', 'RunningAppInstance');
            
            for i=1:length(runningAppFigures)
                fig = runningAppFigures(i);
                appMeta = metaclass(fig.RunningAppInstance);
                whichResult = which('-all', appMeta.Name);
                
                if any(cellfun(@(x) strcmp(x,fullFileName), whichResult))
                    appFig = fig;
                end
            end
        end
    end
    
end