diff --git a/oo/jsonlab/AUTHORS.txt b/oo/jsonlab/AUTHORS.txt new file mode 100644 index 00000000..9dd3fc7b --- /dev/null +++ b/oo/jsonlab/AUTHORS.txt @@ -0,0 +1,41 @@ +The author of "jsonlab" toolbox is Qianqian Fang. Qianqian +is currently an Assistant Professor at Massachusetts General Hospital, +Harvard Medical School. + +Address: Martinos Center for Biomedical Imaging, + Massachusetts General Hospital, + Harvard Medical School + Bldg 149, 13th St, Charlestown, MA 02129, USA +URL: http://nmr.mgh.harvard.edu/~fangq/ +Email: or + + +The script loadjson.m was built upon previous works by + +- Nedialko Krouchev: http://www.mathworks.com/matlabcentral/fileexchange/25713 + date: 2009/11/02 +- François Glineur: http://www.mathworks.com/matlabcentral/fileexchange/23393 + date: 2009/03/22 +- Joel Feenstra: http://www.mathworks.com/matlabcentral/fileexchange/20565 + date: 2008/07/03 + + +This toolbox contains patches submitted by the following contributors: + +- Blake Johnson + part of revision 341 + +- Niclas Borlin + various fixes in revision 394, including + - loadjson crashes for all-zero sparse matrix. + - loadjson crashes for empty sparse matrix. + - Non-zero size of 0-by-N and N-by-0 empty matrices is lost after savejson/loadjson. + - loadjson crashes for sparse real column vector. + - loadjson crashes for sparse complex column vector. + - Data is corrupted by savejson for sparse real row vector. + - savejson crashes for sparse complex row vector. + +- Yul Kang + patches for svn revision 415. + - savejson saves an empty cell array as [] instead of null + - loadjson differentiates an empty struct from an empty array diff --git a/oo/jsonlab/ChangeLog.txt b/oo/jsonlab/ChangeLog.txt new file mode 100644 index 00000000..5e0cb14d --- /dev/null +++ b/oo/jsonlab/ChangeLog.txt @@ -0,0 +1,55 @@ +============================================================================ + + JSONlab - a toolbox to encode/decode JSON/UBJSON files in MATLAB/Octave + +---------------------------------------------------------------------------- + +JSONlab ChangeLog (key features marked by *): + +== JSONlab 0.9.9 (codename: Optimus - beta), FangQ == + + 2014/01/22 use binary read and write in saveubjson and loadubjson + +== JSONlab 0.9.8-1 (codename: Optimus - alpha update 1), FangQ == + + 2013/10/07 better round-trip conservation for empty arrays and structs (patch submitted by Yul Kang) + +== JSONlab 0.9.8 (codename: Optimus - alpha), FangQ == + 2013/08/23 *universal Binary JSON (UBJSON) support, including both saveubjson and loadubjson + +== JSONlab 0.9.1 (codename: Rodimus, update 1), FangQ == + 2012/12/18 *handling of various empty and sparse matrices (fixes submitted by Niclas Borlin) + +== JSONlab 0.9.0 (codename: Rodimus), FangQ == + + 2012/06/17 *new format for an invalid leading char, unpacking hex code in savejson + 2012/06/01 support JSONP in savejson + 2012/05/25 fix the empty cell bug (reported by Cyril Davin) + 2012/04/05 savejson can save to a file (suggested by Patrick Rapin) + +== JSONlab 0.8.1 (codename: Sentiel, Update 1), FangQ == + + 2012/02/28 loadjson quotation mark escape bug, see http://bit.ly/yyk1nS + 2012/01/25 patch to handle root-less objects, contributed by Blake Johnson + +== JSONlab 0.8.0 (codename: Sentiel), FangQ == + + 2012/01/13 *speed up loadjson by 20 fold when parsing large data arrays in matlab + 2012/01/11 remove row bracket if an array has 1 element, suggested by Mykel Kochenderfer + 2011/12/22 *accept sequence of 'param',value input in savejson and loadjson + 2011/11/18 fix struct array bug reported by Mykel Kochenderfer + +== JSONlab 0.5.1 (codename: Nexus Update 1), FangQ == + + 2011/10/21 fix a bug in loadjson, previous code does not use any of the acceleration + 2011/10/20 loadjson supports JSON collections - concatenated JSON objects + +== JSONlab 0.5.0 (codename: Nexus), FangQ == + + 2011/10/16 package and release jsonlab 0.5.0 + 2011/10/15 *add json demo and regression test, support cpx numbers, fix double quote bug + 2011/10/11 *speed up readjson dramatically, interpret _Array* tags, show data in root level + 2011/10/10 create jsonlab project, start jsonlab website, add online documentation + 2011/10/07 *speed up savejson by 25x using sprintf instead of mat2str, add options support + 2011/10/06 *savejson works for structs, cells and arrays + 2011/09/09 derive loadjson from JSON parser from MATLAB Central, draft savejson.m diff --git a/oo/jsonlab/LICENSE_BSD.txt b/oo/jsonlab/LICENSE_BSD.txt new file mode 100644 index 00000000..5c5cef55 --- /dev/null +++ b/oo/jsonlab/LICENSE_BSD.txt @@ -0,0 +1,25 @@ +Copyright 2011-2014 Qianqian Fang . All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are +permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + + 2. 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 ''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 HOLDERS +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. + +The views and conclusions contained in the software and documentation are those of the +authors and should not be interpreted as representing official policies, either expressed +or implied, of the copyright holders. diff --git a/oo/jsonlab/README.txt b/oo/jsonlab/README.txt new file mode 100644 index 00000000..ad8c2d87 --- /dev/null +++ b/oo/jsonlab/README.txt @@ -0,0 +1,335 @@ +=============================================================================== += JSONlab = += An open-source MATLAB/Octave JSON encoder and decoder = +=============================================================================== + +*Copyright (c) 2011-2014 Qianqian Fang +*License: BSD license, see License*.txt +*Version: 0.9.9 (Optimus - beta) + +------------------------------------------------------------------------------- + +Table of Content: + +I. Introduction +II. Installation +III.Using JSONlab +IV. Known Issues and TODOs +V. Contribution and feedback + +------------------------------------------------------------------------------- + +I. Introduction + +JSON ([http://www.json.org/ JavaScript Object Notation]) is a highly portable, +human-readable and "[http://en.wikipedia.org/wiki/JSON fat-free]" text format +to represent complex and hierarchical data. It is as powerful as +[http://en.wikipedia.org/wiki/XML XML], but less verbose. JSON format is widely +used for data-exchange in applications, and is essential for the wild success +of [http://en.wikipedia.org/wiki/Ajax_(programming) Ajax] and +[http://en.wikipedia.org/wiki/Web_2.0 Web2.0]. + +UBJSON (Universal Binary JSON) is a binary JSON format, specifically +optimized for compact file size and better performance while keeping +the semantics as simple as the text-based JSON format. Using the UBJSON +format allows to wrap complex binary data in a flexible and extensible +structure, making it possible to process complex and large dataset +without accuracy loss due to text conversions. + +We envision that both JSON and its binary version will serve as part of +the mainstream data-exchange formats for scientific research in the future. +It will provide the flexibility and generality achieved by other popular +general-purpose file specifications, such as +[http://www.hdfgroup.org/HDF5/whatishdf5.html HDF5], with significantly +reduced complexity and enhanced performance. + +JSONlab is a free and open-source implementation of a JSON/UBJSON encoder +and a decoder in the native MATLAB language. It can be used to convert a MATLAB +data structure (array, struct, cell, struct array and cell array) into +JSON/UBJSON formatted strings, or to decode a JSON/UBJSON file into MATLAB +data structure. JSONlab supports both MATLAB and +[http://www.gnu.org/software/octave/ GNU Octave] (a free MATLAB clone). + +------------------------------------------------------------------------------- + +II. Installation + +The installation of JSONlab is no different than any other simple +MATLAB toolbox. You only need to download/unzip the JSONlab package +to a folder, and add the folder's path to MATLAB/Octave's path list +by using the following command: + + addpath('/path/to/jsonlab'); + +If you want to add this path permanently, you need to type "pathtool", +browse to the jsonlab root folder and add to the list, then click "Save". +Then, run "rehash" in MATLAB, and type "which loadjson", if you see an +output, that means JSONlab is installed for MATLAB/Octave. + +------------------------------------------------------------------------------- + +III.Using JSONlab + +JSONlab provides two functions, loadjson.m -- a MATLAB->JSON decoder, +and savejson.m -- a MATLAB->JSON encoder, for the text-based JSON, and +two equivallent functions -- loadubjson and saveubjson for the binary +JSON. The detailed help info for the four functions can be found below: + +=== loadjson.m === +
+  data=loadjson(fname,opt)
+     or
+  data=loadjson(fname,'param1',value1,'param2',value2,...)
+ 
+  parse a JSON (JavaScript Object Notation) file or string
+ 
+  authors:Qianqian Fang (fangq nmr.mgh.harvard.edu)
+             date: 2011/09/09
+          Nedialko Krouchev: http://www.mathworks.com/matlabcentral/fileexchange/25713
+             date: 2009/11/02
+          Fran�ois Glineur: http://www.mathworks.com/matlabcentral/fileexchange/23393
+             date: 2009/03/22
+          Joel Feenstra:
+          http://www.mathworks.com/matlabcentral/fileexchange/20565
+             date: 2008/07/03
+ 
+  $Id: loadjson.m 394 2012-12-18 17:58:11Z fangq $
+ 
+  input:
+       fname: input file name, if fname contains "{}" or "[]", fname
+              will be interpreted as a JSON string
+       opt: a struct to store parsing options, opt can be replaced by 
+            a list of ('param',value) pairs. The param string is equivallent
+            to a field in opt.
+ 
+  output:
+       dat: a cell array, where {...} blocks are converted into cell arrays,
+            and [...] are converted to arrays
+
+ +=== savejson.m === + +
+  json=savejson(rootname,obj,filename)
+     or
+  json=savejson(rootname,obj,opt)
+  json=savejson(rootname,obj,'param1',value1,'param2',value2,...)
+ 
+  convert a MATLAB object (cell, struct or array) into a JSON (JavaScript
+  Object Notation) string
+ 
+  author: Qianqian Fang (fangq nmr.mgh.harvard.edu)
+             created on 2011/09/09
+ 
+  $Id: savejson.m 394 2012-12-18 17:58:11Z fangq $
+ 
+  input:
+       rootname: name of the root-object, if set to '', will use variable name
+       obj: a MATLAB object (array, cell, cell array, struct, struct array)
+       filename: a string for the file name to save the output JSON data
+       opt: a struct for additional options, use [] if all use default
+         opt can have the following fields (first in [.|.] is the default)
+ 
+         opt.FileName [''|string]: a file name to save the output JSON data
+         opt.FloatFormat ['%.10g'|string]: format to show each numeric element
+                          of a 1D/2D array;
+         opt.ArrayIndent [1|0]: if 1, output explicit data array with
+                          precedent indentation; if 0, no indentation
+         opt.ArrayToStruct[0|1]: when set to 0, savejson outputs 1D/2D
+                          array in JSON array format; if sets to 1, an
+                          array will be shown as a struct with fields
+                          "_ArrayType_", "_ArraySize_" and "_ArrayData_"; for
+                          sparse arrays, the non-zero elements will be
+                          saved to _ArrayData_ field in triplet-format i.e.
+                          (ix,iy,val) and "_ArrayIsSparse_" will be added
+                          with a value of 1; for a complex array, the 
+                          _ArrayData_ array will include two columns 
+                          (4 for sparse) to record the real and imaginary 
+                          parts, and also "_ArrayIsComplex_":1 is added. 
+         opt.ParseLogical [0|1]: if this is set to 1, logical array elem
+                          will use true/false rather than 1/0.
+         opt.NoRowBracket [1|0]: if this is set to 1, arrays with a single
+                          numerical element will be shown without a square
+                          bracket, unless it is the root object; if 0, square
+                          brackets are forced for any numerical arrays.
+         opt.ForceRootName [0|1]: when set to 1 and rootname is empty, savejson
+                          will use the name of the passed obj variable as the 
+                          root object name; if obj is an expression and 
+                          does not have a name, 'root' will be used; if this 
+                          is set to 0 and rootname is empty, the root level 
+                          will be merged down to the lower level.
+         opt.Inf ['"$1_Inf_"'|string]: a customized regular expression pattern
+                          to represent +/-Inf. The matched pattern is '([-+]*)Inf'
+                          and $1 represents the sign. For those who want to use
+                          1e999 to represent Inf, they can set opt.Inf to '$11e999'
+         opt.NaN ['"_NaN_"'|string]: a customized regular expression pattern
+                          to represent NaN
+         opt.JSONP [''|string]: to generate a JSONP output (JSON with padding),
+                          for example, if opt.JSON='foo', the JSON data is
+                          wrapped inside a function call as 'foo(...);'
+         opt.UnpackHex [1|0]: conver the 0x[hex code] output by loadjson 
+                          back to the string form
+         opt can be replaced by a list of ('param',value) pairs. The param 
+         string is equivallent to a field in opt.
+  output:
+       json: a string in the JSON format (see http://json.org)
+ 
+  examples:
+       a=struct('node',[1  9  10; 2 1 1.2], 'elem',[9 1;1 2;2 3],...
+            'face',[9 01 2; 1 2 3; NaN,Inf,-Inf], 'author','FangQ');
+       savejson('mesh',a)
+       savejson('',a,'ArrayIndent',0,'FloatFormat','\t%.5g')
+
+ +=== loadubjson.m === + +
+  data=loadubjson(fname,opt)
+     or
+  data=loadubjson(fname,'param1',value1,'param2',value2,...)
+ 
+  parse a JSON (JavaScript Object Notation) file or string
+ 
+  authors:Qianqian Fang (fangq nmr.mgh.harvard.edu)
+             date: 2013/08/01
+ 
+  $Id: loadubjson.m 410 2013-08-24 03:33:18Z fangq $
+ 
+  input:
+       fname: input file name, if fname contains "{}" or "[]", fname
+              will be interpreted as a UBJSON string
+       opt: a struct to store parsing options, opt can be replaced by 
+            a list of ('param',value) pairs. The param string is equivallent
+            to a field in opt.
+ 
+  output:
+       dat: a cell array, where {...} blocks are converted into cell arrays,
+            and [...] are converted to arrays
+
+ +=== saveubjson.m === + +
+  json=saveubjson(rootname,obj,filename)
+     or
+  json=saveubjson(rootname,obj,opt)
+  json=saveubjson(rootname,obj,'param1',value1,'param2',value2,...)
+ 
+  convert a MATLAB object (cell, struct or array) into a Universal 
+  Binary JSON (UBJSON) binary string
+ 
+  author: Qianqian Fang (fangq nmr.mgh.harvard.edu)
+             created on 2013/08/17
+ 
+  $Id: saveubjson.m 410 2013-08-24 03:33:18Z fangq $
+ 
+  input:
+       rootname: name of the root-object, if set to '', will use variable name
+       obj: a MATLAB object (array, cell, cell array, struct, struct array)
+       filename: a string for the file name to save the output JSON data
+       opt: a struct for additional options, use [] if all use default
+         opt can have the following fields (first in [.|.] is the default)
+ 
+         opt.FileName [''|string]: a file name to save the output JSON data
+         opt.ArrayToStruct[0|1]: when set to 0, saveubjson outputs 1D/2D
+                          array in JSON array format; if sets to 1, an
+                          array will be shown as a struct with fields
+                          "_ArrayType_", "_ArraySize_" and "_ArrayData_"; for
+                          sparse arrays, the non-zero elements will be
+                          saved to _ArrayData_ field in triplet-format i.e.
+                          (ix,iy,val) and "_ArrayIsSparse_" will be added
+                          with a value of 1; for a complex array, the 
+                          _ArrayData_ array will include two columns 
+                          (4 for sparse) to record the real and imaginary 
+                          parts, and also "_ArrayIsComplex_":1 is added. 
+         opt.ParseLogical [1|0]: if this is set to 1, logical array elem
+                          will use true/false rather than 1/0.
+         opt.NoRowBracket [1|0]: if this is set to 1, arrays with a single
+                          numerical element will be shown without a square
+                          bracket, unless it is the root object; if 0, square
+                          brackets are forced for any numerical arrays.
+         opt.ForceRootName [0|1]: when set to 1 and rootname is empty, saveubjson
+                          will use the name of the passed obj variable as the 
+                          root object name; if obj is an expression and 
+                          does not have a name, 'root' will be used; if this 
+                          is set to 0 and rootname is empty, the root level 
+                          will be merged down to the lower level.
+         opt.JSONP [''|string]: to generate a JSONP output (JSON with padding),
+                          for example, if opt.JSON='foo', the JSON data is
+                          wrapped inside a function call as 'foo(...);'
+         opt.UnpackHex [1|0]: conver the 0x[hex code] output by loadjson 
+                          back to the string form
+         opt can be replaced by a list of ('param',value) pairs. The param 
+         string is equivallent to a field in opt.
+  output:
+       json: a string in the JSON format (see http://json.org)
+ 
+  examples:
+       a=struct('node',[1  9  10; 2 1 1.2], 'elem',[9 1;1 2;2 3],...
+            'face',[9 01 2; 1 2 3; NaN,Inf,-Inf], 'author','FangQ');
+       saveubjson('mesh',a)
+       saveubjson('',a,'ArrayIndent',0,'FloatFormat','\t%.5g')
+
+ + +=== examples === + +Under the "examples" folder, you can find several scripts to demonstrate the +basic utilities of JSONlab. Running the "demo_jsonlab_basic.m" script, you +will see the conversions from MATLAB data structure to JSON text and backward. +In "jsonlab_selftest.m", we load complex JSON files downloaded from the Internet +and validate the loadjson/savejson functions for regression testing purposes. +Similarly, a "demo_ubjson_basic.m" script is provided to test the saveubjson +and loadubjson pairs for various matlab data structures. + +Please run these examples and understand how JSONlab works before you use +it to process your data. + +------------------------------------------------------------------------------- + +IV. Known Issues and TODOs + +JSONlab has several known limitations. We are striving to make it more general +and robust. Hopefully in a few future releases, the limitations become less. + +Here are the known issues: + +# Any high-dimensional cell-array will be converted to a 1D array; +# When processing names containing multi-byte characters, Octave and MATLAB \ +can give different field-names; you can use feature('DefaultCharacterSet','latin1') \ +in MATLAB to get consistant results +# Can not handle classes. +# saveubjson has not yet supported arbitrary data ([H] in the UBJSON specification) +# saveubjson now converts a logical array into a uint8 ([U]) array for now +# an unofficial N-D array count syntax is implemented in saveubjson. We are \ +actively communicating with the UBJSON spec maintainer to investigate the \ +possibility of making it upstream + +------------------------------------------------------------------------------- + +V. Contribution and feedback + +JSONlab is an open-source project. This means you can not only use it and modify +it as you wish, but also you can contribute your changes back to JSONlab so +that everyone else can enjoy the improvement. For anyone who want to contribute, +please download JSONlab source code from it's subversion repository by using the +following command: + + svn checkout svn://svn.code.sf.net/p/iso2mesh/code/trunk/jsonlab jsonlab + +You can make changes to the files as needed. Once you are satisfied with your +changes, and ready to share it with others, please cd the root directory of +JSONlab, and type + + svn diff > yourname_featurename.patch + +You then email the .patch file to JSONlab's maintainer, Qianqian Fang, at +the email address shown in the beginning of this file. Qianqian will review +the changes and commit it to the subversion if they are satisfactory. + +We appreciate any suggestions and feedbacks from you. Please use iso2mesh's +mailing list to report any questions you may have with JSONlab: + +http://groups.google.com/group/iso2mesh-users?hl=en&pli=1 + +(Subscription to the mailing list is needed in order to post messages). diff --git a/oo/jsonlab/examples/demo_jsonlab_basic.m b/oo/jsonlab/examples/demo_jsonlab_basic.m new file mode 100644 index 00000000..d094a64e --- /dev/null +++ b/oo/jsonlab/examples/demo_jsonlab_basic.m @@ -0,0 +1,161 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Demonstration of Basic Utilities of JSONlab +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +rngstate = rand ('state'); +randseed=hex2dec('623F9A9E'); +clear data2json json2data + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a simple scalar value \n') +fprintf(1,'%%=================================================\n\n') + +data2json=pi +savejson('',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a complex number\n') +fprintf(1,'%%=================================================\n\n') + +clear i; +data2json=1+2*i +savejson('',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a complex matrix\n') +fprintf(1,'%%=================================================\n\n') + +data2json=magic(6); +data2json=data2json(:,1:3)+data2json(:,4:6)*i +savejson('',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% MATLAB special constants\n') +fprintf(1,'%%=================================================\n\n') + +data2json=[NaN Inf -Inf] +savejson('specials',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a real sparse matrix\n') +fprintf(1,'%%=================================================\n\n') + +data2json=sprand(10,10,0.1) +savejson('sparse',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a complex sparse matrix\n') +fprintf(1,'%%=================================================\n\n') + +data2json=data2json-data2json*i +savejson('complex_sparse',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% an all-zero sparse matrix\n') +fprintf(1,'%%=================================================\n\n') + +data2json=sparse(2,3); +savejson('all_zero_sparse',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% an empty sparse matrix\n') +fprintf(1,'%%=================================================\n\n') + +data2json=sparse([]); +savejson('empty_sparse',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% an empty 0-by-0 real matrix\n') +fprintf(1,'%%=================================================\n\n') + +data2json=[]; +savejson('empty_0by0_real',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% an empty 0-by-3 real matrix\n') +fprintf(1,'%%=================================================\n\n') + +data2json=zeros(0,3); +savejson('empty_0by3_real',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a sparse real column vector\n') +fprintf(1,'%%=================================================\n\n') + +data2json=sparse([0,3,0,1,4]'); +savejson('sparse_column_vector',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a sparse complex column vector\n') +fprintf(1,'%%=================================================\n\n') + +data2json=data2json-1i*data2json; +savejson('complex_sparse_column_vector',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a sparse real row vector\n') +fprintf(1,'%%=================================================\n\n') + +data2json=sparse([0,3,0,1,4]); +savejson('sparse_row_vector',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a sparse complex row vector\n') +fprintf(1,'%%=================================================\n\n') + +data2json=data2json-1i*data2json; +savejson('complex_sparse_row_vector',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a structure\n') +fprintf(1,'%%=================================================\n\n') + +data2json=struct('name','Think Different','year',1997,'magic',magic(3),... + 'misfits',[Inf,NaN],'embedded',struct('left',true,'right',false)) +savejson('astruct',data2json,struct('ParseLogical',1)) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a structure array\n') +fprintf(1,'%%=================================================\n\n') + +data2json=struct('name','Nexus Prime','rank',9); +data2json(2)=struct('name','Sentinel Prime','rank',9); +data2json(3)=struct('name','Optimus Prime','rank',9); +savejson('Supreme Commander',data2json) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a cell array\n') +fprintf(1,'%%=================================================\n\n') + +data2json=cell(3,1); +data2json{1}=struct('buzz',1.1,'rex',1.2,'bo',1.3,'hamm',2.0,'slink',2.1,'potato',2.2,... + 'woody',3.0,'sarge',3.1,'etch',4.0,'lenny',5.0,'squeeze',6.0,'wheezy',7.0); +data2json{2}=struct('Ubuntu',['Kubuntu';'Xubuntu';'Lubuntu']); +data2json{3}=[10.04,10.10,11.04,11.10] +savejson('debian',data2json,struct('FloatFormat','%.2f')) +json2data=loadjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% invalid field-name handling\n') +fprintf(1,'%%=================================================\n\n') + +json2data=loadjson('{"ValidName":1, "_InvalidName":2, ":Field:":3, "项目":"绝密"}') + +rand ('state',rngstate); + diff --git a/oo/jsonlab/examples/demo_ubjson_basic.m b/oo/jsonlab/examples/demo_ubjson_basic.m new file mode 100644 index 00000000..fd5096c3 --- /dev/null +++ b/oo/jsonlab/examples/demo_ubjson_basic.m @@ -0,0 +1,161 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Demonstration of Basic Utilities of JSONlab +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +rngstate = rand ('state'); +randseed=hex2dec('623F9A9E'); +clear data2json json2data + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a simple scalar value \n') +fprintf(1,'%%=================================================\n\n') + +data2json=pi +saveubjson('',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a complex number\n') +fprintf(1,'%%=================================================\n\n') + +clear i; +data2json=1+2*i +saveubjson('',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a complex matrix\n') +fprintf(1,'%%=================================================\n\n') + +data2json=magic(6); +data2json=data2json(:,1:3)+data2json(:,4:6)*i +saveubjson('',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% MATLAB special constants\n') +fprintf(1,'%%=================================================\n\n') + +data2json=[NaN Inf -Inf] +saveubjson('specials',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a real sparse matrix\n') +fprintf(1,'%%=================================================\n\n') + +data2json=sprand(10,10,0.1) +saveubjson('sparse',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a complex sparse matrix\n') +fprintf(1,'%%=================================================\n\n') + +data2json=data2json-data2json*i +saveubjson('complex_sparse',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% an all-zero sparse matrix\n') +fprintf(1,'%%=================================================\n\n') + +data2json=sparse(2,3); +saveubjson('all_zero_sparse',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% an empty sparse matrix\n') +fprintf(1,'%%=================================================\n\n') + +data2json=sparse([]); +saveubjson('empty_sparse',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% an empty 0-by-0 real matrix\n') +fprintf(1,'%%=================================================\n\n') + +data2json=[]; +saveubjson('empty_0by0_real',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% an empty 0-by-3 real matrix\n') +fprintf(1,'%%=================================================\n\n') + +data2json=zeros(0,3); +saveubjson('empty_0by3_real',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a sparse real column vector\n') +fprintf(1,'%%=================================================\n\n') + +data2json=sparse([0,3,0,1,4]'); +saveubjson('sparse_column_vector',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a sparse complex column vector\n') +fprintf(1,'%%=================================================\n\n') + +data2json=data2json-1i*data2json; +saveubjson('complex_sparse_column_vector',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a sparse real row vector\n') +fprintf(1,'%%=================================================\n\n') + +data2json=sparse([0,3,0,1,4]); +saveubjson('sparse_row_vector',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a sparse complex row vector\n') +fprintf(1,'%%=================================================\n\n') + +data2json=data2json-1i*data2json; +saveubjson('complex_sparse_row_vector',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a structure\n') +fprintf(1,'%%=================================================\n\n') + +data2json=struct('name','Think Different','year',1997,'magic',magic(3),... + 'misfits',[Inf,NaN],'embedded',struct('left',true,'right',false)) +saveubjson('astruct',data2json,struct('ParseLogical',1)) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a structure array\n') +fprintf(1,'%%=================================================\n\n') + +data2json=struct('name','Nexus Prime','rank',9); +data2json(2)=struct('name','Sentinel Prime','rank',9); +data2json(3)=struct('name','Optimus Prime','rank',9); +saveubjson('Supreme Commander',data2json) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% a cell array\n') +fprintf(1,'%%=================================================\n\n') + +data2json=cell(3,1); +data2json{1}=struct('buzz',1.1,'rex',1.2,'bo',1.3,'hamm',2.0,'slink',2.1,'potato',2.2,... + 'woody',3.0,'sarge',3.1,'etch',4.0,'lenny',5.0,'squeeze',6.0,'wheezy',7.0); +data2json{2}=struct('Ubuntu',['Kubuntu';'Xubuntu';'Lubuntu']); +data2json{3}=[10.04,10.10,11.04,11.10] +saveubjson('debian',data2json,struct('FloatFormat','%.2f')) +json2data=loadubjson(ans) + +fprintf(1,'\n%%=================================================\n') +fprintf(1,'%% invalid field-name handling\n') +fprintf(1,'%%=================================================\n\n') + +json2data=loadubjson(saveubjson('',loadjson('{"ValidName":1, "_InvalidName":2, ":Field:":3, "项目":"绝密"}'))) + +rand ('state',rngstate); + diff --git a/oo/jsonlab/examples/example1.json b/oo/jsonlab/examples/example1.json new file mode 100644 index 00000000..be0993ef --- /dev/null +++ b/oo/jsonlab/examples/example1.json @@ -0,0 +1,23 @@ + { + "firstName": "John", + "lastName": "Smith", + "age": 25, + "address": + { + "streetAddress": "21 2nd Street", + "city": "New York", + "state": "NY", + "postalCode": "10021" + }, + "phoneNumber": + [ + { + "type": "home", + "number": "212 555-1234" + }, + { + "type": "fax", + "number": "646 555-4567" + } + ] + } diff --git a/oo/jsonlab/examples/example2.json b/oo/jsonlab/examples/example2.json new file mode 100644 index 00000000..eacfbf5e --- /dev/null +++ b/oo/jsonlab/examples/example2.json @@ -0,0 +1,22 @@ +{ + "glossary": { + "title": "example glossary", + "GlossDiv": { + "title": "S", + "GlossList": { + "GlossEntry": { + "ID": "SGML", + "SortAs": "SGML", + "GlossTerm": "Standard Generalized Markup Language", + "Acronym": "SGML", + "Abbrev": "ISO 8879:1986", + "GlossDef": { + "para": "A meta-markup language, used to create markup languages such as DocBook.", + "GlossSeeAlso": ["GML", "XML"] + }, + "GlossSee": "markup" + } + } + } + } +} diff --git a/oo/jsonlab/examples/example3.json b/oo/jsonlab/examples/example3.json new file mode 100644 index 00000000..b7ca9411 --- /dev/null +++ b/oo/jsonlab/examples/example3.json @@ -0,0 +1,11 @@ +{"menu": { + "id": "file", + "value": "_&File", + "popup": { + "menuitem": [ + {"value": "_&New", "onclick": "CreateNewDoc(\"\"\")"}, + {"value": "_&Open", "onclick": "OpenDoc()"}, + {"value": "_&Close", "onclick": "CloseDoc()"} + ] + } +}} diff --git a/oo/jsonlab/examples/example4.json b/oo/jsonlab/examples/example4.json new file mode 100644 index 00000000..66deeafb --- /dev/null +++ b/oo/jsonlab/examples/example4.json @@ -0,0 +1,34 @@ +[ + { + "sample" : { + "rho" : 1 + } + }, + { + "sample" : { + "rho" : 2 + } + }, + [ + { + "_ArrayType_" : "double", + "_ArraySize_" : [1,2], + "_ArrayData_" : [1,0] + }, + { + "_ArrayType_" : "double", + "_ArraySize_" : [1,2], + "_ArrayData_" : [1,1] + }, + { + "_ArrayType_" : "double", + "_ArraySize_" : [1,2], + "_ArrayData_" : [1,2] + } + ], + [ + "Paper", + "Scissors", + "Stone" + ] +] diff --git a/oo/jsonlab/examples/jsonlab_basictest.matlab b/oo/jsonlab/examples/jsonlab_basictest.matlab new file mode 100644 index 00000000..ff8380a1 --- /dev/null +++ b/oo/jsonlab/examples/jsonlab_basictest.matlab @@ -0,0 +1,361 @@ + + < M A T L A B > + Copyright 1984-2007 The MathWorks, Inc. + Version 7.4.0.287 (R2007a) + January 29, 2007 + + + To get started, type one of these: helpwin, helpdesk, or demo. + For product information, visit www.mathworks.com. + +>> >> >> >> >> >> >> >> >> +%================================================= +>> % a simple scalar value +>> %================================================= + +>> >> +data2json = + + 3.1416 + +>> +ans = + +[3.141592654] + + +>> +json2data = + + 3.1416 + +>> >> +%================================================= +>> % a complex number +>> %================================================= + +>> >> >> +data2json = + + 1.0000 + 2.0000i + +>> +ans = + +{ + "_ArrayType_": "double", + "_ArraySize_": [1,1], + "_ArrayIsComplex_": 1, + "_ArrayData_": [1,2] +} + + +>> +json2data = + + 1.0000 + 2.0000i + +>> >> +%================================================= +>> % a complex matrix +>> %================================================= + +>> >> >> +data2json = + + 35.0000 +26.0000i 1.0000 +19.0000i 6.0000 +24.0000i + 3.0000 +21.0000i 32.0000 +23.0000i 7.0000 +25.0000i + 31.0000 +22.0000i 9.0000 +27.0000i 2.0000 +20.0000i + 8.0000 +17.0000i 28.0000 +10.0000i 33.0000 +15.0000i + 30.0000 +12.0000i 5.0000 +14.0000i 34.0000 +16.0000i + 4.0000 +13.0000i 36.0000 +18.0000i 29.0000 +11.0000i + +>> +ans = + +{ + "_ArrayType_": "double", + "_ArraySize_": [6,3], + "_ArrayIsComplex_": 1, + "_ArrayData_": [ + [35,26], + [3,21], + [31,22], + [8,17], + [30,12], + [4,13], + [1,19], + [32,23], + [9,27], + [28,10], + [5,14], + [36,18], + [6,24], + [7,25], + [2,20], + [33,15], + [34,16], + [29,11] + ] +} + + +>> +json2data = + + 35.0000 +26.0000i 1.0000 +19.0000i 6.0000 +24.0000i + 3.0000 +21.0000i 32.0000 +23.0000i 7.0000 +25.0000i + 31.0000 +22.0000i 9.0000 +27.0000i 2.0000 +20.0000i + 8.0000 +17.0000i 28.0000 +10.0000i 33.0000 +15.0000i + 30.0000 +12.0000i 5.0000 +14.0000i 34.0000 +16.0000i + 4.0000 +13.0000i 36.0000 +18.0000i 29.0000 +11.0000i + +>> >> +%================================================= +>> % MATLAB special constants +>> %================================================= + +>> >> +data2json = + + NaN Inf -Inf + +>> +ans = + +{ + "specials": ["_NaN_","_Inf_","-_Inf_"] +} + + +>> +json2data = + + specials: [NaN Inf -Inf] + +>> >> +%================================================= +>> % a real sparse matrix +>> %================================================= + +>> >> +data2json = + + (1,2) 0.6557 + (9,2) 0.7577 + (3,5) 0.8491 + (10,5) 0.7431 + (10,8) 0.3922 + (7,9) 0.6787 + (2,10) 0.0357 + (6,10) 0.9340 + (10,10) 0.6555 + +>> +ans = + +{ + "sparse": { + "_ArrayType_": "double", + "_ArraySize_": [10,10], + "_ArrayIsSparse_": 1, + "_ArrayData_": [ + [1,2,0.6557406992], + [9,2,0.7577401306], + [3,5,0.8491293059], + [10,5,0.7431324681], + [10,8,0.3922270195], + [7,9,0.6787351549], + [2,10,0.03571167857], + [6,10,0.9339932478], + [10,10,0.6554778902] + ] + } +} + + +>> +json2data = + + sparse: [10x10 double] + +>> >> +%================================================= +>> % a complex sparse matrix +>> %================================================= + +>> >> +data2json = + + (1,2) 0.6557 - 0.6557i + (9,2) 0.7577 - 0.7577i + (3,5) 0.8491 - 0.8491i + (10,5) 0.7431 - 0.7431i + (10,8) 0.3922 - 0.3922i + (7,9) 0.6787 - 0.6787i + (2,10) 0.0357 - 0.0357i + (6,10) 0.9340 - 0.9340i + (10,10) 0.6555 - 0.6555i + +>> +ans = + +{ + "complex_sparse": { + "_ArrayType_": "double", + "_ArraySize_": [10,10], + "_ArrayIsComplex_": 1, + "_ArrayIsSparse_": 1, + "_ArrayData_": [ + [1,2,0.6557406992,-0.6557406992], + [9,2,0.7577401306,-0.7577401306], + [3,5,0.8491293059,-0.8491293059], + [10,5,0.7431324681,-0.7431324681], + [10,8,0.3922270195,-0.3922270195], + [7,9,0.6787351549,-0.6787351549], + [2,10,0.03571167857,-0.03571167857], + [6,10,0.9339932478,-0.9339932478], + [10,10,0.6554778902,-0.6554778902] + ] + } +} + + +>> +json2data = + + complex_sparse: [10x10 double] + +>> >> +%================================================= +>> % a structure +>> %================================================= + +>> >> +data2json = + + name: 'Think Different' + year: 1997 + magic: [3x3 double] + misfits: [Inf NaN] + embedded: [1x1 struct] + +>> +ans = + +{ + "astruct": { + "name": "Think Different", + "year": 1997, + "magic": [ + [8,1,6], + [3,5,7], + [4,9,2] + ], + "misfits": ["_Inf_","_NaN_"], + "embedded": { + "left": true, + "right": false + } + } +} + + +>> +json2data = + + astruct: [1x1 struct] + +>> >> +%================================================= +>> % a structure array +>> %================================================= + +>> >> >> >> >> +ans = + +{ + "Supreme Commander": [ + { + "name": "Nexus Prime", + "rank": 9 + }, + { + "name": "Sentinel Prime", + "rank": 9 + }, + { + "name": "Optimus Prime", + "rank": 9 + } + ] +} + + +>> +json2data = + + Supreme_0x20_Commander: [1x3 struct] + +>> >> +%================================================= +>> % a cell array +>> %================================================= + +>> >> >> >> >> +data2json = + + [1x1 struct] + [1x1 struct] + [1x4 double] + +>> +ans = + +{ + "debian": [ + { + "buzz": 1.10, + "rex": 1.20, + "bo": 1.30, + "hamm": 2.00, + "slink": 2.10, + "potato": 2.20, + "woody": 3.00, + "sarge": 3.10, + "etch": 4.00, + "lenny": 5.00, + "squeeze": 6.00, + "wheezy": 7.00 + }, + { + "Ubuntu": [ + "Kubuntu", + "Xubuntu", + "Lubuntu" + ] + }, + [10.04,10.10,11.04,11.10] + ] +} + + +>> +json2data = + + debian: {[1x1 struct] [1x1 struct] [10.0400 10.1000 11.0400 11.1000]} + +>> >> +%================================================= +>> % invalid field-name handling +>> %================================================= + +>> >> +json2data = + + ValidName: 1 + x0x5F_InvalidName: 2 + x0x3A_Field_0x3A_: 3 + x0xE9A1B9__0xE79BAE_: '绝密' + +>> >> >> >> \ No newline at end of file diff --git a/oo/jsonlab/examples/jsonlab_selftest.m b/oo/jsonlab/examples/jsonlab_selftest.m new file mode 100644 index 00000000..1caaf28e --- /dev/null +++ b/oo/jsonlab/examples/jsonlab_selftest.m @@ -0,0 +1,22 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Regression Test Unit of loadjson and savejson +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +for i=1:4 + fname=sprintf('example%d.json',i); + if(exist(fname,'file')==0) break; end + fprintf(1,'===============================================\n>> %s\n',fname); + json=savejson('data',loadjson(fname)); + fprintf(1,'%s\n',json); + data=loadjson(json); +end + +for i=1:4 + fname=sprintf('example%d.json',i); + if(exist(fname,'file')==0) break; end + fprintf(1,'===============================================\n>> %s\n',fname); + json=saveubjson('data',loadjson(fname)); + fprintf(1,'%s\n',json); + data=loadubjson(json); + savejson('',data) +end diff --git a/oo/jsonlab/examples/jsonlab_selftest.matlab b/oo/jsonlab/examples/jsonlab_selftest.matlab new file mode 100644 index 00000000..84e66437 --- /dev/null +++ b/oo/jsonlab/examples/jsonlab_selftest.matlab @@ -0,0 +1,121 @@ + + < M A T L A B > + Copyright 1984-2007 The MathWorks, Inc. + Version 7.4.0.287 (R2007a) + January 29, 2007 + + + To get started, type one of these: helpwin, helpdesk, or demo. + For product information, visit www.mathworks.com. + +>> >> >> >> >> =============================================== +>> example1.json +{ + "data": { + "firstName": "John", + "lastName": "Smith", + "age": 25, + "address": { + "streetAddress": "21 2nd Street", + "city": "New York", + "state": "NY", + "postalCode": "10021" + }, + "phoneNumber": [ + { + "type": "home", + "number": "212 555-1234" + }, + { + "type": "fax", + "number": "646 555-4567" + } + ] + } +} + +=============================================== +>> example2.json +{ + "data": { + "glossary": { + "title": "example glossary", + "GlossDiv": { + "title": "S", + "GlossList": { + "GlossEntry": { + "ID": "SGML", + "SortAs": "SGML", + "GlossTerm": "Standard Generalized Markup Language", + "Acronym": "SGML", + "Abbrev": "ISO 8879:1986", + "GlossDef": { + "para": "A meta-markup language, used to create markup languages such as DocBook.", + "GlossSeeAlso": [ + "GML", + "XML" + ] + }, + "GlossSee": "markup" + } + } + } + } + } +} + +=============================================== +>> example3.json +{ + "data": { + "menu": { + "id": "file", + "value": "_&File", + "popup": { + "menuitem": [ + { + "value": "_&New", + "onclick": "CreateNewDoc(\"\"\")" + }, + { + "value": "_&Open", + "onclick": "OpenDoc()" + }, + { + "value": "_&Close", + "onclick": "CloseDoc()" + } + ] + } + } + } +} + +=============================================== +>> example4.json +{ + "data": [ + { + "sample": { + "rho": 1 + } + }, + { + "sample": { + "rho": 2 + } + }, + [ + [1,0], + [1,1], + [1,2] + ], + [ + "Paper", + "Scissors", + "Stone" + ] + ] +} + +>> \ No newline at end of file diff --git a/oo/jsonlab/examples/jsonlab_speedtest.m b/oo/jsonlab/examples/jsonlab_speedtest.m new file mode 100644 index 00000000..4990fba0 --- /dev/null +++ b/oo/jsonlab/examples/jsonlab_speedtest.m @@ -0,0 +1,21 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Benchmarking processing speed of savejson and loadjson +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +datalen=[1e3 1e4 1e5 1e6]; +len=length(datalen); +tsave=zeros(len,1); +tload=zeros(len,1); +for i=1:len + tic; + json=savejson('data',struct('d1',rand(datalen(i),3),'d2',rand(datalen(i),3)>0.5)); + tsave(i)=toc; + data=loadjson(json); + tload(i)=toc-tsave(i); + fprintf(1,'matrix size: %d\n',datalen(i)); +end + +loglog(datalen,tsave,'o-',datalen,tload,'r*-'); +legend('savejson runtime (s)','loadjson runtime (s)'); +xlabel('array size'); +ylabel('running time (s)'); diff --git a/oo/jsonlab/jsonopt.m b/oo/jsonlab/jsonopt.m new file mode 100644 index 00000000..235a007b --- /dev/null +++ b/oo/jsonlab/jsonopt.m @@ -0,0 +1,32 @@ +function val=jsonopt(key,default,varargin) +% +% val=jsonopt(key,default,optstruct) +% +% setting options based on a struct. The struct can be produced +% by varargin2struct from a list of 'param','value' pairs +% +% authors:Qianqian Fang (fangq nmr.mgh.harvard.edu) +% +% $Id: loadjson.m 371 2012-06-20 12:43:06Z fangq $ +% +% input: +% key: a string with which one look up a value from a struct +% default: if the key does not exist, return default +% optstruct: a struct where each sub-field is a key +% +% output: +% val: if key exists, val=optstruct.key; otherwise val=default +% +% license: +% BSD license, see LICENSE_BSD.txt files for details +% +% -- this function is part of jsonlab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab) +% + +val=default; +if(nargin<=2) return; end +opt=varargin{1}; +if(isstruct(opt) && isfield(opt,key)) + val=getfield(opt,key); +end + diff --git a/oo/jsonlab/loadjson.m b/oo/jsonlab/loadjson.m new file mode 100644 index 00000000..18c38216 --- /dev/null +++ b/oo/jsonlab/loadjson.m @@ -0,0 +1,526 @@ +function data = loadjson(fname,varargin) +% +% data=loadjson(fname,opt) +% or +% data=loadjson(fname,'param1',value1,'param2',value2,...) +% +% parse a JSON (JavaScript Object Notation) file or string +% +% authors:Qianqian Fang (fangq nmr.mgh.harvard.edu) +% date: 2011/09/09 +% Nedialko Krouchev: http://www.mathworks.com/matlabcentral/fileexchange/25713 +% date: 2009/11/02 +% François Glineur: http://www.mathworks.com/matlabcentral/fileexchange/23393 +% date: 2009/03/22 +% Joel Feenstra: +% http://www.mathworks.com/matlabcentral/fileexchange/20565 +% date: 2008/07/03 +% +% $Id: loadjson.m 415 2013-10-07 16:38:31Z fangq $ +% +% input: +% fname: input file name, if fname contains "{}" or "[]", fname +% will be interpreted as a JSON string +% opt: a struct to store parsing options, opt can be replaced by +% a list of ('param',value) pairs. The param string is equivallent +% to a field in opt. +% +% output: +% dat: a cell array, where {...} blocks are converted into cell arrays, +% and [...] are converted to arrays +% +% license: +% BSD license, see LICENSE_BSD.txt files for details +% +% -- this function is part of jsonlab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab) +% + +global pos inStr len esc index_esc len_esc isoct arraytoken + +if(regexp(fname,'[\{\}\]\[]','once')) + string=fname; +elseif(exist(fname,'file')) + fid = fopen(fname,'rt'); + string = fscanf(fid,'%c'); + fclose(fid); +else + error('input file does not exist'); +end + +pos = 1; len = length(string); inStr = string; +isoct=exist('OCTAVE_VERSION'); +arraytoken=find(inStr=='[' | inStr==']' | inStr=='"'); +jstr=regexprep(inStr,'\\\\',' '); +escquote=regexp(jstr,'\\"'); +arraytoken=sort([arraytoken escquote]); + +% String delimiters and escape chars identified to improve speed: +esc = find(inStr=='"' | inStr=='\' ); % comparable to: regexp(inStr, '["\\]'); +index_esc = 1; len_esc = length(esc); + +opt=varargin2struct(varargin{:}); +jsoncount=1; +while pos <= len + switch(next_char) + case '{' + data{jsoncount} = parse_object(opt); + case '[' + data{jsoncount} = parse_array(opt); + otherwise + error_pos('Outer level structure must be an object or an array'); + end + jsoncount=jsoncount+1; +end % while + +jsoncount=length(data); +if(jsoncount==1 && iscell(data)) + data=data{1}; +end + +if(~isempty(data)) + if(isstruct(data)) % data can be a struct array + data=jstruct2array(data); + elseif(iscell(data)) + data=jcell2array(data); + end +end + + +%% +function newdata=parse_collection(id,data,obj) + +if(jsoncount>0 && exist('data','var')) + if(~iscell(data)) + newdata=cell(1); + newdata{1}=data; + data=newdata; + end +end + +%% +function newdata=jcell2array(data) +len=length(data); +newdata=data; +for i=1:len + if(isstruct(data{i})) + newdata{i}=jstruct2array(data{i}); + elseif(iscell(data{i})) + newdata{i}=jcell2array(data{i}); + end +end + +%%------------------------------------------------------------------------- +function newdata=jstruct2array(data) +fn=fieldnames(data); +newdata=data; +len=length(data); +for i=1:length(fn) % depth-first + for j=1:len + if(isstruct(getfield(data(j),fn{i}))) + newdata(j)=setfield(newdata(j),fn{i},jstruct2array(getfield(data(j),fn{i}))); + end + end +end +if(~isempty(strmatch('x0x5F_ArrayType_',fn)) && ~isempty(strmatch('x0x5F_ArrayData_',fn))) + newdata=cell(len,1); + for j=1:len + ndata=cast(data(j).x0x5F_ArrayData_,data(j).x0x5F_ArrayType_); + iscpx=0; + if(~isempty(strmatch('x0x5F_ArrayIsComplex_',fn))) + if(data(j).x0x5F_ArrayIsComplex_) + iscpx=1; + end + end + if(~isempty(strmatch('x0x5F_ArrayIsSparse_',fn))) + if(data(j).x0x5F_ArrayIsSparse_) + if(~isempty(strmatch('x0x5F_ArraySize_',fn))) + dim=data(j).x0x5F_ArraySize_; + if(iscpx && size(ndata,2)==4-any(dim==1)) + ndata(:,end-1)=complex(ndata(:,end-1),ndata(:,end)); + end + if isempty(ndata) + % All-zeros sparse + ndata=sparse(dim(1),prod(dim(2:end))); + elseif dim(1)==1 + % Sparse row vector + ndata=sparse(1,ndata(:,1),ndata(:,2),dim(1),prod(dim(2:end))); + elseif dim(2)==1 + % Sparse column vector + ndata=sparse(ndata(:,1),1,ndata(:,2),dim(1),prod(dim(2:end))); + else + % Generic sparse array. + ndata=sparse(ndata(:,1),ndata(:,2),ndata(:,3),dim(1),prod(dim(2:end))); + end + else + if(iscpx && size(ndata,2)==4) + ndata(:,3)=complex(ndata(:,3),ndata(:,4)); + end + ndata=sparse(ndata(:,1),ndata(:,2),ndata(:,3)); + end + end + elseif(~isempty(strmatch('x0x5F_ArraySize_',fn))) + if(iscpx && size(ndata,2)==2) + ndata=complex(ndata(:,1),ndata(:,2)); + end + ndata=reshape(ndata(:),data(j).x0x5F_ArraySize_); + end + newdata{j}=ndata; + end + if(len==1) + newdata=newdata{1}; + end +end + +%%------------------------------------------------------------------------- +function object = parse_object(varargin) + parse_char('{'); + object = []; + if next_char ~= '}' + while 1 + str = parseStr(varargin{:}); + if isempty(str) + error_pos('Name of value at position %d cannot be empty'); + end + parse_char(':'); + val = parse_value(varargin{:}); + eval( sprintf( 'object.%s = val;', valid_field(str) ) ); + if next_char == '}' + break; + end + parse_char(','); + end + end + parse_char('}'); + +%%------------------------------------------------------------------------- + +function object = parse_array(varargin) % JSON array is written in row-major order +global pos inStr isoct + parse_char('['); + object = cell(0, 1); + dim2=[]; + if next_char ~= ']' + [endpos e1l e1r maxlevel]=matching_bracket(inStr,pos); + arraystr=['[' inStr(pos:endpos)]; + arraystr=regexprep(arraystr,'"_NaN_"','NaN'); + arraystr=regexprep(arraystr,'"([-+]*)_Inf_"','$1Inf'); + arraystr(find(arraystr==sprintf('\n')))=[]; + arraystr(find(arraystr==sprintf('\r')))=[]; + %arraystr=regexprep(arraystr,'\s*,',','); % this is slow,sometimes needed + if(~isempty(e1l) && ~isempty(e1r)) % the array is in 2D or higher D + astr=inStr((e1l+1):(e1r-1)); + astr=regexprep(astr,'"_NaN_"','NaN'); + astr=regexprep(astr,'"([-+]*)_Inf_"','$1Inf'); + astr(find(astr==sprintf('\n')))=[]; + astr(find(astr==sprintf('\r')))=[]; + astr(find(astr==' '))=''; + if(isempty(find(astr=='[', 1))) % array is 2D + dim2=length(sscanf(astr,'%f,',[1 inf])); + end + else % array is 1D + astr=arraystr(2:end-1); + astr(find(astr==' '))=''; + [obj count errmsg nextidx]=sscanf(astr,'%f,',[1,inf]); + if(nextidx>=length(astr)-1) + object=obj; + pos=endpos; + parse_char(']'); + return; + end + end + if(~isempty(dim2)) + astr=arraystr; + astr(find(astr=='['))=''; + astr(find(astr==']'))=''; + astr(find(astr==' '))=''; + [obj count errmsg nextidx]=sscanf(astr,'%f,',inf); + if(nextidx>=length(astr)-1) + object=reshape(obj,dim2,numel(obj)/dim2)'; + pos=endpos; + parse_char(']'); + return; + end + end + arraystr=regexprep(arraystr,'\]\s*,','];'); + try + if(isoct && regexp(arraystr,'"','once')) + error('Octave eval can produce empty cells for JSON-like input'); + end + object=eval(arraystr); + pos=endpos; + catch + while 1 + val = parse_value(varargin{:}); + object{end+1} = val; + if next_char == ']' + break; + end + parse_char(','); + end + end + end + if(jsonopt('SimplifyCell',0,varargin{:})==1) + try + oldobj=object; + object=cell2mat(object')'; + if(iscell(oldobj) && isstruct(object) && numel(object)>1 && jsonopt('SimplifyCellArray',1,varargin{:})==0) + object=oldobj; + elseif(size(object,1)>1 && ndims(object)==2) + object=object'; + end + catch + end + end + parse_char(']'); + +%%------------------------------------------------------------------------- + +function parse_char(c) + global pos inStr len + skip_whitespace; + if pos > len || inStr(pos) ~= c + error_pos(sprintf('Expected %c at position %%d', c)); + else + pos = pos + 1; + skip_whitespace; + end + +%%------------------------------------------------------------------------- + +function c = next_char + global pos inStr len + skip_whitespace; + if pos > len + c = []; + else + c = inStr(pos); + end + +%%------------------------------------------------------------------------- + +function skip_whitespace + global pos inStr len + while pos <= len && isspace(inStr(pos)) + pos = pos + 1; + end + +%%------------------------------------------------------------------------- +function str = parseStr(varargin) + global pos inStr len esc index_esc len_esc + % len, ns = length(inStr), keyboard + if inStr(pos) ~= '"' + error_pos('String starting with " expected at position %d'); + else + pos = pos + 1; + end + str = ''; + while pos <= len + while index_esc <= len_esc && esc(index_esc) < pos + index_esc = index_esc + 1; + end + if index_esc > len_esc + str = [str inStr(pos:len)]; + pos = len + 1; + break; + else + str = [str inStr(pos:esc(index_esc)-1)]; + pos = esc(index_esc); + end + nstr = length(str); switch inStr(pos) + case '"' + pos = pos + 1; + if(~isempty(str)) + if(strcmp(str,'_Inf_')) + str=Inf; + elseif(strcmp(str,'-_Inf_')) + str=-Inf; + elseif(strcmp(str,'_NaN_')) + str=NaN; + end + end + return; + case '\' + if pos+1 > len + error_pos('End of file reached right after escape character'); + end + pos = pos + 1; + switch inStr(pos) + case {'"' '\' '/'} + str(nstr+1) = inStr(pos); + pos = pos + 1; + case {'b' 'f' 'n' 'r' 't'} + str(nstr+1) = sprintf(['\' inStr(pos)]); + pos = pos + 1; + case 'u' + if pos+4 > len + error_pos('End of file reached in escaped unicode character'); + end + str(nstr+(1:6)) = inStr(pos-1:pos+4); + pos = pos + 5; + end + otherwise % should never happen + str(nstr+1) = inStr(pos), keyboard + pos = pos + 1; + end + end + error_pos('End of file while expecting end of inStr'); + +%%------------------------------------------------------------------------- + +function num = parse_number(varargin) + global pos inStr len isoct + currstr=inStr(pos:end); + numstr=0; + if(isoct~=0) + numstr=regexp(currstr,'^\s*-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+\-]?\d+)?','end'); + [num, one] = sscanf(currstr, '%f', 1); + delta=numstr+1; + else + [num, one, err, delta] = sscanf(currstr, '%f', 1); + if ~isempty(err) + error_pos('Error reading number at position %d'); + end + end + pos = pos + delta-1; + +%%------------------------------------------------------------------------- + +function val = parse_value(varargin) + global pos inStr len + % true = 1; false = 0; + + switch(inStr(pos)) + case '"' + val = parseStr(varargin{:}); + return; + case '[' + val = parse_array(varargin{:}); + return; + case '{' + val = parse_object(varargin{:}); + if isstruct(val) + if(~isempty(strmatch('x0x5F_ArrayType_',fieldnames(val), 'exact'))) + val=jstruct2array(val); + end + elseif isempty(val) + val = struct; + end + return; + case {'-','0','1','2','3','4','5','6','7','8','9'} + val = parse_number(varargin{:}); + return; + case 't' + if pos+3 <= len && strcmpi(inStr(pos:pos+3), 'true') + val = true; + pos = pos + 4; + return; + end + case 'f' + if pos+4 <= len && strcmpi(inStr(pos:pos+4), 'false') + val = false; + pos = pos + 5; + return; + end + case 'n' + if pos+3 <= len && strcmpi(inStr(pos:pos+3), 'null') + val = NaN; + pos = pos + 4; + return; + end + end + error_pos('Value expected at position %d'); +%%------------------------------------------------------------------------- + +function error_pos(msg) + global pos inStr len + poShow = max(min([pos-15 pos-1 pos pos+20],len),1); + if poShow(3) == poShow(2) + poShow(3:4) = poShow(2)+[0 -1]; % display nothing after + end + msg = [sprintf(msg, pos) ': ' ... + inStr(poShow(1):poShow(2)) '' inStr(poShow(3):poShow(4)) ]; + error( ['JSONparser:invalidFormat: ' msg] ); + +%%------------------------------------------------------------------------- + +function str = valid_field(str) +global isoct +% From MATLAB doc: field names must begin with a letter, which may be +% followed by any combination of letters, digits, and underscores. +% Invalid characters will be converted to underscores, and the prefix +% "x0x[Hex code]_" will be added if the first character is not a letter. + pos=regexp(str,'^[^A-Za-z]','once'); + if(~isempty(pos)) + if(~isoct) + str=regexprep(str,'^([^A-Za-z])','x0x${sprintf(''%X'',unicode2native($1))}_','once'); + else + str=sprintf('x0x%X_%s',char(str(1)),str(2:end)); + end + end + if(isempty(regexp(str,'[^0-9A-Za-z_]', 'once' ))) return; end + if(~isoct) + str=regexprep(str,'([^0-9A-Za-z_])','_0x${sprintf(''%X'',unicode2native($1))}_'); + else + pos=regexp(str,'[^0-9A-Za-z_]'); + if(isempty(pos)) return; end + str0=str; + pos0=[0 pos(:)' length(str)]; + str=''; + for i=1:length(pos) + str=[str str0(pos0(i)+1:pos(i)-1) sprintf('_0x%X_',str0(pos(i)))]; + end + if(pos(end)~=length(str)) + str=[str str0(pos0(end-1)+1:pos0(end))]; + end + end + %str(~isletter(str) & ~('0' <= str & str <= '9')) = '_'; + +%%------------------------------------------------------------------------- +function endpos = matching_quote(str,pos) +len=length(str); +while(pos1 && str(pos-1)=='\')) + endpos=pos; + return; + end + end + pos=pos+1; +end +error('unmatched quotation mark'); +%%------------------------------------------------------------------------- +function [endpos e1l e1r maxlevel] = matching_bracket(str,pos) +global arraytoken +level=1; +maxlevel=level; +endpos=0; +bpos=arraytoken(arraytoken>=pos); +tokens=str(bpos); +len=length(tokens); +pos=1; +e1l=[]; +e1r=[]; +while(pos<=len) + c=tokens(pos); + if(c==']') + level=level-1; + if(isempty(e1r)) e1r=bpos(pos); end + if(level==0) + endpos=bpos(pos); + return + end + end + if(c=='[') + if(isempty(e1l)) e1l=bpos(pos); end + level=level+1; + maxlevel=max(maxlevel,level); + end + if(c=='"') + pos=matching_quote(tokens,pos+1); + end + pos=pos+1; +end +if(endpos==0) + error('unmatched "]"'); +end + diff --git a/oo/jsonlab/loadubjson.m b/oo/jsonlab/loadubjson.m new file mode 100644 index 00000000..2e48c581 --- /dev/null +++ b/oo/jsonlab/loadubjson.m @@ -0,0 +1,498 @@ +function data = loadubjson(fname,varargin) +% +% data=loadubjson(fname,opt) +% or +% data=loadubjson(fname,'param1',value1,'param2',value2,...) +% +% parse a JSON (JavaScript Object Notation) file or string +% +% authors:Qianqian Fang (fangq nmr.mgh.harvard.edu) +% date: 2013/08/01 +% +% $Id: loadubjson.m 417 2014-01-21 22:34:49Z fangq $ +% +% input: +% fname: input file name, if fname contains "{}" or "[]", fname +% will be interpreted as a UBJSON string +% opt: a struct to store parsing options, opt can be replaced by +% a list of ('param',value) pairs. The param string is equivallent +% to a field in opt. +% +% output: +% dat: a cell array, where {...} blocks are converted into cell arrays, +% and [...] are converted to arrays +% +% license: +% BSD license, see LICENSE_BSD.txt files for details +% +% -- this function is part of jsonlab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab) +% + +global pos inStr len esc index_esc len_esc isoct arraytoken + +if(regexp(fname,'[\{\}\]\[]','once')) + string=fname; +elseif(exist(fname,'file')) + fid = fopen(fname,'rb'); + string = fread(fid,inf,'uint8=>char')'; + fclose(fid); +else + error('input file does not exist'); +end + +pos = 1; len = length(string); inStr = string; +isoct=exist('OCTAVE_VERSION'); +arraytoken=find(inStr=='[' | inStr==']' | inStr=='"'); +jstr=regexprep(inStr,'\\\\',' '); +escquote=regexp(jstr,'\\"'); +arraytoken=sort([arraytoken escquote]); + +% String delimiters and escape chars identified to improve speed: +esc = find(inStr=='"' | inStr=='\' ); % comparable to: regexp(inStr, '["\\]'); +index_esc = 1; len_esc = length(esc); + +opt=varargin2struct(varargin{:}); +jsoncount=1; +while pos <= len + switch(next_char) + case '{' + data{jsoncount} = parse_object(opt); + case '[' + data{jsoncount} = parse_array(opt); + otherwise + error_pos('Outer level structure must be an object or an array'); + end + jsoncount=jsoncount+1; +end % while + +jsoncount=length(data); +if(jsoncount==1 && iscell(data)) + data=data{1}; +end + +if(~isempty(data)) + if(isstruct(data)) % data can be a struct array + data=jstruct2array(data); + elseif(iscell(data)) + data=jcell2array(data); + end +end + + +%% +function newdata=parse_collection(id,data,obj) + +if(jsoncount>0 && exist('data','var')) + if(~iscell(data)) + newdata=cell(1); + newdata{1}=data; + data=newdata; + end +end + +%% +function newdata=jcell2array(data) +len=length(data); +newdata=data; +for i=1:len + if(isstruct(data{i})) + newdata{i}=jstruct2array(data{i}); + elseif(iscell(data{i})) + newdata{i}=jcell2array(data{i}); + end +end + +%%------------------------------------------------------------------------- +function newdata=jstruct2array(data) +fn=fieldnames(data); +newdata=data; +len=length(data); +for i=1:length(fn) % depth-first + for j=1:len + if(isstruct(getfield(data(j),fn{i}))) + newdata(j)=setfield(newdata(j),fn{i},jstruct2array(getfield(data(j),fn{i}))); + end + end +end +if(~isempty(strmatch('x0x5F_ArrayType_',fn)) && ~isempty(strmatch('x0x5F_ArrayData_',fn))) + newdata=cell(len,1); + for j=1:len + ndata=cast(data(j).x0x5F_ArrayData_,data(j).x0x5F_ArrayType_); + iscpx=0; + if(~isempty(strmatch('x0x5F_ArrayIsComplex_',fn))) + if(data(j).x0x5F_ArrayIsComplex_) + iscpx=1; + end + end + if(~isempty(strmatch('x0x5F_ArrayIsSparse_',fn))) + if(data(j).x0x5F_ArrayIsSparse_) + if(~isempty(strmatch('x0x5F_ArraySize_',fn))) + dim=double(data(j).x0x5F_ArraySize_); + if(iscpx && size(ndata,2)==4-any(dim==1)) + ndata(:,end-1)=complex(ndata(:,end-1),ndata(:,end)); + end + if isempty(ndata) + % All-zeros sparse + ndata=sparse(dim(1),prod(dim(2:end))); + elseif dim(1)==1 + % Sparse row vector + ndata=sparse(1,ndata(:,1),ndata(:,2),dim(1),prod(dim(2:end))); + elseif dim(2)==1 + % Sparse column vector + ndata=sparse(ndata(:,1),1,ndata(:,2),dim(1),prod(dim(2:end))); + else + % Generic sparse array. + ndata=sparse(ndata(:,1),ndata(:,2),ndata(:,3),dim(1),prod(dim(2:end))); + end + else + if(iscpx && size(ndata,2)==4) + ndata(:,3)=complex(ndata(:,3),ndata(:,4)); + end + ndata=sparse(ndata(:,1),ndata(:,2),ndata(:,3)); + end + end + elseif(~isempty(strmatch('x0x5F_ArraySize_',fn))) + if(iscpx && size(ndata,2)==2) + ndata=complex(ndata(:,1),ndata(:,2)); + end + ndata=reshape(ndata(:),data(j).x0x5F_ArraySize_); + end + newdata{j}=ndata; + end + if(len==1) + newdata=newdata{1}; + end +end + +%%------------------------------------------------------------------------- +function object = parse_object(varargin) + parse_char('{'); + object = []; + type=''; + count=-1; + if(next_char == '$') + type=inStr(pos+1); % TODO + pos=pos+2; + end + if(next_char == '#') + pos=pos+1; + count=double(parse_number()); + end + if next_char ~= '}' + num=0; + while 1 + str = parseStr(varargin{:}); + if isempty(str) + error_pos('Name of value at position %d cannot be empty'); + end + %parse_char(':'); + val = parse_value(varargin{:}); + num=num+1; + eval( sprintf( 'object.%s = val;', valid_field(str) ) ); + if next_char == '}' || (count>=0 && num>=count) + break; + end + %parse_char(','); + end + end + if(count==-1) + parse_char('}'); + end + +%%------------------------------------------------------------------------- +function [cid,len]=elem_info(type) +id=strfind('iUIlLdD',type); +dataclass={'int8','uint8','int16','int32','int64','single','double'}; +bytelen=[1,1,2,4,8,4,8]; +if(id>0) + cid=dataclass{id}; + len=bytelen(id); +else + error_pos('unsupported type at position %d'); +end +%%------------------------------------------------------------------------- + + +function [data adv]=parse_block(type,count,varargin) +global pos inStr isoct +[cid,len]=elem_info(type); +if(isoct) + data=typecast(int8(inStr(pos:pos+len*count-1)),cid); +else + data=typecast(uint8(inStr(pos:pos+len*count-1)),cid); +end +adv=double(len*count); + +%%------------------------------------------------------------------------- + + +function object = parse_array(varargin) % JSON array is written in row-major order +global pos inStr isoct + parse_char('['); + object = cell(0, 1); + dim=[]; + type=''; + count=-1; + if(next_char == '$') + type=inStr(pos+1); + pos=pos+2; + end + if(next_char == '#') + pos=pos+1; + if(next_char=='[') + dim=parse_array(varargin{:}); + count=prod(double(dim)); + else + count=double(parse_number()); + end + end + if(~isempty(type)) + if(count>=0) + [object adv]=parse_block(type,count,varargin{:}); + if(~isempty(dim)) + object=reshape(object,dim); + end + pos=pos+adv; + return; + else + endpos=matching_bracket(inStr,pos); + [cid,len]=elem_info(type); + count=(endpos-pos)/len; + [object adv]=parse_block(type,count,varargin{:}); + pos=pos+adv; + parse_char(']'); + return; + end + end + if next_char ~= ']' + while 1 + val = parse_value(varargin{:}); + object{end+1} = val; + if next_char == ']' + break; + end + %parse_char(','); + end + end + if(jsonopt('SimplifyCell',0,varargin{:})==1) + try + oldobj=object; + object=cell2mat(object')'; + if(iscell(oldobj) && isstruct(object) && numel(object)>1 && jsonopt('SimplifyCellArray',1,varargin{:})==0) + object=oldobj; + elseif(size(object,1)>1 && ndims(object)==2) + object=object'; + end + catch + end + end + if(count==-1) + parse_char(']'); + end + +%%------------------------------------------------------------------------- + +function parse_char(c) + global pos inStr len + skip_whitespace; + if pos > len || inStr(pos) ~= c + error_pos(sprintf('Expected %c at position %%d', c)); + else + pos = pos + 1; + skip_whitespace; + end + +%%------------------------------------------------------------------------- + +function c = next_char + global pos inStr len + skip_whitespace; + if pos > len + c = []; + else + c = inStr(pos); + end + +%%------------------------------------------------------------------------- + +function skip_whitespace + global pos inStr len + while pos <= len && isspace(inStr(pos)) + pos = pos + 1; + end + +%%------------------------------------------------------------------------- +function str = parseStr(varargin) + global pos inStr esc index_esc len_esc + % len, ns = length(inStr), keyboard + type=inStr(pos); + if type ~= 'S' && type ~= 'C' && type ~= 'H' + error_pos('String starting with S expected at position %d'); + else + pos = pos + 1; + end + if(type == 'C') + str=inStr(pos); + pos=pos+1; + return; + end + bytelen=double(parse_number()); + if(length(inStr)>=pos+bytelen-1) + str=inStr(pos:pos+bytelen-1); + pos=pos+bytelen; + else + error_pos('End of file while expecting end of inStr'); + end + +%%------------------------------------------------------------------------- + +function num = parse_number(varargin) + global pos inStr len isoct + id=strfind('iUIlLdD',inStr(pos)); + if(isempty(id)) + error_pos('expecting a number at position %d'); + end + type={'int8','uint8','int16','int32','int64','single','double'}; + bytelen=[1,1,2,4,8,4,8]; + if(isoct) + num=typecast(int8(inStr(pos+1:pos+bytelen(id))),type{id}); + else + num=typecast(uint8(inStr(pos+1:pos+bytelen(id))),type{id}); + end + pos = pos + bytelen(id)+1; + +%%------------------------------------------------------------------------- + +function val = parse_value(varargin) + global pos inStr len + true = 1; false = 0; + + switch(inStr(pos)) + case {'S','C','H'} + val = parseStr(varargin{:}); + return; + case '[' + val = parse_array(varargin{:}); + return; + case '{' + val = parse_object(varargin{:}); + if isstruct(val) + if(~isempty(strmatch('x0x5F_ArrayType_',fieldnames(val), 'exact'))) + val=jstruct2array(val); + end + elseif isempty(val) + val = struct; + end + return; + case {'i','U','I','l','L','d','D'} + val = parse_number(varargin{:}); + return; + case 'T' + val = true; + pos = pos + 1; + return; + case 'F' + val = false; + pos = pos + 1; + return; + case {'Z','N'} + val = []; + pos = pos + 1; + return; + end + error_pos('Value expected at position %d'); +%%------------------------------------------------------------------------- + +function error_pos(msg) + global pos inStr len + poShow = max(min([pos-15 pos-1 pos pos+20],len),1); + if poShow(3) == poShow(2) + poShow(3:4) = poShow(2)+[0 -1]; % display nothing after + end + msg = [sprintf(msg, pos) ': ' ... + inStr(poShow(1):poShow(2)) '' inStr(poShow(3):poShow(4)) ]; + error( ['JSONparser:invalidFormat: ' msg] ); + +%%------------------------------------------------------------------------- + +function str = valid_field(str) +global isoct +% From MATLAB doc: field names must begin with a letter, which may be +% followed by any combination of letters, digits, and underscores. +% Invalid characters will be converted to underscores, and the prefix +% "x0x[Hex code]_" will be added if the first character is not a letter. + pos=regexp(str,'^[^A-Za-z]','once'); + if(~isempty(pos)) + if(~isoct) + str=regexprep(str,'^([^A-Za-z])','x0x${sprintf(''%X'',unicode2native($1))}_','once'); + else + str=sprintf('x0x%X_%s',char(str(1)),str(2:end)); + end + end + if(isempty(regexp(str,'[^0-9A-Za-z_]', 'once' ))) return; end + if(~isoct) + str=regexprep(str,'([^0-9A-Za-z_])','_0x${sprintf(''%X'',unicode2native($1))}_'); + else + pos=regexp(str,'[^0-9A-Za-z_]'); + if(isempty(pos)) return; end + str0=str; + pos0=[0 pos(:)' length(str)]; + str=''; + for i=1:length(pos) + str=[str str0(pos0(i)+1:pos(i)-1) sprintf('_0x%X_',str0(pos(i)))]; + end + if(pos(end)~=length(str)) + str=[str str0(pos0(end-1)+1:pos0(end))]; + end + end + %str(~isletter(str) & ~('0' <= str & str <= '9')) = '_'; + +%%------------------------------------------------------------------------- +function endpos = matching_quote(str,pos) +len=length(str); +while(pos1 && str(pos-1)=='\')) + endpos=pos; + return; + end + end + pos=pos+1; +end +error('unmatched quotation mark'); +%%------------------------------------------------------------------------- +function [endpos e1l e1r maxlevel] = matching_bracket(str,pos) +global arraytoken +level=1; +maxlevel=level; +endpos=0; +bpos=arraytoken(arraytoken>=pos); +tokens=str(bpos); +len=length(tokens); +pos=1; +e1l=[]; +e1r=[]; +while(pos<=len) + c=tokens(pos); + if(c==']') + level=level-1; + if(isempty(e1r)) e1r=bpos(pos); end + if(level==0) + endpos=bpos(pos); + return + end + end + if(c=='[') + if(isempty(e1l)) e1l=bpos(pos); end + level=level+1; + maxlevel=max(maxlevel,level); + end + if(c=='"') + pos=matching_quote(tokens,pos+1); + end + pos=pos+1; +end +if(endpos==0) + error('unmatched "]"'); +end + diff --git a/oo/jsonlab/mergestruct.m b/oo/jsonlab/mergestruct.m new file mode 100644 index 00000000..f044866e --- /dev/null +++ b/oo/jsonlab/mergestruct.m @@ -0,0 +1,33 @@ +function s=mergestruct(s1,s2) +% +% s=mergestruct(s1,s2) +% +% merge two struct objects into one +% +% authors:Qianqian Fang (fangq nmr.mgh.harvard.edu) +% date: 2012/12/22 +% +% input: +% s1,s2: a struct object, s1 and s2 can not be arrays +% +% output: +% s: the merged struct object. fields in s1 and s2 will be combined in s. +% +% license: +% BSD license, see LICENSE_BSD.txt files for details +% +% -- this function is part of jsonlab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab) +% + +if(~isstruct(s1) || ~isstruct(s2)) + error('input parameters contain non-struct'); +end +if(length(s1)>1 || length(s2)>1) + error('can not merge struct arrays'); +end +fn=fieldnames(s2); +s=s1; +for i=1:length(fn) + s=setfield(s,fn{i},getfield(s2,fn{i})); +end + diff --git a/oo/jsonlab/savejson.m b/oo/jsonlab/savejson.m new file mode 100644 index 00000000..0a6b7da9 --- /dev/null +++ b/oo/jsonlab/savejson.m @@ -0,0 +1,385 @@ +function json=savejson(rootname,obj,varargin) +% +% json=savejson(rootname,obj,filename) +% or +% json=savejson(rootname,obj,opt) +% json=savejson(rootname,obj,'param1',value1,'param2',value2,...) +% +% convert a MATLAB object (cell, struct or array) into a JSON (JavaScript +% Object Notation) string +% +% author: Qianqian Fang (fangq nmr.mgh.harvard.edu) +% created on 2011/09/09 +% +% $Id: savejson.m 415 2013-10-07 16:38:31Z fangq $ +% +% input: +% rootname: name of the root-object, if set to '', will use variable name +% obj: a MATLAB object (array, cell, cell array, struct, struct array) +% filename: a string for the file name to save the output JSON data +% opt: a struct for additional options, use [] if all use default +% opt can have the following fields (first in [.|.] is the default) +% +% opt.FileName [''|string]: a file name to save the output JSON data +% opt.FloatFormat ['%.10g'|string]: format to show each numeric element +% of a 1D/2D array; +% opt.ArrayIndent [1|0]: if 1, output explicit data array with +% precedent indentation; if 0, no indentation +% opt.ArrayToStruct[0|1]: when set to 0, savejson outputs 1D/2D +% array in JSON array format; if sets to 1, an +% array will be shown as a struct with fields +% "_ArrayType_", "_ArraySize_" and "_ArrayData_"; for +% sparse arrays, the non-zero elements will be +% saved to _ArrayData_ field in triplet-format i.e. +% (ix,iy,val) and "_ArrayIsSparse_" will be added +% with a value of 1; for a complex array, the +% _ArrayData_ array will include two columns +% (4 for sparse) to record the real and imaginary +% parts, and also "_ArrayIsComplex_":1 is added. +% opt.ParseLogical [0|1]: if this is set to 1, logical array elem +% will use true/false rather than 1/0. +% opt.NoRowBracket [1|0]: if this is set to 1, arrays with a single +% numerical element will be shown without a square +% bracket, unless it is the root object; if 0, square +% brackets are forced for any numerical arrays. +% opt.ForceRootName [0|1]: when set to 1 and rootname is empty, savejson +% will use the name of the passed obj variable as the +% root object name; if obj is an expression and +% does not have a name, 'root' will be used; if this +% is set to 0 and rootname is empty, the root level +% will be merged down to the lower level. +% opt.Inf ['"$1_Inf_"'|string]: a customized regular expression pattern +% to represent +/-Inf. The matched pattern is '([-+]*)Inf' +% and $1 represents the sign. For those who want to use +% 1e999 to represent Inf, they can set opt.Inf to '$11e999' +% opt.NaN ['"_NaN_"'|string]: a customized regular expression pattern +% to represent NaN +% opt.JSONP [''|string]: to generate a JSONP output (JSON with padding), +% for example, if opt.JSON='foo', the JSON data is +% wrapped inside a function call as 'foo(...);' +% opt.UnpackHex [1|0]: conver the 0x[hex code] output by loadjson +% back to the string form +% opt can be replaced by a list of ('param',value) pairs. The param +% string is equivallent to a field in opt. +% output: +% json: a string in the JSON format (see http://json.org) +% +% examples: +% a=struct('node',[1 9 10; 2 1 1.2], 'elem',[9 1;1 2;2 3],... +% 'face',[9 01 2; 1 2 3; NaN,Inf,-Inf], 'author','FangQ'); +% savejson('mesh',a) +% savejson('',a,'ArrayIndent',0,'FloatFormat','\t%.5g') +% +% license: +% BSD license, see LICENSE_BSD.txt files for details +% +% -- this function is part of jsonlab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab) +% + +if(nargin==1) + varname=inputname(1); + obj=rootname; + if(isempty(varname)) + varname='root'; + end + rootname=varname; +else + varname=inputname(2); +end +if(length(varargin)==1 && ischar(varargin{1})) + opt=struct('FileName',varargin{1}); +else + opt=varargin2struct(varargin{:}); +end +opt.IsOctave=exist('OCTAVE_VERSION'); +rootisarray=0; +rootlevel=1; +forceroot=jsonopt('ForceRootName',0,opt); +if((isnumeric(obj) || islogical(obj) || ischar(obj) || isstruct(obj) || iscell(obj)) && isempty(rootname) && forceroot==0) + rootisarray=1; + rootlevel=0; +else + if(isempty(rootname)) + rootname=varname; + end +end +if((isstruct(obj) || iscell(obj))&& isempty(rootname) && forceroot) + rootname='root'; +end +json=obj2json(rootname,obj,rootlevel,opt); +if(rootisarray) + json=sprintf('%s\n',json); +else + json=sprintf('{\n%s\n}\n',json); +end + +jsonp=jsonopt('JSONP','',opt); +if(~isempty(jsonp)) + json=sprintf('%s(%s);\n',jsonp,json); +end + +% save to a file if FileName is set, suggested by Patrick Rapin +if(~isempty(jsonopt('FileName','',opt))) + fid = fopen(opt.FileName, 'wt'); + fwrite(fid,json,'char'); + fclose(fid); +end + +%%------------------------------------------------------------------------- +function txt=obj2json(name,item,level,varargin) + +if(iscell(item)) + txt=cell2json(name,item,level,varargin{:}); +elseif(isstruct(item)) + txt=struct2json(name,item,level,varargin{:}); +elseif(ischar(item)) + txt=str2json(name,item,level,varargin{:}); +else + txt=mat2json(name,item,level,varargin{:}); +end + +%%------------------------------------------------------------------------- +function txt=cell2json(name,item,level,varargin) +txt=''; +if(~iscell(item)) + error('input is not a cell'); +end + +dim=size(item); +len=numel(item); % let's handle 1D cell first +padding1=repmat(sprintf('\t'),1,level-1); +padding0=repmat(sprintf('\t'),1,level); +if(len>1) + if(~isempty(name)) + txt=sprintf('%s"%s": [\n',padding0, checkname(name,varargin{:})); name=''; + else + txt=sprintf('%s[\n',padding0); + end +elseif(len==0) + if(~isempty(name)) + txt=sprintf('%s"%s": []',padding0, checkname(name,varargin{:})); name=''; + else + txt=sprintf('%s[]',padding0); + end +end +for i=1:len + txt=sprintf('%s%s%s',txt,padding1,obj2json(name,item{i},level+(len>1),varargin{:})); + if(i1) txt=sprintf('%s\n%s]',txt,padding0); end + +%%------------------------------------------------------------------------- +function txt=struct2json(name,item,level,varargin) +txt=''; +if(~isstruct(item)) + error('input is not a struct'); +end +len=numel(item); +padding1=repmat(sprintf('\t'),1,level-1); +padding0=repmat(sprintf('\t'),1,level); +sep=','; + +if(~isempty(name)) + if(len>1) txt=sprintf('%s"%s": [\n',padding0,checkname(name,varargin{:})); end +else + if(len>1) txt=sprintf('%s[\n',padding0); end +end +for e=1:len + names = fieldnames(item(e)); + if(~isempty(name) && len==1) + txt=sprintf('%s%s"%s": {\n',txt,repmat(sprintf('\t'),1,level+(len>1)), checkname(name,varargin{:})); + else + txt=sprintf('%s%s{\n',txt,repmat(sprintf('\t'),1,level+(len>1))); + end + if(~isempty(names)) + for i=1:length(names) + txt=sprintf('%s%s',txt,obj2json(names{i},getfield(item(e),... + names{i}),level+1+(len>1),varargin{:})); + if(i1))); + if(e==len) sep=''; end + if(e1) txt=sprintf('%s\n%s]',txt,padding0); end + +%%------------------------------------------------------------------------- +function txt=str2json(name,item,level,varargin) +txt=''; +if(~ischar(item)) + error('input is not a string'); +end +item=reshape(item, max(size(item),[1 0])); +len=size(item,1); +sep=sprintf(',\n'); + +padding1=repmat(sprintf('\t'),1,level); +padding0=repmat(sprintf('\t'),1,level+1); + +if(~isempty(name)) + if(len>1) txt=sprintf('%s"%s": [\n',padding1,checkname(name,varargin{:})); end +else + if(len>1) txt=sprintf('%s[\n',padding1); end +end +isoct=jsonopt('IsOctave',0,varargin{:}); +for e=1:len + if(isoct) + val=regexprep(item(e,:),'\\','\\'); + val=regexprep(val,'"','\"'); + val=regexprep(val,'^"','\"'); + else + val=regexprep(item(e,:),'\\','\\\\'); + val=regexprep(val,'"','\\"'); + val=regexprep(val,'^"','\\"'); + end + if(len==1) + obj=['"' checkname(name,varargin{:}) '": ' '"',val,'"']; + if(isempty(name)) obj=['"',val,'"']; end + txt=sprintf('%s%s%s%s',txt,repmat(sprintf('\t'),1,level),obj); + else + txt=sprintf('%s%s%s%s',txt,repmat(sprintf('\t'),1,level+1),['"',val,'"']); + end + if(e==len) sep=''; end + txt=sprintf('%s%s',txt,sep); +end +if(len>1) txt=sprintf('%s\n%s%s',txt,padding1,']'); end + +%%------------------------------------------------------------------------- +function txt=mat2json(name,item,level,varargin) +if(~isnumeric(item) && ~islogical(item)) + error('input is not an array'); +end + +padding1=repmat(sprintf('\t'),1,level); +padding0=repmat(sprintf('\t'),1,level+1); + +if(length(size(item))>2 || issparse(item) || ~isreal(item) || ... + isempty(item) ||jsonopt('ArrayToStruct',0,varargin{:})) + if(isempty(name)) + txt=sprintf('%s{\n%s"_ArrayType_": "%s",\n%s"_ArraySize_": %s,\n',... + padding1,padding0,class(item),padding0,regexprep(mat2str(size(item)),'\s+',',') ); + else + txt=sprintf('%s"%s": {\n%s"_ArrayType_": "%s",\n%s"_ArraySize_": %s,\n',... + padding1,checkname(name,varargin{:}),padding0,class(item),padding0,regexprep(mat2str(size(item)),'\s+',',') ); + end +else + if(isempty(name)) + txt=sprintf('%s%s',padding1,matdata2json(item,level+1,varargin{:})); + else + if(numel(item)==1 && jsonopt('NoRowBracket',1,varargin{:})==1) + numtxt=regexprep(regexprep(matdata2json(item,level+1,varargin{:}),'^\[',''),']',''); + txt=sprintf('%s"%s": %s',padding1,checkname(name,varargin{:}),numtxt); + else + txt=sprintf('%s"%s": %s',padding1,checkname(name,varargin{:}),matdata2json(item,level+1,varargin{:})); + end + end + return; +end +dataformat='%s%s%s%s%s'; + +if(issparse(item)) + [ix,iy]=find(item); + data=full(item(find(item))); + if(~isreal(item)) + data=[real(data(:)),imag(data(:))]; + if(size(item,1)==1) + % Kludge to have data's 'transposedness' match item's. + % (Necessary for complex row vector handling below.) + data=data'; + end + txt=sprintf(dataformat,txt,padding0,'"_ArrayIsComplex_": ','1', sprintf(',\n')); + end + txt=sprintf(dataformat,txt,padding0,'"_ArrayIsSparse_": ','1', sprintf(',\n')); + if(size(item,1)==1) + % Row vector, store only column indices. + txt=sprintf(dataformat,txt,padding0,'"_ArrayData_": ',... + matdata2json([iy(:),data'],level+2,varargin{:}), sprintf('\n')); + elseif(size(item,2)==1) + % Column vector, store only row indices. + txt=sprintf(dataformat,txt,padding0,'"_ArrayData_": ',... + matdata2json([ix,data],level+2,varargin{:}), sprintf('\n')); + else + % General case, store row and column indices. + txt=sprintf(dataformat,txt,padding0,'"_ArrayData_": ',... + matdata2json([ix,iy,data],level+2,varargin{:}), sprintf('\n')); + end +else + if(isreal(item)) + txt=sprintf(dataformat,txt,padding0,'"_ArrayData_": ',... + matdata2json(item(:)',level+2,varargin{:}), sprintf('\n')); + else + txt=sprintf(dataformat,txt,padding0,'"_ArrayIsComplex_": ','1', sprintf(',\n')); + txt=sprintf(dataformat,txt,padding0,'"_ArrayData_": ',... + matdata2json([real(item(:)) imag(item(:))],level+2,varargin{:}), sprintf('\n')); + end +end +txt=sprintf('%s%s%s',txt,padding1,'}'); + +%%------------------------------------------------------------------------- +function txt=matdata2json(mat,level,varargin) +if(size(mat,1)==1) + pre=''; + post=''; + level=level-1; +else + pre=sprintf('[\n'); + post=sprintf('\n%s]',repmat(sprintf('\t'),1,level-1)); +end +if(isempty(mat)) + txt='null'; + return; +end +floatformat=jsonopt('FloatFormat','%.10g',varargin{:}); +formatstr=['[' repmat([floatformat ','],1,size(mat,2)-1) [floatformat sprintf('],\n')]]; + +if(nargin>=2 && size(mat,1)>1 && jsonopt('ArrayIndent',1,varargin{:})==1) + formatstr=[repmat(sprintf('\t'),1,level) formatstr]; +end +txt=sprintf(formatstr,mat'); +txt(end-1:end)=[]; +if(islogical(mat) && jsonopt('ParseLogical',0,varargin{:})==1) + txt=regexprep(txt,'1','true'); + txt=regexprep(txt,'0','false'); +end +%txt=regexprep(mat2str(mat),'\s+',','); +%txt=regexprep(txt,';',sprintf('],\n[')); +% if(nargin>=2 && size(mat,1)>1) +% txt=regexprep(txt,'\[',[repmat(sprintf('\t'),1,level) '[']); +% end +txt=[pre txt post]; +if(any(isinf(mat(:)))) + txt=regexprep(txt,'([-+]*)Inf',jsonopt('Inf','"$1_Inf_"',varargin{:})); +end +if(any(isnan(mat(:)))) + txt=regexprep(txt,'NaN',jsonopt('NaN','"_NaN_"',varargin{:})); +end + +%%------------------------------------------------------------------------- +function newname=checkname(name,varargin) +isunpack=jsonopt('UnpackHex',1,varargin{:}); +newname=name; +if(isempty(regexp(name,'0x([0-9a-fA-F]+)_','once'))) + return +end +if(isunpack) + isoct=jsonopt('IsOctave',0,varargin{:}); + if(~isoct) + newname=regexprep(name,'(^x|_){1}0x([0-9a-fA-F]+)_','${native2unicode(hex2dec($2))}'); + else + pos=regexp(name,'(^x|_){1}0x([0-9a-fA-F]+)_','start'); + pend=regexp(name,'(^x|_){1}0x([0-9a-fA-F]+)_','end'); + if(isempty(pos)) return; end + str0=name; + pos0=[0 pend(:)' length(name)]; + newname=''; + for i=1:length(pos) + newname=[newname str0(pos0(i)+1:pos(i)-1) char(hex2dec(str0(pos(i)+3:pend(i)-1)))]; + end + if(pos(end)~=length(name)) + newname=[newname str0(pos0(end-1)+1:pos0(end))]; + end + end +end + diff --git a/oo/jsonlab/saveubjson.m b/oo/jsonlab/saveubjson.m new file mode 100644 index 00000000..5242f875 --- /dev/null +++ b/oo/jsonlab/saveubjson.m @@ -0,0 +1,495 @@ +function json=saveubjson(rootname,obj,varargin) +% +% json=saveubjson(rootname,obj,filename) +% or +% json=saveubjson(rootname,obj,opt) +% json=saveubjson(rootname,obj,'param1',value1,'param2',value2,...) +% +% convert a MATLAB object (cell, struct or array) into a Universal +% Binary JSON (UBJSON) binary string +% +% author: Qianqian Fang (fangq nmr.mgh.harvard.edu) +% created on 2013/08/17 +% +% $Id: saveubjson.m 417 2014-01-21 22:34:49Z fangq $ +% +% input: +% rootname: name of the root-object, if set to '', will use variable name +% obj: a MATLAB object (array, cell, cell array, struct, struct array) +% filename: a string for the file name to save the output JSON data +% opt: a struct for additional options, use [] if all use default +% opt can have the following fields (first in [.|.] is the default) +% +% opt.FileName [''|string]: a file name to save the output JSON data +% opt.ArrayToStruct[0|1]: when set to 0, saveubjson outputs 1D/2D +% array in JSON array format; if sets to 1, an +% array will be shown as a struct with fields +% "_ArrayType_", "_ArraySize_" and "_ArrayData_"; for +% sparse arrays, the non-zero elements will be +% saved to _ArrayData_ field in triplet-format i.e. +% (ix,iy,val) and "_ArrayIsSparse_" will be added +% with a value of 1; for a complex array, the +% _ArrayData_ array will include two columns +% (4 for sparse) to record the real and imaginary +% parts, and also "_ArrayIsComplex_":1 is added. +% opt.ParseLogical [1|0]: if this is set to 1, logical array elem +% will use true/false rather than 1/0. +% opt.NoRowBracket [1|0]: if this is set to 1, arrays with a single +% numerical element will be shown without a square +% bracket, unless it is the root object; if 0, square +% brackets are forced for any numerical arrays. +% opt.ForceRootName [0|1]: when set to 1 and rootname is empty, saveubjson +% will use the name of the passed obj variable as the +% root object name; if obj is an expression and +% does not have a name, 'root' will be used; if this +% is set to 0 and rootname is empty, the root level +% will be merged down to the lower level. +% opt.JSONP [''|string]: to generate a JSONP output (JSON with padding), +% for example, if opt.JSON='foo', the JSON data is +% wrapped inside a function call as 'foo(...);' +% opt.UnpackHex [1|0]: conver the 0x[hex code] output by loadjson +% back to the string form +% opt can be replaced by a list of ('param',value) pairs. The param +% string is equivallent to a field in opt. +% output: +% json: a string in the JSON format (see http://json.org) +% +% examples: +% a=struct('node',[1 9 10; 2 1 1.2], 'elem',[9 1;1 2;2 3],... +% 'face',[9 01 2; 1 2 3; NaN,Inf,-Inf], 'author','FangQ'); +% saveubjson('mesh',a) +% saveubjson('',a,'ArrayIndent',0,'FloatFormat','\t%.5g') +% +% license: +% BSD license, see LICENSE_BSD.txt files for details +% +% -- this function is part of jsonlab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab) +% + +if(nargin==1) + varname=inputname(1); + obj=rootname; + if(isempty(varname)) + varname='root'; + end + rootname=varname; +else + varname=inputname(2); +end +if(length(varargin)==1 && ischar(varargin{1})) + opt=struct('FileName',varargin{1}); +else + opt=varargin2struct(varargin{:}); +end +opt.IsOctave=exist('OCTAVE_VERSION'); +rootisarray=0; +rootlevel=1; +forceroot=jsonopt('ForceRootName',0,opt); +if((isnumeric(obj) || islogical(obj) || ischar(obj) || isstruct(obj) || iscell(obj)) && isempty(rootname) && forceroot==0) + rootisarray=1; + rootlevel=0; +else + if(isempty(rootname)) + rootname=varname; + end +end +if((isstruct(obj) || iscell(obj))&& isempty(rootname) && forceroot) + rootname='root'; +end +json=obj2ubjson(rootname,obj,rootlevel,opt); +if(~rootisarray) + json=['{' json '}']; +end + +jsonp=jsonopt('JSONP','',opt); +if(~isempty(jsonp)) + json=[jsonp '(' json ')']; +end + +% save to a file if FileName is set, suggested by Patrick Rapin +if(~isempty(jsonopt('FileName','',opt))) + fid = fopen(opt.FileName, 'wb'); + fwrite(fid,json,'char'); + fclose(fid); +end + +%%------------------------------------------------------------------------- +function txt=obj2ubjson(name,item,level,varargin) + +if(iscell(item)) + txt=cell2ubjson(name,item,level,varargin{:}); +elseif(isstruct(item)) + txt=struct2ubjson(name,item,level,varargin{:}); +elseif(ischar(item)) + txt=str2ubjson(name,item,level,varargin{:}); +else + txt=mat2ubjson(name,item,level,varargin{:}); +end + +%%------------------------------------------------------------------------- +function txt=cell2ubjson(name,item,level,varargin) +txt=''; +if(~iscell(item)) + error('input is not a cell'); +end + +dim=size(item); +len=numel(item); % let's handle 1D cell first +padding1=''; +padding0=''; +if(len>1) + if(~isempty(name)) + txt=[S_(checkname(name,varargin{:})) '[']; name=''; + else + txt='['; + end +elseif(len==0) + if(~isempty(name)) + txt=[S_(checkname(name,varargin{:})) 'Z']; name=''; + else + txt='Z'; + end +end +for i=1:len + txt=[txt obj2ubjson(name,item{i},level+(len>1),varargin{:})]; +end +if(len>1) txt=[txt ']']; end + +%%------------------------------------------------------------------------- +function txt=struct2ubjson(name,item,level,varargin) +txt=''; +if(~isstruct(item)) + error('input is not a struct'); +end +len=numel(item); +padding1=''; +padding0=''; +sep=','; + +if(~isempty(name)) + if(len>1) txt=[S_(checkname(name,varargin{:})) '[']; end +else + if(len>1) txt='['; end +end +for e=1:len + names = fieldnames(item(e)); + if(~isempty(name) && len==1) + txt=[txt S_(checkname(name,varargin{:})) '{']; + else + txt=[txt '{']; + end + if(~isempty(names)) + for i=1:length(names) + txt=[txt obj2ubjson(names{i},getfield(item(e),... + names{i}),level+1+(len>1),varargin{:})]; + end + end + txt=[txt '}']; + if(e==len) sep=''; end +end +if(len>1) txt=[txt ']']; end + +%%------------------------------------------------------------------------- +function txt=str2ubjson(name,item,level,varargin) +txt=''; +if(~ischar(item)) + error('input is not a string'); +end +item=reshape(item, max(size(item),[1 0])); +len=size(item,1); +sep=''; + +padding1=''; +padding0=''; + +if(~isempty(name)) + if(len>1) txt=[S_(checkname(name,varargin{:})) '[']; end +else + if(len>1) txt='['; end +end +isoct=jsonopt('IsOctave',0,varargin{:}); +for e=1:len + val=item(e,:); + if(len==1) + obj=['' S_(checkname(name,varargin{:})) '' '',S_(val),'']; + if(isempty(name)) obj=['',S_(val),'']; end + txt=[txt,'',obj]; + else + txt=[txt,'',['',S_(val),'']]; + end + if(e==len) sep=''; end + txt=[txt sep]; +end +if(len>1) txt=[txt ']']; end + +%%------------------------------------------------------------------------- +function txt=mat2ubjson(name,item,level,varargin) +if(~isnumeric(item) && ~islogical(item)) + error('input is not an array'); +end + +padding1=''; +padding0=''; + +if(length(size(item))>2 || issparse(item) || ~isreal(item) || ... + isempty(item) || jsonopt('ArrayToStruct',0,varargin{:})) + cid=I_(uint32(max(size(item)))); + if(isempty(name)) + txt=['{' S_('_ArrayType_'),S_(class(item)),padding0,S_('_ArraySize_'),I_a(size(item),cid(1)) ]; + else + if(isempty(item)) + txt=[S_(checkname(name,varargin{:})),'Z']; + return; + else + txt=[S_(checkname(name,varargin{:})),'{',S_('_ArrayType_'),S_(class(item)),padding0,S_('_ArraySize_'),I_a(size(item),cid(1))]; + end + end +else + if(isempty(name)) + txt=matdata2ubjson(item,level+1,varargin{:}); + else + if(numel(item)==1 && jsonopt('NoRowBracket',1,varargin{:})==1) + numtxt=regexprep(regexprep(matdata2ubjson(item,level+1,varargin{:}),'^\[',''),']',''); + txt=[S_(checkname(name,varargin{:})) numtxt]; + else + txt=[S_(checkname(name,varargin{:})),matdata2ubjson(item,level+1,varargin{:})]; + end + end + return; +end +dataformat='%s%s%s%s%s'; + +if(issparse(item)) + [ix,iy]=find(item); + data=full(item(find(item))); + if(~isreal(item)) + data=[real(data(:)),imag(data(:))]; + if(size(item,1)==1) + % Kludge to have data's 'transposedness' match item's. + % (Necessary for complex row vector handling below.) + data=data'; + end + txt=[txt,S_('_ArrayIsComplex_'),'T']; + end + txt=[txt,S_('_ArrayIsSparse_'),'T']; + if(size(item,1)==1) + % Row vector, store only column indices. + txt=[txt,S_('_ArrayData_'),... + matdata2ubjson([iy(:),data'],level+2,varargin{:})]; + elseif(size(item,2)==1) + % Column vector, store only row indices. + txt=[txt,S_('_ArrayData_'),... + matdata2ubjson([ix,data],level+2,varargin{:})]; + else + % General case, store row and column indices. + txt=[txt,S_('_ArrayData_'),... + matdata2ubjson([ix,iy,data],level+2,varargin{:})]; + end +else + if(isreal(item)) + txt=[txt,S_('_ArrayData_'),... + matdata2ubjson(item(:)',level+2,varargin{:})]; + else + txt=[txt,S_('_ArrayIsComplex_'),'T']; + txt=[txt,S_('_ArrayData_'),... + matdata2ubjson([real(item(:)) imag(item(:))],level+2,varargin{:})]; + end +end +txt=[txt,'}']; + +%%------------------------------------------------------------------------- +function txt=matdata2ubjson(mat,level,varargin) +if(isempty(mat)) + txt='Z'; + return; +end +if(size(mat,1)==1) + level=level-1; +end +type=''; +hasnegtive=find(mat<0); +if(isa(mat,'integer') || (isfloat(mat) && all(mod(mat(:),1) == 0))) + if(isempty(hasnegtive)) + if(max(mat(:))<=2^8) + type='U'; + end + end + if(isempty(type)) + % todo - need to consider negative ones separately + id= histc(abs(max(mat(:))),[0 2^7 2^15 2^31 2^63]); + if(isempty(find(id))) + error('high-precision data is not yet supported'); + end + key='iIlL'; + type=key(find(id)); + end + txt=[I_a(mat(:),type,size(mat))]; +elseif(islogical(mat)) + logicalval='FT'; + if(numel(mat)==1) + txt=logicalval(mat+1); + else + txt=['[$U#' I_a(size(mat),'l') typecast(uint8(mat(:)'),'uint8')]; + end +else + if(numel(mat)==1) + txt=['[' D_(mat) ']']; + else + txt=D_a(mat(:),'D',size(mat)); + end +end + +%txt=regexprep(mat2str(mat),'\s+',','); +%txt=regexprep(txt,';',sprintf('],[')); +% if(nargin>=2 && size(mat,1)>1) +% txt=regexprep(txt,'\[',[repmat(sprintf('\t'),1,level) '[']); +% end +if(any(isinf(mat(:)))) + txt=regexprep(txt,'([-+]*)Inf',jsonopt('Inf','"$1_Inf_"',varargin{:})); +end +if(any(isnan(mat(:)))) + txt=regexprep(txt,'NaN',jsonopt('NaN','"_NaN_"',varargin{:})); +end + +%%------------------------------------------------------------------------- +function newname=checkname(name,varargin) +isunpack=jsonopt('UnpackHex',1,varargin{:}); +newname=name; +if(isempty(regexp(name,'0x([0-9a-fA-F]+)_','once'))) + return +end +if(isunpack) + isoct=jsonopt('IsOctave',0,varargin{:}); + if(~isoct) + newname=regexprep(name,'(^x|_){1}0x([0-9a-fA-F]+)_','${native2unicode(hex2dec($2))}'); + else + pos=regexp(name,'(^x|_){1}0x([0-9a-fA-F]+)_','start'); + pend=regexp(name,'(^x|_){1}0x([0-9a-fA-F]+)_','end'); + if(isempty(pos)) return; end + str0=name; + pos0=[0 pend(:)' length(name)]; + newname=''; + for i=1:length(pos) + newname=[newname str0(pos0(i)+1:pos(i)-1) char(hex2dec(str0(pos(i)+3:pend(i)-1)))]; + end + if(pos(end)~=length(name)) + newname=[newname str0(pos0(end-1)+1:pos0(end))]; + end + end +end +%%------------------------------------------------------------------------- +function val=S_(str) +if(length(str)==1) + val=['C' str]; +else + val=['S' I_(int32(length(str))) str]; +end +%%------------------------------------------------------------------------- +function val=I_(num) +if(~isinteger(num)) + error('input is not an integer'); +end +if(num>=0 && num<255) + val=['U' data2byte(cast(num,'uint8'),'uint8')]; + return; +end +key='iIlL'; +cid={'int8','int16','int32','int64'}; +for i=1:4 + if((num>0 && num<2^(i*8-1)) || (num<0 && num>=-2^(i*8-1))) + val=[key(i) data2byte(cast(num,cid{i}),'uint8')]; + return; + end +end +error('unsupported integer'); + +%%------------------------------------------------------------------------- +function val=D_(num) +if(~isfloat(num)) + error('input is not a float'); +end + +if(isa(num,'single')) + val=['d' data2byte(num,'uint8')]; +else + val=['D' data2byte(num,'uint8')]; +end +%%------------------------------------------------------------------------- +function data=I_a(num,type,dim,format) +id=find(ismember('iUIlL',type)); + +if(id==0) + error('unsupported integer array'); +end + +if(id==1) + data=data2byte(int8(num),'uint8'); + blen=1; +elseif(id==2) + data=data2byte(uint8(num),'uint8'); + blen=1; +elseif(id==3) + data=data2byte(int16(num),'uint8'); + blen=2; +elseif(id==4) + data=data2byte(int32(num),'uint8'); + blen=4; +elseif(id==5) + data=data2byte(int64(num),'uint8'); + blen=8; +end + +if(nargin>=3 && length(dim)>=2 && prod(dim)~=dim(2)) + format='opt'; +end +if((nargin<4 || strcmp(format,'opt')) && numel(num)>1) + if(nargin>=3 && (length(dim)==1 || (length(dim)>=2 && prod(dim)~=dim(2)))) + cid=I_(uint32(max(dim))); + data=['$' type '#' I_a(dim,cid(1)) data(:)']; + else + data=['$' type '#' I_(int32(numel(data)/blen)) data(:)']; + end + data=['[' data(:)']; +else + data=reshape(data,blen,numel(data)/blen); + data(2:blen+1,:)=data; + data(1,:)=type; + data=data(:)'; + data=['[' data(:)' ']']; +end +%%------------------------------------------------------------------------- +function data=D_a(num,type,dim,format) +id=find(ismember('dD',type)); + +if(id==0) + error('unsupported float array'); +end + +if(id==1) + data=data2byte(single(num),'uint8'); +elseif(id==2) + data=data2byte(double(num),'uint8'); +end + +if(nargin>=3 && length(dim)>=2 && prod(dim)~=dim(2)) + format='opt'; +end +if((nargin<4 || strcmp(format,'opt')) && numel(num)>1) + if(nargin>=3 && (length(dim)==1 || (length(dim)>=2 && prod(dim)~=dim(2)))) + cid=I_(uint32(max(dim))); + data=['$' type '#' I_a(dim,cid(1)) data(:)']; + else + data=['$' type '#' I_(int32(numel(data)/(id*4))) data(:)']; + end + data=['[' data]; +else + data=reshape(data,(id*4),length(data)/(id*4)); + data(2:(id*4+1),:)=data; + data(1,:)=type; + data=data(:)'; + data=['[' data(:)' ']']; +end +%%------------------------------------------------------------------------- +function bytes=data2byte(varargin) +bytes=typecast(varargin{:}); +bytes=bytes(:)'; diff --git a/oo/jsonlab/varargin2struct.m b/oo/jsonlab/varargin2struct.m new file mode 100644 index 00000000..2dae992a --- /dev/null +++ b/oo/jsonlab/varargin2struct.m @@ -0,0 +1,40 @@ +function opt=varargin2struct(varargin) +% +% opt=varargin2struct('param1',value1,'param2',value2,...) +% or +% opt=varargin2struct(...,optstruct,...) +% +% convert a series of input parameters into a structure +% +% authors:Qianqian Fang (fangq nmr.mgh.harvard.edu) +% date: 2012/12/22 +% +% input: +% 'param', value: the input parameters should be pairs of a string and a value +% optstruct: if a parameter is a struct, the fields will be merged to the output struct +% +% output: +% opt: a struct where opt.param1=value1, opt.param2=value2 ... +% +% license: +% BSD license, see LICENSE_BSD.txt files for details +% +% -- this function is part of jsonlab toolbox (http://iso2mesh.sf.net/cgi-bin/index.cgi?jsonlab) +% + +len=length(varargin); +opt=struct; +if(len==0) return; end +i=1; +while(i<=len) + if(isstruct(varargin{i})) + opt=mergestruct(opt,varargin{i}); + elseif(ischar(varargin{i}) && i0 && strcmp('untitled', plot_name) + plot_name = title; + end + + % send graph request + origin = 'plot'; + structargs = struct('layout', layout, 'filename',plot_name, 'fileopt', 'overwrite'); + obj.Response = makecall(data, obj.User, obj.Key, origin, structargs); + + if open_browser + status = dos(['open ' obj.Response.url ' > nul 2> nul']); + if status==1 + status = dos(['start ' obj.Response.url ' > nul 2> nul']); + end + end + + end + + function obj = plot(obj,varargin) + obj = plothelper(obj, @plot, varargin); + end + + % SAVE + function obj = savecredentials(obj) + % SAVECREDENTIALS Save/overwrite plotly authentication credentials + + % Call external + savecredentials(obj.User,obj.Key) + end + end + + methods(Access = private) + + function obj = plothelper(obj, fun, inputs) + % Create invisible figure and axes + hf = figure('visible','off'); + ha = axes('parent',hf); + % Invoke matlab function + fun(ha, inputs{:}) + % Convert figure + obj = obj.fig2plotly(hf); + % Cleanup + delete([hf,ha]) + end + + function obj = signup(obj) + % SIGNUP + + % Call external + response = signup; + if isempty(response), return, end + obj.User = response.un; + obj.Key = response.api_key; + end + + function obj = loadcredentials(obj) + % LOADCREDENTIALS + + % Credentials file name + userhome = getuserdir(); + if ispc + filename = fullfile(userhome, 'plotly', 'credentials'); + else + filename = fullfile(userhome,'.plotly', '.credentials'); + end + + % Check if credentials file exist + if ~exist(filename, 'file') + error('Plotly:CredentialsNotFound',... + ['It looks like you haven''t set up your plotly '... + 'account credentials yet.\nTo get started, save your '... + 'plotly username and API key by calling:\n'... + '>>> saveplotlycredentials(username, api_key)\n\n'... + 'For more help, see https://plot.ly/MATLAB or contact '... + 'chris@plot.ly.']); + end + + % Open file + fileID = fopen(filename, 'r'); + if(fileID == -1) + error('plotly:loadcredentials', ... + ['There was an error reading your credentials file at '... + filename '. Contact chris@plot.ly for support.']); + end + + % Read in credentials + credentials = fread(fileID, [1,inf],'*char'); + credentials = json2struct(credentials); + status = fclose(fileID); + + % Save state + obj.User = credentials.username; + obj.Key = credentials.api_key; + + % Printf result + if status == 0, fprintf('Credentials successfully loaded.\n'),end + end + end + +end \ No newline at end of file diff --git a/oo/private/base64decode.m b/oo/private/base64decode.m new file mode 100644 index 00000000..949a075a --- /dev/null +++ b/oo/private/base64decode.m @@ -0,0 +1,117 @@ +function y = base64decode(x, outfname, alg) +%BASE64DECODE Perform base64 decoding on a string. +% +% INPUT: +% x - block of data to be decoded. Can be a string or a numeric +% vector containing integers in the range 0-255. Any character +% not part of the 65-character base64 subset set is silently +% ignored. Characters occuring after a '=' padding character are +% never decoded. If the length of the string to decode (after +% ignoring non-base64 chars) is not a multiple of 4, then a +% warning is generated. +% +% outfname - if provided the binary date from decoded string will be +% saved into a file. Since Base64 coding is often used to embbed +% binary data in xml files, this option can be used to extract and +% save them. +% +% alg - Algorithm to use: can take values 'java' or 'matlab'. Optional +% variable defaulting to 'java' which is a little faster. If +% 'java' is chosen than core of the code is performed by a call to +% a java library. Optionally all operations can be performed using +% matleb code. +% +% OUTPUT: +% y - array of binary data returned as uint8 +% +% This function is used to decode strings from the Base64 encoding specified +% in RFC 2045 - MIME (Multipurpose Internet Mail Extensions). The Base64 +% encoding is designed to represent arbitrary sequences of octets in a form +% that need not be humanly readable. A 65-character subset ([A-Za-z0-9+/=]) +% of US-ASCII is used, enabling 6 bits to be represented per printable +% character. +% +% See also BASE64ENCODE. +% +% Written by Jarek Tuszynski, SAIC, jaroslaw.w.tuszynski_at_saic.com +% +% Matlab version based on 2004 code by Peter J. Acklam +% E-mail: pjacklam@online.no +% URL: http://home.online.no/~pjacklam +% http://home.online.no/~pjacklam/matlab/software/util/datautil/base64encode.m + +if nargin<3, alg='java'; end +if nargin<2, outfname=''; end + +%% if x happen to be a filename than read the file +if (numel(x)<256) + if (exist(x, 'file')==2) + fid = fopen(x,'rb'); + x = fread(fid, 'uint8'); + fclose(fid); + end +end +x = uint8(x(:)); % unify format + +%% Perform conversion +switch (alg) + case 'java' + base64 = org.apache.commons.codec.binary.Base64; + y = base64.decode(x); + y = mod(int16(y),256); % convert from int8 to uint8 + case 'matlab' + %% Perform the mapping + % A-Z -> 0 - 25 + % a-z -> 26 - 51 + % 0-9 -> 52 - 61 + % + - -> 62 '-' is URL_SAFE alternative + % / _ -> 63 '_' is URL_SAFE alternative + map = uint8(zeros(1,256)+65); + map(uint8(['A':'Z', 'a':'z', '0':'9', '+/=']))= 0:64; + map(uint8('-_'))= 62:63; % URL_SAFE alternatives + x = map(x); % mapping + + x(x>64)=[]; % remove non-base64 chars + if rem(numel(x), 4) + warning('Length of base64 data not a multiple of 4; padding input.'); + end + x(x==64)=[]; % remove padding characters + + %% add padding and reshape + nebytes = length(x); % number of encoded bytes + nchunks = ceil(nebytes/4); % number of chunks/groups + if rem(nebytes, 4)>0 + x(end+1 : 4*nchunks) = 0; % add padding + end + x = reshape(uint8(x), 4, nchunks); + y = repmat(uint8(0), 3, nchunks); % for the decoded data + + %% Rearrange every 4 bytes into 3 bytes + % 00aaaaaa 00bbbbbb 00cccccc 00dddddd + % to form + % aaaaaabb bbbbcccc ccdddddd + y(1,:) = bitshift(x(1,:), 2); % 6 highest bits of y(1,:) + y(1,:) = bitor(y(1,:), bitshift(x(2,:), -4)); % 2 lowest bits of y(1,:) + y(2,:) = bitshift(x(2,:), 4); % 4 highest bits of y(2,:) + y(2,:) = bitor(y(2,:), bitshift(x(3,:), -2)); % 4 lowest bits of y(2,:) + y(3,:) = bitshift(x(3,:), 6); % 2 highest bits of y(3,:) + y(3,:) = bitor(y(3,:), x(4,:)); % 6 lowest bits of y(3,:) + + %% remove extra padding + switch rem(nebytes, 4) + case 2 + y = y(1:end-2); + case 3 + y = y(1:end-1); + end +end + +%% reshape to a row vector and make it a character array +y = uint8(reshape(y, 1, numel(y))); + +%% save to file if needed +if ~isempty(outfname) + fid = fopen(outfname,'wb'); + fwrite(fid, y, 'uint8'); + fclose(fid); +end \ No newline at end of file diff --git a/oo/private/cell2json.m b/oo/private/cell2json.m new file mode 100644 index 00000000..5f8ca90a --- /dev/null +++ b/oo/private/cell2json.m @@ -0,0 +1,10 @@ +function str = cell2json(s) + str = ''; + for i =1:length(s) + val = s{i}; + valstr = m2json(val); + str = [str ', ' valstr]; + end + str = str(3:end); % snip leading comma + str = ['[' str ']']; +end \ No newline at end of file diff --git a/oo/private/checkescape.m b/oo/private/checkescape.m new file mode 100644 index 00000000..f05f5221 --- /dev/null +++ b/oo/private/checkescape.m @@ -0,0 +1,20 @@ +function [ escaped_val ] = check_escape(val) +%adds '\' escape character if needed +ec = '\'; +ind = find( (val == '"') | (val == '\' )); +if(ind) + if(ind(1) == 1) + val = ['\' val]; + ind = ind + 1; + ind(1) = []; + end + if (ind) + val = [val ec(ones(1,length(ind)))]; %extend lengh of val to prep for char shifts. + for i = 1:length(ind) + val(ind(i):end) = [ec val(ind(i):end-1)]; + ind = ind+1; + end + end +end + +escaped_val = val; \ No newline at end of file diff --git a/oo/private/convertFigure.m b/oo/private/convertFigure.m new file mode 100644 index 00000000..cf40b9a2 --- /dev/null +++ b/oo/private/convertFigure.m @@ -0,0 +1,192 @@ +function [data, layout, title] = convertFigure(f, strip_style) +% convertFigure - converts a matlab figure object into data and layout +% plotly structs. +% [data, layout] = convertFigure(f) +% f - root figure object in the form of a struct. Use f = get(gcf); to +% get the current figure struct. +% strip_style - boolean, strips all stlying to plotly defaults +% data - a cell containing plotly data structs +% layout - a plotly layout struct +% title - a string with the title of the plot +% +% For full documentation and examples, see https://plot.ly/api + + +axis_num = numel(f.Children); + +if ~strcmp('figure', f.Type) + error('Input object is not a figure') +end + +if axis_num==0 + error('Input figure object is empty!') +end + +% placeholders +data = {}; +data_counter = 1; +annotations = {}; +annot_counter = 1; +bar_counter = 0; +layout = {}; +legend={}; +x_axis={}; +y_axis={}; +colorbar=[]; +empty_axis=[]; +title = ''; + +% copy general layout fields +layout = extractLayoutGeneral(f, layout, strip_style); + +% For each axes +%TOIMPROVE: for now, reverse order of children. This works well for most +%cases, not a perfect solution. +for i=axis_num:-1:1 + %get figure child struct + m_axis = get(f.Children(i)); + + %test if legend + if strcmp('legend',m_axis.Tag) + legend = extractLegend(m_axis); + else + %test if axes + if strcmp('axes',m_axis.Type) + %extract axis and add to axis list + [xid, yid, x_axis y_axis] = extractAxes(m_axis, layout, x_axis, y_axis, strip_style); + %extract title and add to annotations + m_title = get(m_axis.Title); + annot_tmp = extractTitle(m_title, x_axis{xid}, y_axis{yid}, strip_style); + if numel(annot_tmp)>0 + annotations{annot_counter} = annot_tmp; + annot_counter = annot_counter+1; + title = annot_tmp.text; + end + data_num = numel(m_axis.Children); + if data_num>0 + % For each data object in a given axes + for j=1:data_num + m_data = get(m_axis.Children(j)); + + %conditions for deretmining the data type + data_type = findDataType(m_data, m_axis); + + if strcmp('box',data_type) + datas = extractDataBox(m_data, xid, yid, m_axis.CLim, f.Colormap, strip_style); + for dt=1:numel(datas) + data{data_counter} = datas{dt}; + data_counter = data_counter+1; + end + end + if strcmp('heatmap',data_type) + data{data_counter} = extractDataHeatMap(m_data, xid, yid, m_axis.CLim, f.Colormap, strip_style); + data_counter = data_counter+1; + end + if strcmp('contour',data_type) + data{data_counter} = extractDataContourMap(m_data, xid, yid, m_axis.CLim, f.Colormap, strip_style); + data_counter = data_counter+1; + end + if strcmp('colorbar',data_type) + empty_axis=[empty_axis; xid, yid]; + colorbar = extractColorBar(m_axis, strip_style); + end + if strcmp('scatter',data_type) + data{data_counter} = extractDataScatter(m_data, xid, yid, m_axis.CLim, f.Colormap, strip_style); + %account for datetime + data{data_counter} = dateTimeScale(m_axis, x_axis{xid}, y_axis{yid},data{data_counter}); + data_counter = data_counter+1; + end + if strcmp('annotation',data_type) + annot_tmp = extractDataAnnotation(m_data, xid, yid, strip_style); + if numel(annot_tmp)>0 + annotations{annot_counter} = annot_tmp; + annot_counter = annot_counter+1; + end + end + if strcmp('histogram',data_type) + [data{data_counter} layout] = extractDataHist(m_data, layout, xid, yid, m_axis.CLim, f.Colormap, strip_style); + data_counter = data_counter+1; + bar_counter = bar_counter+1; + end + if strcmp('area',data_type) + data{data_counter} = extractDataScatter(m_data, xid, yid, m_axis.CLim, f.Colormap, strip_style); + data{data_counter} = parseFill(m_data, data{data_counter}, m_axis.CLim, f.Colormap, strip_style); + %account for datetime + data{data_counter} = dateTimeScale(m_axis, x_axis{xid}, y_axis{yid},data{data_counter}); + data_counter = data_counter+1; + end + if strcmp('bar',data_type) + [data{data_counter} layout] = extractDataBar(m_data, layout, xid, yid, m_axis.CLim, f.Colormap, strip_style); + data_counter = data_counter+1; + bar_counter = bar_counter+1; + end + + + end + end + + + end + end +end + +% MODIFY WHEN MULTIPLE BARS +if bar_counter>1 && strcmp(layout.barmode, 'group') + layout.bargroupgap = layout.bargap; + layout.bargap = 0.3; +end + +% INSERT COLORBAR IN THE FIRST HEATMAP DATA STRUCT +ptr = 1; +while ptr<=numel(data) + if strcmp('heatmap', data{ptr}.type) || strcmp('contour', data{ptr}.type) + if numel(colorbar)>0 + data{ptr}.colorbar = colorbar; + data{ptr}.showscale = true; + end + break; + end + ptr = ptr+1; +end + +% ANNOTATIONS +layout.annotations = annotations; + + +% LEGEND +if numel(legend)==0 + layout.showlegend = false; +else + layout.legend = legend; + layout.showlegend = true; +end + + +% ASSEMBLE AXIS +% rescale domain of after removal of empty axis (if any) +if numel(empty_axis)>0 + [x_axis, y_axis] = reevaluateDomains(x_axis, y_axis, empty_axis); +end + +for i = 1:numel(x_axis) + if numel(empty_axis)==0 || ~any(empty_axis(:,1)==i) + if i==1 + eval('layout.xaxis=x_axis{1};') + else + eval(['layout.xaxis' num2str(i) '=x_axis{' num2str(i) '};']) + end + end +end + +for i = 1:numel(y_axis) + if numel(empty_axis)==0 || ~any(empty_axis(:,2)==i) + if i==1 + eval('layout.yaxis=y_axis{1};') + else + eval(['layout.yaxis' num2str(i) '=y_axis{' num2str(i) '};']) + end + end +end + + +end \ No newline at end of file diff --git a/oo/private/dateTimeScale.m b/oo/private/dateTimeScale.m new file mode 100644 index 00000000..1af6dc87 --- /dev/null +++ b/oo/private/dateTimeScale.m @@ -0,0 +1,33 @@ +function data = dateTimeScale(a, xaxis, yaxis,data) + + +if strcmp(xaxis.type, 'date') + + %rescale data + + x_range = a.XLim(2)-a.XLim(1); + x_start = a.XLim(1); + + xaxis_range = xaxis.range(2)-xaxis.range(1); + xaxis_start = xaxis.range(1); + + data.x = (data.x - x_start)* xaxis_range/x_range + xaxis_start; + +end + +if strcmp(yaxis.type, 'date') + + %rescale data + + y_range = a.YLim(2)-a.YLim(1); + y_start = a.YLim(1); + + yaxis_range = yaxis.range(2)-yaxis.range(1); + yaxis_start = yaxis.range(1); + + data.x = (data.y - y_start)* yaxis_range/y_range + yaxis_start; + +end + + +end \ No newline at end of file diff --git a/oo/private/extractAxes.m b/oo/private/extractAxes.m new file mode 100644 index 00000000..b137a6ab --- /dev/null +++ b/oo/private/extractAxes.m @@ -0,0 +1,102 @@ +function [xid, yid, x_axis y_axis] = extractAxes(a, layout, x_axis, y_axis, strip_style) +% extractAxes - create an axes struct +% [xid, yid, x_axis y_axis] = extractAxes(a, layout, x_axis, y_axis) +% a - a data struct from matlab describing an axes +% layout - a plotly layout strcut +% x_axis, y_axis - current cells containing axis objects +% xid,yid - reference axis indices +% +% For full documentation and examples, see https://plot.ly/api + + +xaxes={}; +yaxes={}; + +%copy over general properties + +[xaxes, yaxes] = extractAxesGeneral(a, layout, xaxes, yaxes, strip_style); + + +%OVERLAY CHECK +x_bounds = xaxes.domain; +y_bounds = yaxes.domain; +x_check=0;x_duplicate=0; +y_check=0;y_duplicate=0; +for i=1:numel(x_axis) + if x_bounds(1) == x_axis{i}.domain(1) && x_bounds(2) == x_axis{i}.domain(2) + x_check = i; + if strcmp(a.XAxisLocation, x_axis{i}.side) + if sum(a.XLim == x_axis{i}.range)==2 + x_duplicate=1; + end + end + end +end +for i=1:numel(y_axis) + if y_bounds(1) == y_axis{i}.domain(1) && y_bounds(2) == y_axis{i}.domain(2) + y_check = i; + if strcmp(a.YAxisLocation, y_axis{i}.side) + if sum(a.YLim == y_axis{i}.range)==2 + y_duplicate=1; + end + end + end +end + + +if x_check>0 && y_check>0 + + %anchors + ax_num = x_check; + if x_check==1 + ax_num = []; + end + ay_num = y_check; + if y_check==1 + ay_num = []; + end + xaxes.overlaying = ['x' num2str(ax_num)]; + yaxes.overlaying = ['y' num2str(ay_num)]; + xaxes.anchor = ['y' num2str(ay_num)]; + yaxes.anchor = ['x' num2str(ax_num)]; + + xaxes.mirror = false; + yaxes.mirror = false; + + % some overlay happens + if x_duplicate==1 + %xaxes will not be added + xid = x_check; + else + x_axis{numel(x_axis)+1} = xaxes; + xid = numel(x_axis); + end + if y_duplicate==1 + %yaxes will not be added + yid = y_check; + else + y_axis{numel(y_axis)+1} = yaxes; + yid = numel(y_axis); + end + + +else + + ax_num = numel(x_axis)+1; + if numel(x_axis)==0 + ax_num = []; + end + ay_num = numel(y_axis)+1; + if numel(y_axis)==0 + ay_num = []; + end + xaxes.anchor = ['y' num2str(ay_num)]; + yaxes.anchor = ['x' num2str(ax_num)]; + % add both + x_axis{numel(x_axis)+1} = xaxes; + y_axis{numel(y_axis)+1} = yaxes; + xid = numel(x_axis); + yid = numel(y_axis); +end + +end \ No newline at end of file diff --git a/oo/private/extractAxesGeneral.m b/oo/private/extractAxesGeneral.m new file mode 100644 index 00000000..74625768 --- /dev/null +++ b/oo/private/extractAxesGeneral.m @@ -0,0 +1,171 @@ +function [xaxes, yaxes] = extractAxesGeneral(a, layout, xaxes, yaxes, strip_style) +% extractAxesGeneral - copy general axes struct attributes +% [xaxes, yaxes] = extractAxesGeneral(a, layout, xaxes, yaxes) +% a - a data struct from matlab describing an axes +% layout - a plotly layout strcut +% x_axis, y_axis - axis objects +% +% For full documentation and examples, see https://plot.ly/api + +%POSITION +if ~strip_style + xaxes.domain = [a.Position(1) a.Position(1)+a.Position(3)]; + yaxes.domain = [a.Position(2) a.Position(2)+a.Position(4)] ... + *(layout.height-layout.margin.t)/layout.height; +else + xaxes.domain = [a.Position(1) a.Position(1)+a.Position(3)]; + yaxes.domain = [a.Position(2) a.Position(2)+a.Position(4)]; +end +if yaxes.domain(1)>1 + yaxes.domain(1)=1; +end +if yaxes.domain(2)>1 + yaxes.domain(2)=1; +end +xaxes.side = a.XAxisLocation; +yaxes.side = a.YAxisLocation; + +if ~strip_style + xaxes.showline = true; + yaxes.showline = true; + %TICKS + if strcmp(a.TickDir, 'in') + xaxes.ticks = 'inside'; + yaxes.ticks = 'inside'; + else + xaxes.ticks = 'outside'; + yaxes.ticks = 'outside'; + end + total_length = max(layout.height*(yaxes.domain(2)-yaxes.domain(1)), ... + layout.width*(xaxes.domain(2)-xaxes.domain(1)))*a.TickLength(1); + xaxes.ticklen = total_length; + yaxes.ticklen = total_length; + if strcmp(a.Box,'on') + xaxes.mirror = 'ticks'; + yaxes.mirror = 'ticks'; + else + xaxes.mirror = false; + yaxes.mirror = false; + end + + %TODO: should this multiplier remain? + if strcmp(a.FontUnits, 'points') + xaxes.tickfont.size = 1.3*a.FontSize; + yaxes.tickfont.size = 1.3*a.FontSize; + end + + %LINES + if strcmp(a.XGrid, 'on') || strcmp(a.XMinorGrid, 'on') + xaxes.showgrid = true; + else + xaxes.showgrid = false; + end + if strcmp(a.YGrid, 'on') || strcmp(a.YMinorGrid, 'on') + yaxes.showgrid = true; + else + yaxes.showgrid = false; + end + xaxes.zeroline = false; + yaxes.zeroline = false; + + %COLORS + xaxes.linecolor = parseColor(a.XColor); + xaxes.tickcolor = parseColor(a.XColor); + xaxes.tickfont.color = parseColor(a.XColor); + yaxes.linecolor = parseColor(a.YColor); + yaxes.tickcolor = parseColor(a.YColor); + yaxes.tickfont.color = parseColor(a.YColor); + +end + + + + +%SCALE +xaxes.type = a.XScale; +yaxes.type = a.YScale; +xaxes.range = a.XLim; +yaxes.range = a.YLim; +if strcmp(a.XDir, 'reverse') + xaxes.range = [a.XLim(2) a.XLim(1)]; +end +if strcmp(a.YDir, 'reverse') + yaxes.range = [a.YLim(2) a.YLim(1)]; +end + +if strcmp('log', xaxes.type) + xaxes.range = log10(xaxes.range); +end +if strcmp('log', yaxes.type) + yaxes.range = log10(yaxes.range); +end +if strcmp('linear', xaxes.type) + if numel(a.XTick)>1 + xaxes.tick0 = a.XTick(1); + xaxes.dtick = a.XTick(2)-a.XTick(1); + xaxes.autotick = false; + else + xaxes.autotick = true; + end +end +if strcmp('linear', yaxes.type) + if numel(a.YTick)>1 + yaxes.tick0 = a.YTick(1); + yaxes.dtick = a.YTick(2)-a.YTick(1); + yaxes.autotick = false; + else + yaxes.autotick = true; + end +end +%TOIMPROVE: check if the axis is a datetime. There is no implementatin for +%type category yet. +if numel(a.XTickLabel)>0 + [start, finish, t0, tstep] = extractDateTicks(a.XTickLabel, a.XTick); + if numel(start)>0 + xaxes.type = 'date'; + xaxes.range = [start, finish]; + xaxes.tick0 = t0; + xaxes.dtick = tstep; + xaxes.autotick = true; + end +end +if numel(a.YTickLabel)>0 + [start, finish, t0, tstep] = extractDateTicks(a.YTickLabel, a.YTick); + if numel(start)>0 + yaxes.type = 'date'; + yaxes.range = [start, finish]; + yaxes.tick0 = t0; + yaxes.dtick = tstep; + yaxes.autotick = true; + end +end + +%LABELS +if numel(a.XLabel)==1 + m_title = get(a.XLabel); + if numel(m_title.String)>0 + xaxes.title = parseText(m_title.String); + if ~strip_style + if strcmp(m_title.FontUnits, 'points') + xaxes.titlefont.size = 1.3*m_title.FontSize; + end + xaxes.titlefont.color = parseColor(m_title.Color); + end + end +end + +if numel(a.YLabel)==1 + m_title = get(a.YLabel); + if numel(m_title.String)>0 + yaxes.title = parseText(m_title.String); + if ~strip_style + if strcmp(m_title.FontUnits, 'points') + yaxes.titlefont.size = 1.3*m_title.FontSize; + end + yaxes.titlefont.color = parseColor(m_title.Color); + end + end +end + + +end \ No newline at end of file diff --git a/oo/private/extractColorBar.m b/oo/private/extractColorBar.m new file mode 100644 index 00000000..41c23674 --- /dev/null +++ b/oo/private/extractColorBar.m @@ -0,0 +1,61 @@ +function colorbar = extractColorBar(d, strip_style) +% extractColorBar - create a data struct for the colorbar +% data = extractDataHeatMap(d, strip_style) +% d - a data struct from matlab describing a scatter plot +% strip_style - boolean, if true, uses default plotly styling +% +% For full documentation and examples, see https://plot.ly/api + +colorbar = {}; + +if ~strip_style + + colorbar.titleside = 'right'; + colorbar.xanchor='right'; + %TICKS + if strcmp(d.TickDir, 'in') + colorbar.ticks = 'inside'; + else + colorbar.ticks = 'outside'; + end + + %SIZE AND TICKS + colorbar.xpad = 0; + colorbar.ypad = 0; + + colorbar.lenmode = 'fraction'; + colorbar.thicknessmode = 'fraction'; + + %matlab colorbars can be vertical or horizontal, we need to find the + %dominant dimension and output the plotly vertical colorbar with that + %dimension's properties + + color_ticks = []; + if numel(d.XTick)>numel(d.YTick) + color_ticks = d.XTick; + colorbar.thickness = d.Position(4); + colorbar.len = d.Position(3); + else + color_ticks = d.YTick; + colorbar.thickness = d.Position(3); + colorbar.len = d.Position(4); + end + colorbar.autotick = false; + if numel(color_ticks)>0 + colorbar.tick0 = color_ticks(1); + colorbar.dtick = color_ticks(2)-color_ticks(1); + colorbar.nticks = numel(color_ticks); + end + + %TODO: should this multiplier remain? + if strcmp(d.FontUnits, 'points') + colorbar.tickfont.size = 1.3*d.FontSize; + end + %TODO: additional font properties (when implemented, needs to keep in + %mind that not all matlab fonts are supported by plotly) + + + +end + +end \ No newline at end of file diff --git a/oo/private/extractDataAnnotation.m b/oo/private/extractDataAnnotation.m new file mode 100644 index 00000000..a1dbdeb6 --- /dev/null +++ b/oo/private/extractDataAnnotation.m @@ -0,0 +1,52 @@ +function data = extractDataAnnotation(d, xid, yid, strip_style) +% extractDataAnnotation - create a general purpose annotation struct +% [data] = extractDataAnnotation(d, xid, yid) +% xid,yid - reference axis indices +% d - a data struct from matlab describing an annotation +% data - a plotly annotation struct +% +% For full documentation and examples, see https://plot.ly/api + +data = {}; + +if numel(d.String)==0 + return +end + +% set reference axis +if xid==1 + xid=[]; +end +if yid==1 + yid=[]; +end +data.xref = ['x' num2str(xid)]; +data.yref = ['y' num2str(yid)]; + +%TEXT +data.text = parseText(d.String); +if ~strip_style + if strcmp(d.FontUnits, 'points') + data.font.size = 1.3*d.FontSize; + end + data.font.color = parseColor(d.Color); + %TODO: add font type +end + +%POSITION +%use center of bounding box as reference +data.x = d.Extent(1)+d.Extent(3)/2; +data.y = d.Extent(2)+d.Extent(4)/2; +data.align = d.HorizontalAlignment; +data.xanchor = 'center'; +data.yanchor = 'middle'; + +%ARROW +data.showarrow = false; + +%TODO: if visible, set ax, ay + + + + +end \ No newline at end of file diff --git a/oo/private/extractDataBar.m b/oo/private/extractDataBar.m new file mode 100644 index 00000000..c618c6c7 --- /dev/null +++ b/oo/private/extractDataBar.m @@ -0,0 +1,79 @@ +function [data, layout] = extractDataBar(d, layout, xid, yid, CLim, colormap, strip_style) +% extractDataScatter - create a data struct for scatter plots +% [data, layout] = extractDataBar(d, layout, xid, yid, CLim, colormap) +% d - a data struct from matlab describing a scatter plot +% layout - a layout strcut +% xid,yid - reference axis indices +% CLim - a 1x2 vector of extents of the color map +% colormap - a kx3 matrix representing the colormap +% data - a data strcut +% +% For full documentation and examples, see https://plot.ly/api + +data = {}; + +% copy general +data = extractDataGeneral(d, data); + +% copy in data type and values +data.type = 'bar'; + +% set reference axis +if xid==1 + xid=[]; +end +if yid==1 + yid=[]; +end +data.xaxis = ['x' num2str(xid)]; +data.yaxis = ['y' num2str(yid)]; + +if strcmp(d.BarLayout,'grouped') + layout.barmode='group'; +end +if strcmp(d.BarLayout,'stacked') + layout.barmode='stack'; +end + +layout.bargap = 1-d.BarWidth; + +%other attributes +if ~strip_style + m_child = get(d.Children(1)); + if isfield(m_child, 'CData') + color_ref = m_child.CData; + else + color_ref = m_child.Color; + end + + color_field=[]; + if isfield(d, 'Color') + color_field = d.Color; + else + if isfield(d, 'EdgeColor') + color_field = d.EdgeColor; + end + end + colors = setColorProperty(color_field, color_ref, CLim, colormap); + if numel(colors{1})>0 + data.marker.line.color = colors{1}; + end + + color_field=[]; + if isfield(d, 'Color') + color_field = d.Color; + else + if isfield(d, 'FaceColor') + color_field = d.FaceColor; + end + end + colors = setColorProperty(color_field, color_ref, CLim, colormap); + if numel(colors{1})>0 + data.marker.color = colors{1}; + end + + data.marker.line.width = d.LineWidth; + +end + +end \ No newline at end of file diff --git a/oo/private/extractDataBox.m b/oo/private/extractDataBox.m new file mode 100644 index 00000000..e2f133df --- /dev/null +++ b/oo/private/extractDataBox.m @@ -0,0 +1,121 @@ +function datas = extractDataBox(d, xid, yid, CLim, colormap, strip_style) +% extractDataBox - create a data struct for box plots +% data = extractDataHeatMap(d, xid, yid, CLim, colormap) +% d - a data struct from matlab describing a scatter plot +% xid,yid - reference axis indices +% CLim - a 1x2 vector of extents of the color map +% colormap - a kx3 matrix representing the colormap +% +% For full documentation and examples, see https://plot.ly/api + +datas = {}; + +%GENERAL IDEA: matlab box plot figures are a collection of traces +%representing the result of the box plot analysis. They describe the data +%in terms of percentiles and outliers, whithout keeping the underlying +%data. For each box, a total of 8 traces are generated. Each trace is a +%type of scatter plot. For styling purposes, each trace could be parsed +%using extractDataScatter. However, currently, plotly supports only a +%limited set of styling options, thus, for some traces, a simpler parse is +%used. + +%TOIMPROVE: as plotly evolves, each trace could be fully parsed for +%styling. + +%TOIMPORVE: for now, assume that the number of children is a multiple of 8 +if mod(numel(d.Children),8)~=0 + return +end + +num_boxes = numel(d.Children)/8; + +%each box is it's own data struct + +for b=1:num_boxes + + + %COMMON PARAMETERS + + data = {}; + + % copy in data type and values + data.type = 'box'; + + % set reference axis + if xid==1 + xid=[]; + end + if yid==1 + yid=[]; + end + data.xaxis = ['x' num2str(xid)]; + data.yaxis = ['y' num2str(yid)]; + + %TOIMPORVE: for now assume that all boxes are visible (should generally + %be the case) + data.visible = true; + + id_basis = num_boxes-b+1; + + outliers = []; + md=[];p25=[];p75=[];lev=[];uel=[]; + + for c=1:8 + dc = get(d.Children(num_boxes*(c-1) + id_basis)); + + %BOX NAME + if strcmp('text',dc.Type) + data.name = dc.String; + end + + %RECORD OUTLIERS + if strcmp('Outliers',dc.Tag) + if ~isnan(dc.YData) + outliers = dc.YData; + %TODO: extract marker style + marker_data = extractDataScatter(dc, xid, yid, CLim, colormap, strip_style); + end + end + %RECORD MEDIAN + if strcmp('Median',dc.Tag) + md = dc.YData(1); + end + %RECORD 25 and 75 % + if strcmp('Box',dc.Tag) + p25 = min(dc.YData); + p75 = max(dc.YData); + line_data = extractDataScatter(dc, xid, yid, CLim, colormap, strip_style); + end + %RECORD EXTREME VALUES + if strcmp('Upper Adjacent Value',dc.Tag) + uev = dc.YData(1); + end + if strcmp('Lower Adjacent Value',dc.Tag) + lev = dc.YData(1); + end + + end + + %GENERATE DATA + %TOIMPORVE: for now, given the statisitcal values, some dummy data is + %generated as to replocate exactly these results + + data.y = generateBoxData(outliers, lev, p25, md, p75, uev); + + + %styiling + data.marker = marker_data.marker; + data.line = line_data.line; + %TOIMPORVE: fill color might be an option in matlab, have not seen it + %yet, so default to transparent + data.fillcolor = 'rgba(0, 0, 0, 0)'; + + datas{b}=data; + + + +end + + + +end \ No newline at end of file diff --git a/oo/private/extractDataContourMap.m b/oo/private/extractDataContourMap.m new file mode 100644 index 00000000..df29319b --- /dev/null +++ b/oo/private/extractDataContourMap.m @@ -0,0 +1,77 @@ +function data = extractDataContourMap(d, xid, yid, CLim, colormap, strip_style) +% extractDataContourMap - create a data struct for contour maps +% data = extractDataContourMap(d, xid, yid, CLim, colormap) +% d - a data struct from matlab describing a scatter plot +% xid,yid - reference axis indices +% CLim - a 1x2 vector of extents of the color map +% colormap - a kx3 matrix representing the colormap +% +% For full documentation and examples, see https://plot.ly/api + +data = {}; + +% copy general +if strcmp('on', d.Visible) + data.visible = true; +else + data.visible = false; +end + +if numel(d.DisplayName)>0 + data.name = parseText(d.DisplayName); +else + data.showlegend = false; +end + +% copy in data type and values +data.type = 'contour'; +data.showscale = false; + +% set reference axis +if xid==1 + xid=[]; +end +if yid==1 + yid=[]; +end +data.xaxis = ['x' num2str(xid)]; +data.yaxis = ['y' num2str(yid)]; + + +%other attributes + +data.z = d.ZData; +min_x = min(min(d.XData)); +min_y = min(min(d.YData)); +max_x = max(max(d.XData)); +max_y = max(max(d.YData)); + +if ~strip_style + + data.zmin = CLim(1); + data.zmax = CLim(2); + + data.zauto = false; + + data.scl = {}; + for i=1:size(colormap,1) + data.scl{i} = {(i-1)/(size(colormap,1)-1), parseColor(colormap(i,:))}; + end + + data.colorbar = {}; + + +end + +%contour attributes +data.autocontour = false; +data.contours = struct( 'start', d.LevelList(1), 'size', d.LevelStep, ... + 'end', d.LevelList(end)); + +data.dx = (max_x-min_x)/size(d.ZData,1); +data.dy = (max_y-min_y)/size(d.ZData,2); +data.x0 = min_x; +data.y0 = min_y; + + +end \ No newline at end of file diff --git a/oo/private/extractDataGeneral.m b/oo/private/extractDataGeneral.m new file mode 100644 index 00000000..e2a5b382 --- /dev/null +++ b/oo/private/extractDataGeneral.m @@ -0,0 +1,24 @@ +function data = extractDataGeneral(d, data) +% extractDataGeneral - copy general data struct attributes +% data = extractDataGeneral(d, data) +% d - a data struct from matlab describing data +% data - a plotly data struct +% +% For full documentation and examples, see https://plot.ly/api + +data.x = d.XData; +data.y = d.YData; +if strcmp('on', d.Visible) + data.visible = true; +else + data.visible = false; +end + +if numel(d.DisplayName)>0 + data.name = parseText(d.DisplayName); +else + data.showlegend = false; +end + + +end \ No newline at end of file diff --git a/oo/private/extractDataHeatMap.m b/oo/private/extractDataHeatMap.m new file mode 100644 index 00000000..eeb997f3 --- /dev/null +++ b/oo/private/extractDataHeatMap.m @@ -0,0 +1,63 @@ +function data = extractDataHeatMap(d, xid, yid, CLim, colormap, strip_style) +% extractDataHeatMap - create a data struct for heat maps +% data = extractDataHeatMap(d, xid, yid, CLim, colormap) +% d - a data struct from matlab describing a scatter plot +% xid,yid - reference axis indices +% CLim - a 1x2 vector of extents of the color map +% colormap - a kx3 matrix representing the colormap +% +% For full documentation and examples, see https://plot.ly/api + +data = {}; + +% copy general +if strcmp('on', d.Visible) + data.visible = true; +else + data.visible = false; +end + +if numel(d.DisplayName)>0 + data.name = parseText(d.DisplayName); +else + data.showlegend = false; +end + +% copy in data type and values +data.type = 'heatmap'; +data.showscale = false; + +% set reference axis +if xid==1 + xid=[]; +end +if yid==1 + yid=[]; +end +data.xaxis = ['x' num2str(xid)]; +data.yaxis = ['y' num2str(yid)]; + + +%other attributes + +data.z = d.CData; + +if ~strip_style + + data.zmin = CLim(1); + data.zmax = CLim(2); + + data.zauto = false; + + data.scl = {}; + for i=1:size(colormap,1) + data.scl{i} = {(i-1)/(size(colormap,1)-1), parseColor(colormap(i,:))}; + end + + data.colorbar = {}; + + +end + + +end \ No newline at end of file diff --git a/oo/private/extractDataHist.m b/oo/private/extractDataHist.m new file mode 100644 index 00000000..b05724e8 --- /dev/null +++ b/oo/private/extractDataHist.m @@ -0,0 +1,75 @@ +function [data, layout] = extractDataBar(d, layout, xid, yid, CLim, colormap, strip_style) +% extractDataScatter - create a data struct for scatter plots +% [data, layout] = extractDataBar(d, layout, xid, yid, CLim, colormap) +% d - a data struct from matlab describing a scatter plot +% layout - a layout strcut +% xid,yid - reference axis indices +% CLim - a 1x2 vector of extents of the color map +% colormap - a kx3 matrix representing the colormap +% data - a data strcut +% +% For full documentation and examples, see https://plot.ly/api + +data = {}; + +% copy general +data = extractDataGeneral(d, data); + +data.x = (d.XData(2,:)+d.XData(3,:))/2; +data.y = d.YData(2,:); + +% copy in data type and values +data.type = 'bar'; + +% set reference axis +if xid==1 + xid=[]; +end +if yid==1 + yid=[]; +end +data.xaxis = ['x' num2str(xid)]; +data.yaxis = ['y' num2str(yid)]; + +layout.bargap = (d.XData(3,1)-d.XData(2,2))/(d.XData(3,1)-d.XData(2,1)); + + +%other attributes +if ~strip_style + if isfield(d, 'CData') + color_ref = d.CData(2,:); + else + color_ref = d.Color; + end + + color_field=[]; + if isfield(d, 'Color') + color_field = d.Color; + else + if isfield(d, 'EdgeColor') + color_field = d.EdgeColor; + end + end + colors = setColorProperty(color_field, color_ref, CLim, colormap); + if numel(colors{1})>0 + data.marker.line.color = colors{1}; + end + + color_field=[]; + if isfield(d, 'Color') + color_field = d.Color; + else + if isfield(d, 'FaceColor') + color_field = d.FaceColor; + end + end + colors = setColorProperty(color_field, color_ref, CLim, colormap); + if numel(colors{1})>0 + data.marker.color = colors{1}; + end + + data.marker.line.width = d.LineWidth; + +end + +end \ No newline at end of file diff --git a/oo/private/extractDataScatter.m b/oo/private/extractDataScatter.m new file mode 100644 index 00000000..dedf9d9c --- /dev/null +++ b/oo/private/extractDataScatter.m @@ -0,0 +1,91 @@ +function data = extractDataScatter(d, xid, yid, CLim, colormap, strip_style) +% extractDataScatter - create a data struct for scatter plots +% data = extractDataScatter(d, xid, yid, CLim, colormap) +% d - a data struct from matlab describing a scatter plot +% xid,yid - reference axis indices +% CLim - a 1x2 vector of extents of the color map +% colormap - a kx3 matrix representing the colormap +% +% For full documentation and examples, see https://plot.ly/api + +data = {}; + +% copy general +data = extractDataGeneral(d, data); + +% copy in data type and values +data.type = 'scatter'; + +% set reference axis +if xid==1 + xid=[]; +end +if yid==1 + yid=[]; +end +data.xaxis = ['x' num2str(xid)]; +data.yaxis = ['y' num2str(yid)]; + + +%other attributes + +marker_bool = false; +line_bool = false; +if isfield(d, 'Marker') && ~strcmp('none', d.Marker) + marker_bool = true; + if ~strip_style + marker_str = parseMarker(d,CLim, colormap); + + + if numel(marker_str)~=0 + data.marker = marker_str; + end + end +end + +if isfield(d, 'LineStyle') && ~strcmp('none', d.LineStyle) + line_bool = true; + if ~strip_style + line_str = parseLine(d); + + + if numel(line_str)~=0 + data.line = line_str; + end + end +end + +%define mode +if marker_bool && line_bool + data.mode = 'lines+markers'; +else + if marker_bool + data.mode = 'markers'; + end + if line_bool + data.mode = 'lines'; + end +end + +% ERROR BARS +if isfield(d, 'LData') + data.error_y.type = 'data'; + data.error_y.array = d.LData; + data.error_y.visible = true; + if ~strip_style + if isfield(d, 'Color') && size(d.Color,2)==3 + data.error_y.color = parseColor(d.Color); + else + data.error_y.color = 'rgb(100, 100, 100)'; + end + if isfield(data, 'marker') ... + && isfield(data.marker, 'line') ... + && isfield(data.marker.line, 'width') + data.error_y.thickness = data.marker.line.width; + end + end +end + + + +end \ No newline at end of file diff --git a/oo/private/extractDateTicks.m b/oo/private/extractDateTicks.m new file mode 100644 index 00000000..c426a556 --- /dev/null +++ b/oo/private/extractDateTicks.m @@ -0,0 +1,52 @@ +function [start, finish, t0, tstep] = extractDateTicks(tick_labels, ticks) + +[start, finish, t0, tstep] = deal(zeros(0,0)); + +num_ticks = size(tick_labels,1); + +if num_ticks<2 + return +end + +date_nums = zeros(num_ticks,1); +dates_registered = 0; +for i=1:num_ticks + try + n=datenum(tick_labels(i,:)); + catch + n=[]; + if num_ticks==numel(ticks) + [y,mo,d,h,mi,s]=datevec(ticks(i)); + if y==str2num(tick_labels(i,:)) + n=datenum([y,mo,d,h,mi,s]); + end + end + end + + if numel(n)>0 + dates_registered=dates_registered+1; + date_nums(i) = n; + end + +end + + + +if dates_registered==num_ticks + %display('these are dates!') + millis_from_epoch = zeros(num_ticks,1); + for i=1:num_ticks + millis_from_epoch(i) = (date_nums(i)-datenum(1970,1,1))*1000*60*60*24; + end + + + start = millis_from_epoch(1); + finish = millis_from_epoch(end); + t0 = millis_from_epoch(1); + tstep = millis_from_epoch(2)-millis_from_epoch(1); + +end + + + +end \ No newline at end of file diff --git a/oo/private/extractLayoutGeneral.m b/oo/private/extractLayoutGeneral.m new file mode 100644 index 00000000..cf00357c --- /dev/null +++ b/oo/private/extractLayoutGeneral.m @@ -0,0 +1,26 @@ +function layout = extractLayoutGeneral(f, layout, strip_style) + +%General attributes of the layout. +if strip_style + layout.autosize = true; + layout.margin.l=40; + layout.margin.r=40; + layout.margin.t=50; + layout.margin.b=40; + layout.margin.pad=2; + +else + layout.margin.l=0; + layout.margin.r=0; + layout.margin.t=5; + layout.margin.b=0; + layout.margin.pad=0; + layout.autosize = false; +end + +layout.width = f.Position(3); +layout.height = f.Position(4)+layout.margin.t; + + + +end \ No newline at end of file diff --git a/oo/private/extractLegend.m b/oo/private/extractLegend.m new file mode 100644 index 00000000..e3e03457 --- /dev/null +++ b/oo/private/extractLegend.m @@ -0,0 +1,47 @@ +function legend = extractLegend(a) +% extractLegend - create a legend struct +% [legend] = extractLegend(a) +% a - a data struct from matlab describing an axis used as a legend +% legend - a plotly legend struct +% +% For full documentation and examples, see https://plot.ly/api + +legend = {}; + +if strcmp(a.Visible, 'on') + + legend.traceorder = 'reversed'; + %POSITION + x_ref = a.Position(1)+a.Position(3)/2; + y_ref = a.Position(2)+a.Position(4)/2; + if x_ref>0.333 + if x_ref>0.666 + legend.x = a.Position(1)+a.Position(3); + legend.xanchor = 'right'; + else + legend.x = a.Position(1)+a.Position(3)/2; + legend.xanchor = 'middle'; + end + else + legend.x = a.Position(1); + legend.xanchor = 'left'; + end + + if y_ref>0.333 + if y_ref>0.666 + legend.y = a.Position(2)+a.Position(4); + legend.yanchor = 'top'; + else + legend.y = a.Position(2)+a.Position(4)/2; + legend.yanchor = 'middle'; + end + else + legend.y = a.Position(2); + legend.yanchor = 'bottom'; + end + + + +end + +end \ No newline at end of file diff --git a/oo/private/extractTitle.m b/oo/private/extractTitle.m new file mode 100644 index 00000000..5bac4b67 --- /dev/null +++ b/oo/private/extractTitle.m @@ -0,0 +1,59 @@ +function data = extractTitle(d, xa, ya, strip_style) +% extractTitle - create an annotation struct for plot titles +% [data] = extractTitle(d, xa, ya) +% xa,ya - reference axis structs +% d - a data struct from matlab describing an annotation +% data - a plotly annotation struct +% +% For full documentation and examples, see https://plot.ly/api + +data = {}; + +if numel(d.String)==0 + return +end + +% set reference axis +data.xref = 'paper'; +data.yref = 'paper'; + +%TEXT +data.text = parseText(d.String); +if ~strip_style + if strcmp(d.FontUnits, 'points') + data.font.size = 1.3*d.FontSize; + end + data.font.color = parseColor(d.Color); + %TODO: add font type +end + +%POSITION +xd_range = xa.domain(2) - xa.domain(1); +xr_range = xa.range(2) - xa.range(1); +if strcmp('linear', xa.type) || strcmp('date', xa.type) +data.x = xa.domain(1)+ (d.Extent(1) - xa.range(1))*xd_range / xr_range; +data.x = xa.domain(1)+ 0.5*xd_range; +end +if strcmp('log', xa.type) +data.x = xa.domain(1)+ 0.5*xd_range; +end +yd_range = ya.domain(2) - ya.domain(1); +yr_range = ya.range(2) - ya.range(1); +if strcmp('linear', ya.type) +data.y = ya.domain(1)+ (d.Extent(2) - ya.range(1))*yd_range / yr_range; +data.y = ya.domain(2)+ 0.04; +end +if strcmp('log', ya.type) +data.y = ya.domain(2)+ 0.04; +end + +data.align = d.HorizontalAlignment; +data.xanchor = 'center'; +data.yanchor = 'middle'; + +%ARROW +data.showarrow = false; + + + +end \ No newline at end of file diff --git a/oo/private/findDataType.m b/oo/private/findDataType.m new file mode 100644 index 00000000..0d1e9c55 --- /dev/null +++ b/oo/private/findDataType.m @@ -0,0 +1,71 @@ +function data_type = findDataType(m_data, m_axis) + +data_type = []; + +if strcmp('image',m_data.Type) + %heatmap plot + %test if image plot is the colorbar + if ~strcmp('Colorbar',m_axis.Tag) + data_type = 'heatmap'; + + else + data_type = 'colorbar'; + end +end +if strcmp('line',m_data.Type) + data_type = 'scatter'; +end +if strcmp('text',m_data.Type) + data_type = 'annotation'; +end + +if strcmp('patch',m_data.Type) + %TOIMPROVE: histogram appears as 'patch' as well, need + %to differentiate!!! + % For now, test if XData/YData are 4 by k matrices and rectangular + % The matlab fig doesnt keep the data, only the + % rectagle geometries (thus the 'patch' type), so + % we can only infer from the shapes a BAR CHART + if (size(m_data.XData,1)==4 && ... + all(m_data.XData(1,:)==m_data.XData(2,:)) && ... + all(m_data.XData(3,:)==m_data.XData(4,:)) && ... + all(m_data.YData(1,:)==m_data.YData(4,:)) && ... + all(m_data.YData(2,:)==m_data.YData(3,:))) + data_type = 'histogram'; + else + + data_type = 'area'; + end + +end +if strcmp('hggroup',m_data.Type) + + %TOIMPROVE: improve condition to differentiate between + %scatter and bar chart + if isfield(m_data, 'BarLayout') + data_type = 'bar'; + + else + if isfield(m_data, 'Marker') && numel(m_data.Marker)>0 + data_type = 'scatter'; + end + if isfield(m_data, 'EdgeColor') && isfield(m_data, 'FaceColor') + data_type = 'area'; + + end + end + + %TOIMPORVE: condition to detect contour plot + if isfield(m_data, 'LevelStep') + data_type = 'contour'; + end + + %if none of the above, assume box plot + if numel(data_type)==0 + data_type = 'box'; + end + +end + + +end \ No newline at end of file diff --git a/oo/private/generateBoxData.m b/oo/private/generateBoxData.m new file mode 100644 index 00000000..9331072d --- /dev/null +++ b/oo/private/generateBoxData.m @@ -0,0 +1,54 @@ +function y = generateBoxData(outliers, pmin, p25, md, p75, pmax) + +%set number of data points +N = numel(outliers)*5+20; + + +%find percentile numbers + +%pn = 100*(1:N-1/2)/N; +pid_25 = floor(N*25/100 + 0.5); +pid_50 = floor(N*50/100 + 0.5); +pid_75 = floor(N*75/100 + 0.5); + +% %find p25 +% pidx = find(pn<=25); +% pid_25 = pidx(end); +% %find median +% pidx = find(pn<=50); +% pid_50 = pidx(end); +% %find p75 +% pidx = find(pn<=75); +% pid_75 = pidx(end); + +low_o = outliers(outliersmd); + +y=[low_o ... + linspace(pmin, p25, pid_25-numel(low_o)) ... + linspace(p25, md, pid_50-pid_25) ... + linspace(md, p75, pid_75-pid_50) ... + linspace(p75, pmax, N-pid_75-numel(high_o)) ... + high_o]; + + + +% +% if numel(low_o)>0 +% y(1:numel(low_o))=low_o; +% end +% if numel(high_o)>0 +% y(end-numel(high_o)+1:end)=high_o; +% end + + + + + + + + + + + +end \ No newline at end of file diff --git a/oo/private/getuserdir.m b/oo/private/getuserdir.m new file mode 100644 index 00000000..c8d114b4 --- /dev/null +++ b/oo/private/getuserdir.m @@ -0,0 +1,13 @@ +function userDir = getuserdir +% GETUSERDIR Retrieve the user directory +% - Under Windows returns the %APPDATA% directory +% - For other OSs uses java to retrieve the user.home directory + +if ispc +% userDir = winqueryreg('HKEY_CURRENT_USER',... +% ['Software\Microsoft\Windows\CurrentVersion\' ... +% 'Explorer\Shell Folders'],'Personal'); + userDir = getenv('appdata'); +else + userDir = char(java.lang.System.getProperty('user.home')); +end \ No newline at end of file diff --git a/oo/private/is_octave.m b/oo/private/is_octave.m new file mode 100644 index 00000000..9895a1ae --- /dev/null +++ b/oo/private/is_octave.m @@ -0,0 +1,8 @@ +% subfunction that checks if we are in octave +function r = is_octave () + persistent x; + if (isempty (x)) + x = exist ('OCTAVE_VERSION', 'builtin'); + end + r = x; +end diff --git a/oo/private/json2struct.m b/oo/private/json2struct.m new file mode 100644 index 00000000..dc968d34 --- /dev/null +++ b/oo/private/json2struct.m @@ -0,0 +1,11 @@ +function st = json2struct(j) + + % everything is between double quotes. sweet! + st = struct(); + idx = strfind(j,'"'); + for i = 1:4:(length(idx)-2) + jf = j( (idx(i)+1):(idx(i+1)-1) ); + jv = j( (idx(i+2)+1):(idx(i+3)-1) ); + st = setfield(st, jf, jv); + end +end \ No newline at end of file diff --git a/oo/private/m2json.m b/oo/private/m2json.m new file mode 100644 index 00000000..05cae99a --- /dev/null +++ b/oo/private/m2json.m @@ -0,0 +1,39 @@ +function valstr = m2json(val) + if isstruct(val) + valstr = struct2json(val); + elseif iscell(val) + valstr = cell2json(val); + elseif isa(val, 'numeric') + sz = size(val); + if length(find(sz>1))>1 % 2D or higher array + valstr = ''; + for i = 1:sz(1) + valsubstr = [sprintf('%d, ', val(i,:))]; + valsubstr = valsubstr(1:(end-2)); + valstr = [valstr ', [' valsubstr ']']; + end + valstr = valstr(3:end); % trail leading commas + else + valstr = [sprintf('%d, ', val)]; + valstr = valstr(1:(end-2)); + end + if length(val)>1 + valstr = ['[' valstr ']']; + elseif length(val) == 0 + valstr = '[]'; + end + valstr = strrep(valstr, 'Inf', 'null'); + valstr = strrep(valstr, 'NaN', 'null'); + elseif ischar(val) + val = checkescape(val); %add escape characters + valstr = ['"' val '"']; + elseif islogical(val) + if val + valstr = 'true'; + else + valstr = 'false'; + end + else + disp [val ' iselse']; + valstr = ''; % wtf is it? + end \ No newline at end of file diff --git a/oo/private/makecall.m b/oo/private/makecall.m new file mode 100644 index 00000000..8ab84527 --- /dev/null +++ b/oo/private/makecall.m @@ -0,0 +1,22 @@ +function st = makecall(args, un, key, origin, structargs) + version = '0.5.5'; + platform = 'MATLAB'; + + args = m2json(args); + kwargs = m2json(structargs); + url = 'https://plot.ly/clientresp'; + payload = {'platform', platform, 'version', version, 'args', args, 'un', un, 'key', key, 'origin', origin, 'kwargs', kwargs}; + + if (is_octave) + % use octave super_powers + resp = urlread(url, 'post', payload); + else + % do it matlab way + resp = urlread(url, 'Post', payload); + end + + st = loadjson(resp); + + response_handler(resp); + +end \ No newline at end of file diff --git a/oo/private/mapColors.m b/oo/private/mapColors.m new file mode 100644 index 00000000..9ebbe33f --- /dev/null +++ b/oo/private/mapColors.m @@ -0,0 +1,19 @@ +function color_cell = mapColors(c, limits, colormap) + +%normalize +range = limits(2)-limits(1); +cn = 1+floor(((c-limits(1))/range)*(size(colormap,1)-1)); + +color_cell=cell(1,numel(c)); + +for i=1:numel(c) + if cn(i)>size(colormap,1) + cn(i)=size(colormap,1); + end + if cn(i)<1 + cn(i)=1; + end + color_cell{i} = parseColor(colormap(cn(i),:)); +end + +end \ No newline at end of file diff --git a/oo/private/parseColor.m b/oo/private/parseColor.m new file mode 100644 index 00000000..ac7cc543 --- /dev/null +++ b/oo/private/parseColor.m @@ -0,0 +1,7 @@ +function color_rgb = parseColor(c) + +% c is a 1x3 vector +c_int = floor(c*255); +color_rgb = ['rgb(' num2str(c_int(1)) ',' num2str(c_int(2)) ',' num2str(c_int(3)) ')']; + +end \ No newline at end of file diff --git a/oo/private/parseFill.m b/oo/private/parseFill.m new file mode 100644 index 00000000..8029969f --- /dev/null +++ b/oo/private/parseFill.m @@ -0,0 +1,37 @@ +function data = parseFill(d, data, CLim, colormap, strip_style) +% parseFill - parses fill attribute for area plots +% data = parseFill(d, data, limits, colormap) +% d - a data struct from matlab describing an annotation +% data - a plotly annotation struct +% CLim - a 1x2 vector of extents of the color map +% colormap - a kx3 matrix representing the colormap +% +% For full documentation and examples, see https://plot.ly/api + +data.fill = 'tozeroy'; + +%get child +if strcmp(d.Type, 'patch') + if ~strip_style + colors = setColorProperty(d.FaceColor,d.CData, CLim, colormap); + data.fillcolor = colors{1}; + end +end +if strcmp(d.Type, 'hggroup') + if ~strip_style + m_data = get(d.Children(1)); + colors = setColorProperty(m_data.FaceColor,m_data.CData, CLim, colormap); + if numel(colors{1})>0 + data.fillcolor = colors{1}; + end + end + %assume they are in right order + %TODO: improve data ordering + data.y = m_data.YData(2:(numel(m_data.YData)-1)/2+1); +end + + + + + +end \ No newline at end of file diff --git a/oo/private/parseLine.m b/oo/private/parseLine.m new file mode 100644 index 00000000..deaa287b --- /dev/null +++ b/oo/private/parseLine.m @@ -0,0 +1,31 @@ +function line_str = parseLine(d) + +%build marker struct + line_str = []; + % ommitted: '-' = solid by default + if strcmp('--', d.LineStyle) + line_str.dash = 'dash'; + end + if strcmp(':', d.LineStyle) + line_str.dash = 'dot'; + end + if strcmp('-.', d.LineStyle) + line_str.dash = 'dashdot'; + end + + line_str.width = d.LineWidth; + color_field=[]; + if isfield(d, 'Color') + color_field = d.Color; + else + if isfield(d, 'EdgeColor') + color_field = d.EdgeColor; + end + end + + colors = setColorProperty(color_field, [], [], []); + if numel(colors{1})>0 + line_str.color = colors{1}; + end + +end \ No newline at end of file diff --git a/oo/private/parseMarker.m b/oo/private/parseMarker.m new file mode 100644 index 00000000..e088c05c --- /dev/null +++ b/oo/private/parseMarker.m @@ -0,0 +1,71 @@ +function marker_str = parseMarker(d, CLim, colormap) + +%build marker struct + marker_str = []; + % not supported: *, X, diamond, pentagram, hexagram + if strcmp('o', d.Marker) + marker_str.symbol = 'circle'; + end + if strcmp('+', d.Marker) + marker_str.symbol = 'cross'; + end + if strcmp('square', d.Marker) || strcmp('s', d.Marker) + marker_str.symbol = 'square'; + end + if strcmp('^', d.Marker) + marker_str.symbol = 'triangle-up'; + end + if strcmp('V', d.Marker) + marker_str.symbol = 'triangle-down'; + end + if strcmp('>', d.Marker) + marker_str.symbol = 'triangle-right'; + end + if strcmp('<', d.Marker) + marker_str.symbol = 'triangle-left'; + end + + marker_str.line.width = d.LineWidth; + + %SIZE + if isfield(d, 'MarkerSize') + marker_str.size = 1.3*d.MarkerSize; + end + if isfield(d, 'SizeData') + if numel(d.SizeData)==1 + marker_str.size = 2.7*sqrt(d.SizeData/3.14); + end + if numel(d.SizeData)==numel(d.XData) + marker_str.size = 2.7*sqrt(d.SizeData/3.14); + end + end + + %COLOR + if isfield(d, 'CData') + color_ref = d.CData; + else + color_ref = d.Color; + end + + color_field = d.MarkerEdgeColor; + colors = setColorProperty(color_field, color_ref, CLim, colormap); + if numel(colors)==1 + if numel(colors{1})>0 + marker_str.line.color = colors{1}; + end + else + marker_str.line.color = colors; + end + + color_field = d.MarkerFaceColor; + colors = setColorProperty(color_field, color_ref, CLim, colormap); + if numel(colors)==1 + if numel(colors{1})>0 + marker_str.color = colors{1}; + end + else + marker_str.color = colors; + end + + +end \ No newline at end of file diff --git a/oo/private/parseText.m b/oo/private/parseText.m new file mode 100644 index 00000000..3cc81df8 --- /dev/null +++ b/oo/private/parseText.m @@ -0,0 +1,29 @@ +function text_str = parseText(input_text) + +%check for '\' and double up + +cases = []; + +for i=1:numel(input_text) + + if strcmp('\', input_text(i)) + cases = [cases i]; + end +end + +if numel(cases)==0 + text_str = input_text; +else + text_str=[]; + if cases(1)>1 + text_str = [text_str input_text(1:cases(1)-1)]; + end + for i=1:numel(cases)-1 + text_str = [text_str '\' input_text(cases(i):cases(i+1)-1)]; + end + text_str = [text_str '\' input_text(cases(numel(cases)):end)]; +end + + + +end \ No newline at end of file diff --git a/oo/private/plotlyhelp.m b/oo/private/plotlyhelp.m new file mode 100644 index 00000000..858bba55 --- /dev/null +++ b/oo/private/plotlyhelp.m @@ -0,0 +1 @@ +web('http://plot.ly/matlab/','-browser') \ No newline at end of file diff --git a/oo/private/reevaluateDomains.m b/oo/private/reevaluateDomains.m new file mode 100644 index 00000000..b688126f --- /dev/null +++ b/oo/private/reevaluateDomains.m @@ -0,0 +1,75 @@ +function [x_axis, y_axis] = reevaluateDomains(x_axis, y_axis, empty_axis) + +%find current domain range + +x_min = 1; +x_max = 0; +y_min = 1; +y_max = 0; + +x_min_r = 1; +x_max_r = 0; +y_min_r = 1; +y_max_r = 0; + +for i = 1:numel(x_axis) + if ~any(empty_axis(:,1)==i) + if x_axis{i}.domain(1)x_max_r + x_max_r=x_axis{i}.domain(2); + end + end + + if x_axis{i}.domain(1)x_max + x_max=x_axis{i}.domain(2); + end +end + +for i = 1:numel(y_axis) + if ~any(empty_axis(:,2)==i) + if y_axis{i}.domain(1)y_max_r + y_max_r=y_axis{i}.domain(2); + end + end + + if y_axis{i}.domain(1)y_max + y_max=y_axis{i}.domain(2); + end +end + +%rescale domain after removing empty axis + +x_scaling = (x_max_r-x_min_r)/(x_max-x_min); +y_scaling = (y_max_r-y_min_r)/(y_max-y_min); +a = x_max-x_max_r-x_min+x_min_r; +b = x_min_r-x_max_r; +x_center = (x_max-x_max_r) * b/a + x_max_r; +a = y_max-x_max_r-y_min+y_min_r; +b = y_min_r-y_max_r; +y_center = (y_max-y_max_r) * b/a + y_max_r; + +for i = 1:numel(x_axis) + for k=1:2 + x_axis{i}.domain(k)=(x_axis{i}.domain(k)-x_center)/x_scaling + x_center; + end +end + +for i = 1:numel(y_axis) + for k=1:2 + y_axis{i}.domain(k)=(y_axis{i}.domain(k)-y_center)/y_scaling + y_center; + end +end + + +end \ No newline at end of file diff --git a/oo/private/response_handler.m b/oo/private/response_handler.m new file mode 100644 index 00000000..bd9ede64 --- /dev/null +++ b/oo/private/response_handler.m @@ -0,0 +1,31 @@ +function response_handler(response_body, varargin) + % varargin is the optional `extras` struct + % returned by urlread2 + response_struct = loadjson(response_body); + + if(isempty(fieldnames(response_struct))) + error(['Unexpected Response: ', response_body]) + end + f = fieldnames(response_struct); + + if ((any(strcmp(f, 'error')) && (~isempty(response_struct.error))) || ... + (length(varargin)==1 && varargin{1}.status.value ~= 200)) + % If the error string is nonempty + % then check the `extras` + % object for a status code + % and embed that in the response + if(length(varargin)==1) + extras = varargin{1}; + error(['BadResponse:StatusCode',num2str(extras.status.value)], response_struct.error) + else + error(response_struct.error) + end + end + if any(strcmp(f,'warning')) + fprintf(response_struct.warning) + end + if any(strcmp(f,'message')) + fprintf(response_struct.message) + end + +end \ No newline at end of file diff --git a/oo/private/setColorProperty.m b/oo/private/setColorProperty.m new file mode 100644 index 00000000..ffca2104 --- /dev/null +++ b/oo/private/setColorProperty.m @@ -0,0 +1,31 @@ +function color = setColorProperty(prop, color_ref, limits, colormap) + +color = {}; +%if one direct color +if isa(prop, 'double') + if numel(prop)==3 + color{1} = parseColor(prop); + end +end + +%if no color +if strcmp(prop, 'none') + color{1} = 'rgba(0,0,0,0)'; +end + +%if color defined by map +if strcmp(prop, 'flat') || strcmp(prop, 'auto') + %if direct color mapping + if size(color_ref,2)==3 + color = cell(1, size(color_ref,1)); + for i=1:size(color_ref,1) + color{i} = parseColor(color_ref(i,:)); + end + else %if indirect color mapping + color = mapColors(color_ref, limits, colormap); + + end +end + + +end \ No newline at end of file diff --git a/oo/private/struct2json.m b/oo/private/struct2json.m new file mode 100644 index 00000000..6c6eb4ce --- /dev/null +++ b/oo/private/struct2json.m @@ -0,0 +1,11 @@ +function str = struct2json(s) + f = fieldnames(s); + str = ''; + for i = 1:length(fieldnames(s)) + val = s.(f{i}); + valstr = m2json(val); + str = [str '"' f{i} '"' ': ' valstr ', ' ]; + end + str = str(1:(end-2)); % trim trailing comma + str = ['{' str '}']; +end \ No newline at end of file diff --git a/oo/savecredentials.m b/oo/savecredentials.m new file mode 100644 index 00000000..a7ea40e8 --- /dev/null +++ b/oo/savecredentials.m @@ -0,0 +1,57 @@ +function savecredentials(username, api_key) +% SAVECREDENTIALS Save/ovewrite plotly authentication credentials +% +% SAVECREDENTIALS(USERNAME, API_KEY) + +% Plotly credentials are saved as JSON strings: +% - Under Windows in $APPDATA$\credentials\plotly +% - Other OSs in ~/.plotly/.credentials + + % Hidden folder and credentials file names + userhome = getuserdir(); + if ispc + folder = fullfile(userhome,'plotly'); + filename = fullfile(folder, 'credentials'); + else + folder = fullfile(userhome,'.plotly'); + filename = fullfile(folder, '.credentials'); + end + + % Create folder + [status, mess, messid] = mkdir(folder); + if (status == 0) + if(~strcmp(messid, 'MATLAB:MKDIR:DirectoryExists')) + error('plotly:savecredentials',... + ['Error saving credentials folder at %s:\n' ... + '%s %s.\nGet in touch at ' ... + 'chris@plot.ly for support.'], filename,mess,messid); + end + end + + % Unhide if already exists and overwrite + if exist(filename, 'file') + fileattrib(filename, '-h') + end + fileID = fopen(filename, 'w'); + if(fileID == -1) + error('plotly:savecredentials',... + ['Error opening credentials file at %s.\n',... + 'Get in touch at chris@plot.ly for support.'], filename); + end + + % Save credentials and close + credentials = m2json(struct('username', username, 'api_key', api_key)); + fprintf(fileID, credentials); + status = fclose(fileID); + + % Hide folder and file + if ispc + user = ''; + else + user = 'u'; + end + fileattrib(folder,'+h',user,'s') + + % Print result + if status == 0, fprintf('Credentials successfully saved.\n'), end +end \ No newline at end of file diff --git a/oo/signup.m b/oo/signup.m new file mode 100644 index 00000000..7959a3b7 --- /dev/null +++ b/oo/signup.m @@ -0,0 +1,36 @@ +function response = signup +% SIGNUP Sign up to plot.ly with a new email and username + +% Prompt for email and username +s = inputdlg({'Email','Username'},'Sign up'); +if isempty(s) + fprintf('Sign up cancelled.\n') + response = []; + return +end + +% Post request +platform = 'MATLAB'; +payload = {'version', '0.2', 'un', s{2}, 'email', s{1},'platform',platform}; +url = 'https://plot.ly/apimkacct'; +resp = urlread(url, 'Post', payload); +response = json2struct(resp); + +% Some error handling +if isfield(response,'error') && ~isempty(response.error) + error(response.error) +end +if isfield(response,'warning') && ~isempty(response.warning) + fprintf(response.warning) +end + +% Print temp pass +fprintf('You successfully signed up!\nYour temporary password is: %s\n', response.tmp_pw) + +% Prompt to save credentials +choice = questdlg('Do you want to save your credentials?', ... + 'Plot.ly credentials', 'Yes','No','Yes'); +if strcmpi(choice,'yes') + saveplotlycredentials(response.un, response.api_key) +end +end \ No newline at end of file diff --git a/oo/urlread2/http_createHeader.m b/oo/urlread2/http_createHeader.m new file mode 100644 index 00000000..0e80241f --- /dev/null +++ b/oo/urlread2/http_createHeader.m @@ -0,0 +1,11 @@ +function header = http_createHeader(name,value) +%http_createHeader Simple function for creating input header to urlread2 +% +% header = http_createHeader(name,value) +% +% CODE: header = struct('name',name,'value',value); +% +% See Also: +% urlread2 + +header = struct('name',name,'value',value); \ No newline at end of file diff --git a/oo/urlread2/http_paramsToString.m b/oo/urlread2/http_paramsToString.m new file mode 100644 index 00000000..376a0fa1 --- /dev/null +++ b/oo/urlread2/http_paramsToString.m @@ -0,0 +1,62 @@ +function [str,header] = http_paramsToString(params,encodeOption) +%http_paramsToString Creates string for a POST or GET requests +% +% [queryString,header] = http_paramsToString(params, *encodeOption) +% +% INPUTS +% ======================================================================= +% params: cell array of property/value pairs +% NOTE: If the input is in a 2 column matrix, then first column +% entries are properties and the second column entries are +% values, however this is NOT necessary (generally linear) +% encodeOption: (default 1) +% 1 - the typical URL encoding scheme (Java call) +% +% OUTPUTS +% ======================================================================= +% queryString: querystring to add onto URL (LACKS "?", see example) +% header : the header that should be attached for post requests when +% using urlread2 +% +% EXAMPLE: +% ============================================================== +% params = {'cmd' 'search' 'db' 'pubmed' 'term' 'wtf batman'}; +% queryString = http_paramsToString(params); +% queryString => cmd=search&db=pubmed&term=wtf+batman +% +% IMPORTANT: This function does not filter parameters, sort them, +% or remove empty inputs (if necessary), this must be done before hand + +if ~exist('encodeOption','var') + encodeOption = 1; +end + +if size(params,2) == 2 && size(params,1) > 1 + params = params'; + params = params(:); +end + +str = ''; +for i=1:2:length(params) + if (i == 1), separator = ''; else separator = '&'; end + switch encodeOption + case 1 + param = urlencode(params{i}); + value = urlencode(params{i+1}); +% case 2 +% param = oauth.percentEncodeString(params{i}); +% value = oauth.percentEncodeString(params{i+1}); +% header = http_getContentTypeHeader(1); + otherwise + error('Case not used') + end + str = [str separator param '=' value]; %#ok +end + +switch encodeOption + case 1 + header = http_createHeader('Content-Type','application/x-www-form-urlencoded'); +end + + +end \ No newline at end of file diff --git a/oo/urlread2/license.txt b/oo/urlread2/license.txt new file mode 100644 index 00000000..1d783b74 --- /dev/null +++ b/oo/urlread2/license.txt @@ -0,0 +1,24 @@ +Copyright (c) 2012, Jim Hokanson +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. diff --git a/oo/urlread2/urlread2.m b/oo/urlread2/urlread2.m new file mode 100644 index 00000000..b552861c --- /dev/null +++ b/oo/urlread2/urlread2.m @@ -0,0 +1,371 @@ +function [output,extras] = urlread2(urlChar,method,body,headersIn,varargin) +%urlread2 Makes HTTP requests and processes response +% +% [output,extras] = urlread2(urlChar, *method, *body, *headersIn, varargin) +% +% * indicates optional inputs that must be entered in place +% +% UNDOCUMENTED MATLAB VERSION +% +% EXAMPLE CALLING FORMS +% ... = urlread2(urlChar) +% ... = urlread2(urlChar,'GET','',[],prop1,value1,prop2,value2,etc) +% ... = urlread2(urlChar,'POST',body,headers) +% +% FEATURES +% ======================================================================= +% 1) Allows specification of any HTTP method +% 2) Allows specification of any header. Very little is hard-coded +% in for header handling. +% 3) Returns response status and headers +% 4) Should handle unicode properly ... +% +% OUTPUTS +% ======================================================================= +% output : body of the response, either text or binary depending upon +% CAST_OUTPUT property +% extras : (structure) +% .allHeaders - stucture, fields have cellstr values, HTTP headers may +% may be repeated but will have a single field entry, with each +% repeat's value another being another entry in the cellstr, for +% example: +% .Set_Cookie = {'first_value' 'second_value'} +% .firstHeaders - (structure), variable fields, contains the first +% string entry for each field in allHeaders, this +% structure can be used to avoid dereferencing a cell +% for fields you expect not to be repeated ... +% EXAMPLE: +% .Response : 'HTTP/1.1 200 OK'} +% .Server : 'nginx' +% .Date : 'Tue, 29 Nov 2011 02:23:16 GMT' +% .Content_Type : 'text/html; charset=UTF-8' +% .Content_Length : '109155' +% .Connection : 'keep-alive' +% .Vary : 'Accept-Encoding, User-Agent' +% .Cache_Control : 'max-age=60, private' +% .Set_Cookie : 'first_value' +% .status - (structure) +% .value : numeric value of status, ex. 200 +% .msg : message that goes along with status, ex. 'OK' +% .url - eventual url that led to output, this can change from +% the input with redirects, see FOLLOW_REDIRECTS +% .isGood - (logical) I believe this is an indicator of the presence of 400 +% or 500 status codes (see status.value) but more +% testing is needed. In other words, true if status.value < 400. +% In code, set true if the response was obtainable without +% resorting to checking the error stream. +% +% INPUTS +% ======================================================================= +% urlChar : The full url, must include scheme (http, https) +% method : examples: 'GET' 'POST' etc +% body : (vector)(char, uint8 or int8) body to write, generally used +% with POST or PUT, use of uint8 or int8 ensures that the +% body input is not manipulated before sending, char is sent +% via unicode2native function with ENCODING input (see below) +% headersIn : (structure array), use empty [] or '' if no headers are needed +% but varargin property/value pairs are, multiple headers +% may be passed in as a structure array +% .name - (string), name of the header, a name property is used +% instead of a field because the name must match a valid +% header +% .value - (string), value to use +% +% OPTIONAL INPUTS (varargin, property/value pairs) +% ======================================================================= +% CAST_OUTPUT : (default true) output is uint8, useful if the body +% of the response is not text +% ENCODING : (default ''), ENCODING input to function unicode2native +% FOLLOW_REDIRECTS : (default true), if false 3xx status codes will +% be returned and need to be handled by the user, +% note this does not handle javascript or meta tag +% redirects, just server based ones +% READ_TIMEOUT : (default 0), 0 means no timeout, value is in +% milliseconds +% +% EXAMPLES +% ======================================================================= +% GET: +% -------------------------------------------- +% url = 'http://www.mathworks.com/matlabcentral/fileexchange/'; +% query = 'urlread2'; +% params = {'term' query}; +% queryString = http_paramsToString(params,1); +% url = [url '?' queryString]; +% [output,extras] = urlread2(url); +% +% POST: +% -------------------------------------------- +% url = 'http://posttestserver.com/post.php'; +% params = {'testChars' char([2500 30000]) 'new code' '?'}; +% [paramString,header] = http_paramsToString(params,1); +% [output,extras] = urlread2(url,'POST',paramString,header); +% +% From behind a firewall, use the Preferences to set your proxy server. +% +% See Also: +% http_paramsToString +% unicode2native +% native2unicode +% +% Subfunctions: +% fixHeaderCasing - small subfunction to fix case errors encountered in real +% world, requires updating when casing doesn't match expected form, like +% if someone sent the header content-Encoding instead of +% Content-Encoding +% +% Based on original urlread code by Matthew J. Simoneau +% +% VERSION = 1.1 + +in.CAST_OUTPUT = true; +in.FOLLOW_REDIRECTS = true; +in.READ_TIMEOUT = 0; +in.ENCODING = ''; + +%Input handling +%--------------------------------------- +if ~isempty(varargin) + for i = 1:2:numel(varargin) + prop = upper(varargin{i}); + value = varargin{i+1}; + if isfield(in,prop) + in.(prop) = value; + else + error('Unrecognized input to function: %s',prop) + end + end +end + +if ~exist('method','var') || isempty(method), method = 'GET'; end +if ~exist('body','var'), body = ''; end +if ~exist('headersIn','var'), headersIn = []; end + +assert(usejava('jvm'),'Function requires Java') + +import com.mathworks.mlwidgets.io.InterruptibleStreamCopier; +com.mathworks.mlwidgets.html.HTMLPrefs.setProxySettings %Proxy settings need to be set + +%Create a urlConnection. +%----------------------------------- +urlConnection = getURLConnection(urlChar); +%For HTTP uses sun.net.www.protocol.http.HttpURLConnection +%Might use ice.net.HttpURLConnection but this has more overhead + +%SETTING PROPERTIES +%------------------------------------------------------- +urlConnection.setRequestMethod(upper(method)); +urlConnection.setFollowRedirects(in.FOLLOW_REDIRECTS); +urlConnection.setReadTimeout(in.READ_TIMEOUT); + +for iHeader = 1:length(headersIn) + curHeader = headersIn(iHeader); + urlConnection.setRequestProperty(curHeader.name,curHeader.value); +end + +if ~isempty(body) + %Ensure vector? + if size(body,1) > 1 + if size(body,2) > 1 + error('Input parameter to function: body, must be a vector') + else + body = body'; + end + end + + if ischar(body) + %NOTE: '' defaults to Matlab's default encoding scheme + body = unicode2native(body,in.ENCODING); + elseif ~(strcmp(class(body),'uint8') || strcmp(class(body),'int8')) + error('Function input: body, should be of class char, uint8, or int8, detected: %s',class(body)) + end + + urlConnection.setRequestProperty('Content-Length',int2str(length(body))); + urlConnection.setDoOutput(true); + outputStream = urlConnection.getOutputStream; + outputStream.write(body); + outputStream.close; +else + urlConnection.setRequestProperty('Content-Length','0'); +end + +%========================================================================== +% Read the data from the connection. +%========================================================================== +%This should be done first because it tells us if things are ok or not +%NOTE: If there is an error, functions below using urlConnection, notably +%getResponseCode, will fail as well +try + inputStream = urlConnection.getInputStream; + isGood = true; +catch ME + isGood = false; +%NOTE: HTTP error codes will throw an error here, we'll allow those for now +%We might also get another error in which case the inputStream will be +%undefined, those we will throw here + inputStream = urlConnection.getErrorStream; + + if isempty(inputStream) + msg = ME.message; + I = strfind(msg,char([13 10 9])); %see example by setting timeout to 1 + %Should remove the barf of the stack, at ... at ... at ... etc + %Likely that this could be improved ... (generate link with full msg) + if ~isempty(I) + msg = msg(1:I(1)-1); + end + fprintf(2,'Response stream is undefined\n below is a Java Error dump (truncated):\n'); + error(msg) + end +end + +%POPULATING HEADERS +%-------------------------------------------------------------------------- +allHeaders = struct; +allHeaders.Response = {char(urlConnection.getHeaderField(0))}; +done = false; +headerIndex = 0; + +while ~done + headerIndex = headerIndex + 1; + headerValue = char(urlConnection.getHeaderField(headerIndex)); + if ~isempty(headerValue) + headerName = char(urlConnection.getHeaderFieldKey(headerIndex)); + headerName = fixHeaderCasing(headerName); %NOT YET FINISHED + + %Important, for name safety all hyphens are replace with underscores + headerName(headerName == '-') = '_'; + if isfield(allHeaders,headerName) + allHeaders.(headerName) = [allHeaders.(headerName) headerValue]; + else + allHeaders.(headerName) = {headerValue}; + end + else + done = true; + end +end + +firstHeaders = struct; +fn = fieldnames(allHeaders); +for iHeader = 1:length(fn) + curField = fn{iHeader}; + firstHeaders.(curField) = allHeaders.(curField){1}; +end + +status = struct(... + 'value', urlConnection.getResponseCode(),... + 'msg', char(urlConnection.getResponseMessage)); + +%PROCESSING OF OUTPUT +%---------------------------------------------------------- +byteArrayOutputStream = java.io.ByteArrayOutputStream; +% This StreamCopier is unsupported and may change at any time. OH GREAT :/ +isc = InterruptibleStreamCopier.getInterruptibleStreamCopier; +isc.copyStream(inputStream,byteArrayOutputStream); +inputStream.close; +byteArrayOutputStream.close; + +if in.CAST_OUTPUT + charset = ''; + + %Extraction of character set from Content-Type header if possible + if isfield(firstHeaders,'Content_Type') + text = firstHeaders.Content_Type; + %Always open to regexp improvements + charset = regexp(text,'(?<=charset=)[^\s]*','match','once'); + end + + if ~isempty(charset) + output = native2unicode(typecast(byteArrayOutputStream.toByteArray','uint8'),charset); + else + output = char(typecast(byteArrayOutputStream.toByteArray','uint8')); + end +else + %uint8 is more useful for later charecter conversions + %uint8 or int8 is somewhat arbitary at this point + output = typecast(byteArrayOutputStream.toByteArray','uint8'); +end + +extras = struct; +extras.allHeaders = allHeaders; +extras.firstHeaders = firstHeaders; +extras.status = status; +%Gets eventual url even with redirection +extras.url = char(urlConnection.getURL); +extras.isGood = isGood; + + + +end + +function headerNameOut = fixHeaderCasing(headerName) +%fixHeaderCasing Forces standard casing of headers +% +% headerNameOut = fixHeaderCasing(headerName) +% +% This is important for field access in a structure which +% is case sensitive +% +% Not yet finished. +% I've been adding to this function as problems come along + + switch lower(headerName) + case 'location' + headerNameOut = 'Location'; + case 'content_type' + headerNameOut = 'Content_Type'; + otherwise + headerNameOut = headerName; + end +end + +%========================================================================== +%========================================================================== +%========================================================================== + +function urlConnection = getURLConnection(urlChar) +%getURLConnection +% +% urlConnection = getURLConnection(urlChar) + +% Determine the protocol (before the ":"). +protocol = urlChar(1:find(urlChar==':',1)-1); + + +% Try to use the native handler, not the ice.* classes. +try + switch protocol + case 'http' + %http://www.docjar.com/docs/api/sun/net/www/protocol/http/HttpURLConnection.html + handler = sun.net.www.protocol.http.Handler; + case 'https' + handler = sun.net.www.protocol.https.Handler; + end +catch ME + handler = []; +end + +% Create the URL object. +try + if isempty(handler) + url = java.net.URL(urlChar); + else + url = java.net.URL([],urlChar,handler); + end +catch ME + error('Failure to parse URL or protocol not supported for:\nURL: %s',urlChar); +end + +% Get the proxy information using MathWorks facilities for unified proxy +% preference settings. +mwtcp = com.mathworks.net.transport.MWTransportClientPropertiesFactory.create(); +proxy = mwtcp.getProxy(); + +% Open a connection to the URL. +if isempty(proxy) + urlConnection = url.openConnection; +else + urlConnection = url.openConnection(proxy); +end + + +end diff --git a/oo/urlread2/urlread_notes.txt b/oo/urlread2/urlread_notes.txt new file mode 100644 index 00000000..8257f092 --- /dev/null +++ b/oo/urlread2/urlread_notes.txt @@ -0,0 +1,86 @@ +========================================================================== + Unicode & Matlab +========================================================================== +native2unicode - works with uint8, fails with char + +Taking a unicode character and encoding as bytes: +unicode2native(char(1002),'UTF-8') +back to the character: +native2unicode(uint8([207 170]),'UTF-8') +this doesn't work: +native2unicode(char([207 170]),'UTF-8') +in documentation: If BYTES is a CHAR vector, it is returned unchanged. + +Java - only supports int8 +Matlab to Java -> uint8 or int8 to bytes +Java to Matlab -> bytes to int8 +char - 16 bit + +Maintenance of underlying bytes: +typecast(java.lang.String(uint8(250)).getBytes,'uint8') = 250 +see documentation: Handling Data Returned from a Java Method + +Command Window difficulty +-------------------------------------------------------------------- +The typical font in the Matlab command window will often fail to render +unicode properly. I often can see unicode better in the variable editor +although this may be fixed if you change your font preferences ... +Copying unicode from the command window often results in the +generations of the value 26 (aka substitute) + +More documentation on input/output to urlread2 to follow eventually ... + +small notes to self: +for output +native2unicode(uint8(output),encoding) + +========================================================================== + HTTP Headers +========================================================================== +Handling of repeated http readers is a bit of a tricky situation. Most +headers are not repeated although sometimes http clients will assume this +for too many headers which can result in a problem if you want to see +duplicated headers. I've passed the problem onto the user who can decide +to handle it how they wish instead of providing the right solution, which +after some brief searching, I am not sure exists. + +========================================================================== + PROBLEMS +========================================================================== +1) Page requires following a redirect: +%------------------------------------------- +ref: http://www.mathworks.com/matlabcentral/newsreader/view_thread/302571 +fix: FOLLOW_REDIRECTS is enabled by default, you're fine. + +2) Basic authentication required: +%------------------------------------------ +Create and pass in the following header: +user = 'test'; +password = 'test'; +encoder = sun.misc.BASE64Encoder(); +str = java.lang.String([user ':' password]) %NOTE: format may be +%different for your server +header = http_createHeader('Authorization',char(encoder.encode(str.getBytes()))) +NOTE: Ideally you would make this a function + +3) The text returned doesn't make sense. +%----------------------------------------- +The text may not be encoded correctly. Requires native2unicode function. +See Unicode & Matlab section above. + +4) I get a different result in my web browser than I do in Matlab +%----------------------------------------- +This is generally seen for two reasons. +1 - The easiest and silly reason is user agent filtering. +When you make a request you identify yourself +as being a particular "broswer" or "user agent". Setting a header +with the user agent of the browser may fix the problem. +See: http://en.wikipedia.org/wiki/User_agent +See: http://whatsmyuseragent.com +value = '' +header = http_createHeader('User-Agent',value); +2 - You are not processing cookies and the server is not sending +you information because you haven't sent it cookies (everyone likes em!) +I've implemented cookie support but it requires some extra files that +I need to clean up. Feel free to email me if you'd really like to have them. + diff --git a/oo/urlread2/urlread_todos.txt b/oo/urlread2/urlread_todos.txt new file mode 100644 index 00000000..0434bcea --- /dev/null +++ b/oo/urlread2/urlread_todos.txt @@ -0,0 +1,13 @@ +========================================================================== + IMPROVEMENTS +========================================================================== +1) The function could be improved to support file streaming both in sending +and in receiving (saving) to reduce memory usage but this is very low priority + +2) Implement better casing handling + +3) Choose a better function name than urlread2 -> sorry Chris :) + +4) Support multipart/form-data, this ideally would be handled by a helper function + +5) Allow for throwing an error if the HTTP status code is an error (400) and 500 as well? \ No newline at end of file diff --git a/oo/urlread2/urlread_versionInfo.txt b/oo/urlread2/urlread_versionInfo.txt new file mode 100644 index 00000000..a8448f49 --- /dev/null +++ b/oo/urlread2/urlread_versionInfo.txt @@ -0,0 +1,13 @@ +===================== +Version 1.1 +3/25/2012 + +Summary: Bug fixes related to Matlab throwing errors when it shouldn't have been. Thanks to Shane Lin for pointing out the problems. + +Bug Fix: Modified code so that http status errors wouldn't throw errors in the code, but rather would be passed on to the user to process + +Bug Fix: Provided GET example code apparently doesn't work for all users, changed to a different example. + +===================== +Version 1 +3/17/2012 \ No newline at end of file