1 /*
2 Copyright (c) 2008-2009 NetAllied Systems GmbH
3 
4 This file is part of COLLADASaxFrameworkLoader.
5 
6 Licensed under the MIT Open Source License,
7 for details please see LICENSE file or the website
8 http://www.opensource.org/licenses/mit-license.php
9 */
10 
11 #include "COLLADASaxFWLStableHeaders.h"
12 #include "COLLADASaxFWLSidAddress.h"
13 #include "COLLADABUPcreCompiledPattern.h"
14 
15 #include "pcre.h"
16 
17 
18 namespace COLLADASaxFWL
19 {
20 
21 	const int regExpMatchesVectorLength = 30;    /* should be a multiple of 3 */
22 	const char* sidSeparator = "/";
23 
24 	//------------------------------
SidAddress()25 	SidAddress::SidAddress( )
26 		: mMemberSelection(MEMBER_SELECTION_NONE)
27 		, mFirstIndex(0)
28 		, mSecondIndex(0)
29 		, mIsValid(false)
30 	{
31 	}
32 
33 	//------------------------------
SidAddress(const String & sidAddress)34 	SidAddress::SidAddress( const String& sidAddress )
35 		: mMemberSelection(MEMBER_SELECTION_NONE)
36 		, mFirstIndex(0)
37 		, mSecondIndex(0)
38 		, mIsValid(false)
39 	{
40 		parseAddress( sidAddress );
41 	}
42 
43 	//------------------------------
SidAddress(const COLLADABU::URI & id,const String & sid)44 	SidAddress::SidAddress( const COLLADABU::URI& id, const String& sid )
45 		: mId(id.getFragment())
46 		, mMemberSelection(MEMBER_SELECTION_NONE)
47 		, mFirstIndex(0)
48 		, mSecondIndex(0)
49 		, mIsValid( !id.getFragment().empty() && !sid.empty() )
50 	{
51 		mSids.push_back( sid );
52 	}
53 
54 	//------------------------------
SidAddress(const COLLADABU::URI & id)55 	SidAddress::SidAddress( const COLLADABU::URI& id )
56 		: mId(id.getFragment())
57 		, mMemberSelection(MEMBER_SELECTION_NONE)
58 		, mFirstIndex(0)
59 		, mSecondIndex(0)
60 		, mIsValid( !id.getFragment().empty() )
61 	{
62 	}
63 
64 	//------------------------------
~SidAddress()65 	SidAddress::~SidAddress()
66 	{
67 	}
68 
69 	//------------------------------
parseAddress(const String & sidAddress)70 	void SidAddress::parseAddress( const String& sidAddress )
71 	{
72 		/* An sid path looks as follows
73 		<id>/sid1/sid2/sid3<accessor>
74 		where,
75 		<id> is either a valid id or a "." for relative paths
76 		<accessor> is either "." followed by a name eg "ANGLE"
77 		or a one dimensional array access of the form (<number>)
78 		or a two dimensional array access of the form (<number>)(<number>)
79 		or empty
80 		The number of sids is arbitrary an can be zero
81 
82 		*/
83 
84 		// Find the last sid separator "/"
85 		// we use this to split the address in to parts "<id>/sid1/sid2/"  and  "sid3<accessor>"
86 		size_t lastSidSeparator = sidAddress.find_last_of(sidSeparator);
87 
88 		bool hasId = false;
89 		if ( lastSidSeparator != String::npos )
90 		{
91 			size_t nextTokenIndex = 0;
92 			size_t startPos = 0;
93 			while (nextTokenIndex != lastSidSeparator)
94 			{
95 				nextTokenIndex = sidAddress.find_first_of(sidSeparator, startPos);
96 				if ( hasId )
97 				{
98 					mSids.push_back(String(sidAddress, startPos, nextTokenIndex- startPos));
99 				}
100 				else
101 				{
102 					if ( sidAddress[startPos] != '.' )
103 						mId.assign(sidAddress, startPos, nextTokenIndex- startPos);
104 					hasId = true;
105 				}
106 				startPos = nextTokenIndex + 1;
107 			}
108 		}
109 
110 		const char * secondPart = sidAddress.c_str() + lastSidSeparator + 1;
111 		int secondPartLength = (int)sidAddress.length() - (int)lastSidSeparator - 1;
112 
113 
114 		// regular expression: "(.+)\.(.+)"
115 		static const COLLADABU::PcreCompiledPattern accessorNameRegexCompiledPattern("(.+)\\.(.+)");
116 		pcre* accessorNameRegex = accessorNameRegexCompiledPattern.getCompiledPattern();
117 
118 		int accessorNameMatches[regExpMatchesVectorLength];
119 
120 
121 		int  accessorNameResult = pcre_exec(
122 											accessorNameRegex,			/* the compiled pattern */
123 											0,							/* no extra data - we didn't study the pattern */
124 											secondPart,					/* the subject string */
125 											secondPartLength,			/* the length of the subject */
126 											0,							/* start at offset 0 in the subject */
127 											0,							/* default options */
128 											accessorNameMatches,		/* output vector for substring information */
129 											regExpMatchesVectorLength);	/* number of elements in the output vector */
130 
131 
132 		if ( accessorNameResult >= 0 )
133 		{
134 			// first try the name accessor
135 			// this matches only, if the name accessor is present. Therefor there are exactly two matches
136 			int idOrSidStart = accessorNameMatches[2*1];
137 			int idOrSidEnd = accessorNameMatches[2*1+1];
138 			COLLADABU_ASSERT( idOrSidStart >= 0 );
139 			if ( idOrSidStart >= 0 )
140 			{
141 				if ( hasId )
142 				{
143 					mSids.push_back(String( secondPart + idOrSidStart, idOrSidEnd - idOrSidStart));
144 				}
145 				else
146 				{
147 					if ( secondPart[idOrSidStart] != '.' )
148 						mId.assign(secondPart + idOrSidStart, idOrSidEnd - idOrSidStart);
149 					hasId = true;
150 				}
151 			}
152 
153 			int& nameStart = accessorNameMatches[2*2];
154 			int& nameEnd = accessorNameMatches[2*2+1];
155 			COLLADABU_ASSERT(nameStart>=0);
156 			if ( nameStart>=0 )
157 			{
158 				mMemberSelectionName.assign(secondPart + nameStart, nameEnd - nameStart);
159 			}
160 
161 			mMemberSelection = MEMBER_SELECTION_NAME;
162 
163 			mIsValid = true;
164 		}
165 		else
166 		{
167 			// regular expression: "([^(]+)(?:\(([0-9]+)\))?(?:\(([0-9]+)\))?"
168 			static const COLLADABU::PcreCompiledPattern accessorIndexRegexCompiledPattern("([^(]+)(?:\\(([0-9]+)\\))?(?:\\(([0-9]+)\\))?");
169 			pcre* accessorIndexRegex = accessorIndexRegexCompiledPattern.getCompiledPattern();
170 
171 			int accessorIndexMatches[regExpMatchesVectorLength];
172 
173 			int  accessorIndexResult = pcre_exec(
174 				accessorIndexRegex,			/* the compiled pattern */
175 				0,							/* no extra data - we didn't study the pattern */
176 				secondPart,					/* the subject string */
177 				secondPartLength,			/* the length of the subject */
178 				0,							/* start at offset 0 in the subject */
179 				0,							/* default options */
180 				accessorIndexMatches,		/* output vector for substring information */
181 				regExpMatchesVectorLength);
182 
183 			if ( accessorIndexResult >= 0 )
184 			{
185 				//check all other cases
186 				// the first match is id or sid
187 				int& idOrSidStart = accessorIndexMatches[2*1];
188 				int& idOrSidEnd = accessorIndexMatches[2*1+1];
189 				COLLADABU_ASSERT( idOrSidStart >= 0 );
190 
191 				if ( idOrSidStart >= 0 )
192 				{
193 					if ( hasId )
194 					{
195 						mSids.push_back(String( secondPart + idOrSidStart, idOrSidEnd - idOrSidStart));
196 					}
197 					else
198 					{
199 						if ( secondPart[idOrSidStart] != '.' )
200 							mId.assign(secondPart + idOrSidStart, idOrSidEnd - idOrSidStart);
201 						hasId = true;
202 					}
203 				}
204 				mMemberSelection = MEMBER_SELECTION_NONE;
205 
206 				// this one matches only if two indices are specified. In case of one index, only the next matches
207 				int& firstIndexStart = accessorIndexMatches[2*2];
208 				int& firstIndexEnd = accessorIndexMatches[2*2+1];
209 				if ( firstIndexStart >= 0)
210 				{
211 					mMemberSelection = MEMBER_SELECTION_ONE_INDEX;
212 					bool failed = false;
213 					const char* bufferBegin = secondPart + firstIndexStart;
214 					mFirstIndex = (size_t)GeneratedSaxParser::Utils::toUint32(&bufferBegin, secondPart + firstIndexEnd, failed);
215 					if ( failed )
216 					{
217 						mIsValid = false;
218 						return;
219 					}
220 				}
221 
222 				// this one matches if two indices or only index are specified.
223 				int& secondIndexStart = accessorIndexMatches[2*3];
224 				int& secondIndexEnd = accessorIndexMatches[2*3+1];
225 				if ( secondIndexStart >= 0)
226 				{
227 					bool failed = false;
228 					const char* bufferBegin = secondPart + secondIndexStart;
229 					size_t index = (size_t)GeneratedSaxParser::Utils::toUint32(&bufferBegin, secondPart + secondIndexEnd, failed);
230 
231 					mMemberSelection = MEMBER_SELECTION_TWO_INDICES;
232 					mSecondIndex = index;
233 					if ( failed )
234 					{
235 						mIsValid = false;
236 						return;
237 					}
238 				}
239 
240 				mIsValid = true;
241 			}
242 			else
243 			{
244 				mIsValid = false;
245 			}
246 		}
247 
248 	}
249 
250 	//------------------------------
getSidAddressString() const251 	String SidAddress::getSidAddressString() const
252 	{
253 		if ( !mIsValid )
254 			return String();
255 
256 		std::ostringstream stream;
257 		stream << mId;
258 		SidList::const_iterator it = mSids.begin();
259 		for ( ; it != mSids.end(); ++it )
260 		{
261 			stream << "/" << *it;
262 		}
263 		switch ( mMemberSelection )
264 		{
265 		case MEMBER_SELECTION_NAME:
266 			stream << "." << mMemberSelectionName;
267 			break;
268 		case MEMBER_SELECTION_ONE_INDEX:
269 			stream << "(" << mFirstIndex << ")";
270 			break;
271 		case MEMBER_SELECTION_TWO_INDICES:
272 			stream << "(" << mFirstIndex << ")";
273 			stream << "(" << mSecondIndex<< ")";
274 			break;
275 		default:
276 			break;
277 		}
278 
279 		return stream.str();
280 	}
281 } // namespace COLLADASaxFWL
282