/// SymbolNameAttribute:
///
/// Constrains an argument to a LispFunction to be a string that
/// is the user- or application-defined name of a symbol table
/// entry (e.g., a layer, block, etc., existing or proposed).
///
/// In addition to validating string names, this attribute can
/// accept and validate the entity name of a symbol table entry,
/// by setting the AcceptObjectId property to true. When that
/// property is true, the caller can pass either the name of the
/// symbol table entry as a string, or its entity name (in cases
/// where the entry exists). The symbol table entry must reside
/// in the database of the active document. To work with other
/// databases, a type derived from this class can override the
/// Database property to return the database that is used.
///
/// This class can be used directly by specifying the managed
/// wrapper type of the SymbolTableRecord for the given table,
/// or the included LayerNameAttribute and BlockNameAttribute
/// specializations can be used as well. The BlockNameAttribute
/// specialization adds block-specific validation rules to the
/// validation rules provided by this type (which are common to
/// all types of symbol table entries).
///
/// Specializaions for other symbol tables can also be easily
/// implemented in just a few lines of code.
public class SymbolNameAttribute : StringAttribute
{
string dxfName = string.Empty;
Type recordType = null;
RXClass recordClass = null;
ObjectId objectId = ObjectId.Null;
Func<Database, ObjectId> accessor = null;
string entryName = null;
static Dictionary<Type, Func<Database, ObjectId>> accessors =
new Dictionary
<Type, Func
<Database, ObjectId
>>();
static SymbolNameAttribute()
{
accessors
[typeof( BlockTableRecord
)] = db
=> db
.BlockTableId; accessors
[typeof( LayerTableRecord
)] = db
=> db
.LayerTableId; accessors
[typeof( LinetypeTableRecord
)] = db
=> db
.LinetypeTableId; accessors
[typeof( ViewTableRecord
)] = db
=> db
.ViewTableId; accessors
[typeof( ViewportTableRecord
)] = db
=> db
.ViewportTableId; accessors
[typeof( DimStyleTableRecord
)] = db
=> db
.DimStyleTableId; accessors
[typeof( RegAppTableRecord
)] = db
=> db
.RegAppTableId; accessors
[typeof( TextStyleTableRecord
)] = db
=> db
.TextStyleTableId; accessors
[typeof( UcsTableRecord
)] = db
=> db
.UcsTableId; }
SymbolNameAttribute( int position )
: base( position )
{
base.AcceptEmpty = false;
base.AcceptAllWhiteSpace = false;
}
public SymbolNameAttribute( int position, Type symbolTableRecordType )
: this( position )
{
if( !symbolTableRecordType
.IsSubclassOf( typeof( SymbolTableRecord
) ) ) InternalException( "Requires a type derived from SymbolTableRecord" );
this.recordType = symbolTableRecordType;
this.dxfName = symbolTableRecordType.Name.ParseLeft( "TableRecord" ).ToUpper();
this.recordClass = RXClass.GetClass( symbolTableRecordType );
this.accessor = accessors[symbolTableRecordType];
// this.IsDependent = Condition.False;
}
public override bool IsSupportedType( short typeCode )
{
return base.IsSupportedType( typeCode )
|| AcceptObjectId && Exists != Condition.False && typeCode == RTENAME;
}
protected ObjectId SymbolTableId
{
get
{
return GetSymbolTableId( this.Database );
}
}
protected virtual ObjectId GetSymbolTableId( Database database )
{
if( database == null )
throw new ArgumentNullException
( "No database" ); if( !database.BlockTableId.IsValid )
throw new ArgumentException
( "Invalid database" ); ObjectId id = accessor( database );
if( id.IsNull || !id.IsValid )
throw new InvalidOperationException
( "Null or Invalid Symbol Table ObjectId" ); return id;
}
/// <summary>
/// Condition.False = disallows vertical bar separator in name
/// (e.g., xref dependents) enforced on both names and objectIds.
/// Condition.True = require vertical bar separator in name.
/// Condition.None = accept but not require vertical bar separator.
/// </summary>
public Condition IsDependent
{
get;
set;
}
///<summary>
/// If true, the caller can supply either the name of the
/// symbol table entry, or its entity name (as returned
/// by the LISP (tblobjname) function). Default is false.
/// </summary>
public bool AcceptObjectId
{
get;
set;
}
protected virtual Database Database
{
get
{
return HostApplicationServices.WorkingDatabase;
}
}
/// <summary>
/// The ObjectId of the validated symbol table entry.
/// </summary>
public ObjectId ObjectId
{
get
{
CheckIsValid();
if( string.IsNullOrEmpty( this.entryName ) )
ArgumentException( "Argument not validated" );
if( objectId.IsNull )
{
{
using( SymbolTable tbl = (SymbolTable) SymbolTableId.Open( OpenMode.ForRead ) )
{
this.objectId = tbl[this.entryName];
}
}
}
if( this.objectId.IsNull || !this.objectId.IsValid )
throw new InvalidOperationException
( "Null or invalid symbol table ObjectId" ); return this.objectId;
}
}
protected void CheckIsValid()
{
if( !this.IsValid )
ArgumentException( "Argument has not been validated" );
}
protected virtual void ValidateObjectId( ObjectId id )
{
}
// This can get a string or an objectid
public override void Validate( TypedValue typedValue )
{
base.Validate( typedValue );
ObjectId id;
if( TryGetValue( typedValue, out id ) )
{
if( Exists == Condition.False )
ArgumentException( "Requires the name of a non-existing table entry" );
Validate( id );
}
else
{
string value = GetValue<string>( typedValue );
try
{
SymbolUtilityServices.ValidateSymbolName( value, IsDependent != Condition.False );
}
catch( System.Exception )
{
ArgumentException( "Invalid {0} name: {1}", dxfName, value );
}
this.entryName = value;
this.objectId = GetObjectId( value );
bool found = !this.objectId.IsNull;
ValidateExists( Exists, found, "{0} {1}", dxfName, value );
if( found )
this.entryName = ValidateSymbolTableRecord( this.objectId );
}
}
private void Validate( ObjectId id )
{
if( id.IsNull || !id.IsValid )
ArgumentException( "Null or invalid ename" );
if( id.ObjectClass != this.recordClass )
ArgumentException( "Requires the name or entity name of a {0}", this.dxfName );
if( id.Database != this.Database )
ArgumentException( "The specified {0} is from the wrong database", this.dxfName );
this.entryName = ValidateSymbolTableRecord( id );
this.objectId = id;
}
static RXClass symbolTableRecordClass
= RXClass
.GetClass( typeof( SymbolTableRecord
) );
protected virtual string ValidateSymbolTableRecord( ObjectId id )
{
if( id.IsNull || !id.IsValid )
ArgumentException( "Null or invalid ObjectId" );
if( !id.ObjectClass.IsDerivedFrom( symbolTableRecordClass ) )
ArgumentException( "Specified ObjectId is not a SymbolTableRecord" );
using( SymbolTableRecord rec = (SymbolTableRecord) id.Open( OpenMode.ForRead ) )
{
ValidateSymbolTableRecord( rec );
return rec.Name;
}
}
protected virtual void ValidateSymbolTableRecord( SymbolTableRecord rec )
{
ValidateCondition( IsDependent, rec.IsDependent, "externally-dependent entry" );
}
ObjectId GetObjectId( string name )
{
if( !this.objectId.IsNull )
return this.objectId;
ObjectId tableId = SymbolTableId;
if( !tableId.IsNull && tableId.IsValid )
{
using( SymbolTable tbl = (SymbolTable) tableId.Open( OpenMode.ForRead ) )
{
if( tbl.Has( name ) )
{
ObjectId recId = tbl[name];
if( !recId.IsErased )
{
this.objectId = recId;
return recId;
}
foreach( ObjectId entry in tbl )
{
using( SymbolTableRecord rec = (SymbolTableRecord) entry.Open( OpenMode.ForRead ) )
{
if( rec.Name.Equals( name, StringComparison.OrdinalIgnoreCase ) )
return entry;
}
}
}
}
}
return ObjectId.Null;
}
bool Find( string name )
{
ObjectId id = SymbolTableId;
if( !id.IsNull && id.IsValid )
{
using( SymbolTable tbl = (SymbolTable) id.Open( OpenMode.ForRead ) )
return tbl.Has( name );
}
return false;
}
}
/// Constrains an argument to be the name of a layer
/// or the entity name of the layer as returned by
/// the LISP (tblobjname) function
/// This is incomplete, and doesn't yet implement
/// the code that validates the included rules.
public class LayerNameAttribute : SymbolNameAttribute
{
public LayerNameAttribute( int position )
: base( position,
typeof( LayerTableRecord
) ) {
LayerTableRecord ltr = null;
}
/// Require or Reject frozen layers
public Condition IsFrozen
{
get;
set;
}
/// Require or Reject hidden layers
public Condition IsHidden
{
get;
set;
}
/// Require or Reject locked layers
public Condition IsLocked
{
get;
set;
}
/// Require or Reject non-visible layers
public Condition IsOff
{
get;
set;
}
/// Require or Reject plottable layers
public Condition IsPlottable
{
get;
set;
}
/// Require or Reject reconciled layers
public Condition IsReconciled
{
get;
set;
}
/// Require or Reject unreferenced layers
public Condition IsUsed
{
get;
set;
}
/// Require or Reject layers with property overrides
public Condition HasOverrides
{
get;
set;
}
public override Validate( TypedValue typedValue )
{
base.Validate( typedValue );
/// TODO....
}
}
/// Constrains an argument to be the name of a block
/// or the entity name of the block, as returned by
/// the LISP (tblobjname) function.
/// Includes rules for validating various properties
/// of BlockTableRecord. For example, to require an
/// argument to be the name or entity name of a block
/// but reject layout and xref blocks, you would use
/// the following attribute:
/// [LispFunction("somefunction")]
/// [BlockName( 0 , AcceptObjectId = true, IsLayout = Condition.MustBeFalse, IsXref = Condition.MustBeFalse )]
///
public class BlockNameAttribute : SymbolNameAttribute
{
public BlockNameAttribute( int position )
: base( position,
typeof( BlockTableRecord
) ) {
}
// Require/Disallow xref blocks
public Condition IsXref
{
get;
set;
}
// Require/Disallow xref overlay blocks
public Condition IsOverlay
{
get;
set;
}
// Require/Disallow anonymous blocks
public Condition IsAnonymous
{
get;
set;
}
// Require/Disallow dynamic blocks
public Condition IsDynamic
{
get;
set;
}
// Require/Disallow layout blocks
public Condition IsLayout
{
get;
set;
}
// Require/Disallow resolved xref blocks
public Condition IsResolved
{
get;
set;
}
// Require/Disallow loaded blocks
public Condition IsLoaded
{
get;
set;
}
protected override void ValidateSymbolTableRecord( SymbolTableRecord rec )
{
base.ValidateSymbolTableRecord( rec );
BlockTableRecord btr = rec as BlockTableRecord;
if( btr == null )
ArgumentException( "Object is not a BLOCK" );
bool xref = btr.IsFromExternalReference || btr.IsFromOverlayReference;
ValidateCondition( IsAnonymous, btr.IsAnonymous, "Anonymous block" );
ValidateCondition( IsDynamic, btr.IsDynamicBlock, "Dynamic block" );
ValidateCondition( IsLayout, btr.IsLayout, "Layout block" );
ValidateCondition( IsXref, btr.IsFromExternalReference, "External Reference" );
ValidateCondition( IsOverlay, btr.IsFromOverlayReference, "Overlay Reference" );
if( xref || btr.IsDependent )
{
ValidateCondition( IsResolved, btr.IsResolved, "Resolved external reference" );
ValidateCondition( IsLoaded, !btr.IsUnloaded, "Loaded external reference" );
}
}
}