The SelectBoldElement function provides a reusable and flexible way to select objects or lists.
function SelectBoldElement( Root: TBoldElement; sOCL: string; Params: array of const;
EClass: ExceptClass; ErrMsg: string;
Container:TBoldElement=nil;
InPS: Boolean = False;
Vars: TBoldExternalVariableList = nil ): TBoldElement;
const
cLastElement = $FFFF;
var
ocl : string;
id : TBoldIndirectElement;
ReturnListElementX : Boolean;
Element : Integer;
begin
result := nil;
Element := 0;
ReturnListElementX := False;
if Root = nil then
Root := TBoldSystem.DefaultSystem;
Assert( not (Container is TBoldObject), 'SelectBoldElement cannot return BoldObjects in the container parameter' );
Assert( Assigned( Root ), 'Attempt to Select without an open system' );
id := TBoldIndirectElement.Create;
try
ocl := Format( sOCL, Params );
// This code allows this routine to transparently select a 'first' or 'last' object in the PS
if (UpperCase(Copy( ocl, Length(ocl)-6, 8)) = '->FIRST') and (InPS) then begin
SetLength( ocl, Length(ocl)-7);
ReturnListElementX := true;
end;
if (UpperCase(Copy( ocl, Length(ocl)-5, 8)) = '->LAST') and (InPS) then begin
SetLength( ocl, Length(ocl)-6);
ReturnListElementX := true;
Element := cLastElement;
end;
Root.EvaluateExpression( ocl, id, InPS, Vars );
// if nothing was selected, get out, either by raising the user-supplied error, or quietly
if (not Assigned( id.Value )) or
((id.Value is TBoldList) and (TBoldList(id.Value).Count = 0)) then
if Assigned(EClass) then
raise EClass.Create( ErrMsg )
else
Exit; // Nothing to return, get out
// Owned values can't be returned in result, so must have a container, unless this is an InPS selection (ReturnListElementX)
if id.OwnsValue and
(not Assigned( Container )) and
(not ReturnListElementX) then
// You may want to replace this error class with something more specific to make it easier to trap
raise Exception.CreateFmt( 'SelectBoldElement cannot return selected type of %s', [id.Value.ClassName] );
if id.Value is TBoldObject then begin
// BoldObjects are owned by the system and can only go back in result
result := id.Value;
end else if id.Value is TBoldObjectReference then begin
// SingleLinks hold owned objects that go back in result
result := (id.Value as TBoldObjectReference).BoldObject;
end else if (id.Value is TBoldList) then begin
// Some lists are owned and can go back in result
if not id.OwnsValue then
result := id.Value;
// An InPS selection always returns a list, if the caller wanted the first element, this will be set
if ReturnListElementX then begin
Assert( TBoldList(id.Value).Count > 0, 'Logic failure, SelectBoldElement' );
if Element = cLastElement then
result := TBoldList(id.Value)[TBoldList(id.Value).Count-1]
else
result := TBoldList(id.Value)[0]; // The case of zero count was already taken care of
end else
// If the user has passed a container for the result, put it there
if (Container is TBoldList) then
TBoldList(Container).AddList( TBoldList(id.Value) );
end else if (id.Value is TBoldAttribute) then begin
if Assigned( Container ) and (Container is TBoldAttribute) then
// this depends on the caller to get the classes right, will raise an exception if incorrect
TBoldAttribute( Container ).Assign( id.Value );
result := id.Value;
end;
finally
id.Free;
end;
end;
SelectBoldElement can be used directly, but it is useful to create wrappers to simplify calling it. For instance, if one often needed to retrieve integer values from a simple select, a function like the following would be useful:
function SelectInteger( sOCL: string; Default:Integer=0 ): Integer;
var
anInt : TBAInteger;
begin
anInt := TBAInteger.Create;
try
try
SelectBoldElement( nil, sOCL, [], Exception, '', anInt );
result := anInt.AsInteger;
except
result := Default;
end;
finally
anInt.Free;
end;
end;
// This example searches a product database by UPC and PLU, demonstrating
// the use of the EClass parameter.
class function TProduct.GetProduct( PLU, UPC :string; ):TProduct;
const
cPLUOCL = 'Product.allInstances->select( plu =''%s'')->first';
cUPCOCL = 'Product.allInstances->select( upc =''%s'')->first';
begin
// attempt to locate by UPC first
result := SelectBoldElement( nil, cUPCOCL, [ UPC ],
nil, '') as TProduct;
if not Assigned( result ) then
// Didn't find the UPC, try the PLU
result := SelectBoldElement( nil, cPLUOCL, [ PLU],
EProductNotFound, 'Product not found') as TProduct;
// This one includes an exception class, so if the product still isn't found,
// the EProductNotFound error will be raised and handled elsewhere
end;
// This silly method of handling permissions demonstrates selection using
// a root other than the system.
function TEmployee.GetPermissions( Category: string ): TPermissionList;
const
cOCL = 'permissions->select( category = ''%s'' )';
begin
SelectBoldElement( self, cOCL, [Category], nil, '', result );
end;

Delicious
Digg
Google
Yahoo