www.gusucode.com > appdesigner工具箱matlab源码程序 > appdesigner/+appdesigner/+internal/+codegeneration/ComponentCodeGenerator.m
classdef ComponentCodeGenerator %COMPONENTCODEGENERATOR - This class has all knowledge to use the %adapters to assemble code for a component. properties (Access = 'private', Transient) % Map of component specific data. The key is the component type % and the value is a struct with three fields % ComponentDefaults - a struct containing default values for each % field of the component % PropertyList - a 1xn cell array of property names for the % component in the order they should appear in the generated code % IsModeSibling - a 1xn logical array containing true if the % Component has a proeprty corresponding to the % [propertyName 'Mode'] string ComponentData = containers.Map(); % Design time properties to ignore DesignTimeProperties end methods function obj = ComponentCodeGenerator(designTimeProperties) obj.DesignTimeProperties = designTimeProperties; end function generatedCode = getComponentGenerationCode(obj, model, adapter) % GETCOMPONENTCODE - This class uses the current state of the % model and the adapter to generate a cell array of strings % representing the code to generate that component. % Example generated code % {'app.ad_CODENAME_ad = uilabel(app.ad_PARENTCODENAME);'} % {'app.ad_CODENAME_ad.Location = [100 20]'} % Get Code replacement map. replaceMap = appdesigner.internal.codegeneration.ComponentCodeGenerator.getCodeReplaceMap(); componentType = adapter.getComponentType(); if obj.ComponentData.isKey(componentType) % Get per component data from the map componentData = obj.ComponentData(componentType); componentDefaults = componentData.ComponentDefaults; propertyList = componentData.PropertyList; isPropertyModeSibling = componentData.IsModeSibling; else % Calculate the per component data once, then store it in % the ComponentData map componentDefaults = adapter.getComponentRunTimeDefaults(); % Property List in the order it should appear in the code propertyList = adapter.getCodeGenPropertyNames(model); % Remove the design time only properties from the property list propertyList = setdiff(propertyList, obj.DesignTimeProperties, 'stable'); % Status of whether the property has a corresponding Mode % sibling isPropertyModeSibling = adapter.isModeSibling(propertyList, model); % Store three pieces of component specific information in a % struct, then add entry to ComponentData map. componentData = struct(); componentData.ComponentDefaults = componentDefaults; componentData.PropertyList = propertyList; componentData.IsModeSibling = isPropertyModeSibling; obj.ComponentData(componentType) = componentData; end % Generate component code using the adapter methods as helpers. % componentStr will be something like: % 'app.ad_CODENAME_ad = uilabel(app.ad_PARENTCODENAME);' componentStr = obj.generateComponentConstructor(... model,... adapter,... replaceMap('CodeName'),... replaceMap('ParentCodeName')); % Generate component code to set the properties on the model % propertiesStr will be a cell array, one string per property % set, for example: % {'app.ad_CODENAME_ad.Location = [200 200]'} % {'app.ad_CODENAME_ad.Size = [100 20]'} propertiesStr = obj.generateComponentProperties(... model, ... adapter,... replaceMap('CodeName'), ... replaceMap('ParentCodeName'), ... propertyList, ... isPropertyModeSibling,... componentDefaults); generatedCode = [{componentStr}; propertiesStr]; end end methods (Static) function replaceMap = getCodeReplaceMap() % Here is a class that stores the replacement strings code % generation will use when components generate code. The % strings will be replaced in a code model object on the client % and the code item will update the view if one of these % properites changes. keyValuePairs = {'CodeName', 'ad_CODENAME_ad';... 'ParentCodeName', 'ad_PARENTCODENAME_ad'}; % Create Map for the replacements. All entries % should be string, so set uniformValues to true replaceMap = containers.Map(keyValuePairs(:, 1), keyValuePairs(:, 2), ... 'uniformValues', true); end end % The following block contains functions specific to generating code % using the MATLAB component adapters methods(Access = private) function entireLine = generateComponentConstructor(obj, model, adapter, codeName, parentCodeName) % Generates a single line to create the component % % Ex: app.CircularGauge = uigauge(app.UIFigure, 'circular'); % Ex: app.UIFigure parentVariableName = sprintf('app.%s', parentCodeName); % The right hand side for the component creation % % Ex: uigauge(UIFigure, 'circular') componentCreationSnippet = adapter.getCodeGenCreation( ... model, codeName, parentVariableName); % Create the entire line % % (indent) app.CircularGauge = (component creation snippet) entireLine = sprintf('app.%s = %s;', ... codeName, ... componentCreationSnippet); end function propertyStr = generateComponentProperties(obj, model, adapter,... codeName, parentCodeName, propertyList, isPropertyModeSibling, defaultComponent) % The propertyList order should be honored and assumed to be % correct. % % In general we will not generate code if the property value is % the same as the default. % If the propertyName has a 'Mode' sibling, and the 'Mode' % sibling is manual, we should always generate the code for % that propertyName. propertyStr = []; for index = 1:numel(propertyList) propertyName = propertyList{index}; showPropertyCode = adapter.shouldShowPropertySetCode(... isPropertyModeSibling(index), ... model,... propertyName, ... defaultComponent); if showPropertyCode % Ask adapter how to generate the line of code that % sets this specific value propertySegment = adapter.getCodeGenPropertySet(model, propertyName, codeName, parentCodeName); % Format the propertySegment to include leading white % space the equivalent of three tabs, add a newline to % the end of the property segment propertyStr = [propertyStr; {propertySegment}]; end end end end methods ( Static, Access = {?appdesigner.internal.componentadapterapi.VisualComponentAdapter} ) function valueStr = propertyValueToString(className, value) % This function returns a string representing the values to be % modified for the component. The string returned would be: % '[105 399]' % in order to modify the Location property of app.Pushbutton1: % app.PushButton1.Location = [105 399]; valueStr = ''; switch(class(value)) case {'logical', 'double'} valueStr = mat2str(value); case {'char'} valueStr = sprintf('''%s''', ... appdesigner.internal.codegeneration.ComponentCodeGenerator.escapeQuote(value)); case {'cell'} % case of empty cell if numel(value) == 0 valueStr = '{}'; % case of 1x1 cell elseif numel(value) == 1 % Values in cell can be string or double % 'States' property can be a double if ischar(value{1}) % 1x1 cells containing characters newValue = appdesigner.internal.codegeneration.ComponentCodeGenerator.escapeQuote(value{1}); valueStr = sprintf('{''%s''}', newValue); elseif (isnumeric(value{1})... || islogical(value{1}))... && numel(value{1}) == 1 valueStr = sprintf('{%f}', value{1}); end % case of nx1 or 1xn cell array elseif numel(value) == length(value) % case of nx1 cell array if ischar(value{1}) newValue = appdesigner.internal.codegeneration.ComponentCodeGenerator.escapeQuote(value{1}); valueStr = sprintf('{''%s''', newValue); elseif (isnumeric(value{1})... && numel(value{1}) == 1) valueStr = sprintf('{%g', value{1}); elseif (islogical(value{1}) && numel(value{1}) == 1) if value{1} valueStr = '{true'; else valueStr = '{false'; end else assert(false, ... sprintf(... 'Unexpected data type found for %s',... className)) end % case of 1xn cell array if size(value, 2) == length(value) separator = ','; else separator = ';'; end % On a rare occasion, a 1xn cell array may be of % mixed type which is why each value should be % handled separately. for entry = ... reshape(value(2:end), ... 1, numel(value(2:end))) if ischar(entry{1}) valueStr = ... sprintf('%s%s ''%s''', ... valueStr, separator,... appdesigner.internal.codegeneration.ComponentCodeGenerator.escapeQuote(entry{1})); elseif (isnumeric(entry{1})... && numel(entry{1}) == 1) valueStr = ... [valueStr, sprintf('%s %g', ... separator, entry{1})]; %#ok<AGROW> elseif (islogical(entry{1}) && numel(entry{1}) == 1) if entry{1} valueStr = ... [valueStr, sprintf('%s %s', ... separator, 'true')]; %#ok<AGROW> else valueStr = ... [valueStr, sprintf('%s %s', ... separator, 'false')]; %#ok<AGROW> end else assert(false, ... sprintf(... 'Unexpected data type found for %s',... className)) end end valueStr = [valueStr, '}']; end case 'matlab.ui.control.Label' display('Component Has Label, code cannot be generated') otherwise, % This will catch future component properties that are % not currently implemented by may be in the future % TODO, deal with LABELS assert(false, ... ['This compare class has '... 'not been implemented: %s'],... class(value)); end end function str = escapeQuote(str) % sprintf will format '' to be ', so this doubles the % single quotes so they are preserved through sprintf str = regexprep(str, '''', ''''''); end function str = generateStringForPropertySegment(codeName, ... thisProperty, value) % Example text to add the Location property to HmiFigure1: % app.HmiFigure1.Location = [50 50]; % This is the value in the form of a char where feval(valueStr) % would help produce a value that is equivalent to the original valueStr = appdesigner.internal.codegeneration.ComponentCodeGenerator.propertyValueToString(codeName, value); str = sprintf('app.%s.%s = %s;', ... codeName, thisProperty, valueStr); end end end