Auswertung Experiment

Database tüddelei
DBHanlder läuft gut für DIESE Datanbank struktur...
App begonnen aber weit entfernt von gutem Stand
This commit is contained in:
sioe
2024-11-15 16:51:37 +01:00
parent 553ed19b9f
commit 397cfa61dd
219 changed files with 584 additions and 854 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,87 @@
function formatWhitespace(filename)
% FORMATWHITESPACE Formats whitespace and indentation of a document
%
% FORMATWHITESPACE(FILENAME)
% Indents currently active document if FILENAME is empty or not
% specified. FILENAME must be the name of an open document in the
% editor.
%
% Rules:
% - Smart-indent with all function indent option
% - Indentation is 4 spaces
% - Remove whitespace in empty lines
% - Preserve indentantion after line continuations, i.e. ...
%
import matlab.desktop.editor.*
if nargin < 1, filename = ''; end
d = getDoc(filename);
oldLines = textToLines(d.Text);
% Smart indent as AllFunctionIndent
% Using undocumented feature from http://undocumentedmatlab.com/blog/changing-system-preferences-programmatically
editorProp = 'EditorMFunctionIndentType';
oldVal = com.mathworks.services.Prefs.getStringPref(editorProp);
com.mathworks.services.Prefs.setStringPref(editorProp, 'AllFunctionIndent');
restoreSettings = onCleanup(@() com.mathworks.services.Prefs.setStringPref(editorProp, oldVal));
d.smartIndentContents()
% Preserve crafted continuations of line
lines = textToLines(d.Text);
iContinuation = ~cellfun('isempty',strfind(lines, '...'));
iComment = ~cellfun('isempty',regexp(lines, '^ *%([^%]|$)','once'));
pAfterDots = find(iContinuation & ~iComment)+1;
for ii = 1:numel(pAfterDots)
% Carry over the change in space due to smart-indenting from the
% first continuation line to the last
p = pAfterDots(ii);
nWhiteBefore = find(~isspace(oldLines{p-1}),1,'first');
nWhiteAfter = find(~isspace(lines{p-1}),1,'first');
df = nWhiteAfter - nWhiteBefore;
if df > 0
lines{p} = [blanks(df) oldLines{p}];
elseif df < 0
df = min(abs(df)+1, find(~isspace(oldLines{p}),1,'first'));
lines{p} = oldLines{p}(df:end);
else
lines{p} = oldLines{p};
end
end
% Remove whitespace lines
idx = cellfun('isempty',regexp(lines, '[^ \t\n]','once'));
lines(idx) = {''};
d.Text = linesToText(lines);
end
function d = getDoc(filename)
import matlab.desktop.editor.*
if ~ischar(filename)
error('formatWhitespace:charFilename','The FILENAME should be a char.')
end
try
isEditorAvailable();
catch
error('formatWhitespace:noEditorApi','Check that the Editor API is available.')
end
if isempty(filename)
d = getActive();
else
% TODO: open file if it isn't open in the editor already
d = findOpenDocument(filename);
try
[~,filenameFound] = fileparts(d.Filename);
catch
filenameFound = '';
end
isExactMatch = strcmp(filename, filenameFound);
if ~isExactMatch
error('formatWhitespace:filenameNotFound','Filename "%s" not found in the editor.', filename)
end
end
end

View File

@@ -0,0 +1,123 @@
function figure2dot(filename, varargin)
%FIGURE2DOT Save figure in Graphviz (.dot) file.
% FIGURE2DOT(filename) saves the current figure as dot-file.
%
% FIGURE2DOT(filename, 'object', HGOBJECT) constructs the graph representation
% of the specified object (default: gcf)
%
% You can visualize the constructed DOT file using:
% - [GraphViz](http://www.graphviz.org) on your computer
% - [WebGraphViz](http://www.webgraphviz.com) online
% - [Gravizo](http://www.gravizo.com) for your markdown files
% - and a lot of other software such as OmniGraffle
%
% See also: matlab2tikz, cleanfigure, uiinspect, inspect
ipp = m2tInputParser();
ipp = ipp.addRequired(ipp, 'filename', @ischar);
ipp = ipp.addParamValue(ipp, 'object', gcf, @ishghandle);
ipp = ipp.parse(ipp, filename, varargin{:});
args = ipp.Results;
filehandle = fopen(args.filename, 'w');
finally_fclose_filehandle = onCleanup(@() fclose(filehandle));
% start printing
fprintf(filehandle, 'digraph simple_hierarchy {\n\n');
fprintf(filehandle, 'node[shape=box];\n\n');
% define the root node
node_number = 0;
p = get(args.object, 'Parent');
% define root element
type = get(p, 'Type');
fprintf(filehandle, 'N%d [label="%s"]\n\n', node_number, type);
% start recursion
plot_children(filehandle, p, node_number);
% finish off
fprintf(filehandle, '}');
% ----------------------------------------------------------------------------
function plot_children(fh, h, parent_node)
children = allchild(h);
for h = children(:)'
if shouldSkip(h), continue, end;
node_number = node_number + 1;
label = {};
label = addHGProperty(label, h, 'Type', '');
try
hClass = class(handle(h));
label = addProperty(label, 'Class', hClass);
catch
% don't do anything
end
label = addProperty(label, 'Handle', sprintf('%g', double(h)));
label = addHGProperty(label, h, 'Title', '');
label = addHGProperty(label, h, 'Axes', []);
label = addHGProperty(label, h, 'String', '');
label = addHGProperty(label, h, 'Tag', '');
label = addHGProperty(label, h, 'DisplayName', '');
label = addHGProperty(label, h, 'Visible', 'on');
label = addHGProperty(label, h, 'HandleVisibility', 'on');
% print node
fprintf(fh, 'N%d [label="%s"]\n', ...
node_number, m2tstrjoin(label, '\n'));
% connect to the child
fprintf(fh, 'N%d -> N%d;\n\n', parent_node, node_number);
% recurse
plot_children(fh, h, node_number);
end
end
end
% ==============================================================================
function bool = shouldSkip(h)
% returns TRUE for objects that can be skipped
objType = get(h, 'Type');
bool = ismember(lower(objType), guitypes());
end
% ==============================================================================
function label = addHGProperty(label, h, propName, default)
% get a HG property and assign it to a GraphViz node label
if ~exist('default','var') || isempty(default)
shouldOmit = @isempty;
elseif isa(default, 'function_handle')
shouldOmit = default;
else
shouldOmit = @(v) isequal(v,default);
end
if isprop(h, propName)
propValue = get(h, propName);
if numel(propValue) == 1 && ishghandle(propValue) && isprop(propValue, 'String')
% dereference Titles, labels, ...
propValue = get(propValue, 'String');
elseif ishghandle(propValue)
% dereference other HG objects to their raw handle value (double)
propValue = double(propValue);
elseif iscell(propValue)
propValue = ['{' m2tstrjoin(propValue,',') '}'];
end
if ~shouldOmit(propValue)
label = addProperty(label, propName, propValue);
end
end
end
function label = addProperty(label, propName, propValue)
% add a property to a GraphViz node label
if isnumeric(propValue)
propValue = num2str(propValue);
elseif iscell(propValue)
propValue = m2tstrjoin(propValue,sprintf('\n'));
end
label = [label, sprintf('%s: %s', propName, propValue)];
end
% ==============================================================================

View File

@@ -0,0 +1,231 @@
function parser = m2tInputParser()
%MATLAB2TIKZINPUTPARSER Input parsing for matlab2tikz.
% This implementation exists because Octave is lacking one.
% Initialize the structure.
parser = struct ();
% Public Properties
parser.Results = {};
% Enabel/disable parameters case sensitivity.
parser.CaseSensitive = false;
% Keep parameters not defined by the constructor.
parser.KeepUnmatched = false;
% Enable/disable warning for parameters not defined by the constructor.
parser.WarnUnmatched = true;
% Enable/disable passing arguments in a structure.
parser.StructExpand = true;
% Names of parameters defined in input parser constructor.
parser.Parameters = {};
% Names of parameters not defined in the constructor.
parser.Unmatched = struct ();
% Names of parameters using default values.
parser.UsingDefaults = {};
% Names of deprecated parameters and their alternatives
parser.DeprecatedParameters = struct();
% Handles for functions that act on the object.
parser.addRequired = @addRequired;
parser.addOptional = @addOptional;
parser.addParamValue = @addParamValue;
parser.deprecateParam = @deprecateParam;
parser.parse = @parse;
% Initialize the parser plan
parser.plan = {};
end
% =========================================================================
function p = parser_plan (q, arg_type, name, default, validator)
p = q;
plan = p.plan;
if (isempty (plan))
plan = struct ();
n = 1;
else
n = numel (plan) + 1;
end
plan(n).type = arg_type;
plan(n).name = name;
plan(n).default = default;
plan(n).validator = validator;
p.plan = plan;
end
% =========================================================================
function p = addRequired (p, name, validator)
p = parser_plan (p, 'required', name, [], validator);
end
% =========================================================================
function p = addOptional (p, name, default, validator)
p = parser_plan (p, 'optional', name, default, validator);
end
% =========================================================================
function p = addParamValue (p, name, default, validator)
p = parser_plan (p, 'paramvalue', name, default, validator);
end
% =========================================================================
function p = deprecateParam (p, name, alternatives)
if isempty(alternatives)
alternatives = {};
elseif ischar(alternatives)
alternatives = {alternatives}; % make cellstr
elseif ~iscellstr(alternatives)
error('m2tInputParser:BadAlternatives',...
'Alternatives for a deprecated parameter must be a char or cellstr');
end
p.DeprecatedParameters.(name) = alternatives;
end
% =========================================================================
function p = parse (p, varargin)
plan = p.plan;
results = p.Results;
using_defaults = {};
if (p.CaseSensitive)
name_cmp = @strcmp;
else
name_cmp = @strcmpi;
end
if (p.StructExpand)
k = find (cellfun (@isstruct, varargin));
for m = numel(k):-1:1
n = k(m);
s = varargin{n};
c = [fieldnames(s).'; struct2cell(s).'];
c = c(:).';
if (n > 1 && n < numel (varargin))
varargin = horzcat (varargin(1:n-1), c, varargin(n+1:end));
elseif (numel (varargin) == 1)
varargin = c;
elseif (n == 1);
varargin = horzcat (c, varargin(n+1:end));
else % n == numel (varargin)
varargin = horzcat (varargin(1:n-1), c);
end
end
end
if (isempty (results))
results = struct ();
end
type = {plan.type};
n = find( strcmp( type, 'paramvalue' ) );
m = setdiff (1:numel( plan ), n );
plan = plan ([n,m]);
for n = 1 : numel (plan)
found = false;
results.(plan(n).name) = plan(n).default;
if (~ isempty (varargin))
switch plan(n).type
case 'required'
found = true;
if (strcmpi (varargin{1}, plan(n).name))
varargin(1) = [];
end
value = varargin{1};
varargin(1) = [];
case 'optional'
m = find (cellfun (@ischar, varargin));
k = find (name_cmp (plan(n).name, varargin(m)));
if (isempty (k) && validate_arg (plan(n).validator, varargin{1}))
found = true;
value = varargin{1};
varargin(1) = [];
elseif (~ isempty (k))
m = m(k);
found = true;
value = varargin{max(m)+1};
varargin(union(m,m+1)) = [];
end
case 'paramvalue'
m = find( cellfun (@ischar, varargin) );
k = find (name_cmp (plan(n).name, varargin(m)));
if (~ isempty (k))
found = true;
m = m(k);
value = varargin{max(m)+1};
varargin(union(m,m+1)) = [];
end
otherwise
error( sprintf ('%s:parse', mfilename), ...
'parse (%s): Invalid argument type.', mfilename ...
)
end
end
if (found)
if (validate_arg (plan(n).validator, value))
results.(plan(n).name) = value;
else
error( sprintf ('%s:invalidinput', mfilename), ...
'%s: Input argument ''%s'' has invalid value.\n', mfilename, plan(n).name ...
);
end
p.Parameters = union (p.Parameters, {plan(n).name});
elseif (strcmp (plan(n).type, 'required'))
error( sprintf ('%s:missinginput', mfilename), ...
'%s: input ''%s'' is missing.\n', mfilename, plan(n).name ...
);
else
using_defaults = union (using_defaults, {plan(n).name});
end
end
if ~isempty(varargin)
% Include properties that do not match specified properties
for n = 1:2:numel(varargin)
if ischar(varargin{n})
if p.KeepUnmatched
results.(varargin{n}) = varargin{n+1};
end
if p.WarnUnmatched
warning(sprintf('%s:unmatchedArgument',mfilename), ...
'Ignoring unknown argument "%s"', varargin{n});
end
p.Unmatched.(varargin{n}) = varargin{n+1};
else
error (sprintf ('%s:invalidinput', mfilename), ...
'%s: invalid input', mfilename)
end
end
end
% Store the results of the parsing
p.Results = results;
p.UsingDefaults = using_defaults;
warnForDeprecatedParameters(p);
end
% =========================================================================
function result = validate_arg (validator, arg)
try
result = validator (arg);
catch %#ok
result = false;
end
end
% =========================================================================
function warnForDeprecatedParameters(p)
usedDeprecatedParameters = intersect(p.Parameters, fieldnames(p.DeprecatedParameters));
for iParam = 1:numel(usedDeprecatedParameters)
oldParameter = usedDeprecatedParameters{iParam};
alternatives = p.DeprecatedParameters.(oldParameter);
switch numel(alternatives)
case 0
replacements = '';
case 1
replacements = ['''' alternatives{1} ''''];
otherwise
replacements = deblank(sprintf('''%s'' and ',alternatives{:}));
replacements = regexprep(replacements,' and$','');
end
if ~isempty(replacements)
replacements = sprintf('From now on, please use %s to control the output.\n',replacements);
end
message = ['\n===============================================================================\n', ...
'You are using the deprecated parameter ''%s''.\n', ...
'%s', ...
'==============================================================================='];
warning('matlab2tikz:deprecatedParameter', ...
message, oldParameter, replacements);
end
end

View File

@@ -0,0 +1,216 @@
function [value] = m2tcustom(handle, varargin)
% M2TCUSTOM creates user-defined options for matlab2tikz output
%
% M2TCUSTOM can be used to create, get and set a properly formatted data
% structure to customize the conversion of MATLAB figures to TikZ using
% matlab2tikz. In particular, it allows to:
%
% * add blocks of LaTeX/TikZ code around any HG object,
% * add blocks of comments around any HG object,
% * add TikZ options to any HG object,
% * add LaTeX/TikZ code inside some HG objects (e.g. axes),
% * provide a custom handler to convert a particular HG object.
%
% Note that this provides advanced functionality. Only very basic
% sanity checks are performed such that injudicious use may produce
% broken TikZ figures!
%
% It is HIGHLY recommended that you are comfortable with:
%
% * writing pgfplots, TikZ and LaTeX code,
% * using the Handle Graphics (HG) framework in MATLAB/Octave, and
% * the inner working of matlab2tikz (for custom handlers)
%
% when you use this function. I.e. you should know what you are doing.
%
%
% Usage as a GETTER:
% ------------------
%
% value = M2TCUSTOM(handle)
% retrieves the current custom data structure from the HG object "handle"
%
%
% Usage as a SETTER:
% ------------------
%
% M2TCUSTOM(handle, ...)
% value = M2TCUSTOM(handle, ...)
% will construct the proper data structure and try to set it to the object
% |handle| if possible. The arguments (see below) are specified in
% key-value pairs akin to a normal |struct|, but here we do a few checks
% and data normalization.
%
% If we denote BLOCK to mean either a |char| or a |cellstr|, the following
% options can be passed. Different entries in a cellstr are assumed
% separated by a newline. The default values are empty.
%
% M2TCUSTOM(handle, 'commentBefore', BLOCK, ...)
% M2TCUSTOM(handle, 'commentAfter' , BLOCK, ...)
% to add comments before/after the object. Our code translates newlines
% and adds the percentage signs for you.
%
% M2TCUSTOM(handle, 'codeBefore', BLOCK, ...)
% M2TCUSTOM(handle, 'codeAfter', BLOCK, ...)
% M2TCUSTOM(handle, 'codeInsideFirst', BLOCK, ...)
% M2TCUSTOM(handle, 'codeInsideLast', BLOCK, ...)
% to add raw LaTeX/TikZ code respectively before, after, as first thing
% inside or as last thing inside the pgfplots representation of the object.
% Note that for some HG objects, (e.g. line objects), |codeInsideFirst|
% and |codeInsideLast| do not make any sense and are hence ignored.
%
% M2TCUSTOM(handle, 'extraOptions', OPTIONS, ...)
% adds extra pgfplots/TikZ options to the end of the option list. Here,
% OPTIONS is properly formatted TikZ code in
% - a |char| (e.g. 'color=red, line width=1pt' )
% - a |cellstr| (e.g. {'color=red','line width=1pt'})
%
% M2TCUSTOM(handle, 'customHandler', FUNCTION_HANDLE, ...)
% allows you to replace the default matlab2tikz handler for this object.
% This is not for the faint of heart and requires intimate knowledge of
% the matlab2tikz code base! We expect a function (either as |char| or
% function handle) that will be called as
%
% [m2t, str] = feval(handler, m2t, handle, custom)
%
% such that the expected function signature is:
%
% function [m2t, str] = handler(m2t, handle, custom)
%
% where |m2t| is an undocumented/unstable data structure,
% |str| is a char containing TikZ code representing the
% HG object |handle| as generated by your handler,
% |custom| is a structure as returned by |m2tcustom|
% from which you only need to handle |extraOptions|,
% |codeInsideFirst| and |codeInsideLast| when applicable.
% A particularly useful value for |customHandler| is 'drawNothing',
% which remove the object from the output.
%
%
% Example:
% --------
%
% Executing the following MATLAB code fragment:
%
% figure;
% plot(1:10);
% EOL = sprintf('\n');
% m2tCustom(gca, 'codeBefore' , ['<codeBefore>' EOL] , ...
% 'codeAfter' , ['<codeAfter>' EOL] , ...
% 'commentsBefore' , '<commentsBefore>' , ...
% 'commentsAfter' , '<commentsAfter>' , ...
% 'codeInsideFirst' , ['<codeInsideFirst>' EOL], ...
% 'codeInsideLast' , ['<codeInsideLast>' EOL], ...
% 'extraOptions' , '<extraOptions>');
%
% matlab2tikz('test.tikz')
%
% Should result in a |test.tikz| file with contents that look somewhat
% like this:
%
% \begin{tikzpicture}
% %<commentsBefore>
% <codeBefore>
% \begin{axis}[..., <extraOptions>]
% <codeInsideFirst>
% \addplot{...};
% <codeInsideLast>
% \end{axis}
% %<commentsAfter>
% <codeAfter>
% \end{tikzpicture}
%
% See also: matlab2tikz, setappdata, getappdata
%% arguments specific to this constructor function
ipp = m2tInputParser();
ipp = ipp.addRequired(ipp, 'handle', @isHgObject);
%% Declaration of the custom data structure
ipp = ipp.addParamValue(ipp, 'codeBefore', '', @isCellstrOrChar);
ipp = ipp.addParamValue(ipp, 'codeAfter', '', @isCellstrOrChar);
ipp = ipp.addParamValue(ipp, 'commentsBefore', '', @isCellstrOrChar);
ipp = ipp.addParamValue(ipp, 'commentsAfter', '', @isCellstrOrChar);
ipp = ipp.addParamValue(ipp, 'codeInsideFirst', '', @isCellstrOrChar);
ipp = ipp.addParamValue(ipp, 'codeInsideLast', '', @isCellstrOrChar);
ipp = ipp.addParamValue(ipp, 'extraOptions', '', @isCellstrOrChar);
ipp = ipp.addParamValue(ipp, 'customHandler', '', @isHandler);
%% Parse the arguments
ipp = ipp.parse(ipp, handle, varargin{:});
%% Construct custom data structure
% We leverage the results from the input parser. It provides us
% with validation already. We just need to remove bookkeeping fields
value = ipp.Results;
value = rmfield(value, {'handle'});
%% Normalize the actual values
value.codeBefore = cellstr2char(value.codeBefore);
value.codeAfter = cellstr2char(value.codeAfter);
value.codeInsideFirst = cellstr2char(value.codeInsideFirst);
value.codeInsideLast = cellstr2char(value.codeInsideLast);
value.commentsBefore = cellstr2char(value.commentsBefore);
value.commentsAfter = cellstr2char(value.commentsAfter);
if isempty(value.customHandler)
value = rmfield(value, 'customHandler');
end
% extraOptions gets normalized by |opts_append_userdefined|
%% Different Usage modes
MATLAB2TIKZ = 'matlab2tikz'; % key used for application data storage
if numel(varargin) == 0
%% GETTER MODE
% syntax: value = m2tcustom(h);
if ~isempty(handle)
object = getappdata(handle, MATLAB2TIKZ);
else
object = [];
end
if ~isempty(object)
value = object;
else
% |value| contains all default (empty) values
end
else
%% SETTER MODE
% syntax: m2tcustom(h , key1, val1, ...)
% syntax: value = m2tcustom([], key1, val1, ...)
if ~isempty(handle)
setappdata(handle, MATLAB2TIKZ, value);
end
end
end
% == INPUT VALIDATORS ==========================================================
function bool = isHgObject(value)
% true for HG object or empty (or numeric for backwards compatibility)
bool = isempty(value) || ishghandle(value) || isnumeric(value);
end
function bool = isCellstrOrChar(value)
% true for cellstr or char
bool = ischar(value) || iscellstr(value);
end
function bool = isHandler(value)
% true for char or function handle of the form [m2t, str] = f(m2t, h, opts)
bool = isempty(value) || ischar(value) || ...
(isa(value, 'function_handle') && ...
atLeastOrUnknown(nargin(value), 3) && ...
atLeastOrUnknown(nargout(value), 2));
end
function bool = atLeastOrUnknown(nargs, limit)
% checks for |nargin| and |nargout| >= |limit| (or equal to -1)
UNKNOWN = -1;
bool = (nargs == UNKNOWN) || nargs >= limit;
end
% == FIELD NORMALIZATION =======================================================
function value = cellstr2char(value)
% convert cellstr to char (and keep char unaffected)
if iscellstr(value)
EOL = sprintf('\n');
value = m2tstrjoin(value, EOL);
end
end
% ==============================================================================

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
function errorUnknownEnvironment()
% Throw an error to indicate an unknwon environment (i.e. not MATLAB/Octave).
error('matlab2tikz:unknownEnvironment',...
'Unknown environment "%s". Need MATLAB(R) or Octave.', getEnvironment);
end

View File

@@ -0,0 +1,25 @@
function [env, versionString] = getEnvironment()
% Determine environment (Octave, MATLAB) and version string
% TODO: Unify private `getEnvironment` functions
persistent cache
if isempty(cache)
isOctave = exist('OCTAVE_VERSION', 'builtin') ~= 0;
if isOctave
env = 'Octave';
versionString = OCTAVE_VERSION;
else
env = 'MATLAB';
vData = ver(env);
versionString = vData.Version;
end
% store in cache
cache.env = env;
cache.versionString = versionString;
else
env = cache.env;
versionString = cache.versionString;
end
end

View File

@@ -0,0 +1,19 @@
function types = guitypes()
% GUITYPES returns a cell array of MATLAB/Octave GUI object types
%
% Syntax
% types = guitypes()
%
% These types are ignored by matlab2tikz and figure2dot.
%
% See also: matlab2tikz, figure2dot
types = {'uitoolbar', 'uimenu', 'uicontextmenu', 'uitoggletool',...
'uitogglesplittool', 'uipushtool', 'hgjavacomponent',...
'matlab.graphics.shape.internal.Button', ...
'matlab.ui.controls.ToolbarPushButton', ...
'matlab.ui.controls.ToolbarStateButton', ...
'matlab.ui.controls.ToolbarDropdown', ...
'matlab.ui.controls.AxesToolbar', ...
};
end

View File

@@ -0,0 +1,5 @@
function bool = isAxis3D(axisHandle)
% Check if elevation is not orthogonal to xy plane
axisView = get(axisHandle,'view');
bool = ~ismember(axisView(2),[90,-90]);
end

View File

@@ -0,0 +1,13 @@
function isBelow = isVersionBelow(versionA, versionB)
% Checks if versionA is smaller than versionB
vA = versionArray(versionA);
vB = versionArray(versionB);
n = min(length(vA), length(vB));
deltaAB = vA(1:n) - vB(1:n);
difference = find(deltaAB, 1, 'first');
if isempty(difference)
isBelow = false; % equal versions
else
isBelow = (deltaAB(difference) < 0);
end
end

View File

@@ -0,0 +1,320 @@
function m2tUpdater(about, verbose)
%UPDATER Auto-update matlab2tikz.
% Only for internal usage.
% Copyright (c) 2012--2014, Nico Schlömer <nico.schloemer@gmail.com>
% All rights reserved.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are
% met:
%
% * Redistributions of source code must retain the above copyright
% notice, this list of conditions and the following disclaimer.
% * Redistributions in binary form must reproduce the above copyright
% notice, this list of conditions and the following disclaimer in
% the documentation and/or other materials provided with the distribution
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
% POSSIBILITY OF SUCH DAMAGE.
% =========================================================================
fileExchangeUrl = about.website;
version = about.version;
mostRecentVersion = determineLatestRelease(version, fileExchangeUrl);
if askToUpgrade(mostRecentVersion, version, verbose)
tryToUpgrade(fileExchangeUrl, verbose);
userInfo(verbose, '');
end
end
% ==============================================================================
function shouldUpgrade = askToUpgrade(mostRecentVersion, version, verbose)
shouldUpgrade = false;
if ~isempty(mostRecentVersion)
userInfo(verbose, '**********************************************\n');
userInfo(verbose, 'New version (%s) available!\n', mostRecentVersion);
userInfo(verbose, '**********************************************\n');
warnAboutUpgradeImplications(version, mostRecentVersion, verbose);
askToShowChangelog(version);
reply = input(' *** Would you like to upgrade? y/n [n]:','s');
shouldUpgrade = ~isempty(reply) && strcmpi(reply(1),'y');
if ~shouldUpgrade
userInfo(verbose, ['\nTo disable the self-updater in the future, add ' ...
'"''checkForUpdates'',false" to the parameters.\n'] );
end
end
end
% ==============================================================================
function tryToUpgrade(fileExchangeUrl, verbose)
% Download the files and unzip its contents into two folders
% above the folder that contains the current script.
% This assumes that the file structure is something like
%
% src/matlab2tikz.m
% src/[...]
% src/private/m2tUpdater
% src/private/[...]
% AUTHORS
% ChangeLog
% [...]
%
% on the hard drive and the zip file. In particular, this assumes
% that the folder on the hard drive is writable by the user
% and that matlab2tikz.m is not symlinked from some other place.
pathstr = fileparts(mfilename('fullpath'));
targetPath = fullfile(pathstr, '..', '..');
% Let the user know where the .zip is downloaded to
userInfo(verbose, 'Downloading and unzipping to ''%s'' ...', targetPath);
% Try upgrading
try
% List current folder structure. Will use last for cleanup
currentFolderFiles = rdirfiles(targetPath);
% The FEX now forwards the download request to Github.
% Go through the forwarding to update the download count and
% unzip
html = urlread([fileExchangeUrl, '?download=true']);
expression = '(?<=\<a href=")[\w\-\/:\.]+(?=">redirected)';
url = regexp(html, expression,'match','once');
unzippedFiles = unzip(url, targetPath);
% The folder structure is additionally packed into the
% 'MATLAB Search Path' folder defined in FEX. Retrieve the
% top folder name
tmp = strrep(unzippedFiles,[targetPath, filesep],'');
tmp = regexp(tmp, filesep,'split','once');
tmp = cat(1,tmp{:});
topZipFolder = unique(tmp(:,1));
% If packed into the top folder, overwrite files into m2t
% main directory
if numel(topZipFolder) == 1
unzippedFilesTarget = fullfile(targetPath, tmp(:,2));
for ii = 1:numel(unzippedFiles)
movefile(unzippedFiles{ii}, unzippedFilesTarget{ii})
end
% Add topZipFolder to current folder structure
currentFolderFiles = [currentFolderFiles; fullfile(targetPath, topZipFolder{1})];
end
cleanupOldFiles(currentFolderFiles, unzippedFilesTarget);
userInfo(verbose, 'Upgrade has completed successfully.');
catch
err = lasterror(); %#ok needed for Octave
userInfo(verbose, ...
['Upgrade has failed with error message "%s".\n', ...
'Please install the latest version manually from %s !'], ...
err.message, fileExchangeUrl);
end
end
% ==============================================================================
function cleanupOldFiles(currentFolderFiles, unzippedFilesTarget)
% Delete files that were there in the old folder, but that are no longer
% present in the new release.
newFolderStructure = [getFolders(unzippedFilesTarget); unzippedFilesTarget];
deleteFolderFiles = setdiff(currentFolderFiles, newFolderStructure);
for ii = 1:numel(deleteFolderFiles)
x = deleteFolderFiles{ii};
if exist(x, 'dir') == 7
% First check for directories since
% `exist(x, 'file')` also checks for directories!
rmdir(x,'s');
elseif exist(x, 'file') == 2
delete(x);
end
end
end
% ==============================================================================
function mostRecentVersion = determineLatestRelease(version, fileExchangeUrl)
% Read in the Github releases page
url = 'https://github.com/matlab2tikz/matlab2tikz/releases/';
try
html = urlread(url);
catch %#ok
% Couldn't load the URL -- never mind.
html = '';
warning('m2tUpdate:siteNotFound', ...
['Cannot determine the latest version.\n', ...
'Either your internet is down or something went wrong.\n', ...
'You might want to check for updates by hand at %s.\n'], ...
fileExchangeUrl);
end
% Parse tag names which are the version number in the format ##.##.##
% It assumes that releases will always be tagged with the version number
expression = '(?<=matlab2tikz\/matlab2tikz\/releases\/tag\/)\d+\.\d+\.\d+';
tags = regexp(html, expression, 'match');
ntags = numel(tags);
% Keep only new releases
inew = false(ntags,1);
for ii = 1:ntags
inew(ii) = isVersionBelow(version, tags{ii});
end
nnew = nnz(inew);
% One new release
if nnew == 1
mostRecentVersion = tags{inew};
% Several new release, pick latest
elseif nnew > 1
tags = tags(inew);
tagnum = zeros(nnew,1);
for ii = 1:nnew
tagnum(ii) = [10000,100,1] * versionArray(tags{ii});
end
[~, imax] = max(tagnum);
mostRecentVersion = tags{imax};
% No new
else
mostRecentVersion = '';
end
end
% ==============================================================================
function askToShowChangelog(currentVersion)
% Asks whether the user wants to see the changelog and then shows it.
reply = input(' *** Would you like to see the changelog? y/n [y]:' ,'s');
shouldShow = isempty(reply) || ~strcmpi(reply(1),'n') ;
if shouldShow
fprintf(1, '\n%s\n', changelogUntilVersion(currentVersion));
end
end
% ==============================================================================
function changelog = changelogUntilVersion(currentVersion)
% This function retrieves the chunk of the changelog until the current version.
URL = 'https://github.com/matlab2tikz/matlab2tikz/raw/master/CHANGELOG.md';
changelog = urlread(URL);
currentVersion = versionString(currentVersion);
% Header is "# YYYY-MM-DD Version major.minor.patch [Manager](email)"
% Just match for the part until the version number. Here, we're actually
% matching a tiny bit too broad due to the periods in the version number
% but the outcome should be the same if we keep the changelog format
% identical.
pattern = ['\#\s*[\d-]+\s*Version\s*' currentVersion];
idxVersion = regexpi(changelog, pattern);
if ~isempty(idxVersion)
changelog = changelog(1:idxVersion-1);
else
% Just show the whole changelog if we don't find the old version.
end
changelog = replaceIssuesWithUrls(changelog);
end
% ==============================================================================
function changelog = replaceIssuesWithUrls(changelog)
% Replaces GitHub issues ("#...") with URLs
baseurl = 'https://github.com/matlab2tikz/matlab2tikz/issues/';
if strcmpi(getEnvironment(), 'MATLAB')
replacement = sprintf('<a href="%s$1">#$1</a>', baseurl);
changelog = regexprep(changelog, '\#(\d+)', replacement);
end
end
% ==============================================================================
function warnAboutUpgradeImplications(currentVersion, latestVersion, verbose)
% This warns the user about the implications of upgrading as dictated by
% Semantic Versioning.
switch upgradeSize(currentVersion, latestVersion);
case 'major'
% The API might have changed in a backwards incompatible way.
userInfo(verbose, 'This is a MAJOR upgrade!\n');
userInfo(verbose, ' - New features may have been introduced.');
userInfo(verbose, ' - Some old code/options may no longer work!\n');
case 'minor'
% The API may NOT have changed in a backwards incompatible way.
userInfo(verbose, 'This is a MINOR upgrade.\n');
userInfo(verbose, ' - New features may have been introduced.');
userInfo(verbose, ' - Some options may have been deprecated.');
userInfo(verbose, ' - Old code should continue to work but might produce warnings.\n');
case 'patch'
% No new functionality is introduced
userInfo(verbose, 'This is a PATCH.\n');
userInfo(verbose, ' - Only bug fixes are included in this upgrade.');
userInfo(verbose, ' - Old code should continue to work as before.')
end
userInfo(verbose, 'Please check the changelog for detailed information.\n');
userWarn(verbose, '\n!! By upgrading you will lose any custom changes !!\n');
end
% ==============================================================================
function cls = upgradeSize(currentVersion, latestVersion)
% Determines whether the upgrade is major, minor or a patch.
currentVersion = versionArray(currentVersion);
latestVersion = versionArray(latestVersion);
description = {'major', 'minor', 'patch'};
for ii = 1:numel(description)
if latestVersion(ii) > currentVersion(ii)
cls = description{ii};
return
end
end
cls = 'unknown';
end
% ==============================================================================
function userInfo(verbose, message, varargin)
% Display information (i.e. to stdout)
if verbose
userPrint(1, message, varargin{:});
end
end
function userWarn(verbose, message, varargin)
% Display warnings (i.e. to stderr)
if verbose
userPrint(2, message, varargin{:});
end
end
function userPrint(fid, message, varargin)
% Print messages (info/warnings) to a stream/file.
mess = sprintf(message, varargin{:});
% Replace '\n' by '\n *** ' and print.
mess = strrep( mess, sprintf('\n'), sprintf('\n *** ') );
fprintf(fid, ' *** %s\n', mess );
end
% =========================================================================
function list = rdirfiles(rootdir)
% Recursive files listing
s = dir(rootdir);
list = {s.name}';
% Exclude .git, .svn, . and ..
[list, idx] = setdiff(list, {'.git','.svn','.','..'});
% Add root
list = fullfile(rootdir, list);
% Loop for sub-directories
pdir = find([s(idx).isdir]);
for ii = pdir
list = [list; rdirfiles(list{ii})]; %#ok<AGROW>
end
% Drop directories
list(pdir) = [];
end
% =========================================================================
function list = getFolders(list)
% Extract the folder structure from a list of files and folders
for ii = 1:numel(list)
if exist(list{ii},'file') == 2
list{ii} = fileparts(list{ii});
end
end
list = unique(list);
end
% =========================================================================

View File

@@ -0,0 +1,35 @@
function newstr = m2tstrjoin(cellstr, delimiter, floatFormat)
% This function joins a cell of strings to a single string (with a
% given delimiter in between two strings, if desired).
if ~exist('delimiter','var') || isempty(delimiter)
delimiter = '';
end
if ~exist('floatFormat','var') || isempty(floatFormat)
floatFormat = '%g';
end
if isempty(cellstr)
newstr = '';
return
end
% convert all values to strings first
nElem = numel(cellstr);
for k = 1:nElem
if isnumeric(cellstr{k})
cellstr{k} = sprintf(floatFormat, cellstr{k});
elseif iscell(cellstr{k})
cellstr{k} = m2tstrjoin(cellstr{k}, delimiter, floatFormat);
% this will fail for heavily nested cells
elseif ~ischar(cellstr{k})
error('matlab2tikz:join:NotCellstrOrNumeric',...
'Expected cellstr or numeric.');
end
end
% inspired by strjoin of recent versions of MATLAB
newstr = cell(2,nElem);
newstr(1,:) = reshape(cellstr, 1, nElem);
newstr(2,1:nElem-1) = {delimiter}; % put delimiters in-between the elements
newstr(2,end) = {''}; % for Octave 4 compatibility
newstr = [newstr{:}];
end

View File

@@ -0,0 +1,18 @@
function arr = versionArray(str)
% Converts a version string to an array.
if ischar(str)
% Translate version string from '2.62.8.1' to [2; 62; 8; 1].
switch getEnvironment
case 'MATLAB'
split = regexp(str, '\.', 'split'); % compatibility MATLAB < R2013a
case 'Octave'
split = strsplit(str, '.');
otherwise
errorUnknownEnvironment();
end
arr = str2num(char(split)); %#ok
else
arr = str;
end
arr = arr(:)';
end

View File

@@ -0,0 +1,9 @@
function str = versionString(arr)
% Converts a version array to string
if ischar(arr)
str = arr;
elseif isnumeric(arr)
str = sprintf('%d.', arr);
str = str(1:end-1); % remove final period
end
end