Skip to content
This repository was archived by the owner on Nov 25, 2018. It is now read-only.

Commit 6331883

Browse files
committed
Refactor, add skeleton for extra code conversion
The XML processing code has been split into individual components and wrapped for better portability in the code. No change to functionality has been made. Framework has been added to support additional conversion parameters. Inline documentation updated with new syntax. See: #6
1 parent 8adb419 commit 6331883

File tree

1 file changed

+91
-49
lines changed

1 file changed

+91
-49
lines changed

mlapp2classdef.m

Lines changed: 91 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
function mlapp2classdef(pathToMLapp)
1+
function mlapp2classdef(pathToMLapp, varargin)
22
% MLAPP2CLASSDEF converts an App Designer GUI's class definition, packaged
33
% as a *.mlapp file, from XML to a standalone *.m class definition.
44
%
@@ -9,6 +9,12 @@ function mlapp2classdef(pathToMLapp)
99
% pathToMLapp can be a string for a single file or a cell array of strings
1010
% for multiple files. Filepaths should be absolute.
1111
%
12+
% MLAPP2CLASSDEF(..., 'ReplaceAppUI', flag) replaces App Designer UI
13+
% elements with their "regular" MATLAB equivalents (e.g. App Designer uses
14+
% UIFIGURE where MATLAB uses FIGURE). flag is a boolean value, the default
15+
% is false. To prompt the user to select an app file with this syntax, pass
16+
% an empty first argument (e.g. MLAPP2CLASSDEF([], 'ReplaceAppUI', True)).
17+
%
1218
% The class definition for an App Designer GUI is embedded in an XML file
1319
% located in a subfolder of the packaged *.mlapp file, which can be
1420
% accessed like a *.zip file. MLAPP2CLASSDEF strips the XML header & footer
@@ -25,13 +31,17 @@ function mlapp2classdef(pathToMLapp)
2531
end
2632

2733
% Choose appropriate behavior based on number of inputs
28-
if nargin == 0
34+
if nargin == 0 || ~exist('pathToMLapp', 'var') || isempty(pathToMLapp)
2935
% No input selected, prompt user to select a MATLAB app to process
3036
% Currently limited to single file selection
31-
[filename, pathname] = uigetfile('*.mlapp', 'Select MATLAB App');
37+
[filename, pathname] = uigetfile('*.mlapp', 'Select MATLAB App', 'MultiSelect', 'on');
38+
pathToMLapp = fullfile(pathname, filename);
3239
if ~filename
3340
error('mlapp2classdef:NoFileSelected', 'No file selected, exiting...');
3441
else
42+
% uigetfile's multiselect doesn't currently allow to select files
43+
% in different directories, so we can inherit the pathname(s) from
44+
% the uigetfile call.
3545
[~, appname, ext] = fileparts(filename);
3646
end
3747
else
@@ -48,63 +58,27 @@ function mlapp2classdef(pathToMLapp)
4858
filename = strcat(appname, ext);
4959
end
5060

61+
% Check varargin for optional processing flags
62+
% Placeholder logic to be replaced by inputparser instance
63+
if isempty(varargin)
64+
uielementflag = false;
65+
else
66+
uielementflag = false;
67+
end
68+
5169
if iscell(pathToMLapp)
5270
for indF = 1:numel(pathToMLapp)
5371
checkfile(pathname{indF}, filename{indF}, ext{indF});
54-
processMlapp(pathname{indF}, filename{indF}, appname{indF});
72+
processapp(pathname{indF}, filename{indF}, appname{indF}, uielementflag)
5573
% TODO: Add a counter of successfully converted files.
5674
end
5775
else
5876
checkfile(pathname, filename, ext);
59-
processMlapp(pathname, filename, appname);
77+
processapp(pathname, filename, appname, uielementflag)
6078
end
6179

6280
end
6381

64-
function processMlapp(pathname, filename, appname)
65-
% Unzip user selected MATLAB App, which are packaged in a renamed zip file
66-
tmpdir = fullfile(pathname, sprintf('%s_tmp', appname));
67-
unzip(fullfile(pathname, filename), tmpdir);
68-
69-
% Read in XML file
70-
% Since there isn't really much XML-ness to this XML file, no need to
71-
% utilize a full-fledged parser. MATLAB's won't open it anyway...
72-
xmlfile = fullfile(tmpdir, 'matlab', 'document.xml');
73-
74-
% Get a count of lines in the xml file to preallocate the cell array in
75-
% memory. If no count can be made, revert to growing the array in memory
76-
nlines = countlines(xmlfile);
77-
if ~isempty(nlines)
78-
A = cell(nlines, 1);
79-
else
80-
A = {};
81-
end
82-
83-
% Read XML file line-by-line into a cell array to make later export simpler
84-
fID = fopen(xmlfile, 'r');
85-
ii = 1;
86-
while ~feof(fID)
87-
A{ii} = fgetl(fID);
88-
ii = ii + 1;
89-
end
90-
fclose(fID);
91-
92-
% Strip out header & footer, then save to a *.m file
93-
% Limit search to first & last lines of file, currently all that is
94-
% modified by MATLAB to wrap the class definition in XML
95-
A([1,end]) = regexprep(A([1,end]), '(^.*)\[(?=classdef)|(?<=end)(\].*$)', '');
96-
97-
fID = fopen(fullfile(pathname, sprintf('%s.m', appname)), 'w');
98-
for ii = 1:length(A)
99-
fprintf(fID, '%s\n', A{ii});
100-
end
101-
fclose(fID);
102-
103-
rmdir(tmpdir, 's');
104-
105-
disp(['Successfully unpacked ' filename '!']);
106-
end
107-
10882

10983
function A = validateattributes_wrapped(A, classes, attributes)
11084
% Wrap validateattributes with try-catch block for more verbose error
@@ -152,6 +126,74 @@ function checkfile(pathname, filename, ext)
152126
end
153127

154128

129+
function processapp(pathname, filename, appname, uielementflag)
130+
tmpdir = unpackapp(pathname, filename, appname);
131+
rawXML = loadXML(tmpdir);
132+
mymcode = stripXML(rawXML);
133+
134+
if uielementflag
135+
% Convert App Designer UI elements to "regular" MATLAB UI elements
136+
end
137+
138+
writemfile(mymcode, pathname, appname);
139+
rmdir(tmpdir, 's');
140+
disp(['Successfully converted ' filename '!']);
141+
end
142+
143+
144+
function [tmpdir] = unpackapp(pathname, filename, appname)
145+
% Unzip user selected MATLAB App, which are packaged in a renamed zip file
146+
tmpdir = fullfile(pathname, sprintf('%s_tmp', appname));
147+
unzip(fullfile(pathname, filename), tmpdir);
148+
end
149+
150+
151+
function [rawXML] = loadXML(tmpdir)
152+
% Read in XML file
153+
% Since there isn't really much XML-ness to this XML file, no need to
154+
% utilize a full-fledged parser. MATLAB's won't open it anyway...
155+
xmlfile = fullfile(tmpdir, 'matlab', 'document.xml');
156+
157+
% Get a count of lines in the xml file to preallocate the cell array in
158+
% memory. If no count can be made, revert to growing the array in memory
159+
nlines = countlines(xmlfile);
160+
if ~isempty(nlines)
161+
rawXML = cell(nlines, 1);
162+
else
163+
rawXML = {};
164+
end
165+
166+
% Read XML file line-by-line into a cell array to make later export simpler
167+
fID = fopen(xmlfile, 'r');
168+
ii = 1;
169+
while ~feof(fID)
170+
rawXML{ii} = fgetl(fID);
171+
ii = ii + 1;
172+
end
173+
fclose(fID);
174+
end
175+
176+
177+
function [mymcode] = stripXML(rawXML)
178+
% Strip out XML header & footer
179+
% Limit search to first & last lines of file, currently all that is
180+
% modified by MATLAB to wrap the class definition in XML
181+
mymcode = rawXML;
182+
mymcode([1,end]) = regexprep(mymcode([1,end]), '(^.*)\[(?=classdef)|(?<=end)(\].*$)', '');
183+
end
184+
185+
186+
function writemfile(mymcode, pathname, appname)
187+
% Write a cell array of strings to a *.m file.
188+
% Assumes each cell is a separate line.
189+
fID = fopen(fullfile(pathname, sprintf('%s.m', appname)), 'w');
190+
for ii = 1:length(mymcode)
191+
fprintf(fID, '%s\n', mymcode{ii});
192+
end
193+
fclose(fID);
194+
end
195+
196+
155197
function nlines = countlines(filepath)
156198
% Count the number of lines present in the specified file.
157199
% filepath should be an absolute path

0 commit comments

Comments
 (0)