function [diffStruct,consolidatedData1,consolidatedData2] = compareFigFiles(name1, name2) % compareFigFiles - compare .fig files or folders containing .fig files % % Syntax: % compareFigFiles(folderName1, folderName2) % compareFigFiles(folderName, figFilename) % compareFigFiles(figFilename, folderName) % [diffStruct,data1,data2] = compareFigFiles(figFilename1, figFilename2) % % Description: % compareFigFiles compares *.fig files, reporting internal components % and properties that are different between corresponding fig files. % % Inputs can be either a figure filename or folder name. When a % folder name is specified, then all the corresponding fig file(s) % in that folder will be compared to the other folder/file. % Note: when one of the inputs is a folder, then only files that % have the same name will be compared. % % compareFigFiles(folderOrFilename) compares the specified input to % the current folder (pwd). % % [diffStruct,data1,data2] = compareFigFiles(file1,file2) returns % a Matlab struct containing the non-matching components/properties. % Each of the struct fields corresponds to a specific figure handle % and property name, and contains a cell array of 2 values, for each % of the compared files. data1 and data2 contain the raw data used % for the comparison - a Matlab struct with fields corresponding to % each of the components/properties defined in the fig file. % % Examples: % compareFigFiles('C:\Yair',pwd); % compares corresponding fig files in 2 folders % compareFigFiles('C:\Yair'); % (same as above) % % compareFigFiles('myApp', 'hisApp'); % compare 2 FIG files % compareFigFiles('myApp.fig','hisApp.fig'); % (same as above) % % compareFigFiles('C:\Yair\myApp'); % compare C:\Yair\myApp.fig to (pwd)\myApp.fig % compareFigFiles('C:\Yair\myApp',pwd); % (same as above) % compareFigFiles(pwd,'C:\Yair\myApp'); % (same as above) % % Technical Description: % See http://undocumentedmatlab.com/blog/fig-files-format/ % % Bugs and suggestions: % Please send to Yair Altman (altmany at gmail dot com) % % Change log: % 2013-07-02: First version posted on MathWorks File Exchange % License to use and modify this code is granted freely to all interested, as long as the original author is % referenced and attributed as such. The original author maintains the right to be solely associated with this work. % Programmed and Copyright by Yair M. Altman: altmany(at)gmail.com % $Revision: 1.01 $ $Date: 2013/07/02 15:22:51 $ % Process optional input args if nargin < 1, help(mfilename); return; end if nargin == 1, name2 = pwd; end % Parse folder/file args if isdir(name1) && isdir(name2) % Treat as a folder comparison %clc % this might be useful when there are numerous files being compared... files = dir(fullfile(name1, '*.fig')); for fileIdx = 1 : length(files) % Compare all corresponding fig files one at a time thisFile = fullfile(name1, files(fileIdx).name); otherFile = fullfile(name2, files(fileIdx).name); if exist(otherFile,'file') diffStruct_I = compareSingleFigFiles(thisFile, otherFile); if ~isempty(diffStruct_I) fprintf('\n'); end end end % Outputs are not relevant for folder comparisons [diffStruct_I,consolidatedData1_I,consolidatedData2_I] = deal([]); elseif isdir(name1) % but not isdir(name2) % Compare name2.fig to the same file in the name1 folder [fpath,fname,fext] = fileparts(name2); %#ok otherFile = fullfile(name1, [fname,fext]); [diffStruct_I,consolidatedData1_I,consolidatedData2_I] = compareSingleFigFiles(otherFile, name2); elseif isdir(name2) % but not isdir(name1) % Compare name1.fig to the same file in the name2 folder [fpath,fname,fext] = fileparts(name1); %#ok otherFile = fullfile(name2, [fname,fext]); [diffStruct_I,consolidatedData1_I,consolidatedData2_I] = compareSingleFigFiles(name1, otherFile); else % Compare single .fig files [diffStruct_I,consolidatedData1_I,consolidatedData2_I] = compareSingleFigFiles(name1, name2); end % Process output args if nargout diffStruct = diffStruct_I; consolidatedData1 = consolidatedData1_I; consolidatedData2 = consolidatedData2_I; end end % Compare 2 FIG files, reporting their differences function [diffStruct,consolidatedData1,consolidatedData2] = compareSingleFigFiles(fig1Filename, fig2Filename) fig1Filename = normalizeFigFilename(fig1Filename); fig2Filename = normalizeFigFilename(fig2Filename); f1 = strrep(fig1Filename,'\','\\'); f2 = strrep(fig2Filename,'\','\\'); f1FullPath = getFullPath(fig1Filename); f2FullPath = getFullPath(fig2Filename); fprintf(['Comparing ' strrep(f1,'.fig','') ', ' ... '' strrep(f2,'.fig','') '\n']); data1 = getFigData(fig1Filename); data2 = getFigData(fig2Filename); consolidatedData1 = processStruct([],data1,''); consolidatedData2 = processStruct([],data2,''); diffStruct = objdiff(consolidatedData1, consolidatedData2); %if ~nargout disp(diffStruct); %end end % Read a FIG file and extract its (unconsolidated) data struct function data = getFigData(figFilename) % Load the FIG file as a MAT file (see http://undocumentedmatlab.com/blog/fig-files-format/) data = load(figFilename,'-mat'); % The file contains a struct with version info (will be 080000 in HG2) %data = data.hgS_070000; fn = fieldnames(data); data = data.(fn{1}); end % Get the full path of the specified filename function filename = getFullPath(filename) f = which(filename); if ~isempty(f) filename = f; elseif exist(filename,'file') if ~any(filename==':') % i.e., not absolute path f = fullfile(pwd,filename); if exist(filename,'file') filename = f; end end else %error([filename ' file was not found']); end end % Append .FIG to filenames that do not have it specified function filename = normalizeFigFilename(filename) [fpath,fname,fext] = fileparts(filename); %#ok if isempty(fext) filename = [filename '.fig']; end end % Recursively process a FIG-format struct to get consolidated data in a flat struct function consolidatedData = processStruct(consolidatedData,data,parentTag) thisType = data.type; dataProps = data.properties; try thisTag = dataProps.Tag; catch, thisTag = thisType; end if isempty(thisTag), thisTag = 'tag'; end thisTag = strrep(thisTag,'.',''); thisTag = [parentTag thisTag '_']; if ~isempty(parentTag) thisTag = strrep(thisTag, 'figure1_', ''); end newFieldname = [thisTag 'Type']; newFieldname(64:end) = ''; consolidatedData.(newFieldname) = thisType; propNames = fieldnames(dataProps); for propIdx = 1 : length(propNames) propName = propNames{propIdx}; newFieldname = [thisTag propName]; newFieldname(64:end) = ''; consolidatedData.(newFieldname) = dataProps.(propName); end for childIdx = 1 : length(data.children) consolidatedData = processStruct(consolidatedData, data.children(childIdx), thisTag); end end % The following was taken from the ObjDiff utility (FEX #14395) % http://www.mathworks.com/matlabcentral/fileexchange/14395-objdiff-generic-object-comparator % =========================================================================================== %% objdiff - compares two objects & returns an object of the same type with just the different fields/values function [objectC,IA,IB] = objdiff(objectA,objectB,varargin) % OBJDIFF compares two objects & returns an object of the same type with just the different fields/values. % % OBJDIFF (unlike Matlab's SETDIFF or SETXOR) also compares structs, GUI % handles, ActiveX, Matlab & Java objects, in addition to arrays & cells. % OBJDIFF also allows comparison of numeric cell arrays, unlike SETDIFF/ % SETXOR. It also accepts everything that SETDIFF/SETXOR accept. % % Syntax: [objectC,IA,IB] = objdiff (objectA, objectB, options, ...) % % Inputs: % objectA - first object to compare % objectB - second object to compare. Field order in opaque objects does not matter. % Note: If objectB is not supplied, then objectA(1) is compared to objectA(2) % options - optional flags as follows: % 'rows' - see documentation for SETXOR % 'dontIgnoreJava' - show different instances of the same java class (default=ignore them) % % Outputs: % objectC - object containing only the different (or new) fields, in a {old, new} pattern % IA,IB - index vector into objectA,objectB such that objectC = [objectA(IA),objectB(IB)] (see SETXOR) % % Examples: % >> objectA = struct('a',3, 'b',5, 'd',9); % >> objectB = struct('a','ert', 'c',struct('t',pi), 'd',9); % >> objectC = objdiff(objectA, objectB) % a=different, b=new in objectA, c=new in objectB, d=same % objectC = % a: {[3] 'ert'} % b: {[5] {}} % c: {{} [1x1 struct]} % % >> objectC = objdiff(java.awt.Color.red, java.awt.Color.blue) % objectC = % Blue: {[0] [255]} % RGB: {[-65536] [-16776961]} % Red: {[255] [0]} % % >> objectC = objdiff(0,gcf) % 0 is the root handle % objectC = % children: {[2x1 struct] []} % handle: {[0] [1]} % properties: {[1x1 struct] [1x1 struct]} % type: {'root' 'figure'} % % >> [objectC,IA,IB] = objdiff({2,3,4,7}, {2,4,5}) % objectC = % 3 5 7 % IA = % 2 4 % IB = % 3 % % Bugs and suggestions: % Please send to Yair Altman (altmany at gmail dot com) % % Change log: % 2007-07-27: Fixed handling of identical objects per D. Gamble % 2007-03-23: First version posted on MathWorks File Exchange % % See also: % SETDIFF, SETXOR, ISSTRUCT, ISJAVA, ISHGHANDLE, ISOBJECT, ISCELL % Programmed by Yair M. Altman: altmany(at)gmail.com % $Revision: 1.2 $ $Date: 2007/07/26 23:41:20 $ % Process input args if (nargin<1) %|| ~isstruct(objectA) || ~isstruct(objectB) help objdiff error('YMA:OBJDIFF:NotEnoughInputs', 'Not enough input arguments'); elseif (nargin<2) || (nargin==2 && ~strcmp(class(objectA),class(objectB))) if numel(objectA) < 2 error('YMA:OBJDIFF:NotEnoughInputs', 'Not enough input arguments'); elseif numel(objectA) > 2 warning('YMA:OBJDIFF:TooManyInputs', 'Too many elements in objectA - only comparing first 2'); end objectB = objectA(2); objectA = objectA(1); varargin = {objectB, varargin{:}}; elseif ~strcmp(class(objectA),class(objectB)) error('YMA:OBJDIFF:DissimilarObjects', 'Input objects must be of the same type'); end % Process optional options ignoreJavaObjectsFlag = true; if ~isempty(varargin) ignoreJavaIdx = strmatch('dontignorejava',lower(varargin{:})); if ~isempty(ignoreJavaIdx) ignoreJavaObjectsFlag = false; varargin(ignoreJavaIdx) = []; end end % TODO: check for array of java/struct objs % Convert opaque objects to structs with the relevant property fields if ishghandle(objectA) objectA = handle2struct(objectA); objectB = handle2struct(objectB); else%if isjava(object(A)) try % This should work for any opaque object: Java, ActiveX & Matlab objectA = get(objectA); objectB = get(objectB); catch % never mind - try to process as-is end end % Enable comparison of numeric cell arrays objectA = decell(objectA); objectB = decell(objectB); % Process based on object type if isstruct(objectA) % Structs - loop over all fields [objectC, IA, IB] = compareStructs(objectA, objectB, ignoreJavaObjectsFlag); else % Cells and arrays - process with the regular setdiff function [objectC, IA, IB] = setxor(objectA, objectB, varargin{:}); end end % Compare two structs function [objectC,IA,IB] = compareStructs(objectA,objectB,ignoreJavaObjectsFlag) % Ensure singleton objects are compared objectA = getSingleton(objectA); objectB = getSingleton(objectB); objectC = struct(); % Get all the fieldnames fieldsA = fieldnames(objectA); fieldsB = fieldnames(objectB); allFields = union(fieldsA, fieldsB); % Loop over all fields and compare the objects for fieldIdx = 1 : length(allFields) fieldName = allFields{fieldIdx}; if ~isfield(objectA,fieldName) objectC.(fieldName) = {{}, objectB.(fieldName)}; elseif ~isfield(objectB,fieldName) objectC.(fieldName) = {objectA.(fieldName), {}}; elseif isa(objectA.(fieldName),'function_handle') && isa(objectB.(fieldName),'function_handle') funcA = char(objectA.(fieldName)); funcB = char(objectB.(fieldName)); if ~strcmp(funcA,funcB) objectC.(fieldName) = {funcA,funcB}; end elseif ~isequalwithequalnans(objectA.(fieldName), objectB.(fieldName)) if ignoreJavaObjectsFlag && isjava(objectA.(fieldName)) && isjava(objectB.(fieldName)) && ... isequalwithequalnans(objectA.(fieldName).getClass, objectB.(fieldName).getClass) continue; elseif isempty(objectA.(fieldName)) && isempty(objectB.(fieldName)) % e.g., [] vs. {} continue; % treat as being the same end objectC.(fieldName) = {objectA.(fieldName), objectB.(fieldName)}; end end % Check for empty diff struct (identical input objects) if isempty(fieldnames(objectC)) objectC = struct([]); end % no use for IA,IB... IA = []; IB = []; end % De-cell-ize a numeric cell-array function obj = decell(obj) if iscell(obj) && ~iscellstr(obj) obj = cell2mat(obj); end end % Ensure singleton object function obj = getSingleton(obj) if numel(obj) > 1 warning('YMA:OBJDIFF:TooManyElements', 'Too many elements in %s - only comparing the first', inputname(1)); obj = obj(1); end end