Commit graph

584 commits

Author SHA1 Message Date
William S Fulton
3cc4b21163 Parameter name expansion fix for template functions.
Fix regression in 4.0.0 where a template function containing a parameter
with the same name as the function name led to the parameter name used in the
target language being incorrectly modified.

Closes #1602
2019-08-01 19:46:16 +01:00
William S Fulton
2cf075558c Replace all exit() with SWIG_exit()
For consistent cleanup on error
2019-07-31 00:08:49 +01:00
Vadim Zeitlin
c321aca2b4 Fix missing value for first item of enums with trailing comma
The value of the first item of an enum with a trailing comma after its
last item was not correctly initialized to 0 any more after the changes
of 74adaa5738 (see #1515) because "_last"
attribute was not set correctly in this case.

Do set it for the last item when it's followed by a comma too and add
more unit tests checking for this.

Closes #1566.
2019-07-11 17:15:38 +02:00
Vadim Zeitlin
74adaa5738 Fix parsing of enums with trailing comma with -doxygen
To correctly parse Doxygen post-comments after the trailing comma,
switch to right recursion in "enumlist" production rule definition: this
does consume more stack space when parsing, but makes the rules much
easier to write and to understand and hopefully shouldn't result in any
problems with real code (which shouldn't have thousands of enums items
in it).

Closes #1514.
2019-04-20 13:52:56 +02:00
Zackery Spytz
c3d652c785 Fix the handling of director classes with final methods
Generated SwigDirector_* classes were attempting to override
methods marked as final.

In addition, give a warning if the destructor of a director class is
final.

Closes #564.
2019-02-22 06:28:53 -07:00
William S Fulton
b9350614b5 Fix typemap matching to expand template parameters when the name contains template parameters.
In the %typemap below the type is T and the name is X<T>::make
which now expands correctly to X< int >::make

template<typename T> struct X {
  %typemap(out) T X<T>::make "..."
  T make();
};

%template(Xint) X<int>;
2019-02-17 21:38:48 +00:00
William S Fulton
eb7b989c61 Fix parser error containing multiple #define statements inside an enum.
The second #define fails to parse:

  enum FooEnum {
    ENUM1 = 0,
    ENUM2 = 1,

  #define MACRO_DEF1 "Hello"
  #define MACRO_DEF2 "World!"

    ENUM3 = 2,
    ENUM4 = 3,
  };

Bug mentioned at https://sourceforge.net/p/swig/patches/333/
2019-02-16 08:09:56 +00:00
Seth R Johnson
373a21aa3f Fix failure of %apply directive in anonymous templates 2019-02-06 08:48:40 -05:00
William S Fulton
45a8c8cfa3 Merge branch 'vadz-fix-dtor-nested-template'
* vadz-fix-dtor-nested-template:
  Stop mangling dtors of nested classes instead a template class
  Remove unused cparse_template_expand() return type
2018-12-06 08:13:25 +00:00
Vadim Zeitlin
30bb977a64 Fix handling of abstract base classes nested inside templates
Code handling %template in the parser created a totally new top-level
module child of namespace type when handling templates inside a
namespace and copied the nodes from the previously parsed C++ template
declaration to it. However copies of this node kept their original
values of "abstracts" attribute, which contained pointers to the classes
in the original template declaration, i.e. outside of the subtree
created for the instantiated template. This, in turn, meant that during
the types resolution pass, the code in TypePass did not update the types
used in the methods of the classes appearing in the "abstracts" List,
even though it did update the types for the children of the instantiated
template subtree.

And this finally resulted in wrongly detecting overridden virtual
methods as abstract in Allocate::is_abstract_inherit() during the next
pass, as the signatures of the overridden method -- using resolved types
-- and of the method from the class pointed to by "abstract" -- using
the original types from C++ code -- didn't match.

Resolve this simply by not copying "abstracts" attributes when creating
the template subtree and doing another pass over this tree to recreate
them using the new nodes, just as it's already done for "defaultargs"
attribute, presumably for similar reasons. Note that doing another pass
over the tree is not as efficient as doing everything in a single pass,
but merging the new update_abstracts() with update_defaultargs() is not
completely obvious, so for now keep it simple and optimize it later if
necessary.

Also, add a test checking for the situation described above.

Closes #1353.
2018-11-24 19:21:47 +01:00
Vadim Zeitlin
4af2e95010 Stop mangling dtors of nested classes instead a template class
cparse_template_expand() incorrectly appended template parameters to all
destructor nodes it encountered during the tree traversal, including the
dtors of any nested classes.

This resulted in WARN_LANG_ILLEGAL_DESTRUCTOR warnings from
Language::destructorDeclaration() later and possibly other problems due
to not actually wrapping these dtors.

Fix this by explicitly checking if the dtor is a child or, to account
for %extend, a grandchild of the template node itself before appending
template parameters to it.

This commit is best viewed with "-w" (ignore whitespace changes) option
as it indents, without changing, a block of code.
2018-11-23 02:48:55 +01:00
Vadim Zeitlin
2c16de295b Remove unused cparse_template_expand() return type
No real changes, just make a function which always returned 0 void
as its return value was never used anyhow.
2018-11-23 02:48:55 +01:00
Vladimir Kalinin
5a22bcf007 Nested structures in "C" mode symbol table fix 2018-08-14 21:25:35 +03:00
William S Fulton
33921666a1 Merge branch 'vadz-doxygen'
This is the Doxygen work begun in Google Summer of Code projects 2008
and 2012 and subsequently improved by numerous contributors.

* vadz-doxygen: (314 commits)
  Add changes entry for Doxygen support
  Add some missing doctype tyemaps
  Doxygen warnings cleanup
  Move doxygen warning numbers
  Add Python doxygen example
  Doxygen example
  Add Doxygen to include paths
  Doxygen source rename
  More merge fixes from doxygen branches
  Correct python example headers
  Correct source code headers
  Another merge fix from doxygen branches
  Java enums output format fixes
  Add omitted doxygen_parsing_enums testcase
  PEP8 conformance for comment verifier module
  Clean up merge problem
  Doxygen html tweaks
  Update html chapter numbering for added Doxygen chapter
  Fixes to makechap.py to detect ill-formed headers
  html fixes for Doxygen
  Add missing CPlusPlus17.html file
  Format files to unix format
  Doxygen testcase tweak to match that in the html docs
  Doxygen html documentation updates and corrections
  Remove doxygen Examples subdirectory
  Beautify doxygen source code
  Code formatting fixes in doxygen code
  Remove unused doxygen code
  new_node refactor
  Various merge fixes in doxygen branches
  Unused variable warning fix
  Fix wrongly resetting indent after formulae in Doxygen comments
  Add support for doxygen:alias feature
  Get rid of meaningless return type of DoxygenParser methods
  Return enum, not untyped int, when classifying Doxygen commands
  Get rid of unnecessary "typedef enum" in C++ code
  Use slash, not backslash, in "C/C++" in the documentation
  Replace literal "<" with "&lt;" in HTML documentation
  Fix broken link to java.sun.com in Doxygen documentation
  Fix using com.sun.tools.javadoc package under macOS
  Fix error reporting for special characters in Doxygen parsing code
  Switch Python Doxygen unit tests to use inspect.getdoc()
  Use correct separator in Java class path under Windows.
  Remove executable permission from appveyor.yml.
  Use JAVA_HOME value in configure to detect Java.
  Display JAVA_HOME value in "make java_version".
  Fix harmless MSVC warning in DoxygenTranslator code.
  Reset "_last" for all but first enum elements.
  Don't duplicate Javadoc from global enum Doxygen comments twice.
  Move Doxygen comments concatenation from the parser to the lexer.
  Fix shift/reduce conflicts in Doxygen pre/post comment parsing.
  Rewrote part of the grammar dealing with Doxygen comments for enums.
  No changes, just remove spurious white space only differences.
  Move Doxygen comment mangling from the parser to the lexer.
  Merge "-builtin" autodoc bugs workarounds from master into test.
  Quote JAVA_HOME variable value in Java test suite makefile.
  Remove unused C_COMMENT_STRING terminal from the grammar.
  Fix missing returns in the Doxygen test suite code.
  Fix trimming whitespace from Doxygen comments.
  Remove code not doing anything from PyDocConverter.
  Remove unused <sstream> header.
  Remove unreferenced struct declaration.
  Remove unused Swig_warn() function.
  Remove any whitespace before ignored Doxygen commands.
  Remove trailing space from one of Doxygen tests.
  Fix autodoc strings generated in Python builtin case and the test.
  Fix Doxygen unit test in Python "-builtin" case.
  Use class docstrings in "-builtin" Python case.
  Don't indent Doxygen doc strings in generated Python code.
  Add a possibility to flexibly ignore custom Doxygen tags.
  Stop completely ignoring many Doxygen comments.
  Fix structural Doxygen comment recognition in the parser.
  No changes, just make checking for Doxygen structural tags more sane.
  Use "//", not "#", for comments in SWIG input.
  Allow upper case letters and digits in Doxygen words.
  Pass the node the Doxygen comment is attached to to DoxygenParser.
  Get rid of findCommand() which duplicaed commandBelongs().
  Recognize unknown Doxygen tags correctly.
  No real changes, just pass original command to commandBelongs().
  Describe Doxygen-specific %features in a single place.
  Give warnings for unknown Doxygen commands in Doxygen parser.
  Document the return type when translating Doxygen @return to Python.
  Fix translated Doxygen comments for overloaded functions in Python.
  Also merge Doxygen comments for overloaded constructors in Python.
  Allow using enum elements as default values for Python functions.
  Don't always use "*args" for all Python wrapper functions.
  No real changes, just make PYTHON::check_kwargs() const.
  Refactor: move makeParameterName() to common Language base class.
  Remove long line wrapping from Python parameter list generation code.
  Simplify and make more efficient building Python docstrings.
  Translate Doxygen code blocks to Sphinx code blocks.
  Add a simple test of multiple parameters to Doxygen test suite.
  Make Python parameters types hyperlinks in the doc strings.
  Make Language::classLookup() and enumLookup() static.
  Fix arguments of @param, @return etc translations to Python.
  Remove unused method from PyDocConverter.
  No real changes, just remove an unnecessary variable.
  Preserve relative indentation when parsing Doxygen comments.
  Use Sphinx-friendly formatting for overloaded functions documentation.
  Add poor man trailing white space detection to Doxygen Python tests.
  ...
2018-06-07 08:13:10 +01:00
William S Fulton
73c585b994 Code formatting fixes in doxygen code 2018-05-25 07:56:39 +01:00
William S Fulton
9deaa6ff24 new_node refactor
Reduce code duplication for doxygen to match code in master prior to
merge to master
2018-05-19 09:00:06 +01:00
William S Fulton
0cfd53cff7 Merge branch 'master' into vadz-doxygen
* master:
  Add Octave 4.4 to Travis allow_failures
  Fixes for appveyor image changes
  Javascript test-suite Makefile parallel jobs
  Add changes entry for csconstruct, dconstruct and javaconstruct fix
  Fix lookup of csconstruct, dconstructor and javaconstruct typemaps
  Javascript %nspace fix in generated C++ code
  Add C++17 documentation chapter
  Add changes notes for C++17 nested namespaces support
  Test for invalid C++17 nested namespace aliases
  Test c++17 nested namespaces and %nspace
  Add c++17 nested namespaces runtime test for C#
  Add c++17 nested namespaces runtime test for Python
  Add support for c++17 nested namespaces
  Update CHANGES.current
  .travis.yml: test against Octave 4.4
  Examples/test-suite/register_par.i: rename 'tree' to 'swig_tree'
  Examples/octave/module_load/runme.m: update 'exist()' statements for Octave >= 4.4
  Examples/octave/module_load/runme.m: do not use duplicate function names
  Examples/Makefile.in: unset OCTAVE_PATH when running Octave for tests
  Lib/octave: fix getting/setting global variables for Octave >= 4.4
  Lib/octave: use new class for function member dereference with Octave >= 4.4
  Lib/octave: fix operator installation for Octave >= 4.4
  Lib/Octave: in Octave >= 4.4, assign reference to base class in subclass
  Lib/octave: fix call to mlock() for Octave >= 4.4
  Lib/octave: fix call to octave::call_stack::current() for Octave >= 4.4
  Lib/octave: 'octave_exit' not longer exists in Octave >= 4.4
  Lib/octave: replace is_bool_type() with islogical() for Octave >= 4.4
  Lib/octave: replace is_numeric_type() with isnumeric() for Octave >= 4.4
  Lib/octave: replace is_cell() with iscell() for Octave >= 4.4
  Lib/octave: call octave::feval() instead of feval() for Octave >= 4.4
  Lib/octave: fix function name passed to unwind_protect::begin_frame()
  C#, D, Java methodmodifiers on destructors
  Javascript assert.h - move to header section
  Appveyor cl compiler warning fixes during configure
  Java vector wrappers cast correction
  test-suite fixes (Java directors) for compilers that don't support varargs
  Go - use director.swg like other languages
  test-suite fixes (2) for compilers that don't support varargs
  Consistent spacing in generated exception specifications
  test-suite fixes for compilers that don't support vararg macros
  Enhance Travis testing to use gcc 8 and test C++17 and C17
  Enhance SWIG_isfinite for older standards: C++03/C++98/C89
  test-suite support for gcc-8 targeting C++11 and C++14
  Scilab portability fixes - remove use of strdup
  Scilab array overbounds fix handling char type exceptions
  test-suite fix for c++17 and throw macro
  Remove use of 'register' in C source
  test-suite support for C++17: switch testing of the deprecated C++17 'register' keyword from C++ to C
  Examples update to support C++17: exception specification throw removal
  Cosmetic syntax tweak using throw in Octave directors
  test-suite support for C++17 (Java): exception specification throw removal
  test-suite support for C++17: exception specification throw removal
  __cplusplus macro usage tweak
  Improve detection of Python's 2to3 tool
  Correct C shared library creation when specifing CC to configure
  Remove superfluous parens in generated Python scripts.
  [ci] guile 2.2 build no longer expected to fail
  guile - resstructure some configure tests
  Disable guile configuration if guile-config and guile report a different version
  Fix guile executable detection on early 2.0.x guile versions
  guile - drop GDB_INTERFACE related stuff
  guile - replace obsolete scm_listify with scm_list_n
  guile - use more reliable method of finding guile executable based on guile-config
  Fix go version matching in configure for go1.10
  [Python] Suppress new pycodestyle warning
  Add if-no-present action for jsv8inc arg
  Fix typo in help --with-jscoreinc and --with-jscorelib
  Fix off-by-one error
  * Makefile.in (configfiles): Update URLs for latest configfiles.
  Add changes entry for Ruby %alias fix for global functions
  [Ruby] Pass Qnil instead of NULL to rb_funcall()
  Fix typo
  Fix ruby %alias directive for native c functions
  Stop testing Python on Appveyor msys/mingw
  Fix -Wimplicit-fallthrough gcc-7.3 warning
2018-05-17 19:57:02 +01:00
luz.paz
60dfa31a67 Misc. typos
found via `codespell` and `grep`
2018-05-17 10:04:23 -04:00
William S Fulton
348efc22ba Add support for c++17 nested namespaces
For example:
  namespace A::B { ... }
which is the equivalent to C++98 namespaces:
  namespace A { namespace B { ... } }
2018-05-14 21:02:10 +01:00
Vadim Zeitlin
b7f78dd5a7 Merge branch 'master' into doxygen 2018-03-19 21:54:46 +01:00
William S Fulton
ef378407d0 Fix parsing of default argument expressions containing ->. 2018-01-05 07:58:25 +00:00
Olly Betts
0863eca5e7 Fix constants involved parenthesised char literals
Fixes https://sourceforge.net/p/swig/bugs/1168/
2017-10-08 13:43:02 +13:00
William S Fulton
d37e41fed8 Cosmetic variable name change in parser 2017-09-29 21:16:19 +01:00
Vadim Zeitlin
db65ae5aea Merge branch 'master' into doxygen
Fix the usual conflicts in autodoc unit test due to fixing the
divergences in autodoc generation between builtin and default cases in
this branch.
2017-09-19 14:02:53 +02:00
William S Fulton
9e2a12416c Fix type promotion wrapping some non-trivial constant expressions
This was previously an integral type instead of a floating point type:
2017-09-18 07:06:27 +01:00
William S Fulton
90ba174fce Fix generated code for constant expressions containing wchar_t L literals.
Such as:
  # define __WCHAR_MAX    (0x7fffffff + L'\0')

Reported on swig-user mailing list.
2017-09-17 19:02:55 +01:00
William S Fulton
c4e280024f Add support for %typemap and member function pointers with qualifiers 2017-09-09 23:46:14 +01:00
William S Fulton
eb68e4375d Add support for member function pointers with ref-qualifiers 2017-09-05 18:44:00 +01:00
William S Fulton
7e4717320b Add error for constructors, destructors, static methods declared with qualifiers
SWIG parses a superset of valid C++ declarations. These ill-formed declarations
were previously successfully parsed but now result an error message.
2017-08-30 18:17:05 +01:00
William S Fulton
950edc1c00 Add support for conversion operators with ref-qualifiers 2017-08-30 18:17:05 +01:00
William S Fulton
0e50ef30b1 Alternate function syntax parsing improvement
This was incorrectly accepting qualifiers and exception specifications
in the wrong place.
2017-08-30 18:17:04 +01:00
William S Fulton
8227c02267 Re-organise parser grammar for initializer rules
Simply split initializer into const_cpp and initializer
2017-08-30 18:17:04 +01:00
William S Fulton
c9d425604c Re-organise parser grammar for declarator and initializer rules
Step 1 to make these closer to the definitions used in the standard.
The initializer rule should not include qualifiers and exception specification.

- split cpp_const into add qualifiers_exception_specification and empty rules
- simplify initializer to use new qualifiers_exception_specification rule
2017-08-30 18:17:04 +01:00
William S Fulton
8a40327aa8 Add unignore for rvalue ref-qualifiers
Use std::move on this pointer as the default approach to supporting
rvalue ref-qualifiers if a user really wants to wrap.

std::move requires <memory> headers so add swigfragments.swg for all
languages to use common fragments. Just header file fragments for now.
2017-08-30 18:17:04 +01:00
William S Fulton
1cf599bccb Improve ref-qualifier implementation
Internally, handle function ref-qualifiers in the function decl type string.
Needed for a whole host of things to work like %feature and %rename.
Add %feature %rename and %ignore testing for ref-qualifiers.
2017-08-30 18:17:04 +01:00
William S Fulton
eeab152901 Fix support for member const function pointer variables
Was not generating code that compiled when the variable was not
a simple member pointer, for example,
a const reference member pointer:
  short (Funcs::* const& cc7)(bool) const = cc1;
2017-08-30 18:16:59 +01:00
William S Fulton
685ee6cdc4 Use normal SWIG encodings for ref-qualifiers 2017-08-19 09:38:19 +01:00
William S Fulton
9e19fe7868 C++11 ref-qualifier support added
Fixes #1059

Methods with rvalue ref-qualifiers are ignored by default as it is not
possible to have an rvalue temporary from the target language (which is
needed to call the rvalue ref-qualified method).
A warning 405 is shown mentioning the ignored rvalue ref-qualifier method
which can be seen with the -Wextra option.

  cpp_refqualifier.i:15: Warning 405: Method with rvalue ref-qualifier ignored h() const &&.

Usually rvalue and lvalue ref-qualifier overloaded methods are written - the
lvalue method will then be wrapped.
2017-08-19 01:02:34 +01:00
William S Fulton
32a454cfef Merge branch 'templates-scope-enforcement'
* templates-scope-enforcement:
  Test a few %template errors
  Add using declarations to templates into typedef table.
  Fix type lookup in the presence of using directives and using declarations
  More docs on %template
  Testcase fix for nameclash in php
  %template scope enforcement and class definition fixes
  Template documentation tweaks
  More consistent formatting of examples in documentation
  More consistent formatting of examples in documentation
  Documentation corrections to use targetlang formatting
  More consistent formatting of examples in documentation
  More consistent formatting of examples in documentation
  More consistent formatting of examples in documentation
  Namespace documentation minor corrections
  Improve description of template_parameters_resolve
  Minor code optimisation in template_parameters_resolve
  Fix scope lookup for template parameters containing unary scope operators
  Typemap change for templates
2017-08-16 21:44:51 +01:00
William S Fulton
959e627208 %template scope enforcement and class definition fixes
The scoping rules around %template have been specified and enforced.
The %template directive for a class template is the equivalent to an
explicit instantiation of a C++ class template. The scope for a valid
%template instantiation is now the same as the scope required for a
valid explicit instantiation of a C++ template. A definition of the
template for the explicit instantiation must be in scope where the
instantiation is declared and must not be enclosed within a different
namespace.

For example, a few %template and explicit instantiations of std::vector
are shown below:

  // valid
  namespace std {
    %template(vin) vector<int>;
    template class vector<int>;
  }

  // valid
  using namespace std;
  %template(vin) vector<int>;
  template class vector<int>;

  // valid
  using std::vector;
  %template(vin) vector<int>;
  template class vector<int>;

  // ill-formed
  namespace unrelated {
    using std::vector;
    %template(vin) vector<int>;
    template class vector<int>;
  }

  // ill-formed
  namespace unrelated {
    using namespace std;
    %template(vin) vector<int>;
    template class vector<int>;
  }

  // ill-formed
  namespace unrelated {
    namespace std {
      %template(vin) vector<int>;
      template class vector<int>;
    }
  }

  // ill-formed
  namespace unrelated {
    %template(vin) std::vector<int>;
    template class std::vector<int>;
  }

When the scope is incorrect, an error now occurs such as:

cpp_template_scope.i:34: Error: 'vector' resolves to 'std::vector' and
was incorrectly instantiated in scope 'unrelated' instead of within scope 'std'.

Previously SWIG accepted the ill-formed examples above but this led to
numerous subtle template scope problems especially in the presence of
using declarations and using directives as well as with %feature and %typemap.

Actually, a valid instantiation is one which conforms to the C++03
standard as C++11 made a change to disallow using declarations and
using directives to find a template.

  // valid C++03, ill-formed C++11
  using std::vector;
  template class vector<int>;

Similar fixes for defining classes using forward class references have
also been put in place. For example:

namespace Space1 {
  struct A;
}
namespace Space2 {
  struct Space1::A {
    void x();
  }
}

will now error out with:

cpp_class_definition.i:5: Error: 'Space1::A' resolves to 'Space1::A' and
was incorrectly instantiated in scope 'Space2' instead of within scope 'Space1'.
2017-08-16 00:24:25 +01:00
Olly Betts
a92137a708 [C++11] Allow static_assert at the top level
And disallow it right after template<T>).

Fixes https://github.com/swig/swig/issues/1031 reported by Artem V L.
2017-08-04 14:09:30 +12:00
William S Fulton
b851e45e0a Fix seg fault for global declarations declared erroneously without a name 2017-03-10 23:25:31 +00:00
William S Fulton
4f235027f4 Improved member function pointer parsing
Add support for parsing member function pointers with qualifiers,
references and pointers, eg

short (Funcs::* const parm)(bool)
2017-03-10 23:25:31 +00:00
William S Fulton
0ccef1424a C++11 alternate syntax for function declarations improvements
Functions can now be declared const/noexcept/final etc
2017-03-10 23:25:31 +00:00
William S Fulton
fc638814ae Add support for unnamed parameters: member const function pointers 2017-03-10 23:25:31 +00:00
William S Fulton
5aff26fcb5 Add support for parsing and wrapping member const function pointers 2017-03-10 23:25:31 +00:00
Asen Alexandrov
a6e9c087d8 [Issue 905] Add recognition for throw (.+) final|override in parser.y
PROBLEM:
 There is a small ommission in parser.y, which will lead
 to syntax errors in cases when non-empty throw declaration is
 followed by `override`, `final` or both. E.g. in cases like:

       void finalOverriden() throw(std::exception) final override;

SOLUTION:
 - Add a `THROW LPAREN parms RPAREN virt_specifier_seq` to
   exception_specification in Source/CParse/parser.y
 - Add several methods in test-suite/cpp11_final_override.i
   to verify the fix works.
2017-02-27 10:08:23 +13:00
Vadim Zeitlin
294ab27b90 Merge branch 'master' into doxygen
Merge 3.0.12 release changes from master.
2017-02-01 02:21:35 +01:00
William S Fulton
83519138e8 Tweak %extend templates
Previous commit turned on the generation of an extra extend function
wrapper of a member template function when %template was inside a %extend
block instead of calling the real member template - reversed this side
effect.
2017-01-24 19:01:43 +00:00
William S Fulton
449aa08124 %extend changes and remove default use of javatype, dtype, cstype %typemaps
1) The %extend directive can now optionally support one of the 'class', 'struct' or 'union'.
2) The SWIG library no longer uses the javatype, dtype or cstype typemaps, thereby
   completely freeing them up for users to use without having to replicate the library
   code that they previously added

Tested by changes to test: java_lib_arrays
2017-01-15 16:50:24 +00:00