├── .gitignore ├── AUTHORS ├── COPYING ├── ChangeLog ├── INSTALL ├── LICENSE ├── Makefile.am ├── NEWS ├── README.md ├── autogen.sh ├── configure.ac ├── data ├── layout.json └── layout.xml └── src ├── command-switches.h ├── edge.h ├── ext ├── json │ ├── autolink.h │ ├── config.h │ ├── features.h │ ├── forwards.h │ ├── json.h │ ├── json_batchallocator.h │ ├── json_internalarray.inl │ ├── json_internalmap.inl │ ├── json_reader.cpp │ ├── json_value.cpp │ ├── json_valueiterator.inl │ ├── json_writer.cpp │ ├── reader.h │ ├── value.h │ └── writer.h └── xml │ └── rapidxml.h ├── fdl.cc ├── fdl.h ├── file-handler.cc ├── file-handler.h ├── layout.cc ├── layout.h ├── node.cc └── node.h /.gitignore: -------------------------------------------------------------------------------- 1 | /Makefile 2 | /Makefile.in 3 | /aclocal.m4 4 | /autom4te.cache/ 5 | /config.* 6 | /configure 7 | /depcomp 8 | /install-sh 9 | /libtool 10 | /ltmain.sh 11 | /m4/ 12 | /missing 13 | /stamp-h? 14 | /src/.deps/ 15 | /src/ext/.deps/ 16 | /src/json/.deps/ 17 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Jason Graves (GodLikeMouse/Collaboradev) 2 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | /opt/local/share/automake-1.14/COPYING -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/godlikemouse/ForceDirectedLayout/c29dce98c2fd8b7148c3c8c278e5734c763d8f14/ChangeLog -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | /opt/local/share/automake-1.14/INSTALL -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | AUTOMAKE_OPTIONS = subdir-objects foreign 2 | ACLOCAL_AMFLAGS = ${ACLOCAL_FLAGS} 3 | 4 | bin_PROGRAMS = fdl 5 | fdl_SOURCES = config.h \ 6 | src/command-switches.h \ 7 | src/node.h \ 8 | src/node.cc \ 9 | src/link.h \ 10 | src/layout.h \ 11 | src/layout.cc \ 12 | src/fdl.h \ 13 | src/fdl.cc \ 14 | src/file-handler.h \ 15 | src/file-handler.cc \ 16 | src/ext/json/autolink.h \ 17 | src/ext/json/config.h \ 18 | src/ext/json/features.h \ 19 | src/ext/json/forwards.h \ 20 | src/ext/json/json.h \ 21 | src/ext/json/reader.h \ 22 | src/ext/json/value.h \ 23 | src/ext/json/writer.h \ 24 | src/ext/json/json_reader.cpp \ 25 | src/ext/json/json_value.cpp \ 26 | src/ext/json/json_writer.cpp 27 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | fdl 1.0 (2014-03-19) 2 | -------------------- 3 | 4 | * Added support for GraphML and JSON 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ForceDirectedLayout 2 | =================== 3 | 4 | A simple C++ based force directed layout system for performing command line layouts of nodes and edges in both 2D and 3D. Supports JSON and GraphML supported input and output. Supports additional attributes and parameters in both JSON and GraphML. 5 | 6 | ## Building 7 | 8 | autoreconf --install && ./configure && make 9 | 10 | ## Usage 11 | 12 | Usage: fdl [OPTION]... --in=[INPUT-FILE] --out=[OUTPUT-FILE] 13 | 14 | Options: 15 | --in-type=TYPE Specifies the input file type, default JSON (JSON, GraphML). 16 | --out-type=TYPE Specifies the output file type, default JSON (JSON, GraphML). 17 | --in=FILE Specifies the input file. 18 | --out=FILE Specifies the output file. 19 | --mode=MODE Specifies the detangle mode, default 2D (2D, 3D). 20 | --help Displays this help message. 21 | 22 | ## Quick Start 23 | 24 | Read in JSON file, output to GraphML file. 25 | 26 | ./fdl --in-type=JSON --in=data/layout.json --out-type=GraphML --out=output.xml 27 | 28 | Read in GraphML file, output JSON to stdout. 29 | 30 | ./fdl --in-type=GraphML --in=data/layout.xml --out-type=JSON 31 | 32 | Output file will contain modified nodes with x,y,z values. 33 | 34 | ## Data Formats 35 | 36 | ForceDirectedLayout currently supports JSON and GraphML. Example data input files are available under the [data](https://github.com/godlikemouse/ForceDirectedLayout/tree/master/data) directory. 37 | 38 | * Required node attributes are: id 39 | * Required edge attributes are: id, source and target 40 | * Any additional attributes get passed directly through to the output 41 | 42 | ## Community 43 | 44 | Keep track of development and community news. 45 | 46 | * Follow [@Collaboradev on Twitter](https://twitter.com/collaboradev). 47 | * Follow the [Collaboradev Blog](http://www.collaboradev.com). 48 | 49 | ## License 50 | 51 | ForceDirectedLayout is released under [GPL, version 2.0](http://www.gnu.org/licenses/gpl-2.0.html) 52 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | autoreconf --install && ./configure && make 4 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([ForceDirectedLayout], [1.0], [j.graves@collaboradev.com], [fdl], [http://www.collaboradev.com]) 2 | AM_INIT_AUTOMAKE([1.10 no-define]) 3 | AC_CONFIG_HEADERS([config.h]) 4 | AC_PROG_CXX 5 | AC_CONFIG_FILES([Makefile]) 6 | AC_OUTPUT 7 | -------------------------------------------------------------------------------- /data/layout.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": [ 3 | { 4 | "id": "node_1", 5 | "color": "red" 6 | }, 7 | { 8 | "id": "node_2", 9 | "color": "red" 10 | }, 11 | { 12 | "id": "node_3", 13 | "color": "red" 14 | }, 15 | { 16 | "id": "node_4", 17 | "color": "red" 18 | }, 19 | { 20 | "id": "node_5", 21 | "color": "red" 22 | } 23 | ], 24 | "edges": [ 25 | { 26 | "source": "node_1", 27 | "target": "node_2", 28 | "id": "link_0" 29 | }, 30 | { 31 | "source": "node_2", 32 | "target": "node_3", 33 | "id": "link_1" 34 | }, 35 | { 36 | "source": "node_3", 37 | "target": "node_4", 38 | "id": "link_2" 39 | }, 40 | { 41 | "source": "node_4", 42 | "target": "node_5", 43 | "id": "link_3" 44 | }, 45 | { 46 | "source": "node_5", 47 | "target": "node_1", 48 | "id": "link_4" 49 | } 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /data/layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/command-switches.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Jason Graves (GodLikeMouse/Collaboradev) 3 | * http://www.collaboradev.com 4 | * 5 | * This file is part of ForceDirectedLayout. 6 | * 7 | * ForceDirectedLayout is free software: you can redistribute it 8 | * and/or modify it under the terms of the GNU General Public 9 | * License as published by the Free Software Foundation, either 10 | * version 3 of the License, or (at your option) any later version. 11 | * 12 | * ForceDirectedLayout is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with ForceDirectedLayout. If not, see http://www.gnu.org/licenses/. 19 | */ 20 | 21 | #ifndef __COMMANDSWITCHES_H 22 | #define __COMMANDSWITCHES_H 23 | 24 | #include 25 | 26 | using namespace std; 27 | 28 | class CommandSwitches { 29 | public: 30 | string in_file; 31 | string out_file; 32 | string in_type; 33 | string out_type; 34 | string mode; 35 | bool show_help; 36 | 37 | // Constructor 38 | CommandSwitches(){ 39 | in_type = "JSON"; 40 | out_type = "JSON"; 41 | mode = "2D"; 42 | show_help = false; 43 | } 44 | 45 | // Method for retreiving the error string. 46 | // @return string 47 | string& error(){ 48 | return error_; 49 | } 50 | 51 | // Method for determining if the configuration is sane. 52 | // @return bool 53 | bool sane(){ 54 | if(in_file.empty()){ 55 | error_ = "--in=[FILE] not specified."; 56 | return false; 57 | } 58 | 59 | return true; 60 | } 61 | 62 | // Method for parsing command line parameters 63 | // @param int argc 64 | // @param char** argv 65 | // @returns bool 66 | bool parse(int argc, char ** argv){ 67 | 68 | if(argc == 1){ 69 | show_help = true; 70 | return true; 71 | } 72 | 73 | //iterate options 74 | for(int i=1; i 26 | #include 27 | 28 | using namespace std; 29 | 30 | class Edge { 31 | 32 | public: 33 | string id; 34 | Node * source; 35 | Node * target; 36 | string source_id; 37 | string target_id; 38 | map attributes; 39 | 40 | }; 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/ext/json/autolink.h: -------------------------------------------------------------------------------- 1 | #ifndef JSON_AUTOLINK_H_INCLUDED 2 | # define JSON_AUTOLINK_H_INCLUDED 3 | 4 | # include "config.h" 5 | 6 | # ifdef JSON_IN_CPPTL 7 | # include 8 | # endif 9 | 10 | # if !defined(JSON_NO_AUTOLINK) && !defined(JSON_DLL_BUILD) && !defined(JSON_IN_CPPTL) 11 | # define CPPTL_AUTOLINK_NAME "json" 12 | # undef CPPTL_AUTOLINK_DLL 13 | # ifdef JSON_DLL 14 | # define CPPTL_AUTOLINK_DLL 15 | # endif 16 | # include "autolink.h" 17 | # endif 18 | 19 | #endif // JSON_AUTOLINK_H_INCLUDED 20 | -------------------------------------------------------------------------------- /src/ext/json/config.h: -------------------------------------------------------------------------------- 1 | #ifndef JSON_CONFIG_H_INCLUDED 2 | # define JSON_CONFIG_H_INCLUDED 3 | 4 | /// If defined, indicates that json library is embedded in CppTL library. 5 | //# define JSON_IN_CPPTL 1 6 | 7 | /// If defined, indicates that json may leverage CppTL library 8 | //# define JSON_USE_CPPTL 1 9 | /// If defined, indicates that cpptl vector based map should be used instead of std::map 10 | /// as Value container. 11 | //# define JSON_USE_CPPTL_SMALLMAP 1 12 | /// If defined, indicates that Json specific container should be used 13 | /// (hash table & simple deque container with customizable allocator). 14 | /// THIS FEATURE IS STILL EXPERIMENTAL! 15 | //# define JSON_VALUE_USE_INTERNAL_MAP 1 16 | /// Force usage of standard new/malloc based allocator instead of memory pool based allocator. 17 | /// The memory pools allocator used optimization (initializing Value and ValueInternalLink 18 | /// as if it was a POD) that may cause some validation tool to report errors. 19 | /// Only has effects if JSON_VALUE_USE_INTERNAL_MAP is defined. 20 | //# define JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 1 21 | 22 | /// If defined, indicates that Json use exception to report invalid type manipulation 23 | /// instead of C assert macro. 24 | # define JSON_USE_EXCEPTION 1 25 | 26 | # ifdef JSON_IN_CPPTL 27 | # include 28 | # ifndef JSON_USE_CPPTL 29 | # define JSON_USE_CPPTL 1 30 | # endif 31 | # endif 32 | 33 | # ifdef JSON_IN_CPPTL 34 | # define JSON_API CPPTL_API 35 | # elif defined(JSON_DLL_BUILD) 36 | # define JSON_API __declspec(dllexport) 37 | # elif defined(JSON_DLL) 38 | # define JSON_API __declspec(dllimport) 39 | # else 40 | # define JSON_API 41 | # endif 42 | 43 | #endif // JSON_CONFIG_H_INCLUDED 44 | -------------------------------------------------------------------------------- /src/ext/json/features.h: -------------------------------------------------------------------------------- 1 | #ifndef CPPTL_JSON_FEATURES_H_INCLUDED 2 | # define CPPTL_JSON_FEATURES_H_INCLUDED 3 | 4 | # include "forwards.h" 5 | 6 | namespace Json { 7 | 8 | /** \brief Configuration passed to reader and writer. 9 | * This configuration object can be used to force the Reader or Writer 10 | * to behave in a standard conforming way. 11 | */ 12 | class JSON_API Features 13 | { 14 | public: 15 | /** \brief A configuration that allows all features and assumes all strings are UTF-8. 16 | * - C & C++ comments are allowed 17 | * - Root object can be any JSON value 18 | * - Assumes Value strings are encoded in UTF-8 19 | */ 20 | static Features all(); 21 | 22 | /** \brief A configuration that is strictly compatible with the JSON specification. 23 | * - Comments are forbidden. 24 | * - Root object must be either an array or an object value. 25 | * - Assumes Value strings are encoded in UTF-8 26 | */ 27 | static Features strictMode(); 28 | 29 | /** \brief Initialize the configuration like JsonConfig::allFeatures; 30 | */ 31 | Features(); 32 | 33 | /// \c true if comments are allowed. Default: \c true. 34 | bool allowComments_; 35 | 36 | /// \c true if root must be either an array or an object value. Default: \c false. 37 | bool strictRoot_; 38 | }; 39 | 40 | } // namespace Json 41 | 42 | #endif // CPPTL_JSON_FEATURES_H_INCLUDED 43 | -------------------------------------------------------------------------------- /src/ext/json/forwards.h: -------------------------------------------------------------------------------- 1 | #ifndef JSON_FORWARDS_H_INCLUDED 2 | # define JSON_FORWARDS_H_INCLUDED 3 | 4 | # include "config.h" 5 | 6 | namespace Json { 7 | 8 | // writer.h 9 | class FastWriter; 10 | class StyledWriter; 11 | 12 | // reader.h 13 | class Reader; 14 | 15 | // features.h 16 | class Features; 17 | 18 | // value.h 19 | typedef int Int; 20 | typedef unsigned int UInt; 21 | class StaticString; 22 | class Path; 23 | class PathArgument; 24 | class Value; 25 | class ValueIteratorBase; 26 | class ValueIterator; 27 | class ValueConstIterator; 28 | #ifdef JSON_VALUE_USE_INTERNAL_MAP 29 | class ValueAllocator; 30 | class ValueMapAllocator; 31 | class ValueInternalLink; 32 | class ValueInternalArray; 33 | class ValueInternalMap; 34 | #endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP 35 | 36 | } // namespace Json 37 | 38 | 39 | #endif // JSON_FORWARDS_H_INCLUDED 40 | -------------------------------------------------------------------------------- /src/ext/json/json.h: -------------------------------------------------------------------------------- 1 | #ifndef JSON_JSON_H_INCLUDED 2 | # define JSON_JSON_H_INCLUDED 3 | 4 | # include "autolink.h" 5 | # include "value.h" 6 | # include "reader.h" 7 | # include "writer.h" 8 | # include "features.h" 9 | 10 | #endif // JSON_JSON_H_INCLUDED 11 | -------------------------------------------------------------------------------- /src/ext/json/json_batchallocator.h: -------------------------------------------------------------------------------- 1 | #ifndef JSONCPP_BATCHALLOCATOR_H_INCLUDED 2 | # define JSONCPP_BATCHALLOCATOR_H_INCLUDED 3 | 4 | # include 5 | # include 6 | 7 | # ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION 8 | 9 | namespace Json { 10 | 11 | /* Fast memory allocator. 12 | * 13 | * This memory allocator allocates memory for a batch of object (specified by 14 | * the page size, the number of object in each page). 15 | * 16 | * It does not allow the destruction of a single object. All the allocated objects 17 | * can be destroyed at once. The memory can be either released or reused for future 18 | * allocation. 19 | * 20 | * The in-place new operator must be used to construct the object using the pointer 21 | * returned by allocate. 22 | */ 23 | template 25 | class BatchAllocator 26 | { 27 | public: 28 | typedef AllocatedType Type; 29 | 30 | BatchAllocator( unsigned int objectsPerPage = 255 ) 31 | : freeHead_( 0 ) 32 | , objectsPerPage_( objectsPerPage ) 33 | { 34 | // printf( "Size: %d => %s\n", sizeof(AllocatedType), typeid(AllocatedType).name() ); 35 | assert( sizeof(AllocatedType) * objectPerAllocation >= sizeof(AllocatedType *) ); // We must be able to store a slist in the object free space. 36 | assert( objectsPerPage >= 16 ); 37 | batches_ = allocateBatch( 0 ); // allocated a dummy page 38 | currentBatch_ = batches_; 39 | } 40 | 41 | ~BatchAllocator() 42 | { 43 | for ( BatchInfo *batch = batches_; batch; ) 44 | { 45 | BatchInfo *nextBatch = batch->next_; 46 | free( batch ); 47 | batch = nextBatch; 48 | } 49 | } 50 | 51 | /// allocate space for an array of objectPerAllocation object. 52 | /// @warning it is the responsability of the caller to call objects constructors. 53 | AllocatedType *allocate() 54 | { 55 | if ( freeHead_ ) // returns node from free list. 56 | { 57 | AllocatedType *object = freeHead_; 58 | freeHead_ = *(AllocatedType **)object; 59 | return object; 60 | } 61 | if ( currentBatch_->used_ == currentBatch_->end_ ) 62 | { 63 | currentBatch_ = currentBatch_->next_; 64 | while ( currentBatch_ && currentBatch_->used_ == currentBatch_->end_ ) 65 | currentBatch_ = currentBatch_->next_; 66 | 67 | if ( !currentBatch_ ) // no free batch found, allocate a new one 68 | { 69 | currentBatch_ = allocateBatch( objectsPerPage_ ); 70 | currentBatch_->next_ = batches_; // insert at the head of the list 71 | batches_ = currentBatch_; 72 | } 73 | } 74 | AllocatedType *allocated = currentBatch_->used_; 75 | currentBatch_->used_ += objectPerAllocation; 76 | return allocated; 77 | } 78 | 79 | /// Release the object. 80 | /// @warning it is the responsability of the caller to actually destruct the object. 81 | void release( AllocatedType *object ) 82 | { 83 | assert( object != 0 ); 84 | *(AllocatedType **)object = freeHead_; 85 | freeHead_ = object; 86 | } 87 | 88 | private: 89 | struct BatchInfo 90 | { 91 | BatchInfo *next_; 92 | AllocatedType *used_; 93 | AllocatedType *end_; 94 | AllocatedType buffer_[objectPerAllocation]; 95 | }; 96 | 97 | // disabled copy constructor and assignement operator. 98 | BatchAllocator( const BatchAllocator & ); 99 | void operator =( const BatchAllocator &); 100 | 101 | static BatchInfo *allocateBatch( unsigned int objectsPerPage ) 102 | { 103 | const unsigned int mallocSize = sizeof(BatchInfo) - sizeof(AllocatedType)* objectPerAllocation 104 | + sizeof(AllocatedType) * objectPerAllocation * objectsPerPage; 105 | BatchInfo *batch = static_cast( malloc( mallocSize ) ); 106 | batch->next_ = 0; 107 | batch->used_ = batch->buffer_; 108 | batch->end_ = batch->buffer_ + objectsPerPage; 109 | return batch; 110 | } 111 | 112 | BatchInfo *batches_; 113 | BatchInfo *currentBatch_; 114 | /// Head of a single linked list within the allocated space of freeed object 115 | AllocatedType *freeHead_; 116 | unsigned int objectsPerPage_; 117 | }; 118 | 119 | 120 | } // namespace Json 121 | 122 | # endif // ifndef JSONCPP_DOC_INCLUDE_IMPLEMENTATION 123 | 124 | #endif // JSONCPP_BATCHALLOCATOR_H_INCLUDED 125 | 126 | -------------------------------------------------------------------------------- /src/ext/json/json_internalarray.inl: -------------------------------------------------------------------------------- 1 | // included by json_value.cpp 2 | // everything is within Json namespace 3 | 4 | // ////////////////////////////////////////////////////////////////// 5 | // ////////////////////////////////////////////////////////////////// 6 | // ////////////////////////////////////////////////////////////////// 7 | // class ValueInternalArray 8 | // ////////////////////////////////////////////////////////////////// 9 | // ////////////////////////////////////////////////////////////////// 10 | // ////////////////////////////////////////////////////////////////// 11 | 12 | ValueArrayAllocator::~ValueArrayAllocator() 13 | { 14 | } 15 | 16 | // ////////////////////////////////////////////////////////////////// 17 | // class DefaultValueArrayAllocator 18 | // ////////////////////////////////////////////////////////////////// 19 | #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 20 | class DefaultValueArrayAllocator : public ValueArrayAllocator 21 | { 22 | public: // overridden from ValueArrayAllocator 23 | virtual ~DefaultValueArrayAllocator() 24 | { 25 | } 26 | 27 | virtual ValueInternalArray *newArray() 28 | { 29 | return new ValueInternalArray(); 30 | } 31 | 32 | virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) 33 | { 34 | return new ValueInternalArray( other ); 35 | } 36 | 37 | virtual void destructArray( ValueInternalArray *array ) 38 | { 39 | delete array; 40 | } 41 | 42 | virtual void reallocateArrayPageIndex( Value **&indexes, 43 | ValueInternalArray::PageIndex &indexCount, 44 | ValueInternalArray::PageIndex minNewIndexCount ) 45 | { 46 | ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; 47 | if ( minNewIndexCount > newIndexCount ) 48 | newIndexCount = minNewIndexCount; 49 | void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); 50 | if ( !newIndexes ) 51 | throw std::bad_alloc(); 52 | indexCount = newIndexCount; 53 | indexes = static_cast( newIndexes ); 54 | } 55 | virtual void releaseArrayPageIndex( Value **indexes, 56 | ValueInternalArray::PageIndex indexCount ) 57 | { 58 | if ( indexes ) 59 | free( indexes ); 60 | } 61 | 62 | virtual Value *allocateArrayPage() 63 | { 64 | return static_cast( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) ); 65 | } 66 | 67 | virtual void releaseArrayPage( Value *value ) 68 | { 69 | if ( value ) 70 | free( value ); 71 | } 72 | }; 73 | 74 | #else // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 75 | /// @todo make this thread-safe (lock when accessign batch allocator) 76 | class DefaultValueArrayAllocator : public ValueArrayAllocator 77 | { 78 | public: // overridden from ValueArrayAllocator 79 | virtual ~DefaultValueArrayAllocator() 80 | { 81 | } 82 | 83 | virtual ValueInternalArray *newArray() 84 | { 85 | ValueInternalArray *array = arraysAllocator_.allocate(); 86 | new (array) ValueInternalArray(); // placement new 87 | return array; 88 | } 89 | 90 | virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) 91 | { 92 | ValueInternalArray *array = arraysAllocator_.allocate(); 93 | new (array) ValueInternalArray( other ); // placement new 94 | return array; 95 | } 96 | 97 | virtual void destructArray( ValueInternalArray *array ) 98 | { 99 | if ( array ) 100 | { 101 | array->~ValueInternalArray(); 102 | arraysAllocator_.release( array ); 103 | } 104 | } 105 | 106 | virtual void reallocateArrayPageIndex( Value **&indexes, 107 | ValueInternalArray::PageIndex &indexCount, 108 | ValueInternalArray::PageIndex minNewIndexCount ) 109 | { 110 | ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; 111 | if ( minNewIndexCount > newIndexCount ) 112 | newIndexCount = minNewIndexCount; 113 | void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); 114 | if ( !newIndexes ) 115 | throw std::bad_alloc(); 116 | indexCount = newIndexCount; 117 | indexes = static_cast( newIndexes ); 118 | } 119 | virtual void releaseArrayPageIndex( Value **indexes, 120 | ValueInternalArray::PageIndex indexCount ) 121 | { 122 | if ( indexes ) 123 | free( indexes ); 124 | } 125 | 126 | virtual Value *allocateArrayPage() 127 | { 128 | return static_cast( pagesAllocator_.allocate() ); 129 | } 130 | 131 | virtual void releaseArrayPage( Value *value ) 132 | { 133 | if ( value ) 134 | pagesAllocator_.release( value ); 135 | } 136 | private: 137 | BatchAllocator arraysAllocator_; 138 | BatchAllocator pagesAllocator_; 139 | }; 140 | #endif // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 141 | 142 | static ValueArrayAllocator *&arrayAllocator() 143 | { 144 | static DefaultValueArrayAllocator defaultAllocator; 145 | static ValueArrayAllocator *arrayAllocator = &defaultAllocator; 146 | return arrayAllocator; 147 | } 148 | 149 | static struct DummyArrayAllocatorInitializer { 150 | DummyArrayAllocatorInitializer() 151 | { 152 | arrayAllocator(); // ensure arrayAllocator() statics are initialized before main(). 153 | } 154 | } dummyArrayAllocatorInitializer; 155 | 156 | // ////////////////////////////////////////////////////////////////// 157 | // class ValueInternalArray 158 | // ////////////////////////////////////////////////////////////////// 159 | bool 160 | ValueInternalArray::equals( const IteratorState &x, 161 | const IteratorState &other ) 162 | { 163 | return x.array_ == other.array_ 164 | && x.currentItemIndex_ == other.currentItemIndex_ 165 | && x.currentPageIndex_ == other.currentPageIndex_; 166 | } 167 | 168 | 169 | void 170 | ValueInternalArray::increment( IteratorState &it ) 171 | { 172 | JSON_ASSERT_MESSAGE( it.array_ && 173 | (it.currentPageIndex_ - it.array_->pages_)*itemsPerPage + it.currentItemIndex_ 174 | != it.array_->size_, 175 | "ValueInternalArray::increment(): moving iterator beyond end" ); 176 | ++(it.currentItemIndex_); 177 | if ( it.currentItemIndex_ == itemsPerPage ) 178 | { 179 | it.currentItemIndex_ = 0; 180 | ++(it.currentPageIndex_); 181 | } 182 | } 183 | 184 | 185 | void 186 | ValueInternalArray::decrement( IteratorState &it ) 187 | { 188 | JSON_ASSERT_MESSAGE( it.array_ && it.currentPageIndex_ == it.array_->pages_ 189 | && it.currentItemIndex_ == 0, 190 | "ValueInternalArray::decrement(): moving iterator beyond end" ); 191 | if ( it.currentItemIndex_ == 0 ) 192 | { 193 | it.currentItemIndex_ = itemsPerPage-1; 194 | --(it.currentPageIndex_); 195 | } 196 | else 197 | { 198 | --(it.currentItemIndex_); 199 | } 200 | } 201 | 202 | 203 | Value & 204 | ValueInternalArray::unsafeDereference( const IteratorState &it ) 205 | { 206 | return (*(it.currentPageIndex_))[it.currentItemIndex_]; 207 | } 208 | 209 | 210 | Value & 211 | ValueInternalArray::dereference( const IteratorState &it ) 212 | { 213 | JSON_ASSERT_MESSAGE( it.array_ && 214 | (it.currentPageIndex_ - it.array_->pages_)*itemsPerPage + it.currentItemIndex_ 215 | < it.array_->size_, 216 | "ValueInternalArray::dereference(): dereferencing invalid iterator" ); 217 | return unsafeDereference( it ); 218 | } 219 | 220 | void 221 | ValueInternalArray::makeBeginIterator( IteratorState &it ) const 222 | { 223 | it.array_ = const_cast( this ); 224 | it.currentItemIndex_ = 0; 225 | it.currentPageIndex_ = pages_; 226 | } 227 | 228 | 229 | void 230 | ValueInternalArray::makeIterator( IteratorState &it, ArrayIndex index ) const 231 | { 232 | it.array_ = const_cast( this ); 233 | it.currentItemIndex_ = index % itemsPerPage; 234 | it.currentPageIndex_ = pages_ + index / itemsPerPage; 235 | } 236 | 237 | 238 | void 239 | ValueInternalArray::makeEndIterator( IteratorState &it ) const 240 | { 241 | makeIterator( it, size_ ); 242 | } 243 | 244 | 245 | ValueInternalArray::ValueInternalArray() 246 | : pages_( 0 ) 247 | , size_( 0 ) 248 | , pageCount_( 0 ) 249 | { 250 | } 251 | 252 | 253 | ValueInternalArray::ValueInternalArray( const ValueInternalArray &other ) 254 | : pages_( 0 ) 255 | , pageCount_( 0 ) 256 | , size_( other.size_ ) 257 | { 258 | PageIndex minNewPages = other.size_ / itemsPerPage; 259 | arrayAllocator()->reallocateArrayPageIndex( pages_, pageCount_, minNewPages ); 260 | JSON_ASSERT_MESSAGE( pageCount_ >= minNewPages, 261 | "ValueInternalArray::reserve(): bad reallocation" ); 262 | IteratorState itOther; 263 | other.makeBeginIterator( itOther ); 264 | Value *value; 265 | for ( ArrayIndex index = 0; index < size_; ++index, increment(itOther) ) 266 | { 267 | if ( index % itemsPerPage == 0 ) 268 | { 269 | PageIndex pageIndex = index / itemsPerPage; 270 | value = arrayAllocator()->allocateArrayPage(); 271 | pages_[pageIndex] = value; 272 | } 273 | new (value) Value( dereference( itOther ) ); 274 | } 275 | } 276 | 277 | 278 | ValueInternalArray & 279 | ValueInternalArray::operator =( const ValueInternalArray &other ) 280 | { 281 | ValueInternalArray temp( other ); 282 | swap( temp ); 283 | return *this; 284 | } 285 | 286 | 287 | ValueInternalArray::~ValueInternalArray() 288 | { 289 | // destroy all constructed items 290 | IteratorState it; 291 | IteratorState itEnd; 292 | makeBeginIterator( it); 293 | makeEndIterator( itEnd ); 294 | for ( ; !equals(it,itEnd); increment(it) ) 295 | { 296 | Value *value = &dereference(it); 297 | value->~Value(); 298 | } 299 | // release all pages 300 | PageIndex lastPageIndex = size_ / itemsPerPage; 301 | for ( PageIndex pageIndex = 0; pageIndex < lastPageIndex; ++pageIndex ) 302 | arrayAllocator()->releaseArrayPage( pages_[pageIndex] ); 303 | // release pages index 304 | arrayAllocator()->releaseArrayPageIndex( pages_, pageCount_ ); 305 | } 306 | 307 | 308 | void 309 | ValueInternalArray::swap( ValueInternalArray &other ) 310 | { 311 | Value **tempPages = pages_; 312 | pages_ = other.pages_; 313 | other.pages_ = tempPages; 314 | ArrayIndex tempSize = size_; 315 | size_ = other.size_; 316 | other.size_ = tempSize; 317 | PageIndex tempPageCount = pageCount_; 318 | pageCount_ = other.pageCount_; 319 | other.pageCount_ = tempPageCount; 320 | } 321 | 322 | void 323 | ValueInternalArray::clear() 324 | { 325 | ValueInternalArray dummy; 326 | swap( dummy ); 327 | } 328 | 329 | 330 | void 331 | ValueInternalArray::resize( ArrayIndex newSize ) 332 | { 333 | if ( newSize == 0 ) 334 | clear(); 335 | else if ( newSize < size_ ) 336 | { 337 | IteratorState it; 338 | IteratorState itEnd; 339 | makeIterator( it, newSize ); 340 | makeIterator( itEnd, size_ ); 341 | for ( ; !equals(it,itEnd); increment(it) ) 342 | { 343 | Value *value = &dereference(it); 344 | value->~Value(); 345 | } 346 | PageIndex pageIndex = (newSize + itemsPerPage - 1) / itemsPerPage; 347 | PageIndex lastPageIndex = size_ / itemsPerPage; 348 | for ( ; pageIndex < lastPageIndex; ++pageIndex ) 349 | arrayAllocator()->releaseArrayPage( pages_[pageIndex] ); 350 | size_ = newSize; 351 | } 352 | else if ( newSize > size_ ) 353 | resolveReference( newSize ); 354 | } 355 | 356 | 357 | void 358 | ValueInternalArray::makeIndexValid( ArrayIndex index ) 359 | { 360 | // Need to enlarge page index ? 361 | if ( index >= pageCount_ * itemsPerPage ) 362 | { 363 | PageIndex minNewPages = (index + 1) / itemsPerPage; 364 | arrayAllocator()->reallocateArrayPageIndex( pages_, pageCount_, minNewPages ); 365 | JSON_ASSERT_MESSAGE( pageCount_ >= minNewPages, "ValueInternalArray::reserve(): bad reallocation" ); 366 | } 367 | 368 | // Need to allocate new pages ? 369 | ArrayIndex nextPageIndex = 370 | (size_ % itemsPerPage) != 0 ? size_ - (size_%itemsPerPage) + itemsPerPage 371 | : size_; 372 | if ( nextPageIndex <= index ) 373 | { 374 | PageIndex pageIndex = nextPageIndex / itemsPerPage; 375 | PageIndex pageToAllocate = (index - nextPageIndex) / itemsPerPage + 1; 376 | for ( ; pageToAllocate-- > 0; ++pageIndex ) 377 | pages_[pageIndex] = arrayAllocator()->allocateArrayPage(); 378 | } 379 | 380 | // Initialize all new entries 381 | IteratorState it; 382 | IteratorState itEnd; 383 | makeIterator( it, size_ ); 384 | size_ = index + 1; 385 | makeIterator( itEnd, size_ ); 386 | for ( ; !equals(it,itEnd); increment(it) ) 387 | { 388 | Value *value = &dereference(it); 389 | new (value) Value(); // Construct a default value using placement new 390 | } 391 | } 392 | 393 | Value & 394 | ValueInternalArray::resolveReference( ArrayIndex index ) 395 | { 396 | if ( index >= size_ ) 397 | makeIndexValid( index ); 398 | return pages_[index/itemsPerPage][index%itemsPerPage]; 399 | } 400 | 401 | Value * 402 | ValueInternalArray::find( ArrayIndex index ) const 403 | { 404 | if ( index >= size_ ) 405 | return 0; 406 | return &(pages_[index/itemsPerPage][index%itemsPerPage]); 407 | } 408 | 409 | ValueInternalArray::ArrayIndex 410 | ValueInternalArray::size() const 411 | { 412 | return size_; 413 | } 414 | 415 | int 416 | ValueInternalArray::distance( const IteratorState &x, const IteratorState &y ) 417 | { 418 | return indexOf(y) - indexOf(x); 419 | } 420 | 421 | 422 | ValueInternalArray::ArrayIndex 423 | ValueInternalArray::indexOf( const IteratorState &iterator ) 424 | { 425 | if ( !iterator.array_ ) 426 | return ArrayIndex(-1); 427 | return ArrayIndex( 428 | (iterator.currentPageIndex_ - iterator.array_->pages_) * itemsPerPage 429 | + iterator.currentItemIndex_ ); 430 | } 431 | 432 | 433 | int 434 | ValueInternalArray::compare( const ValueInternalArray &other ) const 435 | { 436 | int sizeDiff( size_ - other.size_ ); 437 | if ( sizeDiff != 0 ) 438 | return sizeDiff; 439 | 440 | for ( ArrayIndex index =0; index < size_; ++index ) 441 | { 442 | int diff = pages_[index/itemsPerPage][index%itemsPerPage].compare( 443 | other.pages_[index/itemsPerPage][index%itemsPerPage] ); 444 | if ( diff != 0 ) 445 | return diff; 446 | } 447 | return 0; 448 | } 449 | -------------------------------------------------------------------------------- /src/ext/json/json_internalmap.inl: -------------------------------------------------------------------------------- 1 | // included by json_value.cpp 2 | // everything is within Json namespace 3 | 4 | // ////////////////////////////////////////////////////////////////// 5 | // ////////////////////////////////////////////////////////////////// 6 | // ////////////////////////////////////////////////////////////////// 7 | // class ValueInternalMap 8 | // ////////////////////////////////////////////////////////////////// 9 | // ////////////////////////////////////////////////////////////////// 10 | // ////////////////////////////////////////////////////////////////// 11 | 12 | /** \internal MUST be safely initialized using memset( this, 0, sizeof(ValueInternalLink) ); 13 | * This optimization is used by the fast allocator. 14 | */ 15 | ValueInternalLink::ValueInternalLink() 16 | : previous_( 0 ) 17 | , next_( 0 ) 18 | { 19 | } 20 | 21 | ValueInternalLink::~ValueInternalLink() 22 | { 23 | for ( int index =0; index < itemPerLink; ++index ) 24 | { 25 | if ( !items_[index].isItemAvailable() ) 26 | { 27 | if ( !items_[index].isMemberNameStatic() ) 28 | free( keys_[index] ); 29 | } 30 | else 31 | break; 32 | } 33 | } 34 | 35 | 36 | 37 | ValueMapAllocator::~ValueMapAllocator() 38 | { 39 | } 40 | 41 | #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 42 | class DefaultValueMapAllocator : public ValueMapAllocator 43 | { 44 | public: // overridden from ValueMapAllocator 45 | virtual ValueInternalMap *newMap() 46 | { 47 | return new ValueInternalMap(); 48 | } 49 | 50 | virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) 51 | { 52 | return new ValueInternalMap( other ); 53 | } 54 | 55 | virtual void destructMap( ValueInternalMap *map ) 56 | { 57 | delete map; 58 | } 59 | 60 | virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) 61 | { 62 | return new ValueInternalLink[size]; 63 | } 64 | 65 | virtual void releaseMapBuckets( ValueInternalLink *links ) 66 | { 67 | delete [] links; 68 | } 69 | 70 | virtual ValueInternalLink *allocateMapLink() 71 | { 72 | return new ValueInternalLink(); 73 | } 74 | 75 | virtual void releaseMapLink( ValueInternalLink *link ) 76 | { 77 | delete link; 78 | } 79 | }; 80 | #else 81 | /// @todo make this thread-safe (lock when accessign batch allocator) 82 | class DefaultValueMapAllocator : public ValueMapAllocator 83 | { 84 | public: // overridden from ValueMapAllocator 85 | virtual ValueInternalMap *newMap() 86 | { 87 | ValueInternalMap *map = mapsAllocator_.allocate(); 88 | new (map) ValueInternalMap(); // placement new 89 | return map; 90 | } 91 | 92 | virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) 93 | { 94 | ValueInternalMap *map = mapsAllocator_.allocate(); 95 | new (map) ValueInternalMap( other ); // placement new 96 | return map; 97 | } 98 | 99 | virtual void destructMap( ValueInternalMap *map ) 100 | { 101 | if ( map ) 102 | { 103 | map->~ValueInternalMap(); 104 | mapsAllocator_.release( map ); 105 | } 106 | } 107 | 108 | virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) 109 | { 110 | return new ValueInternalLink[size]; 111 | } 112 | 113 | virtual void releaseMapBuckets( ValueInternalLink *links ) 114 | { 115 | delete [] links; 116 | } 117 | 118 | virtual ValueInternalLink *allocateMapLink() 119 | { 120 | ValueInternalLink *link = linksAllocator_.allocate(); 121 | memset( link, 0, sizeof(ValueInternalLink) ); 122 | return link; 123 | } 124 | 125 | virtual void releaseMapLink( ValueInternalLink *link ) 126 | { 127 | link->~ValueInternalLink(); 128 | linksAllocator_.release( link ); 129 | } 130 | private: 131 | BatchAllocator mapsAllocator_; 132 | BatchAllocator linksAllocator_; 133 | }; 134 | #endif 135 | 136 | static ValueMapAllocator *&mapAllocator() 137 | { 138 | static DefaultValueMapAllocator defaultAllocator; 139 | static ValueMapAllocator *mapAllocator = &defaultAllocator; 140 | return mapAllocator; 141 | } 142 | 143 | static struct DummyMapAllocatorInitializer { 144 | DummyMapAllocatorInitializer() 145 | { 146 | mapAllocator(); // ensure mapAllocator() statics are initialized before main(). 147 | } 148 | } dummyMapAllocatorInitializer; 149 | 150 | 151 | 152 | // h(K) = value * K >> w ; with w = 32 & K prime w.r.t. 2^32. 153 | 154 | /* 155 | use linked list hash map. 156 | buckets array is a container. 157 | linked list element contains 6 key/values. (memory = (16+4) * 6 + 4 = 124) 158 | value have extra state: valid, available, deleted 159 | */ 160 | 161 | 162 | ValueInternalMap::ValueInternalMap() 163 | : buckets_( 0 ) 164 | , tailLink_( 0 ) 165 | , bucketsSize_( 0 ) 166 | , itemCount_( 0 ) 167 | { 168 | } 169 | 170 | 171 | ValueInternalMap::ValueInternalMap( const ValueInternalMap &other ) 172 | : buckets_( 0 ) 173 | , tailLink_( 0 ) 174 | , bucketsSize_( 0 ) 175 | , itemCount_( 0 ) 176 | { 177 | reserve( other.itemCount_ ); 178 | IteratorState it; 179 | IteratorState itEnd; 180 | other.makeBeginIterator( it ); 181 | other.makeEndIterator( itEnd ); 182 | for ( ; !equals(it,itEnd); increment(it) ) 183 | { 184 | bool isStatic; 185 | const char *memberName = key( it, isStatic ); 186 | const Value &aValue = value( it ); 187 | resolveReference(memberName, isStatic) = aValue; 188 | } 189 | } 190 | 191 | 192 | ValueInternalMap & 193 | ValueInternalMap::operator =( const ValueInternalMap &other ) 194 | { 195 | ValueInternalMap dummy( other ); 196 | swap( dummy ); 197 | return *this; 198 | } 199 | 200 | 201 | ValueInternalMap::~ValueInternalMap() 202 | { 203 | if ( buckets_ ) 204 | { 205 | for ( BucketIndex bucketIndex =0; bucketIndex < bucketsSize_; ++bucketIndex ) 206 | { 207 | ValueInternalLink *link = buckets_[bucketIndex].next_; 208 | while ( link ) 209 | { 210 | ValueInternalLink *linkToRelease = link; 211 | link = link->next_; 212 | mapAllocator()->releaseMapLink( linkToRelease ); 213 | } 214 | } 215 | mapAllocator()->releaseMapBuckets( buckets_ ); 216 | } 217 | } 218 | 219 | 220 | void 221 | ValueInternalMap::swap( ValueInternalMap &other ) 222 | { 223 | ValueInternalLink *tempBuckets = buckets_; 224 | buckets_ = other.buckets_; 225 | other.buckets_ = tempBuckets; 226 | ValueInternalLink *tempTailLink = tailLink_; 227 | tailLink_ = other.tailLink_; 228 | other.tailLink_ = tempTailLink; 229 | BucketIndex tempBucketsSize = bucketsSize_; 230 | bucketsSize_ = other.bucketsSize_; 231 | other.bucketsSize_ = tempBucketsSize; 232 | BucketIndex tempItemCount = itemCount_; 233 | itemCount_ = other.itemCount_; 234 | other.itemCount_ = tempItemCount; 235 | } 236 | 237 | 238 | void 239 | ValueInternalMap::clear() 240 | { 241 | ValueInternalMap dummy; 242 | swap( dummy ); 243 | } 244 | 245 | 246 | ValueInternalMap::BucketIndex 247 | ValueInternalMap::size() const 248 | { 249 | return itemCount_; 250 | } 251 | 252 | bool 253 | ValueInternalMap::reserveDelta( BucketIndex growth ) 254 | { 255 | return reserve( itemCount_ + growth ); 256 | } 257 | 258 | bool 259 | ValueInternalMap::reserve( BucketIndex newItemCount ) 260 | { 261 | if ( !buckets_ && newItemCount > 0 ) 262 | { 263 | buckets_ = mapAllocator()->allocateMapBuckets( 1 ); 264 | bucketsSize_ = 1; 265 | tailLink_ = &buckets_[0]; 266 | } 267 | // BucketIndex idealBucketCount = (newItemCount + ValueInternalLink::itemPerLink) / ValueInternalLink::itemPerLink; 268 | return true; 269 | } 270 | 271 | 272 | const Value * 273 | ValueInternalMap::find( const char *key ) const 274 | { 275 | if ( !bucketsSize_ ) 276 | return 0; 277 | HashKey hashedKey = hash( key ); 278 | BucketIndex bucketIndex = hashedKey % bucketsSize_; 279 | for ( const ValueInternalLink *current = &buckets_[bucketIndex]; 280 | current != 0; 281 | current = current->next_ ) 282 | { 283 | for ( BucketIndex index=0; index < ValueInternalLink::itemPerLink; ++index ) 284 | { 285 | if ( current->items_[index].isItemAvailable() ) 286 | return 0; 287 | if ( strcmp( key, current->keys_[index] ) == 0 ) 288 | return ¤t->items_[index]; 289 | } 290 | } 291 | return 0; 292 | } 293 | 294 | 295 | Value * 296 | ValueInternalMap::find( const char *key ) 297 | { 298 | const ValueInternalMap *constThis = this; 299 | return const_cast( constThis->find( key ) ); 300 | } 301 | 302 | 303 | Value & 304 | ValueInternalMap::resolveReference( const char *key, 305 | bool isStatic ) 306 | { 307 | HashKey hashedKey = hash( key ); 308 | if ( bucketsSize_ ) 309 | { 310 | BucketIndex bucketIndex = hashedKey % bucketsSize_; 311 | ValueInternalLink **previous = 0; 312 | BucketIndex index; 313 | for ( ValueInternalLink *current = &buckets_[bucketIndex]; 314 | current != 0; 315 | previous = ¤t->next_, current = current->next_ ) 316 | { 317 | for ( index=0; index < ValueInternalLink::itemPerLink; ++index ) 318 | { 319 | if ( current->items_[index].isItemAvailable() ) 320 | return setNewItem( key, isStatic, current, index ); 321 | if ( strcmp( key, current->keys_[index] ) == 0 ) 322 | return current->items_[index]; 323 | } 324 | } 325 | } 326 | 327 | reserveDelta( 1 ); 328 | return unsafeAdd( key, isStatic, hashedKey ); 329 | } 330 | 331 | 332 | void 333 | ValueInternalMap::remove( const char *key ) 334 | { 335 | HashKey hashedKey = hash( key ); 336 | if ( !bucketsSize_ ) 337 | return; 338 | BucketIndex bucketIndex = hashedKey % bucketsSize_; 339 | for ( ValueInternalLink *link = &buckets_[bucketIndex]; 340 | link != 0; 341 | link = link->next_ ) 342 | { 343 | BucketIndex index; 344 | for ( index =0; index < ValueInternalLink::itemPerLink; ++index ) 345 | { 346 | if ( link->items_[index].isItemAvailable() ) 347 | return; 348 | if ( strcmp( key, link->keys_[index] ) == 0 ) 349 | { 350 | doActualRemove( link, index, bucketIndex ); 351 | return; 352 | } 353 | } 354 | } 355 | } 356 | 357 | void 358 | ValueInternalMap::doActualRemove( ValueInternalLink *link, 359 | BucketIndex index, 360 | BucketIndex bucketIndex ) 361 | { 362 | // find last item of the bucket and swap it with the 'removed' one. 363 | // set removed items flags to 'available'. 364 | // if last page only contains 'available' items, then desallocate it (it's empty) 365 | ValueInternalLink *&lastLink = getLastLinkInBucket( index ); 366 | BucketIndex lastItemIndex = 1; // a link can never be empty, so start at 1 367 | for ( ; 368 | lastItemIndex < ValueInternalLink::itemPerLink; 369 | ++lastItemIndex ) // may be optimized with dicotomic search 370 | { 371 | if ( lastLink->items_[lastItemIndex].isItemAvailable() ) 372 | break; 373 | } 374 | 375 | BucketIndex lastUsedIndex = lastItemIndex - 1; 376 | Value *valueToDelete = &link->items_[index]; 377 | Value *valueToPreserve = &lastLink->items_[lastUsedIndex]; 378 | if ( valueToDelete != valueToPreserve ) 379 | valueToDelete->swap( *valueToPreserve ); 380 | if ( lastUsedIndex == 0 ) // page is now empty 381 | { // remove it from bucket linked list and delete it. 382 | ValueInternalLink *linkPreviousToLast = lastLink->previous_; 383 | if ( linkPreviousToLast != 0 ) // can not deleted bucket link. 384 | { 385 | mapAllocator()->releaseMapLink( lastLink ); 386 | linkPreviousToLast->next_ = 0; 387 | lastLink = linkPreviousToLast; 388 | } 389 | } 390 | else 391 | { 392 | Value dummy; 393 | valueToPreserve->swap( dummy ); // restore deleted to default Value. 394 | valueToPreserve->setItemUsed( false ); 395 | } 396 | --itemCount_; 397 | } 398 | 399 | 400 | ValueInternalLink *& 401 | ValueInternalMap::getLastLinkInBucket( BucketIndex bucketIndex ) 402 | { 403 | if ( bucketIndex == bucketsSize_ - 1 ) 404 | return tailLink_; 405 | ValueInternalLink *&previous = buckets_[bucketIndex+1].previous_; 406 | if ( !previous ) 407 | previous = &buckets_[bucketIndex]; 408 | return previous; 409 | } 410 | 411 | 412 | Value & 413 | ValueInternalMap::setNewItem( const char *key, 414 | bool isStatic, 415 | ValueInternalLink *link, 416 | BucketIndex index ) 417 | { 418 | char *duplicatedKey = valueAllocator()->makeMemberName( key ); 419 | ++itemCount_; 420 | link->keys_[index] = duplicatedKey; 421 | link->items_[index].setItemUsed(); 422 | link->items_[index].setMemberNameIsStatic( isStatic ); 423 | return link->items_[index]; // items already default constructed. 424 | } 425 | 426 | 427 | Value & 428 | ValueInternalMap::unsafeAdd( const char *key, 429 | bool isStatic, 430 | HashKey hashedKey ) 431 | { 432 | JSON_ASSERT_MESSAGE( bucketsSize_ > 0, "ValueInternalMap::unsafeAdd(): internal logic error." ); 433 | BucketIndex bucketIndex = hashedKey % bucketsSize_; 434 | ValueInternalLink *&previousLink = getLastLinkInBucket( bucketIndex ); 435 | ValueInternalLink *link = previousLink; 436 | BucketIndex index; 437 | for ( index =0; index < ValueInternalLink::itemPerLink; ++index ) 438 | { 439 | if ( link->items_[index].isItemAvailable() ) 440 | break; 441 | } 442 | if ( index == ValueInternalLink::itemPerLink ) // need to add a new page 443 | { 444 | ValueInternalLink *newLink = mapAllocator()->allocateMapLink(); 445 | index = 0; 446 | link->next_ = newLink; 447 | previousLink = newLink; 448 | link = newLink; 449 | } 450 | return setNewItem( key, isStatic, link, index ); 451 | } 452 | 453 | 454 | ValueInternalMap::HashKey 455 | ValueInternalMap::hash( const char *key ) const 456 | { 457 | HashKey hash = 0; 458 | while ( *key ) 459 | hash += *key++ * 37; 460 | return hash; 461 | } 462 | 463 | 464 | int 465 | ValueInternalMap::compare( const ValueInternalMap &other ) const 466 | { 467 | int sizeDiff( itemCount_ - other.itemCount_ ); 468 | if ( sizeDiff != 0 ) 469 | return sizeDiff; 470 | // Strict order guaranty is required. Compare all keys FIRST, then compare values. 471 | IteratorState it; 472 | IteratorState itEnd; 473 | makeBeginIterator( it ); 474 | makeEndIterator( itEnd ); 475 | for ( ; !equals(it,itEnd); increment(it) ) 476 | { 477 | if ( !other.find( key( it ) ) ) 478 | return 1; 479 | } 480 | 481 | // All keys are equals, let's compare values 482 | makeBeginIterator( it ); 483 | for ( ; !equals(it,itEnd); increment(it) ) 484 | { 485 | const Value *otherValue = other.find( key( it ) ); 486 | int valueDiff = value(it).compare( *otherValue ); 487 | if ( valueDiff != 0 ) 488 | return valueDiff; 489 | } 490 | return 0; 491 | } 492 | 493 | 494 | void 495 | ValueInternalMap::makeBeginIterator( IteratorState &it ) const 496 | { 497 | it.map_ = const_cast( this ); 498 | it.bucketIndex_ = 0; 499 | it.itemIndex_ = 0; 500 | it.link_ = buckets_; 501 | } 502 | 503 | 504 | void 505 | ValueInternalMap::makeEndIterator( IteratorState &it ) const 506 | { 507 | it.map_ = const_cast( this ); 508 | it.bucketIndex_ = bucketsSize_; 509 | it.itemIndex_ = 0; 510 | it.link_ = 0; 511 | } 512 | 513 | 514 | bool 515 | ValueInternalMap::equals( const IteratorState &x, const IteratorState &other ) 516 | { 517 | return x.map_ == other.map_ 518 | && x.bucketIndex_ == other.bucketIndex_ 519 | && x.link_ == other.link_ 520 | && x.itemIndex_ == other.itemIndex_; 521 | } 522 | 523 | 524 | void 525 | ValueInternalMap::incrementBucket( IteratorState &iterator ) 526 | { 527 | ++iterator.bucketIndex_; 528 | JSON_ASSERT_MESSAGE( iterator.bucketIndex_ <= iterator.map_->bucketsSize_, 529 | "ValueInternalMap::increment(): attempting to iterate beyond end." ); 530 | if ( iterator.bucketIndex_ == iterator.map_->bucketsSize_ ) 531 | iterator.link_ = 0; 532 | else 533 | iterator.link_ = &(iterator.map_->buckets_[iterator.bucketIndex_]); 534 | iterator.itemIndex_ = 0; 535 | } 536 | 537 | 538 | void 539 | ValueInternalMap::increment( IteratorState &iterator ) 540 | { 541 | JSON_ASSERT_MESSAGE( iterator.map_, "Attempting to iterator using invalid iterator." ); 542 | ++iterator.itemIndex_; 543 | if ( iterator.itemIndex_ == ValueInternalLink::itemPerLink ) 544 | { 545 | JSON_ASSERT_MESSAGE( iterator.link_ != 0, 546 | "ValueInternalMap::increment(): attempting to iterate beyond end." ); 547 | iterator.link_ = iterator.link_->next_; 548 | if ( iterator.link_ == 0 ) 549 | incrementBucket( iterator ); 550 | } 551 | else if ( iterator.link_->items_[iterator.itemIndex_].isItemAvailable() ) 552 | { 553 | incrementBucket( iterator ); 554 | } 555 | } 556 | 557 | 558 | void 559 | ValueInternalMap::decrement( IteratorState &iterator ) 560 | { 561 | if ( iterator.itemIndex_ == 0 ) 562 | { 563 | JSON_ASSERT_MESSAGE( iterator.map_, "Attempting to iterate using invalid iterator." ); 564 | if ( iterator.link_ == &iterator.map_->buckets_[iterator.bucketIndex_] ) 565 | { 566 | JSON_ASSERT_MESSAGE( iterator.bucketIndex_ > 0, "Attempting to iterate beyond beginning." ); 567 | --(iterator.bucketIndex_); 568 | } 569 | iterator.link_ = iterator.link_->previous_; 570 | iterator.itemIndex_ = ValueInternalLink::itemPerLink - 1; 571 | } 572 | } 573 | 574 | 575 | const char * 576 | ValueInternalMap::key( const IteratorState &iterator ) 577 | { 578 | JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." ); 579 | return iterator.link_->keys_[iterator.itemIndex_]; 580 | } 581 | 582 | const char * 583 | ValueInternalMap::key( const IteratorState &iterator, bool &isStatic ) 584 | { 585 | JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." ); 586 | isStatic = iterator.link_->items_[iterator.itemIndex_].isMemberNameStatic(); 587 | return iterator.link_->keys_[iterator.itemIndex_]; 588 | } 589 | 590 | 591 | Value & 592 | ValueInternalMap::value( const IteratorState &iterator ) 593 | { 594 | JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." ); 595 | return iterator.link_->items_[iterator.itemIndex_]; 596 | } 597 | 598 | 599 | int 600 | ValueInternalMap::distance( const IteratorState &x, const IteratorState &y ) 601 | { 602 | int offset = 0; 603 | IteratorState it = x; 604 | while ( !equals( it, y ) ) 605 | increment( it ); 606 | return offset; 607 | } 608 | -------------------------------------------------------------------------------- /src/ext/json/json_reader.cpp: -------------------------------------------------------------------------------- 1 | #include "reader.h" 2 | #include "value.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #if _MSC_VER >= 1400 // VC++ 8.0 11 | #pragma warning( disable : 4996 ) // disable warning about strdup being deprecated. 12 | #endif 13 | 14 | namespace Json { 15 | 16 | // Implementation of class Features 17 | // //////////////////////////////// 18 | 19 | Features::Features() 20 | : allowComments_( true ) 21 | , strictRoot_( false ) 22 | { 23 | } 24 | 25 | 26 | Features 27 | Features::all() 28 | { 29 | return Features(); 30 | } 31 | 32 | 33 | Features 34 | Features::strictMode() 35 | { 36 | Features features; 37 | features.allowComments_ = false; 38 | features.strictRoot_ = true; 39 | return features; 40 | } 41 | 42 | // Implementation of class Reader 43 | // //////////////////////////////// 44 | 45 | 46 | static inline bool 47 | in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4 ) 48 | { 49 | return c == c1 || c == c2 || c == c3 || c == c4; 50 | } 51 | 52 | static inline bool 53 | in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4, Reader::Char c5 ) 54 | { 55 | return c == c1 || c == c2 || c == c3 || c == c4 || c == c5; 56 | } 57 | 58 | 59 | static bool 60 | containsNewLine( Reader::Location begin, 61 | Reader::Location end ) 62 | { 63 | for ( ;begin < end; ++begin ) 64 | if ( *begin == '\n' || *begin == '\r' ) 65 | return true; 66 | return false; 67 | } 68 | 69 | static std::string codePointToUTF8(unsigned int cp) 70 | { 71 | std::string result; 72 | 73 | // based on description from http://en.wikipedia.org/wiki/UTF-8 74 | 75 | if (cp <= 0x7f) 76 | { 77 | result.resize(1); 78 | result[0] = static_cast(cp); 79 | } 80 | else if (cp <= 0x7FF) 81 | { 82 | result.resize(2); 83 | result[1] = static_cast(0x80 | (0x3f & cp)); 84 | result[0] = static_cast(0xC0 | (0x1f & (cp >> 6))); 85 | } 86 | else if (cp <= 0xFFFF) 87 | { 88 | result.resize(3); 89 | result[2] = static_cast(0x80 | (0x3f & cp)); 90 | result[1] = 0x80 | static_cast((0x3f & (cp >> 6))); 91 | result[0] = 0xE0 | static_cast((0xf & (cp >> 12))); 92 | } 93 | else if (cp <= 0x10FFFF) 94 | { 95 | result.resize(4); 96 | result[3] = static_cast(0x80 | (0x3f & cp)); 97 | result[2] = static_cast(0x80 | (0x3f & (cp >> 6))); 98 | result[1] = static_cast(0x80 | (0x3f & (cp >> 12))); 99 | result[0] = static_cast(0xF0 | (0x7 & (cp >> 18))); 100 | } 101 | 102 | return result; 103 | } 104 | 105 | 106 | // Class Reader 107 | // ////////////////////////////////////////////////////////////////// 108 | 109 | Reader::Reader() 110 | : features_( Features::all() ) 111 | { 112 | } 113 | 114 | 115 | Reader::Reader( const Features &features ) 116 | : features_( features ) 117 | { 118 | } 119 | 120 | 121 | bool 122 | Reader::parse( const std::string &document, 123 | Value &root, 124 | bool collectComments ) 125 | { 126 | document_ = document; 127 | const char *begin = document_.c_str(); 128 | const char *end = begin + document_.length(); 129 | return parse( begin, end, root, collectComments ); 130 | } 131 | 132 | 133 | bool 134 | Reader::parse( std::istream& sin, 135 | Value &root, 136 | bool collectComments ) 137 | { 138 | //std::istream_iterator begin(sin); 139 | //std::istream_iterator end; 140 | // Those would allow streamed input from a file, if parse() were a 141 | // template function. 142 | 143 | // Since std::string is reference-counted, this at least does not 144 | // create an extra copy. 145 | std::string doc; 146 | std::getline(sin, doc, (char)EOF); 147 | return parse( doc, root, collectComments ); 148 | } 149 | 150 | bool 151 | Reader::parse( const char *beginDoc, const char *endDoc, 152 | Value &root, 153 | bool collectComments ) 154 | { 155 | if ( !features_.allowComments_ ) 156 | { 157 | collectComments = false; 158 | } 159 | 160 | begin_ = beginDoc; 161 | end_ = endDoc; 162 | collectComments_ = collectComments; 163 | current_ = begin_; 164 | lastValueEnd_ = 0; 165 | lastValue_ = 0; 166 | commentsBefore_ = ""; 167 | errors_.clear(); 168 | while ( !nodes_.empty() ) 169 | nodes_.pop(); 170 | nodes_.push( &root ); 171 | 172 | bool successful = readValue(); 173 | Token token; 174 | skipCommentTokens( token ); 175 | if ( collectComments_ && !commentsBefore_.empty() ) 176 | root.setComment( commentsBefore_, commentAfter ); 177 | if ( features_.strictRoot_ ) 178 | { 179 | if ( !root.isArray() && !root.isObject() ) 180 | { 181 | // Set error location to start of doc, ideally should be first token found in doc 182 | token.type_ = tokenError; 183 | token.start_ = beginDoc; 184 | token.end_ = endDoc; 185 | addError( "A valid JSON document must be either an array or an object value.", 186 | token ); 187 | return false; 188 | } 189 | } 190 | return successful; 191 | } 192 | 193 | 194 | bool 195 | Reader::readValue() 196 | { 197 | Token token; 198 | skipCommentTokens( token ); 199 | bool successful = true; 200 | 201 | if ( collectComments_ && !commentsBefore_.empty() ) 202 | { 203 | currentValue().setComment( commentsBefore_, commentBefore ); 204 | commentsBefore_ = ""; 205 | } 206 | 207 | 208 | switch ( token.type_ ) 209 | { 210 | case tokenObjectBegin: 211 | successful = readObject( token ); 212 | break; 213 | case tokenArrayBegin: 214 | successful = readArray( token ); 215 | break; 216 | case tokenNumber: 217 | successful = decodeNumber( token ); 218 | break; 219 | case tokenString: 220 | successful = decodeString( token ); 221 | break; 222 | case tokenTrue: 223 | currentValue() = true; 224 | break; 225 | case tokenFalse: 226 | currentValue() = false; 227 | break; 228 | case tokenNull: 229 | currentValue() = Value(); 230 | break; 231 | default: 232 | return addError( "Syntax error: value, object or array expected.", token ); 233 | } 234 | 235 | if ( collectComments_ ) 236 | { 237 | lastValueEnd_ = current_; 238 | lastValue_ = ¤tValue(); 239 | } 240 | 241 | return successful; 242 | } 243 | 244 | 245 | void 246 | Reader::skipCommentTokens( Token &token ) 247 | { 248 | if ( features_.allowComments_ ) 249 | { 250 | do 251 | { 252 | readToken( token ); 253 | } 254 | while ( token.type_ == tokenComment ); 255 | } 256 | else 257 | { 258 | readToken( token ); 259 | } 260 | } 261 | 262 | 263 | bool 264 | Reader::expectToken( TokenType type, Token &token, const char *message ) 265 | { 266 | readToken( token ); 267 | if ( token.type_ != type ) 268 | return addError( message, token ); 269 | return true; 270 | } 271 | 272 | 273 | bool 274 | Reader::readToken( Token &token ) 275 | { 276 | skipSpaces(); 277 | token.start_ = current_; 278 | Char c = getNextChar(); 279 | bool ok = true; 280 | switch ( c ) 281 | { 282 | case '{': 283 | token.type_ = tokenObjectBegin; 284 | break; 285 | case '}': 286 | token.type_ = tokenObjectEnd; 287 | break; 288 | case '[': 289 | token.type_ = tokenArrayBegin; 290 | break; 291 | case ']': 292 | token.type_ = tokenArrayEnd; 293 | break; 294 | case '"': 295 | token.type_ = tokenString; 296 | ok = readString(); 297 | break; 298 | case '/': 299 | token.type_ = tokenComment; 300 | ok = readComment(); 301 | break; 302 | case '0': 303 | case '1': 304 | case '2': 305 | case '3': 306 | case '4': 307 | case '5': 308 | case '6': 309 | case '7': 310 | case '8': 311 | case '9': 312 | case '-': 313 | token.type_ = tokenNumber; 314 | readNumber(); 315 | break; 316 | case 't': 317 | token.type_ = tokenTrue; 318 | ok = match( "rue", 3 ); 319 | break; 320 | case 'f': 321 | token.type_ = tokenFalse; 322 | ok = match( "alse", 4 ); 323 | break; 324 | case 'n': 325 | token.type_ = tokenNull; 326 | ok = match( "ull", 3 ); 327 | break; 328 | case ',': 329 | token.type_ = tokenArraySeparator; 330 | break; 331 | case ':': 332 | token.type_ = tokenMemberSeparator; 333 | break; 334 | case 0: 335 | token.type_ = tokenEndOfStream; 336 | break; 337 | default: 338 | ok = false; 339 | break; 340 | } 341 | if ( !ok ) 342 | token.type_ = tokenError; 343 | token.end_ = current_; 344 | return true; 345 | } 346 | 347 | 348 | void 349 | Reader::skipSpaces() 350 | { 351 | while ( current_ != end_ ) 352 | { 353 | Char c = *current_; 354 | if ( c == ' ' || c == '\t' || c == '\r' || c == '\n' ) 355 | ++current_; 356 | else 357 | break; 358 | } 359 | } 360 | 361 | 362 | bool 363 | Reader::match( Location pattern, 364 | int patternLength ) 365 | { 366 | if ( end_ - current_ < patternLength ) 367 | return false; 368 | int index = patternLength; 369 | while ( index-- ) 370 | if ( current_[index] != pattern[index] ) 371 | return false; 372 | current_ += patternLength; 373 | return true; 374 | } 375 | 376 | 377 | bool 378 | Reader::readComment() 379 | { 380 | Location commentBegin = current_ - 1; 381 | Char c = getNextChar(); 382 | bool successful = false; 383 | if ( c == '*' ) 384 | successful = readCStyleComment(); 385 | else if ( c == '/' ) 386 | successful = readCppStyleComment(); 387 | if ( !successful ) 388 | return false; 389 | 390 | if ( collectComments_ ) 391 | { 392 | CommentPlacement placement = commentBefore; 393 | if ( lastValueEnd_ && !containsNewLine( lastValueEnd_, commentBegin ) ) 394 | { 395 | if ( c != '*' || !containsNewLine( commentBegin, current_ ) ) 396 | placement = commentAfterOnSameLine; 397 | } 398 | 399 | addComment( commentBegin, current_, placement ); 400 | } 401 | return true; 402 | } 403 | 404 | 405 | void 406 | Reader::addComment( Location begin, 407 | Location end, 408 | CommentPlacement placement ) 409 | { 410 | assert( collectComments_ ); 411 | if ( placement == commentAfterOnSameLine ) 412 | { 413 | assert( lastValue_ != 0 ); 414 | lastValue_->setComment( std::string( begin, end ), placement ); 415 | } 416 | else 417 | { 418 | if ( !commentsBefore_.empty() ) 419 | commentsBefore_ += "\n"; 420 | commentsBefore_ += std::string( begin, end ); 421 | } 422 | } 423 | 424 | 425 | bool 426 | Reader::readCStyleComment() 427 | { 428 | while ( current_ != end_ ) 429 | { 430 | Char c = getNextChar(); 431 | if ( c == '*' && *current_ == '/' ) 432 | break; 433 | } 434 | return getNextChar() == '/'; 435 | } 436 | 437 | 438 | bool 439 | Reader::readCppStyleComment() 440 | { 441 | while ( current_ != end_ ) 442 | { 443 | Char c = getNextChar(); 444 | if ( c == '\r' || c == '\n' ) 445 | break; 446 | } 447 | return true; 448 | } 449 | 450 | 451 | void 452 | Reader::readNumber() 453 | { 454 | while ( current_ != end_ ) 455 | { 456 | if ( !(*current_ >= '0' && *current_ <= '9') && 457 | !in( *current_, '.', 'e', 'E', '+', '-' ) ) 458 | break; 459 | ++current_; 460 | } 461 | } 462 | 463 | bool 464 | Reader::readString() 465 | { 466 | Char c = 0; 467 | while ( current_ != end_ ) 468 | { 469 | c = getNextChar(); 470 | if ( c == '\\' ) 471 | getNextChar(); 472 | else if ( c == '"' ) 473 | break; 474 | } 475 | return c == '"'; 476 | } 477 | 478 | 479 | bool 480 | Reader::readObject( Token &tokenStart ) 481 | { 482 | Token tokenName; 483 | std::string name; 484 | currentValue() = Value( objectValue ); 485 | while ( readToken( tokenName ) ) 486 | { 487 | bool initialTokenOk = true; 488 | while ( tokenName.type_ == tokenComment && initialTokenOk ) 489 | initialTokenOk = readToken( tokenName ); 490 | if ( !initialTokenOk ) 491 | break; 492 | if ( tokenName.type_ == tokenObjectEnd && name.empty() ) // empty object 493 | return true; 494 | if ( tokenName.type_ != tokenString ) 495 | break; 496 | 497 | name = ""; 498 | if ( !decodeString( tokenName, name ) ) 499 | return recoverFromError( tokenObjectEnd ); 500 | 501 | Token colon; 502 | if ( !readToken( colon ) || colon.type_ != tokenMemberSeparator ) 503 | { 504 | return addErrorAndRecover( "Missing ':' after object member name", 505 | colon, 506 | tokenObjectEnd ); 507 | } 508 | Value &value = currentValue()[ name ]; 509 | nodes_.push( &value ); 510 | bool ok = readValue(); 511 | nodes_.pop(); 512 | if ( !ok ) // error already set 513 | return recoverFromError( tokenObjectEnd ); 514 | 515 | Token comma; 516 | if ( !readToken( comma ) 517 | || ( comma.type_ != tokenObjectEnd && 518 | comma.type_ != tokenArraySeparator && 519 | comma.type_ != tokenComment ) ) 520 | { 521 | return addErrorAndRecover( "Missing ',' or '}' in object declaration", 522 | comma, 523 | tokenObjectEnd ); 524 | } 525 | bool finalizeTokenOk = true; 526 | while ( comma.type_ == tokenComment && 527 | finalizeTokenOk ) 528 | finalizeTokenOk = readToken( comma ); 529 | if ( comma.type_ == tokenObjectEnd ) 530 | return true; 531 | } 532 | return addErrorAndRecover( "Missing '}' or object member name", 533 | tokenName, 534 | tokenObjectEnd ); 535 | } 536 | 537 | 538 | bool 539 | Reader::readArray( Token &tokenStart ) 540 | { 541 | currentValue() = Value( arrayValue ); 542 | skipSpaces(); 543 | if ( *current_ == ']' ) // empty array 544 | { 545 | Token endArray; 546 | readToken( endArray ); 547 | return true; 548 | } 549 | int index = 0; 550 | while ( true ) 551 | { 552 | Value &value = currentValue()[ index++ ]; 553 | nodes_.push( &value ); 554 | bool ok = readValue(); 555 | nodes_.pop(); 556 | if ( !ok ) // error already set 557 | return recoverFromError( tokenArrayEnd ); 558 | 559 | Token token; 560 | // Accept Comment after last item in the array. 561 | ok = readToken( token ); 562 | while ( token.type_ == tokenComment && ok ) 563 | { 564 | ok = readToken( token ); 565 | } 566 | bool badTokenType = ( token.type_ == tokenArraySeparator && 567 | token.type_ == tokenArrayEnd ); 568 | if ( !ok || badTokenType ) 569 | { 570 | return addErrorAndRecover( "Missing ',' or ']' in array declaration", 571 | token, 572 | tokenArrayEnd ); 573 | } 574 | if ( token.type_ == tokenArrayEnd ) 575 | break; 576 | } 577 | return true; 578 | } 579 | 580 | 581 | bool 582 | Reader::decodeNumber( Token &token ) 583 | { 584 | bool isDouble = false; 585 | for ( Location inspect = token.start_; inspect != token.end_; ++inspect ) 586 | { 587 | isDouble = isDouble 588 | || in( *inspect, '.', 'e', 'E', '+' ) 589 | || ( *inspect == '-' && inspect != token.start_ ); 590 | } 591 | if ( isDouble ) 592 | return decodeDouble( token ); 593 | Location current = token.start_; 594 | bool isNegative = *current == '-'; 595 | if ( isNegative ) 596 | ++current; 597 | Value::UInt threshold = (isNegative ? Value::UInt(-Value::minInt) 598 | : Value::maxUInt) / 10; 599 | Value::UInt value = 0; 600 | while ( current < token.end_ ) 601 | { 602 | Char c = *current++; 603 | if ( c < '0' || c > '9' ) 604 | return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); 605 | if ( value >= threshold ) 606 | return decodeDouble( token ); 607 | value = value * 10 + Value::UInt(c - '0'); 608 | } 609 | if ( isNegative ) 610 | currentValue() = -Value::Int( value ); 611 | else if ( value <= Value::UInt(Value::maxInt) ) 612 | currentValue() = Value::Int( value ); 613 | else 614 | currentValue() = value; 615 | return true; 616 | } 617 | 618 | 619 | bool 620 | Reader::decodeDouble( Token &token ) 621 | { 622 | double value = 0; 623 | const int bufferSize = 32; 624 | int count; 625 | int length = int(token.end_ - token.start_); 626 | if ( length <= bufferSize ) 627 | { 628 | Char buffer[bufferSize]; 629 | memcpy( buffer, token.start_, length ); 630 | buffer[length] = 0; 631 | count = sscanf( buffer, "%lf", &value ); 632 | } 633 | else 634 | { 635 | std::string buffer( token.start_, token.end_ ); 636 | count = sscanf( buffer.c_str(), "%lf", &value ); 637 | } 638 | 639 | if ( count != 1 ) 640 | return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); 641 | currentValue() = value; 642 | return true; 643 | } 644 | 645 | 646 | bool 647 | Reader::decodeString( Token &token ) 648 | { 649 | std::string decoded; 650 | if ( !decodeString( token, decoded ) ) 651 | return false; 652 | currentValue() = decoded; 653 | return true; 654 | } 655 | 656 | 657 | bool 658 | Reader::decodeString( Token &token, std::string &decoded ) 659 | { 660 | decoded.reserve( token.end_ - token.start_ - 2 ); 661 | Location current = token.start_ + 1; // skip '"' 662 | Location end = token.end_ - 1; // do not include '"' 663 | while ( current != end ) 664 | { 665 | Char c = *current++; 666 | if ( c == '"' ) 667 | break; 668 | else if ( c == '\\' ) 669 | { 670 | if ( current == end ) 671 | return addError( "Empty escape sequence in string", token, current ); 672 | Char escape = *current++; 673 | switch ( escape ) 674 | { 675 | case '"': decoded += '"'; break; 676 | case '/': decoded += '/'; break; 677 | case '\\': decoded += '\\'; break; 678 | case 'b': decoded += '\b'; break; 679 | case 'f': decoded += '\f'; break; 680 | case 'n': decoded += '\n'; break; 681 | case 'r': decoded += '\r'; break; 682 | case 't': decoded += '\t'; break; 683 | case 'u': 684 | { 685 | unsigned int unicode; 686 | if ( !decodeUnicodeCodePoint( token, current, end, unicode ) ) 687 | return false; 688 | decoded += codePointToUTF8(unicode); 689 | } 690 | break; 691 | default: 692 | return addError( "Bad escape sequence in string", token, current ); 693 | } 694 | } 695 | else 696 | { 697 | decoded += c; 698 | } 699 | } 700 | return true; 701 | } 702 | 703 | bool 704 | Reader::decodeUnicodeCodePoint( Token &token, 705 | Location ¤t, 706 | Location end, 707 | unsigned int &unicode ) 708 | { 709 | 710 | if ( !decodeUnicodeEscapeSequence( token, current, end, unicode ) ) 711 | return false; 712 | if (unicode >= 0xD800 && unicode <= 0xDBFF) 713 | { 714 | // surrogate pairs 715 | if (end - current < 6) 716 | return addError( "additional six characters expected to parse unicode surrogate pair.", token, current ); 717 | unsigned int surrogatePair; 718 | if (*(current++) == '\\' && *(current++)== 'u') 719 | { 720 | if (decodeUnicodeEscapeSequence( token, current, end, surrogatePair )) 721 | { 722 | unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); 723 | } 724 | else 725 | return false; 726 | } 727 | else 728 | return addError( "expecting another \\u token to begin the second half of a unicode surrogate pair", token, current ); 729 | } 730 | return true; 731 | } 732 | 733 | bool 734 | Reader::decodeUnicodeEscapeSequence( Token &token, 735 | Location ¤t, 736 | Location end, 737 | unsigned int &unicode ) 738 | { 739 | if ( end - current < 4 ) 740 | return addError( "Bad unicode escape sequence in string: four digits expected.", token, current ); 741 | unicode = 0; 742 | for ( int index =0; index < 4; ++index ) 743 | { 744 | Char c = *current++; 745 | unicode *= 16; 746 | if ( c >= '0' && c <= '9' ) 747 | unicode += c - '0'; 748 | else if ( c >= 'a' && c <= 'f' ) 749 | unicode += c - 'a' + 10; 750 | else if ( c >= 'A' && c <= 'F' ) 751 | unicode += c - 'A' + 10; 752 | else 753 | return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", token, current ); 754 | } 755 | return true; 756 | } 757 | 758 | 759 | bool 760 | Reader::addError( const std::string &message, 761 | Token &token, 762 | Location extra ) 763 | { 764 | ErrorInfo info; 765 | info.token_ = token; 766 | info.message_ = message; 767 | info.extra_ = extra; 768 | errors_.push_back( info ); 769 | return false; 770 | } 771 | 772 | 773 | bool 774 | Reader::recoverFromError( TokenType skipUntilToken ) 775 | { 776 | int errorCount = int(errors_.size()); 777 | Token skip; 778 | while ( true ) 779 | { 780 | if ( !readToken(skip) ) 781 | errors_.resize( errorCount ); // discard errors caused by recovery 782 | if ( skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream ) 783 | break; 784 | } 785 | errors_.resize( errorCount ); 786 | return false; 787 | } 788 | 789 | 790 | bool 791 | Reader::addErrorAndRecover( const std::string &message, 792 | Token &token, 793 | TokenType skipUntilToken ) 794 | { 795 | addError( message, token ); 796 | return recoverFromError( skipUntilToken ); 797 | } 798 | 799 | 800 | Value & 801 | Reader::currentValue() 802 | { 803 | return *(nodes_.top()); 804 | } 805 | 806 | 807 | Reader::Char 808 | Reader::getNextChar() 809 | { 810 | if ( current_ == end_ ) 811 | return 0; 812 | return *current_++; 813 | } 814 | 815 | 816 | void 817 | Reader::getLocationLineAndColumn( Location location, 818 | int &line, 819 | int &column ) const 820 | { 821 | Location current = begin_; 822 | Location lastLineStart = current; 823 | line = 0; 824 | while ( current < location && current != end_ ) 825 | { 826 | Char c = *current++; 827 | if ( c == '\r' ) 828 | { 829 | if ( *current == '\n' ) 830 | ++current; 831 | lastLineStart = current; 832 | ++line; 833 | } 834 | else if ( c == '\n' ) 835 | { 836 | lastLineStart = current; 837 | ++line; 838 | } 839 | } 840 | // column & line start at 1 841 | column = int(location - lastLineStart) + 1; 842 | ++line; 843 | } 844 | 845 | 846 | std::string 847 | Reader::getLocationLineAndColumn( Location location ) const 848 | { 849 | int line, column; 850 | getLocationLineAndColumn( location, line, column ); 851 | char buffer[18+16+16+1]; 852 | sprintf( buffer, "Line %d, Column %d", line, column ); 853 | return buffer; 854 | } 855 | 856 | 857 | std::string 858 | Reader::getFormatedErrorMessages() const 859 | { 860 | std::string formattedMessage; 861 | for ( Errors::const_iterator itError = errors_.begin(); 862 | itError != errors_.end(); 863 | ++itError ) 864 | { 865 | const ErrorInfo &error = *itError; 866 | formattedMessage += "* " + getLocationLineAndColumn( error.token_.start_ ) + "\n"; 867 | formattedMessage += " " + error.message_ + "\n"; 868 | if ( error.extra_ ) 869 | formattedMessage += "See " + getLocationLineAndColumn( error.extra_ ) + " for detail.\n"; 870 | } 871 | return formattedMessage; 872 | } 873 | 874 | 875 | std::istream& operator>>( std::istream &sin, Value &root ) 876 | { 877 | Json::Reader reader; 878 | bool ok = reader.parse(sin, root, true); 879 | //JSON_ASSERT( ok ); 880 | if (!ok) throw std::runtime_error(reader.getFormatedErrorMessages()); 881 | return sin; 882 | } 883 | 884 | 885 | } // namespace Json 886 | -------------------------------------------------------------------------------- /src/ext/json/json_valueiterator.inl: -------------------------------------------------------------------------------- 1 | // included by json_value.cpp 2 | // everything is within Json namespace 3 | 4 | 5 | // ////////////////////////////////////////////////////////////////// 6 | // ////////////////////////////////////////////////////////////////// 7 | // ////////////////////////////////////////////////////////////////// 8 | // class ValueIteratorBase 9 | // ////////////////////////////////////////////////////////////////// 10 | // ////////////////////////////////////////////////////////////////// 11 | // ////////////////////////////////////////////////////////////////// 12 | 13 | ValueIteratorBase::ValueIteratorBase() 14 | #ifndef JSON_VALUE_USE_INTERNAL_MAP 15 | : current_() 16 | , isNull_( true ) 17 | { 18 | } 19 | #else 20 | : isArray_( true ) 21 | , isNull_( true ) 22 | { 23 | iterator_.array_ = ValueInternalArray::IteratorState(); 24 | } 25 | #endif 26 | 27 | 28 | #ifndef JSON_VALUE_USE_INTERNAL_MAP 29 | ValueIteratorBase::ValueIteratorBase( const Value::ObjectValues::iterator ¤t ) 30 | : current_( current ) 31 | , isNull_( false ) 32 | { 33 | } 34 | #else 35 | ValueIteratorBase::ValueIteratorBase( const ValueInternalArray::IteratorState &state ) 36 | : isArray_( true ) 37 | { 38 | iterator_.array_ = state; 39 | } 40 | 41 | 42 | ValueIteratorBase::ValueIteratorBase( const ValueInternalMap::IteratorState &state ) 43 | : isArray_( false ) 44 | { 45 | iterator_.map_ = state; 46 | } 47 | #endif 48 | 49 | Value & 50 | ValueIteratorBase::deref() const 51 | { 52 | #ifndef JSON_VALUE_USE_INTERNAL_MAP 53 | return current_->second; 54 | #else 55 | if ( isArray_ ) 56 | return ValueInternalArray::dereference( iterator_.array_ ); 57 | return ValueInternalMap::value( iterator_.map_ ); 58 | #endif 59 | } 60 | 61 | 62 | void 63 | ValueIteratorBase::increment() 64 | { 65 | #ifndef JSON_VALUE_USE_INTERNAL_MAP 66 | ++current_; 67 | #else 68 | if ( isArray_ ) 69 | ValueInternalArray::increment( iterator_.array_ ); 70 | ValueInternalMap::increment( iterator_.map_ ); 71 | #endif 72 | } 73 | 74 | 75 | void 76 | ValueIteratorBase::decrement() 77 | { 78 | #ifndef JSON_VALUE_USE_INTERNAL_MAP 79 | --current_; 80 | #else 81 | if ( isArray_ ) 82 | ValueInternalArray::decrement( iterator_.array_ ); 83 | ValueInternalMap::decrement( iterator_.map_ ); 84 | #endif 85 | } 86 | 87 | 88 | ValueIteratorBase::difference_type 89 | ValueIteratorBase::computeDistance( const SelfType &other ) const 90 | { 91 | #ifndef JSON_VALUE_USE_INTERNAL_MAP 92 | # ifdef JSON_USE_CPPTL_SMALLMAP 93 | return current_ - other.current_; 94 | # else 95 | // Iterator for null value are initialized using the default 96 | // constructor, which initialize current_ to the default 97 | // std::map::iterator. As begin() and end() are two instance 98 | // of the default std::map::iterator, they can not be compared. 99 | // To allow this, we handle this comparison specifically. 100 | if ( isNull_ && other.isNull_ ) 101 | { 102 | return 0; 103 | } 104 | 105 | 106 | // Usage of std::distance is not portable (does not compile with Sun Studio 12 RogueWave STL, 107 | // which is the one used by default). 108 | // Using a portable hand-made version for non random iterator instead: 109 | // return difference_type( std::distance( current_, other.current_ ) ); 110 | difference_type myDistance = 0; 111 | for ( Value::ObjectValues::iterator it = current_; it != other.current_; ++it ) 112 | { 113 | ++myDistance; 114 | } 115 | return myDistance; 116 | # endif 117 | #else 118 | if ( isArray_ ) 119 | return ValueInternalArray::distance( iterator_.array_, other.iterator_.array_ ); 120 | return ValueInternalMap::distance( iterator_.map_, other.iterator_.map_ ); 121 | #endif 122 | } 123 | 124 | 125 | bool 126 | ValueIteratorBase::isEqual( const SelfType &other ) const 127 | { 128 | #ifndef JSON_VALUE_USE_INTERNAL_MAP 129 | if ( isNull_ ) 130 | { 131 | return other.isNull_; 132 | } 133 | return current_ == other.current_; 134 | #else 135 | if ( isArray_ ) 136 | return ValueInternalArray::equals( iterator_.array_, other.iterator_.array_ ); 137 | return ValueInternalMap::equals( iterator_.map_, other.iterator_.map_ ); 138 | #endif 139 | } 140 | 141 | 142 | void 143 | ValueIteratorBase::copy( const SelfType &other ) 144 | { 145 | #ifndef JSON_VALUE_USE_INTERNAL_MAP 146 | current_ = other.current_; 147 | #else 148 | if ( isArray_ ) 149 | iterator_.array_ = other.iterator_.array_; 150 | iterator_.map_ = other.iterator_.map_; 151 | #endif 152 | } 153 | 154 | 155 | Value 156 | ValueIteratorBase::key() const 157 | { 158 | #ifndef JSON_VALUE_USE_INTERNAL_MAP 159 | const Value::CZString czstring = (*current_).first; 160 | if ( czstring.c_str() ) 161 | { 162 | if ( czstring.isStaticString() ) 163 | return Value( StaticString( czstring.c_str() ) ); 164 | return Value( czstring.c_str() ); 165 | } 166 | return Value( czstring.index() ); 167 | #else 168 | if ( isArray_ ) 169 | return Value( ValueInternalArray::indexOf( iterator_.array_ ) ); 170 | bool isStatic; 171 | const char *memberName = ValueInternalMap::key( iterator_.map_, isStatic ); 172 | if ( isStatic ) 173 | return Value( StaticString( memberName ) ); 174 | return Value( memberName ); 175 | #endif 176 | } 177 | 178 | 179 | UInt 180 | ValueIteratorBase::index() const 181 | { 182 | #ifndef JSON_VALUE_USE_INTERNAL_MAP 183 | const Value::CZString czstring = (*current_).first; 184 | if ( !czstring.c_str() ) 185 | return czstring.index(); 186 | return Value::UInt( -1 ); 187 | #else 188 | if ( isArray_ ) 189 | return Value::UInt( ValueInternalArray::indexOf( iterator_.array_ ) ); 190 | return Value::UInt( -1 ); 191 | #endif 192 | } 193 | 194 | 195 | const char * 196 | ValueIteratorBase::memberName() const 197 | { 198 | #ifndef JSON_VALUE_USE_INTERNAL_MAP 199 | const char *name = (*current_).first.c_str(); 200 | return name ? name : ""; 201 | #else 202 | if ( !isArray_ ) 203 | return ValueInternalMap::key( iterator_.map_ ); 204 | return ""; 205 | #endif 206 | } 207 | 208 | 209 | // ////////////////////////////////////////////////////////////////// 210 | // ////////////////////////////////////////////////////////////////// 211 | // ////////////////////////////////////////////////////////////////// 212 | // class ValueConstIterator 213 | // ////////////////////////////////////////////////////////////////// 214 | // ////////////////////////////////////////////////////////////////// 215 | // ////////////////////////////////////////////////////////////////// 216 | 217 | ValueConstIterator::ValueConstIterator() 218 | { 219 | } 220 | 221 | 222 | #ifndef JSON_VALUE_USE_INTERNAL_MAP 223 | ValueConstIterator::ValueConstIterator( const Value::ObjectValues::iterator ¤t ) 224 | : ValueIteratorBase( current ) 225 | { 226 | } 227 | #else 228 | ValueConstIterator::ValueConstIterator( const ValueInternalArray::IteratorState &state ) 229 | : ValueIteratorBase( state ) 230 | { 231 | } 232 | 233 | ValueConstIterator::ValueConstIterator( const ValueInternalMap::IteratorState &state ) 234 | : ValueIteratorBase( state ) 235 | { 236 | } 237 | #endif 238 | 239 | ValueConstIterator & 240 | ValueConstIterator::operator =( const ValueIteratorBase &other ) 241 | { 242 | copy( other ); 243 | return *this; 244 | } 245 | 246 | 247 | // ////////////////////////////////////////////////////////////////// 248 | // ////////////////////////////////////////////////////////////////// 249 | // ////////////////////////////////////////////////////////////////// 250 | // class ValueIterator 251 | // ////////////////////////////////////////////////////////////////// 252 | // ////////////////////////////////////////////////////////////////// 253 | // ////////////////////////////////////////////////////////////////// 254 | 255 | ValueIterator::ValueIterator() 256 | { 257 | } 258 | 259 | 260 | #ifndef JSON_VALUE_USE_INTERNAL_MAP 261 | ValueIterator::ValueIterator( const Value::ObjectValues::iterator ¤t ) 262 | : ValueIteratorBase( current ) 263 | { 264 | } 265 | #else 266 | ValueIterator::ValueIterator( const ValueInternalArray::IteratorState &state ) 267 | : ValueIteratorBase( state ) 268 | { 269 | } 270 | 271 | ValueIterator::ValueIterator( const ValueInternalMap::IteratorState &state ) 272 | : ValueIteratorBase( state ) 273 | { 274 | } 275 | #endif 276 | 277 | ValueIterator::ValueIterator( const ValueConstIterator &other ) 278 | : ValueIteratorBase( other ) 279 | { 280 | } 281 | 282 | ValueIterator::ValueIterator( const ValueIterator &other ) 283 | : ValueIteratorBase( other ) 284 | { 285 | } 286 | 287 | ValueIterator & 288 | ValueIterator::operator =( const SelfType &other ) 289 | { 290 | copy( other ); 291 | return *this; 292 | } 293 | -------------------------------------------------------------------------------- /src/ext/json/json_writer.cpp: -------------------------------------------------------------------------------- 1 | #include "writer.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #if _MSC_VER >= 1400 // VC++ 8.0 11 | #pragma warning( disable : 4996 ) // disable warning about strdup being deprecated. 12 | #endif 13 | 14 | namespace Json { 15 | 16 | static bool isControlCharacter(char ch) 17 | { 18 | return ch > 0 && ch <= 0x1F; 19 | } 20 | 21 | static bool containsControlCharacter( const char* str ) 22 | { 23 | while ( *str ) 24 | { 25 | if ( isControlCharacter( *(str++) ) ) 26 | return true; 27 | } 28 | return false; 29 | } 30 | static void uintToString( unsigned int value, 31 | char *¤t ) 32 | { 33 | *--current = 0; 34 | do 35 | { 36 | *--current = (value % 10) + '0'; 37 | value /= 10; 38 | } 39 | while ( value != 0 ); 40 | } 41 | 42 | std::string valueToString( Int value ) 43 | { 44 | char buffer[32]; 45 | char *current = buffer + sizeof(buffer); 46 | bool isNegative = value < 0; 47 | if ( isNegative ) 48 | value = -value; 49 | uintToString( UInt(value), current ); 50 | if ( isNegative ) 51 | *--current = '-'; 52 | assert( current >= buffer ); 53 | return current; 54 | } 55 | 56 | 57 | std::string valueToString( UInt value ) 58 | { 59 | char buffer[32]; 60 | char *current = buffer + sizeof(buffer); 61 | uintToString( value, current ); 62 | assert( current >= buffer ); 63 | return current; 64 | } 65 | 66 | std::string valueToString( double value ) 67 | { 68 | char buffer[32]; 69 | #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning. 70 | sprintf_s(buffer, sizeof(buffer), "%#.16g", value); 71 | #else 72 | sprintf(buffer, "%#.16g", value); 73 | #endif 74 | char* ch = buffer + strlen(buffer) - 1; 75 | if (*ch != '0') return buffer; // nothing to truncate, so save time 76 | while(ch > buffer && *ch == '0'){ 77 | --ch; 78 | } 79 | char* last_nonzero = ch; 80 | while(ch >= buffer){ 81 | switch(*ch){ 82 | case '0': 83 | case '1': 84 | case '2': 85 | case '3': 86 | case '4': 87 | case '5': 88 | case '6': 89 | case '7': 90 | case '8': 91 | case '9': 92 | --ch; 93 | continue; 94 | case '.': 95 | // Truncate zeroes to save bytes in output, but keep one. 96 | *(last_nonzero+2) = '\0'; 97 | return buffer; 98 | default: 99 | return buffer; 100 | } 101 | } 102 | return buffer; 103 | } 104 | 105 | 106 | std::string valueToString( bool value ) 107 | { 108 | return value ? "true" : "false"; 109 | } 110 | 111 | std::string valueToQuotedString( const char *value ) 112 | { 113 | // Not sure how to handle unicode... 114 | if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter( value )) 115 | return std::string("\"") + value + "\""; 116 | // We have to walk value and escape any special characters. 117 | // Appending to std::string is not efficient, but this should be rare. 118 | // (Note: forward slashes are *not* rare, but I am not escaping them.) 119 | unsigned maxsize = strlen(value)*2 + 3; // allescaped+quotes+NULL 120 | std::string result; 121 | result.reserve(maxsize); // to avoid lots of mallocs 122 | result += "\""; 123 | for (const char* c=value; *c != 0; ++c) 124 | { 125 | switch(*c) 126 | { 127 | case '\"': 128 | result += "\\\""; 129 | break; 130 | case '\\': 131 | result += "\\\\"; 132 | break; 133 | case '\b': 134 | result += "\\b"; 135 | break; 136 | case '\f': 137 | result += "\\f"; 138 | break; 139 | case '\n': 140 | result += "\\n"; 141 | break; 142 | case '\r': 143 | result += "\\r"; 144 | break; 145 | case '\t': 146 | result += "\\t"; 147 | break; 148 | //case '/': 149 | // Even though \/ is considered a legal escape in JSON, a bare 150 | // slash is also legal, so I see no reason to escape it. 151 | // (I hope I am not misunderstanding something. 152 | // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); 161 | result += oss.str(); 162 | } 163 | else 164 | { 165 | result += *c; 166 | } 167 | break; 168 | } 169 | } 170 | result += "\""; 171 | return result; 172 | } 173 | 174 | // Class Writer 175 | // ////////////////////////////////////////////////////////////////// 176 | Writer::~Writer() 177 | { 178 | } 179 | 180 | 181 | // Class FastWriter 182 | // ////////////////////////////////////////////////////////////////// 183 | 184 | FastWriter::FastWriter() 185 | : yamlCompatiblityEnabled_( false ) 186 | { 187 | } 188 | 189 | 190 | void 191 | FastWriter::enableYAMLCompatibility() 192 | { 193 | yamlCompatiblityEnabled_ = true; 194 | } 195 | 196 | 197 | std::string 198 | FastWriter::write( const Value &root ) 199 | { 200 | document_ = ""; 201 | writeValue( root ); 202 | document_ += "\n"; 203 | return document_; 204 | } 205 | 206 | 207 | void 208 | FastWriter::writeValue( const Value &value ) 209 | { 210 | switch ( value.type() ) 211 | { 212 | case nullValue: 213 | document_ += "null"; 214 | break; 215 | case intValue: 216 | document_ += valueToString( value.asInt() ); 217 | break; 218 | case uintValue: 219 | document_ += valueToString( value.asUInt() ); 220 | break; 221 | case realValue: 222 | document_ += valueToString( value.asDouble() ); 223 | break; 224 | case stringValue: 225 | document_ += valueToQuotedString( value.asCString() ); 226 | break; 227 | case booleanValue: 228 | document_ += valueToString( value.asBool() ); 229 | break; 230 | case arrayValue: 231 | { 232 | document_ += "["; 233 | int size = value.size(); 234 | for ( int index =0; index < size; ++index ) 235 | { 236 | if ( index > 0 ) 237 | document_ += ","; 238 | writeValue( value[index] ); 239 | } 240 | document_ += "]"; 241 | } 242 | break; 243 | case objectValue: 244 | { 245 | Value::Members members( value.getMemberNames() ); 246 | document_ += "{"; 247 | for ( Value::Members::iterator it = members.begin(); 248 | it != members.end(); 249 | ++it ) 250 | { 251 | const std::string &name = *it; 252 | if ( it != members.begin() ) 253 | document_ += ","; 254 | document_ += valueToQuotedString( name.c_str() ); 255 | document_ += yamlCompatiblityEnabled_ ? ": " 256 | : ":"; 257 | writeValue( value[name] ); 258 | } 259 | document_ += "}"; 260 | } 261 | break; 262 | } 263 | } 264 | 265 | 266 | // Class StyledWriter 267 | // ////////////////////////////////////////////////////////////////// 268 | 269 | StyledWriter::StyledWriter() 270 | : rightMargin_( 74 ) 271 | , indentSize_( 3 ) 272 | { 273 | } 274 | 275 | 276 | std::string 277 | StyledWriter::write( const Value &root ) 278 | { 279 | document_ = ""; 280 | addChildValues_ = false; 281 | indentString_ = ""; 282 | writeCommentBeforeValue( root ); 283 | writeValue( root ); 284 | writeCommentAfterValueOnSameLine( root ); 285 | document_ += "\n"; 286 | return document_; 287 | } 288 | 289 | 290 | void 291 | StyledWriter::writeValue( const Value &value ) 292 | { 293 | switch ( value.type() ) 294 | { 295 | case nullValue: 296 | pushValue( "null" ); 297 | break; 298 | case intValue: 299 | pushValue( valueToString( value.asInt() ) ); 300 | break; 301 | case uintValue: 302 | pushValue( valueToString( value.asUInt() ) ); 303 | break; 304 | case realValue: 305 | pushValue( valueToString( value.asDouble() ) ); 306 | break; 307 | case stringValue: 308 | pushValue( valueToQuotedString( value.asCString() ) ); 309 | break; 310 | case booleanValue: 311 | pushValue( valueToString( value.asBool() ) ); 312 | break; 313 | case arrayValue: 314 | writeArrayValue( value); 315 | break; 316 | case objectValue: 317 | { 318 | Value::Members members( value.getMemberNames() ); 319 | if ( members.empty() ) 320 | pushValue( "{}" ); 321 | else 322 | { 323 | writeWithIndent( "{" ); 324 | indent(); 325 | Value::Members::iterator it = members.begin(); 326 | while ( true ) 327 | { 328 | const std::string &name = *it; 329 | const Value &childValue = value[name]; 330 | writeCommentBeforeValue( childValue ); 331 | writeWithIndent( valueToQuotedString( name.c_str() ) ); 332 | document_ += " : "; 333 | writeValue( childValue ); 334 | if ( ++it == members.end() ) 335 | { 336 | writeCommentAfterValueOnSameLine( childValue ); 337 | break; 338 | } 339 | document_ += ","; 340 | writeCommentAfterValueOnSameLine( childValue ); 341 | } 342 | unindent(); 343 | writeWithIndent( "}" ); 344 | } 345 | } 346 | break; 347 | } 348 | } 349 | 350 | 351 | void 352 | StyledWriter::writeArrayValue( const Value &value ) 353 | { 354 | unsigned size = value.size(); 355 | if ( size == 0 ) 356 | pushValue( "[]" ); 357 | else 358 | { 359 | bool isArrayMultiLine = isMultineArray( value ); 360 | if ( isArrayMultiLine ) 361 | { 362 | writeWithIndent( "[" ); 363 | indent(); 364 | bool hasChildValue = !childValues_.empty(); 365 | unsigned index =0; 366 | while ( true ) 367 | { 368 | const Value &childValue = value[index]; 369 | writeCommentBeforeValue( childValue ); 370 | if ( hasChildValue ) 371 | writeWithIndent( childValues_[index] ); 372 | else 373 | { 374 | writeIndent(); 375 | writeValue( childValue ); 376 | } 377 | if ( ++index == size ) 378 | { 379 | writeCommentAfterValueOnSameLine( childValue ); 380 | break; 381 | } 382 | document_ += ","; 383 | writeCommentAfterValueOnSameLine( childValue ); 384 | } 385 | unindent(); 386 | writeWithIndent( "]" ); 387 | } 388 | else // output on a single line 389 | { 390 | assert( childValues_.size() == size ); 391 | document_ += "[ "; 392 | for ( unsigned index =0; index < size; ++index ) 393 | { 394 | if ( index > 0 ) 395 | document_ += ", "; 396 | document_ += childValues_[index]; 397 | } 398 | document_ += " ]"; 399 | } 400 | } 401 | } 402 | 403 | 404 | bool 405 | StyledWriter::isMultineArray( const Value &value ) 406 | { 407 | int size = value.size(); 408 | bool isMultiLine = size*3 >= rightMargin_ ; 409 | childValues_.clear(); 410 | for ( int index =0; index < size && !isMultiLine; ++index ) 411 | { 412 | const Value &childValue = value[index]; 413 | isMultiLine = isMultiLine || 414 | ( (childValue.isArray() || childValue.isObject()) && 415 | childValue.size() > 0 ); 416 | } 417 | if ( !isMultiLine ) // check if line length > max line length 418 | { 419 | childValues_.reserve( size ); 420 | addChildValues_ = true; 421 | int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]' 422 | for ( int index =0; index < size && !isMultiLine; ++index ) 423 | { 424 | writeValue( value[index] ); 425 | lineLength += int( childValues_[index].length() ); 426 | isMultiLine = isMultiLine && hasCommentForValue( value[index] ); 427 | } 428 | addChildValues_ = false; 429 | isMultiLine = isMultiLine || lineLength >= rightMargin_; 430 | } 431 | return isMultiLine; 432 | } 433 | 434 | 435 | void 436 | StyledWriter::pushValue( const std::string &value ) 437 | { 438 | if ( addChildValues_ ) 439 | childValues_.push_back( value ); 440 | else 441 | document_ += value; 442 | } 443 | 444 | 445 | void 446 | StyledWriter::writeIndent() 447 | { 448 | if ( !document_.empty() ) 449 | { 450 | char last = document_[document_.length()-1]; 451 | if ( last == ' ' ) // already indented 452 | return; 453 | if ( last != '\n' ) // Comments may add new-line 454 | document_ += '\n'; 455 | } 456 | document_ += indentString_; 457 | } 458 | 459 | 460 | void 461 | StyledWriter::writeWithIndent( const std::string &value ) 462 | { 463 | writeIndent(); 464 | document_ += value; 465 | } 466 | 467 | 468 | void 469 | StyledWriter::indent() 470 | { 471 | indentString_ += std::string( indentSize_, ' ' ); 472 | } 473 | 474 | 475 | void 476 | StyledWriter::unindent() 477 | { 478 | assert( int(indentString_.size()) >= indentSize_ ); 479 | indentString_.resize( indentString_.size() - indentSize_ ); 480 | } 481 | 482 | 483 | void 484 | StyledWriter::writeCommentBeforeValue( const Value &root ) 485 | { 486 | if ( !root.hasComment( commentBefore ) ) 487 | return; 488 | document_ += normalizeEOL( root.getComment( commentBefore ) ); 489 | document_ += "\n"; 490 | } 491 | 492 | 493 | void 494 | StyledWriter::writeCommentAfterValueOnSameLine( const Value &root ) 495 | { 496 | if ( root.hasComment( commentAfterOnSameLine ) ) 497 | document_ += " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) ); 498 | 499 | if ( root.hasComment( commentAfter ) ) 500 | { 501 | document_ += "\n"; 502 | document_ += normalizeEOL( root.getComment( commentAfter ) ); 503 | document_ += "\n"; 504 | } 505 | } 506 | 507 | 508 | bool 509 | StyledWriter::hasCommentForValue( const Value &value ) 510 | { 511 | return value.hasComment( commentBefore ) 512 | || value.hasComment( commentAfterOnSameLine ) 513 | || value.hasComment( commentAfter ); 514 | } 515 | 516 | 517 | std::string 518 | StyledWriter::normalizeEOL( const std::string &text ) 519 | { 520 | std::string normalized; 521 | normalized.reserve( text.length() ); 522 | const char *begin = text.c_str(); 523 | const char *end = begin + text.length(); 524 | const char *current = begin; 525 | while ( current != end ) 526 | { 527 | char c = *current++; 528 | if ( c == '\r' ) // mac or dos EOL 529 | { 530 | if ( *current == '\n' ) // convert dos EOL 531 | ++current; 532 | normalized += '\n'; 533 | } 534 | else // handle unix EOL & other char 535 | normalized += c; 536 | } 537 | return normalized; 538 | } 539 | 540 | 541 | // Class StyledStreamWriter 542 | // ////////////////////////////////////////////////////////////////// 543 | 544 | StyledStreamWriter::StyledStreamWriter( std::string indentation ) 545 | : document_(NULL) 546 | , rightMargin_( 74 ) 547 | , indentation_( indentation ) 548 | { 549 | } 550 | 551 | 552 | void 553 | StyledStreamWriter::write( std::ostream &out, const Value &root ) 554 | { 555 | document_ = &out; 556 | addChildValues_ = false; 557 | indentString_ = ""; 558 | writeCommentBeforeValue( root ); 559 | writeValue( root ); 560 | writeCommentAfterValueOnSameLine( root ); 561 | *document_ << "\n"; 562 | document_ = NULL; // Forget the stream, for safety. 563 | } 564 | 565 | 566 | void 567 | StyledStreamWriter::writeValue( const Value &value ) 568 | { 569 | switch ( value.type() ) 570 | { 571 | case nullValue: 572 | pushValue( "null" ); 573 | break; 574 | case intValue: 575 | pushValue( valueToString( value.asInt() ) ); 576 | break; 577 | case uintValue: 578 | pushValue( valueToString( value.asUInt() ) ); 579 | break; 580 | case realValue: 581 | pushValue( valueToString( value.asDouble() ) ); 582 | break; 583 | case stringValue: 584 | pushValue( valueToQuotedString( value.asCString() ) ); 585 | break; 586 | case booleanValue: 587 | pushValue( valueToString( value.asBool() ) ); 588 | break; 589 | case arrayValue: 590 | writeArrayValue( value); 591 | break; 592 | case objectValue: 593 | { 594 | Value::Members members( value.getMemberNames() ); 595 | if ( members.empty() ) 596 | pushValue( "{}" ); 597 | else 598 | { 599 | writeWithIndent( "{" ); 600 | indent(); 601 | Value::Members::iterator it = members.begin(); 602 | while ( true ) 603 | { 604 | const std::string &name = *it; 605 | const Value &childValue = value[name]; 606 | writeCommentBeforeValue( childValue ); 607 | writeWithIndent( valueToQuotedString( name.c_str() ) ); 608 | *document_ << " : "; 609 | writeValue( childValue ); 610 | if ( ++it == members.end() ) 611 | { 612 | writeCommentAfterValueOnSameLine( childValue ); 613 | break; 614 | } 615 | *document_ << ","; 616 | writeCommentAfterValueOnSameLine( childValue ); 617 | } 618 | unindent(); 619 | writeWithIndent( "}" ); 620 | } 621 | } 622 | break; 623 | } 624 | } 625 | 626 | 627 | void 628 | StyledStreamWriter::writeArrayValue( const Value &value ) 629 | { 630 | unsigned size = value.size(); 631 | if ( size == 0 ) 632 | pushValue( "[]" ); 633 | else 634 | { 635 | bool isArrayMultiLine = isMultineArray( value ); 636 | if ( isArrayMultiLine ) 637 | { 638 | writeWithIndent( "[" ); 639 | indent(); 640 | bool hasChildValue = !childValues_.empty(); 641 | unsigned index =0; 642 | while ( true ) 643 | { 644 | const Value &childValue = value[index]; 645 | writeCommentBeforeValue( childValue ); 646 | if ( hasChildValue ) 647 | writeWithIndent( childValues_[index] ); 648 | else 649 | { 650 | writeIndent(); 651 | writeValue( childValue ); 652 | } 653 | if ( ++index == size ) 654 | { 655 | writeCommentAfterValueOnSameLine( childValue ); 656 | break; 657 | } 658 | *document_ << ","; 659 | writeCommentAfterValueOnSameLine( childValue ); 660 | } 661 | unindent(); 662 | writeWithIndent( "]" ); 663 | } 664 | else // output on a single line 665 | { 666 | assert( childValues_.size() == size ); 667 | *document_ << "[ "; 668 | for ( unsigned index =0; index < size; ++index ) 669 | { 670 | if ( index > 0 ) 671 | *document_ << ", "; 672 | *document_ << childValues_[index]; 673 | } 674 | *document_ << " ]"; 675 | } 676 | } 677 | } 678 | 679 | 680 | bool 681 | StyledStreamWriter::isMultineArray( const Value &value ) 682 | { 683 | int size = value.size(); 684 | bool isMultiLine = size*3 >= rightMargin_ ; 685 | childValues_.clear(); 686 | for ( int index =0; index < size && !isMultiLine; ++index ) 687 | { 688 | const Value &childValue = value[index]; 689 | isMultiLine = isMultiLine || 690 | ( (childValue.isArray() || childValue.isObject()) && 691 | childValue.size() > 0 ); 692 | } 693 | if ( !isMultiLine ) // check if line length > max line length 694 | { 695 | childValues_.reserve( size ); 696 | addChildValues_ = true; 697 | int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]' 698 | for ( int index =0; index < size && !isMultiLine; ++index ) 699 | { 700 | writeValue( value[index] ); 701 | lineLength += int( childValues_[index].length() ); 702 | isMultiLine = isMultiLine && hasCommentForValue( value[index] ); 703 | } 704 | addChildValues_ = false; 705 | isMultiLine = isMultiLine || lineLength >= rightMargin_; 706 | } 707 | return isMultiLine; 708 | } 709 | 710 | 711 | void 712 | StyledStreamWriter::pushValue( const std::string &value ) 713 | { 714 | if ( addChildValues_ ) 715 | childValues_.push_back( value ); 716 | else 717 | *document_ << value; 718 | } 719 | 720 | 721 | void 722 | StyledStreamWriter::writeIndent() 723 | { 724 | /* 725 | Some comments in this method would have been nice. ;-) 726 | 727 | if ( !document_.empty() ) 728 | { 729 | char last = document_[document_.length()-1]; 730 | if ( last == ' ' ) // already indented 731 | return; 732 | if ( last != '\n' ) // Comments may add new-line 733 | *document_ << '\n'; 734 | } 735 | */ 736 | *document_ << '\n' << indentString_; 737 | } 738 | 739 | 740 | void 741 | StyledStreamWriter::writeWithIndent( const std::string &value ) 742 | { 743 | writeIndent(); 744 | *document_ << value; 745 | } 746 | 747 | 748 | void 749 | StyledStreamWriter::indent() 750 | { 751 | indentString_ += indentation_; 752 | } 753 | 754 | 755 | void 756 | StyledStreamWriter::unindent() 757 | { 758 | assert( indentString_.size() >= indentation_.size() ); 759 | indentString_.resize( indentString_.size() - indentation_.size() ); 760 | } 761 | 762 | 763 | void 764 | StyledStreamWriter::writeCommentBeforeValue( const Value &root ) 765 | { 766 | if ( !root.hasComment( commentBefore ) ) 767 | return; 768 | *document_ << normalizeEOL( root.getComment( commentBefore ) ); 769 | *document_ << "\n"; 770 | } 771 | 772 | 773 | void 774 | StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root ) 775 | { 776 | if ( root.hasComment( commentAfterOnSameLine ) ) 777 | *document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) ); 778 | 779 | if ( root.hasComment( commentAfter ) ) 780 | { 781 | *document_ << "\n"; 782 | *document_ << normalizeEOL( root.getComment( commentAfter ) ); 783 | *document_ << "\n"; 784 | } 785 | } 786 | 787 | 788 | bool 789 | StyledStreamWriter::hasCommentForValue( const Value &value ) 790 | { 791 | return value.hasComment( commentBefore ) 792 | || value.hasComment( commentAfterOnSameLine ) 793 | || value.hasComment( commentAfter ); 794 | } 795 | 796 | 797 | std::string 798 | StyledStreamWriter::normalizeEOL( const std::string &text ) 799 | { 800 | std::string normalized; 801 | normalized.reserve( text.length() ); 802 | const char *begin = text.c_str(); 803 | const char *end = begin + text.length(); 804 | const char *current = begin; 805 | while ( current != end ) 806 | { 807 | char c = *current++; 808 | if ( c == '\r' ) // mac or dos EOL 809 | { 810 | if ( *current == '\n' ) // convert dos EOL 811 | ++current; 812 | normalized += '\n'; 813 | } 814 | else // handle unix EOL & other char 815 | normalized += c; 816 | } 817 | return normalized; 818 | } 819 | 820 | 821 | std::ostream& operator<<( std::ostream &sout, const Value &root ) 822 | { 823 | Json::StyledStreamWriter writer; 824 | writer.write(sout, root); 825 | return sout; 826 | } 827 | 828 | 829 | } // namespace Json 830 | -------------------------------------------------------------------------------- /src/ext/json/reader.h: -------------------------------------------------------------------------------- 1 | #ifndef CPPTL_JSON_READER_H_INCLUDED 2 | # define CPPTL_JSON_READER_H_INCLUDED 3 | 4 | # include "features.h" 5 | # include "value.h" 6 | # include 7 | # include 8 | # include 9 | # include 10 | 11 | namespace Json { 12 | 13 | /** \brief Unserialize a JSON document into a Value. 14 | * 15 | */ 16 | class JSON_API Reader 17 | { 18 | public: 19 | typedef char Char; 20 | typedef const Char *Location; 21 | 22 | /** \brief Constructs a Reader allowing all features 23 | * for parsing. 24 | */ 25 | Reader(); 26 | 27 | /** \brief Constructs a Reader allowing the specified feature set 28 | * for parsing. 29 | */ 30 | Reader( const Features &features ); 31 | 32 | /** \brief Read a Value from a JSON document. 33 | * \param document UTF-8 encoded string containing the document to read. 34 | * \param root [out] Contains the root value of the document if it was 35 | * successfully parsed. 36 | * \param collectComments \c true to collect comment and allow writing them back during 37 | * serialization, \c false to discard comments. 38 | * This parameter is ignored if Features::allowComments_ 39 | * is \c false. 40 | * \return \c true if the document was successfully parsed, \c false if an error occurred. 41 | */ 42 | bool parse( const std::string &document, 43 | Value &root, 44 | bool collectComments = true ); 45 | 46 | /** \brief Read a Value from a JSON document. 47 | * \param document UTF-8 encoded string containing the document to read. 48 | * \param root [out] Contains the root value of the document if it was 49 | * successfully parsed. 50 | * \param collectComments \c true to collect comment and allow writing them back during 51 | * serialization, \c false to discard comments. 52 | * This parameter is ignored if Features::allowComments_ 53 | * is \c false. 54 | * \return \c true if the document was successfully parsed, \c false if an error occurred. 55 | */ 56 | bool parse( const char *beginDoc, const char *endDoc, 57 | Value &root, 58 | bool collectComments = true ); 59 | 60 | /// \brief Parse from input stream. 61 | /// \see Json::operator>>(std::istream&, Json::Value&). 62 | bool parse( std::istream &is, 63 | Value &root, 64 | bool collectComments = true ); 65 | 66 | /** \brief Returns a user friendly string that list errors in the parsed document. 67 | * \return Formatted error message with the list of errors with their location in 68 | * the parsed document. An empty string is returned if no error occurred 69 | * during parsing. 70 | */ 71 | std::string getFormatedErrorMessages() const; 72 | 73 | private: 74 | enum TokenType 75 | { 76 | tokenEndOfStream = 0, 77 | tokenObjectBegin, 78 | tokenObjectEnd, 79 | tokenArrayBegin, 80 | tokenArrayEnd, 81 | tokenString, 82 | tokenNumber, 83 | tokenTrue, 84 | tokenFalse, 85 | tokenNull, 86 | tokenArraySeparator, 87 | tokenMemberSeparator, 88 | tokenComment, 89 | tokenError 90 | }; 91 | 92 | class Token 93 | { 94 | public: 95 | TokenType type_; 96 | Location start_; 97 | Location end_; 98 | }; 99 | 100 | class ErrorInfo 101 | { 102 | public: 103 | Token token_; 104 | std::string message_; 105 | Location extra_; 106 | }; 107 | 108 | typedef std::deque Errors; 109 | 110 | bool expectToken( TokenType type, Token &token, const char *message ); 111 | bool readToken( Token &token ); 112 | void skipSpaces(); 113 | bool match( Location pattern, 114 | int patternLength ); 115 | bool readComment(); 116 | bool readCStyleComment(); 117 | bool readCppStyleComment(); 118 | bool readString(); 119 | void readNumber(); 120 | bool readValue(); 121 | bool readObject( Token &token ); 122 | bool readArray( Token &token ); 123 | bool decodeNumber( Token &token ); 124 | bool decodeString( Token &token ); 125 | bool decodeString( Token &token, std::string &decoded ); 126 | bool decodeDouble( Token &token ); 127 | bool decodeUnicodeCodePoint( Token &token, 128 | Location ¤t, 129 | Location end, 130 | unsigned int &unicode ); 131 | bool decodeUnicodeEscapeSequence( Token &token, 132 | Location ¤t, 133 | Location end, 134 | unsigned int &unicode ); 135 | bool addError( const std::string &message, 136 | Token &token, 137 | Location extra = 0 ); 138 | bool recoverFromError( TokenType skipUntilToken ); 139 | bool addErrorAndRecover( const std::string &message, 140 | Token &token, 141 | TokenType skipUntilToken ); 142 | void skipUntilSpace(); 143 | Value ¤tValue(); 144 | Char getNextChar(); 145 | void getLocationLineAndColumn( Location location, 146 | int &line, 147 | int &column ) const; 148 | std::string getLocationLineAndColumn( Location location ) const; 149 | void addComment( Location begin, 150 | Location end, 151 | CommentPlacement placement ); 152 | void skipCommentTokens( Token &token ); 153 | 154 | typedef std::stack Nodes; 155 | Nodes nodes_; 156 | Errors errors_; 157 | std::string document_; 158 | Location begin_; 159 | Location end_; 160 | Location current_; 161 | Location lastValueEnd_; 162 | Value *lastValue_; 163 | std::string commentsBefore_; 164 | Features features_; 165 | bool collectComments_; 166 | }; 167 | 168 | /** \brief Read from 'sin' into 'root'. 169 | 170 | Always keep comments from the input JSON. 171 | 172 | This can be used to read a file into a particular sub-object. 173 | For example: 174 | \code 175 | Json::Value root; 176 | cin >> root["dir"]["file"]; 177 | cout << root; 178 | \endcode 179 | Result: 180 | \verbatim 181 | { 182 | "dir": { 183 | "file": { 184 | // The input stream JSON would be nested here. 185 | } 186 | } 187 | } 188 | \endverbatim 189 | \throw std::exception on parse error. 190 | \see Json::operator<<() 191 | */ 192 | std::istream& operator>>( std::istream&, Value& ); 193 | 194 | } // namespace Json 195 | 196 | #endif // CPPTL_JSON_READER_H_INCLUDED 197 | -------------------------------------------------------------------------------- /src/ext/json/value.h: -------------------------------------------------------------------------------- 1 | #ifndef CPPTL_JSON_H_INCLUDED 2 | # define CPPTL_JSON_H_INCLUDED 3 | 4 | # include "forwards.h" 5 | # include 6 | # include 7 | 8 | # ifndef JSON_USE_CPPTL_SMALLMAP 9 | # include 10 | # else 11 | # include 12 | # endif 13 | # ifdef JSON_USE_CPPTL 14 | # include 15 | # endif 16 | 17 | /** \brief JSON (JavaScript Object Notation). 18 | */ 19 | namespace Json { 20 | 21 | /** \brief Type of the value held by a Value object. 22 | */ 23 | enum ValueType 24 | { 25 | nullValue = 0, ///< 'null' value 26 | intValue, ///< signed integer value 27 | uintValue, ///< unsigned integer value 28 | realValue, ///< double value 29 | stringValue, ///< UTF-8 string value 30 | booleanValue, ///< bool value 31 | arrayValue, ///< array value (ordered list) 32 | objectValue ///< object value (collection of name/value pairs). 33 | }; 34 | 35 | enum CommentPlacement 36 | { 37 | commentBefore = 0, ///< a comment placed on the line before a value 38 | commentAfterOnSameLine, ///< a comment just after a value on the same line 39 | commentAfter, ///< a comment on the line after a value (only make sense for root value) 40 | numberOfCommentPlacement 41 | }; 42 | 43 | //# ifdef JSON_USE_CPPTL 44 | // typedef CppTL::AnyEnumerator EnumMemberNames; 45 | // typedef CppTL::AnyEnumerator EnumValues; 46 | //# endif 47 | 48 | /** \brief Lightweight wrapper to tag static string. 49 | * 50 | * Value constructor and objectValue member assignement takes advantage of the 51 | * StaticString and avoid the cost of string duplication when storing the 52 | * string or the member name. 53 | * 54 | * Example of usage: 55 | * \code 56 | * Json::Value aValue( StaticString("some text") ); 57 | * Json::Value object; 58 | * static const StaticString code("code"); 59 | * object[code] = 1234; 60 | * \endcode 61 | */ 62 | class JSON_API StaticString 63 | { 64 | public: 65 | explicit StaticString( const char *czstring ) 66 | : str_( czstring ) 67 | { 68 | } 69 | 70 | operator const char *() const 71 | { 72 | return str_; 73 | } 74 | 75 | const char *c_str() const 76 | { 77 | return str_; 78 | } 79 | 80 | private: 81 | const char *str_; 82 | }; 83 | 84 | /** \brief Represents a JSON value. 85 | * 86 | * This class is a discriminated union wrapper that can represents a: 87 | * - signed integer [range: Value::minInt - Value::maxInt] 88 | * - unsigned integer (range: 0 - Value::maxUInt) 89 | * - double 90 | * - UTF-8 string 91 | * - boolean 92 | * - 'null' 93 | * - an ordered list of Value 94 | * - collection of name/value pairs (javascript object) 95 | * 96 | * The type of the held value is represented by a #ValueType and 97 | * can be obtained using type(). 98 | * 99 | * values of an #objectValue or #arrayValue can be accessed using operator[]() methods. 100 | * Non const methods will automatically create the a #nullValue element 101 | * if it does not exist. 102 | * The sequence of an #arrayValue will be automatically resize and initialized 103 | * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. 104 | * 105 | * The get() methods can be used to obtanis default value in the case the required element 106 | * does not exist. 107 | * 108 | * It is possible to iterate over the list of a #objectValue values using 109 | * the getMemberNames() method. 110 | */ 111 | class JSON_API Value 112 | { 113 | friend class ValueIteratorBase; 114 | # ifdef JSON_VALUE_USE_INTERNAL_MAP 115 | friend class ValueInternalLink; 116 | friend class ValueInternalMap; 117 | # endif 118 | public: 119 | typedef std::vector Members; 120 | typedef ValueIterator iterator; 121 | typedef ValueConstIterator const_iterator; 122 | typedef Json::UInt UInt; 123 | typedef Json::Int Int; 124 | typedef UInt ArrayIndex; 125 | 126 | static const Value null; 127 | static const Int minInt; 128 | static const Int maxInt; 129 | static const UInt maxUInt; 130 | 131 | private: 132 | #ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION 133 | # ifndef JSON_VALUE_USE_INTERNAL_MAP 134 | class CZString 135 | { 136 | public: 137 | enum DuplicationPolicy 138 | { 139 | noDuplication = 0, 140 | duplicate, 141 | duplicateOnCopy 142 | }; 143 | CZString( int index ); 144 | CZString( const char *cstr, DuplicationPolicy allocate ); 145 | CZString( const CZString &other ); 146 | ~CZString(); 147 | CZString &operator =( const CZString &other ); 148 | bool operator<( const CZString &other ) const; 149 | bool operator==( const CZString &other ) const; 150 | int index() const; 151 | const char *c_str() const; 152 | bool isStaticString() const; 153 | private: 154 | void swap( CZString &other ); 155 | const char *cstr_; 156 | int index_; 157 | }; 158 | 159 | public: 160 | # ifndef JSON_USE_CPPTL_SMALLMAP 161 | typedef std::map ObjectValues; 162 | # else 163 | typedef CppTL::SmallMap ObjectValues; 164 | # endif // ifndef JSON_USE_CPPTL_SMALLMAP 165 | # endif // ifndef JSON_VALUE_USE_INTERNAL_MAP 166 | #endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION 167 | 168 | public: 169 | /** \brief Create a default Value of the given type. 170 | 171 | This is a very useful constructor. 172 | To create an empty array, pass arrayValue. 173 | To create an empty object, pass objectValue. 174 | Another Value can then be set to this one by assignment. 175 | This is useful since clear() and resize() will not alter types. 176 | 177 | Examples: 178 | \code 179 | Json::Value null_value; // null 180 | Json::Value arr_value(Json::arrayValue); // [] 181 | Json::Value obj_value(Json::objectValue); // {} 182 | \endcode 183 | */ 184 | Value( ValueType type = nullValue ); 185 | Value( Int value ); 186 | Value( UInt value ); 187 | Value( double value ); 188 | Value( const char *value ); 189 | Value( const char *beginValue, const char *endValue ); 190 | /** \brief Constructs a value from a static string. 191 | 192 | * Like other value string constructor but do not duplicate the string for 193 | * internal storage. The given string must remain alive after the call to this 194 | * constructor. 195 | * Example of usage: 196 | * \code 197 | * Json::Value aValue( StaticString("some text") ); 198 | * \endcode 199 | */ 200 | Value( const StaticString &value ); 201 | Value( const std::string &value ); 202 | # ifdef JSON_USE_CPPTL 203 | Value( const CppTL::ConstString &value ); 204 | # endif 205 | Value( bool value ); 206 | Value( const Value &other ); 207 | ~Value(); 208 | 209 | Value &operator=( const Value &other ); 210 | /// Swap values. 211 | /// \note Currently, comments are intentionally not swapped, for 212 | /// both logic and efficiency. 213 | void swap( Value &other ); 214 | 215 | ValueType type() const; 216 | 217 | bool operator <( const Value &other ) const; 218 | bool operator <=( const Value &other ) const; 219 | bool operator >=( const Value &other ) const; 220 | bool operator >( const Value &other ) const; 221 | 222 | bool operator ==( const Value &other ) const; 223 | bool operator !=( const Value &other ) const; 224 | 225 | int compare( const Value &other ); 226 | 227 | const char *asCString() const; 228 | std::string asString() const; 229 | # ifdef JSON_USE_CPPTL 230 | CppTL::ConstString asConstString() const; 231 | # endif 232 | Int asInt() const; 233 | UInt asUInt() const; 234 | double asDouble() const; 235 | bool asBool() const; 236 | 237 | bool isNull() const; 238 | bool isBool() const; 239 | bool isInt() const; 240 | bool isUInt() const; 241 | bool isIntegral() const; 242 | bool isDouble() const; 243 | bool isNumeric() const; 244 | bool isString() const; 245 | bool isArray() const; 246 | bool isObject() const; 247 | 248 | bool isConvertibleTo( ValueType other ) const; 249 | 250 | /// Number of values in array or object 251 | UInt size() const; 252 | 253 | /// \brief Return true if empty array, empty object, or null; 254 | /// otherwise, false. 255 | bool empty() const; 256 | 257 | /// Return isNull() 258 | bool operator!() const; 259 | 260 | /// Remove all object members and array elements. 261 | /// \pre type() is arrayValue, objectValue, or nullValue 262 | /// \post type() is unchanged 263 | void clear(); 264 | 265 | /// Resize the array to size elements. 266 | /// New elements are initialized to null. 267 | /// May only be called on nullValue or arrayValue. 268 | /// \pre type() is arrayValue or nullValue 269 | /// \post type() is arrayValue 270 | void resize( UInt size ); 271 | 272 | /// Access an array element (zero based index ). 273 | /// If the array contains less than index element, then null value are inserted 274 | /// in the array so that its size is index+1. 275 | /// (You may need to say 'value[0u]' to get your compiler to distinguish 276 | /// this from the operator[] which takes a string.) 277 | Value &operator[]( UInt index ); 278 | /// Access an array element (zero based index ) 279 | /// (You may need to say 'value[0u]' to get your compiler to distinguish 280 | /// this from the operator[] which takes a string.) 281 | const Value &operator[]( UInt index ) const; 282 | /// If the array contains at least index+1 elements, returns the element value, 283 | /// otherwise returns defaultValue. 284 | Value get( UInt index, 285 | const Value &defaultValue ) const; 286 | /// Return true if index < size(). 287 | bool isValidIndex( UInt index ) const; 288 | /// \brief Append value to array at the end. 289 | /// 290 | /// Equivalent to jsonvalue[jsonvalue.size()] = value; 291 | Value &append( const Value &value ); 292 | 293 | /// Access an object value by name, create a null member if it does not exist. 294 | Value &operator[]( const char *key ); 295 | /// Access an object value by name, returns null if there is no member with that name. 296 | const Value &operator[]( const char *key ) const; 297 | /// Access an object value by name, create a null member if it does not exist. 298 | Value &operator[]( const std::string &key ); 299 | /// Access an object value by name, returns null if there is no member with that name. 300 | const Value &operator[]( const std::string &key ) const; 301 | /** \brief Access an object value by name, create a null member if it does not exist. 302 | 303 | * If the object as no entry for that name, then the member name used to store 304 | * the new entry is not duplicated. 305 | * Example of use: 306 | * \code 307 | * Json::Value object; 308 | * static const StaticString code("code"); 309 | * object[code] = 1234; 310 | * \endcode 311 | */ 312 | Value &operator[]( const StaticString &key ); 313 | # ifdef JSON_USE_CPPTL 314 | /// Access an object value by name, create a null member if it does not exist. 315 | Value &operator[]( const CppTL::ConstString &key ); 316 | /// Access an object value by name, returns null if there is no member with that name. 317 | const Value &operator[]( const CppTL::ConstString &key ) const; 318 | # endif 319 | /// Return the member named key if it exist, defaultValue otherwise. 320 | Value get( const char *key, 321 | const Value &defaultValue ) const; 322 | /// Return the member named key if it exist, defaultValue otherwise. 323 | Value get( const std::string &key, 324 | const Value &defaultValue ) const; 325 | # ifdef JSON_USE_CPPTL 326 | /// Return the member named key if it exist, defaultValue otherwise. 327 | Value get( const CppTL::ConstString &key, 328 | const Value &defaultValue ) const; 329 | # endif 330 | /// \brief Remove and return the named member. 331 | /// 332 | /// Do nothing if it did not exist. 333 | /// \return the removed Value, or null. 334 | /// \pre type() is objectValue or nullValue 335 | /// \post type() is unchanged 336 | Value removeMember( const char* key ); 337 | /// Same as removeMember(const char*) 338 | Value removeMember( const std::string &key ); 339 | 340 | /// Return true if the object has a member named key. 341 | bool isMember( const char *key ) const; 342 | /// Return true if the object has a member named key. 343 | bool isMember( const std::string &key ) const; 344 | # ifdef JSON_USE_CPPTL 345 | /// Return true if the object has a member named key. 346 | bool isMember( const CppTL::ConstString &key ) const; 347 | # endif 348 | 349 | /// \brief Return a list of the member names. 350 | /// 351 | /// If null, return an empty list. 352 | /// \pre type() is objectValue or nullValue 353 | /// \post if type() was nullValue, it remains nullValue 354 | Members getMemberNames() const; 355 | 356 | //# ifdef JSON_USE_CPPTL 357 | // EnumMemberNames enumMemberNames() const; 358 | // EnumValues enumValues() const; 359 | //# endif 360 | 361 | /// Comments must be //... or /* ... */ 362 | void setComment( const char *comment, 363 | CommentPlacement placement ); 364 | /// Comments must be //... or /* ... */ 365 | void setComment( const std::string &comment, 366 | CommentPlacement placement ); 367 | bool hasComment( CommentPlacement placement ) const; 368 | /// Include delimiters and embedded newlines. 369 | std::string getComment( CommentPlacement placement ) const; 370 | 371 | std::string toStyledString() const; 372 | 373 | const_iterator begin() const; 374 | const_iterator end() const; 375 | 376 | iterator begin(); 377 | iterator end(); 378 | 379 | private: 380 | Value &resolveReference( const char *key, 381 | bool isStatic ); 382 | 383 | # ifdef JSON_VALUE_USE_INTERNAL_MAP 384 | inline bool isItemAvailable() const 385 | { 386 | return itemIsUsed_ == 0; 387 | } 388 | 389 | inline void setItemUsed( bool isUsed = true ) 390 | { 391 | itemIsUsed_ = isUsed ? 1 : 0; 392 | } 393 | 394 | inline bool isMemberNameStatic() const 395 | { 396 | return memberNameIsStatic_ == 0; 397 | } 398 | 399 | inline void setMemberNameIsStatic( bool isStatic ) 400 | { 401 | memberNameIsStatic_ = isStatic ? 1 : 0; 402 | } 403 | # endif // # ifdef JSON_VALUE_USE_INTERNAL_MAP 404 | 405 | private: 406 | struct CommentInfo 407 | { 408 | CommentInfo(); 409 | ~CommentInfo(); 410 | 411 | void setComment( const char *text ); 412 | 413 | char *comment_; 414 | }; 415 | 416 | //struct MemberNamesTransform 417 | //{ 418 | // typedef const char *result_type; 419 | // const char *operator()( const CZString &name ) const 420 | // { 421 | // return name.c_str(); 422 | // } 423 | //}; 424 | 425 | union ValueHolder 426 | { 427 | Int int_; 428 | UInt uint_; 429 | double real_; 430 | bool bool_; 431 | char *string_; 432 | # ifdef JSON_VALUE_USE_INTERNAL_MAP 433 | ValueInternalArray *array_; 434 | ValueInternalMap *map_; 435 | #else 436 | ObjectValues *map_; 437 | # endif 438 | } value_; 439 | ValueType type_ : 8; 440 | int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. 441 | # ifdef JSON_VALUE_USE_INTERNAL_MAP 442 | unsigned int itemIsUsed_ : 1; // used by the ValueInternalMap container. 443 | int memberNameIsStatic_ : 1; // used by the ValueInternalMap container. 444 | # endif 445 | CommentInfo *comments_; 446 | }; 447 | 448 | 449 | /** \brief Experimental and untested: represents an element of the "path" to access a node. 450 | */ 451 | class PathArgument 452 | { 453 | public: 454 | friend class Path; 455 | 456 | PathArgument(); 457 | PathArgument( UInt index ); 458 | PathArgument( const char *key ); 459 | PathArgument( const std::string &key ); 460 | 461 | private: 462 | enum Kind 463 | { 464 | kindNone = 0, 465 | kindIndex, 466 | kindKey 467 | }; 468 | std::string key_; 469 | UInt index_; 470 | Kind kind_; 471 | }; 472 | 473 | /** \brief Experimental and untested: represents a "path" to access a node. 474 | * 475 | * Syntax: 476 | * - "." => root node 477 | * - ".[n]" => elements at index 'n' of root node (an array value) 478 | * - ".name" => member named 'name' of root node (an object value) 479 | * - ".name1.name2.name3" 480 | * - ".[0][1][2].name1[3]" 481 | * - ".%" => member name is provided as parameter 482 | * - ".[%]" => index is provied as parameter 483 | */ 484 | class Path 485 | { 486 | public: 487 | Path( const std::string &path, 488 | const PathArgument &a1 = PathArgument(), 489 | const PathArgument &a2 = PathArgument(), 490 | const PathArgument &a3 = PathArgument(), 491 | const PathArgument &a4 = PathArgument(), 492 | const PathArgument &a5 = PathArgument() ); 493 | 494 | const Value &resolve( const Value &root ) const; 495 | Value resolve( const Value &root, 496 | const Value &defaultValue ) const; 497 | /// Creates the "path" to access the specified node and returns a reference on the node. 498 | Value &make( Value &root ) const; 499 | 500 | private: 501 | typedef std::vector InArgs; 502 | typedef std::vector Args; 503 | 504 | void makePath( const std::string &path, 505 | const InArgs &in ); 506 | void addPathInArg( const std::string &path, 507 | const InArgs &in, 508 | InArgs::const_iterator &itInArg, 509 | PathArgument::Kind kind ); 510 | void invalidPath( const std::string &path, 511 | int location ); 512 | 513 | Args args_; 514 | }; 515 | 516 | /** \brief Experimental do not use: Allocator to customize member name and string value memory management done by Value. 517 | * 518 | * - makeMemberName() and releaseMemberName() are called to respectively duplicate and 519 | * free an Json::objectValue member name. 520 | * - duplicateStringValue() and releaseStringValue() are called similarly to 521 | * duplicate and free a Json::stringValue value. 522 | */ 523 | class ValueAllocator 524 | { 525 | public: 526 | enum { unknown = (unsigned)-1 }; 527 | 528 | virtual ~ValueAllocator(); 529 | 530 | virtual char *makeMemberName( const char *memberName ) = 0; 531 | virtual void releaseMemberName( char *memberName ) = 0; 532 | virtual char *duplicateStringValue( const char *value, 533 | unsigned int length = unknown ) = 0; 534 | virtual void releaseStringValue( char *value ) = 0; 535 | }; 536 | 537 | #ifdef JSON_VALUE_USE_INTERNAL_MAP 538 | /** \brief Allocator to customize Value internal map. 539 | * Below is an example of a simple implementation (default implementation actually 540 | * use memory pool for speed). 541 | * \code 542 | class DefaultValueMapAllocator : public ValueMapAllocator 543 | { 544 | public: // overridden from ValueMapAllocator 545 | virtual ValueInternalMap *newMap() 546 | { 547 | return new ValueInternalMap(); 548 | } 549 | 550 | virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) 551 | { 552 | return new ValueInternalMap( other ); 553 | } 554 | 555 | virtual void destructMap( ValueInternalMap *map ) 556 | { 557 | delete map; 558 | } 559 | 560 | virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) 561 | { 562 | return new ValueInternalLink[size]; 563 | } 564 | 565 | virtual void releaseMapBuckets( ValueInternalLink *links ) 566 | { 567 | delete [] links; 568 | } 569 | 570 | virtual ValueInternalLink *allocateMapLink() 571 | { 572 | return new ValueInternalLink(); 573 | } 574 | 575 | virtual void releaseMapLink( ValueInternalLink *link ) 576 | { 577 | delete link; 578 | } 579 | }; 580 | * \endcode 581 | */ 582 | class JSON_API ValueMapAllocator 583 | { 584 | public: 585 | virtual ~ValueMapAllocator(); 586 | virtual ValueInternalMap *newMap() = 0; 587 | virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) = 0; 588 | virtual void destructMap( ValueInternalMap *map ) = 0; 589 | virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) = 0; 590 | virtual void releaseMapBuckets( ValueInternalLink *links ) = 0; 591 | virtual ValueInternalLink *allocateMapLink() = 0; 592 | virtual void releaseMapLink( ValueInternalLink *link ) = 0; 593 | }; 594 | 595 | /** \brief ValueInternalMap hash-map bucket chain link (for internal use only). 596 | * \internal previous_ & next_ allows for bidirectional traversal. 597 | */ 598 | class JSON_API ValueInternalLink 599 | { 600 | public: 601 | enum { itemPerLink = 6 }; // sizeof(ValueInternalLink) = 128 on 32 bits architecture. 602 | enum InternalFlags { 603 | flagAvailable = 0, 604 | flagUsed = 1 605 | }; 606 | 607 | ValueInternalLink(); 608 | 609 | ~ValueInternalLink(); 610 | 611 | Value items_[itemPerLink]; 612 | char *keys_[itemPerLink]; 613 | ValueInternalLink *previous_; 614 | ValueInternalLink *next_; 615 | }; 616 | 617 | 618 | /** \brief A linked page based hash-table implementation used internally by Value. 619 | * \internal ValueInternalMap is a tradional bucket based hash-table, with a linked 620 | * list in each bucket to handle collision. There is an addional twist in that 621 | * each node of the collision linked list is a page containing a fixed amount of 622 | * value. This provides a better compromise between memory usage and speed. 623 | * 624 | * Each bucket is made up of a chained list of ValueInternalLink. The last 625 | * link of a given bucket can be found in the 'previous_' field of the following bucket. 626 | * The last link of the last bucket is stored in tailLink_ as it has no following bucket. 627 | * Only the last link of a bucket may contains 'available' item. The last link always 628 | * contains at least one element unless is it the bucket one very first link. 629 | */ 630 | class JSON_API ValueInternalMap 631 | { 632 | friend class ValueIteratorBase; 633 | friend class Value; 634 | public: 635 | typedef unsigned int HashKey; 636 | typedef unsigned int BucketIndex; 637 | 638 | # ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION 639 | struct IteratorState 640 | { 641 | IteratorState() 642 | : map_(0) 643 | , link_(0) 644 | , itemIndex_(0) 645 | , bucketIndex_(0) 646 | { 647 | } 648 | ValueInternalMap *map_; 649 | ValueInternalLink *link_; 650 | BucketIndex itemIndex_; 651 | BucketIndex bucketIndex_; 652 | }; 653 | # endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION 654 | 655 | ValueInternalMap(); 656 | ValueInternalMap( const ValueInternalMap &other ); 657 | ValueInternalMap &operator =( const ValueInternalMap &other ); 658 | ~ValueInternalMap(); 659 | 660 | void swap( ValueInternalMap &other ); 661 | 662 | BucketIndex size() const; 663 | 664 | void clear(); 665 | 666 | bool reserveDelta( BucketIndex growth ); 667 | 668 | bool reserve( BucketIndex newItemCount ); 669 | 670 | const Value *find( const char *key ) const; 671 | 672 | Value *find( const char *key ); 673 | 674 | Value &resolveReference( const char *key, 675 | bool isStatic ); 676 | 677 | void remove( const char *key ); 678 | 679 | void doActualRemove( ValueInternalLink *link, 680 | BucketIndex index, 681 | BucketIndex bucketIndex ); 682 | 683 | ValueInternalLink *&getLastLinkInBucket( BucketIndex bucketIndex ); 684 | 685 | Value &setNewItem( const char *key, 686 | bool isStatic, 687 | ValueInternalLink *link, 688 | BucketIndex index ); 689 | 690 | Value &unsafeAdd( const char *key, 691 | bool isStatic, 692 | HashKey hashedKey ); 693 | 694 | HashKey hash( const char *key ) const; 695 | 696 | int compare( const ValueInternalMap &other ) const; 697 | 698 | private: 699 | void makeBeginIterator( IteratorState &it ) const; 700 | void makeEndIterator( IteratorState &it ) const; 701 | static bool equals( const IteratorState &x, const IteratorState &other ); 702 | static void increment( IteratorState &iterator ); 703 | static void incrementBucket( IteratorState &iterator ); 704 | static void decrement( IteratorState &iterator ); 705 | static const char *key( const IteratorState &iterator ); 706 | static const char *key( const IteratorState &iterator, bool &isStatic ); 707 | static Value &value( const IteratorState &iterator ); 708 | static int distance( const IteratorState &x, const IteratorState &y ); 709 | 710 | private: 711 | ValueInternalLink *buckets_; 712 | ValueInternalLink *tailLink_; 713 | BucketIndex bucketsSize_; 714 | BucketIndex itemCount_; 715 | }; 716 | 717 | /** \brief A simplified deque implementation used internally by Value. 718 | * \internal 719 | * It is based on a list of fixed "page", each page contains a fixed number of items. 720 | * Instead of using a linked-list, a array of pointer is used for fast item look-up. 721 | * Look-up for an element is as follow: 722 | * - compute page index: pageIndex = itemIndex / itemsPerPage 723 | * - look-up item in page: pages_[pageIndex][itemIndex % itemsPerPage] 724 | * 725 | * Insertion is amortized constant time (only the array containing the index of pointers 726 | * need to be reallocated when items are appended). 727 | */ 728 | class JSON_API ValueInternalArray 729 | { 730 | friend class Value; 731 | friend class ValueIteratorBase; 732 | public: 733 | enum { itemsPerPage = 8 }; // should be a power of 2 for fast divide and modulo. 734 | typedef Value::ArrayIndex ArrayIndex; 735 | typedef unsigned int PageIndex; 736 | 737 | # ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION 738 | struct IteratorState // Must be a POD 739 | { 740 | IteratorState() 741 | : array_(0) 742 | , currentPageIndex_(0) 743 | , currentItemIndex_(0) 744 | { 745 | } 746 | ValueInternalArray *array_; 747 | Value **currentPageIndex_; 748 | unsigned int currentItemIndex_; 749 | }; 750 | # endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION 751 | 752 | ValueInternalArray(); 753 | ValueInternalArray( const ValueInternalArray &other ); 754 | ValueInternalArray &operator =( const ValueInternalArray &other ); 755 | ~ValueInternalArray(); 756 | void swap( ValueInternalArray &other ); 757 | 758 | void clear(); 759 | void resize( ArrayIndex newSize ); 760 | 761 | Value &resolveReference( ArrayIndex index ); 762 | 763 | Value *find( ArrayIndex index ) const; 764 | 765 | ArrayIndex size() const; 766 | 767 | int compare( const ValueInternalArray &other ) const; 768 | 769 | private: 770 | static bool equals( const IteratorState &x, const IteratorState &other ); 771 | static void increment( IteratorState &iterator ); 772 | static void decrement( IteratorState &iterator ); 773 | static Value &dereference( const IteratorState &iterator ); 774 | static Value &unsafeDereference( const IteratorState &iterator ); 775 | static int distance( const IteratorState &x, const IteratorState &y ); 776 | static ArrayIndex indexOf( const IteratorState &iterator ); 777 | void makeBeginIterator( IteratorState &it ) const; 778 | void makeEndIterator( IteratorState &it ) const; 779 | void makeIterator( IteratorState &it, ArrayIndex index ) const; 780 | 781 | void makeIndexValid( ArrayIndex index ); 782 | 783 | Value **pages_; 784 | ArrayIndex size_; 785 | PageIndex pageCount_; 786 | }; 787 | 788 | /** \brief Experimental: do not use. Allocator to customize Value internal array. 789 | * Below is an example of a simple implementation (actual implementation use 790 | * memory pool). 791 | \code 792 | class DefaultValueArrayAllocator : public ValueArrayAllocator 793 | { 794 | public: // overridden from ValueArrayAllocator 795 | virtual ~DefaultValueArrayAllocator() 796 | { 797 | } 798 | 799 | virtual ValueInternalArray *newArray() 800 | { 801 | return new ValueInternalArray(); 802 | } 803 | 804 | virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) 805 | { 806 | return new ValueInternalArray( other ); 807 | } 808 | 809 | virtual void destruct( ValueInternalArray *array ) 810 | { 811 | delete array; 812 | } 813 | 814 | virtual void reallocateArrayPageIndex( Value **&indexes, 815 | ValueInternalArray::PageIndex &indexCount, 816 | ValueInternalArray::PageIndex minNewIndexCount ) 817 | { 818 | ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; 819 | if ( minNewIndexCount > newIndexCount ) 820 | newIndexCount = minNewIndexCount; 821 | void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); 822 | if ( !newIndexes ) 823 | throw std::bad_alloc(); 824 | indexCount = newIndexCount; 825 | indexes = static_cast( newIndexes ); 826 | } 827 | virtual void releaseArrayPageIndex( Value **indexes, 828 | ValueInternalArray::PageIndex indexCount ) 829 | { 830 | if ( indexes ) 831 | free( indexes ); 832 | } 833 | 834 | virtual Value *allocateArrayPage() 835 | { 836 | return static_cast( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) ); 837 | } 838 | 839 | virtual void releaseArrayPage( Value *value ) 840 | { 841 | if ( value ) 842 | free( value ); 843 | } 844 | }; 845 | \endcode 846 | */ 847 | class JSON_API ValueArrayAllocator 848 | { 849 | public: 850 | virtual ~ValueArrayAllocator(); 851 | virtual ValueInternalArray *newArray() = 0; 852 | virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) = 0; 853 | virtual void destructArray( ValueInternalArray *array ) = 0; 854 | /** \brief Reallocate array page index. 855 | * Reallocates an array of pointer on each page. 856 | * \param indexes [input] pointer on the current index. May be \c NULL. 857 | * [output] pointer on the new index of at least 858 | * \a minNewIndexCount pages. 859 | * \param indexCount [input] current number of pages in the index. 860 | * [output] number of page the reallocated index can handle. 861 | * \b MUST be >= \a minNewIndexCount. 862 | * \param minNewIndexCount Minimum number of page the new index must be able to 863 | * handle. 864 | */ 865 | virtual void reallocateArrayPageIndex( Value **&indexes, 866 | ValueInternalArray::PageIndex &indexCount, 867 | ValueInternalArray::PageIndex minNewIndexCount ) = 0; 868 | virtual void releaseArrayPageIndex( Value **indexes, 869 | ValueInternalArray::PageIndex indexCount ) = 0; 870 | virtual Value *allocateArrayPage() = 0; 871 | virtual void releaseArrayPage( Value *value ) = 0; 872 | }; 873 | #endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP 874 | 875 | 876 | /** \brief base class for Value iterators. 877 | * 878 | */ 879 | class ValueIteratorBase 880 | { 881 | public: 882 | typedef unsigned int size_t; 883 | typedef int difference_type; 884 | typedef ValueIteratorBase SelfType; 885 | 886 | ValueIteratorBase(); 887 | #ifndef JSON_VALUE_USE_INTERNAL_MAP 888 | explicit ValueIteratorBase( const Value::ObjectValues::iterator ¤t ); 889 | #else 890 | ValueIteratorBase( const ValueInternalArray::IteratorState &state ); 891 | ValueIteratorBase( const ValueInternalMap::IteratorState &state ); 892 | #endif 893 | 894 | bool operator ==( const SelfType &other ) const 895 | { 896 | return isEqual( other ); 897 | } 898 | 899 | bool operator !=( const SelfType &other ) const 900 | { 901 | return !isEqual( other ); 902 | } 903 | 904 | difference_type operator -( const SelfType &other ) const 905 | { 906 | return computeDistance( other ); 907 | } 908 | 909 | /// Return either the index or the member name of the referenced value as a Value. 910 | Value key() const; 911 | 912 | /// Return the index of the referenced Value. -1 if it is not an arrayValue. 913 | UInt index() const; 914 | 915 | /// Return the member name of the referenced Value. "" if it is not an objectValue. 916 | const char *memberName() const; 917 | 918 | protected: 919 | Value &deref() const; 920 | 921 | void increment(); 922 | 923 | void decrement(); 924 | 925 | difference_type computeDistance( const SelfType &other ) const; 926 | 927 | bool isEqual( const SelfType &other ) const; 928 | 929 | void copy( const SelfType &other ); 930 | 931 | private: 932 | #ifndef JSON_VALUE_USE_INTERNAL_MAP 933 | Value::ObjectValues::iterator current_; 934 | // Indicates that iterator is for a null value. 935 | bool isNull_; 936 | #else 937 | union 938 | { 939 | ValueInternalArray::IteratorState array_; 940 | ValueInternalMap::IteratorState map_; 941 | } iterator_; 942 | bool isArray_; 943 | #endif 944 | }; 945 | 946 | /** \brief const iterator for object and array value. 947 | * 948 | */ 949 | class ValueConstIterator : public ValueIteratorBase 950 | { 951 | friend class Value; 952 | public: 953 | typedef unsigned int size_t; 954 | typedef int difference_type; 955 | typedef const Value &reference; 956 | typedef const Value *pointer; 957 | typedef ValueConstIterator SelfType; 958 | 959 | ValueConstIterator(); 960 | private: 961 | /*! \internal Use by Value to create an iterator. 962 | */ 963 | #ifndef JSON_VALUE_USE_INTERNAL_MAP 964 | explicit ValueConstIterator( const Value::ObjectValues::iterator ¤t ); 965 | #else 966 | ValueConstIterator( const ValueInternalArray::IteratorState &state ); 967 | ValueConstIterator( const ValueInternalMap::IteratorState &state ); 968 | #endif 969 | public: 970 | SelfType &operator =( const ValueIteratorBase &other ); 971 | 972 | SelfType operator++( int ) 973 | { 974 | SelfType temp( *this ); 975 | ++*this; 976 | return temp; 977 | } 978 | 979 | SelfType operator--( int ) 980 | { 981 | SelfType temp( *this ); 982 | --*this; 983 | return temp; 984 | } 985 | 986 | SelfType &operator--() 987 | { 988 | decrement(); 989 | return *this; 990 | } 991 | 992 | SelfType &operator++() 993 | { 994 | increment(); 995 | return *this; 996 | } 997 | 998 | reference operator *() const 999 | { 1000 | return deref(); 1001 | } 1002 | }; 1003 | 1004 | 1005 | /** \brief Iterator for object and array value. 1006 | */ 1007 | class ValueIterator : public ValueIteratorBase 1008 | { 1009 | friend class Value; 1010 | public: 1011 | typedef unsigned int size_t; 1012 | typedef int difference_type; 1013 | typedef Value &reference; 1014 | typedef Value *pointer; 1015 | typedef ValueIterator SelfType; 1016 | 1017 | ValueIterator(); 1018 | ValueIterator( const ValueConstIterator &other ); 1019 | ValueIterator( const ValueIterator &other ); 1020 | private: 1021 | /*! \internal Use by Value to create an iterator. 1022 | */ 1023 | #ifndef JSON_VALUE_USE_INTERNAL_MAP 1024 | explicit ValueIterator( const Value::ObjectValues::iterator ¤t ); 1025 | #else 1026 | ValueIterator( const ValueInternalArray::IteratorState &state ); 1027 | ValueIterator( const ValueInternalMap::IteratorState &state ); 1028 | #endif 1029 | public: 1030 | 1031 | SelfType &operator =( const SelfType &other ); 1032 | 1033 | SelfType operator++( int ) 1034 | { 1035 | SelfType temp( *this ); 1036 | ++*this; 1037 | return temp; 1038 | } 1039 | 1040 | SelfType operator--( int ) 1041 | { 1042 | SelfType temp( *this ); 1043 | --*this; 1044 | return temp; 1045 | } 1046 | 1047 | SelfType &operator--() 1048 | { 1049 | decrement(); 1050 | return *this; 1051 | } 1052 | 1053 | SelfType &operator++() 1054 | { 1055 | increment(); 1056 | return *this; 1057 | } 1058 | 1059 | reference operator *() const 1060 | { 1061 | return deref(); 1062 | } 1063 | }; 1064 | 1065 | 1066 | } // namespace Json 1067 | 1068 | 1069 | #endif // CPPTL_JSON_H_INCLUDED 1070 | -------------------------------------------------------------------------------- /src/ext/json/writer.h: -------------------------------------------------------------------------------- 1 | #ifndef JSON_WRITER_H_INCLUDED 2 | # define JSON_WRITER_H_INCLUDED 3 | 4 | # include "value.h" 5 | # include 6 | # include 7 | # include 8 | 9 | namespace Json { 10 | 11 | class Value; 12 | 13 | /** \brief Abstract class for writers. 14 | */ 15 | class JSON_API Writer 16 | { 17 | public: 18 | virtual ~Writer(); 19 | 20 | virtual std::string write( const Value &root ) = 0; 21 | }; 22 | 23 | /** \brief Outputs a Value in JSON format without formatting (not human friendly). 24 | * 25 | * The JSON document is written in a single line. It is not intended for 'human' consumption, 26 | * but may be usefull to support feature such as RPC where bandwith is limited. 27 | * \sa Reader, Value 28 | */ 29 | class JSON_API FastWriter : public Writer 30 | { 31 | public: 32 | FastWriter(); 33 | virtual ~FastWriter(){} 34 | 35 | void enableYAMLCompatibility(); 36 | 37 | public: // overridden from Writer 38 | virtual std::string write( const Value &root ); 39 | 40 | private: 41 | void writeValue( const Value &value ); 42 | 43 | std::string document_; 44 | bool yamlCompatiblityEnabled_; 45 | }; 46 | 47 | /** \brief Writes a Value in JSON format in a human friendly way. 48 | * 49 | * The rules for line break and indent are as follow: 50 | * - Object value: 51 | * - if empty then print {} without indent and line break 52 | * - if not empty the print '{', line break & indent, print one value per line 53 | * and then unindent and line break and print '}'. 54 | * - Array value: 55 | * - if empty then print [] without indent and line break 56 | * - if the array contains no object value, empty array or some other value types, 57 | * and all the values fit on one lines, then print the array on a single line. 58 | * - otherwise, it the values do not fit on one line, or the array contains 59 | * object or non empty array, then print one value per line. 60 | * 61 | * If the Value have comments then they are outputed according to their #CommentPlacement. 62 | * 63 | * \sa Reader, Value, Value::setComment() 64 | */ 65 | class JSON_API StyledWriter: public Writer 66 | { 67 | public: 68 | StyledWriter(); 69 | virtual ~StyledWriter(){} 70 | 71 | public: // overridden from Writer 72 | /** \brief Serialize a Value in JSON format. 73 | * \param root Value to serialize. 74 | * \return String containing the JSON document that represents the root value. 75 | */ 76 | virtual std::string write( const Value &root ); 77 | 78 | private: 79 | void writeValue( const Value &value ); 80 | void writeArrayValue( const Value &value ); 81 | bool isMultineArray( const Value &value ); 82 | void pushValue( const std::string &value ); 83 | void writeIndent(); 84 | void writeWithIndent( const std::string &value ); 85 | void indent(); 86 | void unindent(); 87 | void writeCommentBeforeValue( const Value &root ); 88 | void writeCommentAfterValueOnSameLine( const Value &root ); 89 | bool hasCommentForValue( const Value &value ); 90 | static std::string normalizeEOL( const std::string &text ); 91 | 92 | typedef std::vector ChildValues; 93 | 94 | ChildValues childValues_; 95 | std::string document_; 96 | std::string indentString_; 97 | int rightMargin_; 98 | int indentSize_; 99 | bool addChildValues_; 100 | }; 101 | 102 | /** \brief Writes a Value in JSON format in a human friendly way, 103 | to a stream rather than to a string. 104 | * 105 | * The rules for line break and indent are as follow: 106 | * - Object value: 107 | * - if empty then print {} without indent and line break 108 | * - if not empty the print '{', line break & indent, print one value per line 109 | * and then unindent and line break and print '}'. 110 | * - Array value: 111 | * - if empty then print [] without indent and line break 112 | * - if the array contains no object value, empty array or some other value types, 113 | * and all the values fit on one lines, then print the array on a single line. 114 | * - otherwise, it the values do not fit on one line, or the array contains 115 | * object or non empty array, then print one value per line. 116 | * 117 | * If the Value have comments then they are outputed according to their #CommentPlacement. 118 | * 119 | * \param indentation Each level will be indented by this amount extra. 120 | * \sa Reader, Value, Value::setComment() 121 | */ 122 | class JSON_API StyledStreamWriter 123 | { 124 | public: 125 | StyledStreamWriter( std::string indentation="\t" ); 126 | ~StyledStreamWriter(){} 127 | 128 | public: 129 | /** \brief Serialize a Value in JSON format. 130 | * \param out Stream to write to. (Can be ostringstream, e.g.) 131 | * \param root Value to serialize. 132 | * \note There is no point in deriving from Writer, since write() should not return a value. 133 | */ 134 | void write( std::ostream &out, const Value &root ); 135 | 136 | private: 137 | void writeValue( const Value &value ); 138 | void writeArrayValue( const Value &value ); 139 | bool isMultineArray( const Value &value ); 140 | void pushValue( const std::string &value ); 141 | void writeIndent(); 142 | void writeWithIndent( const std::string &value ); 143 | void indent(); 144 | void unindent(); 145 | void writeCommentBeforeValue( const Value &root ); 146 | void writeCommentAfterValueOnSameLine( const Value &root ); 147 | bool hasCommentForValue( const Value &value ); 148 | static std::string normalizeEOL( const std::string &text ); 149 | 150 | typedef std::vector ChildValues; 151 | 152 | ChildValues childValues_; 153 | std::ostream* document_; 154 | std::string indentString_; 155 | int rightMargin_; 156 | std::string indentation_; 157 | bool addChildValues_; 158 | }; 159 | 160 | std::string JSON_API valueToString( Int value ); 161 | std::string JSON_API valueToString( UInt value ); 162 | std::string JSON_API valueToString( double value ); 163 | std::string JSON_API valueToString( bool value ); 164 | std::string JSON_API valueToQuotedString( const char *value ); 165 | 166 | /// \brief Output using the StyledStreamWriter. 167 | /// \see Json::operator>>() 168 | std::ostream& operator<<( std::ostream&, const Value &root ); 169 | 170 | } // namespace Json 171 | 172 | 173 | 174 | #endif // JSON_WRITER_H_INCLUDED 175 | -------------------------------------------------------------------------------- /src/fdl.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Jason Graves (GodLikeMouse/Collaboradev) 3 | * http://www.collaboradev.com 4 | * 5 | * This file is part of ForceDirectedLayout. 6 | * 7 | * ForceDirectedLayout is free software: you can redistribute it 8 | * and/or modify it under the terms of the GNU General Public 9 | * License as published by the Free Software Foundation, either 10 | * version 3 of the License, or (at your option) any later version. 11 | * 12 | * ForceDirectedLayout is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with ForceDirectedLayout. If not, see http://www.gnu.org/licenses/. 19 | */ 20 | 21 | #include "fdl.h" 22 | 23 | int main(int argc, char ** argv){ 24 | 25 | srand(time(NULL)); 26 | 27 | CommandSwitches switches; 28 | std::vector nodes; 29 | std::vector edges; 30 | 31 | //parse command line arguments 32 | if(!switches.parse(argc, argv)) 33 | return 0; 34 | 35 | if(switches.show_help){ 36 | show_usage(); 37 | return 0; 38 | } 39 | 40 | //sanity check 41 | if(!switches.sane()){ 42 | cerr << switches.error() << endl; 43 | return 1; 44 | } 45 | 46 | //read input file 47 | FileHandler read_handler(switches.in_file, switches.in_type, switches); 48 | if(!read_handler.read(nodes, edges)){ 49 | cerr << read_handler.error() << endl; 50 | return 1; 51 | } 52 | 53 | Layout layout(&nodes, &edges, switches); 54 | 55 | //verify node to link mapping 56 | if(!layout.error().empty()){ 57 | cerr << layout.error() << endl; 58 | return 1; 59 | } 60 | 61 | layout.layout(); 62 | 63 | //write output file 64 | FileHandler write_handler(switches.out_file, switches.out_type, switches); 65 | if(!write_handler.write(nodes, edges)){ 66 | cerr << write_handler.error() << endl; 67 | return 1; 68 | } 69 | 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /src/fdl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Jason Graves (GodLikeMouse/Collaboradev) 3 | * http://www.collaboradev.com 4 | * 5 | * This file is part of ForceDirectedLayout. 6 | * 7 | * ForceDirectedLayout is free software: you can redistribute it 8 | * and/or modify it under the terms of the GNU General Public 9 | * License as published by the Free Software Foundation, either 10 | * version 3 of the License, or (at your option) any later version. 11 | * 12 | * ForceDirectedLayout is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with ForceDirectedLayout. If not, see http://www.gnu.org/licenses/. 19 | */ 20 | 21 | #ifndef __FDL_H 22 | #define __FDL_H 23 | 24 | #include 25 | #include 26 | 27 | #include "command-switches.h" 28 | #include "file-handler.h" 29 | #include "config.h" 30 | #include "layout.h" 31 | 32 | using namespace std; 33 | 34 | // Method for showing usage 35 | void show_usage(){ 36 | cout << "fdl - " << PACKAGE_STRING << " " << PACKAGE_BUGREPORT << " " << PACKAGE_URL << endl; 37 | cout << "\t Performs a force directed layout algorithm against the specified file." << endl; 38 | cout << endl; 39 | cout << "Usage: fdl [OPTION]... --in=[INPUT-FILE] --out=[OUTPUT-FILE]" << endl; 40 | cout << endl; 41 | cout << "Options:" << endl; 42 | cout << "\t --in-type=TYPE\t\tSpecifies the input file type, default JSON (JSON, GraphML)." << endl; 43 | cout << "\t --out-type=TYPE\tSpecifies the output file type, default JSON (JSON, GraphML)." << endl; 44 | cout << "\t --in=FILE\t\tSpecifies the input file." << endl; 45 | cout << "\t --out=FILE\t\tSpecifies the output file." << endl; 46 | cout << "\t --mode=MODE\t\tSpecifies the detangle mode, default 2D (2D, 3D)." << endl; 47 | cout << "\t --help\t\t\tDisplays this help message." << endl; 48 | cout << endl; 49 | } 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/file-handler.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Jason Graves (GodLikeMouse/Collaboradev) 3 | * http://www.collaboradev.com 4 | * 5 | * This file is part of ForceDirectedLayout. 6 | * 7 | * ForceDirectedLayout is free software: you can redistribute it 8 | * and/or modify it under the terms of the GNU General Public 9 | * License as published by the Free Software Foundation, either 10 | * version 3 of the License, or (at your option) any later version. 11 | * 12 | * ForceDirectedLayout is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with ForceDirectedLayout. If not, see http://www.gnu.org/licenses/. 19 | */ 20 | 21 | #include "file-handler.h" 22 | 23 | // Constructor 24 | // @param string filename 25 | // @param string type 26 | // @param CommandSwitches switches 27 | FileHandler::FileHandler(string filename, string type, CommandSwitches& switches){ 28 | filename_ = filename; 29 | type_ = type; 30 | switches_ = switches; 31 | } 32 | 33 | // Method for reading nodes and edges from a file 34 | // @param vector nodes 35 | // @param vector edges 36 | // @return bool 37 | bool FileHandler::read(vector& nodes, vector& edges){ 38 | 39 | if(type_ == "JSON"){ 40 | parse_json(nodes, edges); 41 | } 42 | 43 | if(type_ == "GraphML"){ 44 | parse_graphml(nodes, edges); 45 | } 46 | 47 | return true; 48 | } 49 | 50 | // Method for writing nodes and edges out to a file 51 | // @param vector nodes 52 | // @param vector edges 53 | // @return bool 54 | bool FileHandler::write(vector& nodes, vector& edges){ 55 | 56 | if(type_ == "JSON"){ 57 | write_json(nodes, edges); 58 | } 59 | 60 | if(type_ == "GraphML"){ 61 | write_graphml(nodes, edges); 62 | } 63 | 64 | return true; 65 | } 66 | 67 | // Method for retrieving the error string. 68 | // @return string 69 | string& FileHandler::error(){ 70 | return error_; 71 | } 72 | 73 | // Method for parsing JSON into nodes and edges 74 | // @param vector nodes 75 | // @param vector edges 76 | // @return bool 77 | bool FileHandler::parse_json(vector& nodes, vector& edges){ 78 | 79 | ifstream file(filename_.data()); 80 | string json_text; 81 | string line; 82 | 83 | //read json file 84 | if(file.is_open()){ 85 | while(getline(file, line)){ 86 | json_text += line; 87 | } 88 | file.close(); 89 | } 90 | else{ 91 | error_ = "Could not open file: " + filename_ + "."; 92 | return false; 93 | } 94 | 95 | //parse json 96 | Json::Value root; 97 | Json::Reader reader; 98 | 99 | if(!reader.parse(json_text.c_str(), root)){ 100 | error_ = "Failed to parse JSON file."; 101 | return false; 102 | } 103 | 104 | Json::Value nodes_array = root["nodes"]; 105 | Json::Value edges_array = root["edges"]; 106 | 107 | //load nodes 108 | for(int i=0; i nodes 187 | // @param vector edges 188 | // @return bool 189 | bool FileHandler::parse_graphml(vector& nodes, vector& edges){ 190 | 191 | ifstream file(filename_.data()); 192 | string xml_text; 193 | string line; 194 | 195 | //read xml file 196 | if(file.is_open()){ 197 | while(getline(file, line)){ 198 | xml_text += line; 199 | } 200 | file.close(); 201 | } 202 | else{ 203 | error_ = "Could not open file: " + filename_ + "."; 204 | return false; 205 | } 206 | 207 | xml_document<> doc; 208 | doc.parse<0>( const_cast(xml_text.c_str()) ); 209 | 210 | //get graphml node 211 | xml_node<> *root_node = doc.first_node("graphml"); 212 | 213 | if(!root_node){ 214 | error_ = "GraphML \"graphml\" root node missing."; 215 | return false; 216 | } 217 | 218 | //get graph node 219 | xml_node<> *graph_node = root_node->first_node("graph"); 220 | 221 | if(!graph_node){ 222 | error_ = "GraphML \"graph\" node missing."; 223 | return false; 224 | } 225 | 226 | //iterate nodes and edges 227 | for(xml_node<> * xml_node = graph_node->first_node(); 228 | xml_node; xml_node = xml_node->next_sibling()){ 229 | 230 | if( strcmp(xml_node->name(), "node") == 0 ){ 231 | //node 232 | 233 | Node node; 234 | 235 | //iterate attributes 236 | for(xml_attribute<> *xml_attr = xml_node->first_attribute(); 237 | xml_attr; xml_attr = xml_attr->next_attribute()){ 238 | 239 | if( strcmp(xml_attr->name(), "id") == 0 ){ 240 | //id 241 | node.id = xml_attr->value(); 242 | } 243 | else{ 244 | //additional attributes 245 | node.attributes[ xml_attr->name() ] = xml_attr->value(); 246 | } 247 | } 248 | 249 | nodes.push_back(node); 250 | } 251 | else if( strcmp(xml_node->name(), "edge") == 0 ){ 252 | //edge 253 | 254 | Edge edge; 255 | 256 | //iterate attributes 257 | for(xml_attribute<> *xml_attr = xml_node->first_attribute(); 258 | xml_attr; xml_attr = xml_attr->next_attribute()){ 259 | 260 | if( strcmp(xml_attr->name(), "id") == 0 ){ 261 | //id 262 | edge.id = xml_attr->value(); 263 | } 264 | else if( strcmp(xml_attr->name(), "source") == 0 ){ 265 | //source 266 | edge.source_id = xml_attr->value(); 267 | } 268 | else if( strcmp(xml_attr->name(), "target") == 0 ){ 269 | //target 270 | edge.target_id = xml_attr->value(); 271 | } 272 | else{ 273 | //additional attributes 274 | edge.attributes[ xml_attr->name() ] = xml_attr->value(); 275 | } 276 | } 277 | 278 | edges.push_back(edge); 279 | } 280 | 281 | } 282 | 283 | return true; 284 | } 285 | 286 | // Method for writing nodes and edges out to a JSON file. 287 | // @param vector nodes 288 | // @param vector edges 289 | // @return bool 290 | bool FileHandler::write_json(vector& nodes, vector& edges){ 291 | 292 | Json::Value root; 293 | 294 | //nodes 295 | Json::Value jsonNodes(Json::arrayValue); 296 | 297 | for(int i=0; i::iterator itr = n.attributes.begin(); 308 | itr != n.attributes.end(); 309 | itr++){ 310 | 311 | jsonNode[ itr->first ] = itr->second; 312 | } 313 | 314 | jsonNodes.append(jsonNode); 315 | } 316 | 317 | root["nodes"] = jsonNodes; 318 | 319 | //edges 320 | Json::Value jsonEdges(Json::arrayValue); 321 | 322 | for(int i=0; i::iterator itr = e.attributes.begin(); 332 | itr != e.attributes.end(); 333 | itr++){ 334 | 335 | jsonEdge[ itr->first ] = itr->second; 336 | } 337 | 338 | jsonEdges.append(jsonEdge); 339 | } 340 | 341 | root["edges"] = jsonEdges; 342 | 343 | Json::StyledWriter writer; 344 | string json_text = writer.write(root); 345 | 346 | if(filename_.empty()){ 347 | //write to std out 348 | cout << root << endl; 349 | } 350 | else{ 351 | //write out to file 352 | ofstream file(filename_.data()); 353 | file.write(json_text.c_str(), json_text.size()); 354 | file.close(); 355 | } 356 | 357 | return true; 358 | } 359 | 360 | // Method for writing nodes and edges out to a GraphML file. 361 | // @param vector nodes 362 | // @param vector edges 363 | // @return bool 364 | bool FileHandler::write_graphml(vector& nodes, vector& edges){ 365 | 366 | std::ostringstream stream; 367 | 368 | //graphml header 369 | stream << ""; 373 | stream << endl; 374 | 375 | //write graph 376 | stream << "" << endl; 377 | 378 | //write nodes 379 | for(int i=0; i::iterator itr = n.attributes.begin(); 390 | itr != n.attributes.end(); 391 | itr++){ 392 | 393 | stream << itr->first << "=\"" << itr->second << "\" "; 394 | } 395 | 396 | stream << "/>" << endl; 397 | } 398 | 399 | //write edges 400 | for(int i=0; i::iterator itr = e.attributes.begin(); 410 | itr != e.attributes.end(); 411 | itr++){ 412 | 413 | stream << itr->first << "=\"" << itr->second << "\" "; 414 | } 415 | 416 | stream << "/>" << endl; 417 | } 418 | 419 | stream << "" << endl; 420 | 421 | stream << "" << endl; 422 | 423 | if(filename_.empty()){ 424 | //write to std out 425 | cout << stream.str() << endl; 426 | } 427 | else{ 428 | //write out to file 429 | ofstream file(filename_.data()); 430 | std::string xml_text = stream.str(); 431 | 432 | file.write(xml_text.c_str(), xml_text.size()); 433 | file.close(); 434 | } 435 | 436 | return true; 437 | } 438 | -------------------------------------------------------------------------------- /src/file-handler.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Jason Graves (GodLikeMouse/Collaboradev) 3 | * http://www.collaboradev.com 4 | * 5 | * This file is part of ForceDirectedLayout. 6 | * 7 | * ForceDirectedLayout is free software: you can redistribute it 8 | * and/or modify it under the terms of the GNU General Public 9 | * License as published by the Free Software Foundation, either 10 | * version 3 of the License, or (at your option) any later version. 11 | * 12 | * ForceDirectedLayout is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with ForceDirectedLayout. If not, see http://www.gnu.org/licenses/. 19 | */ 20 | 21 | #ifndef __FILE_HANDLER 22 | #define __FILE_HANDLER 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "ext/json/json.h" 31 | #include "ext/xml/rapidxml.h" 32 | #include "command-switches.h" 33 | #include "node.h" 34 | #include "edge.h" 35 | 36 | 37 | using namespace std; 38 | using namespace rapidxml; 39 | 40 | class FileHandler { 41 | 42 | public: 43 | FileHandler(string, string, CommandSwitches&); 44 | 45 | bool read(vector&, vector&); 46 | bool write(vector&, vector&); 47 | string& error(); 48 | 49 | private: 50 | string type_; 51 | string filename_; 52 | string error_; 53 | CommandSwitches switches_; 54 | 55 | bool parse_json(vector&, vector&); 56 | bool write_json(vector&, vector&); 57 | bool parse_graphml(vector&, vector&); 58 | bool write_graphml(vector&, vector&); 59 | 60 | }; 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /src/layout.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Jason Graves (GodLikeMouse/Collaboradev) 3 | * http://www.collaboradev.com 4 | * 5 | * This file is part of ForceDirectedLayout. 6 | * 7 | * ForceDirectedLayout is free software: you can redistribute it 8 | * and/or modify it under the terms of the GNU General Public 9 | * License as published by the Free Software Foundation, either 10 | * version 3 of the License, or (at your option) any later version. 11 | * 12 | * ForceDirectedLayout is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with ForceDirectedLayout. If not, see http://www.gnu.org/licenses/. 19 | */ 20 | 21 | #include "layout.h" 22 | 23 | // Constructor 24 | // @param vector nodes 25 | // @param vector edges 26 | // @param CommandSwitches switches 27 | Layout::Layout(vector* nodes, vector* edges, CommandSwitches& switches){ 28 | 29 | switches_ = switches; 30 | nodes_ = nodes; 31 | edges_ = edges; 32 | 33 | width_ = 1024; 34 | height_ = 768; 35 | layout_iterations_ = 0; 36 | attraction_multiplier_ = 5.0f; 37 | repulsion_multiplier_ = 0.75f; 38 | max_iterations_ = 1000; 39 | EPSILON_ = 0.000001f; 40 | temperature_ = width_ / 10.0f; 41 | force_constant_ = sqrt(height_ * width_ / nodes_->size()); 42 | attraction_constant_ = attraction_multiplier_ * force_constant_; 43 | repulsion_constant_ = repulsion_multiplier_ * force_constant_; 44 | repulsion_constant_sq_ = repulsion_constant_ * repulsion_constant_; 45 | 46 | clog << "Mapping nodes" << endl; 47 | 48 | //map link to nodes 49 | for(int i=0; isize(); i++){ 50 | Edge& e = edges_->at(i); 51 | 52 | if(!find_node(e.source_id, &(e.source))){ 53 | error_ = "Could not find source node: " + e.source_id + " for edge: " + e.id + "."; 54 | } 55 | 56 | if(!find_node(e.target_id, &(e.target))){ 57 | error_ = "Could not find target node: " + e.target_id + " for edge: " + e.id + "."; 58 | } 59 | } 60 | 61 | } 62 | 63 | // Method for finding a node by id. 64 | // @param string id 65 | // @param Node** node 66 | // @return bool 67 | bool Layout::find_node(string id, Node** node){ 68 | for(int i=0; isize(); i++){ 69 | if(nodes_->at(i).id == id){ 70 | *node= &(nodes_->at(i)); 71 | return true; 72 | } 73 | } 74 | 75 | return false; 76 | } 77 | 78 | // Method for performing the force directed layout. 79 | // @return bool 80 | bool Layout::layout(){ 81 | 82 | clog << "Performing layout" << flush; 83 | 84 | bool mode_3d = switches_.mode == "3D" ? true : false; 85 | 86 | while(layout_iterations_ < max_iterations_ && temperature_ > 0.000001f){ 87 | 88 | if(layout_iterations_ % 10 == 0) 89 | clog << "." << flush; 90 | 91 | //calculate repulsion 92 | for(int i=0; isize(); i++){ 93 | 94 | Node& node_v = nodes_->at(i); 95 | 96 | if(i == 0){ 97 | node_v.layout.offset_x = 0; 98 | node_v.layout.offset_y = 0; 99 | node_v.layout.offset_z = 0; 100 | } 101 | 102 | node_v.layout.force = 0; 103 | 104 | node_v.layout.tmp_pos_x = max(node_v.layout.tmp_pos_x, node_v.x); 105 | node_v.layout.tmp_pos_y = max(node_v.layout.tmp_pos_y, node_v.y); 106 | 107 | if(mode_3d) 108 | node_v.layout.tmp_pos_z = max(node_v.layout.tmp_pos_z, node_v.z); 109 | 110 | 111 | for(int j=i+1; jsize(); j++){ 112 | if(i == j) continue; 113 | 114 | Node& node_u = nodes_->at(j); 115 | 116 | node_u.layout.tmp_pos_x = max(node_u.layout.tmp_pos_x, node_u.x); 117 | node_u.layout.tmp_pos_y = max(node_u.layout.tmp_pos_y, node_u.y); 118 | 119 | if(mode_3d) 120 | node_u.layout.tmp_pos_z = max(node_u.layout.tmp_pos_z, node_u.z); 121 | 122 | float delta_x = node_v.layout.tmp_pos_x - node_u.layout.tmp_pos_x; 123 | float delta_y = node_v.layout.tmp_pos_y - node_u.layout.tmp_pos_y; 124 | 125 | float delta_z = 0.0f; 126 | if(mode_3d) 127 | delta_z = node_v.layout.tmp_pos_z - node_u.layout.tmp_pos_z; 128 | 129 | float dx = sqrt((delta_x * delta_x) + (delta_y * delta_y)); 130 | 131 | float dy = 0.0f; 132 | if(mode_3d) 133 | dy = sqrt((delta_z * delta_z) + (delta_y * delta_y)); 134 | 135 | float delta_length = max(EPSILON_, dx); 136 | 137 | float delta_length_z = 0.0f; 138 | if(mode_3d) 139 | delta_length_z = max(EPSILON_, dy); 140 | 141 | float force = repulsion_constant_sq_ / delta_length; 142 | 143 | float force_z = 0.0f; 144 | if(mode_3d) 145 | force_z = repulsion_constant_sq_ / delta_length_z; 146 | 147 | node_v.layout.force += force; 148 | node_u.layout.force += force; 149 | 150 | float dxf = (delta_x / delta_length) * force; 151 | float dyf = (delta_y / delta_length) * force; 152 | 153 | float dzf = 0.0f; 154 | if(mode_3d) 155 | dzf = (delta_z / delta_length_z) * force_z; 156 | 157 | node_v.layout.offset_x += dxf; 158 | node_v.layout.offset_y += dyf; 159 | 160 | if(i == 0){ 161 | node_u.layout.offset_x = 0.0f; 162 | node_u.layout.offset_y = 0.0f; 163 | node_u.layout.offset_z = 0.0f; 164 | } 165 | 166 | node_u.layout.offset_x -= dxf; 167 | node_u.layout.offset_y -= dyf; 168 | 169 | if(mode_3d){ 170 | node_v.layout.offset_z += dzf; 171 | node_u.layout.offset_z -= dzf; 172 | } 173 | } 174 | 175 | } 176 | 177 | 178 | //calculate attraction 179 | for(int i=0; isize(); i++){ 180 | Edge& edge = edges_->at(i); 181 | 182 | float delta_x = edge.source->layout.tmp_pos_x - edge.target->layout.tmp_pos_x; 183 | float delta_y = edge.source->layout.tmp_pos_y - edge.target->layout.tmp_pos_y; 184 | 185 | float delta_z = 0.0f; 186 | if(mode_3d) 187 | delta_z = edge.source->layout.tmp_pos_z - edge.target->layout.tmp_pos_z; 188 | 189 | float dx = sqrt((delta_x * delta_x) + (delta_y * delta_y)); 190 | 191 | float dy = 0.0f; 192 | if(mode_3d) 193 | dy = sqrt((delta_z * delta_z) + (delta_y * delta_y)); 194 | 195 | float delta_length = max(EPSILON_, dx); 196 | 197 | float delta_length_z = 0.0f; 198 | if(mode_3d) 199 | delta_length_z = max(EPSILON_, dy); 200 | 201 | float force = (delta_length * delta_length) / attraction_constant_; 202 | 203 | float force_z = 0.0f; 204 | if(mode_3d) 205 | force_z = (delta_length_z * delta_length_z) / attraction_constant_; 206 | 207 | edge.source->layout.force -= force; 208 | edge.target->layout.force += force; 209 | 210 | float dxf = (delta_x / delta_length) * force; 211 | float dyf = (delta_y / delta_length) * force; 212 | 213 | float dzf = 0.0f; 214 | if(mode_3d) 215 | dzf = (delta_z / delta_length_z) * force_z; 216 | 217 | edge.source->layout.offset_x -= dxf; 218 | edge.source->layout.offset_y -= dyf; 219 | 220 | if(mode_3d) 221 | edge.source->layout.offset_z -= dzf; 222 | 223 | edge.target->layout.offset_x += dxf; 224 | edge.target->layout.offset_y += dyf; 225 | 226 | if(mode_3d) 227 | edge.target->layout.offset_z += dzf; 228 | } 229 | 230 | //calculate positions 231 | for(int i=0; isize(); i++){ 232 | Node& node = nodes_->at(i); 233 | 234 | float dx = sqrt((node.layout.offset_x * node.layout.offset_x) + (node.layout.offset_y * node.layout.offset_y)); 235 | 236 | float dy = 0.0f; 237 | if(mode_3d) 238 | dy = sqrt((node.layout.offset_z * node.layout.offset_z) + (node.layout.offset_y * node.layout.offset_y)); 239 | 240 | float delta_length = max(EPSILON_, dx); 241 | 242 | float delta_length_z = 0.0f; 243 | if(mode_3d) 244 | delta_length_z = max(EPSILON_, dy); 245 | 246 | node.layout.tmp_pos_x += (node.layout.offset_x / delta_length) * min(delta_length, temperature_); 247 | node.layout.tmp_pos_y += (node.layout.offset_y / delta_length) * min(delta_length, temperature_); 248 | 249 | if(mode_3d) 250 | node.layout.tmp_pos_z += (node.layout.offset_z / delta_length_z) * min(delta_length_z, temperature_); 251 | 252 | node.x -= (node.x - node.layout.tmp_pos_x)/10; 253 | node.y -= (node.y - node.layout.tmp_pos_y)/10; 254 | 255 | if(mode_3d) 256 | node.z -= (node.z - node.layout.tmp_pos_z)/10; 257 | } 258 | 259 | temperature_ *= (1.0f - (layout_iterations_ / max_iterations_)); 260 | layout_iterations_++; 261 | } 262 | 263 | clog << endl; 264 | 265 | return true; 266 | } 267 | 268 | // Method for retrieving the error string. 269 | // @return string 270 | string& Layout::error(){ 271 | return error_; 272 | } 273 | -------------------------------------------------------------------------------- /src/layout.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Jason Graves (GodLikeMouse/Collaboradev) 3 | * http://www.collaboradev.com 4 | * 5 | * This file is part of ForceDirectedLayout. 6 | * 7 | * ForceDirectedLayout is free software: you can redistribute it 8 | * and/or modify it under the terms of the GNU General Public 9 | * License as published by the Free Software Foundation, either 10 | * version 3 of the License, or (at your option) any later version. 11 | * 12 | * ForceDirectedLayout is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with ForceDirectedLayout. If not, see http://www.gnu.org/licenses/. 19 | */ 20 | 21 | #ifndef __LAYOUT_H 22 | #define __LAYOUT_H 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "command-switches.h" 30 | #include "node.h" 31 | #include "edge.h" 32 | 33 | using namespace std; 34 | 35 | class Layout { 36 | 37 | public: 38 | Layout(vector*, vector*, CommandSwitches&); 39 | bool layout(); 40 | string& error(); 41 | 42 | private: 43 | CommandSwitches switches_; 44 | vector* nodes_; 45 | vector* edges_; 46 | float attraction_multiplier_; 47 | float repulsion_multiplier_; 48 | int max_iterations_; 49 | float EPSILON_; 50 | float attraction_constant_; 51 | float repulsion_constant_; 52 | float force_constant_; 53 | int layout_iterations_; 54 | float temperature_; 55 | float repulsion_constant_sq_; 56 | int width_; 57 | int height_; 58 | string error_; 59 | 60 | bool find_node(string, Node**); 61 | 62 | }; 63 | 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /src/node.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Jason Graves (GodLikeMouse/Collaboradev) 3 | * http://www.collaboradev.com 4 | * 5 | * This file is part of ForceDirectedLayout. 6 | * 7 | * ForceDirectedLayout is free software: you can redistribute it 8 | * and/or modify it under the terms of the GNU General Public 9 | * License as published by the Free Software Foundation, either 10 | * version 3 of the License, or (at your option) any later version. 11 | * 12 | * ForceDirectedLayout is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with ForceDirectedLayout. If not, see http://www.gnu.org/licenses/. 19 | */ 20 | 21 | #include "node.h" 22 | #include 23 | #include 24 | #include 25 | 26 | // Constructor 27 | NodeLayout::NodeLayout(){ 28 | offset_x = 0.0f; 29 | offset_y = 0.0f; 30 | offset_z = 0.0f; 31 | tmp_pos_x = 0.0f; 32 | tmp_pos_y = 0.0f; 33 | tmp_pos_z = 0.0f; 34 | force = 0.0f; 35 | force_z = 0.0f; 36 | } 37 | 38 | // Constructor 39 | Node::Node(){ 40 | x = rand() % 10000000 / 1000000.0f; 41 | y = rand() % 10000000 / 1000000.0f; 42 | z = rand() % 10000000 / 1000000.0f; 43 | } 44 | -------------------------------------------------------------------------------- /src/node.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Jason Graves (GodLikeMouse/Collaboradev) 3 | * http://www.collaboradev.com 4 | * 5 | * This file is part of ForceDirectedLayout. 6 | * 7 | * ForceDirectedLayout is free software: you can redistribute it 8 | * and/or modify it under the terms of the GNU General Public 9 | * License as published by the Free Software Foundation, either 10 | * version 3 of the License, or (at your option) any later version. 11 | * 12 | * ForceDirectedLayout is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with ForceDirectedLayout. If not, see http://www.gnu.org/licenses/. 19 | */ 20 | 21 | #ifndef __NODE_H 22 | #define __NODE_H 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | using namespace std; 29 | 30 | class NodeLayout { 31 | 32 | public: 33 | NodeLayout(); 34 | 35 | float offset_x; 36 | float offset_y; 37 | float offset_z; 38 | float tmp_pos_x; 39 | float tmp_pos_y; 40 | float tmp_pos_z; 41 | float force; 42 | float force_z; 43 | 44 | }; 45 | 46 | class Node { 47 | 48 | public: 49 | Node(); 50 | 51 | string id; 52 | float x; 53 | float y; 54 | float z; 55 | NodeLayout layout; 56 | map attributes; 57 | 58 | }; 59 | 60 | #endif 61 | --------------------------------------------------------------------------------