In previous posts, we have been looking at the ‘AlignmentEntityCollection’ class and how to use the alignment entity objects. We found a couple of areas where the use of the API was not intuitive. We saw how, by default, the ‘AlignmentEntityCollection’ enumerates entities by the ‘EntityId’, which may or may not be the order of the entities within the alignment. We also saw how the ‘AlignmentEntity’ class type does not indicate the type of entity, and how classes are reused for different entity types.We explained how the correct way to identify an ‘AlignmentEntity’ type is evaluating its ‘EntityType’ property, as opposed to its class type. Today, we will look at ways to adapt the design to provide a different interface, which may be more suited for certain requirements.
Tell, Don’t Ask
One of the problems with the ‘AlignmentEntity’ class hierarchy is that it violates an important Object Oriented Design principle “Tell, Don’t Ask’. “Tell, Don’t Ask” is a principle, which prefers that an ‘objectA’ tells an ‘objectB’ to do something, rather than query its state and make a decision on what to do. Many API’s violate this principle for a very simple reason; they want to expose as much information as possible to insure users can implement their own processes and extend the functionality. The results are API’s that are hard to use because users need to understand the details of the implementation and client code full of conditional statements trying to figure out what API objects can do.
The ‘Adapter Pattern’ is used to convert the interface of a class/system into a different interface that adapts to the client needs.We will start by adapting the current Civil 3D API interface to a design that removes the need to ask the entities for their type. To achieve this goal, we define a base class ‘AAlignmentEntity’ (the first ‘A’ indicates that the class is abstract), and we derive entity classes from it. The following diagram shows the design.
The diagram does not show all the classes in the hierarchy, but you get the idea. We will implement an alignment entity adapter for each alignment entity type. Common functionality will be implemented in the base class ‘AAlignmentEntity’.
C#
/// <summary>
/// Abstract base class for all alignment entities.
/// </summary>
/// <para>
/// This class serves as a wrapper for alignment entity objects.
/// The class it self is abstract so it cannot be instantiated,
/// but sub classes must provide the wrapped entity, which will
/// be managed in this class.
/// </para>
public abstract class AAlignmentEntity{internal AAlignmentEntity()
{m_TheEntity = null;
}/// <summary>
/// Returns whether the wrapper entity is valid (has been assigned).
/// </summary>
public bool IsValid{get { return m_TheEntity != null; }}/// <summary>
/// Writes the alignment entity information to the specified writer.
/// </summary>
/// <param name="writer">Writer to which write the inforamtion.</param>
public void WriteInfo(IAlignmentEntityInfoWriter writer){writeCommonInfo(writer);WriteCustomInfo(writer);writer.EntityInfoDone();}/// <summary>
/// Assigns the wrapped entity.
/// </summary>
/// <param name="entity">Entity to wrap.</param>
internal void AssignEntity(AlignmentEntity entity){m_TheEntity = entity;}/// <summary>
/// This method must be implemented in derived classes to write
/// the custom entity information.
/// </summary>
/// <param name="writer"></param>
protected abstract void WriteCustomInfo(IAlignmentEntityInfoWriter writer);private void writeCommonInfo(IAlignmentEntityInfoWriter writer){writer.WriteEntityId(m_TheEntity.EntityId);writer.WriteWrappedEntityClassType(m_TheEntity.GetType());writer.WriteSubEntityCount(m_TheEntity.SubEntityCount);}private AlignmentEntity m_TheEntity;
}
VB.NET
''' <summary>
''' Abstract base class for all alignment entities.
''' </summary>
''' <para>
''' This class serves as a wrapper for alignment entity objects.
''' The class it self is abstract so it cannot be instantiated,
''' but sub classes must provide the wrapped entity, which will
''' be managed in this class.
''' </para>
Public MustInherit Class AAlignmentEntityFriend Sub New()m_TheEntity = Nothing
End Sub''' <summary>
''' Returns whether the wrapper entity is valid (has been assigned).
''' </summary>
Public ReadOnly Property IsValid() As BooleanGet
Return m_TheEntity IsNot NothingEnd GetEnd Property''' <summary>
''' Writes the alignment entity information to the specified writer.
''' </summary>
''' <param name="writer">Writer to which write the inforamtion.</param>
Public Sub WriteInfo(writer As IAlignmentEntityInfoWriter)writeCommonInfo(writer)WriteCustomInfo(writer)writer.EntityInfoDone()End Sub''' <summary>
''' Assigns the wrapped entity.
''' </summary>
''' <param name="entity">Entity to wrap.</param>
Friend Sub AssignEntity(entity As AlignmentEntity)m_TheEntity = entityEnd Sub''' <summary>
''' This method must be implemented in derived classes to write
''' the custom entity information.
''' </summary>
''' <param name="writer"></param>
Protected MustOverride Sub WriteCustomInfo( _writer As IAlignmentEntityInfoWriter)
Private Sub writeCommonInfo(writer As IAlignmentEntityInfoWriter)writer.WriteEntityId(m_TheEntity.EntityId)writer.WriteWrappedEntityClassType(m_TheEntity.[GetType]())
writer.WriteSubEntityCount(m_TheEntity.SubEntityCount)End SubPrivate m_TheEntity As AlignmentEntityEnd Class
The ‘AAlignmentEntity’ class declares a ‘WriteInfo()’ method that we will use to display the information about each entities in an ‘Alignment’. The implementation calls ‘writeCommonInfo()’, which will write the common information, and then calls ‘WriteCustomInfo()’, which is a protected abstract method that derived classes must implement to write any custom information. Here is a sample implementation in a derived entity class.
C#
protected override void WriteCustomInfo(IAlignmentEntityInfoWriter writer){writer.WriteCurveGroupName("Line");
}
VB.NET
Protected Overrides Sub WriteCustomInfo( _writer As IAlignmentEntityInfoWriter)
writer.WriteCurveGroupName("Line")
End Sub
Centralizing Conditional Code
Violating the “Tell, Don’t Ask” principle causes a “conditional mess” in client code. That happens when our code constantly queries objects to verify their state. The code ends up full of ‘if/else’ and ‘switch’ statements and becomes hard to follow. Our adapters will help us with that, but we still need conditional code to decide what entity adapter we should create. The ‘Factory Method Pattern’ can help us achieve this goal. There are different ways to implement ‘Factory Method’, but I like to extract the logic into a separate factory class.
C#
/// <summary>
/// Factory class that creates the wrapping adapters for alignment
/// entity objects.
/// </summary>
internal class AlignmentEntityWrapperFactory{/// <summary>
/// Creates an AAlignmentEntity wraping an alignment entity.
/// </summary>
/// <param name="entity">Entity to be wrapped.</param>
/// <returns>A wrapper AAlignmentEntity object.</returns>
public static AAlignmentEntity WrapEntity(AlignmentEntity entity){AAlignmentEntity wrapped = CreateWrapper(entity.EntityType);wrapped.AssignEntity(entity);return wrapped;
}/// <summary>
/// Factory method that creates the correct wrapper for a specified
/// entity type.
/// </summary>
/// <remarks>
/// This method was provided to be able to test the correct wrapper
/// class is created for all supported entity types. Users should
/// not call this method directly because it returns an empty
/// wrapper. Instead, users should call WrapEntity(), which
/// initializes the wrapper after is created.
/// </remarks>
/// <param name="type"></param>
/// <returns></returns>
internal static AAlignmentEntity CreateWrapper(AlignmentEntityType type){switch(type)
{case AlignmentEntityType.Line:
return new AlignmentEntityLine();case AlignmentEntityType.Arc:
return new AlignmentEntityCurve();case AlignmentEntityType.Spiral:
return new AlignmentEntitySpiral();case AlignmentEntityType.SpiralCurveSpiral:
return new AlignmentEntitySCS();case AlignmentEntityType.SpiralLineSpiral:
return new AlignmentEntitySLS();case AlignmentEntityType.SpiralLine:
return new AlignmentEntitySL();case AlignmentEntityType.LineSpiral:
return new AlignmentEntityLS();case AlignmentEntityType.SpiralCurve:
return new AlignmentEntitySC();case AlignmentEntityType.CurveSpiral:
return new AlignmentEntityCS();case AlignmentEntityType.SpiralSpiralCurveSpiralSpiral:
return new AlignmentEntitySSCSS();case AlignmentEntityType.SpiralCurveSpiralCurveSpiral:
return new AlignmentEntitySCSCS();case AlignmentEntityType.SpiralCurveSpiralSpiralCurveSpiral:
return new AlignmentEntitySCSSCS();case AlignmentEntityType.SpiralSpiral:
return new AlignmentEntitySS();case AlignmentEntityType.SpiralSpiralCurve:
return new AlignmentEntitySSC();case AlignmentEntityType.CurveSpiralSpiral:
return new AlignmentEntityCSS();// Not used for now.
// case AlignmentEntityType.MultipleSegments:
//
case AlignmentEntityType.CurveLineCurve:
return new AlignmentEntityCLC();case AlignmentEntityType.CurveReverseCurve:
return new AlignmentEntityCRC();case AlignmentEntityType.CurveCurveReverseCurve:
return new AlignmentEntityCCRC();default:
throw new NotImplementedException("Specified entity wrapper not implemented.");
}}}
VB.NET
Friend Class AlignmentEntityWrapperFactory''' <summary>
''' Creates an AAlignmentEntity wraping an alignment entity.
''' </summary>
''' <param name="entity">Entity to be wrapped.</param>
''' <returns>A wrapper AAlignmentEntity object.</returns>
Public Shared Function WrapEntity(entity As AlignmentEntity) _As AAlignmentEntity
Dim wrapped As AAlignmentEntity = CreateWrapper(entity.EntityType)wrapped.AssignEntity(entity)Return wrapped
End Function''' <summary>
''' Factory method that creates the correct wrapper for a specified
''' entity type.
''' </summary>
''' <remarks>
''' This method was provided to be able to test the correct wrapper
''' class is created for all supported entity types. Users should
''' not call this method directly because it returns an empty
''' wrapper. Instead, users should call WrapEntity(), which
''' initializes the wrapper after is created.
''' </remarks>
''' <param name="type"></param>
''' <returns></returns>
Friend Shared Function CreateWrapper(type As AlignmentEntityType) _As AAlignmentEntity
Select Case typeCase AlignmentEntityType.Line
Return New AlignmentEntityLine()Case AlignmentEntityType.Arc
Return New AlignmentEntityCurve()Case AlignmentEntityType.Spiral
Return New AlignmentEntitySpiral()Case AlignmentEntityType.SpiralCurveSpiral
Return New AlignmentEntitySCS()Case AlignmentEntityType.SpiralLineSpiral
Return New AlignmentEntitySLS()Case AlignmentEntityType.SpiralLine
Return New AlignmentEntitySL()Case AlignmentEntityType.LineSpiral
Return New AlignmentEntityLS()Case AlignmentEntityType.SpiralCurve
Return New AlignmentEntitySC()Case AlignmentEntityType.CurveSpiral
Return New AlignmentEntityCS()Case AlignmentEntityType.SpiralSpiralCurveSpiralSpiral
Return New AlignmentEntitySSCSS()Case AlignmentEntityType.SpiralCurveSpiralCurveSpiral
Return New AlignmentEntitySCSCS()Case AlignmentEntityType.SpiralCurveSpiralSpiralCurveSpiral
Return New AlignmentEntitySCSSCS()Case AlignmentEntityType.SpiralSpiral
Return New AlignmentEntitySS()Case AlignmentEntityType.SpiralSpiralCurve
Return New AlignmentEntitySSC()Case AlignmentEntityType.CurveSpiralSpiral
Return New AlignmentEntityCSS()' Not used for now.
' case AlignmentEntityType.MultipleSegments:
'
Case AlignmentEntityType.CurveLineCurve
Return New AlignmentEntityCLC()Case AlignmentEntityType.CurveReverseCurve
Return New AlignmentEntityCRC()Case AlignmentEntityType.CurveCurveReverseCurve
Return New AlignmentEntityCCRC()Case ElseThrow New NotImplementedException("Specified entity wrapper not implemented.")
End SelectEnd FunctionEnd Class
The ‘AlignmentEntityWrapperFactory’ class responsibility is to create the entity adapters and initialize them using the underlying entity from the Civil 3D API.
Processing Alignment Entities
As we have seen in a previous post, There are different ways to iterate through the alignment entities. In our samples, we saw how by default the ‘AlignmentEntityCollection’ iterated through the entities in the order they were created and added to the alignment, but we could also use the ‘GetEntityByOrder()’ method to iterate from the beginning of the alignment to the end. In our sample commands, we want to iterate through the entities and display information about them. In the future, we may want to add other functionality that involves iterating through the entities, so we are going to implement an ‘AlignmentEntityProcessor’ class that will provide methods to do the processing. Since there are different ways to iterate through the entities and we want the flexibility to change the behavior, we will use the ‘Strategy Pattern’ to customize how we iterate through them.
The ‘Strategy Pattern’ encapsulates an object behavior, in our case the iteration through the alignment entities, into a policy set of classes, so the behavior can be modified by assigning a different policy. The ‘IAlignmentEntityEnumerationPolicy’ interface defines the policy. This interface implements ‘IEnumerable (of AAlignmentEntity)’, which provides the interface to iterate through the entities.
C#
public interface IAlignmentEntityEnumerationPolicy :IEnumerable<AAlignmentEntity>{void Initialize(AlignmentEntityCollection entities);}
VB.NET
Public Interface IAlignmentEntityEnumerationPolicyInherits IEnumerable(Of AAlignmentEntity)Sub Initialize(entities As AlignmentEntityCollection)End Interface
We provide two implementations of the “IAlignmentEntityEnumerationPolicy’ interface. The first one; ‘ByEntityIdEnumerationPolicy’ iterates through the entities in order of creation (of ID).
C#
/// <summary>
/// Enumeration policy that enumerates the alignment entities by
/// order of ID.
/// </summary>
public class ByEntityIdEnumerationPolicy : IAlignmentEntityEnumerationPolicy{/// <summary>
/// Initializes the policy object with the current entity collection
/// for the alignment.
/// </summary>
/// <param name="entities">Entities to enunmerate.</param>
public void Initialize(AlignmentEntityCollection entities){m_TheEntities = entities;}/// <summary>
/// Returns an alignment entity enumerator.
/// </summary>
/// <returns>Returns the enumerator object.</returns>
public IEnumerator<AAlignmentEntity> GetEnumerator()
{return new ByEntityIdEnumerator(m_TheEntities);}/// <summary>
/// Returns an alignment entity enumerator.
/// </summary>
/// <returns>Returns the enumerator object.</returns>
System.Collections.IEnumeratorSystem.Collections.IEnumerable.GetEnumerator(){return new ByEntityIdEnumerator(m_TheEntities);}private AlignmentEntityCollection m_TheEntities;
}/// <summary>
/// Enumerator object that enumerates entities by ID order.
/// </summary>
public class ByEntityIdEnumerator : IEnumerator<AAlignmentEntity>{/// <summary>
/// Initialzies the enumerator using the entity collection.
/// </summary>
/// <param name="entities">Alignment entity collection to enumerate.
/// </param>
internal ByEntityIdEnumerator(AlignmentEntityCollection entities)
{m_TheEntities = entities;m_Enumerator = m_TheEntities.GetEnumerator();}/// <summary>
/// Returns the current entity.
/// </summary>
public AAlignmentEntity Current
{get {
return AlignmentEntityWrapperFactory.WrapEntity(
m_Enumerator.Current);}}/// <summary>
/// Disposes the object.
/// </summary>
public void Dispose(){m_Enumerator.Dispose();m_TheEntities.Dispose();}/// <summary>
/// Returns the current entity as an object.
/// </summary>
object System.Collections.IEnumerator.Current
{get {
return AlignmentEntityWrapperFactory.WrapEntity(
m_Enumerator.Current);}}/// <summary>
/// Moves to the next entity.
/// </summary>
/// <returns>Returns true if there is an entity left or false
/// otherwise.</returns>
public bool MoveNext(){return m_Enumerator.MoveNext();
}/// <summary>
/// Resets the enumerator.
/// </summary>
public void Reset(){m_Enumerator.Reset();}private AlignmentEntityCollection m_TheEntities;
private IEnumerator<AlignmentEntity> m_Enumerator;
}
VB.NET
''' <summary>
''' Enumeration policy that enumerates the alignment entities by
''' order of ID.
''' </summary>
Public Class ByEntityIdEnumerationPolicyImplements IAlignmentEntityEnumerationPolicy
''' <summary>
''' Initializes the policy object with the current entity collection
''' for the alignment.
''' </summary>
''' <param name="entities">Entities to enunmerate.</param>
Public Sub Initialize(entities As AlignmentEntityCollection) _Implements IAlignmentEntityEnumerationPolicy.Initialize
m_TheEntities = entitiesEnd Sub''' <summary>
''' Returns an alignment entity enumerator.
''' </summary>
''' <returns>Returns the enumerator object.</returns>
Public Function GetEnumerator() As IEnumerator(Of AAlignmentEntity) _Implements IAlignmentEntityEnumerationPolicy.GetEnumerator
Return New ByEntityIdEnumerator(m_TheEntities)End Function''' <summary>
''' Returns an alignment entity enumerator.
''' </summary>
''' <returns>Returns the enumerator object.</returns>
Private Function System_Collections_IEnumerable_GetEnumerator() _As System.Collections.IEnumerator _
Implements System.Collections.IEnumerable.GetEnumerator
Return New ByEntityIdEnumerator(m_TheEntities)End FunctionPrivate m_TheEntities As AlignmentEntityCollectionEnd Class''' <summary>
''' Enumerator object that enumerates entities by ID order.
''' </summary>
Public Class ByEntityIdEnumeratorImplements IEnumerator(Of AAlignmentEntity)''' <summary>
''' Initialzies the enumerator using the entity collection.
''' </summary>
''' <param name="entities">Alignment entity collection to enumerate.
''' </param>
Friend Sub New(entities As AlignmentEntityCollection)m_TheEntities = entitiesm_Enumerator = m_TheEntities.GetEnumerator()End Sub''' <summary>
''' Returns the current entity.
''' </summary>
Public ReadOnly Property Current() As AAlignmentEntity _Implements IEnumerator(Of AAlignmentEntity).CurrentGet
Return AlignmentEntityWrapperFactory.WrapEntity( _
m_Enumerator.Current)End GetEnd Property''' <summary>
''' Disposes the object.
''' </summary>
Public Sub Dispose() Implements IDisposable.Disposem_Enumerator.Dispose()m_TheEntities.Dispose()End Sub''' <summary>
''' Returns the current entity as an object.
''' </summary>
Private ReadOnly Property System_Collections_IEnumerator_Current() _As Object Implements System.Collections.IEnumerator.CurrentGet
Return AlignmentEntityWrapperFactory.WrapEntity( _
m_Enumerator.Current)End GetEnd Property''' <summary>
''' Moves to the next entity.
''' </summary>
''' <returns>Returns true if there is an entity left or false
''' otherwise.</returns>
Public Function MoveNext() As Boolean _Implements System.Collections.IEnumerator.MoveNext
Return m_Enumerator.MoveNext()
End Function''' <summary>
''' Resets the enumerator.
''' </summary>
Public Sub Reset() Implements System.Collections.IEnumerator.Resetm_Enumerator.Reset()End SubPrivate m_TheEntities As AlignmentEntityCollectionPrivate m_Enumerator As IEnumerator(Of AlignmentEntity)End Class
The implementation of ‘ByEntityIdEnumerationPolicy’ uses a ‘ByEntityIdEnumerator’ class that enumerates the entities in order of ID (or creation). The implementation of ‘BySequenceEnumerationPolicy’ is very similar; although, we will use ‘GetEntityByOrder()’ to access the entity.
C#
/// <summary>
/// Enumeration policy that enumerates the alignment entities by
/// sequence in the alignment.
/// </summary>
public class BySequenceEnumerationPolicy : IAlignmentEntityEnumerationPolicy{/// <summary>
/// Initializes the policy from the alignment entities.
/// </summary>
/// <param name="entities">Entities to enumerate.</param>
public void Initialize(AlignmentEntityCollection entities){m_TheEntities = entities;}/// <summary>
/// Returns enumerator.
/// </summary>
/// <returns>Returns enumerator object.</returns>
public IEnumerator<AAlignmentEntity> GetEnumerator()
{return new BySequenceEnumerator(m_TheEntities);}/// <summary>
/// Returns the enumerator.
/// </summary>
/// <returns>Returns enumerator object.</returns>
System.Collections.IEnumeratorSystem.Collections.IEnumerable.GetEnumerator(){return new BySequenceEnumerator(m_TheEntities);}private AlignmentEntityCollection m_TheEntities;
}/// <summary>
/// Enumerator object that enumerates the entities by sequence order.
/// </summary>
public class BySequenceEnumerator : IEnumerator<AAlignmentEntity>{/// <summary>
/// Initializes the class from the alignment entities.
/// </summary>
/// <param name="entities">Entities to enumerate</param>
internal BySequenceEnumerator(AlignmentEntityCollection entities)
{m_TheEntities = entities;m_EntityIndex = -1;}/// <summary>
/// Returns the current entity.
/// </summary>
public AAlignmentEntity Current
{get {
AlignmentEntity entity =m_TheEntities.GetEntityByOrder(m_EntityIndex);return AlignmentEntityWrapperFactory.WrapEntity(entity);
}}/// <summary>
/// Disposes the object.
/// </summary>
public void Dispose(){m_TheEntities = null;
m_EntityIndex = -1;}/// <summary>
/// Returns the current entity.
/// </summary>
object System.Collections.IEnumerator.Current
{get {
AlignmentEntity entity =m_TheEntities.GetEntityByOrder(m_EntityIndex);return AlignmentEntityWrapperFactory.WrapEntity(entity);
}}/// <summary>
/// Moves to the next entity.
/// </summary>
/// <returns>Returns true if there are entities remaining or
/// false otherwise.</returns>
public bool MoveNext(){m_EntityIndex++;return m_EntityIndex < m_TheEntities.Count;
}/// <summary>
/// Resets the enumerator.
/// </summary>
public void Reset(){m_EntityIndex = -1;}private AlignmentEntityCollection m_TheEntities;
private int m_EntityIndex;}
VB.NET
''' <summary>
''' Enumeration policy that enumerates the alignment entities by
''' sequence in the alignment.
''' </summary>
Public Class BySequenceEnumerationPolicyImplements IAlignmentEntityEnumerationPolicy
''' <summary>
''' Initializes the policy from the alignment entities.
''' </summary>
''' <param name="entities">Entities to enumerate.</param>
Public Sub Initialize(entities As AlignmentEntityCollection) _Implements IAlignmentEntityEnumerationPolicy.Initialize
m_TheEntities = entitiesEnd Sub''' <summary>
''' Returns enumerator.
''' </summary>
''' <returns>Returns enumerator object.</returns>
Public Function GetEnumerator() As IEnumerator(Of AAlignmentEntity) _Implements IAlignmentEntityEnumerationPolicy.GetEnumerator
Return New BySequenceEnumerator(m_TheEntities)End Function''' <summary>
''' Returns the enumerator.
''' </summary>
''' <returns>Returns enumerator object.</returns>
Private Function System_Collections_IEnumerable_GetEnumerator() _As System.Collections.IEnumerator _
Implements System.Collections.IEnumerable.GetEnumerator
Return New BySequenceEnumerator(m_TheEntities)End FunctionPrivate m_TheEntities As AlignmentEntityCollectionEnd Class''' <summary>
''' Enumerator object that enumerates the entities by sequence order.
''' </summary>
Public Class BySequenceEnumeratorImplements IEnumerator(Of AAlignmentEntity)''' <summary>
''' Initializes the class from the alignment entities.
''' </summary>
''' <param name="entities">Entities to enumerate</param>
Friend Sub New(entities As AlignmentEntityCollection)m_TheEntities = entitiesm_EntityIndex = -1End Sub''' <summary>
''' Returns the current entity.
''' </summary>
Public ReadOnly Property Current() As AAlignmentEntity _Implements IEnumerator(Of AAlignmentEntity).CurrentGet
Dim entity As AlignmentEntity = _m_TheEntities.GetEntityByOrder(m_EntityIndex)Return AlignmentEntityWrapperFactory.WrapEntity(entity)
End GetEnd Property''' <summary>
''' Disposes the object.
''' </summary>
Public Sub Dispose() Implements IDisposable.Disposem_TheEntities = Nothing
m_EntityIndex = -1End Sub''' <summary>
''' Returns the current entity.
''' </summary>
Private ReadOnly Property System_Collections_IEnumerator_Current() _As Object Implements System.Collections.IEnumerator.CurrentGet
Dim entity As AlignmentEntity = _m_TheEntities.GetEntityByOrder(m_EntityIndex)Return AlignmentEntityWrapperFactory.WrapEntity(entity)
End GetEnd Property''' <summary>
''' Moves to the next entity.
''' </summary>
''' <returns>Returns true if there are entities remaining or
''' false otherwise.</returns>
Public Function MoveNext() As Boolean _Implements System.Collections.IEnumerator.MoveNext
m_EntityIndex += 1Return m_EntityIndex < m_TheEntities.Count
End Function''' <summary>
''' Resets the enumerator.
''' </summary>
Public Sub Reset() Implements System.Collections.IEnumerator.Resetm_EntityIndex = -1End SubPrivate m_TheEntities As AlignmentEntityCollectionPrivate m_EntityIndex As IntegerEnd Class
The ‘BySequenceEnumerationPolicy’ class uses a ‘BySequenceEnumerator’ that iterates through the entity in sequential order.
Test Commands
The new implementation for the sample commands now can leverage of our new design. The code for the command class will look something like the following.
C#
public class AlignmentEntitiesCommandsColibra{[CommandMethod("CDS_WriteAlignmentEntitiesById")]
public void CDS_WriteAlignmentEntitiesById(){doWriteAlignmentEntities(new ByEntityIdEnumerationPolicy());
}[CommandMethod("CDS_WriteAlignmentEntitiesBySequence")]
public void CDS_WriteAlignmentEntitiesBySequence(){doWriteAlignmentEntities(new BySequenceEnumerationPolicy());
}private void doWriteAlignmentEntities(IAlignmentEntityEnumerationPolicy enumerationPolicy){Document current = DocumentManager.ActiveDocument;using (Transaction tr = current.StartTransaction())
{// First we select the alignment to write.
//
SingleObjectSelector<Alignment> selector =new SingleObjectSelector<Alignment>();
if (!selector.Select(current))
{_output.WriteMessage("\nNo Alignment was selected.");
return;
}// We create the processor and assign the policy.
//
AlignmentEntityProcessor processor =new AlignmentEntityProcessor(selector.SelectedId);
processor.EnumerationPolicy = enumerationPolicy;// We create the writer and write the information to it.
//
DocumentOutputAlignmentEntityInfoWriter writer =new DocumentOutputAlignmentEntityInfoWriter(current);
processor.WriteInfo(writer);}}private Editor _output
{get
{return Autodesk.AutoCAD.ApplicationServices.Application
.DocumentManager.MdiActiveDocument.Editor;}}}
VB.NET
Public Class AlignmentEntitiesCommandsColibra<CommandMethod("CDS_WriteAlignmentEntitiesById")> _
Public Sub CDS_WriteAlignmentEntitiesById()doWriteAlignmentEntities(New ByEntityIdEnumerationPolicy())
End Sub<CommandMethod("CDS_WriteAlignmentEntitiesBySequence")> _
Public Sub CDS_WriteAlignmentEntitiesBySequence()doWriteAlignmentEntities(New BySequenceEnumerationPolicy())
End SubPrivate Sub doWriteAlignmentEntities(enumerationPolicy _
As IAlignmentEntityEnumerationPolicy)
Dim current As Document = DocumentManager.ActiveDocumentUsing tr As Transaction = current.StartTransaction()' First we select the alignment to write.
'
Dim selector As New SingleObjectSelector(Of Alignment)()If Not selector.[Select](current) Then_output.WriteMessage(vbLf & "No Alignment was selected.")
Return
End If' We create the processor and assign the policy.
'
Dim processor As New AlignmentEntityProcessor( _
selector.SelectedId)
processor.EnumerationPolicy = enumerationPolicy' We create the writer and write the information to it.
'
Dim writer As New DocumentOutputAlignmentEntityInfoWriter( _
current)
processor.WriteInfo(writer)End UsingEnd SubPrivate ReadOnly Property _output() As EditorGet
Return Autodesk.AutoCAD.ApplicationServices.Application _
.DocumentManager.MdiActiveDocument.Editor
End GetEnd PropertyEnd Class
The Implementation Source Code
The implementation of the commands show some classes the we have not discussed. Even the code in this post refers to some interfaces that we have not covered. The complete implementation can be found in Colibra 1.0.3.0, which you can download and review.
AutoCAD Civil 3D 2012 adds support for some additional entities that were not supported in the 2011 version. For that reason, I updated the drawings to 2012 and ported the Colibra project to use AutoCAD Civil 3D 2012 and Visual Studio 2010. Let me know if you run into any issues.
Comments
You can follow this conversation by subscribing to the comment feed for this post.