ISIS Core Library 0.7.2 (api 3.0.0)
|
00001 // 00002 // C++ Implementation: propmap 00003 // 00004 // Description: 00005 // 00006 // 00007 // Author: <Enrico Reimer>, (C) 2009 00008 // 00009 // Copyright: See COPYING file that comes with this distribution 00010 // 00011 // 00012 00013 #include "propmap.hpp" 00014 #include <boost/foreach.hpp> 00015 00016 namespace isis 00017 { 00018 namespace util 00019 { 00020 API_EXCLUDE_BEGIN 00022 namespace _internal 00023 { 00036 template<typename ForwardIterator, typename T, typename CMP> bool 00037 continousFind( ForwardIterator ¤t, const ForwardIterator end, const T &compare, CMP compOp ) 00038 { 00039 //find the first iterator which is does not compare less 00040 current = std::lower_bound( current, end, compare, compOp ); 00041 00042 if ( current == end //if we're at the end 00043 || compOp( compare, *current ) //or compare is less than that iterator 00044 ) 00045 return false;//we didn't find a match 00046 else 00047 return true;//not(current <> compare) makes compare == current 00048 } 00049 } 00051 API_EXCLUDE_END 00052 00054 // Contructors 00056 00057 PropertyMap::PropertyMap( const PropertyMap::Container &src ): Container( src ) {} 00058 00059 bool PropertyMap::operator==( const PropertyMap &src )const 00060 { 00061 const Container &other = src, &me = *this; 00062 return me == other; 00063 } 00064 00065 PropertyMap::PropertyMap() {} 00066 00067 00069 // The core tree traversal functions 00071 PropertyMap::mapped_type &PropertyMap::fetchEntry( const PropPath &path ) 00072 { 00073 return fetchEntry( *this, path.begin(), path.end() ); 00074 } 00079 PropertyMap::mapped_type &PropertyMap::fetchEntry( 00080 PropertyMap &root, 00081 const PropertyMap::propPathIterator at, const PropertyMap::propPathIterator pathEnd ) 00082 { 00083 PropPath::const_iterator next = at; 00084 next++; 00085 Container &rootRef = root; 00086 iterator found = static_cast<Container &>( root ).find( *at ); 00087 00088 if ( next != pathEnd ) {//we are not at the end of the path (a proposed leaf in the PropMap) 00089 if ( found != root.end() ) {//and we found the entry 00090 mapped_type &ref = found->second; 00091 LOG_IF( ref.is_leaf(), Runtime, error ) 00092 << MSubject( found->first ) << " is a leaf, but was requested as a branch in " 00093 << MSubject( listToString( at, pathEnd, "/" ) ); 00094 return fetchEntry( ref.getBranch(), next, pathEnd ); //continue there 00095 } else { // if we should create a sub-map 00096 //insert a empty branch (aka PropMap) at "*at" (and fetch the reference of that) 00097 LOG( Debug, verbose_info ) << "Creating an empty branch " << *at << " trough fetching"; 00098 return fetchEntry( rootRef[*at].getBranch(), next, pathEnd ); // and continue there 00099 } 00100 } else { //if its the leaf 00101 return rootRef[*at]; // (create and) return that entry 00102 } 00103 } 00104 00109 const PropertyMap::mapped_type *PropertyMap::findEntry( 00110 const PropertyMap &root, 00111 const propPathIterator at, const propPathIterator pathEnd ) 00112 { 00113 propPathIterator next = at; 00114 next++; 00115 PropertyMap::const_iterator found = static_cast<const Container &>( root ).find( *at ); 00116 00117 if ( next != pathEnd ) {//we are not at the end of the path (aka the leaf) 00118 if ( found != root.end() ) {//and we found the entry 00119 const mapped_type &ref = found->second; 00120 return findEntry( ref.getBranch(), next, pathEnd ); //continue there 00121 } 00122 } else if ( found != root.end() ) {// if its the leaf and we found the entry 00123 return &found->second; // return that entry 00124 } 00125 00126 return NULL; 00127 } 00128 bool PropertyMap::recursiveRemove( PropertyMap &root, const propPathIterator path_it, const propPathIterator pathEnd ) 00129 { 00130 bool ret = false; 00131 00132 if ( path_it != pathEnd ) { 00133 propPathIterator next = path_it; 00134 next++; 00135 iterator found = static_cast<Container &>( root ).find( *path_it ); 00136 00137 if ( found != root.end() ) { 00138 mapped_type &ref = found->second; 00139 00140 if ( ! ref.is_leaf() ) { 00141 ret = recursiveRemove( ref.getBranch(), next, pathEnd ); 00142 00143 if ( ref.getBranch().isEmpty() ) 00144 root.erase( found ); // remove the now empty branch 00145 } else { 00146 root.erase( found ); 00147 ret = true; 00148 } 00149 } else { 00150 LOG( Runtime, warning ) << "Entry " << MSubject( *path_it ) << " not found, skipping it"; 00151 } 00152 } 00153 00154 return ret; 00155 } 00156 00157 00159 // Generic interface for accessing elements 00161 00162 const std::vector< PropertyValue >& PropertyMap::propertyValueVec( const PropertyMap::PropPath &path ) const 00163 { 00164 const mapped_type *ref = findEntry( *this, path.begin(), path.end() ); 00165 00166 if( ref && ref->is_leaf() ) { 00167 return ref->getLeaf(); 00168 } else { 00169 LOG( Debug, warning ) << "Property " << path << " not found. Returning empty property."; 00170 static const _internal::treeNode emptyEntry; 00171 return emptyEntry.getLeaf(); 00172 } 00173 } 00174 00175 std::vector< PropertyValue >& PropertyMap::propertyValueVec( const PropertyMap::PropPath &path ) 00176 { 00177 mapped_type &n = fetchEntry( *this, path.begin(), path.end() ); 00178 LOG_IF( ! n.is_leaf(), Debug, error ) << "Using branch " << path << " as PropertyValue"; 00179 return n.getLeaf(); 00180 } 00181 00182 00183 const PropertyValue &PropertyMap::propertyValue( const PropertyMap::PropPath &path )const 00184 { 00185 return propertyValueVec( path )[0]; 00186 } 00187 00188 PropertyValue &PropertyMap::propertyValue( const PropertyMap::PropPath &path ) 00189 { 00190 std::vector< PropertyValue > &p = propertyValueVec( path ); 00191 p.resize( 1 ); // the user is expecting only one entry, so remove the others 00192 return p[0]; 00193 } 00194 00195 const PropertyMap &PropertyMap::branch( const PropertyMap::PropPath &path ) const 00196 { 00197 const mapped_type *ref = findEntry( *this, path.begin(), path.end() ); 00198 00199 if( ! ref ) { 00200 LOG( Runtime, warning ) << "Trying to access non existing branch " << path << "."; 00201 static const _internal::treeNode emptyEntry; 00202 return emptyEntry.getBranch(); 00203 } else { 00204 LOG_IF( ref->getBranch().isEmpty(), Runtime, warning ) << "Accessing empty branch " << path; 00205 return ref->getBranch(); 00206 } 00207 } 00208 PropertyMap &PropertyMap::branch( const PropPath &path ) 00209 { 00210 mapped_type &n = fetchEntry( *this, path.begin(), path.end() ); 00211 return n.getBranch(); 00212 } 00213 00214 bool PropertyMap::remove( const PropPath &path ) 00215 { 00216 return recursiveRemove( *this, path.begin(), path.end() ); 00217 } 00218 00219 bool PropertyMap::remove( const KeyList &removeList, bool keep_needed ) 00220 { 00221 bool ret = true; 00222 BOOST_FOREACH( const KeyType & key, removeList ) { 00223 if( hasProperty( key ) ) { // remove everything which is there 00224 if( !( propertyValue( key ).isNeeded() && keep_needed ) ) { // if its not needed or keep_need is not true 00225 ret &= remove( key ); 00226 } 00227 } 00228 } 00229 return ret; 00230 } 00231 00232 00233 bool PropertyMap::remove( const PropertyMap &removeMap, bool keep_needed ) 00234 { 00235 iterator thisIt = begin(); 00236 bool ret = true; 00237 00238 //remove everything that is also in second 00239 for ( const_iterator otherIt = removeMap.begin(); otherIt != removeMap.end(); otherIt++ ) { 00240 //find the closest match for otherIt->first in this (use the value-comparison-functor of PropMap) 00241 if ( continousFind( thisIt, end(), *otherIt, value_comp() ) ) { //thisIt->first == otherIt->first - so its the same property or propmap 00242 if ( ! thisIt->second.is_leaf() ) { //this is a branch 00243 if ( ! otherIt->second.is_leaf() ) { // recurse if its a branch in the removal map as well 00244 PropertyMap &mySub = thisIt->second.getBranch(); 00245 const PropertyMap &otherSub = otherIt->second.getBranch(); 00246 ret &= mySub.remove( otherSub ); 00247 00248 if( mySub.isEmpty() ) // delete my branch, if its empty 00249 erase( thisIt++ ); 00250 } else { 00251 LOG( Debug, warning ) << "Not deleting branch " << MSubject( thisIt->first ) << " because its no subtree in the removal map"; 00252 ret = false; 00253 } 00254 } else if( !( thisIt->second.getLeaf()[0].isNeeded() && keep_needed ) ) { // this is a leaf 00255 erase( thisIt++ ); // so delete this (they are equal - kind of) 00256 } 00257 } 00258 } 00259 00260 return ret; 00261 } 00262 00263 00265 // utilities 00267 bool PropertyMap::isValid() const 00268 { 00269 //iterate through the whole map and return false as soon as we find something needed _and_ empty 00270 const const_iterator found = std::find_if( begin(), end(), treeInvalidP() ); 00271 return found == end(); 00272 } 00273 00274 bool PropertyMap::isEmpty() const 00275 { 00276 return Container::empty(); 00277 } 00278 00279 PropertyMap::DiffMap PropertyMap::getDifference( const PropertyMap &other ) const 00280 { 00281 PropertyMap::DiffMap ret; 00282 diffTree( other, ret, "" ); 00283 return ret; 00284 } 00285 00286 void PropertyMap::diffTree( const PropertyMap &other, PropertyMap::DiffMap &ret, istring prefix ) const 00287 { 00288 const_iterator otherIt = other.begin(); 00289 00290 //insert everything that is in this, but not in second or is on both but differs 00291 for ( const_iterator thisIt = begin(); thisIt != end(); thisIt++ ) { 00292 const PropPath::value_type pathname = prefix + thisIt->first; 00293 00294 //find the closest match for thisIt->first in other (use the value-comparison-functor of PropMap) 00295 if ( _internal::continousFind( otherIt, other.end(), *thisIt, value_comp() ) ) { //otherIt->first == thisIt->first - so its the same property 00296 const mapped_type &first = thisIt->second, &second = otherIt->second; 00297 00298 if ( ! ( first.is_leaf() || second.is_leaf() ) ) { // if both are a branch 00299 const PropertyMap &thisMap = first.getBranch(); 00300 const PropertyMap &refMap = second.getBranch(); 00301 thisMap.diffTree( refMap, ret, pathname + "/" ); 00302 } else if ( ! ( first == second ) ) { // if they are not equal 00303 const PropertyValue firstVal = first.is_leaf() ? first.getLeaf()[0] : PropertyValue( Value<std::string>( first.toString() ) ); 00304 const PropertyValue secondVal = second.is_leaf() ? second.getLeaf()[0] : PropertyValue( Value<std::string>( second.toString() ) ); 00305 ret.insert( // add (propertyname|(value1|value2)) 00306 ret.end(), // we know it has to be at the end 00307 std::make_pair( 00308 pathname, //the key 00309 std::make_pair( firstVal, secondVal ) //pair of both values 00310 ) 00311 ); 00312 } 00313 } else { // if ref is not in the other map 00314 const PropertyValue firstVal = thisIt->second.is_leaf() ? thisIt->second.getLeaf()[0] : PropertyValue( Value<std::string>( thisIt->second.toString() ) ); 00315 ret.insert( // add (propertyname|(value1|[empty])) 00316 ret.end(), // we know it has to be at the end 00317 std::make_pair( 00318 pathname, 00319 std::make_pair( firstVal, PropertyValue() ) 00320 ) 00321 ); 00322 } 00323 } 00324 00325 //insert everything that is in second but not in this 00326 const_iterator thisIt = begin(); 00327 00328 for ( otherIt = other.begin(); otherIt != other.end(); otherIt++ ) { 00329 const PropPath::value_type pathname = prefix + otherIt->first; 00330 00331 if ( ! _internal::continousFind( thisIt, end(), *otherIt, value_comp() ) ) { //there is nothing in this which has the same key as ref 00332 const PropertyValue secondVal = otherIt->second.is_leaf() ? otherIt->second.getLeaf()[0] : PropertyValue( Value<std::string>( otherIt->second.toString() ) ); 00333 ret.insert( 00334 std::make_pair( // add (propertyname|([empty]|value2)) 00335 pathname, 00336 std::make_pair( PropertyValue(), secondVal ) 00337 ) 00338 ); 00339 } 00340 } 00341 } 00342 00343 void PropertyMap::removeEqual ( const PropertyMap &other, bool removeNeeded ) 00344 { 00345 iterator thisIt = begin(); 00346 00347 //remove everything that is also in second and equal (or also empty) 00348 for ( const_iterator otherIt = other.begin(); otherIt != other.end(); otherIt++ ) { 00349 //find the closest match for otherIt->first in this (use the value-comparison-functor of PropMap) 00350 if ( continousFind( thisIt, end(), *otherIt, value_comp() ) ) { //thisIt->first == otherIt->first - so its the same property 00351 if ( ! removeNeeded && thisIt->second.getLeaf()[0].isNeeded() ) {//Skip needed 00352 thisIt++; 00353 continue; 00354 } 00355 00356 if ( thisIt->second.empty() ) { //this is empty 00357 if ( otherIt->second.empty() ) { //the other is empty 00358 erase( thisIt++ ); // so delete this (they are equal - kind of) 00359 } else { 00360 LOG( Debug, verbose_info ) << "Keeping the empty " << thisIt->first << " because its is not empty in the other (" << *otherIt << ")"; 00361 thisIt++; 00362 } 00363 } else if ( ! otherIt->second.empty() ) { // if the other is not empty as well 00364 if ( thisIt->second == otherIt->second ) { //delete this, if they are equal 00365 LOG( Debug, verbose_info ) << "Removing " << *thisIt << " because its equal with the other (" << *otherIt << ")"; 00366 erase( thisIt++ ); // so delete this (they are equal - kind of) 00367 } else if ( ! ( thisIt->second.is_leaf() || otherIt->second.is_leaf() ) ) { //but maybe they are branches 00368 PropertyMap &thisMap = thisIt->second.getBranch(); 00369 const PropertyMap &otherMap = otherIt->second.getBranch(); 00370 thisMap.removeEqual( otherMap ); 00371 thisIt++; 00372 } 00373 } else {//only the other is empty 00374 LOG( Debug, verbose_info ) << "Keeping " << *thisIt << " because the other is empty"; 00375 thisIt++; 00376 } 00377 } 00378 } 00379 } 00380 00381 00382 PropertyMap::KeyList PropertyMap::join( const PropertyMap &other, bool overwrite ) 00383 { 00384 KeyList rejects; 00385 joinTree( other, overwrite, "", rejects ); 00386 return rejects; 00387 } 00388 00389 void PropertyMap::joinTree( const PropertyMap &other, bool overwrite, istring prefix, KeyList &rejects ) 00390 { 00391 iterator thisIt = begin(); 00392 00393 for ( const_iterator otherIt = other.begin(); otherIt != other.end(); otherIt++ ) { //iterate through the elements of other 00394 if ( continousFind( thisIt, end(), *otherIt, value_comp() ) ) { // if the element is allready here 00395 if ( thisIt->second.empty() ) { // if ours is empty 00396 LOG( Debug, verbose_info ) << "Replacing empty property " << MSubject( thisIt->first ) << " by " << MSubject( otherIt->second ); 00397 thisIt->second.insert( otherIt->second ); 00398 } else if ( ! ( thisIt->second.is_leaf() || otherIt->second.is_leaf() ) ) { // if both are a subtree 00399 PropertyMap &thisMap = thisIt->second.getBranch(); 00400 const PropertyMap &refMap = otherIt->second.getBranch(); 00401 thisMap.joinTree( refMap, overwrite, prefix + thisIt->first + "/", rejects ); //recursion 00402 } else if ( overwrite ) { // otherwise replace ours by the other (if we shall overwrite) 00403 LOG( Debug, info ) << "Replacing property " << MSubject( *thisIt ) << " by " << MSubject( otherIt->second ); 00404 thisIt->second = otherIt->second; 00405 } else if ( ! ( thisIt->second == otherIt->second ) ) { // otherwise put the other into rejected if its not equal to our 00406 LOG( Debug, info ) 00407 << "Rejecting property " << MSubject( *otherIt ) 00408 << " because " << MSubject( thisIt->second ) << " is allready there"; 00409 rejects.insert( rejects.end(), prefix + otherIt->first ); 00410 } 00411 } else { // ok we dont have that - just insert it 00412 std::pair<const_iterator, bool> inserted = insert( *otherIt ); 00413 LOG_IF( inserted.second, Debug, verbose_info ) << "Inserted property " << MSubject( *inserted.first ) << "."; 00414 } 00415 } 00416 } 00417 00418 00419 void PropertyMap::makeFlatMap( FlatMap &out, KeyType key_prefix ) const 00420 { 00421 for ( const_iterator i = begin(); i != end(); i++ ) { 00422 KeyType key = ( key_prefix.empty() ? "" : key_prefix + pathSeperator ) + i->first; 00423 00424 if ( i->second.is_leaf() ) { 00425 out.insert( std::make_pair( key, i->second.getLeaf()[0] ) ); 00426 } else { 00427 i->second.getBranch().makeFlatMap( out, key ); 00428 } 00429 } 00430 } 00431 00432 PropertyMap::FlatMap PropertyMap::getFlatMap() const 00433 { 00434 FlatMap buff; 00435 makeFlatMap( buff ); 00436 return buff; 00437 } 00438 00439 00440 bool PropertyMap::transform( const PropPath &from, const PropPath &to, int dstID, bool delSource ) 00441 { 00442 const PropertyValue &found = propertyValue( from ); 00443 bool ret = false; 00444 00445 if( ! found.isEmpty() ) { 00446 ValueReference &dst = static_cast<ValueReference &>( propertyValue( to ) ); 00447 00448 if ( found.getTypeID() == dstID ) { 00449 if( from != to ) { 00450 dst = found ; 00451 ret = true; 00452 } else { 00453 LOG( Debug, info ) << "Not transforming " << MSubject( found ) << " into same type at same place."; 00454 } 00455 } else { 00456 LOG_IF( from == to, Debug, warning ) << "Transforming " << MSubject( found ) << " in place."; 00457 dst = ( *found ).copyByID( dstID ); 00458 ret = !dst.isEmpty(); 00459 } 00460 } 00461 00462 if ( ret && delSource )remove( from ); 00463 00464 return ret; 00465 } 00466 00467 00468 const PropertyMap::KeyList PropertyMap::getKeys()const 00469 { 00470 KeyList ret; 00471 std::for_each( begin(), end(), walkTree<trueP>( ret ) ); 00472 return ret; 00473 } 00474 00475 PropertyMap::KeyList PropertyMap::findLists()const 00476 { 00477 KeyList ret; 00478 std::for_each( begin(), end(), walkTree<listP>( ret ) ); 00479 return ret; 00480 } 00481 00482 00483 const PropertyMap::KeyList PropertyMap::getMissing() const 00484 { 00485 PropertyMap::KeyList ret; 00486 std::for_each( begin(), end(), walkTree<invalidP>( ret ) ); 00487 return ret; 00488 } 00489 00490 00491 void PropertyMap::addNeeded( const PropPath &path ) 00492 { 00493 propertyValue( path ).needed() = true; 00494 } 00495 00496 00497 bool PropertyMap::hasProperty( const PropPath &path ) const 00498 { 00499 const mapped_type *ref = findEntry( *this, path.begin(), path.end() ); 00500 return ( ref && ref->is_leaf() && ! ref->getLeaf()[0].isEmpty() ); 00501 } 00502 00503 PropertyMap::KeyType PropertyMap::find( PropertyMap::KeyType key, bool allowProperty, bool allowBranch ) const 00504 { 00505 // make sure we only get the last part of the path if its one 00506 const PropPath path = stringToList<KeyType>( key, pathSeperator ); 00507 00508 if( path.empty() ) { 00509 LOG( Debug, error ) << "Search key " << MSubject( key ) << " is invalid, won't search"; 00510 return KeyType(); 00511 } else if( path.size() > 1 ) { 00512 LOG( Debug, warning ) << "Stripping search key " << MSubject( key ) << " to " << path.back(); 00513 } 00514 00515 key = path.back(); 00516 00517 // if the searched key is on this brach return its name 00518 const Container &map = static_cast<const Container &>( *this ); 00519 Container::const_iterator found = map.find( key ); 00520 00521 if( found != map.end() && 00522 ( ( found->second.is_leaf() && allowProperty ) || ( !found->second.is_leaf() && allowBranch ) ) 00523 ) { 00524 return found->first; 00525 } else { // otherwise search in the branches (getBranch() returns an empty PropMap for leaves - we wont have to check for that) 00526 BOOST_FOREACH( Container::const_reference ref, map ) { 00527 const KeyType foundKey = ref.second.getBranch().find( key, allowProperty, allowBranch ); 00528 00529 if( !foundKey.empty() ) // if the key is found abort search and return it with its branch-name 00530 return ref.first + "/" + foundKey; 00531 } 00532 } 00533 00534 return KeyType(); // nothing found 00535 } 00536 00537 bool PropertyMap::hasBranch( const PropPath &path ) const 00538 { 00539 const mapped_type *ref = findEntry( *this, path.begin(), path.end() ); 00540 return ( ref && ! ref->is_leaf() ); 00541 } 00542 00543 bool PropertyMap::rename( const PropPath &oldname, const PropPath &newname ) 00544 { 00545 const mapped_type *old_e = findEntry( oldname ); 00546 const mapped_type *new_e = findEntry( newname ); 00547 00548 if ( old_e ) { 00549 LOG_IF( new_e && ! new_e->empty(), Runtime, warning ) 00550 << "Overwriting " << std::make_pair( newname, *new_e ) << " with " << *old_e; 00551 fetchEntry( newname ) = *old_e; 00552 return remove( oldname ); 00553 } else { 00554 LOG( Runtime, warning ) 00555 << "Cannot rename " << oldname << " it does not exist"; 00556 return false; 00557 } 00558 } 00559 00560 void PropertyMap::toCommonUnique( PropertyMap &common, std::set<KeyType> &uniques, bool init )const 00561 { 00562 if ( init ) { 00563 common = *this; 00564 uniques.clear(); 00565 return; 00566 } else { 00567 const DiffMap difference = common.getDifference( *this ); 00568 BOOST_FOREACH( const DiffMap::value_type & ref, difference ) { 00569 uniques.insert( ref.first ); 00570 00571 if ( ! ref.second.first.isEmpty() )common.remove( ref.first );//if there is something in common, remove it 00572 } 00573 } 00574 } 00575 00576 std::ostream &PropertyMap::print( std::ostream &out, bool label )const 00577 { 00578 FlatMap buff; 00579 makeFlatMap( buff ); 00580 size_t key_len = 0; 00581 00582 for ( FlatMap::const_iterator i = buff.begin(); i != buff.end(); i++ ) 00583 if ( key_len < i->first.length() ) 00584 key_len = i->first.length(); 00585 00586 for ( FlatMap::const_iterator i = buff.begin(); i != buff.end(); i++ ) 00587 out << i->first << std::string( key_len - i->first.length(), ' ' ) + ":" << i->second.toString( label ) << std::endl; 00588 00589 return out; 00590 } 00591 00592 const PropertyMap::mapped_type *PropertyMap::findEntry( const PropPath &path )const 00593 { 00594 return findEntry( *this, path.begin(), path.end() ); 00595 } 00596 00597 00598 bool PropertyMap::listP::operator()( const std::pair< const istring, _internal::treeNode >& ref ) const 00599 { 00600 return ref.second.is_leaf() && ref.second.getLeaf().size() > 1; 00601 } 00602 00603 bool PropertyMap::trueP::operator()( const PropertyMap::value_type &/*ref*/ ) const 00604 { 00605 return true; 00606 } 00607 bool PropertyMap::invalidP::operator()( const PropertyMap::value_type &ref ) const 00608 { 00609 return ref.second.getLeaf()[0].isNeeded() && ref.second.getLeaf()[0].isEmpty(); 00610 } 00611 bool PropertyMap::treeInvalidP::operator()( const PropertyMap::value_type &ref ) const 00612 { 00613 if ( ref.second.is_leaf() ) { 00614 const PropertyValue &val = ref.second.getLeaf()[0]; 00615 return val.isNeeded() && val.isEmpty(); 00616 } else { 00617 return ! ref.second.getBranch().isValid(); 00618 } 00619 } 00620 00621 } 00622 }