function ED = edl_readexpdefs(filename)
%EDL_READEXPDEFS Reads in an experiment definition from XML file
%
% $ Syntax $
% - ED = edl_readexpdefs(filename)
%
% $ Arguments $
% - filename: the filename of the experiment definition XML
% - ED: the read experiment definition struct
%
% $ Description $
% - ED = edl_readexpdefs(filename) reads in an experiment definition
% struct from an XML file. The format of XML can be referred to
% the edl.spec.txt. The struct ED has following fields:
%
% - name: the experiment definition name
% - selfpath: the absolute path of the experiment definition self
% - envconf: the filename of environment configuration
% - env: the environment configuration struct
% - scriptdir: the script directory
% - reportdir: the report directory
% - mfiledir: the m-files directory
% - logfile: the path of log file
% - logger: the logger
%
% - variables: a struct of all variables,
% using variable names as field names
% using variable values as field values
% - scripts: the struct of scripts
% - using name as fieldnames, each field is a struct with
% - func: the scripting function
% - path: the path of the script file
% - ctrlpath: the path of the control file
% - params: the other parameters
% - refs: the struct array of referenced scripts
% using role as field name
% using script name as field value
% - refreps: the struct array of referenced reports
% using role as field name
% using report name as field value
% - refpaths: the struct array of referenced paths
% using role as field name
% using xml path of (script or report) as field value
% - refscopes: the struct array of scope names of references
% (scripts | reports)
% using role as field name
% using scope names as field values
% - experiments: the struct of experiments
% - using name as fieldnames, each field is a struct with
% - func: the experiment function
% - script: the script name
% - scriptpath: the script filepath
% - ctrlpath: the control filepath
% - reports: the struct array of reports
% similar to scripts, except for that there is no ctrlpath
%
% $ History $
% - Created by Dahua Lin, on Aug 12nd, 2006
%
%% Read file
xdoc = xmlread(filename);
edparent = slfilepart(filename, 'parent');
docelem = xdoc.getDocumentElement;
%% Read Header
headerfns = { ...
'name', ...
'envconf', ...
'scriptdir', ...
'reportdir', ...
'mfiledir', ...
'logfile'};
ED = cell2struct(cell(1, length(headerfns)), headerfns, 2);
headers = xml_getattribs(docelem);
ED = slparseprops(ED, headers);
if slisabspath(filename)
ED.selfpath = filename;
else
ED.selfpath = sladdpath(filename, cd());
end
if isempty(ED.envconf)
ED.env = [];
else
envpath = sladdpath(ED.envconf, edparent);
ED.env = edl_readenvvars(envpath);
end
if isempty(ED.env) || ~isfield(ED.env, 'envname')
error('edl:parseerror', ...
'The environment must has variable named %s', 'envname');
end
envname = ED.env.envname;
ED.logger = sllog('rootpath', edparent);
if ~isempty(ED.logfile)
ED.logfile = [ED.logfile, '.', envname, '.log'];
ED.logger = addfiles(ED.logger, ED.logfile);
end
%% First-Pass Parsing
% 1. Build variable table progressively
% 2. Build basic structures
% 3. Translate variables
ED.variables = [];
ED.scripts = [];
ED.experiments = [];
ED.reports = [];
ED = first_pass(ED, docelem, struct('workdir', ''));
%% Second-Pass Parsing
% 1. Extract paths from cross-references
% (a) build refpaths for scripts and reports
% (b) build scriptpath and ctrlpath for experiments
%
% 1.(a)
ED.scripts = build_refpaths(ED, ED.scripts);
ED.reports = build_refpaths(ED, ED.reports);
% 1.(b)
ED.experiments = build_exppaths(ED, ED.experiments);
%% Core function for First-Pass (recursive invoking)
function ED = first_pass(ED, xelem, groupvars)
nodeList = xelem.getChildNodes;
n = nodeList.getLength;
for i = 1 : n % enumerate all children
node = nodeList.item(i-1);
if (node.getNodeType == node.ELEMENT_NODE)
tag = char(node.getTagName);
switch tag
case 'Var'
[varname, varval] = parse_var(ED, node);
ED.variables.(varname) = varval;
case 'Script'
[scriptname, scriptstruct] = parse_script(ED, node);
scriptstruct.params = weak_update(scriptstruct.params, groupvars);
ED.scripts.(scriptname) = scriptstruct;
case 'Report'
[reportname, reportstruct] = parse_report(ED, node);
ED.reports.(reportname) = reportstruct;
case 'Experiment'
[expname, expstruct] = parse_experiment(ED, node);
ED.experiments.(expname) = expstruct;
case 'Group'
subgroupvars = parse_group(ED, groupvars, node);
ED = first_pass(ED, node, subgroupvars);
otherwise
error('edl:parseerror', ...
'Invalid element with tag name %s', tag);
end
end
end
%% Core functions for Second-Pass
function S = build_refpaths(ED, S)
fns = fieldnames(S);
n = length(fns);
for i = 1 : n
fn = fns{i};
S.(fn) = build_refpaths_fortype(ED, fn, S.(fn), 'scripts', 'refs');
S.(fn) = build_refpaths_fortype(ED, fn, S.(fn), 'reports', 'refreps');
end
function S = build_refpaths_fortype(ED, name, S, typeset, typefield)
if ~isfield(S, 'refpaths')
S.refpaths = [];
end
if ~isfield(S, 'refscopes')
S.refscopes = [];
end
curset = S.(typefield);
if isempty(curset)
return;
end
fns = fieldnames(curset);
n = length(fns);
pool = ED.(typeset);
for i = 1 : n
role = fns{i};
refname = curset.(role);
if ~isfield(pool, refname)
error('edl:parseerror', ...
'The reference with role %s (name = %s) in %s is not found in pool %s', ...
role, refname, name, typeset);
end
S.refpaths.(role) = pool.(refname).path;
S.refscopes.(role) = typeset;
end
function ES = build_exppaths(ED, ES)
if isempty(ES)
return;
end
fns = fieldnames(ES);
n = length(fns);
for i = 1 : n
fn = fns{i};
scriptname = ES.(fn).script;
if ~isfield(ED.scripts, scriptname)
error('edl:parseerror', ...
'The referred script named %s for %s is not found', ...
scriptname, fn);
end
curscript = ED.scripts.(scriptname);
ES.(fn).scriptpath = curscript.path;
ES.(fn).ctrlpath = curscript.ctrlpath;
end
%% Node Parsing functions
% variable parsing
function [vname, vval] = parse_var(ED, xelem)
vname = make_name(ED, 'variables', xelem);
vval = get_tattrib(ED, xelem, 'variables', vname, 'val');
% script parsing
function [sname, scr] = parse_script(ED, xelem)
sname = make_name(ED, 'scripts', xelem);
scr.func = get_tattrib(ED, xelem, 'scripts', sname, 'func');
scr.func = sladdpath(scr.func, ED.mfiledir);
scr.path = '';
scr.ctrlpath = '';
scr = update_fields(ED, scr, xelem, {'path', 'ctrlpath'});
scr.params = get_tattribs(ED, xelem, ...
'exclude', {'name', 'func', 'path', 'ctrlpath'});
if isempty(scr.path)
scr.path = [sname, '.script.xml'];
end
if isempty(scr.ctrlpath)
scrfiletitle = slfilepart(scr.path, 'title');
scr.ctrlpath = slchangefilepart(scr.path, 'title', [scrfiletitle, '.control']);
end
scr.path = sladdpath(scr.path, ED.scriptdir);
scr = build_reftables(ED, scr, xelem);
% report parsing
function [rname, rep] = parse_report(ED, xelem)
rname = make_name(ED, 'reports', xelem);
rep.func = get_tattrib(ED, xelem, 'reports', rname, 'func');
rep.func = sladdpath(rep.func, ED.mfiledir);
rep.path = '';
rep = update_fields(ED, rep, xelem, {'path'});
rep.params = get_tattribs(ED, xelem, ...
'exclude', {'name', 'func', 'path', 'ctrlpath'});
if isempty(rep.path)
rep.path = [rname, '.report.xml'];
end
rep.path = sladdpath(rep.path, ED.reportdir);
rep = build_reftables(ED, rep, xelem);
% experiment parsing
function [expname, es] = parse_experiment(ED, xelem)
expname = make_name(ED, 'experiments', xelem);
es.func = get_tattrib(ED, xelem, 'experiments', expname, 'func');
es.func = sladdpath(es.func, ED.mfiledir);
es.script = '';
es = update_fields(ED, es, xelem, {'script'});
if isempty(es.script)
es.script = expname;
end
% group parsing
function newvars = parse_group(ED, gvars, xelem)
newvars = get_tattribs(ED, xelem, 'exclude', {'title'});
if ~isfield(newvars, 'workdir') || isempty(newvars.workdir)
newvars.workdir = gvars.workdir;
else
newvars.workdir = sladdpath(newvars.workdir, gvars.workdir);
end
newvars = weak_update(newvars, gvars);
%% Auxiliary functions
function val = translate_attribval(ED, attrval)
val = attrval;
if length(val) > 1 && val(1) == '$'
varname = val(2:end);
if ~isfield(ED.variables, varname)
error('edl:parseerror', ...
'The variable %s is not found', varname);
end
val = ED.variables.(varname);
end
function aval = get_tattrib(ED, xelem, scope, elemname, attrname)
if xelem.hasAttribute(attrname)
aval = char(xelem.getAttribute(attrname));
aval = translate_attribval(ED, aval);
else
error('edl:parseerror', ...
'The attribute %s is not found in %s of %s', attrname, elemname, scope);
end
function A = get_tattribs(ED, xelem, varargin)
A = xml_getattribs(xelem, varargin{:});
if ~isempty(A)
fns = fieldnames(A);
n = length(fns);
for i = 1 : n
fn = fns{i};
A.(fn) = translate_attribval(ED, A.(fn));
end
end
function name = make_name(ED, scope, xelem)
if xelem.hasAttribute('name')
name = char(xelem.getAttribute('name'));
if isfield(ED.(scope), name)
error('edl:parseerror', ...
'Redefinition of %s in %s', name, scope);
end
else
error('edl:parseerror', ...
'Encounter an element without name in %s', scope);
end
function S = update_fields(ED, S, xelem, fns)
S = slparseprops(S, get_tattribs(ED, xelem, ...
'select', fns, ...
'forceexist', false));
function S = weak_update(S, Scomp)
fns = fieldnames(Scomp);
n = length(fns);
for i = 1 : n
fn = fns{i};
if ~isfield(S, fn)
S.(fn) = Scomp.(fn);
end
end
function S = build_reftables(ED, S, xelem)
refNodes = xelem.getElementsByTagName('Ref');
nrs = refNodes.getLength;
S.refs = [];
S.refreps = [];
for i = 1 : nrs
curnode = refNodes.item(i-1);
cur = get_tattribs(ED, curnode, 'select', {'role', 'name'});
S.refs.(cur.role) = cur.name;
end
refrepNodes = xelem.getElementsByTagName('RefReport');
nrr = refrepNodes.getLength;
for i = 1 : nrr
curnode = refrepNodes.item(i-1);
cur = get_tattribs(ED, curnode, 'select', {'role', 'name'});
S.refreps.(cur.role) = cur.name;
end