Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*************************************************************************
3 : : *
4 : : * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 : : *
6 : : * Copyright 2000, 2010 Oracle and/or its affiliates.
7 : : *
8 : : * OpenOffice.org - a multi-platform office productivity suite
9 : : *
10 : : * This file is part of OpenOffice.org.
11 : : *
12 : : * OpenOffice.org is free software: you can redistribute it and/or modify
13 : : * it under the terms of the GNU Lesser General Public License version 3
14 : : * only, as published by the Free Software Foundation.
15 : : *
16 : : * OpenOffice.org is distributed in the hope that it will be useful,
17 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : : * GNU Lesser General Public License version 3 for more details
20 : : * (a copy is included in the LICENSE file that accompanied this code).
21 : : *
22 : : * You should have received a copy of the GNU Lesser General Public License
23 : : * version 3 along with OpenOffice.org. If not, see
24 : : * <http://www.openoffice.org/license.html>
25 : : * for a copy of the LGPLv3 License.
26 : : *
27 : : ************************************************************************/
28 : :
29 : :
30 : : #include "XMLFootnoteConfigurationImportContext.hxx"
31 : :
32 : : #include <rtl/ustring.hxx>
33 : : #include <rtl/ustrbuf.hxx>
34 : :
35 : : #include <sax/tools/converter.hxx>
36 : :
37 : : #include <xmloff/nmspmap.hxx>
38 : : #include "xmloff/xmlnmspe.hxx"
39 : : #include <xmloff/xmltoken.hxx>
40 : :
41 : : #include <xmloff/families.hxx>
42 : : #include <xmloff/xmluconv.hxx>
43 : : #include <xmloff/xmlimp.hxx>
44 : : #include <xmloff/xmlnumi.hxx>
45 : : #include <com/sun/star/xml/sax/XAttributeList.hpp>
46 : : #include <com/sun/star/beans/XPropertySet.hpp>
47 : : #include <com/sun/star/text/XFootnote.hpp>
48 : : #include <com/sun/star/text/XFootnotesSupplier.hpp>
49 : : #include <com/sun/star/text/XEndnotesSupplier.hpp>
50 : : #include <com/sun/star/text/FootnoteNumbering.hpp>
51 : : #include <com/sun/star/style/NumberingType.hpp>
52 : :
53 : :
54 : : using ::rtl::OUString;
55 : : using ::rtl::OUStringBuffer;
56 : :
57 : : using namespace ::com::sun::star::text;
58 : : using namespace ::com::sun::star::beans;
59 : : using namespace ::com::sun::star::uno;
60 : : using namespace ::com::sun::star::style;
61 : : using namespace ::com::sun::star::xml::sax;
62 : : using namespace ::xmloff::token;
63 : :
64 : : //
65 : : // XMLFootnoteConfigHelper
66 : : //
67 : :
68 : : /// local helper class for import of quo-vadis and ergo-sum elements
69 [ # # ]: 0 : class XMLFootnoteConfigHelper : public SvXMLImportContext
70 : : {
71 : : OUStringBuffer sBuffer;
72 : : XMLFootnoteConfigurationImportContext& rConfig;
73 : : sal_Bool bIsBegin;
74 : :
75 : : public:
76 : : TYPEINFO();
77 : :
78 : : XMLFootnoteConfigHelper(
79 : : SvXMLImport& rImport,
80 : : sal_uInt16 nPrfx,
81 : : const OUString& rLName,
82 : : XMLFootnoteConfigurationImportContext& rConfigImport,
83 : : sal_Bool bBegin);
84 : :
85 : : virtual void EndElement();
86 : :
87 : : virtual void Characters( const OUString& rChars );
88 : : };
89 : :
90 [ # # ][ # # ]: 0 : TYPEINIT1( XMLFootnoteConfigHelper, SvXMLImportContext );
91 : :
92 : 0 : XMLFootnoteConfigHelper::XMLFootnoteConfigHelper(
93 : : SvXMLImport& rImport,
94 : : sal_uInt16 nPrfx,
95 : : const OUString& rLName,
96 : : XMLFootnoteConfigurationImportContext& rConfigImport,
97 : : sal_Bool bBegin)
98 : : : SvXMLImportContext(rImport, nPrfx, rLName)
99 : : , sBuffer()
100 : : , rConfig(rConfigImport)
101 : 0 : , bIsBegin(bBegin)
102 : : {
103 : 0 : }
104 : :
105 : 0 : void XMLFootnoteConfigHelper::EndElement()
106 : : {
107 [ # # ]: 0 : if (bIsBegin)
108 : : {
109 : 0 : rConfig.SetBeginNotice(sBuffer.makeStringAndClear());
110 : : }
111 : : else
112 : : {
113 : 0 : rConfig.SetEndNotice(sBuffer.makeStringAndClear());
114 : : }
115 : : // rConfig = NULL; // import contexts are ref-counted
116 : 0 : }
117 : :
118 : 0 : void XMLFootnoteConfigHelper::Characters( const OUString& rChars )
119 : : {
120 : 0 : sBuffer.append(rChars);
121 : 0 : }
122 : :
123 : :
124 : : //
125 : : // XMLFootnoteConfigurationImportContext
126 : : //
127 : :
128 : :
129 [ # # ][ # # ]: 0 : TYPEINIT1( XMLFootnoteConfigurationImportContext, SvXMLStyleContext );
130 : :
131 : 126 : XMLFootnoteConfigurationImportContext::XMLFootnoteConfigurationImportContext(
132 : : SvXMLImport& rImport,
133 : : sal_uInt16 nPrfx,
134 : : const OUString& rLocalName,
135 : : const Reference<XAttributeList> & xAttrList)
136 : : : SvXMLStyleContext(rImport, nPrfx, rLocalName, xAttrList, XML_STYLE_FAMILY_TEXT_FOOTNOTECONFIG)
137 : : , sPropertyAnchorCharStyleName(RTL_CONSTASCII_USTRINGPARAM("AnchorCharStyleName"))
138 : : , sPropertyCharStyleName(RTL_CONSTASCII_USTRINGPARAM("CharStyleName"))
139 : : , sPropertyNumberingType(RTL_CONSTASCII_USTRINGPARAM("NumberingType"))
140 : : , sPropertyPageStyleName(RTL_CONSTASCII_USTRINGPARAM("PageStyleName"))
141 : : , sPropertyParagraphStyleName(RTL_CONSTASCII_USTRINGPARAM("ParaStyleName"))
142 : : , sPropertyPrefix(RTL_CONSTASCII_USTRINGPARAM("Prefix"))
143 : : , sPropertyStartAt(RTL_CONSTASCII_USTRINGPARAM("StartAt"))
144 : : , sPropertySuffix(RTL_CONSTASCII_USTRINGPARAM("Suffix"))
145 : : , sPropertyPositionEndOfDoc(RTL_CONSTASCII_USTRINGPARAM("PositionEndOfDoc"))
146 : : , sPropertyFootnoteCounting(RTL_CONSTASCII_USTRINGPARAM("FootnoteCounting"))
147 : : , sPropertyEndNotice(RTL_CONSTASCII_USTRINGPARAM("EndNotice"))
148 : : , sPropertyBeginNotice(RTL_CONSTASCII_USTRINGPARAM("BeginNotice"))
149 : : , sNumFormat(RTL_CONSTASCII_USTRINGPARAM("1"))
150 : : , sNumSync(RTL_CONSTASCII_USTRINGPARAM("false"))
151 : : , pAttrTokenMap(NULL)
152 : : , nOffset(0)
153 : : , nNumbering(FootnoteNumbering::PER_PAGE)
154 : : , bPosition(sal_False)
155 [ + - ][ + - ]: 126 : , bIsEndnote(sal_False)
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
156 : : {
157 [ + - ][ + - ]: 126 : sal_Int16 nLength = xAttrList->getLength();
158 [ + - ]: 270 : for(sal_Int16 nAttr = 0; nAttr < nLength; nAttr++)
159 : : {
160 : 144 : OUString sLocalName;
161 : 144 : sal_uInt16 nPrefix = GetImport().GetNamespaceMap().
162 [ + - ]: 144 : GetKeyByAttrName( xAttrList->getNameByIndex(nAttr),
163 [ + - + - ]: 288 : &sLocalName );
164 [ + + ][ + + ]: 284 : if( XML_NAMESPACE_TEXT == nPrefix && IsXMLToken( sLocalName,
[ + + ]
165 [ + - ]: 140 : XML_NOTE_CLASS ) )
166 : : {
167 [ + - ][ + - ]: 126 : const OUString& rValue = xAttrList->getValueByIndex( nAttr );
168 [ + - ][ + + ]: 126 : if( IsXMLToken( rValue, XML_ENDNOTE ) )
169 : : {
170 : 63 : bIsEndnote = sal_True;
171 : 63 : SetFamily( XML_STYLE_FAMILY_TEXT_FOOTNOTECONFIG );
172 : : }
173 : 144 : break;
174 : : }
175 [ + + ]: 144 : }
176 : :
177 : 126 : }
178 : 126 : XMLFootnoteConfigurationImportContext::~XMLFootnoteConfigurationImportContext()
179 : : {
180 [ + - ][ + - ]: 126 : delete pAttrTokenMap;
181 [ - + ]: 252 : }
182 : :
183 : : enum XMLFtnConfigToken
184 : : {
185 : : XML_TOK_FTNCONFIG_CITATION_STYLENAME,
186 : : XML_TOK_FTNCONFIG_ANCHOR_STYLENAME,
187 : : XML_TOK_FTNCONFIG_DEFAULT_STYLENAME,
188 : : XML_TOK_FTNCONFIG_PAGE_STYLENAME,
189 : : XML_TOK_FTNCONFIG_OFFSET,
190 : : XML_TOK_FTNCONFIG_NUM_PREFIX,
191 : : XML_TOK_FTNCONFIG_NUM_SUFFIX,
192 : : XML_TOK_FTNCONFIG_NUM_FORMAT,
193 : : XML_TOK_FTNCONFIG_NUM_SYNC,
194 : : XML_TOK_FTNCONFIG_START_AT,
195 : : XML_TOK_FTNCONFIG_POSITION
196 : : };
197 : :
198 : : static SvXMLTokenMapEntry aTextFieldAttrTokenMap[] =
199 : : {
200 : : { XML_NAMESPACE_TEXT, XML_CITATION_STYLE_NAME, XML_TOK_FTNCONFIG_CITATION_STYLENAME },
201 : : { XML_NAMESPACE_TEXT, XML_CITATION_BODY_STYLE_NAME, XML_TOK_FTNCONFIG_ANCHOR_STYLENAME },
202 : : { XML_NAMESPACE_TEXT, XML_DEFAULT_STYLE_NAME, XML_TOK_FTNCONFIG_DEFAULT_STYLENAME },
203 : : { XML_NAMESPACE_TEXT, XML_MASTER_PAGE_NAME, XML_TOK_FTNCONFIG_PAGE_STYLENAME },
204 : : { XML_NAMESPACE_TEXT, XML_START_VALUE, XML_TOK_FTNCONFIG_OFFSET },
205 : : { XML_NAMESPACE_STYLE, XML_NUM_PREFIX, XML_TOK_FTNCONFIG_NUM_PREFIX },
206 : : { XML_NAMESPACE_STYLE, XML_NUM_SUFFIX, XML_TOK_FTNCONFIG_NUM_SUFFIX },
207 : : { XML_NAMESPACE_STYLE, XML_NUM_FORMAT, XML_TOK_FTNCONFIG_NUM_FORMAT },
208 : : { XML_NAMESPACE_STYLE, XML_NUM_LETTER_SYNC, XML_TOK_FTNCONFIG_NUM_SYNC },
209 : : { XML_NAMESPACE_TEXT, XML_START_NUMBERING_AT, XML_TOK_FTNCONFIG_START_AT},
210 : : { XML_NAMESPACE_TEXT, XML_FOOTNOTES_POSITION, XML_TOK_FTNCONFIG_POSITION},
211 : :
212 : : // for backwards compatibility with SRC630 & earlier
213 : : { XML_NAMESPACE_TEXT, XML_NUM_PREFIX, XML_TOK_FTNCONFIG_NUM_PREFIX },
214 : : { XML_NAMESPACE_TEXT, XML_NUM_SUFFIX, XML_TOK_FTNCONFIG_NUM_SUFFIX },
215 : : { XML_NAMESPACE_TEXT, XML_OFFSET, XML_TOK_FTNCONFIG_OFFSET },
216 : : XML_TOKEN_MAP_END
217 : : };
218 : :
219 : : const SvXMLTokenMap&
220 : 510 : XMLFootnoteConfigurationImportContext::GetFtnConfigAttrTokenMap()
221 : : {
222 [ + + ]: 510 : if (NULL == pAttrTokenMap)
223 : : {
224 [ + - ]: 126 : pAttrTokenMap = new SvXMLTokenMap(aTextFieldAttrTokenMap);
225 : : }
226 : :
227 : 510 : return *pAttrTokenMap;
228 : : }
229 : :
230 : : static SvXMLEnumMapEntry const aFootnoteNumberingMap[] =
231 : : {
232 : : { XML_PAGE, FootnoteNumbering::PER_PAGE },
233 : : { XML_CHAPTER, FootnoteNumbering::PER_CHAPTER },
234 : : { XML_DOCUMENT, FootnoteNumbering::PER_DOCUMENT },
235 : : { XML_TOKEN_INVALID, 0 },
236 : : };
237 : :
238 : 126 : void XMLFootnoteConfigurationImportContext::StartElement(
239 : : const Reference<XAttributeList> & xAttrList )
240 : : {
241 : 126 : sal_Int16 nLength = xAttrList->getLength();
242 [ + + ]: 636 : for(sal_Int16 nAttr = 0; nAttr < nLength; nAttr++)
243 : : {
244 : 510 : OUString sLocalName;
245 : 510 : sal_uInt16 nPrefix = GetImport().GetNamespaceMap().
246 [ + - ]: 510 : GetKeyByAttrName( xAttrList->getNameByIndex(nAttr),
247 [ + - + - ]: 1020 : &sLocalName );
248 [ + - ][ + - ]: 510 : OUString sValue = xAttrList->getValueByIndex(nAttr);
249 [ + - ][ + - ]: 510 : switch (GetFtnConfigAttrTokenMap().Get(nPrefix, sLocalName))
[ + + + +
+ - + + -
+ + + ]
250 : : {
251 : : case XML_TOK_FTNCONFIG_CITATION_STYLENAME:
252 : 10 : sCitationStyle = sValue;
253 : 10 : break;
254 : : case XML_TOK_FTNCONFIG_ANCHOR_STYLENAME:
255 : 6 : sAnchorStyle = sValue;
256 : 6 : break;
257 : : case XML_TOK_FTNCONFIG_DEFAULT_STYLENAME:
258 : 3 : sDefaultStyle = sValue;
259 : 3 : break;
260 : : case XML_TOK_FTNCONFIG_PAGE_STYLENAME:
261 : 8 : sPageStyle = sValue;
262 : 8 : break;
263 : : case XML_TOK_FTNCONFIG_OFFSET:
264 : : {
265 : : sal_Int32 nTmp;
266 [ + - ][ + - ]: 118 : if (::sax::Converter::convertNumber(nTmp, sValue))
267 : : {
268 : 118 : nOffset = (sal_uInt16)nTmp;
269 : : }
270 : : break;
271 : : }
272 : : case XML_TOK_FTNCONFIG_NUM_PREFIX:
273 : 0 : sPrefix = sValue;
274 : 0 : break;
275 : : case XML_TOK_FTNCONFIG_NUM_SUFFIX:
276 : 3 : sSuffix = sValue;
277 : 3 : break;
278 : : case XML_TOK_FTNCONFIG_NUM_FORMAT:
279 : 118 : sNumFormat = sValue;
280 : 118 : break;
281 : : case XML_TOK_FTNCONFIG_NUM_SYNC:
282 : 0 : sNumSync = sValue;
283 : 0 : break;
284 : : case XML_TOK_FTNCONFIG_START_AT:
285 : : {
286 : : sal_uInt16 nTmp;
287 [ + - ]: 59 : if (SvXMLUnitConverter::convertEnum(nTmp, sValue,
288 [ + - ]: 59 : aFootnoteNumberingMap))
289 : : {
290 : 59 : nNumbering = nTmp;
291 : : }
292 : : break;
293 : : }
294 : : case XML_TOK_FTNCONFIG_POSITION:
295 [ + - ]: 59 : bPosition = IsXMLToken( sValue, XML_DOCUMENT );
296 : 59 : break;
297 : : default:
298 : : ; // ignore
299 : : }
300 : 510 : }
301 : 126 : }
302 : :
303 : 0 : SvXMLImportContext *XMLFootnoteConfigurationImportContext::CreateChildContext(
304 : : sal_uInt16 nPrefix,
305 : : const OUString& rLocalName,
306 : : const Reference<XAttributeList> & xAttrList )
307 : : {
308 : 0 : SvXMLImportContext* pContext = NULL;
309 : :
310 [ # # ]: 0 : if (!bIsEndnote)
311 : : {
312 [ # # ]: 0 : if (XML_NAMESPACE_TEXT == nPrefix)
313 : : {
314 [ # # ]: 0 : if ( IsXMLToken( rLocalName,
315 : 0 : XML_FOOTNOTE_CONTINUATION_NOTICE_FORWARD ) )
316 : : {
317 : 0 : pContext = new XMLFootnoteConfigHelper(GetImport(),
318 : : nPrefix, rLocalName,
319 [ # # ]: 0 : *this, sal_False);
320 : : }
321 [ # # ]: 0 : else if ( IsXMLToken( rLocalName,
322 : 0 : XML_FOOTNOTE_CONTINUATION_NOTICE_BACKWARD ) )
323 : : {
324 : 0 : pContext = new XMLFootnoteConfigHelper(GetImport(),
325 : : nPrefix, rLocalName,
326 [ # # ]: 0 : *this, sal_True);
327 : : }
328 : : // else: default context
329 : : }
330 : : // else: unknown namespace -> default context
331 : : }
332 : : // else: endnote -> default context
333 : :
334 [ # # ]: 0 : if (pContext == NULL)
335 : : {
336 : : // default: delegate to super class
337 : : pContext = SvXMLStyleContext::CreateChildContext(nPrefix,
338 : : rLocalName,
339 : 0 : xAttrList);
340 : : }
341 : :
342 : 0 : return pContext;
343 : : }
344 : :
345 : : // Rename method <CreateAndInsertLate(..)> to <Finish(..)> (#i40597#)
346 : 126 : void XMLFootnoteConfigurationImportContext::Finish( sal_Bool bOverwrite )
347 : : {
348 : :
349 [ + - ]: 126 : if (bOverwrite)
350 : : {
351 [ + + ]: 126 : if (bIsEndnote)
352 : : {
353 : : Reference<XEndnotesSupplier> xSupplier(
354 [ + - ]: 63 : GetImport().GetModel(), UNO_QUERY);
355 [ + - ]: 63 : if (xSupplier.is())
356 : : {
357 [ + - ][ + - ]: 63 : ProcessSettings(xSupplier->getEndnoteSettings());
[ + - ]
358 : 63 : }
359 : : }
360 : : else
361 : : {
362 : : Reference<XFootnotesSupplier> xSupplier(
363 [ + - ]: 63 : GetImport().GetModel(), UNO_QUERY);
364 [ + - ]: 63 : if (xSupplier.is())
365 : : {
366 [ + - ][ + - ]: 63 : ProcessSettings(xSupplier->getFootnoteSettings());
[ + - ]
367 : 63 : }
368 : : }
369 : : }
370 : : // else: ignore (there's only one configuration, so we can only overwrite)
371 : 126 : }
372 : :
373 : 126 : void XMLFootnoteConfigurationImportContext::ProcessSettings(
374 : : const Reference<XPropertySet> & rConfig)
375 : : {
376 : 126 : Any aAny;
377 : :
378 [ + + ]: 126 : if (!sCitationStyle.isEmpty())
379 : : {
380 : 10 : aAny <<= GetImport().GetStyleDisplayName(
381 [ + - ][ + - ]: 10 : XML_STYLE_FAMILY_TEXT_TEXT, sCitationStyle );
382 [ + - ][ + - ]: 10 : rConfig->setPropertyValue(sPropertyCharStyleName, aAny);
383 : : }
384 : :
385 [ + + ]: 126 : if (!sAnchorStyle.isEmpty())
386 : : {
387 : 6 : aAny <<= GetImport().GetStyleDisplayName(
388 [ + - ][ + - ]: 6 : XML_STYLE_FAMILY_TEXT_TEXT, sAnchorStyle );
389 [ + - ][ + - ]: 6 : rConfig->setPropertyValue(sPropertyAnchorCharStyleName, aAny);
390 : : }
391 : :
392 [ + + ]: 126 : if (!sPageStyle.isEmpty())
393 : : {
394 : 8 : aAny <<= GetImport().GetStyleDisplayName(
395 [ + - ][ + - ]: 8 : XML_STYLE_FAMILY_MASTER_PAGE, sPageStyle );
396 [ + - ][ + - ]: 8 : rConfig->setPropertyValue(sPropertyPageStyleName, aAny);
397 : : }
398 : :
399 [ + + ]: 126 : if (!sDefaultStyle.isEmpty())
400 : : {
401 : 3 : aAny <<= GetImport().GetStyleDisplayName(
402 [ + - ][ + - ]: 3 : XML_STYLE_FAMILY_TEXT_PARAGRAPH, sDefaultStyle );
403 [ + - ][ + - ]: 3 : rConfig->setPropertyValue(sPropertyParagraphStyleName, aAny);
404 : : }
405 : :
406 [ + - ]: 126 : aAny <<= sPrefix;
407 [ + - ][ + - ]: 126 : rConfig->setPropertyValue(sPropertyPrefix, aAny);
408 : :
409 [ + - ]: 126 : aAny <<= sSuffix;
410 [ + - ][ + - ]: 126 : rConfig->setPropertyValue(sPropertySuffix, aAny);
411 : :
412 : 126 : sal_Int16 nNumType = NumberingType::ARABIC;
413 : 126 : GetImport().GetMM100UnitConverter().convertNumFormat( nNumType, sNumFormat,
414 [ + - ]: 126 : sNumSync );
415 : : // #i61399: Corrupt file? It contains "Bullet" as numbering style for footnotes.
416 : : // Okay, even it seems to be corrupt, we will oversee this and set the style to ARABIC
417 [ - + ]: 126 : if( NumberingType::CHAR_SPECIAL == nNumType )
418 : 0 : nNumType = NumberingType::ARABIC;
419 : :
420 [ + - ]: 126 : aAny <<= nNumType;
421 [ + - ][ + - ]: 126 : rConfig->setPropertyValue(sPropertyNumberingType, aAny);
422 : :
423 [ + - ]: 126 : aAny <<= nOffset;
424 [ + - ][ + - ]: 126 : rConfig->setPropertyValue(sPropertyStartAt, aAny);
425 : :
426 [ + + ]: 126 : if (!bIsEndnote)
427 : : {
428 [ + - ]: 63 : aAny.setValue(&bPosition, ::getBooleanCppuType());
429 [ + - ][ + - ]: 63 : rConfig->setPropertyValue(sPropertyPositionEndOfDoc, aAny);
430 : :
431 [ + - ]: 63 : aAny <<= nNumbering;
432 [ + - ][ + - ]: 63 : rConfig->setPropertyValue(sPropertyFootnoteCounting, aAny);
433 : :
434 [ + - ]: 63 : aAny <<= sEndNotice;
435 [ + - ][ + - ]: 63 : rConfig->setPropertyValue(sPropertyEndNotice, aAny);
436 : :
437 [ + - ]: 63 : aAny <<= sBeginNotice;
438 [ + - ][ + - ]: 63 : rConfig->setPropertyValue(sPropertyBeginNotice, aAny);
439 : 126 : }
440 : 126 : }
441 : :
442 : 0 : void XMLFootnoteConfigurationImportContext::SetBeginNotice(
443 : : OUString sText)
444 : : {
445 : 0 : sBeginNotice = sText;
446 : 0 : }
447 : :
448 : 0 : void XMLFootnoteConfigurationImportContext::SetEndNotice(
449 : : OUString sText)
450 : : {
451 : 0 : sEndNotice = sText;
452 : 0 : }
453 : :
454 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|