text: /***************************************************************
   *
   * Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
   * University of Wisconsin-Madison, WI.
   * 
   * Licensed under the Apache License, Version 2.0 (the "License"); you
   * may not use this file except in compliance with the License.  You may
   * obtain a copy of the License at
   * 
   *    http://www.apache.org/licenses/LICENSE-2.0
   * 
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   *
   ***************************************************************/
  
  
  #include "classad/common.h"
  #include "classad/view.h"
  #include "classad/collection.h"
  #include "classad/collectionBase.h"
  
  using namespace std;
  
  // ----------- <implementation of ViewMember class> -----------------
  
  namespace classad {
  
  ViewMember::
  ViewMember( )
  {
  	rank.SetUndefinedValue( );
  }
  
  ViewMember::ViewMember(const ViewMember &other)
  {
  	key = other.key;
  	rank.CopyFrom( other.rank );
  	return;
  }
  
  ViewMember::
  ~ViewMember( )
  {
  	return;
  }
  
  
  void ViewMember::
  SetRankValue( const Value &rankValue )
  {
  	rank.CopyFrom( rankValue );
  }
  
  
  void ViewMember::
  SetKey( const string &adKey )
  {
  	key = adKey;
  }
  
  
  void ViewMember::
  GetKey( string &adKey ) const 
  {
  	adKey = key;
  }
  
  
  void ViewMember::
  GetRankValue( Value &rankValue ) const
  {
  	rankValue.CopyFrom( rank );
  }
  
  
  ViewMember ViewMember::
  operator=( const ViewMember &vm )
  {
  	key = vm.key;
  	rank.CopyFrom( vm.rank );
  	return( *this );
  }
  
  bool operator<(const ViewMember &vm1, const ViewMember &vm2)
  {
  	bool                lessThan;
  	bool                isEqual;
  	Value				val1, val2;
  	Value::ValueType	vt1, vt2;
  
  	vm1.GetRankValue( val1 );
  	vm2.GetRankValue( val2 );
  
  	vt1 = val1.GetType( );
  	vt2 = val2.GetType( );
  
  	// if the values are of the same scalar type, or if they are both
  	// numbers, use the builtin < comparison operator
  	if( ( vt1==vt2 && vt1!=Value::CLASSAD_VALUE && !val2.IsListValue() ) ||
  			( vt1==Value::INTEGER_VALUE && vt2==Value::REAL_VALUE )			||
  			( vt1 == Value::REAL_VALUE && vt2 == Value::INTEGER_VALUE ) ) {
  		Value	lessThanResult, equalResult;
  		Operation::Operate( Operation::LESS_THAN_OP, val1, val2, lessThanResult );
  		Operation::Operate( Operation::EQUAL_OP, val1, val2, equalResult);
  		lessThan = lessThanResult.IsBooleanValue( lessThan ) && lessThan;
  		isEqual  = equalResult.IsBooleanValue( isEqual) && isEqual;
  	} else {
  		// otherwise, rank them by their type numbers
  		lessThan = (vt1 < vt2);
  		isEqual  = false;
  	}
  
  	// If they are equal, rank them by key.  This is important,
  	// because otherwise two members with the same rank are considered
  	// to be equal, but they may have different keys, and when we try
  	// to erase a single member from the viewMembers, it will erase
  	// everything with the same rank, whether or not they have the
  	// same key.
  	if (isEqual) {
  		lessThan = (vm1.key < vm2.key);
  	}
  	
  	return lessThan;
  }
  
  // ---------------- </implementation of ViewMember class> ------------------
  
  
  // ----------------- <implementation of View class> -------------------
  
  View::
  View( View *parentView )
  {
  	Value				val;
  	vector<ExprTree*>	vec;
  
  	ClassAd	*ad = evalEnviron.GetLeftAd( );
  	parent = parentView;
  	ExprTree* pExp;
  	ad->InsertAttr( ATTR_REQUIREMENTS, true );
  	ad->Insert( ATTR_RANK, (pExp=Literal::MakeLiteral( val )) );
  	ad->Insert( ATTR_PARTITION_EXPRS, (pExp=ExprList::MakeExprList( vec )) );
  	if( parentView ) {
  		ad->InsertAttr( "ParentViewName", parentView->GetViewName( ) );
  	}
  }
  
  
  View::
  ~View( )
  {
  	SubordinateViews::iterator	xi;
  	PartitionedViews::iterator	mi;
  
  		// recursively delete subordinate child views ...
  	for( xi = subordinateViews.begin( ); xi != subordinateViews.end( ); xi++ ) {
  		delete (*xi);
  	}
  
  		// ... and partitioned child views
  	for( mi = partitionedViews.begin( ); mi != partitionedViews.end( ); mi++ ) {
  		delete mi->second;
  	}
  }
  
  
  bool View::
  SetViewName( const ViewName &name )
  {
  	ClassAd	*ad;
  	viewName = name;
  	if( !(ad=evalEnviron.GetLeftAd( )) || !ad->InsertAttr("ViewName", name) ) {
  		CondorErrno = ERR_FAILED_SET_VIEW_NAME;
  		return( false );
  	}
  	return( true );
  }
  
  
  bool View::
  SetViewInfo( ClassAdCollection *coll, ClassAd *ad )
  {
  	ExprTree	*rankExpr=NULL, *constraintExpr=NULL, *tmp=NULL;
  	ExprList	*partitionExprs=NULL;
  
  	if( !( rankExpr = ad->Remove( ATTR_RANK ) ) ) {
  		Value val;
  		val.SetUndefinedValue( );
  		rankExpr = Literal::MakeLiteral( val );
  	}
  
  	if( !( constraintExpr = ad->Remove( ATTR_REQUIREMENTS ) ) ) {
  		Value val;
  		val.SetBooleanValue( true );
  		constraintExpr = Literal::MakeLiteral( val );
  	}
  	
  	if( ( ( tmp = ad->Remove( ATTR_PARTITION_EXPRS ) ) &&
  			tmp->GetKind( ) != ExprTree::EXPR_LIST_NODE ) || !tmp ) {
  		vector<ExprTree*>	vec;
  		if( tmp ) delete tmp;
  		partitionExprs = ExprList::MakeExprList( vec );
  	} else {
  		partitionExprs = (ExprList*) tmp;
  	}
  
  	// Preserve the view name and parent view name.
  	ClassAd  *eval;
  	string view_name, parent_view_name;
  
  	eval = evalEnviron.GetLeftAd();
  	eval->EvaluateAttrString("ViewName", view_name);
  	eval->EvaluateAttrString("ParentViewName", parent_view_name);
  
  	ad->InsertAttr("ViewName", view_name);
  	ad->InsertAttr("ParentViewName", parent_view_name);
  
  	if( !evalEnviron.ReplaceLeftAd( ad ) ) {
  		CondorErrMsg+="; could not replace view info; failed to set view info";
  		delete constraintExpr;
  		delete rankExpr;
  		delete partitionExprs;
  		return( false );
  	}
  
  	if( constraintExpr && !SetConstraintExpr( coll, constraintExpr ) ) {
  		CondorErrMsg += "; failed to set view info";
  		delete constraintExpr;
  		delete rankExpr;
  		delete partitionExprs;
  		return( false );
  	}
  
  	if( !SetRankExpr( coll, rankExpr ) ) {
  		CondorErrMsg += "; failed to set view info";
  		delete rankExpr;
  		delete partitionExprs;
  		return( false );
  	}
  
  	if( !SetPartitionExprs( coll, partitionExprs ) ) {
  		CondorErrMsg += "; failed to set view info";
  		delete partitionExprs;
  		return( false );
  	}
  
  	return( true );
  }
  
  
  ClassAd *View::
  GetViewInfo( )
  {
  	ClassAd				*newAd, *ad = evalEnviron.GetLeftAd( );
  	vector<ExprTree*> 	viewNames;
  	Literal				*lit;
  	ExprTree* pExpr=0;
  
  	if( !ad ) {
  		CLASSAD_EXCEPT( "internal error: view has no view info!" );
  	}
  	
  	if( !( newAd = (ClassAd*) ad->Copy( ) ) ) {
  		CondorErrno = ERR_MEM_ALLOC_FAILED;
  		CondorErrMsg = "";
  		if( newAd ) delete newAd;
  		return( NULL );
  	}
  		// insert number of members
  	newAd->InsertAttr( "NumMembers", (int) viewMembers.size( ) );
  
  	
  		// insert names of subordinate views
  	viewNames.clear( );
  	SubordinateViews::iterator	si;
  	for( si=subordinateViews.begin(); si!=subordinateViews.end(); si++ ) {
  		Value val;
  		val.SetStringValue( (*si)->GetViewName( ) );
  		if( !( lit = Literal::MakeLiteral( val ) ) ) {
  			delete newAd;
  			return( NULL );
  		}
  		viewNames.push_back( lit );
  	}
  	newAd->Insert(ATTR_SUBORDINATE_VIEWS,(pExpr=ExprList::MakeExprList(viewNames)));
  	
  
  		// insert names of partitioned views
  	viewNames.clear( );
  	PartitionedViews::iterator	pi;
  	for( pi = partitionedViews.begin( ); pi != partitionedViews.end( ); pi++ ) {
  		Value val;
  		val.SetStringValue( pi->second->GetViewName( ) );
  		if( !( lit = Literal::MakeLiteral( val ) ) ) {
  			delete newAd;
  			return( NULL );
  		}
  		viewNames.push_back( lit );
  	}
  	newAd->Insert(ATTR_PARTITIONED_VIEWS,(pExpr=ExprList::MakeExprList(viewNames)));
  
  
  	return( newAd );
  }
  
  
  bool View::
  SetConstraintExpr( ClassAdCollection *coll, const string &expr )
  {
  	ExprTree		*constraint;
  
  		// parse the expression and insert it into ad in left context
  	if( !coll->parser.ParseExpression( expr, constraint ) ) {
  		CondorErrMsg += "; failed to set constraint on view";
  		return( false );
  	}
  	return( SetConstraintExpr( coll, constraint ) );
  }
  
  
  bool View::
  SetConstraintExpr( ClassAdCollection *coll, ExprTree *constraint )
  {
  	ClassAd					*ad;
  	ViewMembers::iterator	vmi;
  	bool					match;
  	string					key;
  
  		// insert expression into ad in left context
  	if( !( ad=evalEnviron.GetLeftAd() ) ||
  		!ad->Insert( ATTR_REQUIREMENTS, constraint ) ) {
  		CondorErrMsg += "; failed to set constraint on view";
  		return( false );
  	}
  
  		// check if all members still belong to the view
  	for( vmi = viewMembers.begin( ); vmi != viewMembers.end( ); vmi++ ) {
  		vmi->GetKey( key );
  		if( ( ad = coll->GetClassAd( key ) ) == NULL ) {
  			CLASSAD_EXCEPT( "internal error: classad in view but not in collection" );
  		}
  		evalEnviron.ReplaceRightAd( ad );
  		match = evalEnviron.EvaluateAttrBool("RightMatchesLeft",match) && match;
  		evalEnviron.RemoveRightAd( );
  			// if classad doesn't match constraint remove from view
  		if( !match ) {
  			ClassAdDeleted( coll, key, ad );
  		}
  	}
  
  	return( true );
  }
  
  bool View::
  SetRankExpr( ClassAdCollection *coll, const string &expr )
  {
  	ExprTree	*rank;
  
  		// parse the expression and insert it into ad in left context
  	if( !coll->parser.ParseExpression( expr, rank ) ) {
  		CondorErrMsg += "; failed to set rank on view";
  		return( false );
  	}
  	return( SetRankExpr( coll, rank ) );
  }
  
  
  bool View::
  SetRankExpr( ClassAdCollection *coll, ExprTree *rank )
  {
  	ClassAd					*ad;
  	ViewMember				vm;
  	MemberIndex::iterator	mIdxItr;
  	ViewMembers::iterator	vmi;
  	string					key;
  	Value					rankValue;
  
  		// insert expression into ad in left context
  	if( !( ad=evalEnviron.GetLeftAd() ) ) {
  		CLASSAD_EXCEPT( "internal error:  view has no view info" );
  	}
  	
  	if( !ad->Insert( ATTR_RANK, rank ) ) {
  		CondorErrMsg += "failed to set rank on view";
  		return( false );
  	}
  
  		// clear out member list
  	viewMembers.clear( );
  
  		// re-order content by new rank expression
  	for( mIdxItr=memberIndex.begin(); mIdxItr!=memberIndex.end(); mIdxItr++ ) {
  		key = mIdxItr->first;
  		if( ( ad = coll->GetClassAd( key ) ) == NULL	||
  			!evalEnviron.ReplaceRightAd( ad )			||
  			!evalEnviron.EvaluateAttr( "LeftRankValue", rankValue ) ) {
  				// internal error
  			CLASSAD_EXCEPT( "internal error:  could not determine 'Rank' value" );
  		}
  
  			// insert into member list
  		vm.SetKey( key );
  		vm.SetRankValue( rankValue );
  		viewMembers.insert( vm );
  	}
  
  		// now re-do the index
  	memberIndex.clear( );
  	for( vmi = viewMembers.begin( ); vmi != viewMembers.end( ); vmi++ ) {
  		vmi->GetKey( key );
  		memberIndex[key] = vmi;
  	}
  
  	return( true );
  }
  
  
  bool View::
  SetPartitionExprs( ClassAdCollection *coll, const string &expr )
  {
  	ExprTree	*exprList=NULL;
  
  		// parse the expression and insert it into ad in left context
  	if( !coll->parser.ParseExpression( expr, exprList ) || 
  			(exprList->GetKind( ) != ExprTree::EXPR_LIST_NODE) ) {
  		if( exprList ) delete exprList;
  		CondorErrno = ERR_BAD_PARTITION_EXPRS;
  		CondorErrMsg += "; failed to set partition expresssions";
  		return( false );
  	}
  	
  	return( SetPartitionExprs( coll, (ExprList*)exprList ) );
  }
  
  
  bool View::
  SetPartitionExprs( ClassAdCollection *coll, ExprList *el )
  {
  		// insert expression list into view info
  	ClassAd *ad = evalEnviron.GetLeftAd( );
  	if( !el ) {
  		CondorErrno = ERR_BAD_PARTITION_EXPRS;
  		CondorErrMsg = "invalid 'PartitionExprs'; failed to partition";
  		return( false );
  	}
  	if( !( ad->Insert( ATTR_PARTITION_EXPRS, (ExprTree* &)el ) ) ) {
  		CondorErrMsg += "failed to set partition expressions on view";
  		return( false );
  	}
  
  		// re-establish partition views; first delete all partition views
  	PartitionedViews::iterator  mi;
  	for( mi = partitionedViews.begin( ); mi != partitionedViews.end( ); mi++ ) {
  		mi->second->DeleteView( coll );
  		delete mi->second;
  	}
  	partitionedViews.clear( );
  
  		// if the partition expressions list is empty, we're done
  	vector<ExprTree*> components;
  	el->GetComponents( components );
  	if( components.size( ) == 0 ) return( true );
  	
  		// re-partition content
  	ViewMembers::iterator	vmi;
  	string					key, signature;
  	View					*partition;
  
  	for( vmi = viewMembers.begin( ); vmi != viewMembers.end( ); vmi++ ) {
  			// get signature of this ad
  		vmi->GetKey( key );
  		if( ( ad = coll->GetClassAd( key ) ) == NULL ) {
  			CLASSAD_EXCEPT( "internal error:  classad %s in view but not in collection",
  				key.c_str( ) );
  		}
  		signature = makePartitionSignature( ad );
  
  			// check if we have a partition with this signature
  		if( partitionedViews.find( signature ) == partitionedViews.end( ) ) {
  				// no partition ... make a new one
  			if( ( partition = new View( this ) ) == NULL ) {
  				CondorErrno = ERR_MEM_ALLOC_FAILED;
  				CondorErrMsg = "";
  				return( false );
  			}
  			if( !coll->RegisterView( viewName + ":" + signature, partition ) ) {
  				CondorErrMsg += "; could not complete partitioning";
  				return( false );
  			}
  			partition->SetViewName( viewName + ":" + signature );
  			partitionedViews[signature] = partition;
  		} else {
  				// use the partition that's already there
  			partition = partitionedViews[signature];
  		}
  
  			// add classad to the partition
  		if( !partition->ClassAdInserted( coll, key, ad ) ) {
  			CondorErrMsg += "; failed to set partition expressions";
  			return( false );
  		}
  	}
  
  	return( true );
  }
  
  
  ExprTree *View::
  GetConstraintExpr( )
  {
  	ClassAd		*lAd;
  	ExprTree	*tree;
  		// get requirements expression from ad in left context
  	if( !(lAd = evalEnviron.GetLeftAd( ) ) ) {
  		CLASSAD_EXCEPT( "internal error:  no view info in view" );
  	}
  	if( !(tree = lAd->Lookup( ATTR_REQUIREMENTS ) ) ) {
  		CondorErrno = ERR_NO_REQUIREMENTS_EXPR;
  		CondorErrMsg = "no 'Requirements' expression in view info";
  		return( (ExprTree*) NULL );
  	}
  	return( tree );
  }
  
  
  ExprTree *View::
  GetRankExpr( )
  {
  	ClassAd		*lAd;
  	ExprTree	*tree;
  		// get rank expression from ad in left context
  	if( !(lAd = evalEnviron.GetLeftAd( ) ) ) {
  		CLASSAD_EXCEPT( "internal error:  no view info in view" );
  	}
  	if( !(tree = lAd->Lookup( ATTR_RANK ) ) ) {
  		CondorErrno = ERR_NO_RANK_EXPR;
  		CondorErrMsg = "no 'Rank' expression in view info";
  		return( (ExprTree*) NULL );
  	}
  	return( tree );
  }
  
  
  
  bool View::
  IsMember( const string &key )
  {
  	return( memberIndex.find( key ) != memberIndex.end( ) );
  }
  
  
  bool View::
  FindPartition( ClassAd *rep, ViewName &partition )
  {
  	PartitionedViews::iterator	itr;
  	string sig = makePartitionSignature( rep );
  
  	if( sig.empty( ) || sig == "ERROR" || 
  			( itr = partitionedViews.find( sig ) ) == partitionedViews.end( ) ){
  		CondorErrno = ERR_NO_SUCH_VIEW;
  		CondorErrMsg = "no partition matching representative found";
  		return( false );
  	}
  	partition = itr->second->GetViewName( );
  	return( true );
  }
  
  
  bool View::
  InsertSubordinateView( ClassAdCollection *coll, ClassAd *viewInfo )
  {
  	View					*newView = new View( this );
  	ViewMembers::iterator	vmi;
  	string					key;
  	ClassAd					*ad;
  	string					name;;
  
  	if( !newView ) {
  		CondorErrno = ERR_MEM_ALLOC_FAILED;
  		CondorErrMsg = "";
  		return( false );
  	}
  
  	if( viewInfo ) {
  		viewInfo->EvaluateAttrString( ATTR_VIEW_NAME, name );
  		newView->evalEnviron.ReplaceLeftAd( viewInfo );
  	} 
  
  	newView->SetViewName( name );
  	if( !coll->RegisterView( name, newView ) ) {
  		CondorErrMsg += "; failed to insert new view";
  		delete newView;
  		return( false );
  	}
  	subordinateViews.push_front( newView );
  
  		// insert current view content into new view
  	for( vmi = viewMembers.begin( ); vmi != viewMembers.end( ); vmi++ ) {
  		vmi->GetKey( key );
  		if( ( ad = coll->GetClassAd( key ) ) == NULL ) {
  			CLASSAD_EXCEPT( "internal error:  classad %s in view but not in collection",
  				key.c_str( ) );
  		}
  		if( !newView->ClassAdInserted( coll, key, ad ) ) {
  			CondorErrMsg += "; failed to insert content into new view";
  			return( false );
  		}
  	}
  
  	return( true );
  }
  
  
  bool View::
  InsertPartitionedView( ClassAdCollection *coll, ClassAd *viewInfo, 
  	ClassAd *rep )
  {
  	string	signature;
  	View	*partition;
  	string	tmp;
  
  	signature = makePartitionSignature( rep );
  	delete rep;
  
  		// must have a partitioning
  	if( signature.empty( ) ) {
  		delete viewInfo;
  		CondorErrno = ERR_BAD_PARTITION_EXPRS;
  		CondorErrMsg = "missing or bad partition expressions; cannot add "
  			"partition";
  		return( false );
  	}
  
  		// check if a partition with the given signature already exists
  	if( partitionedViews.find( signature ) != partitionedViews.end( ) ) {
  			// partition already exists
  		delete viewInfo;
  		CondorErrno = ERR_PARTITION_EXISTS;
  		CondorErrMsg = "partition " + signature + " already exists";
  		return( false );
  	}
  		// create new partition
  	if( ( partition = new View( this ) ) == NULL ) {
  		CondorErrno = ERR_MEM_ALLOC_FAILED;
  		CondorErrMsg = "";
  		return( false );
  	}
  
  	if( viewInfo && viewInfo->EvaluateAttrString( ATTR_VIEW_NAME, tmp ) ) {
  		partition->SetViewName( tmp );
  		if( !coll->RegisterView( tmp, partition ) ) {
  			delete viewInfo;
  			delete partition;
  			CondorErrMsg += "; failed to add partition " + tmp;
  			return( false );
  		}
  	} else {
  		partition->SetViewName( viewName + ":" + signature );
  		if( !coll->RegisterView( viewName + ":" + signature, partition ) ) {
  			delete viewInfo;
  			delete partition;
  			CondorErrMsg += "; failed to add partition " + tmp;
  			return( false );
  		}
  	}
  
  		// enter into hash_map
  	partitionedViews[signature] = partition;
  
  		// setup view info of partition
  	if( viewInfo ) {
  		partition->evalEnviron.ReplaceLeftAd( viewInfo );
  	} 
  
  		// NOTE:  since this is a partition which hasn't been created 
  		// previously, none of the classads in this view need be inserted
  		// into the new partition
  	return( true );
  }
  
  
  bool View::
  DeleteChildView( ClassAdCollection *coll, const ViewName &vName )
  {
  	if( !DeleteSubordinateView( coll, vName ) && 
  			!DeletePartitionedView( coll, vName ) ) {
  		return( false );
  	}
  	CondorErrno = ERR_OK;
  	CondorErrMsg = "";
  	return( true );
  }
  
  
  bool View::
  DeleteSubordinateView( ClassAdCollection *coll, const ViewName &vName )
  {
  	SubordinateViews::iterator	xi;
  
  	for( xi = subordinateViews.begin( ); xi != subordinateViews.end( ); xi++ ) {
  		if( (*xi)->GetViewName( ) == vName ) {
  			(*xi)->DeleteView( coll );
  			delete (*xi);
  			subordinateViews.erase( xi );
  			return( true );
  		}
  	}
  	CondorErrno = ERR_NO_SUCH_VIEW;
  	CondorErrMsg = "no child view named " + vName + " in view";
  	return( false );
  }
  
  
  bool View::
  DeletePartitionedView( ClassAdCollection *coll, const ViewName &vName )
  {
  	PartitionedViews::iterator 	mi;
  
  	for( mi = partitionedViews.begin( ); mi != partitionedViews.end( ); mi++ ) {
  		if( mi->second->GetViewName( ) == vName ) {
  				// check if the partition is non-empty ...
  			if( mi->second->Size( ) != 0 ) {
  					// remove all child views and reset view info
  				View						*view = mi->second;
  				SubordinateViews::iterator	svi;
  				PartitionedViews::iterator	pvi;
  
  					// delete subordinate views
  				for( svi = view->subordinateViews.begin( ); 
  						svi != view->subordinateViews.end( ); svi++ ) {
  					(*svi)->DeleteView( coll );
  					delete (*svi);
  				}
  				view->subordinateViews.clear( );
  
  					// delete partitioned views
  				for( pvi = view->partitionedViews.begin( ); 
  						pvi != view->partitionedViews.end( ); pvi++ ) {
  					pvi->second->DeleteView( coll );
  					delete pvi->second;
  				}
  				view->partitionedViews.clear( );
  
  					// reset name of view
  				coll->UnregisterView( vName );
  				coll->RegisterView( viewName + ":" + mi->first, view );
  
  					// reset other view info
  				vector<ExprTree*> vec;
  				ExprTree * pExpr=0;
  				ClassAd *ad = new ClassAd( );
  				if( !ad ) {
  					CondorErrno = ERR_MEM_ALLOC_FAILED;
  					CondorErrMsg = "";
  					return( false );
  				}
  				if( !ad->InsertAttr( ATTR_REQUIREMENTS, true )	||
  						!ad->InsertAttr( ATTR_RANK, 0 )				||
  						!ad->Insert(ATTR_PARTITION_EXPRS,
  							(pExpr=ExprList::MakeExprList( vec )) )	||
  						!view->SetViewInfo( coll, ad ) ) {
  					CondorErrMsg += "; failed to delete partition view " +
  						vName;
  					return( false );
  				}
  				return( true );
  			}
  
  				// empty partition ... can just delete it
  			mi->second->DeleteView( coll );
  			delete mi->second;
  			partitionedViews.erase( mi );
  			return( true );
  		}
  	}
  	CondorErrno = ERR_NO_SUCH_VIEW;
  	CondorErrMsg = "no partition child view named " + vName + " in view";
  	return( false );
  }
  
  
  void View::
  DeleteView( ClassAdCollection *coll )
  {
  	SubordinateViews::iterator	si;
  	PartitionedViews::iterator	pi;
  
  	coll->UnregisterView( viewName );
  
  	for( si = subordinateViews.begin( ); si != subordinateViews.end( ); si++ ) {
  		(*si)->DeleteView( coll );
  		delete (*si);
  	}
  
  	for( pi = partitionedViews.begin( ); pi != partitionedViews.end( ); pi++ ) {
  		pi->second->DeleteView( coll );
  		delete pi->second;
  	}
  }
  
  
  
  bool View::
  DeletePartitionedView( ClassAdCollection *coll, ClassAd *rep )
  {
  	string signature = makePartitionSignature( rep );
  
  	if( signature.empty( ) ) {
  		CondorErrno = ERR_NO_SUCH_VIEW;
  		CondorErrMsg = "no partition corresponds to representative";
  		return( false );
  	}
  
  	ViewName vName = viewName + ":" + signature;
  	return( DeletePartitionedView( coll, vName ) );
  }
  
  
  // The convention is that each view verifies its constraint before accepting
  // an ad.  However, the view assumes that in the case of view partitions, the
  // parent view will have identified the correct child partition to use.
  bool View::
  ClassAdInserted( ClassAdCollection *coll, const string &key, 
  	ClassAd *ad )
  {
  	PartitionedViews::iterator	partition;
  	string						signature;
  	ViewMember					vm;
  	bool 						match;
  	Value						rankValue;
  	View						*childView;
  
  		// check if the ad satisfies the view's constraint; if the constraint 
  		// was not satisfied, the ad can be ignored
  	evalEnviron.ReplaceRightAd( ad );
  	match = evalEnviron.EvaluateAttrBool("RightMatchesLeft",match) && match;
  	if( !match ) {
  		evalEnviron.RemoveRightAd( );
  		return( true );
  	}
  
  		// obtain the rank value of the ad
  	if( !evalEnviron.EvaluateAttr( "LeftRankValue", rankValue ) ) {
  		CondorErrMsg += "; could not get 'Rank' value; failed to insert "
  			"classad " + key + "in view " + viewName;
  		return( false );
  	}
  	evalEnviron.RemoveRightAd( );
  
  		// insert into every subordinate child view
  	SubordinateViews::iterator	xi;
  	for( xi = subordinateViews.begin( ); xi != subordinateViews.end( ); xi++ ) {
  		if( !(*xi)->ClassAdInserted( coll, key, ad ) ) {
  			return( false );
  		}
  	}
  
  		// find partition to insert into
  	signature = makePartitionSignature( ad );
  	if( !signature.empty( ) ) {
  		partition = partitionedViews.find( signature );
  		if( partition == partitionedViews.end( ) ) {
  				// no appropriate partition --- create one and insert
  			if( ( childView = new View( this ) ) == 0 ) {
  				CondorErrno = ERR_MEM_ALLOC_FAILED;
  				CondorErrMsg = "";
  				return( false );
  			}
  
  			if( !coll->RegisterView( viewName + ":" + signature, childView ) ) {
  				if( childView ) delete childView;
  				CondorErrMsg += "; failed to create view; failed to insert "
  					"classad " + key + "in view";
  				return( false );
  			}
  			childView->SetViewName( viewName + ":" + signature );
  			partitionedViews[signature] = childView;
  		} else {
  			childView = partitionedViews[signature];
  		}
  
  			// update the partition
  		if( !childView->ClassAdInserted( coll, key, ad ) ) {
  			return( false );
  		}
  	}
  
  		// insert ad into list of view members and update index
  	vm.SetKey( key );
  	vm.SetRankValue( rankValue );
  	memberIndex[key] = viewMembers.insert(vm);
  
  	return( true );
  }
  
  
  
  void View::
  ClassAdPreModify( ClassAdCollection *coll, ClassAd *ad )
  {
  	SubordinateViews::iterator	xi;
  	PartitionedViews::iterator	mi;
  	
  		// stash signature of ad
  	oldAdSignature = makePartitionSignature( ad );
  
  		// perform premodify on all subordinate children ...
  	for( xi = subordinateViews.begin( ); xi != subordinateViews.end( ); xi++ ) {
  		(*xi)->ClassAdPreModify( coll, ad );
  	}
  
  		// ... and partition children
  	for( mi = partitionedViews.begin( ); mi != partitionedViews.end( ); mi++ ) {
  		mi->second->ClassAdPreModify( coll, ad );
  	}
  
  }
  
  
  bool View::
  ClassAdModified( ClassAdCollection *coll, const string &key, 
  	ClassAd *mad )
  {
  	bool	match, wasMember, sameRank, rval = true;
  	Value	rankValue, oldAdRank, equal;
  	MemberIndex::iterator	itr = memberIndex.find( key );
  
  		// check if classad is currently a member of this view
  	if( itr == memberIndex.end( ) ) {
  		wasMember = false;
  	} else {
  		wasMember = true;
  		((ViewMember) *(itr->second)).GetRankValue( oldAdRank );
  	}
  
  		// evaluate constraint and get new rank value
  	evalEnviron.ReplaceRightAd( mad );
  	match = evalEnviron.EvaluateAttrBool("RightMatchesLeft",match) && match;
  	if( !evalEnviron.EvaluateAttr( "LeftRankValue", rankValue ) ) {
  		rankValue.SetUndefinedValue( );
  	}
  	evalEnviron.RemoveRightAd( );
  
  	if( wasMember && match ) {
  		string 	sig;
  			// was and still is a member; check if rank has changed
  		Operation::Operate( Operation::IS_OP, rankValue, oldAdRank, equal );
  		if( !equal.IsBooleanValue( sameRank ) || !sameRank ) {
  				// rank changed ... need to re-order
  			ViewMember	vm;
  
  				// remove old view member ...
  			vm.SetRankValue( oldAdRank );
  			vm.SetKey(key);
  			viewMembers.erase( vm );
  				// re-insert with new rank value and update member index
  			vm.SetRankValue( rankValue );
  			memberIndex[key] = viewMembers.insert( vm );
  		}
  
  			// check if the signature has changed
  		sig = makePartitionSignature( mad );
  		if( sig != oldAdSignature ) {
  			PartitionedViews::iterator	mi;
  			View						*newPartition;
  				// yes ... remove from old partition and insert into new
  			if( !oldAdSignature.empty( ) ) {
  				mi = partitionedViews.find( oldAdSignature );
  				if( mi == partitionedViews.end( ) ) {
  						// partition of ad not found; some internal error
  					CLASSAD_EXCEPT( "internal error:  partition of classad with "
  						"signature %s not found", oldAdSignature.c_str( ) );
  				}
  					// delete from old partition
  				mi->second->ClassAdDeleted( coll, key, mad );
  			}
  
  				// is there a partition with the new signature?
  			if( !sig.empty( ) ) {
  				mi = partitionedViews.find( sig );
  				if( mi != partitionedViews.end( ) ) {
  						// yes ... insert into this partition
  					if( !mi->second->ClassAdInserted(coll, key, mad) ) {
  						CondorErrMsg+="; failed to relocate ad on modification";
  						return( false );
  					}
  				} else {
  						// no ... create a new partition
  					if( ( newPartition = new View( this ) ) == 0 ) {
  						oldAdSignature.erase( oldAdSignature.begin( ), 
  							oldAdSignature.end( ) );
  						CondorErrno = ERR_MEM_ALLOC_FAILED;
  						CondorErrMsg = "";
  						return( false );
  					} 
  					if( !coll->RegisterView( viewName+":"+sig, newPartition ) ){
  						delete newPartition;
  						CondorErrMsg += "; failed to create new partition for "
  							" modified ad";
  						return( false );
  					}
  					newPartition->SetViewName( viewName + ":" + sig );
  					if( !newPartition->ClassAdInserted(coll, key, mad) ) {
  						CondorErrMsg+="; failed to relocate ad on modification";
  						return( false );
  					}
  					partitionedViews[sig] = newPartition;
  				}
  			}
  		}
  
  			// send modification notification to all subordinate children
  		SubordinateViews::iterator xi;
  		for( xi=subordinateViews.begin( ); xi!=subordinateViews.end( ); xi++ ){
  			if( !(*xi)->ClassAdModified( coll, key, mad ) ) {
  				return( false );
  			}
  		}
  	} else if( !wasMember && match ) {
  			// wasn't a member, but now is --- insert the ad
  		rval = ClassAdInserted( coll, key, mad );
  	} else if( wasMember && !match ) {
  			// was a member, but now isnt --- delete the ad
  		ClassAdDeleted( coll, key, mad );
  		rval = true;
  	} else {
  		// wasn't a member and still isn't --- nothing to do
  	}
  
  	oldAdSignature.erase( oldAdSignature.begin( ), oldAdSignature.end( ) );
  	if( !rval ) {
  		CondorErrMsg += "; failed to modify ad";
  	}
  	return( rval );
  }
  
  
  void View::
  ClassAdDeleted( ClassAdCollection *coll, const string &key, 
  	ClassAd *ad )
  {
  	ViewMembers::iterator	vmi;
  
  		// check if the classad is in the view
  	if( memberIndex.find( key ) == memberIndex.end( ) ) {
  			// not in view; nothing to do
  		return;
  	}
  
  		// delete from member index and member list
  	vmi = memberIndex[key];
  	memberIndex.erase( key );
  	viewMembers.erase( vmi );
  
  		// delete from every subordinate child view
  	SubordinateViews::iterator	xi;
  	for( xi = subordinateViews.begin( ); xi != subordinateViews.end( ); xi++ ) {
  		(*xi)->ClassAdDeleted( coll, key, ad );
  	}
  
  		// delete from partition view
  	string signature = makePartitionSignature( ad );
  	if( !signature.empty( ) ) {
  		PartitionedViews::iterator mi = partitionedViews.find( signature );
  		if( mi == partitionedViews.end( ) ) {
  				// an internal error
  			CLASSAD_EXCEPT( "classad %s doesn't have a partition", signature.c_str( ) );
  		}
  
  			// delete from the partition
  		mi->second->ClassAdDeleted( coll, key, ad );
  	}
  }
  
  
  string View::
  makePartitionSignature( ClassAd *ad )
  {
  	ClassAdUnParser		unparser;
  	ExprListIterator	itr;
  	string				signature;
      Value   			value;
  	ClassAd				*oad, *info;
  	const ExprList		*el = NULL;
  
  		// stash the old ad from the environment and insert the new ad in
  	oad = evalEnviron.RemoveRightAd( );
  	evalEnviron.ReplaceRightAd( ad );
  	if( !( info = evalEnviron.GetLeftAd( ) ) ) {
  		CLASSAD_EXCEPT( "internal error:  view doesn't have view info" );
  	}
  	vector<ExprTree*> exprs;
  	if( !info->EvaluateAttr( ATTR_PARTITION_EXPRS, value ) ||
  			!value.IsListValue( el ) ) {
  		evalEnviron.RemoveRightAd( );
  		return( string( "" ) );
  	}
  	el->GetComponents( exprs );
  	if( exprs.size( ) == 0 ) {
  		evalEnviron.RemoveRightAd( );
  		return( string( "" ) );
  	}
  
  		// go through the expression list and form a value vector
  	signature = "<|";
  	itr.Initialize( el );
  	while( !itr.IsAfterLast( ) ) {
  		itr.CurrentValue( value );
  		unparser.Unparse( signature, value );
  		signature += "|";
  		itr.NextExpr( );
      }
  	signature += ">";
  
  		// put the old ad back in the environmnet
  	evalEnviron.RemoveRightAd( );
  	evalEnviron.ReplaceRightAd( oad );
  
  		// return the computed signature
      return( signature );
  }
  
  
  bool View::
  Display( FILE *file )
  {
  	ViewMembers::iterator	vmi;
  	ClassAdUnParser			unparser;
  	Value					val;
  	ClassAd					*viewInfo;
  	string					buf;
  
  		// display view info
  	if( !( viewInfo = GetViewInfo( ) ) ) return( false );
  	unparser.Unparse( buf, viewInfo );
  	fprintf( file, "%s\n", buf.c_str( ) );
  	delete viewInfo;
  
  	for( vmi = viewMembers.begin( ); vmi != viewMembers.end( ); vmi++ ) {
  		vmi->GetKey( buf );
  		vmi->GetRankValue( val );
  		buf += ": ";
  		unparser.Unparse( buf, val );
  		fprintf( file, "%s\n", buf.c_str() );
  	}
  
  	return( true );
  }
  
  void View::
  GetSubordinateViewNames( vector<string>& views )
  {
  	SubordinateViews::iterator	itr;
  
  	views.clear( );
  	for( itr=subordinateViews.begin( ); itr!=subordinateViews.end( ); itr++ ) {
  		views.push_back( (*itr)->GetViewName( ) );
  	}
  }
  
  void View::
  GetPartitionedViewNames( vector<string>& views )
  {
  	PartitionedViews::iterator	itr;
  
  	views.clear( );
  	for( itr=partitionedViews.begin( ); itr!=partitionedViews.end( ); itr++ ) {
  		views.push_back( itr->second->GetViewName( ) );
  	}
  }
  
  
  bool ViewMemberLT::
  operator()( const ViewMember &vm1, const ViewMember &vm2 ) const
  {
  	bool                lessThan;
  
  	lessThan = vm1 < vm2;
  
  	return lessThan;
  }
  
  }
  