1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9 #ifndef INCLUDED_OOX_MATHML_IMPORTUTILS_HXX
10 #define INCLUDED_OOX_MATHML_IMPORTUTILS_HXX
11 
12 #include <map>
13 #include <vector>
14 
15 #include <com/sun/star/uno/Reference.hxx>
16 #include <oox/dllapi.h>
17 #include <oox/token/tokens.hxx>
18 #include <rtl/ustring.hxx>
19 #include <sal/types.h>
20 
21 namespace com { namespace sun { namespace star {
22     namespace xml { namespace sax { class XFastAttributeList; } }
23 } } }
24 
25 namespace oox
26 {
27 
28 namespace formulaimport
29 {
30 
31 // used to differentiate between tags that opening or closing
32 const int TAG_OPENING = 1 << 29;
33 const int TAG_CLOSING = 1 << 30;
34 
35 // you probably want to #define these to something shorter in the .cxx file,
36 // but they must be done as macros, otherwise they wouldn't be usable for case values,
37 // and macros cannot be namespaced
38 #define XML_STREAM_OPENING( token ) ( TAG_OPENING | token )
39 #define XML_STREAM_CLOSING( token ) ( TAG_CLOSING | token )
40 
41 /**
42  Class for storing a stream of xml tokens.
43 
44  A part of an XML file can be parsed and stored in this stream, from which it can be read
45  as if parsed linearly. The purpose of this class is to allow simpler handling of XML
46  files, unlike the usual LO way of using callbacks, context handlers and similar needlessly
47  complicated stuff (YMMV).
48 
49  The advantages of this approach is easy to read and debug code (as it is just functions
50  reading tokens one by one and calling other functions, compared to having to use callbacks
51  and temporary storage). The disadvantage is that the XML structure needs to be handled
52  manually by the code.
53 
54  Note that tag identifiers are simply int values and the API does not care besides matching
55  their values to XML stream contents and requiring that the values are not as high as TAG_OPENING.
56  Be prepared for the fact that some of the functions may throw exceptions if the input
57  stream does not match the required token (TBD).
58 
59  The API tries to make the common idioms as simple as possible, see the following examples.
60 
61  Parse <tagone attr="value"><tagtwo>text</tagtwo></tagone> , where tagtwo is optional:
62  @code
63 XmlStream::Tag tagoneTag = stream.ensureOpeningTag( tagone );
64 if( attributeTag.hasAttribute( attr ))
65     ... = attributeTag.attribute( attr, defaultValueOfTheRightType );
66 if( XmlStream::Tag tagtwoTag = stream.checkOpeningTag( tagtwo ))
67 {
68     ... = tagtwoTag.text;
69     stream.ensureClosingTag( tagtwo );
70 }
71 stream.ensureClosingTag( tagone );
72  @endcode
73 
74  Parse an element that may contain several sub-elements of different types in random order:
75  @code
76 stream.ensureOpeningTag( element );
77 while( !stream.atEnd() && stream.currentToken() != CLOSING( element ))
78     {
79     switch( stream.currentToken())
80     {
81         case OPENING( subelement1 ):
82             handleSubElement1();
83             break;
84         case OPENING( subelement2 ):
85             ... process subelement2;
86             break;
87         default:
88             stream.handleUnexpectedTag();
89             break;
90     }
91 stream.ensureClosingTag( element );
92  @endcode
93 
94  If there may not be a zero number of sub-elements, use a helper bool variable or use a do-while loop.
95 
96  Parse an element that may contain an unknown number of sub-elements of the same type:
97  @code
98 stream.ensureOpeningTag( element );
99 while( !stream.atEnd() && stream.findTag( OPENING( subelement )))
100     {
101     handleSubelement();
102     }
103 stream.ensureClosingTag( element );
104  @endcode
105 
106  If there may not be a zero number of sub-elements, use a helper bool variable or use a do-while loop.
107 
108  @since 3.5
109 */
110 class OOX_DLLPUBLIC XmlStream
111 {
112 public:
113     XmlStream();
114     /**
115      Structure representing a list of attributes.
116     */
117     // One could theoretically use oox::AttributeList, but that complains if the passed reference is empty,
118     // which would be complicated to avoid here. Also, parsers apparently reuse the same instance of XFastAttributeList,
119     // which means using oox::AttributeList would make them all point to the one instance.
120     struct OOX_DLLPUBLIC AttributeList
121     {
122         OUString& operator[] (int token);
123         OUString attribute( int token, const OUString& def) const;
124         bool attribute( int token, bool def ) const;
125         sal_Unicode attribute( int token, sal_Unicode def ) const;
126         // when adding more attribute() overloads, add also to XmlStream itself
127     protected:
128         std::map< int, OUString > attrs;
129     };
130     /**
131      Structure representing a tag, including its attributes and content text immediately following it.
132     */
133     struct OOX_DLLPUBLIC Tag
134     {
135         Tag( int token = XML_TOKEN_INVALID,
136             const css::uno::Reference< css::xml::sax::XFastAttributeList >& attributes = css::uno::Reference< css::xml::sax::XFastAttributeList >());
137         Tag( int token,
138             const AttributeList& attribs);
139         int token; ///< tag type, or XML_TOKEN_INVALID
140         AttributeList attributes;
141         OUString text;
142         /**
143          This function returns value of the given attribute, or the passed default value if not found.
144          The type of the default value selects the return type (OUString here).
145         */
146         OUString attribute( int token, const OUString& def = OUString()) const;
147         /**
148          @overload
149         */
150         bool attribute( int token, bool def ) const;
151         /**
152          @overload
153         */
154         sal_Unicode attribute( int token, sal_Unicode def ) const;
155         // when adding more attribute() overloads, add also to XmlStream::AttributeList and inline below
156         /**
157          Converts to true if the tag has a valid token, false otherwise. Allows simple
158          usage in if(), for example 'if( XmlStream::Tag foo = stream.checkOpeningTag( footoken ))'.
159         */
160         operator bool() const;
161     };
162     /**
163      @return true if current position is at the end of the XML stream
164     */
165     bool atEnd() const;
166     /**
167      @return data about the current tag
168     */
169     Tag currentTag() const;
170     /**
171      @return the token for the current tag
172     */
173     int currentToken() const;
174     /**
175      Moves position to the next tag.
176     */
177     void moveToNextTag();
178     /**
179      Ensures that an opening tag with the given token is read. If the current tag does not match,
180      writes out a warning and tries to recover by skipping tags until found (or until the current element would end).
181      If found, the position in the stream is afterwards moved to the next tag.
182      @return the matching found opening tag, or empty tag if not found
183     */
184     Tag ensureOpeningTag( int token );
185     /**
186      Tries to find an opening tag with the given token. Works similarly like ensureOpeningTag(),
187      but if a matching tag is not found, the position in the stream is not altered. The primary
188      use of this function is to check for optional elements.
189      @return the matching found opening tag, or empty tag if not found
190     */
191     Tag checkOpeningTag( int token );
192     /**
193      Ensures that a closing tag with the given token is read. Like ensureOpeningTag(),
194      if not, writes out a warning and tries to recover by skipping tags until found (or until the current element would end).
195      If found, the position in the stream is afterwards moved to the next tag.
196     */
197     void ensureClosingTag( int token );
198     /**
199      Tries to find the given token, until either found (returns true) or end of current element.
200      Position in the stream is set to make the tag current (i.e. it will be the next one read).
201     */
202     bool findTag( int token );
203     /**
204      Handle the current (unexpected) tag.
205     */
206     void handleUnexpectedTag();
207 protected:
208     Tag checkTag( int token, bool optional );
209     bool findTagInternal( int token, bool silent );
210     void skipElementInternal( int token, bool silent );
211     std::vector< Tag > tags;
212     unsigned int pos;
213 };
214 
215 /**
216  This class is used for creating XmlStream.
217 
218  Simply use this class and then pass it as XmlStream to the consumer.
219 
220  @since 3.5.0
221 */
222 class OOX_DLLPUBLIC XmlStreamBuilder
223 : public XmlStream
224 {
225 public:
226     void appendOpeningTag( int token,
227         const css::uno::Reference< css::xml::sax::XFastAttributeList >& attributes = css::uno::Reference< css::xml::sax::XFastAttributeList >());
228     void appendOpeningTag( int token,
229         const AttributeList& attribs );
230     void appendClosingTag( int token );
231     // appends the characters after the last appended token
232     void appendCharacters( const OUString& characters );
233 };
234 
235 inline
attribute(int t,const OUString & def) const236 OUString XmlStream::Tag::attribute( int t, const OUString& def ) const
237 {
238     return attributes.attribute( t, def );
239 }
240 
241 inline
attribute(int t,bool def) const242 bool XmlStream::Tag::attribute( int t, bool def ) const
243 {
244     return attributes.attribute( t, def );
245 }
246 
247 inline
attribute(int t,sal_Unicode def) const248 sal_Unicode XmlStream::Tag::attribute( int t, sal_Unicode def ) const
249 {
250     return attributes.attribute( t, def );
251 }
252 
253 } // namespace
254 } // namespace
255 
256 #endif
257 
258 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
259