gusucode.com > datatools工具箱 matlab源码程序 > datatools/inspector/matlab/+internal/+matlab/+inspector/DefaultInspectorProxyMixin.m

     classdef DefaultInspectorProxyMixin < ...
        internal.matlab.inspector.InspectorProxyMixin
    
    % This class is unsupported and might change or be removed without
    % notice in a future version.
    
    % This class is used by the Inspector when inspecting an object, if the
    % object doesn't already inherit from the InspectorProxyMixin.  It
    % sets up the Proxy Mixin on the fly based on the public properties of
    % the original object.  This class handles array of objects as well.
    
    % Copyright 2015 The MathWorks, Inc.
    
    properties(Hidden = true)
        PropertiesAdded = {};
        DeletionListeners = {};
        PropRemovedListeners = {};
        PropAddedListeners = {};
    end
    
    methods
        % Create a new InspectorProxyMixin instance.
        function this = DefaultInspectorProxyMixin(OriginalObjects, ...
                multiplePropertyCombinationMode, ...
                multipleValueCombinationMode)
            
            this@internal.matlab.inspector.InspectorProxyMixin(...
                OriginalObjects);
            this.OriginalObjects = OriginalObjects;
            
            % Setup MultiplePropertyCombinationMode
            if nargin < 2 || isempty(multiplePropertyCombinationMode)
                this.MultiplePropertyCombinationMode = ...
                    internal.matlab.inspector.MultiplePropertyCombinationMode.getDefault;
            else
                this.MultiplePropertyCombinationMode = ...
                    internal.matlab.inspector.MultiplePropertyCombinationMode.getValidMultiPropComboMode(...
                    multiplePropertyCombinationMode);
            end
            
            % Setup MultipleValueCombinationMode
            if nargin < 3 || isempty(multipleValueCombinationMode)
                this.MultipleValueCombinationMode = ...
                    internal.matlab.inspector.MultipleValueCombinationMode.getDefault;
            else
                this.MultipleValueCombinationMode = ...
                    internal.matlab.inspector.MultipleValueCombinationMode.getValidMultiValueComboMode(...
                    multipleValueCombinationMode);
            end
            
            % Get the property list for the objects, taking into account
            % the multi-property combination mode.  This returns a list of
            % only properties which have GetAccess = public, and are not
            % hidden.
            propertyList = this.getPropertyListForMode(OriginalObjects, ...
                this.MultiplePropertyCombinationMode);
            
            % Create properties on this class for each of the original
            % object's unique properties.
            for j=1:length(OriginalObjects)
                o = OriginalObjects(j);
                this.addListeners(o);
                m = metaclass(o);
                
                s = warning('off', 'all');
                this.PreviousData{j} = ...
                    internal.matlab.inspector.Utils.createStructForObject(o);
                warning(s);
                
                metaclassProperties = m.PropertyList;
                classProperties = properties(o);
                for i = 1:length(propertyList)
                    idx = strcmp({metaclassProperties.Name}, propertyList{i});
                    if any(idx)
                        % Does this property actually exist in the metadata
                        % for the class?  Prefer to use this, as it has the
                        % other property data (description, type, etc...)
                        prop = metaclassProperties(idx);
                        propName = prop.Name;
                    elseif ismember(propertyList{i}, classProperties)
                        % Some classes use some trickery to have
                        % 'properties'.  Continue, but it won't have all of
                        % the other property data.
                        prop = [];
                        propName = propertyList{i};
                    else
                        % This property isn't defined for this object (this
                        % can happen in the case of arrays of objects)
                        continue;
                    end
                    
                    if isempty(findprop(this, propName))
                        % This is the first time we've encountered this
                        % property, add it to this object
                        d = addprop(this, propName);
                        this.PropertiesAdded = [this.PropertiesAdded; propName];
                        
                        % Also add an internal property (with suffix _PI)
                        % to help coordinate changes between the proxy and
                        % the original object
                        internalProp = addprop(this, [propName '_PI']);
                        internalProp.Hidden = true;
                        
                        try
                            % Set value to the initial value from the
                            % object.  Don't need to set get/set methods on
                            % the property - since these are properties of
                            % this mixin object directly.
                            this.(propName) = o.(propName);

                            
                            % Also set the initial value of the internal
                            % property, and add get/set methods for it
                            this.([propName '_PI']) = o.(propName);
                            
                            if iscategorical(o.(propName))
                                % Save list of categorical properties for
                                % comparison later, to check when categories
                                % are added or removed
                                this.CategoricalProperties{end+1} = propName;
                            end
                        catch
                            % Typically this won't fail, but it can sometimes with
                            % dependent properties that become invalid (for
                            % example, property d is determined by a+b, but b is a
                            % matrix and b is a char array).  Set to empty in this
                            % case.
                            this.(propName) = [];
                            this.([propName '_PI']) = [];
                        end

                        d.SetMethod = @(this, newValue) ...
                            this.setOriginalPropValue(...
                            propName, newValue);
                        d.GetMethod = @(this) this.([propName '_PI']);

                        if ~isempty(prop)
                            % Retain the Description and
                            % DetailedDescription
                            d.DetailedDescription = prop.Description;
                            d.DetailedDescription = prop.DetailedDescription;
                            
                            % d.Type = prop.Type;  This is not allowed by
                            % MCOS Store in a map instead
                            this.PropertyTypeMap(propName) = prop.Type;
                            
                            % Also retain the SetAccess (so that read-only
                            % properties remain as such)
                            d.SetAccess = prop.SetAccess;
                        else
                            this.PropertyTypeMap(propName) = ...
                                class(o.(propName));
                        end
                        d.SetObservable = true;
                        d.GetObservable = true;
                    else
                        % For properties which exist on multiple objects,
                        % use the multipleValueCombinationMode setting to
                        % determine what value to store
                        this.InternalPropertySet = true;
                        this.([propName '_PI']) = ...
                            internal.matlab.inspector.InspectorProxyMixin.getCombinedValue(...
                            this.(propName), o.(propName), ...
                            this.MultipleValueCombinationMode);
                        this.InternalPropertySet = false;
                    end
                    
                    if ~isempty(prop) && prop.SetObservable
                        % Create a listener for observable properties
                        this.PropChangedListeners{...
                            length(this.PropChangedListeners)+1,1} = ...
                            event.proplistener(o, prop, 'PostSet', ...
                            @this.multiObjPropChangedCallback);
                    end
                end
            end
        end
        
        function delete(this)
            delete@internal.matlab.inspector.InspectorProxyMixin(this);
            
            if ~isempty(this.DeletionListeners)
                cellfun(@(x) delete(x), this.DeletionListeners);
            end
            this.DeletionListeners = {};
            
            if ~isempty(this.PropAddedListeners)
                cellfun(@(x) delete(x), this.PropAddedListeners);
            end
            this.PropAddedListeners = {};
            
            if ~isempty(this.PropRemovedListeners)
                cellfun(@(x) delete(x), this.PropRemovedListeners);
            end
            this.PropRemovedListeners = {};

        end
        
        % Override fieldnames to return the list of properties which were
        % added - this assures that the order is as expected.
        function f = fieldnames(this)
            f = this.PropertiesAdded;
        end
        
        % Override properties to return the list of properties which were
        % added - this assures that the order is as expected.
        function f = properties(this)
            f = this.PropertiesAdded;
        end
        
        function multiObjPropChangedCallback(this, es, ~)
            propName = es.Name;
            this.InternalPropertySet = true;
            this.updatePropertyForChange(propName);
            this.InternalPropertySet = false;
        end
        
        function updatePropertyForChange(this, propName)
            % Set the value on the mixin object for the value which was set
            % Need to recompare values for all properties, and take into
            % account the Multiple Value Combination Mode.
            
            % This will be reinitialized with all of the values, based on
            % the multiple values combination mode
            tempValue = [];
            
            for o = this.OriginalObjects
                if this.isPropertyOfObject(o, propName)
                    if isempty(this.(propName))
                        tempValue = o.(propName);
                    else
                        tempValue = ...
                            internal.matlab.inspector.InspectorProxyMixin.getCombinedValue(...
                            tempValue, o.(propName), ...
                            this.MultipleValueCombinationMode);
                    end
                end
            end
            
            % Only set the actual property once, so any listeners will get
            % the accurate value at the end
            this.(propName) = tempValue;
            
            % Does a property inspector internal property exist?  If
            % so, set this value as well
            if isprop(this, [propName '_PI'])
                this.([propName '_PI']) = tempValue;
            end
        end
        
        % Returns the property value.  It first tries to access the value
        % directly.  If the result is empty, it checks to see if the
        % OriginalObjects has the property value set.
        function val = getPropertyValue(this, propertyName)
            try
                % Try to access the property directly
                val = this.(propertyName);
            catch
                val = [];
            end
        end
        
        % set the property value
        function setPropertyValue(this, varargin)
            propertyName = varargin{1};
            value = varargin{2};
            if nargin > 3
                % Required for value objects
                displayValue = varargin{3};
                varName = varargin{4};
            end

            % Set the property value on all objects which contain that
            % property
            isAnyProperty = false;
            this.InternalPropertySet = true;
            for idx = 1:length(this.OriginalObjects)
                obj = this.OriginalObjects(idx);
                
                % Check to make sure this property is a property of this
                % object in the array, and that its not read-only
                [isProperty, readOnly] = this.isPropertyOfObject(...
                    obj, propertyName);
                if isProperty && ~readOnly
                    if isa(obj, 'handle')
                        if iscategorical(obj.(propertyName)) && ...
                                isscalar(obj.(propertyName))
                            % Need to assign scalar categorical by index,
                            % otherwise it will change the type to char
                            obj.(propertyName)(1) = value;
                        else
                            obj.(propertyName) = value;
                        end
                        isAnyProperty = true;
                    else
                        this.setValueObjectProperty(propertyName, ...
                            displayValue, varName);
                        try
                            this.OriginalObjects(idx).(propertyName) = value;
                        catch
                            if length(this.OriginalObjects) == 1
                                % Some objects fail on indexing like above,
                                % so retry. Let this fail and throw the
                                % exception -- it will be caught and the
                                % error will be displayed.
                                this.OriginalObjects.(propertyName) = value;
                            end
                        end
                        this.(propertyName) = value;
                        isAnyProperty = true;
                    end
                end
            end
            
            if isAnyProperty
                this.InternalPropertySet = true;
                % Update the current value for this object, based on the
                % Multiple Object Value Combination Mode.
                this.updatePropertyForChange(propertyName)
            end

            this.InternalPropertySet = false;
        end
        
        function setValueObjectProperty(this, propertyName, ...
                displayValue, varName)
            evalStr = sprintf('%s.%s = %s;', varName, ...
                propertyName, displayValue);
            errorMsg = ...
                'internal.matlab.inspector.peer.InspectorFactory.getInstance.sendErrorMessage(''%1$s'');';
            
            if ischar(this.Workspace)
                if ~com.mathworks.mlwidgets.array.web.WebWorker.TESTING
                    com.mathworks.mlwidgets.array.web.WebWorker.executeCommandAndFormatError(...
                        evalStr, errorMsg);
                end
            end
        end
    end
    
    methods (Access = protected)
        function propertyAdded = addPropertyToProxy(this, propertyName)
            % Called to see if a property needs to be added to the proxy
            % object (for when dynamic properties are added to the original
            % object).
            propertyAdded = addPropertyToProxy@internal.matlab.inspector.InspectorProxyMixin(...
                this, propertyName);
            if propertyAdded
                this.PropertiesAdded = [this.PropertiesAdded; propertyName];
            end
        end
    end
    
    methods (Access = private)
        function [isProperty, readOnly] = isPropertyOfObject(~, obj, ...
                propertyName)
            
            % Returns true if the propertyName is a property of the given
            % object, obj.  If possible, also returns whether the property
            % is read-only or not.
            readOnly = false;
            
            if isprop(obj, propertyName)
                isProperty = true;
                
                if ismethod(obj, 'findprop')
                    prop = findprop(obj, propertyName);
                else
                    % If findprop is not defined, try to find the property
                    % in the metaclass PropertyList
                    m = metaclass(obj);
                    prop = findobj(m.PropertyList, 'Name', propertyName);
                end
                
                if ~isempty(prop)
                    readOnly = ~strcmp(prop.SetAccess, 'public');
                end
            else
                % Some objects redefine their properties (like timer), so
                % need to handle this case as well
                props = properties(obj);
                isProperty =  ismember(propertyName, props);
            end
        end
        
        function addListeners(this, obj)      
            if isa(obj, 'handle')
                this.DeletionListeners{end+1} = event.listener(obj, ...
                    'ObjectBeingDestroyed', @this.deletionCallback);
                
                if isa(obj, 'dynamicprops')
                    % Add listeners for dynamic properties being added or
                    % removed
                    this.PropAddedListeners{end+1} = event.listener(obj, ...
                        'PropertyAdded', @this.propAddedCallback);
                    this.PropRemovedListeners{end+1} = event.listener(obj, ...
                        'PropertyRemoved', @this.propRemovedCallback);
                end
            end
        end
        
        function deletionCallback(this, varargin)
            % Called when the object that this is the proxy object for is
            % deleted.  If this is the only object, then the proxy should
            % be deleted as well.
            if length(this.OriginalObjects) == 1
                delete(this);
            end
        end
        
        function propAddedCallback(this, es, ed)
            % Redisplay the object by setting DataChanged = true
%             this.DataChanged = true;
%             
%             this.firePropertyAddedEvent('', '');
        end
        
        function propRemovedCallback(this, es, ed)
            % Redisplay the object by setting DataChanged = true
%             this.DataChanged = true;
%             
%             this.firePropertyRemovedEvent('', '');
        end
    end
end