2014-02-10 01:10:30 +00:00
/*************************************************************************/
2020-11-29 03:42:06 +00:00
/* doc_tools.cpp */
2014-02-10 01:10:30 +00:00
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 12:16:55 +00:00
/* https://godotengine.org */
2014-02-10 01:10:30 +00:00
/*************************************************************************/
2022-01-03 20:27:34 +00:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2014-02-10 01:10:30 +00:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
2018-01-04 23:50:27 +00:00
2020-11-29 03:42:06 +00:00
# include "doc_tools.h"
2014-02-16 00:16:33 +00:00
2020-11-07 22:33:38 +00:00
# include "core/config/engine.h"
# include "core/config/project_settings.h"
# include "core/core_constants.h"
2018-09-11 16:13:45 +00:00
# include "core/io/compression.h"
2021-06-11 12:51:48 +00:00
# include "core/io/dir_access.h"
2018-09-11 16:13:45 +00:00
# include "core/io/marshalls.h"
2020-11-07 22:33:38 +00:00
# include "core/object/script_language.h"
2021-12-15 15:01:06 +00:00
# include "core/string/translation.h"
2018-09-11 16:13:45 +00:00
# include "core/version.h"
2021-05-07 20:21:03 +00:00
# include "editor/editor_settings.h"
2014-06-30 01:41:02 +00:00
# include "scene/resources/theme.h"
2022-08-08 16:29:36 +00:00
# include "scene/theme/theme_db.h"
2014-02-16 00:16:33 +00:00
2020-04-20 15:57:38 +00:00
// Used for a hack preserving Mono properties on non-Mono builds.
2021-11-12 12:42:58 +00:00
# include "modules/modules_enabled.gen.h" // For mono.
2020-04-20 15:57:38 +00:00
2021-12-15 15:01:06 +00:00
static String _get_indent ( const String & p_text ) {
String indent ;
bool has_text = false ;
int line_start = 0 ;
for ( int i = 0 ; i < p_text . length ( ) ; i + + ) {
const char32_t c = p_text [ i ] ;
if ( c = = ' \n ' ) {
line_start = i + 1 ;
} else if ( c > 32 ) {
has_text = true ;
indent = p_text . substr ( line_start , i - line_start ) ;
break ; // Indentation of the first line that has text.
}
}
if ( ! has_text ) {
return p_text ;
}
return indent ;
}
static String _translate_doc_string ( const String & p_text ) {
const String indent = _get_indent ( p_text ) ;
const String message = p_text . dedent ( ) . strip_edges ( ) ;
const String translated = TranslationServer : : get_singleton ( ) - > doc_translate ( message , " " ) ;
// No need to restore stripped edges because they'll be stripped again later.
return translated . indent ( indent ) ;
}
2020-11-29 03:42:06 +00:00
void DocTools : : merge_from ( const DocTools & p_data ) {
2021-08-09 20:13:42 +00:00
for ( KeyValue < String , DocData : : ClassDoc > & E : class_list ) {
DocData : : ClassDoc & c = E . value ;
2014-02-10 01:10:30 +00:00
2020-05-14 14:41:43 +00:00
if ( ! p_data . class_list . has ( c . name ) ) {
2014-02-10 01:10:30 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2020-11-29 03:42:06 +00:00
const DocData : : ClassDoc & cf = p_data . class_list [ c . name ] ;
2014-02-10 01:10:30 +00:00
2022-08-28 06:17:25 +00:00
c . is_deprecated = cf . is_deprecated ;
c . is_experimental = cf . is_experimental ;
2014-02-10 01:10:30 +00:00
c . description = cf . description ;
c . brief_description = cf . brief_description ;
2017-09-12 20:42:36 +00:00
c . tutorials = cf . tutorials ;
2014-02-10 01:10:30 +00:00
2021-09-21 02:49:02 +00:00
for ( int i = 0 ; i < c . constructors . size ( ) ; i + + ) {
DocData : : MethodDoc & m = c . constructors . write [ i ] ;
2014-02-10 01:10:30 +00:00
2021-09-21 02:49:02 +00:00
for ( int j = 0 ; j < cf . constructors . size ( ) ; j + + ) {
if ( cf . constructors [ j ] . name ! = m . name ) {
2014-02-10 01:10:30 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2021-08-22 12:12:04 +00:00
2021-09-21 02:49:02 +00:00
{
// Since constructors can repeat, we need to check the type of
2021-08-22 12:12:04 +00:00
// the arguments so we make sure they are different.
2021-09-21 02:49:02 +00:00
if ( cf . constructors [ j ] . arguments . size ( ) ! = m . arguments . size ( ) ) {
2021-08-22 12:12:04 +00:00
continue ;
}
2021-09-21 02:49:02 +00:00
int arg_count = cf . constructors [ j ] . arguments . size ( ) ;
2021-08-22 12:12:04 +00:00
Vector < bool > arg_used ;
arg_used . resize ( arg_count ) ;
2020-05-14 14:41:43 +00:00
for ( int l = 0 ; l < arg_count ; + + l ) {
2021-08-22 12:12:04 +00:00
arg_used . write [ l ] = false ;
}
// also there is no guarantee that argument ordering will match, so we
// have to check one by one so we make sure we have an exact match
for ( int k = 0 ; k < arg_count ; + + k ) {
for ( int l = 0 ; l < arg_count ; + + l ) {
2021-09-21 02:49:02 +00:00
if ( cf . constructors [ j ] . arguments [ k ] . type = = m . arguments [ l ] . type & & ! arg_used [ l ] ) {
2021-08-22 12:12:04 +00:00
arg_used . write [ l ] = true ;
break ;
}
2016-05-03 06:45:54 +00:00
}
2020-05-14 14:41:43 +00:00
}
2021-08-22 12:12:04 +00:00
bool not_the_same = false ;
for ( int l = 0 ; l < arg_count ; + + l ) {
if ( ! arg_used [ l ] ) { // at least one of the arguments was different
not_the_same = true ;
}
}
if ( not_the_same ) {
continue ;
2020-05-14 14:41:43 +00:00
}
}
2015-04-22 03:25:13 +00:00
2021-09-21 02:49:02 +00:00
const DocData : : MethodDoc & mf = cf . constructors [ j ] ;
m . description = mf . description ;
2022-08-28 06:17:25 +00:00
m . is_deprecated = mf . is_deprecated ;
m . is_experimental = mf . is_experimental ;
2021-09-21 02:49:02 +00:00
break ;
}
}
for ( int i = 0 ; i < c . methods . size ( ) ; i + + ) {
DocData : : MethodDoc & m = c . methods . write [ i ] ;
for ( int j = 0 ; j < cf . methods . size ( ) ; j + + ) {
if ( cf . methods [ j ] . name ! = m . name ) {
continue ;
}
2020-11-29 03:42:06 +00:00
const DocData : : MethodDoc & mf = cf . methods [ j ] ;
2014-02-10 01:10:30 +00:00
m . description = mf . description ;
2022-08-28 06:17:25 +00:00
m . is_deprecated = mf . is_deprecated ;
m . is_experimental = mf . is_experimental ;
2014-02-10 01:10:30 +00:00
break ;
}
}
for ( int i = 0 ; i < c . signals . size ( ) ; i + + ) {
2020-11-29 03:42:06 +00:00
DocData : : MethodDoc & m = c . signals . write [ i ] ;
2014-02-10 01:10:30 +00:00
for ( int j = 0 ; j < cf . signals . size ( ) ; j + + ) {
2020-05-14 14:41:43 +00:00
if ( cf . signals [ j ] . name ! = m . name ) {
2014-02-10 01:10:30 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2020-11-29 03:42:06 +00:00
const DocData : : MethodDoc & mf = cf . signals [ j ] ;
2014-02-10 01:10:30 +00:00
m . description = mf . description ;
2022-08-28 06:17:25 +00:00
m . is_deprecated = mf . is_deprecated ;
m . is_experimental = mf . is_experimental ;
2014-02-10 01:10:30 +00:00
break ;
}
}
for ( int i = 0 ; i < c . constants . size ( ) ; i + + ) {
2020-11-29 03:42:06 +00:00
DocData : : ConstantDoc & m = c . constants . write [ i ] ;
2014-02-10 01:10:30 +00:00
for ( int j = 0 ; j < cf . constants . size ( ) ; j + + ) {
2020-05-14 14:41:43 +00:00
if ( cf . constants [ j ] . name ! = m . name ) {
2014-02-10 01:10:30 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2020-11-29 03:42:06 +00:00
const DocData : : ConstantDoc & mf = cf . constants [ j ] ;
2014-02-10 01:10:30 +00:00
m . description = mf . description ;
2022-08-28 06:17:25 +00:00
m . is_deprecated = mf . is_deprecated ;
m . is_experimental = mf . is_experimental ;
2014-02-10 01:10:30 +00:00
break ;
}
}
2022-07-04 15:56:34 +00:00
for ( int i = 0 ; i < c . annotations . size ( ) ; i + + ) {
DocData : : MethodDoc & m = c . annotations . write [ i ] ;
for ( int j = 0 ; j < cf . annotations . size ( ) ; j + + ) {
if ( cf . annotations [ j ] . name ! = m . name ) {
continue ;
}
const DocData : : MethodDoc & mf = cf . annotations [ j ] ;
m . description = mf . description ;
2022-08-28 06:17:25 +00:00
m . is_deprecated = mf . is_deprecated ;
m . is_experimental = mf . is_experimental ;
2022-07-04 15:56:34 +00:00
break ;
}
}
2014-02-10 01:10:30 +00:00
for ( int i = 0 ; i < c . properties . size ( ) ; i + + ) {
2020-11-29 03:42:06 +00:00
DocData : : PropertyDoc & p = c . properties . write [ i ] ;
2014-02-10 01:10:30 +00:00
for ( int j = 0 ; j < cf . properties . size ( ) ; j + + ) {
2020-05-14 14:41:43 +00:00
if ( cf . properties [ j ] . name ! = p . name ) {
2014-02-10 01:10:30 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2020-11-29 03:42:06 +00:00
const DocData : : PropertyDoc & pf = cf . properties [ j ] ;
2014-02-10 01:10:30 +00:00
p . description = pf . description ;
2022-08-28 06:17:25 +00:00
p . is_deprecated = pf . is_deprecated ;
p . is_experimental = pf . is_experimental ;
2014-02-10 01:10:30 +00:00
break ;
}
}
2014-06-30 01:41:02 +00:00
for ( int i = 0 ; i < c . theme_properties . size ( ) ; i + + ) {
2021-08-04 16:54:41 +00:00
DocData : : ThemeItemDoc & ti = c . theme_properties . write [ i ] ;
2014-06-30 01:41:02 +00:00
for ( int j = 0 ; j < cf . theme_properties . size ( ) ; j + + ) {
2021-08-04 16:54:41 +00:00
if ( cf . theme_properties [ j ] . name ! = ti . name | | cf . theme_properties [ j ] . data_type ! = ti . data_type ) {
2014-06-30 01:41:02 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2021-08-04 16:54:41 +00:00
const DocData : : ThemeItemDoc & pf = cf . theme_properties [ j ] ;
2014-06-30 01:41:02 +00:00
2021-08-04 16:54:41 +00:00
ti . description = pf . description ;
2014-06-30 01:41:02 +00:00
break ;
}
}
2020-04-20 15:57:38 +00:00
2021-09-21 02:49:02 +00:00
for ( int i = 0 ; i < c . operators . size ( ) ; i + + ) {
DocData : : MethodDoc & m = c . operators . write [ i ] ;
for ( int j = 0 ; j < cf . operators . size ( ) ; j + + ) {
if ( cf . operators [ j ] . name ! = m . name ) {
continue ;
}
{
// Since operators can repeat, we need to check the type of
// the arguments so we make sure they are different.
if ( cf . operators [ j ] . arguments . size ( ) ! = m . arguments . size ( ) ) {
continue ;
}
int arg_count = cf . operators [ j ] . arguments . size ( ) ;
Vector < bool > arg_used ;
arg_used . resize ( arg_count ) ;
for ( int l = 0 ; l < arg_count ; + + l ) {
arg_used . write [ l ] = false ;
}
// also there is no guarantee that argument ordering will match, so we
// have to check one by one so we make sure we have an exact match
for ( int k = 0 ; k < arg_count ; + + k ) {
for ( int l = 0 ; l < arg_count ; + + l ) {
if ( cf . operators [ j ] . arguments [ k ] . type = = m . arguments [ l ] . type & & ! arg_used [ l ] ) {
arg_used . write [ l ] = true ;
break ;
}
}
}
bool not_the_same = false ;
for ( int l = 0 ; l < arg_count ; + + l ) {
if ( ! arg_used [ l ] ) { // at least one of the arguments was different
not_the_same = true ;
}
}
if ( not_the_same ) {
continue ;
}
}
const DocData : : MethodDoc & mf = cf . operators [ j ] ;
m . description = mf . description ;
2022-08-28 06:17:25 +00:00
m . is_deprecated = mf . is_deprecated ;
m . is_experimental = mf . is_experimental ;
2021-09-21 02:49:02 +00:00
break ;
}
}
2020-04-20 15:57:38 +00:00
# ifndef MODULE_MONO_ENABLED
// The Mono module defines some properties that we want to keep when
// re-generating docs with a non-Mono build, to prevent pointless diffs
// (and loss of descriptions) depending on the config of the doc writer.
// We use a horrible hack to force keeping the relevant properties,
// hardcoded below. At least it's an ad hoc hack... ¯\_(ツ)_/¯
// Don't show this to your kids.
if ( c . name = = " @GlobalScope " ) {
// Retrieve GodotSharp singleton.
for ( int j = 0 ; j < cf . properties . size ( ) ; j + + ) {
if ( cf . properties [ j ] . name = = " GodotSharp " ) {
c . properties . push_back ( cf . properties [ j ] ) ;
}
}
}
# endif
2014-02-10 01:10:30 +00:00
}
}
2020-11-29 03:42:06 +00:00
void DocTools : : remove_from ( const DocTools & p_data ) {
2021-08-09 20:13:42 +00:00
for ( const KeyValue < String , DocData : : ClassDoc > & E : p_data . class_list ) {
if ( class_list . has ( E . key ) ) {
class_list . erase ( E . key ) ;
2020-05-14 14:41:43 +00:00
}
2016-12-16 11:12:22 +00:00
}
}
2020-11-29 03:42:06 +00:00
void DocTools : : add_doc ( const DocData : : ClassDoc & p_class_doc ) {
2021-12-09 09:42:46 +00:00
ERR_FAIL_COND ( p_class_doc . name . is_empty ( ) ) ;
2020-11-29 02:37:57 +00:00
class_list [ p_class_doc . name ] = p_class_doc ;
}
2020-11-29 03:42:06 +00:00
void DocTools : : remove_doc ( const String & p_class_name ) {
2021-12-09 09:42:46 +00:00
ERR_FAIL_COND ( p_class_name . is_empty ( ) | | ! class_list . has ( p_class_name ) ) ;
2020-11-29 02:37:57 +00:00
class_list . erase ( p_class_name ) ;
}
2020-11-29 03:42:06 +00:00
bool DocTools : : has_doc ( const String & p_class_name ) {
2021-12-09 09:42:46 +00:00
if ( p_class_name . is_empty ( ) ) {
2020-11-29 02:37:57 +00:00
return false ;
}
return class_list . has ( p_class_name ) ;
}
2019-09-03 10:42:34 +00:00
static Variant get_documentation_default_value ( const StringName & p_class_name , const StringName & p_property_name , bool & r_default_value_valid ) {
Variant default_value = Variant ( ) ;
r_default_value_valid = false ;
2022-11-09 07:59:49 +00:00
if ( ClassDB : : can_instantiate ( p_class_name ) & & ! ClassDB : : is_virtual ( p_class_name ) ) { // Keep this condition in sync with ClassDB::class_get_default_property_value.
2019-09-03 10:42:34 +00:00
default_value = ClassDB : : class_get_default_property_value ( p_class_name , p_property_name , & r_default_value_valid ) ;
} else {
2021-06-17 22:03:09 +00:00
// Cannot get default value of classes that can't be instantiated
2019-09-03 10:42:34 +00:00
List < StringName > inheriting_classes ;
ClassDB : : get_direct_inheriters_from_class ( p_class_name , & inheriting_classes ) ;
for ( List < StringName > : : Element * E2 = inheriting_classes . front ( ) ; E2 ; E2 = E2 - > next ( ) ) {
2021-06-17 22:03:09 +00:00
if ( ClassDB : : can_instantiate ( E2 - > get ( ) ) ) {
2019-09-03 10:42:34 +00:00
default_value = ClassDB : : class_get_default_property_value ( E2 - > get ( ) , p_property_name , & r_default_value_valid ) ;
2020-05-14 14:41:43 +00:00
if ( r_default_value_valid ) {
2019-09-03 10:42:34 +00:00
break ;
2020-05-14 14:41:43 +00:00
}
2019-09-03 10:42:34 +00:00
}
}
}
return default_value ;
}
2020-11-29 03:42:06 +00:00
void DocTools : : generate ( bool p_basic_types ) {
2022-08-08 12:18:26 +00:00
// Add ClassDB-exposed classes.
{
List < StringName > classes ;
ClassDB : : get_class_list ( & classes ) ;
classes . sort_custom < StringName : : AlphCompare > ( ) ;
// Move ProjectSettings, so that other classes can register properties there.
classes . move_to_back ( classes . find ( " ProjectSettings " ) ) ;
bool skip_setter_getter_methods = true ;
// Populate documentation data for each exposed class.
while ( classes . size ( ) ) {
String name = classes . front ( ) - > get ( ) ;
if ( ! ClassDB : : is_class_exposed ( name ) ) {
print_verbose ( vformat ( " Class '%s' is not exposed, skipping. " , name ) ) ;
classes . pop_front ( ) ;
continue ;
}
2017-01-04 04:16:14 +00:00
2022-08-08 12:18:26 +00:00
String cname = name ;
// Property setters and getters do not get exposed as individual methods.
HashSet < StringName > setters_getters ;
2018-06-11 16:41:16 +00:00
2022-08-08 12:18:26 +00:00
class_list [ cname ] = DocData : : ClassDoc ( ) ;
DocData : : ClassDoc & c = class_list [ cname ] ;
c . name = cname ;
c . inherits = ClassDB : : get_parent_class ( name ) ;
2022-01-18 21:07:47 +00:00
2022-08-08 12:18:26 +00:00
List < PropertyInfo > properties ;
List < PropertyInfo > own_properties ;
2019-09-03 10:42:34 +00:00
2022-08-08 12:18:26 +00:00
// Special case for editor and project settings, so they can be documented.
if ( name = = " EditorSettings " ) {
// We don't create the full blown EditorSettings (+ config file) with `create()`,
// instead we just make a local instance to get default values.
Ref < EditorSettings > edset = memnew ( EditorSettings ) ;
edset - > get_property_list ( & properties ) ;
own_properties = properties ;
} else if ( name = = " ProjectSettings " ) {
ProjectSettings : : get_singleton ( ) - > get_property_list ( & properties ) ;
own_properties = properties ;
} else {
ClassDB : : get_property_list ( name , & properties ) ;
ClassDB : : get_property_list ( name , & own_properties , true ) ;
2020-05-14 14:41:43 +00:00
}
2017-01-04 04:16:14 +00:00
2022-08-08 12:18:26 +00:00
properties . sort ( ) ;
own_properties . sort ( ) ;
2019-09-03 10:42:34 +00:00
2022-08-08 12:18:26 +00:00
List < PropertyInfo > : : Element * EO = own_properties . front ( ) ;
for ( const PropertyInfo & E : properties ) {
bool inherited = true ;
if ( EO & & EO - > get ( ) = = E ) {
inherited = false ;
EO = EO - > next ( ) ;
2021-12-02 19:38:49 +00:00
}
2019-06-29 12:51:33 +00:00
2022-08-08 12:18:26 +00:00
if ( E . usage & PROPERTY_USAGE_GROUP | | E . usage & PROPERTY_USAGE_SUBGROUP | | E . usage & PROPERTY_USAGE_CATEGORY | | E . usage & PROPERTY_USAGE_INTERNAL | | ( E . type = = Variant : : NIL & & E . usage & PROPERTY_USAGE_ARRAY ) ) {
2021-05-07 20:21:03 +00:00
continue ;
}
2022-08-08 12:18:26 +00:00
DocData : : PropertyDoc prop ;
prop . name = E . name ;
prop . overridden = inherited ;
if ( inherited ) {
String parent = ClassDB : : get_parent_class ( c . name ) ;
while ( ! ClassDB : : has_property ( parent , prop . name , true ) ) {
parent = ClassDB : : get_parent_class ( parent ) ;
}
prop . overrides = parent ;
2020-01-22 13:22:21 +00:00
}
2022-08-08 12:18:26 +00:00
bool default_value_valid = false ;
Variant default_value ;
if ( name = = " EditorSettings " ) {
if ( E . name = = " resource_local_to_scene " | | E . name = = " resource_name " | | E . name = = " resource_path " | | E . name = = " script " ) {
// Don't include spurious properties in the generated EditorSettings class reference.
continue ;
2020-05-20 19:49:39 +00:00
}
2020-01-22 13:22:21 +00:00
}
2022-08-08 12:18:26 +00:00
if ( name = = " ProjectSettings " ) {
// Special case for project settings, so that settings are not taken from the current project's settings
if ( E . name = = " script " | | ! ProjectSettings : : get_singleton ( ) - > is_builtin_setting ( E . name ) ) {
2020-01-22 13:22:21 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2022-08-08 12:18:26 +00:00
if ( E . usage & PROPERTY_USAGE_EDITOR ) {
if ( ! ProjectSettings : : get_singleton ( ) - > get_ignore_value_in_docs ( E . name ) ) {
default_value = ProjectSettings : : get_singleton ( ) - > property_get_revert ( E . name ) ;
default_value_valid = true ;
}
}
} else {
default_value = get_documentation_default_value ( name , E . name , default_value_valid ) ;
if ( inherited ) {
bool base_default_value_valid = false ;
Variant base_default_value = get_documentation_default_value ( ClassDB : : get_parent_class ( name ) , E . name , base_default_value_valid ) ;
if ( ! default_value_valid | | ! base_default_value_valid | | default_value = = base_default_value ) {
continue ;
}
}
2020-01-22 13:22:21 +00:00
}
2019-06-01 13:42:22 +00:00
2022-08-08 12:18:26 +00:00
if ( default_value_valid & & default_value . get_type ( ) ! = Variant : : OBJECT ) {
prop . default_value = default_value . get_construct_string ( ) . replace ( " \n " , " " ) ;
}
StringName setter = ClassDB : : get_property_setter ( name , E . name ) ;
StringName getter = ClassDB : : get_property_getter ( name , E . name ) ;
prop . setter = setter ;
prop . getter = getter ;
bool found_type = false ;
if ( getter ! = StringName ( ) ) {
MethodBind * mb = ClassDB : : get_method ( name , getter ) ;
if ( mb ) {
PropertyInfo retinfo = mb - > get_return_info ( ) ;
found_type = true ;
if ( retinfo . type = = Variant : : INT & & retinfo . usage & ( PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD ) ) {
prop . enumeration = retinfo . class_name ;
prop . type = " int " ;
} else if ( retinfo . class_name ! = StringName ( ) ) {
prop . type = retinfo . class_name ;
} else if ( retinfo . type = = Variant : : ARRAY & & retinfo . hint = = PROPERTY_HINT_ARRAY_TYPE ) {
prop . type = retinfo . hint_string + " [] " ;
} else if ( retinfo . hint = = PROPERTY_HINT_RESOURCE_TYPE ) {
prop . type = retinfo . hint_string ;
} else if ( retinfo . type = = Variant : : NIL & & retinfo . usage & PROPERTY_USAGE_NIL_IS_VARIANT ) {
prop . type = " Variant " ;
} else if ( retinfo . type = = Variant : : NIL ) {
prop . type = " void " ;
} else {
prop . type = Variant : : get_type_name ( retinfo . type ) ;
}
}
2019-06-29 12:51:33 +00:00
2022-08-08 12:18:26 +00:00
setters_getters . insert ( getter ) ;
}
if ( setter ! = StringName ( ) ) {
setters_getters . insert ( setter ) ;
}
if ( ! found_type ) {
if ( E . type = = Variant : : OBJECT & & E . hint = = PROPERTY_HINT_RESOURCE_TYPE ) {
prop . type = E . hint_string ;
2017-08-23 22:10:32 +00:00
} else {
2022-08-08 12:18:26 +00:00
prop . type = Variant : : get_type_name ( E . type ) ;
2017-08-23 22:10:32 +00:00
}
}
2017-11-24 15:32:59 +00:00
2022-08-08 12:18:26 +00:00
c . properties . push_back ( prop ) ;
2017-11-24 15:32:59 +00:00
}
2022-08-08 12:18:26 +00:00
List < MethodInfo > method_list ;
ClassDB : : get_method_list ( name , & method_list , true ) ;
method_list . sort ( ) ;
2017-08-23 22:10:32 +00:00
2022-08-08 12:18:26 +00:00
for ( const MethodInfo & E : method_list ) {
if ( E . name . is_empty ( ) | | ( E . name [ 0 ] = = ' _ ' & & ! ( E . flags & METHOD_FLAG_VIRTUAL ) ) ) {
continue ; //hidden, don't count
2020-05-14 14:41:43 +00:00
}
2017-01-04 04:16:14 +00:00
2022-08-08 12:18:26 +00:00
if ( skip_setter_getter_methods & & setters_getters . has ( E . name ) ) {
// Don't skip parametric setters and getters, i.e. method which require
// one or more parameters to define what property should be set or retrieved.
// E.g. CPUParticles3D::set_param(Parameter param, float value).
if ( E . arguments . size ( ) = = 0 /* getter */ | | ( E . arguments . size ( ) = = 1 & & E . return_val . type = = Variant : : NIL /* setter */ ) ) {
continue ;
}
2019-06-27 14:10:09 +00:00
}
2017-11-24 15:32:59 +00:00
2022-08-08 12:18:26 +00:00
DocData : : MethodDoc method ;
DocData : : method_doc_from_methodinfo ( method , E , " " ) ;
2014-02-10 01:10:30 +00:00
2022-08-08 12:18:26 +00:00
Vector < Error > errs = ClassDB : : get_method_error_return_values ( name , E . name ) ;
if ( errs . size ( ) ) {
if ( ! errs . has ( OK ) ) {
errs . insert ( 0 , OK ) ;
}
for ( int i = 0 ; i < errs . size ( ) ; i + + ) {
if ( ! method . errors_returned . has ( errs [ i ] ) ) {
method . errors_returned . push_back ( errs [ i ] ) ;
}
2021-08-24 18:16:25 +00:00
}
}
2022-08-08 12:18:26 +00:00
c . methods . push_back ( method ) ;
2021-08-24 18:16:25 +00:00
}
2022-08-08 12:18:26 +00:00
List < MethodInfo > signal_list ;
ClassDB : : get_signal_list ( name , & signal_list , true ) ;
2014-02-10 01:10:30 +00:00
2022-08-08 12:18:26 +00:00
if ( signal_list . size ( ) ) {
for ( List < MethodInfo > : : Element * EV = signal_list . front ( ) ; EV ; EV = EV - > next ( ) ) {
DocData : : MethodDoc signal ;
signal . name = EV - > get ( ) . name ;
for ( int i = 0 ; i < EV - > get ( ) . arguments . size ( ) ; i + + ) {
const PropertyInfo & arginfo = EV - > get ( ) . arguments [ i ] ;
DocData : : ArgumentDoc argument ;
DocData : : argument_doc_from_arginfo ( argument , arginfo ) ;
2014-02-10 01:10:30 +00:00
2022-08-08 12:18:26 +00:00
signal . arguments . push_back ( argument ) ;
}
2020-01-26 14:18:06 +00:00
2022-08-08 12:18:26 +00:00
c . signals . push_back ( signal ) ;
2014-02-10 01:10:30 +00:00
}
2022-08-08 12:18:26 +00:00
}
List < String > constant_list ;
ClassDB : : get_integer_constant_list ( name , & constant_list , true ) ;
2014-02-10 01:10:30 +00:00
2022-08-08 12:18:26 +00:00
for ( const String & E : constant_list ) {
DocData : : ConstantDoc constant ;
constant . name = E ;
constant . value = itos ( ClassDB : : get_integer_constant ( name , E ) ) ;
constant . is_value_valid = true ;
constant . enumeration = ClassDB : : get_integer_constant_enum ( name , E ) ;
constant . is_bitfield = ClassDB : : is_enum_bitfield ( name , constant . enumeration ) ;
c . constants . push_back ( constant ) ;
2014-02-10 01:10:30 +00:00
}
2022-08-08 12:18:26 +00:00
// Theme items.
{
List < StringName > l ;
2014-02-10 01:10:30 +00:00
2022-08-08 16:29:36 +00:00
ThemeDB : : get_singleton ( ) - > get_default_theme ( ) - > get_color_list ( cname , & l ) ;
2022-08-08 12:18:26 +00:00
for ( const StringName & E : l ) {
DocData : : ThemeItemDoc tid ;
tid . name = E ;
tid . type = " Color " ;
tid . data_type = " color " ;
2022-08-08 16:29:36 +00:00
tid . default_value = Variant ( ThemeDB : : get_singleton ( ) - > get_default_theme ( ) - > get_color ( E , cname ) ) . get_construct_string ( ) . replace ( " \n " , " " ) ;
2022-08-08 12:18:26 +00:00
c . theme_properties . push_back ( tid ) ;
}
2014-02-10 01:10:30 +00:00
2022-08-08 12:18:26 +00:00
l . clear ( ) ;
2022-08-08 16:29:36 +00:00
ThemeDB : : get_singleton ( ) - > get_default_theme ( ) - > get_constant_list ( cname , & l ) ;
2022-08-08 12:18:26 +00:00
for ( const StringName & E : l ) {
DocData : : ThemeItemDoc tid ;
tid . name = E ;
tid . type = " int " ;
tid . data_type = " constant " ;
2022-08-08 16:29:36 +00:00
tid . default_value = itos ( ThemeDB : : get_singleton ( ) - > get_default_theme ( ) - > get_constant ( E , cname ) ) ;
2022-08-08 12:18:26 +00:00
c . theme_properties . push_back ( tid ) ;
}
2014-06-30 01:41:02 +00:00
2022-08-08 12:18:26 +00:00
l . clear ( ) ;
2022-08-08 16:29:36 +00:00
ThemeDB : : get_singleton ( ) - > get_default_theme ( ) - > get_font_list ( cname , & l ) ;
2022-08-08 12:18:26 +00:00
for ( const StringName & E : l ) {
DocData : : ThemeItemDoc tid ;
tid . name = E ;
tid . type = " Font " ;
tid . data_type = " font " ;
c . theme_properties . push_back ( tid ) ;
}
2021-08-04 16:54:41 +00:00
2022-08-08 12:18:26 +00:00
l . clear ( ) ;
2022-08-08 16:29:36 +00:00
ThemeDB : : get_singleton ( ) - > get_default_theme ( ) - > get_font_size_list ( cname , & l ) ;
2022-08-08 12:18:26 +00:00
for ( const StringName & E : l ) {
DocData : : ThemeItemDoc tid ;
tid . name = E ;
tid . type = " int " ;
tid . data_type = " font_size " ;
c . theme_properties . push_back ( tid ) ;
}
2021-08-04 16:54:41 +00:00
2022-08-08 12:18:26 +00:00
l . clear ( ) ;
2022-08-08 16:29:36 +00:00
ThemeDB : : get_singleton ( ) - > get_default_theme ( ) - > get_icon_list ( cname , & l ) ;
2022-08-08 12:18:26 +00:00
for ( const StringName & E : l ) {
DocData : : ThemeItemDoc tid ;
tid . name = E ;
tid . type = " Texture2D " ;
tid . data_type = " icon " ;
c . theme_properties . push_back ( tid ) ;
}
2021-08-04 16:54:41 +00:00
2022-08-08 12:18:26 +00:00
l . clear ( ) ;
2022-08-08 16:29:36 +00:00
ThemeDB : : get_singleton ( ) - > get_default_theme ( ) - > get_stylebox_list ( cname , & l ) ;
2022-08-08 12:18:26 +00:00
for ( const StringName & E : l ) {
DocData : : ThemeItemDoc tid ;
tid . name = E ;
tid . type = " StyleBox " ;
tid . data_type = " style " ;
c . theme_properties . push_back ( tid ) ;
}
2021-08-04 16:54:41 +00:00
2022-08-08 12:18:26 +00:00
c . theme_properties . sort ( ) ;
2014-06-30 01:41:02 +00:00
}
2021-12-01 18:02:20 +00:00
2022-08-08 12:18:26 +00:00
classes . pop_front ( ) ;
2014-06-30 01:41:02 +00:00
}
2014-02-10 01:10:30 +00:00
}
2022-08-08 12:18:26 +00:00
// Add a dummy Variant entry.
2016-06-29 22:54:22 +00:00
{
2022-08-08 12:18:26 +00:00
// This allows us to document the concept of Variant even though
// it's not a ClassDB-exposed class.
2020-11-29 03:42:06 +00:00
class_list [ " Variant " ] = DocData : : ClassDoc ( ) ;
2016-06-29 22:54:22 +00:00
class_list [ " Variant " ] . name = " Variant " ;
}
2022-08-08 12:18:26 +00:00
// If we don't want to populate basic types, break here.
2020-05-14 14:41:43 +00:00
if ( ! p_basic_types ) {
2014-02-10 01:10:30 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2022-08-08 12:18:26 +00:00
// Add Variant data types.
2014-02-10 01:10:30 +00:00
for ( int i = 0 ; i < Variant : : VARIANT_MAX ; i + + ) {
2020-05-14 14:41:43 +00:00
if ( i = = Variant : : NIL ) {
2020-01-26 14:18:06 +00:00
continue ; // Not exposed outside of 'null', should not be in class list.
2020-05-14 14:41:43 +00:00
}
if ( i = = Variant : : OBJECT ) {
2020-01-26 14:18:06 +00:00
continue ; // Use the core type instead.
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2017-05-20 15:38:03 +00:00
String cname = Variant : : get_type_name ( Variant : : Type ( i ) ) ;
2014-02-10 01:10:30 +00:00
2020-11-29 03:42:06 +00:00
class_list [ cname ] = DocData : : ClassDoc ( ) ;
DocData : : ClassDoc & c = class_list [ cname ] ;
2017-05-20 15:38:03 +00:00
c . name = cname ;
2014-02-10 01:10:30 +00:00
2020-02-19 19:27:19 +00:00
Callable : : CallError cerror ;
2020-11-09 03:19:09 +00:00
Variant v ;
Variant : : construct ( Variant : : Type ( i ) , v , nullptr , 0 , cerror ) ;
2014-02-10 01:10:30 +00:00
2017-05-20 15:38:03 +00:00
List < MethodInfo > method_list ;
v . get_method_list ( & method_list ) ;
method_list . sort ( ) ;
Variant : : get_constructor_list ( Variant : : Type ( i ) , & method_list ) ;
2014-02-10 01:10:30 +00:00
2020-11-10 13:13:16 +00:00
for ( int j = 0 ; j < Variant : : OP_AND ; j + + ) { // Showing above 'and' is pretty confusing and there are a lot of variations.
2020-11-09 17:11:15 +00:00
for ( int k = 0 ; k < Variant : : VARIANT_MAX ; k + + ) {
2022-03-14 19:07:26 +00:00
// Prevent generating for comparison with null.
if ( Variant : : Type ( k ) = = Variant : : NIL & & ( Variant : : Operator ( j ) = = Variant : : OP_EQUAL | | Variant : : Operator ( j ) = = Variant : : OP_NOT_EQUAL ) ) {
continue ;
}
2020-11-09 17:11:15 +00:00
Variant : : Type rt = Variant : : get_operator_return_type ( Variant : : Operator ( j ) , Variant : : Type ( i ) , Variant : : Type ( k ) ) ;
2020-11-10 13:13:16 +00:00
if ( rt ! = Variant : : NIL ) { // Has operator.
// Skip String % operator as it's registered separately for each Variant arg type,
// we'll add it manually below.
2022-12-06 02:46:47 +00:00
if ( ( i = = Variant : : STRING | | i = = Variant : : STRING_NAME ) & & Variant : : Operator ( j ) = = Variant : : OP_MODULE ) {
2020-11-10 13:13:16 +00:00
continue ;
}
2020-11-09 17:11:15 +00:00
MethodInfo mi ;
mi . name = " operator " + Variant : : get_operator_name ( Variant : : Operator ( j ) ) ;
mi . return_val . type = rt ;
if ( k ! = Variant : : NIL ) {
PropertyInfo arg ;
arg . name = " right " ;
arg . type = Variant : : Type ( k ) ;
mi . arguments . push_back ( arg ) ;
}
method_list . push_back ( mi ) ;
}
}
}
2022-12-06 02:46:47 +00:00
if ( i = = Variant : : STRING | | i = = Variant : : STRING_NAME ) {
2020-11-10 13:13:16 +00:00
// We skipped % operator above, and we register it manually once for Variant arg type here.
MethodInfo mi ;
mi . name = " operator % " ;
mi . return_val . type = Variant : : STRING ;
PropertyInfo arg ;
arg . name = " right " ;
arg . type = Variant : : NIL ;
arg . usage = PROPERTY_USAGE_NIL_IS_VARIANT ;
mi . arguments . push_back ( arg ) ;
method_list . push_back ( mi ) ;
}
2020-11-09 17:11:15 +00:00
if ( Variant : : is_keyed ( Variant : : Type ( i ) ) ) {
MethodInfo mi ;
mi . name = " operator [] " ;
mi . return_val . type = Variant : : NIL ;
mi . return_val . usage = PROPERTY_USAGE_NIL_IS_VARIANT ;
PropertyInfo arg ;
arg . name = " key " ;
arg . type = Variant : : NIL ;
arg . usage = PROPERTY_USAGE_NIL_IS_VARIANT ;
mi . arguments . push_back ( arg ) ;
method_list . push_back ( mi ) ;
} else if ( Variant : : has_indexing ( Variant : : Type ( i ) ) ) {
MethodInfo mi ;
mi . name = " operator [] " ;
mi . return_val . type = Variant : : get_indexed_element_type ( Variant : : Type ( i ) ) ;
PropertyInfo arg ;
arg . name = " index " ;
arg . type = Variant : : INT ;
mi . arguments . push_back ( arg ) ;
method_list . push_back ( mi ) ;
}
2021-07-24 13:46:25 +00:00
for ( const MethodInfo & mi : method_list ) {
2020-11-29 03:42:06 +00:00
DocData : : MethodDoc method ;
2014-02-10 01:10:30 +00:00
2017-05-20 15:38:03 +00:00
method . name = mi . name ;
2014-02-10 01:10:30 +00:00
2019-02-12 20:10:08 +00:00
for ( int j = 0 ; j < mi . arguments . size ( ) ; j + + ) {
PropertyInfo arginfo = mi . arguments [ j ] ;
2020-11-29 03:42:06 +00:00
DocData : : ArgumentDoc ad ;
DocData : : argument_doc_from_arginfo ( ad , mi . arguments [ j ] ) ;
2017-08-29 05:15:46 +00:00
ad . name = arginfo . name ;
2020-03-09 09:51:17 +00:00
int darg_idx = mi . default_arguments . size ( ) - mi . arguments . size ( ) + j ;
if ( darg_idx > = 0 ) {
Variant default_arg = mi . default_arguments [ darg_idx ] ;
2022-01-18 10:31:15 +00:00
ad . default_value = default_arg . get_construct_string ( ) . replace ( " \n " , " " ) ;
2020-03-09 09:51:17 +00:00
}
2014-02-10 01:10:30 +00:00
2017-08-29 05:15:46 +00:00
method . arguments . push_back ( ad ) ;
2017-05-20 15:38:03 +00:00
}
2014-02-10 01:10:30 +00:00
2020-11-29 03:42:06 +00:00
DocData : : return_doc_from_retinfo ( method , mi . return_val ) ;
2020-02-19 19:27:19 +00:00
if ( mi . flags & METHOD_FLAG_VARARG ) {
2021-12-09 09:42:46 +00:00
if ( ! method . qualifiers . is_empty ( ) ) {
2020-02-19 19:27:19 +00:00
method . qualifiers + = " " ;
2020-05-14 14:41:43 +00:00
}
2020-02-19 19:27:19 +00:00
method . qualifiers + = " vararg " ;
2014-02-10 01:10:30 +00:00
}
2021-02-24 13:56:34 +00:00
if ( mi . flags & METHOD_FLAG_CONST ) {
2021-12-09 09:42:46 +00:00
if ( ! method . qualifiers . is_empty ( ) ) {
2021-02-24 13:56:34 +00:00
method . qualifiers + = " " ;
}
method . qualifiers + = " const " ;
}
if ( mi . flags & METHOD_FLAG_STATIC ) {
2021-12-09 09:42:46 +00:00
if ( ! method . qualifiers . is_empty ( ) ) {
2021-02-24 13:56:34 +00:00
method . qualifiers + = " " ;
}
method . qualifiers + = " static " ;
}
2021-09-21 02:49:02 +00:00
if ( method . name = = cname ) {
c . constructors . push_back ( method ) ;
} else if ( method . name . begins_with ( " operator " ) ) {
c . operators . push_back ( method ) ;
} else {
c . methods . push_back ( method ) ;
}
2017-05-20 15:38:03 +00:00
}
List < PropertyInfo > properties ;
v . get_property_list ( & properties ) ;
2021-07-24 13:46:25 +00:00
for ( const PropertyInfo & pi : properties ) {
2020-11-29 03:42:06 +00:00
DocData : : PropertyDoc property ;
2017-05-20 15:38:03 +00:00
property . name = pi . name ;
property . type = Variant : : get_type_name ( pi . type ) ;
2022-01-18 10:31:15 +00:00
property . default_value = v . get ( pi . name ) . get_construct_string ( ) . replace ( " \n " , " " ) ;
2014-02-10 01:10:30 +00:00
2017-05-20 15:38:03 +00:00
c . properties . push_back ( property ) ;
}
2014-02-10 01:10:30 +00:00
2017-05-20 15:38:03 +00:00
List < StringName > constants ;
2017-12-15 15:43:27 +00:00
Variant : : get_constants_for_type ( Variant : : Type ( i ) , & constants ) ;
2014-02-10 01:10:30 +00:00
2021-07-24 13:46:25 +00:00
for ( const StringName & E : constants ) {
2020-11-29 03:42:06 +00:00
DocData : : ConstantDoc constant ;
2021-07-16 03:45:57 +00:00
constant . name = E ;
Variant value = Variant : : get_constant_value ( Variant : : Type ( i ) , E ) ;
2022-01-18 10:31:15 +00:00
constant . value = value . get_type ( ) = = Variant : : INT ? itos ( value ) : value . get_construct_string ( ) . replace ( " \n " , " " ) ;
2020-05-20 19:49:39 +00:00
constant . is_value_valid = true ;
2017-05-20 15:38:03 +00:00
c . constants . push_back ( constant ) ;
2014-02-10 01:10:30 +00:00
}
}
2022-08-08 12:18:26 +00:00
// Add global API (servers, engine singletons, global constants) and Variant utility functions.
2014-02-10 01:10:30 +00:00
{
2017-11-15 17:45:34 +00:00
String cname = " @GlobalScope " ;
2020-11-29 03:42:06 +00:00
class_list [ cname ] = DocData : : ClassDoc ( ) ;
DocData : : ClassDoc & c = class_list [ cname ] ;
2014-02-10 01:10:30 +00:00
c . name = cname ;
2022-08-08 12:18:26 +00:00
// Global constants.
2020-11-07 22:33:38 +00:00
for ( int i = 0 ; i < CoreConstants : : get_global_constant_count ( ) ; i + + ) {
2020-11-29 03:42:06 +00:00
DocData : : ConstantDoc cd ;
2020-11-07 22:33:38 +00:00
cd . name = CoreConstants : : get_global_constant_name ( i ) ;
if ( ! CoreConstants : : get_ignore_value_in_docs ( i ) ) {
cd . value = itos ( CoreConstants : : get_global_constant_value ( i ) ) ;
2020-05-20 19:49:39 +00:00
cd . is_value_valid = true ;
} else {
cd . is_value_valid = false ;
}
2020-11-07 22:33:38 +00:00
cd . enumeration = CoreConstants : : get_global_constant_enum ( i ) ;
2014-02-10 01:10:30 +00:00
c . constants . push_back ( cd ) ;
}
2022-08-08 12:18:26 +00:00
// Servers/engine singletons.
2017-11-13 20:46:57 +00:00
List < Engine : : Singleton > singletons ;
Engine : : get_singleton ( ) - > get_singletons ( & singletons ) ;
2014-02-10 01:10:30 +00:00
2022-08-08 12:18:26 +00:00
// FIXME: this is kind of hackish...
2021-07-24 13:46:25 +00:00
for ( const Engine : : Singleton & s : singletons ) {
2020-11-29 03:42:06 +00:00
DocData : : PropertyDoc pd ;
2018-05-11 18:20:27 +00:00
if ( ! s . ptr ) {
continue ;
}
2014-02-10 01:10:30 +00:00
pd . name = s . name ;
2017-01-03 02:03:46 +00:00
pd . type = s . ptr - > get_class ( ) ;
2020-05-14 14:41:43 +00:00
while ( String ( ClassDB : : get_parent_class ( pd . type ) ) ! = " Object " ) {
2017-01-03 02:03:46 +00:00
pd . type = ClassDB : : get_parent_class ( pd . type ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
c . properties . push_back ( pd ) ;
}
2020-11-10 21:31:33 +00:00
2022-08-08 12:18:26 +00:00
// Variant utility functions.
2020-11-11 16:16:08 +00:00
List < StringName > utility_functions ;
Variant : : get_utility_function_list ( & utility_functions ) ;
utility_functions . sort_custom < StringName : : AlphCompare > ( ) ;
2021-07-24 13:46:25 +00:00
for ( const StringName & E : utility_functions ) {
2020-11-29 03:42:06 +00:00
DocData : : MethodDoc md ;
2021-07-16 03:45:57 +00:00
md . name = E ;
2022-08-08 12:18:26 +00:00
// Utility function's return type.
2021-07-16 03:45:57 +00:00
if ( Variant : : has_utility_function_return_value ( E ) ) {
2020-11-10 21:31:33 +00:00
PropertyInfo pi ;
2021-07-16 03:45:57 +00:00
pi . type = Variant : : get_utility_function_return_type ( E ) ;
2020-11-10 21:31:33 +00:00
if ( pi . type = = Variant : : NIL ) {
pi . usage = PROPERTY_USAGE_NIL_IS_VARIANT ;
}
DocData : : ArgumentDoc ad ;
2020-11-29 03:42:06 +00:00
DocData : : argument_doc_from_arginfo ( ad , pi ) ;
2020-11-10 21:31:33 +00:00
md . return_type = ad . type ;
}
2022-08-08 12:18:26 +00:00
// Utility function's arguments.
2021-07-16 03:45:57 +00:00
if ( Variant : : is_utility_function_vararg ( E ) ) {
2020-11-10 21:31:33 +00:00
md . qualifiers = " vararg " ;
} else {
2021-07-16 03:45:57 +00:00
for ( int i = 0 ; i < Variant : : get_utility_function_argument_count ( E ) ; i + + ) {
2020-11-10 21:31:33 +00:00
PropertyInfo pi ;
2021-07-16 03:45:57 +00:00
pi . type = Variant : : get_utility_function_argument_type ( E , i ) ;
pi . name = Variant : : get_utility_function_argument_name ( E , i ) ;
2020-11-10 21:31:33 +00:00
if ( pi . type = = Variant : : NIL ) {
pi . usage = PROPERTY_USAGE_NIL_IS_VARIANT ;
}
DocData : : ArgumentDoc ad ;
2020-11-29 03:42:06 +00:00
DocData : : argument_doc_from_arginfo ( ad , pi ) ;
2020-11-10 21:31:33 +00:00
md . arguments . push_back ( ad ) ;
}
}
c . methods . push_back ( md ) ;
}
2014-02-10 01:10:30 +00:00
}
2022-08-08 12:18:26 +00:00
// Add scripting language built-ins.
2014-02-10 01:10:30 +00:00
{
2022-08-08 12:18:26 +00:00
// We only add a doc entry for languages which actually define any built-in
// methods, constants, or annotations.
2014-02-10 01:10:30 +00:00
for ( int i = 0 ; i < ScriptServer : : get_language_count ( ) ; i + + ) {
ScriptLanguage * lang = ScriptServer : : get_language ( i ) ;
String cname = " @ " + lang - > get_name ( ) ;
2020-11-29 03:42:06 +00:00
DocData : : ClassDoc c ;
2014-02-10 01:10:30 +00:00
c . name = cname ;
2020-06-17 13:06:13 +00:00
// Get functions.
2014-02-10 01:10:30 +00:00
List < MethodInfo > minfo ;
lang - > get_public_functions ( & minfo ) ;
2021-07-24 13:46:25 +00:00
for ( const MethodInfo & mi : minfo ) {
2020-11-29 03:42:06 +00:00
DocData : : MethodDoc md ;
2014-02-10 01:10:30 +00:00
md . name = mi . name ;
2017-08-29 16:16:03 +00:00
if ( mi . flags & METHOD_FLAG_VARARG ) {
2021-12-09 09:42:46 +00:00
if ( ! md . qualifiers . is_empty ( ) ) {
2017-08-29 16:16:03 +00:00
md . qualifiers + = " " ;
2020-05-14 14:41:43 +00:00
}
2017-08-29 16:16:03 +00:00
md . qualifiers + = " vararg " ;
}
2020-11-29 03:42:06 +00:00
DocData : : return_doc_from_retinfo ( md , mi . return_val ) ;
2014-02-10 01:10:30 +00:00
2019-02-12 20:10:08 +00:00
for ( int j = 0 ; j < mi . arguments . size ( ) ; j + + ) {
2020-11-29 03:42:06 +00:00
DocData : : ArgumentDoc ad ;
DocData : : argument_doc_from_arginfo ( ad , mi . arguments [ j ] ) ;
2018-01-30 04:06:19 +00:00
2019-02-12 20:10:08 +00:00
int darg_idx = j - ( mi . arguments . size ( ) - mi . default_arguments . size ( ) ) ;
2018-01-30 04:06:19 +00:00
if ( darg_idx > = 0 ) {
2021-07-16 03:45:57 +00:00
Variant default_arg = mi . default_arguments [ darg_idx ] ;
2022-01-18 10:31:15 +00:00
ad . default_value = default_arg . get_construct_string ( ) . replace ( " \n " , " " ) ;
2018-01-30 04:06:19 +00:00
}
2014-02-10 01:10:30 +00:00
md . arguments . push_back ( ad ) ;
}
c . methods . push_back ( md ) ;
}
2020-06-17 13:06:13 +00:00
// Get constants.
2020-03-17 06:33:00 +00:00
List < Pair < String , Variant > > cinfo ;
2014-02-22 23:28:19 +00:00
lang - > get_public_constants ( & cinfo ) ;
2021-07-24 13:46:25 +00:00
for ( const Pair < String , Variant > & E : cinfo ) {
2020-11-29 03:42:06 +00:00
DocData : : ConstantDoc cd ;
2021-07-16 03:45:57 +00:00
cd . name = E . first ;
cd . value = E . second ;
2020-05-20 19:49:39 +00:00
cd . is_value_valid = true ;
2014-02-22 23:28:19 +00:00
c . constants . push_back ( cd ) ;
}
2020-06-17 13:06:13 +00:00
2022-07-04 15:56:34 +00:00
// Get annotations.
List < MethodInfo > ainfo ;
lang - > get_public_annotations ( & ainfo ) ;
for ( const MethodInfo & ai : ainfo ) {
DocData : : MethodDoc atd ;
atd . name = ai . name ;
if ( ai . flags & METHOD_FLAG_VARARG ) {
if ( ! atd . qualifiers . is_empty ( ) ) {
atd . qualifiers + = " " ;
}
atd . qualifiers + = " vararg " ;
}
DocData : : return_doc_from_retinfo ( atd , ai . return_val ) ;
for ( int j = 0 ; j < ai . arguments . size ( ) ; j + + ) {
DocData : : ArgumentDoc ad ;
DocData : : argument_doc_from_arginfo ( ad , ai . arguments [ j ] ) ;
int darg_idx = j - ( ai . arguments . size ( ) - ai . default_arguments . size ( ) ) ;
if ( darg_idx > = 0 ) {
Variant default_arg = ai . default_arguments [ darg_idx ] ;
ad . default_value = default_arg . get_construct_string ( ) . replace ( " \n " , " " ) ;
}
atd . arguments . push_back ( ad ) ;
}
c . annotations . push_back ( atd ) ;
}
2020-06-17 13:06:13 +00:00
// Skip adding the lang if it doesn't expose anything (e.g. C#).
2022-07-04 15:56:34 +00:00
if ( c . methods . is_empty ( ) & & c . constants . is_empty ( ) & & c . annotations . is_empty ( ) ) {
2020-06-17 13:06:13 +00:00
continue ;
}
class_list [ cname ] = c ;
2014-02-10 01:10:30 +00:00
}
}
}
static Error _parse_methods ( Ref < XMLParser > & parser , Vector < DocData : : MethodDoc > & methods ) {
String section = parser - > get_node_name ( ) ;
String element = section . substr ( 0 , section . length ( ) - 1 ) ;
while ( parser - > read ( ) = = OK ) {
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT ) {
if ( parser - > get_node_name ( ) = = element ) {
DocData : : MethodDoc method ;
ERR_FAIL_COND_V ( ! parser - > has_attribute ( " name " ) , ERR_FILE_CORRUPT ) ;
method . name = parser - > get_attribute_value ( " name " ) ;
2020-05-14 14:41:43 +00:00
if ( parser - > has_attribute ( " qualifiers " ) ) {
2014-02-10 01:10:30 +00:00
method . qualifiers = parser - > get_attribute_value ( " qualifiers " ) ;
2020-05-14 14:41:43 +00:00
}
2022-08-28 06:17:25 +00:00
if ( parser - > has_attribute ( " is_deprecated " ) ) {
method . is_deprecated = parser - > get_attribute_value ( " is_deprecated " ) . to_lower ( ) = = " true " ;
}
if ( parser - > has_attribute ( " is_experimental " ) ) {
method . is_experimental = parser - > get_attribute_value ( " is_experimental " ) . to_lower ( ) = = " true " ;
}
2014-02-10 01:10:30 +00:00
while ( parser - > read ( ) = = OK ) {
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT ) {
String name = parser - > get_node_name ( ) ;
if ( name = = " return " ) {
ERR_FAIL_COND_V ( ! parser - > has_attribute ( " type " ) , ERR_FILE_CORRUPT ) ;
method . return_type = parser - > get_attribute_value ( " type " ) ;
2017-09-02 19:52:45 +00:00
if ( parser - > has_attribute ( " enum " ) ) {
method . return_enum = parser - > get_attribute_value ( " enum " ) ;
}
2021-08-24 18:16:25 +00:00
} else if ( name = = " returns_error " ) {
ERR_FAIL_COND_V ( ! parser - > has_attribute ( " number " ) , ERR_FILE_CORRUPT ) ;
method . errors_returned . push_back ( parser - > get_attribute_value ( " number " ) . to_int ( ) ) ;
2022-08-06 18:11:48 +00:00
} else if ( name = = " param " ) {
2014-02-10 01:10:30 +00:00
DocData : : ArgumentDoc argument ;
ERR_FAIL_COND_V ( ! parser - > has_attribute ( " name " ) , ERR_FILE_CORRUPT ) ;
argument . name = parser - > get_attribute_value ( " name " ) ;
ERR_FAIL_COND_V ( ! parser - > has_attribute ( " type " ) , ERR_FILE_CORRUPT ) ;
argument . type = parser - > get_attribute_value ( " type " ) ;
2017-08-23 22:10:32 +00:00
if ( parser - > has_attribute ( " enum " ) ) {
argument . enumeration = parser - > get_attribute_value ( " enum " ) ;
}
2014-02-10 01:10:30 +00:00
method . arguments . push_back ( argument ) ;
} else if ( name = = " description " ) {
parser - > read ( ) ;
2020-05-14 14:41:43 +00:00
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_TEXT ) {
2017-11-11 01:11:24 +00:00
method . description = parser - > get_node_data ( ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
}
2020-05-14 14:41:43 +00:00
} else if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT_END & & parser - > get_node_name ( ) = = element ) {
2014-02-10 01:10:30 +00:00
break ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
}
methods . push_back ( method ) ;
} else {
2021-09-21 02:49:02 +00:00
ERR_FAIL_V_MSG ( ERR_FILE_CORRUPT , " Invalid tag in doc file: " + parser - > get_node_name ( ) + " , expected " + element + " . " ) ;
2014-02-10 01:10:30 +00:00
}
2020-05-14 14:41:43 +00:00
} else if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT_END & & parser - > get_node_name ( ) = = section ) {
2014-02-10 01:10:30 +00:00
break ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
}
return OK ;
}
2020-11-29 03:42:06 +00:00
Error DocTools : : load_classes ( const String & p_dir ) {
2017-09-12 20:42:36 +00:00
Error err ;
2022-03-23 09:08:58 +00:00
Ref < DirAccess > da = DirAccess : : open ( p_dir , & err ) ;
if ( da . is_null ( ) ) {
2014-02-10 01:10:30 +00:00
return err ;
2017-09-12 20:42:36 +00:00
}
2014-02-16 00:16:33 +00:00
2017-09-12 20:42:36 +00:00
da - > list_dir_begin ( ) ;
String path ;
2019-07-25 09:09:57 +00:00
path = da - > get_next ( ) ;
2021-12-09 09:42:46 +00:00
while ( ! path . is_empty ( ) ) {
2019-07-25 09:09:57 +00:00
if ( ! da - > current_is_dir ( ) & & path . ends_with ( " xml " ) ) {
2017-09-12 20:42:36 +00:00
Ref < XMLParser > parser = memnew ( XMLParser ) ;
2022-08-30 00:34:01 +00:00
Error err2 = parser - > open ( p_dir . path_join ( path ) ) ;
2020-05-14 14:41:43 +00:00
if ( err2 ) {
2019-02-12 20:10:08 +00:00
return err2 ;
2020-05-14 14:41:43 +00:00
}
2017-09-12 20:42:36 +00:00
_load ( parser ) ;
}
2019-07-25 09:09:57 +00:00
path = da - > get_next ( ) ;
2017-09-12 20:42:36 +00:00
}
2014-02-10 01:10:30 +00:00
2017-09-12 20:42:36 +00:00
da - > list_dir_end ( ) ;
2014-02-10 01:10:30 +00:00
2017-09-12 20:42:36 +00:00
return OK ;
}
2020-05-14 12:29:06 +00:00
2020-11-29 03:42:06 +00:00
Error DocTools : : erase_classes ( const String & p_dir ) {
2017-09-12 20:42:36 +00:00
Error err ;
2022-03-23 09:08:58 +00:00
Ref < DirAccess > da = DirAccess : : open ( p_dir , & err ) ;
if ( da . is_null ( ) ) {
2017-09-12 20:42:36 +00:00
return err ;
}
List < String > to_erase ;
da - > list_dir_begin ( ) ;
String path ;
2019-07-25 09:09:57 +00:00
path = da - > get_next ( ) ;
2021-12-09 09:42:46 +00:00
while ( ! path . is_empty ( ) ) {
2019-07-25 09:09:57 +00:00
if ( ! da - > current_is_dir ( ) & & path . ends_with ( " xml " ) ) {
2017-09-12 20:42:36 +00:00
to_erase . push_back ( path ) ;
2014-02-10 01:10:30 +00:00
}
2019-07-25 09:09:57 +00:00
path = da - > get_next ( ) ;
2017-09-12 20:42:36 +00:00
}
da - > list_dir_end ( ) ;
2017-09-12 21:06:26 +00:00
while ( to_erase . size ( ) ) {
2017-09-12 20:42:36 +00:00
da - > remove ( to_erase . front ( ) - > get ( ) ) ;
to_erase . pop_front ( ) ;
2014-02-10 01:10:30 +00:00
}
2017-09-12 20:42:36 +00:00
return OK ;
}
2020-05-14 12:29:06 +00:00
2020-11-29 03:42:06 +00:00
Error DocTools : : _load ( Ref < XMLParser > parser ) {
2017-09-12 20:42:36 +00:00
Error err = OK ;
2014-02-10 01:10:30 +00:00
while ( ( err = parser - > read ( ) ) = = OK ) {
2017-09-12 20:42:36 +00:00
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT & & parser - > get_node_name ( ) = = " ?xml " ) {
parser - > skip_section ( ) ;
}
2014-02-10 01:10:30 +00:00
2020-05-14 14:41:43 +00:00
if ( parser - > get_node_type ( ) ! = XMLParser : : NODE_ELEMENT ) {
2014-02-10 01:10:30 +00:00
continue ; //no idea what this may be, but skipping anyway
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
ERR_FAIL_COND_V ( parser - > get_node_name ( ) ! = " class " , ERR_FILE_CORRUPT ) ;
ERR_FAIL_COND_V ( ! parser - > has_attribute ( " name " ) , ERR_FILE_CORRUPT ) ;
String name = parser - > get_attribute_value ( " name " ) ;
2020-11-29 03:42:06 +00:00
class_list [ name ] = DocData : : ClassDoc ( ) ;
DocData : : ClassDoc & c = class_list [ name ] ;
2014-02-10 01:10:30 +00:00
c . name = name ;
2020-05-14 14:41:43 +00:00
if ( parser - > has_attribute ( " inherits " ) ) {
2014-02-10 01:10:30 +00:00
c . inherits = parser - > get_attribute_value ( " inherits " ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2022-08-28 06:17:25 +00:00
if ( parser - > has_attribute ( " is_deprecated " ) ) {
2022-09-14 02:59:17 +00:00
c . is_deprecated = parser - > get_attribute_value ( " is_deprecated " ) . to_lower ( ) = = " true " ;
2022-08-28 06:17:25 +00:00
}
if ( parser - > has_attribute ( " is_experimental " ) ) {
2022-09-14 02:59:17 +00:00
c . is_experimental = parser - > get_attribute_value ( " is_experimental " ) . to_lower ( ) = = " true " ;
2022-08-28 06:17:25 +00:00
}
2014-02-10 01:10:30 +00:00
while ( parser - > read ( ) = = OK ) {
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT ) {
2019-02-12 20:10:08 +00:00
String name2 = parser - > get_node_name ( ) ;
2014-02-10 01:10:30 +00:00
2019-02-12 20:10:08 +00:00
if ( name2 = = " brief_description " ) {
2014-02-10 01:10:30 +00:00
parser - > read ( ) ;
2020-05-14 14:41:43 +00:00
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_TEXT ) {
2017-11-11 01:11:24 +00:00
c . brief_description = parser - > get_node_data ( ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2019-02-12 20:10:08 +00:00
} else if ( name2 = = " description " ) {
2014-02-10 01:10:30 +00:00
parser - > read ( ) ;
2020-05-14 14:41:43 +00:00
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_TEXT ) {
2017-11-11 01:11:24 +00:00
c . description = parser - > get_node_data ( ) ;
2020-05-14 14:41:43 +00:00
}
2019-02-12 20:10:08 +00:00
} else if ( name2 = = " tutorials " ) {
2018-06-11 11:35:44 +00:00
while ( parser - > read ( ) = = OK ) {
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT ) {
2019-02-12 20:10:08 +00:00
String name3 = parser - > get_node_name ( ) ;
2018-06-11 11:35:44 +00:00
2019-02-12 20:10:08 +00:00
if ( name3 = = " link " ) {
2020-11-29 03:42:06 +00:00
DocData : : TutorialDoc tutorial ;
2020-06-07 01:26:35 +00:00
if ( parser - > has_attribute ( " title " ) ) {
tutorial . title = parser - > get_attribute_value ( " title " ) ;
}
2018-06-11 11:35:44 +00:00
parser - > read ( ) ;
2020-05-14 14:41:43 +00:00
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_TEXT ) {
2020-06-07 01:26:35 +00:00
tutorial . link = parser - > get_node_data ( ) . strip_edges ( ) ;
c . tutorials . push_back ( tutorial ) ;
2020-05-14 14:41:43 +00:00
}
2018-06-11 11:35:44 +00:00
} else {
2019-08-15 02:57:49 +00:00
ERR_FAIL_V_MSG ( ERR_FILE_CORRUPT , " Invalid tag in doc file: " + name3 + " . " ) ;
2018-06-11 11:35:44 +00:00
}
2020-05-14 14:41:43 +00:00
} else if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT_END & & parser - > get_node_name ( ) = = " tutorials " ) {
2019-09-24 11:34:03 +00:00
break ; // End of <tutorials>.
2020-05-14 14:41:43 +00:00
}
2018-06-11 11:35:44 +00:00
}
2021-09-21 02:49:02 +00:00
} else if ( name2 = = " constructors " ) {
Error err2 = _parse_methods ( parser , c . constructors ) ;
ERR_FAIL_COND_V ( err2 , err2 ) ;
2019-02-12 20:10:08 +00:00
} else if ( name2 = = " methods " ) {
Error err2 = _parse_methods ( parser , c . methods ) ;
ERR_FAIL_COND_V ( err2 , err2 ) ;
2021-09-21 02:49:02 +00:00
} else if ( name2 = = " operators " ) {
Error err2 = _parse_methods ( parser , c . operators ) ;
ERR_FAIL_COND_V ( err2 , err2 ) ;
2019-02-12 20:10:08 +00:00
} else if ( name2 = = " signals " ) {
Error err2 = _parse_methods ( parser , c . signals ) ;
ERR_FAIL_COND_V ( err2 , err2 ) ;
2022-07-04 15:56:34 +00:00
} else if ( name2 = = " annotations " ) {
Error err2 = _parse_methods ( parser , c . annotations ) ;
ERR_FAIL_COND_V ( err2 , err2 ) ;
2019-02-12 20:10:08 +00:00
} else if ( name2 = = " members " ) {
2014-02-10 01:10:30 +00:00
while ( parser - > read ( ) = = OK ) {
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT ) {
2019-02-12 20:10:08 +00:00
String name3 = parser - > get_node_name ( ) ;
2014-02-10 01:10:30 +00:00
2019-02-12 20:10:08 +00:00
if ( name3 = = " member " ) {
2020-11-29 03:42:06 +00:00
DocData : : PropertyDoc prop2 ;
2014-02-10 01:10:30 +00:00
ERR_FAIL_COND_V ( ! parser - > has_attribute ( " name " ) , ERR_FILE_CORRUPT ) ;
2019-02-12 20:10:08 +00:00
prop2 . name = parser - > get_attribute_value ( " name " ) ;
2014-02-10 01:10:30 +00:00
ERR_FAIL_COND_V ( ! parser - > has_attribute ( " type " ) , ERR_FILE_CORRUPT ) ;
2019-02-12 20:10:08 +00:00
prop2 . type = parser - > get_attribute_value ( " type " ) ;
2020-05-14 14:41:43 +00:00
if ( parser - > has_attribute ( " setter " ) ) {
2019-02-12 20:10:08 +00:00
prop2 . setter = parser - > get_attribute_value ( " setter " ) ;
2020-05-14 14:41:43 +00:00
}
if ( parser - > has_attribute ( " getter " ) ) {
2019-02-12 20:10:08 +00:00
prop2 . getter = parser - > get_attribute_value ( " getter " ) ;
2020-05-14 14:41:43 +00:00
}
if ( parser - > has_attribute ( " enum " ) ) {
2019-02-12 20:10:08 +00:00
prop2 . enumeration = parser - > get_attribute_value ( " enum " ) ;
2020-05-14 14:41:43 +00:00
}
2022-08-28 06:17:25 +00:00
if ( parser - > has_attribute ( " is_deprecated " ) ) {
prop2 . is_deprecated = parser - > get_attribute_value ( " is_deprecated " ) . to_lower ( ) = = " true " ;
}
if ( parser - > has_attribute ( " is_experimental " ) ) {
prop2 . is_experimental = parser - > get_attribute_value ( " is_experimental " ) . to_lower ( ) = = " true " ;
}
2019-09-24 11:34:03 +00:00
if ( ! parser - > is_empty ( ) ) {
parser - > read ( ) ;
2020-05-14 14:41:43 +00:00
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_TEXT ) {
2019-09-24 11:34:03 +00:00
prop2 . description = parser - > get_node_data ( ) ;
2020-05-14 14:41:43 +00:00
}
2019-09-24 11:34:03 +00:00
}
2019-02-12 20:10:08 +00:00
c . properties . push_back ( prop2 ) ;
2014-02-10 01:10:30 +00:00
} else {
2019-08-15 02:57:49 +00:00
ERR_FAIL_V_MSG ( ERR_FILE_CORRUPT , " Invalid tag in doc file: " + name3 + " . " ) ;
2014-02-10 01:10:30 +00:00
}
2020-05-14 14:41:43 +00:00
} else if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT_END & & parser - > get_node_name ( ) = = " members " ) {
2019-09-24 11:34:03 +00:00
break ; // End of <members>.
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
}
2019-02-12 20:10:08 +00:00
} else if ( name2 = = " theme_items " ) {
2014-06-30 01:41:02 +00:00
while ( parser - > read ( ) = = OK ) {
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT ) {
2019-02-12 20:10:08 +00:00
String name3 = parser - > get_node_name ( ) ;
2014-06-30 01:41:02 +00:00
2019-02-12 20:10:08 +00:00
if ( name3 = = " theme_item " ) {
2021-08-04 16:54:41 +00:00
DocData : : ThemeItemDoc prop2 ;
2014-06-30 01:41:02 +00:00
ERR_FAIL_COND_V ( ! parser - > has_attribute ( " name " ) , ERR_FILE_CORRUPT ) ;
2019-02-12 20:10:08 +00:00
prop2 . name = parser - > get_attribute_value ( " name " ) ;
2014-06-30 01:41:02 +00:00
ERR_FAIL_COND_V ( ! parser - > has_attribute ( " type " ) , ERR_FILE_CORRUPT ) ;
2019-02-12 20:10:08 +00:00
prop2 . type = parser - > get_attribute_value ( " type " ) ;
2021-08-04 16:54:41 +00:00
ERR_FAIL_COND_V ( ! parser - > has_attribute ( " data_type " ) , ERR_FILE_CORRUPT ) ;
prop2 . data_type = parser - > get_attribute_value ( " data_type " ) ;
2019-09-24 11:34:03 +00:00
if ( ! parser - > is_empty ( ) ) {
parser - > read ( ) ;
2020-05-14 14:41:43 +00:00
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_TEXT ) {
2019-09-24 11:34:03 +00:00
prop2 . description = parser - > get_node_data ( ) ;
2020-05-14 14:41:43 +00:00
}
2019-09-24 11:34:03 +00:00
}
2019-02-12 20:10:08 +00:00
c . theme_properties . push_back ( prop2 ) ;
2014-06-30 01:41:02 +00:00
} else {
2019-08-15 02:57:49 +00:00
ERR_FAIL_V_MSG ( ERR_FILE_CORRUPT , " Invalid tag in doc file: " + name3 + " . " ) ;
2014-06-30 01:41:02 +00:00
}
2020-05-14 14:41:43 +00:00
} else if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT_END & & parser - > get_node_name ( ) = = " theme_items " ) {
2019-09-24 11:34:03 +00:00
break ; // End of <theme_items>.
2020-05-14 14:41:43 +00:00
}
2014-06-30 01:41:02 +00:00
}
2019-02-12 20:10:08 +00:00
} else if ( name2 = = " constants " ) {
2014-02-10 01:10:30 +00:00
while ( parser - > read ( ) = = OK ) {
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT ) {
2019-02-12 20:10:08 +00:00
String name3 = parser - > get_node_name ( ) ;
2014-02-10 01:10:30 +00:00
2019-02-12 20:10:08 +00:00
if ( name3 = = " constant " ) {
2020-11-29 03:42:06 +00:00
DocData : : ConstantDoc constant2 ;
2014-02-10 01:10:30 +00:00
ERR_FAIL_COND_V ( ! parser - > has_attribute ( " name " ) , ERR_FILE_CORRUPT ) ;
2019-02-12 20:10:08 +00:00
constant2 . name = parser - > get_attribute_value ( " name " ) ;
2014-02-10 01:10:30 +00:00
ERR_FAIL_COND_V ( ! parser - > has_attribute ( " value " ) , ERR_FILE_CORRUPT ) ;
2019-02-12 20:10:08 +00:00
constant2 . value = parser - > get_attribute_value ( " value " ) ;
2020-05-20 19:49:39 +00:00
constant2 . is_value_valid = true ;
2017-08-23 22:10:32 +00:00
if ( parser - > has_attribute ( " enum " ) ) {
2019-02-12 20:10:08 +00:00
constant2 . enumeration = parser - > get_attribute_value ( " enum " ) ;
2017-08-23 22:10:32 +00:00
}
2022-06-24 09:16:37 +00:00
if ( parser - > has_attribute ( " is_bitfield " ) ) {
constant2 . is_bitfield = parser - > get_attribute_value ( " is_bitfield " ) . to_lower ( ) = = " true " ;
}
2022-08-28 06:17:25 +00:00
if ( parser - > has_attribute ( " is_deprecated " ) ) {
2022-09-14 02:59:17 +00:00
constant2 . is_deprecated = parser - > get_attribute_value ( " is_deprecated " ) . to_lower ( ) = = " true " ;
2022-08-28 06:17:25 +00:00
}
if ( parser - > has_attribute ( " is_experimental " ) ) {
2022-09-14 02:59:17 +00:00
constant2 . is_experimental = parser - > get_attribute_value ( " is_experimental " ) . to_lower ( ) = = " true " ;
2022-08-28 06:17:25 +00:00
}
2019-09-24 11:34:03 +00:00
if ( ! parser - > is_empty ( ) ) {
parser - > read ( ) ;
2020-05-14 14:41:43 +00:00
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_TEXT ) {
2019-09-24 11:34:03 +00:00
constant2 . description = parser - > get_node_data ( ) ;
2020-05-14 14:41:43 +00:00
}
2019-09-24 11:34:03 +00:00
}
2019-02-12 20:10:08 +00:00
c . constants . push_back ( constant2 ) ;
2014-02-10 01:10:30 +00:00
} else {
2019-08-15 02:57:49 +00:00
ERR_FAIL_V_MSG ( ERR_FILE_CORRUPT , " Invalid tag in doc file: " + name3 + " . " ) ;
2014-02-10 01:10:30 +00:00
}
2020-05-14 14:41:43 +00:00
} else if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT_END & & parser - > get_node_name ( ) = = " constants " ) {
2019-09-24 11:34:03 +00:00
break ; // End of <constants>.
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
}
} else {
2019-08-15 02:57:49 +00:00
ERR_FAIL_V_MSG ( ERR_FILE_CORRUPT , " Invalid tag in doc file: " + name2 + " . " ) ;
2014-02-10 01:10:30 +00:00
}
2020-05-14 14:41:43 +00:00
} else if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT_END & & parser - > get_node_name ( ) = = " class " ) {
2019-09-24 11:34:03 +00:00
break ; // End of <class>.
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
}
}
return OK ;
}
2022-03-23 09:08:58 +00:00
static void _write_string ( Ref < FileAccess > f , int p_tablevel , const String & p_string ) {
2021-12-09 09:42:46 +00:00
if ( p_string . is_empty ( ) ) {
2017-11-11 01:11:24 +00:00
return ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
String tab ;
2020-05-14 14:41:43 +00:00
for ( int i = 0 ; i < p_tablevel ; i + + ) {
2014-02-10 01:10:30 +00:00
tab + = " \t " ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
f - > store_string ( tab + p_string + " \n " ) ;
}
2022-03-23 09:08:58 +00:00
static void _write_method_doc ( Ref < FileAccess > f , const String & p_name , Vector < DocData : : MethodDoc > & p_method_docs ) {
2021-09-21 00:37:32 +00:00
if ( ! p_method_docs . is_empty ( ) ) {
p_method_docs . sort ( ) ;
_write_string ( f , 1 , " < " + p_name + " s> " ) ;
for ( int i = 0 ; i < p_method_docs . size ( ) ; i + + ) {
const DocData : : MethodDoc & m = p_method_docs [ i ] ;
String qualifiers ;
2021-12-09 09:42:46 +00:00
if ( ! m . qualifiers . is_empty ( ) ) {
2021-09-21 00:37:32 +00:00
qualifiers + = " qualifiers= \" " + m . qualifiers . xml_escape ( ) + " \" " ;
}
2022-08-28 06:17:25 +00:00
String additional_attributes ;
if ( m . is_deprecated ) {
2022-09-14 02:59:17 +00:00
additional_attributes + = " is_deprecated= \" true \" " ;
2022-08-28 06:17:25 +00:00
}
if ( m . is_experimental ) {
2022-09-14 02:59:17 +00:00
additional_attributes + = " is_experimental= \" true \" " ;
2022-08-28 06:17:25 +00:00
}
_write_string ( f , 2 , " < " + p_name + " name= \" " + m . name . xml_escape ( ) + " \" " + qualifiers + additional_attributes + " > " ) ;
2021-09-21 00:37:32 +00:00
2021-12-09 09:42:46 +00:00
if ( ! m . return_type . is_empty ( ) ) {
2021-09-21 00:37:32 +00:00
String enum_text ;
2021-12-09 09:42:46 +00:00
if ( ! m . return_enum . is_empty ( ) ) {
2021-09-21 00:37:32 +00:00
enum_text = " enum= \" " + m . return_enum + " \" " ;
}
_write_string ( f , 3 , " <return type= \" " + m . return_type + " \" " + enum_text + " /> " ) ;
}
if ( m . errors_returned . size ( ) > 0 ) {
for ( int j = 0 ; j < m . errors_returned . size ( ) ; j + + ) {
_write_string ( f , 3 , " <returns_error number= \" " + itos ( m . errors_returned [ j ] ) + " \" /> " ) ;
}
}
for ( int j = 0 ; j < m . arguments . size ( ) ; j + + ) {
const DocData : : ArgumentDoc & a = m . arguments [ j ] ;
String enum_text ;
2021-12-09 09:42:46 +00:00
if ( ! a . enumeration . is_empty ( ) ) {
2021-09-21 00:37:32 +00:00
enum_text = " enum= \" " + a . enumeration + " \" " ;
}
2021-12-09 09:42:46 +00:00
if ( ! a . default_value . is_empty ( ) ) {
2022-08-06 18:11:48 +00:00
_write_string ( f , 3 , " <param index= \" " + itos ( j ) + " \" name= \" " + a . name . xml_escape ( ) + " \" type= \" " + a . type . xml_escape ( ) + " \" " + enum_text + " default= \" " + a . default_value . xml_escape ( true ) + " \" /> " ) ;
2021-09-21 00:37:32 +00:00
} else {
2022-08-06 18:11:48 +00:00
_write_string ( f , 3 , " <param index= \" " + itos ( j ) + " \" name= \" " + a . name . xml_escape ( ) + " \" type= \" " + a . type . xml_escape ( ) + " \" " + enum_text + " /> " ) ;
2021-09-21 00:37:32 +00:00
}
}
_write_string ( f , 3 , " <description> " ) ;
2021-12-15 15:01:06 +00:00
_write_string ( f , 4 , _translate_doc_string ( m . description ) . strip_edges ( ) . xml_escape ( ) ) ;
2021-09-21 00:37:32 +00:00
_write_string ( f , 3 , " </description> " ) ;
_write_string ( f , 2 , " </ " + p_name + " > " ) ;
}
_write_string ( f , 1 , " </ " + p_name + " s> " ) ;
}
}
2022-05-13 13:04:37 +00:00
Error DocTools : : save_classes ( const String & p_default_path , const HashMap < String , String > & p_class_path ) {
2021-08-09 20:13:42 +00:00
for ( KeyValue < String , DocData : : ClassDoc > & E : class_list ) {
DocData : : ClassDoc & c = E . value ;
2017-03-05 15:44:50 +00:00
2017-09-12 20:42:36 +00:00
String save_path ;
if ( p_class_path . has ( c . name ) ) {
2017-09-12 21:06:26 +00:00
save_path = p_class_path [ c . name ] ;
2017-09-12 20:42:36 +00:00
} else {
2017-09-12 21:06:26 +00:00
save_path = p_default_path ;
2017-09-12 20:42:36 +00:00
}
Error err ;
2022-08-30 00:34:01 +00:00
String save_file = save_path . path_join ( c . name + " .xml " ) ;
2022-03-23 09:08:58 +00:00
Ref < FileAccess > f = FileAccess : : open ( save_file , FileAccess : : WRITE , & err ) ;
2019-08-15 02:57:49 +00:00
ERR_CONTINUE_MSG ( err ! = OK , " Can't write doc file: " + save_file + " . " ) ;
2017-09-12 20:42:36 +00:00
_write_string ( f , 0 , " <?xml version= \" 1.0 \" encoding= \" UTF-8 \" ?> " ) ;
2014-02-10 01:10:30 +00:00
String header = " <class name= \" " + c . name + " \" " ;
2021-12-09 09:42:46 +00:00
if ( ! c . inherits . is_empty ( ) ) {
2014-02-10 01:10:30 +00:00
header + = " inherits= \" " + c . inherits + " \" " ;
2022-08-28 06:17:25 +00:00
if ( c . is_deprecated ) {
2022-09-14 02:59:17 +00:00
header + = " is_deprecated= \" true \" " ;
2022-08-28 06:17:25 +00:00
}
if ( c . is_experimental ) {
2022-09-14 02:59:17 +00:00
header + = " is_experimental= \" true \" " ;
2022-08-28 06:17:25 +00:00
}
2020-05-14 14:41:43 +00:00
}
2020-02-14 16:38:20 +00:00
header + = String ( " version= \" " ) + VERSION_BRANCH + " \" " ;
2022-02-14 13:18:53 +00:00
// Reference the XML schema so editors can provide error checking.
// Modules are nested deep, so change the path to reference the same schema everywhere.
const String schema_path = save_path . find ( " modules/ " ) ! = - 1 ? " ../../../doc/class.xsd " : " ../class.xsd " ;
header + = vformat (
R " ( xmlns:xsi= " http : //www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="%s">)",
schema_path ) ;
2014-02-10 01:10:30 +00:00
_write_string ( f , 0 , header ) ;
2020-01-26 15:01:49 +00:00
2014-02-10 01:10:30 +00:00
_write_string ( f , 1 , " <brief_description> " ) ;
2021-12-15 15:01:06 +00:00
_write_string ( f , 2 , _translate_doc_string ( c . brief_description ) . strip_edges ( ) . xml_escape ( ) ) ;
2014-02-10 01:10:30 +00:00
_write_string ( f , 1 , " </brief_description> " ) ;
2020-01-26 15:01:49 +00:00
2014-02-10 01:10:30 +00:00
_write_string ( f , 1 , " <description> " ) ;
2021-12-15 15:01:06 +00:00
_write_string ( f , 2 , _translate_doc_string ( c . description ) . strip_edges ( ) . xml_escape ( ) ) ;
2014-02-10 01:10:30 +00:00
_write_string ( f , 1 , " </description> " ) ;
2020-01-26 15:01:49 +00:00
2017-09-12 20:42:36 +00:00
_write_string ( f , 1 , " <tutorials> " ) ;
2018-06-11 11:35:44 +00:00
for ( int i = 0 ; i < c . tutorials . size ( ) ; i + + ) {
2020-11-29 03:42:06 +00:00
DocData : : TutorialDoc tutorial = c . tutorials . get ( i ) ;
2022-02-17 03:11:23 +00:00
String title_attribute = ( ! tutorial . title . is_empty ( ) ) ? " title= \" " + _translate_doc_string ( tutorial . title ) . xml_escape ( ) + " \" " : " " ;
2020-06-07 01:26:35 +00:00
_write_string ( f , 2 , " <link " + title_attribute + " > " + tutorial . link . xml_escape ( ) + " </link> " ) ;
2018-06-11 11:35:44 +00:00
}
2017-09-12 20:42:36 +00:00
_write_string ( f , 1 , " </tutorials> " ) ;
2020-01-26 15:01:49 +00:00
2021-09-21 02:49:02 +00:00
_write_method_doc ( f , " constructor " , c . constructors ) ;
2021-09-21 00:37:32 +00:00
_write_method_doc ( f , " method " , c . methods ) ;
2014-02-10 01:10:30 +00:00
2021-09-21 00:37:32 +00:00
if ( ! c . properties . is_empty ( ) ) {
2014-02-10 01:10:30 +00:00
_write_string ( f , 1 , " <members> " ) ;
2016-06-21 20:20:34 +00:00
c . properties . sort ( ) ;
2014-02-10 01:10:30 +00:00
for ( int i = 0 ; i < c . properties . size ( ) ; i + + ) {
2019-06-01 13:42:22 +00:00
String additional_attributes ;
2021-12-09 09:42:46 +00:00
if ( ! c . properties [ i ] . enumeration . is_empty ( ) ) {
2019-06-01 13:42:22 +00:00
additional_attributes + = " enum= \" " + c . properties [ i ] . enumeration + " \" " ;
}
2021-12-09 09:42:46 +00:00
if ( ! c . properties [ i ] . default_value . is_empty ( ) ) {
2019-06-01 13:42:22 +00:00
additional_attributes + = " default= \" " + c . properties [ i ] . default_value . xml_escape ( true ) + " \" " ;
2017-08-23 22:10:32 +00:00
}
2022-08-28 06:17:25 +00:00
if ( c . properties [ i ] . is_deprecated ) {
2022-09-14 02:59:17 +00:00
additional_attributes + = " is_deprecated= \" true \" " ;
2022-08-28 06:17:25 +00:00
}
if ( c . properties [ i ] . is_experimental ) {
2022-09-14 02:59:17 +00:00
additional_attributes + = " is_experimental= \" true \" " ;
2022-08-28 06:17:25 +00:00
}
2019-09-03 10:42:34 +00:00
2020-11-29 03:42:06 +00:00
const DocData : : PropertyDoc & p = c . properties [ i ] ;
2019-09-03 10:42:34 +00:00
if ( c . properties [ i ] . overridden ) {
2021-12-02 19:38:49 +00:00
_write_string ( f , 2 , " <member name= \" " + p . name + " \" type= \" " + p . type + " \" setter= \" " + p . setter + " \" getter= \" " + p . getter + " \" overrides= \" " + p . overrides + " \" " + additional_attributes + " /> " ) ;
2019-09-03 10:42:34 +00:00
} else {
_write_string ( f , 2 , " <member name= \" " + p . name + " \" type= \" " + p . type + " \" setter= \" " + p . setter + " \" getter= \" " + p . getter + " \" " + additional_attributes + " > " ) ;
2021-12-15 15:01:06 +00:00
_write_string ( f , 3 , _translate_doc_string ( p . description ) . strip_edges ( ) . xml_escape ( ) ) ;
2019-09-03 10:42:34 +00:00
_write_string ( f , 2 , " </member> " ) ;
}
2014-02-10 01:10:30 +00:00
}
_write_string ( f , 1 , " </members> " ) ;
}
2021-09-21 00:37:32 +00:00
_write_method_doc ( f , " signal " , c . signals ) ;
2014-02-10 01:10:30 +00:00
2021-09-21 00:37:32 +00:00
if ( ! c . constants . is_empty ( ) ) {
_write_string ( f , 1 , " <constants> " ) ;
for ( int i = 0 ; i < c . constants . size ( ) ; i + + ) {
const DocData : : ConstantDoc & k = c . constants [ i ] ;
2022-08-28 06:17:25 +00:00
String additional_attributes ;
if ( c . constants [ i ] . is_deprecated ) {
2022-09-14 02:59:17 +00:00
additional_attributes + = " is_deprecated= \" true \" " ;
2022-08-28 06:17:25 +00:00
}
if ( c . constants [ i ] . is_experimental ) {
2022-09-14 02:59:17 +00:00
additional_attributes + = " is_experimental= \" true \" " ;
2022-08-28 06:17:25 +00:00
}
2021-09-21 00:37:32 +00:00
if ( k . is_value_valid ) {
2021-12-09 09:42:46 +00:00
if ( ! k . enumeration . is_empty ( ) ) {
2022-06-24 09:16:37 +00:00
if ( k . is_bitfield ) {
2022-08-28 06:17:25 +00:00
_write_string ( f , 2 , " <constant name= \" " + k . name + " \" value= \" " + k . value + " \" enum= \" " + k . enumeration + " \" is_bitfield= \" true \" " + additional_attributes + " > " ) ;
2022-06-24 09:16:37 +00:00
} else {
2022-08-28 06:17:25 +00:00
_write_string ( f , 2 , " <constant name= \" " + k . name + " \" value= \" " + k . value + " \" enum= \" " + k . enumeration + " \" " + additional_attributes + " > " ) ;
2022-06-24 09:16:37 +00:00
}
2021-09-21 00:37:32 +00:00
} else {
2022-08-28 06:17:25 +00:00
_write_string ( f , 2 , " <constant name= \" " + k . name + " \" value= \" " + k . value + " \" " + additional_attributes + " > " ) ;
2021-09-21 00:37:32 +00:00
}
2020-05-20 19:49:39 +00:00
} else {
2021-12-09 09:42:46 +00:00
if ( ! k . enumeration . is_empty ( ) ) {
2022-08-28 06:17:25 +00:00
_write_string ( f , 2 , " <constant name= \" " + k . name + " \" value= \" platform-dependent \" enum= \" " + k . enumeration + " \" " + additional_attributes + " > " ) ;
2021-09-21 00:37:32 +00:00
} else {
2022-08-28 06:17:25 +00:00
_write_string ( f , 2 , " <constant name= \" " + k . name + " \" value= \" platform-dependent \" " + additional_attributes + " > " ) ;
2021-09-21 00:37:32 +00:00
}
2020-05-20 19:49:39 +00:00
}
2021-12-15 15:01:06 +00:00
_write_string ( f , 3 , _translate_doc_string ( k . description ) . strip_edges ( ) . xml_escape ( ) ) ;
2021-09-21 00:37:32 +00:00
_write_string ( f , 2 , " </constant> " ) ;
2017-08-23 22:10:32 +00:00
}
2014-02-10 01:10:30 +00:00
2021-09-21 00:37:32 +00:00
_write_string ( f , 1 , " </constants> " ) ;
}
2014-06-30 01:41:02 +00:00
2022-07-04 15:56:34 +00:00
_write_method_doc ( f , " annotation " , c . annotations ) ;
2021-09-21 00:37:32 +00:00
if ( ! c . theme_properties . is_empty ( ) ) {
2016-06-21 20:20:34 +00:00
c . theme_properties . sort ( ) ;
2014-10-14 22:44:41 +00:00
_write_string ( f , 1 , " <theme_items> " ) ;
2014-06-30 01:41:02 +00:00
for ( int i = 0 ; i < c . theme_properties . size ( ) ; i + + ) {
2021-08-04 16:54:41 +00:00
const DocData : : ThemeItemDoc & ti = c . theme_properties [ i ] ;
2019-06-01 13:42:22 +00:00
2021-12-09 09:42:46 +00:00
if ( ! ti . default_value . is_empty ( ) ) {
2021-08-04 16:54:41 +00:00
_write_string ( f , 2 , " <theme_item name= \" " + ti . name + " \" data_type= \" " + ti . data_type + " \" type= \" " + ti . type + " \" default= \" " + ti . default_value . xml_escape ( true ) + " \" > " ) ;
2020-05-14 14:41:43 +00:00
} else {
2021-08-04 16:54:41 +00:00
_write_string ( f , 2 , " <theme_item name= \" " + ti . name + " \" data_type= \" " + ti . data_type + " \" type= \" " + ti . type + " \" > " ) ;
2020-05-14 14:41:43 +00:00
}
2019-06-01 13:42:22 +00:00
2021-12-15 15:01:06 +00:00
_write_string ( f , 3 , _translate_doc_string ( ti . description ) . strip_edges ( ) . xml_escape ( ) ) ;
2019-06-01 13:42:22 +00:00
2014-06-30 01:41:02 +00:00
_write_string ( f , 2 , " </theme_item> " ) ;
}
2014-10-14 22:44:41 +00:00
_write_string ( f , 1 , " </theme_items> " ) ;
2014-06-30 01:41:02 +00:00
}
2021-09-21 02:49:02 +00:00
_write_method_doc ( f , " operator " , c . operators ) ;
2014-10-14 22:44:41 +00:00
_write_string ( f , 0 , " </class> " ) ;
2014-02-10 01:10:30 +00:00
}
return OK ;
}
2020-11-29 03:42:06 +00:00
Error DocTools : : load_compressed ( const uint8_t * p_data , int p_compressed_size , int p_uncompressed_size ) {
2014-02-10 01:10:30 +00:00
Vector < uint8_t > data ;
data . resize ( p_uncompressed_size ) ;
2022-02-18 13:36:13 +00:00
int ret = Compression : : decompress ( data . ptrw ( ) , p_uncompressed_size , p_data , p_compressed_size , Compression : : MODE_DEFLATE ) ;
ERR_FAIL_COND_V_MSG ( ret = = - 1 , ERR_FILE_CORRUPT , " Compressed file is corrupt. " ) ;
2014-02-10 01:10:30 +00:00
class_list . clear ( ) ;
2014-02-16 00:16:33 +00:00
Ref < XMLParser > parser = memnew ( XMLParser ) ;
Error err = parser - > open_buffer ( data ) ;
2020-05-14 14:41:43 +00:00
if ( err ) {
2014-02-16 00:16:33 +00:00
return err ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2014-02-16 00:16:33 +00:00
_load ( parser ) ;
2014-02-10 01:10:30 +00:00
return OK ;
}