Arcane  v3.14.10.0
Documentation développeur
Chargement...
Recherche...
Aucune correspondance
XmlNode.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2023 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* XmlNode.cc (C) 2000-2023 */
9/* */
10/* Noeud quelconque d'un arbre DOM. */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/core/XmlNode.h"
15
16#include "arcane/utils/StringBuilder.h"
17#include "arcane/utils/NotImplementedException.h"
18#include "arcane/utils/Iterator.h"
19#include "arcane/utils/ValueConvert.h"
20#include "arcane/utils/Iostream.h"
21#include "arcane/utils/TraceInfo.h"
22#include "arcane/utils/FatalErrorException.h"
23
24#include "arcane/core/XmlException.h"
25#include "arcane/core/XmlNodeList.h"
26#include "arcane/core/XmlNodeIterator.h"
27#include "arcane/core/DomUtils.h"
28
29#include <algorithm>
30
31/*---------------------------------------------------------------------------*/
32/*---------------------------------------------------------------------------*/
33
34namespace Arcane
35{
36
37/*---------------------------------------------------------------------------*/
38/*---------------------------------------------------------------------------*/
39
41{
42 public:
43 XmlNodeSameName(const String& name) : m_name(name) {}
44 public:
45 bool operator()(const XmlNode& node)
46 { return node.name()==m_name; }
47 private:
48 String m_name;
49};
50
51/*---------------------------------------------------------------------------*/
52/*---------------------------------------------------------------------------*/
53
54XmlNode XmlNode::
55_nullNode() const
56{
57 return XmlNode(m_rm);
58}
59
60/*---------------------------------------------------------------------------*/
61/*---------------------------------------------------------------------------*/
62
64child(const String& child_name) const
65{
66 if (m_node._null())
67 return _nullNode();
69 XmlNodeConstIterator i = ARCANE_STD::find_if(begin(),end(),same_name);
70 if (i!=end())
71 return *i;
72 return XmlNode(m_rm);
73}
74
75/*---------------------------------------------------------------------------*/
76/*---------------------------------------------------------------------------*/
77
80{
81 if (m_node._null())
82 return _nullNode();
84 if (c.null())
85 ARCANE_FATAL("Can not find a child named '{0}' for node '{1}'",child_name,xpathFullName());
86 return c;
87}
88
89/*---------------------------------------------------------------------------*/
90/*---------------------------------------------------------------------------*/
91
93children(const String& child_name) const
94{
95 XmlNodeList nodes;
96 if (m_node._null())
97 return nodes;
99 for( XmlNodeConstIterator n = begin(); n!=end(); ++n )
100 if (same_name(*n))
101 nodes.add(*n);
102 return nodes;
103}
104
105/*---------------------------------------------------------------------------*/
106/*---------------------------------------------------------------------------*/
107
109children() const
110{
111 XmlNodeList nodes;
112 if (m_node._null())
113 return nodes;
114 for( XmlNodeConstIterator n = begin(); n!=end(); ++n )
115 nodes.add(*n);
116 return nodes;
117}
118
119/*---------------------------------------------------------------------------*/
120/*---------------------------------------------------------------------------*/
121
123type() const
124{
125 return static_cast<eType>(m_node.nodeType());
126}
127
128/*---------------------------------------------------------------------------*/
129/*---------------------------------------------------------------------------*/
130
132name() const
133{
134 if (m_node._null())
135 return String();
136 return String(m_node.nodeName());
137}
138
139/*---------------------------------------------------------------------------*/
140/*---------------------------------------------------------------------------*/
141
143xpathFullName() const
144{
146 if (m_node._null())
147 return full_name;
148
149 XmlNode p = parent();
150 if (!p.null()){
151 full_name = p.xpathFullName();
152 full_name.append("/");
153
154 if (m_node.nodeType()==dom::Node::ATTRIBUTE_NODE){
155 full_name += "@";
156 full_name += name();
157 }
158 else if (m_node.nodeType()==dom::Node::ELEMENT_NODE){
159 full_name.append(name());
160 Integer nb_occurence = 1;
161 for( XmlNode i=p.front(); i!=(*this); ++i )
162 if (i.isNamed(name()))
163 ++nb_occurence;
164 if (nb_occurence>1){
165 full_name += "[";
167 full_name += "]";
168 }
169 }
170 else
171 full_name += "?";
172 }
173 else{
174 if (m_node.nodeType()==dom::Node::ATTRIBUTE_NODE){
176 full_name += "/@";
177 full_name += name();
178 }
179 else{
180 full_name = String("/");
181 }
182 }
183 return full_name;
184}
185
186/*---------------------------------------------------------------------------*/
187/*---------------------------------------------------------------------------*/
188
190isNamed(const String& name) const
191{
192 return m_node.nodeName()==name;
193}
194
195/*---------------------------------------------------------------------------*/
196/*---------------------------------------------------------------------------*/
197
199value() const
200{
201 if (null())
202 return String();
203 return _value();
204}
205
206/*---------------------------------------------------------------------------*/
207/*---------------------------------------------------------------------------*/
208
210setValue(const String& v)
211{
212 if (m_node._null())
213 return;
214 if (m_node.nodeType()==dom::Node::ELEMENT_NODE){
215 domutils::textContent(m_node,v);
216 return;
217 }
218 m_node.nodeValue(v);
219}
220
221/*---------------------------------------------------------------------------*/
222/*---------------------------------------------------------------------------*/
223
225attrValue(const String& name,bool throw_exception) const
226{
227 String s = domutils::attrValue(m_node,name);
228 if (s.null() && throw_exception){
229 ARCANE_THROW(XmlException,"No attribute named '{0}' child of '{1}'",
231 }
232 return s;
233}
234
235/*---------------------------------------------------------------------------*/
236/*---------------------------------------------------------------------------*/
237
239setAttrValue(const String& name,const String& value)
240{
241 domutils::setAttr(m_node,name,value);
242}
243
244/*---------------------------------------------------------------------------*/
245/*---------------------------------------------------------------------------*/
246
248attr(const String& name,bool throw_exception) const
249{
250 dom::Element elem(m_node);
251 if (elem._null())
252 return _nullNode();
253
254 XmlNode attr_node(m_rm,elem.getAttributeNode(name));
255 if (throw_exception && attr_node.null()){
256 ARCANE_THROW(XmlException,"No attribute named '{0}' child of '{1}'",
258 }
259 return attr_node;
260}
261
262/*---------------------------------------------------------------------------*/
263/*---------------------------------------------------------------------------*/
264
266forceAttr(const String& name)
267{
268 dom::Element elem(m_node);
269 if (elem._null())
270 return _nullNode();
271 dom::Attr attr = elem.getAttributeNode(name);
272 if (attr._null()){
273 attr = elem.ownerDocument().createAttribute(name);
274 attr = elem.setAttributeNode(attr);
275 }
276 return XmlNode(m_rm,attr);
277}
278
279/*---------------------------------------------------------------------------*/
280/*---------------------------------------------------------------------------*/
281
283removeAttr(const String& name) const
284{
285 dom::Element elem(m_node);
286 elem.removeAttribute(name);
287}
288
289/*---------------------------------------------------------------------------*/
290/*---------------------------------------------------------------------------*/
291
293clear()
294{
295 // Supprime les noeuds fils.
296 XmlNode n = front();
297 while (!n.null()){
298 remove(n);
299 n = front();
300 }
301}
302
303/*---------------------------------------------------------------------------*/
304/*---------------------------------------------------------------------------*/
305
307remove()
308{
309 if (m_node._null())
310 return;
311 dom::Node parent = m_node.parentNode();
312 if (parent._null())
313 return;
314 parent.removeChild(m_node);
315}
316
317/*---------------------------------------------------------------------------*/
318/*---------------------------------------------------------------------------*/
319
322{
323 dom::Node child = m_node.removeChild(child_node.domNode());
324 child.releaseNode();
325}
326
327/*---------------------------------------------------------------------------*/
328/*---------------------------------------------------------------------------*/
329
331nextWithName(const String& name) const
332{
333 //\todo a tester
334 if (m_node._null())
335 return _nullNode();
336 XmlNode n(next());
337 while(!n.null() && !n.isNamed(name))
338 ++n;
339 return n;
340}
341
342/*---------------------------------------------------------------------------*/
343/*---------------------------------------------------------------------------*/
344
346prevWithName(const String& name) const
347{
348 //\todo a tester
349 if (m_node._null())
350 return _nullNode();
351 XmlNode n(prev());
352 while(!n.null() && !n.isNamed(name))
353 --n;
354 return n;
355}
356
357/*---------------------------------------------------------------------------*/
358/*---------------------------------------------------------------------------*/
359
361nextSameType() const
362{
363 //\todo a tester
364 if (m_node._null())
365 return _nullNode();
366 XmlNode n(next());
367 while(!n.null() && n.type()!=type())
368 ++n;
369 return n;
370}
371
372/*---------------------------------------------------------------------------*/
373/*---------------------------------------------------------------------------*/
374
376prevSameType() const
377{
378 //\todo a tester
379 if (m_node._null())
380 return _nullNode();
381 XmlNode n(prev());
382 while(!n.null() && n.type()!=type())
383 --n;
384 return n;
385}
386
387/*---------------------------------------------------------------------------*/
388/*---------------------------------------------------------------------------*/
389
392{
393 if (m_node._null() || new_node.null() || ref_node.null())
394 return;
395 m_node.replaceChild(new_node.domNode(),ref_node.domNode());
396}
397
398/*---------------------------------------------------------------------------*/
399/*---------------------------------------------------------------------------*/
400
401void XmlNode::
402_throwBadConvert(const char* type_name,const String& value) const
403{
404 ARCANE_THROW(XmlException,"XML Node '{0}' can not convert value '{1}' to type '{2}'",
406}
407
408/*---------------------------------------------------------------------------*/
409/*---------------------------------------------------------------------------*/
410
412valueAsBoolean(bool throw_exception) const
413{
414 if (null())
415 return false;
416 String value = _value();
417 if (value=="false" || value=="0")
418 return false;
419 if (value=="true" || value=="1")
420 return true;
421 if (throw_exception)
422 ARCANE_THROW(XmlException,"XML Node '{0}' can not convert value '{1}' to type 'bool'."
423 " Valid values are 'true', 'false', '0' (zero) ou '1'.",
425 return false;
426}
427
428/*---------------------------------------------------------------------------*/
429/*---------------------------------------------------------------------------*/
430
432valueAsInteger(bool throw_exception) const
433{
434 if (null())
435 return 0;
436 String value = _value();
437 Integer v = 0;
438 if (builtInGetValue(v,value))
439 if (throw_exception)
440 _throwBadConvert("Integer",value);
441 return v;
442}
443
444/*---------------------------------------------------------------------------*/
445/*---------------------------------------------------------------------------*/
446
448valueAsInt64(bool throw_exception) const
449{
450 if (null())
451 return 0;
452 String value = _value();
453 Int64 v = 0;
454 if (builtInGetValue(v,value))
455 if (throw_exception)
456 _throwBadConvert("Int64",value);
457 return v;
458}
459
460/*---------------------------------------------------------------------------*/
461/*---------------------------------------------------------------------------*/
462
464valueAsReal(bool throw_exception) const
465{
466 if (null())
467 return 0.0;
468 String value = _value();
469 Real v = 0.0;
470 if (builtInGetValue(v,value))
471 if (throw_exception)
472 _throwBadConvert("Real",value);
473 return v;
474}
475
476/*---------------------------------------------------------------------------*/
477/*---------------------------------------------------------------------------*/
478
481 const String& attr_value) const
482{
483 String name;
484 if (null())
485 return _nullNode();
486 for( XmlNode::const_iter i(*this); i(); ++i ){
487 if (!i->isNamed(elem_name))
488 continue;
489 name = i->attrValue(attr_name);
490 if (name==attr_value)
491 return *i;
492 }
493 return XmlNode(m_rm);
494}
495
496/*---------------------------------------------------------------------------*/
497/*---------------------------------------------------------------------------*/
498
504
505/*---------------------------------------------------------------------------*/
506/*---------------------------------------------------------------------------*/
507
509xpathNode(const String& xpath_expr) const
510{
511 return XmlNode(m_rm,domutils::nodeFromXPath(m_node,xpath_expr));
512}
513
514/*---------------------------------------------------------------------------*/
515/*---------------------------------------------------------------------------*/
516
517String XmlNode::
518_value() const
519{
520 if (m_node.nodeType()==dom::Node::ELEMENT_NODE)
521 return domutils::textContent(m_node);
522 return String(m_node.nodeValue());
523}
524
525/*---------------------------------------------------------------------------*/
526/*---------------------------------------------------------------------------*/
527
529assignDomNode(const dom::Node& node)
530{
531 m_node = node;
532}
533
534/*---------------------------------------------------------------------------*/
535/*---------------------------------------------------------------------------*/
536
539{
540 if (new_child.null())
541 return _nullNode();
543 if (!next.null())
544 ++next;
545 if (next.null())
547 else
548 m_node.insertBefore(new_child.domNode(),next.domNode());
549 return new_child;
550}
551
552/*---------------------------------------------------------------------------*/
553/*---------------------------------------------------------------------------*/
554
556documentElement() const
557{
558 dom::Document doc(m_node);
559 if (doc._null())
560 return _nullNode();
561 return _build(doc.documentElement());
562}
563
564/*---------------------------------------------------------------------------*/
565/*---------------------------------------------------------------------------*/
566
568ownerElement() const
569{
570 dom::Attr attr(m_node);
571 if (attr._null())
572 return _nullNode();
573 return _build(attr.ownerElement());
574}
575
576/*---------------------------------------------------------------------------*/
577/*---------------------------------------------------------------------------*/
578
579XmlNode XmlNode::
580createElement(const String& name)
581{
582 return createNode(ELEMENT,name);
583}
584
585/*---------------------------------------------------------------------------*/
586/*---------------------------------------------------------------------------*/
587
588XmlNode XmlNode::
589createAndAppendElement(const String& name,const String& value)
590{
591 XmlNode n = createNode(ELEMENT,name,value);
592 append(n);
593 return n;
594}
595
596/*---------------------------------------------------------------------------*/
597/*---------------------------------------------------------------------------*/
598
599XmlNode XmlNode::
600createAndAppendElement(const String& name)
601{
602 XmlNode n = createNode(ELEMENT,name);
603 append(n);
604 return n;
605}
606
607/*---------------------------------------------------------------------------*/
608/*---------------------------------------------------------------------------*/
609
612{
613 dom::Document doc(m_node);
614 if (doc._null())
615 doc = m_node.ownerDocument();
616 XmlNode ret_node(m_rm);
618 switch(type){
619 case ELEMENT:
620 ret_node.assignDomNode(doc.createElement(nov));
621 break;
622 case TEXT:
623 ret_node.assignDomNode(doc.createTextNode(nov));
624 break;
625 default:
627 "createNode() not implemented for node type {0}",(int)type);
628 }
629 return ret_node;
630}
631
632/*---------------------------------------------------------------------------*/
633/*---------------------------------------------------------------------------*/
634
636createText(const String& value)
637{
638 return createNode(TEXT,value);
639}
640
641/*---------------------------------------------------------------------------*/
642/*---------------------------------------------------------------------------*/
643
645createNode(eType type,const String& name,const String& value)
646{
647 dom::Document doc(m_node);
648 if (doc._null())
649 doc = m_node.ownerDocument();
650 XmlNode ret_node(m_rm);
651 switch(type){
652 case ELEMENT:
653 ret_node.assignDomNode(doc.createElement(name));
654 ret_node.setValue(value);
655 break;
656 case TEXT:
657 ret_node.assignDomNode(doc.createTextNode(value));
658 break;
659 default:
661 "createNode() not implemented for node type {0}",(int)type);
662 }
663 return ret_node;
664}
665
666/*---------------------------------------------------------------------------*/
667/*---------------------------------------------------------------------------*/
668
669XmlNode XmlNode::
670_build(const dom::Node& node) const
671{
672 return XmlNode(m_rm,node);
673}
674
675/*---------------------------------------------------------------------------*/
676/*---------------------------------------------------------------------------*/
677
678/*---------------------------------------------------------------------------*/
679/*---------------------------------------------------------------------------*/
680
682XmlElement(XmlNode& parent,const String& name,const String& value)
683: XmlNode(parent)
684{
685 _setNode(parent.createAndAppendElement(name,value).domNode());
686}
687
688/*---------------------------------------------------------------------------*/
689/*---------------------------------------------------------------------------*/
690
692XmlElement(XmlNode& parent,const String& name)
693: XmlNode(parent)
694{
695 _setNode(parent.createAndAppendElement(name).domNode());
696}
697
698/*---------------------------------------------------------------------------*/
699/*---------------------------------------------------------------------------*/
700
701/*---------------------------------------------------------------------------*/
702/*---------------------------------------------------------------------------*/
703
704XmlNodeNameIterator::
705XmlNodeNameIterator(const XmlNode& from,const String& ref_name)
706: m_parent (from)
707, m_current (0)
708, m_ref_name (ref_name)
709{
710 _findNextValid(true);
711}
712
713/*---------------------------------------------------------------------------*/
714/*---------------------------------------------------------------------------*/
715
716XmlNodeNameIterator::
717XmlNodeNameIterator(const XmlNode& from,const char* ref_name)
718: m_parent (from)
719, m_current (0)
720, m_ref_name (String(ref_name))
721{
722 _findNextValid(true);
723}
724
725/*---------------------------------------------------------------------------*/
726/*---------------------------------------------------------------------------*/
727
728void XmlNodeNameIterator::
729_findNextValid(bool is_init)
730{
731 if (is_init)
732 m_current = m_parent.front();
733 else{
734 if (m_current.null())
735 return;
736 ++m_current;
737 }
738 while (!m_current.null()){
739 if (m_current.isNamed(m_ref_name))
740 break;
741 ++m_current;
742 }
743}
744
745/*---------------------------------------------------------------------------*/
746/*---------------------------------------------------------------------------*/
747
748}
749
750/*---------------------------------------------------------------------------*/
751/*---------------------------------------------------------------------------*/
752
#define ARCANE_THROW(exception_class,...)
Macro pour envoyer une exception avec formattage.
#define ARCANE_FATAL(...)
Macro envoyant une exception FatalErrorException.
Lecteur des fichiers de maillage via la bibliothèque LIMA.
Definition Lima.cc:120
XmlElement(XmlNode &parent, const String &name, const String &value)
Créé un élément fils de parent. L'élément créé a pour nom name et pour valeur value....
Definition XmlNode.cc:682
Exception liées aux fichiers XML.
Liste de noeuds d'un arbre DOM.
Definition XmlNodeList.h:33
Noeud d'un arbre DOM.
Definition XmlNode.h:51
XmlNode attr(const String &name, bool throw_exception=false) const
Retourne l'attribut de nom name.
Definition XmlNode.cc:248
XmlNode prevSameType() const
Retourne le noeud précédent ce noeud ayant le même type.
Definition XmlNode.cc:376
String xpathFullName() const
Nom XPath du noeud avec ces ancêtres.
Definition XmlNode.cc:143
void setValue(const String &value)
Positionne la valeur du noeud.
Definition XmlNode.cc:210
void assignDomNode(const dom::Node &node)
Definition XmlNode.cc:529
XmlNode documentElement() const
Retourne le noeud élément du document.
Definition XmlNode.cc:556
void replace(const XmlNode &new_node, XmlNode &ref_node)
Remplace le noeud fils ref_node par le noeud new_node.
Definition XmlNode.cc:391
Int64 valueAsInt64(bool throw_exception=false) const
Valeur du noeud convertie en entier 64 bits. 0 si conversion échoue.
Definition XmlNode.cc:448
eType type() const
Type du noeud.
Definition XmlNode.cc:123
iterator end()
Retourne un iterateur sur le premier élément après la fin du tableau.
XmlNode insertAfter(const XmlNode &new_child, const XmlNode &ref_node)
Insère un noeud. Insère le noeud new_child après le noeud ref_node. Si new_child est nul,...
Definition XmlNode.cc:538
XmlNodeList children() const
Ensemble des noeuds fils de ce noeud.
Definition XmlNode.cc:109
void removeAttr(const String &name) const
Supprime l'attribut de nom name de ce noeud. Si ce noeud n'est pas élément, rien n'est effectué.
Definition XmlNode.cc:283
XmlNode prev() const
Noeud précédent (previousSibling())
Definition XmlNode.h:281
String attrValue(const String &name, bool throw_exception=false) const
Valeur de l'attribut name.
Definition XmlNode.cc:225
Real valueAsReal(bool throw_exception=false) const
Valeur du noeud convertie en réel. Si la conversion échoue, si throw_exception vaut false retourne 0....
Definition XmlNode.cc:464
XmlNode childWithNameAttr(const String &elem_name, const String &attr_value) const
Retourne le fils de ce noeud ayant pour nom elem_name et un attribut de nom "name" avec pour valeur a...
Definition XmlNode.cc:500
XmlNode child(const String &name) const
Noeud fils de celui-ci de nom name.
Definition XmlNode.cc:64
void clear()
Supprime tous les noeuds fils.
Definition XmlNode.cc:293
XmlNode forceAttr(const String &name)
Retourne l'attribut de nom name. Si aucun attribut avec ce nom n'existe, un attribut avec comme valeu...
Definition XmlNode.cc:266
XmlNode xpathNode(const String &xpath_expr) const
Retourne un noeud à partir d'une expression XPath.
Definition XmlNode.cc:509
void append(const XmlNode &child_node)
Ajoute child_node comme fils de ce noeud.
Definition XmlNode.h:267
String value() const
Valeur du noeud.
Definition XmlNode.cc:199
XmlNode front() const
Premier fils.
Definition XmlNode.h:275
XmlNode createText(const String &value)
Créé un noeud texte.
Definition XmlNode.cc:636
bool null() const
Vrai si le noeud est nul.
Definition XmlNode.h:294
XmlNode next() const
Noeud suivant (nextSibling())
Definition XmlNode.h:279
eType
NodeType An integer indicating which type of node this is.
Definition XmlNode.h:85
@ TEXT
The node is a Text node.
Definition XmlNode.h:91
@ ELEMENT
The node is an Element.
Definition XmlNode.h:87
bool isNamed(const String &name) const
Vrai si le nom de l'élément est name.
Definition XmlNode.cc:190
XmlNode ownerElement() const
Retourne l'élément propriétaire de cet attribut.
Definition XmlNode.cc:568
XmlNode expectedChild(const String &name) const
Noeud fils de celui-ci de nom name.
Definition XmlNode.cc:79
XmlNode nextWithName(const String &name) const
Retourne le noeud suivant ce noeud ayant le nom name.
Definition XmlNode.cc:331
Integer valueAsInteger(bool throw_exception=false) const
Valeur du noeud convertie en entier.
Definition XmlNode.cc:432
XmlNode parent() const
Parent de ce noeud (null si aucun)
Definition XmlNode.h:261
iterator begin()
Retourne un iterateur sur le premier élément du tableau.
XmlNode nextSameType() const
Retourne le noeud suivant ce noeud ayant le même type.
Definition XmlNode.cc:361
void setAttrValue(const String &name, const String &value)
Positionne l'attribut name à la valeur value.
Definition XmlNode.cc:239
XmlNode childWithAttr(const String &elem_name, const String &attr_name, const String &attr_value) const
Retourne le fils de ce noeud ayant pour nom elem_name et un attribut de nom attr_name avec pour valeu...
Definition XmlNode.cc:480
String name() const
Nom du noeud.
Definition XmlNode.cc:132
bool valueAsBoolean(bool throw_exception=false) const
Valeur du noeud convertie en booléan.
Definition XmlNode.cc:412
XmlNode createNode(eType type, const String &name, const String &value)
Créé un noeud d'un type donné.
Definition XmlNode.cc:645
XmlNode prevWithName(const String &name) const
Retourne le noeud précédent ce noeud ayant le nom name.
Definition XmlNode.cc:346
dom::Node domNode() const
Definition XmlNode.h:298
void remove()
Supprime ce noeud du document.
Definition XmlNode.cc:307
void add(ConstReferenceType val)
Ajoute l'élément val à la fin du tableau.
Exception lorsqu'une fonction n'est pas implémentée.
Constructeur de chaîne de caractère unicode.
Chaîne de caractères unicode.
bool null() const
Retourne true si la chaîne est nulle.
Definition String.cc:304
-*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-