1/*
2Open Asset Import Library (assimp)
3----------------------------------------------------------------------
4
5Copyright (c) 2006-2017, assimp team
6
7All rights reserved.
8
9Redistribution and use of this software in source and binary forms,
10with or without modification, are permitted provided that the
11following conditions are met:
12
13* Redistributions of source code must retain the above
14 copyright notice, this list of conditions and the
15 following disclaimer.
16
17* Redistributions in binary form must reproduce the above
18 copyright notice, this list of conditions and the
19 following disclaimer in the documentation and/or other
20 materials provided with the distribution.
21
22* Neither the name of the assimp team, nor the names of its
23 contributors may be used to endorse or promote products
24 derived from this software without specific prior
25 written permission of the assimp team.
26
27THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
39----------------------------------------------------------------------
40*/
41
42/** @file ObjTools.h
43 * @brief Some helpful templates for text parsing
44 */
45#ifndef OBJ_TOOLS_H_INC
46#define OBJ_TOOLS_H_INC
47
48#include "fast_atof.h"
49#include "ParsingUtils.h"
50#include <vector>
51
52namespace Assimp {
53
54/** @brief Returns true, if the last entry of the buffer is reached.
55 * @param it Iterator of current position.
56 * @param end Iterator with end of buffer.
57 * @return true, if the end of the buffer is reached.
58 */
59template<class char_t>
60inline bool isEndOfBuffer( char_t it, char_t end ) {
61 if ( it == end )
62 {
63 return true;
64 }
65 else
66 {
67 --end;
68 }
69 return ( it == end );
70}
71
72/** @brief Returns next word separated by a space
73 * @param pBuffer Pointer to data buffer
74 * @param pEnd Pointer to end of buffer
75 * @return Pointer to next space
76 */
77template<class Char_T>
78inline Char_T getNextWord( Char_T pBuffer, Char_T pEnd )
79{
80 while ( !isEndOfBuffer( pBuffer, pEnd ) )
81 {
82 if ( !IsSpaceOrNewLine( *pBuffer ) || IsLineEnd( *pBuffer ) ) {
83 //if ( *pBuffer != '\\' )
84 break;
85 }
86 pBuffer++;
87 }
88 return pBuffer;
89}
90
91/** @brief Returns pointer a next token
92 * @param pBuffer Pointer to data buffer
93 * @param pEnd Pointer to end of buffer
94 * @return Pointer to next token
95 */
96template<class Char_T>
97inline Char_T getNextToken( Char_T pBuffer, Char_T pEnd )
98{
99 while ( !isEndOfBuffer( pBuffer, pEnd ) )
100 {
101 if( IsSpaceOrNewLine( *pBuffer ) )
102 break;
103 pBuffer++;
104 }
105 return getNextWord( pBuffer, pEnd );
106}
107
108/** @brief Skips a line
109 * @param it Iterator set to current position
110 * @param end Iterator set to end of scratch buffer for readout
111 * @param uiLine Current line number in format
112 * @return Current-iterator with new position
113 */
114template<class char_t>
115inline char_t skipLine( char_t it, char_t end, unsigned int &uiLine ) {
116 while( !isEndOfBuffer( it, end ) && !IsLineEnd( *it ) ) {
117 ++it;
118 }
119
120 if ( it != end ) {
121 ++it;
122 ++uiLine;
123 }
124 // fix .. from time to time there are spaces at the beginning of a material line
125 while ( it != end && ( *it == '\t' || *it == ' ' ) ) {
126 ++it;
127 }
128
129 return it;
130}
131
132/** @brief Get a name from the current line. Preserve space in the middle,
133 * but trim it at the end.
134 * @param it set to current position
135 * @param end set to end of scratch buffer for readout
136 * @param name Separated name
137 * @return Current-iterator with new position
138 */
139template<class char_t>
140inline char_t getName( char_t it, char_t end, std::string &name )
141{
142 name = "";
143 if( isEndOfBuffer( it, end ) ) {
144 return end;
145 }
146
147 char *pStart = &( *it );
148 while( !isEndOfBuffer( it, end ) && !IsLineEnd( *it )) {
149 ++it;
150 }
151
152 while(IsSpace( *it ) ) {
153 --it;
154 }
155 // Get name
156 // if there is no name, and the previous char is a separator, come back to start
157 while (&(*it) < pStart) {
158 ++it;
159 }
160 std::string strName( pStart, &(*it) );
161 if ( strName.empty() )
162 return it;
163 else
164 name = strName;
165
166 return it;
167}
168
169/** @brief Get a name from the current line. Do not preserve space
170 * in the middle, but trim it at the end.
171 * @param it set to current position
172 * @param end set to end of scratch buffer for readout
173 * @param name Separated name
174 * @return Current-iterator with new position
175 */
176template<class char_t>
177inline char_t getNameNoSpace( char_t it, char_t end, std::string &name )
178{
179 name = "";
180 if( isEndOfBuffer( it, end ) ) {
181 return end;
182 }
183
184 char *pStart = &( *it );
185 while( !isEndOfBuffer( it, end ) && !IsLineEnd( *it )
186 && !IsSpaceOrNewLine( *it ) ) {
187 ++it;
188 }
189
190 while( isEndOfBuffer( it, end ) || IsLineEnd( *it )
191 || IsSpaceOrNewLine( *it ) ) {
192 --it;
193 }
194 ++it;
195
196 // Get name
197 // if there is no name, and the previous char is a separator, come back to start
198 while (&(*it) < pStart) {
199 ++it;
200 }
201 std::string strName( pStart, &(*it) );
202 if ( strName.empty() )
203 return it;
204 else
205 name = strName;
206
207 return it;
208}
209
210/** @brief Get next word from given line
211 * @param it set to current position
212 * @param end set to end of scratch buffer for readout
213 * @param pBuffer Buffer for next word
214 * @param length Buffer length
215 * @return Current-iterator with new position
216 */
217template<class char_t>
218inline char_t CopyNextWord( char_t it, char_t end, char *pBuffer, size_t length )
219{
220 size_t index = 0;
221 it = getNextWord<char_t>( it, end );
222 while( !IsSpaceOrNewLine( *it ) && !isEndOfBuffer( it, end ) )
223 {
224 pBuffer[index] = *it ;
225 index++;
226 if (index == length-1)
227 break;
228 ++it;
229 }
230 pBuffer[ index ] = '\0';
231 return it;
232}
233
234/** @brief Get next float from given line
235 * @param it set to current position
236 * @param end set to end of scratch buffer for readout
237 * @param value Separated float value.
238 * @return Current-iterator with new position
239 */
240template<class char_t>
241inline char_t getFloat( char_t it, char_t end, ai_real &value )
242{
243 static const size_t BUFFERSIZE = 1024;
244 char buffer[ BUFFERSIZE ];
245 it = CopyNextWord<char_t>( it, end, buffer, BUFFERSIZE );
246 value = (ai_real) fast_atof( buffer );
247
248 return it;
249}
250
251/** @brief Will perform a simple tokenize.
252 * @param str String to tokenize.
253 * @param tokens Array with tokens, will be empty if no token was found.
254 * @param delimiters Delimiter for tokenize.
255 * @return Number of found token.
256 */
257template<class string_type>
258unsigned int tokenize( const string_type& str, std::vector<string_type>& tokens,
259 const string_type& delimiters )
260{
261 // Skip delimiters at beginning.
262 typename string_type::size_type lastPos = str.find_first_not_of( delimiters, 0 );
263
264 // Find first "non-delimiter".
265 typename string_type::size_type pos = str.find_first_of( delimiters, lastPos );
266 while ( string_type::npos != pos || string_type::npos != lastPos )
267 {
268 // Found a token, add it to the vector.
269 string_type tmp = str.substr(lastPos, pos - lastPos);
270 if ( !tmp.empty() && ' ' != tmp[ 0 ] )
271 tokens.push_back( tmp );
272
273 // Skip delimiters. Note the "not_of"
274 lastPos = str.find_first_not_of( delimiters, pos );
275
276 // Find next "non-delimiter"
277 pos = str.find_first_of( delimiters, lastPos );
278 }
279
280 return static_cast<unsigned int>( tokens.size() );
281}
282
283template <class string_type>
284string_type trim_whitespaces(string_type str)
285{
286 while (!str.empty() && IsSpace(str[0])) str.erase(0);
287 while (!str.empty() && IsSpace(str[str.length() - 1])) str.erase(str.length() - 1);
288 return str;
289}
290
291template<class T>
292bool hasLineEnd( T it, T end ) {
293 bool hasLineEnd( false );
294 while ( !isEndOfBuffer( it, end ) ) {
295 it++;
296 if ( IsLineEnd( it ) ) {
297 hasLineEnd = true;
298 break;
299 }
300 }
301
302 return hasLineEnd;
303}
304
305} // Namespace Assimp
306
307#endif // OBJ_TOOLS_H_INC
308