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