unit Reflect.ReflectNode;

//
// ReflectNode is a specialized version of a TreeViews TreeNode. Using
// reflection, it adds leaf nodes as appropriate. For example, if a node
// is an assembly, leaf nodes are added that correspond to all modules that
// the assembly contains. These modules are then expanded to include type
// information.
//
// Written by Rick Ross (http://rick-ross.com)
//

interface

uses
  SysUtils,
  System.Reflection,
  System.Text,
  System.Windows.Forms;

type
  // This exception is thrown when any type that is located within the
  // Borland namespace is being ignored.
  EBorlandNamespace = Exception;

  ReflectTreeNode = class(TreeNode)
  private
    FAttributes        : string;
    FOtherInfo         : string;
    theType            : System.Type;
    theAssembly        : Assembly;
    theModule          : Module;
    theMethodInfo      : MethodInfo;
    theConstructorInfo : ConstructorInfo;
    thePropertyInfo    : PropertyInfo;
    theFieldInfo       : FieldInfo;
    theEventInfo       : EventInfo;

    // class variables
    class var
      FBindingFlags : BindingFlags;
      FHideBorNameSpace : boolean;

    // class static method(s)
    class function  GetDefaultBindingFlags : System.Reflection.BindingFlags; static;
    class procedure SetBindingFlags( bf : BindingFlags ); static;

    // normal methods
    procedure AddNode( anAssembly : Assembly; aModule : Module; aType : System.Type;
                       mi : MethodInfo; ci : ConstructorInfo; pi : PropertyInfo;
                       fi : FieldInfo; ei : EventInfo );

    function  BuildCustomAttributes( provider : ICustomAttributeProvider ) : string;
    function  BuildParmInfo(pi : array of ParameterInfo) : string;
    function  BuildVisibility(mb : MethodBase) : string;
    function  BuildMethodSignature(mi : MethodInfo) : string;
    function  GetType( curType : System.Type ) : string;

    procedure BuildClassOrInterfaceDef;

    function  BuildConstructor( ci : ConstructorInfo ) : string;
    function  BuildPropertyInfo( pinfo : PropertyInfo ) : string;
    function  BuildField( finfo : FieldInfo ) : string;
    function  BuildEvent( einfo : EventInfo ) : string;

    function  BuildIntrinsicAttributes( attrs : string ) : string;

    function  GetIntrinsicAttributes( atype : System.Type     ) : string; overload;
    function  GetIntrinsicAttributes( pinfo : PropertyInfo    ) : string; overload;
    function  GetIntrinsicAttributes( finfo : FieldInfo       ) : string; overload;
    function  GetIntrinsicAttributes( einfo : EventInfo       ) : string; overload;
    function  GetIntrinsicAttributes( mb    : MethodBase      ) : string; overload;

    function IsBorlandNamespace( atype : System.Type ) : boolean;
  public
    // constructors
    constructor Create( alabel      : string          ); overload;
    constructor Create( anAppDomain : AppDomain       ); overload;
    constructor Create( anAssembly  : Assembly        ); overload;
    constructor Create( aModule     : Module          ); overload;
    constructor Create( minfo       : MethodInfo      ); overload;
    constructor Create( pinfo       : PropertyInfo    ); overload;
    constructor Create( aType       : System.Type     ); overload;
    constructor Create( cinfo       : ConstructorInfo ); overload;
    constructor Create( finfo       : FieldInfo       ); overload;
    constructor Create( einfo       : EventInfo       ); overload;

    // regular properties
    property OtherInfo  : string read FOtherInfo;
    property Attributes : string read FAttributes;

    // class properties
    class property DefaultBindingFlags   : BindingFlags read GetDefaultBindingFlags;
    class property TheBindingFlags       : BindingFlags write SetBindingFlags;
    class property HideBorlandNamespace  : boolean      write FHideBorNameSpace;

  end;

implementation

//
// Returns the default binding flags. Feel free to change as appropriate
//
class function ReflectTreeNode.GetDefaultBindingFlags : System.Reflection.BindingFlags;
begin
  Result := BindingFlags.Public    or
            BindingFlags.NonPublic or
            BindingFlags.Instance  or
            BindingFlags.Static    or
            // comment DeclaredOnly to see all methods,
            // even inherited ones.
            BindingFlags.DeclaredOnly;
end;

class procedure ReflectTreeNode.SetBindingFlags( bf : BindingFlags );
begin
  FBindingFlags := bf;
end;

procedure ReflectTreeNode.AddNode( anAssembly : Assembly; aModule : Module; aType : System.Type;
                                    mi : MethodInfo; ci : ConstructorInfo; pi : PropertyInfo;
                                    fi : FieldInfo; ei : EventInfo );
begin
  self.theAssembly        := anAssembly;
  self.theModule          := aModule;
  self.theType            := aType;
  self.theMethodInfo      := mi;
  self.theConstructorInfo := ci;
  self.thePropertyInfo    := pi;
  self.theFieldInfo       := fi;
  self.theEventInfo       := ei;
end;

//
// BuildCustomAttributes retrieves any custom attributes from the given provider
//
function ReflectTreeNode.BuildCustomAttributes( provider : ICustomAttributeProvider ) : string;
var
  i : integer;
  ret : StringBuilder;
  custAttrib : array of System.Object;

begin
  // more information can be obtained by reflecting each of xthe custAttrib object
  ret := StringBuilder.Create('');

  custAttrib := provider.GetCustomAttributes(false);
  if (Length(custAttrib) > 0) then
  begin
    ret.Append('[ ');

    for i:=0 to Length(custAttrib)-1 do
    begin
      ret.Append(custAttrib[i].ToString());
      ret.Append(' ');
    end;
    ret.Append(']');
  end;

  Result := ret.ToString();
end;

//
// BuildParmInfo builds a string that represents a parameter list.
//

function ReflectTreeNode.BuildParmInfo(pi : array of ParameterInfo) : string;
var
  ret  : StringBuilder;
  i    : integer;
  name : string;
  len  : integer;

begin
  ret := StringBuilder.Create('');

  ret.Append('( ');

  len := Length(pi)-1;

  for i:=0 to len do
  begin
    //if (pi[i].IsIn) then
    //  ret.Append('in ');

    if (pi[i].IsOut) then
      ret.Append('var ');

    name := pi[i].Name;

    if (name = '') then
      name := 'p' + Int32(i+1).ToString();
    ret.Append( name );

    ret.Append( ' : ' + pi[i].ParameterType.FullName );

    if (i < len) then
      ret.Append( ', ');
  end;

  ret.Append(' )');
  Result := ret.ToString();
end;

//
// BuildVisibility determines the visibility of a method.
//
function ReflectTreeNode.BuildVisibility(mb : MethodBase) : string;
var
  ret : StringBuilder;

begin
  ret := StringBuilder.Create('');
  if (mb.IsAssembly) then
    ret.Append('private ');

  if (mb.IsPrivate) then
    ret.Append('strict private ');

  // FamilyOrAssembly = Protected
  if (mb.IsFamilyOrAssembly) then
    ret.Append('protected ');

  if (mb.IsFamily) then
    ret.Append('strict protected ');

  if (mb.IsPublic) then
    ret.Append('public ');

  if (mb.IsStatic) then
    ret.Append('class ');

  Result := ret.ToString();
end;

//
// BuildMethodSignature uses the information passed in to generate a string
// representing the method
//

function ReflectTreeNode.BuildMethodSignature(mi : MethodInfo) : string;
var
  ret     : StringBuilder;
  oi      : StringBuilder;
  IsFunc  : boolean;
  rettype : StringBuilder;

begin
  ret := StringBuilder.Create( BuildVisibility(mi) );
  oi  := StringBuilder.Create('');

  // is this a function?
  IsFunc := false;
  rettype := StringBuilder.Create(mi.ReturnType.Name);

  if (rettype.ToString().Equals('Void')) then
    ret.Append('procedure ')
  else
  begin
    ret.Append('function ');
    IsFunc := true;
  end;

  ret.Append(mi.Name);
  ret.Append( BuildParmInfo( mi.GetParameters() ));
  if IsFunc then
    ret.Append(' : (isfunc) ' + mi.ReturnType.Name + ';');

  if (mi.IsVirtual) then
    ret.Append('virtual; ');

  if (mi.IsAbstract) then
    ret.Append('abstract; ');

  if (mi.IsFinal) then
    ret.Append('final; ');

  if (IsFunc) then
    ret.Append(' ' + mi.ReturnType.Name + ' ');

  if (mi.IsConstructor) then
    oi.Append('constructor ');

  FOtherInfo := oi.ToString();

  Result := ret.ToString();
end;

//
// GetType returns a string that represents the given type.
//

function ReflectTreeNode.GetType( curType : System.Type ) : string;
var
  ret : string;

begin
  if (curType.IsArray) then
    ret := 'array '
  else if (curType.IsClass) then
    ret := 'class '
  else if (curType.IsCOMObject) then
    ret := 'Com Object '
  else if (curType.IsEnum) then
    ret := 'enum '
  else if (curType.IsInterface) then
    ret := 'interface '
  else if (curType.IsPointer) then
    ret := 'pointer '
  else if (curType.IsPrimitive) then
    ret := 'primative '
  else if (curType.IsValueType) then
    ret := 'value type '
  else
  begin
    ret := 'other/unknown? ';
    FOtherInfo := curType.FullName;
  end;

  if (curType.IsClass) then
    ret := curType.Name + ' = ' + ret + '( ' + curType.BaseType.FullName + ' )'
  else
    ret := ret + curType.FullName;

  Result := ret;
end;

//
// BuildConstructor returns a string representing the constructor
//

function ReflectTreeNode.BuildConstructor( ci : ConstructorInfo ) : string;
var
  ret : StringBuilder;
  oi  : StringBuilder;

begin
  ret := StringBuilder.Create( BuildVisibility(ci) );
  oi  := StringBuilder.Create('');

  ret.Append('Create');
  ret.Append( BuildParmInfo( ci.GetParameters() ));

  if (ci.IsVirtual) then
    ret.Append('virtual; ');

  if (ci.IsAbstract) then
    ret.Append('abstract; ');

  if (ci.IsFinal) then
    ret.Append('final; ');

  if (ci.IsSpecialName) then
    oi.Append('specialname ');

  if (ci.IsAssembly) then
    oi.Append('assembly ');

  if (ci.IsHideBySig) then
    oi.Append('hidebysig ');

  self.FOtherInfo := oi.ToString();
  Result := ret.ToString();
end;

//
// BuildClassOrInterface definition adds addtional nodes that represent
// fields, constructors, methods, properties and events.
//

procedure ReflectTreeNode.BuildClassOrInterfaceDef;
var
  constr   : array of ConstructorInfo;
  methods  : array of MethodInfo;
  propinfo : array of PropertyInfo;
  fields   : array of FieldInfo;
  events   : array of EventInfo;
  i        : integer;

begin
  // get the fields
  fields := theType.GetFields( FBindingFlags );
  for i:=0 to Length(fields)-1 do
  begin
    Self.Nodes.Add( ReflectTreeNode.Create( fields[i] ));
  end;

  if (theType.IsClass) then
  begin
    // get constructors
    constr := theType.GetConstructors( FBindingFlags );
    for i:=0 to Length(constr)-1 do
    begin
      self.Nodes.Add( ReflectTreeNode.Create( constr[i] ));
    end;
  end;

  methods := theType.GetMethods( FBindingFlags );
  for i:=0 to Length(methods)-1 do
  begin
    // skip property methods
    if (not (methods[i].Name.StartsWith('get_')) and (not methods[i].Name.StartsWith('set_'))) then
      self.Nodes.Add( ReflectTreeNode.Create( methods[i] ));
  end;

  propInfo := theType.GetProperties( FBindingFlags );
  for i:=0 to Length(propInfo)-1 do
  begin
    self.Nodes.Add( ReflectTreeNode.Create( propInfo[i] ));
  end;

  events := theType.GetEvents( FBindingFlags );
  for i:=0 to Length(events)-1 do
  begin
    self.Nodes.Add( ReflectTreeNode.Create( events[i] ) );
  end;
end;

//
// BuildPropertyInfo returns the string representation of a property
//

function ReflectTreeNode.BuildPropertyInfo( pinfo : PropertyInfo ) : string;
var
  ret : StringBuilder;
  mi  : MethodInfo;
  name : string;

begin
  ret := StringBuilder.Create('public property ');
  ret.Append(pinfo.Name.ToString());
  ret.Append(' : ');
  ret.Append(pinfo.PropertyType.ToString());

  if (pinfo.CanRead) then
  begin
    ret.Append(' read ');
    mi := pinfo.GetGetMethod();
    name := '<unknown Get Method>';
    if (mi <> nil) then
      name := mi.Name;

    ret.Append( name );
    ret.Append(';');
  end;

  if (pinfo.CanWrite) then
  begin
    ret.Append(' write ');
    mi := pinfo.GetSetMethod();
    name := '<unknown Set Method>';
    if (mi <> nil) then
      name := mi.Name;
    ret.Append( name );
    ret.Append(';');
  end;

  Result := ret.ToString();
end;

//
// BuildField returns a string that represents a field
//

function ReflectTreeNode.BuildField( finfo : FieldInfo ) : string;
var
  ret : StringBuilder;

begin
  ret := StringBuilder.Create('');
  if (finfo.IsAssembly) then
    ret.Append('private ');

  if (finfo.IsPrivate) then
    ret.Append('strict private ');

  // FamilyOrAssembly = Protected
  if (finfo.IsFamilyOrAssembly) then
    ret.Append('protected ');

  if (finfo.IsFamily) then
    ret.Append('strict protected ');

  if (finfo.IsPublic) then
    ret.Append('public ');

  if (finfo.IsStatic) then
    ret.Append('class var ');


  ret.Append( finfo.Name );
  ret.Append( ' : ' );
  ret.Append( finfo.FieldType.FullName );
  ret.Append( ';' );
  Result := ret.ToString();
end;

//
// BuildEvent returns a string representing the given event
//

function  ReflectTreeNode.BuildEvent( einfo : EventInfo ) : string;
var
  ret : StringBuilder;
  oi  : StringBuilder;

begin
  ret := StringBuilder.Create('public property ');
  oi  := StringBuilder.Create('');

  ret.Append( einfo.Name );
  ret.Append( ' : ' );
  ret.Append( einfo.EventHandlerType.FullName );
  ret.Append( ';' );

  if (einfo.IsMulticast) then
    oi.Append( 'multicast ');

  if (einfo.IsSpecialName) then
    oi.Append( 'specialname ');

  self.FOtherInfo := oi.ToString();

  Result := ret.ToString();
end;

function ReflectTreeNode.BuildIntrinsicAttributes( attrs : string ) : string;
begin
  if attrs <> '' then
    Result := 'Intrinsic [' + attrs + ']'
  else
    Result := 'Intrinsic: none.';
end;

//
// GetIntrinsicAttributes returns a string representing any intrinsic (built-in)
// attributes for the given type.
//

function ReflectTreeNode.GetIntrinsicAttributes( atype : System.Type ) : string;
var
  ret        : StringBuilder;
  mask       : TypeAttributes;

  function MaskToStr( mask : TypeAttributes ) : string;
  begin
    case mask of
      TypeAttributes.VisibilityMask     : Result := 'VisibilityMask';
      TypeAttributes.LayoutMask         : Result := 'LayoutMask';
      TypeAttributes.ClassSemanticsMask : Result := 'ClassSemanticsMask';
      TypeAttributes.StringFormatMask   : Result := 'StringFormatMask';
      TypeAttributes.ReservedMask       : Result := 'ReservedMask';
    else
      Result := 'Unknown mask!';
    end;
  end;

  function HasAttrib( mask : TypeAttributes; testfor : TypeAttributes ) : boolean;
  begin
    Result := (aType.Attributes and mask and testfor) = testfor;
  end;

  function GetAttrib( mask : TypeAttributes; testfor : TypeAttributes ) : string;
  var
    tmpStr : string;

  begin
    // While this looks like it's just testing for notpublic
    // it also checks AutoLayout and Class since they all have
    // the same value (0)
    if (testfor = TypeAttributes.NotPublic)  or
       (testfor = TypeAttributes.AutoLayout) or
       (testfor = TypeAttributes.ClassSemanticsMask) then
    begin
      case mask of
        TypeAttributes.VisibilityMask :
          if HasAttrib( mask, testfor ) then
             Result := 'NotPublic ';
        TypeAttributes.LayoutMask :
          if HasAttrib( mask, testfor ) then
             Result := 'AutoLayout ';
        TypeAttributes.ClassSemanticsMask :
          if HasAttrib( mask, testfor ) then
             Result := 'class ';
      end;
    end
    else if HasAttrib( mask, testfor ) then
    begin
      tmpStr := '';
      case testfor of
        TypeAttributes.Public            : tmpStr := 'Public ';
        TypeAttributes.NestedPublic      : tmpStr := 'NestedPublic ';
        TypeAttributes.NestedPrivate     : tmpStr := 'NestedPrivate ';
        TypeAttributes.NestedFamily      : tmpStr := 'NestedFamily ';
        TypeAttributes.NestedAssembly    : tmpStr := 'NestedAssembly ';
        TypeAttributes.NestedFamANDAssem : tmpStr := 'NestedFamANDAssem ';
        TypeAttributes.NestedFamORAssem  : tmpStr := 'NestedFamORAssem ';

        TypeAttributes.SequentialLayout  : tmpStr := 'SequentialLayout ';
        TypeAttributes.ExplicitLayout    : tmpStr := 'ExplicitLayout ';

        TypeAttributes.Interface         : tmpStr := 'Interface ';

        TypeAttributes.Abstract          : tmpStr := 'Abstract ';
        TypeAttributes.Sealed            : tmpStr := 'Sealed ';
        TypeAttributes.SpecialName       : tmpStr := 'SpecialName ';
        TypeAttributes.Import            : tmpStr := 'Import ';

        TypeAttributes.Serializable      : tmpStr := 'Serializable ';
        TypeAttributes.BeforeFieldInit   : tmpStr := 'BeforeFieldInit ';

        TypeAttributes.UnicodeClass      : tmpStr := 'UnicodeClass ';
        TypeAttributes.AutoClass         : tmpStr := 'AutoClass ';

        TypeAttributes.RTSpecialName     : tmpStr := 'RTSpecialName ';
        TypeAttributes.HasSecurity       : tmpStr := 'HasSecurity ';
      else
        tmpStr := 'Unhandled testfor!' + System.Enum( testfor ).ToString() ;
      end;
      Result := tmpStr;
    end
    else
      Result := '';
  end;

begin
  ret := StringBuilder.Create('');

  mask := TypeAttributes.VisibilityMask;
  //ret.Append( GetAttrib( mask, TypeAttributes.NotPublic         ));
  ret.Append( GetAttrib( mask, TypeAttributes.Public            ));
  ret.Append( GetAttrib( mask, TypeAttributes.NestedPublic      ));
  ret.Append( GetAttrib( mask, TypeAttributes.NestedPrivate     ));
  ret.Append( GetAttrib( mask, TypeAttributes.NestedFamily      ));
  ret.Append( GetAttrib( mask, TypeAttributes.NestedAssembly    ));
  ret.Append( GetAttrib( mask, TypeAttributes.NestedFamANDAssem ));
  ret.Append( GetAttrib( mask, TypeAttributes.NestedFamORAssem  ));

  mask := TypeAttributes.LayoutMask;
  //ret.Append( GetAttrib( mask, TypeAttributes.AutoLayout       ));
  ret.Append( GetAttrib( mask, TypeAttributes.SequentialLayout ));
  ret.Append( GetAttrib( mask, TypeAttributes.ExplicitLayout   ));

  mask := TypeAttributes.ClassSemanticsMask;
  //ret.Append( GetAttrib( mask, TypeAttributes.Class ));
  ret.Append( GetAttrib( mask, TypeAttributes.Interface    ));

  ret.Append( GetAttrib( TypeAttributes.Abstract,        TypeAttributes.Abstract        ));
  ret.Append( GetAttrib( TypeAttributes.Sealed,          TypeAttributes.Sealed          ));
  ret.Append( GetAttrib( TypeAttributes.SpecialName,     TypeAttributes.SpecialName     ));
  ret.Append( GetAttrib( TypeAttributes.Import,          TypeAttributes.Import          ));
  ret.Append( GetAttrib( TypeAttributes.Serializable,    TypeAttributes.Serializable    ));
  ret.Append( GetAttrib( TypeAttributes.BeforeFieldInit, TypeAttributes.BeforeFieldInit ));

  mask := TypeAttributes.StringFormatMask;
  ret.Append( GetAttrib( mask, TypeAttributes.AnsiClass    ));
  ret.Append( GetAttrib( mask, TypeAttributes.UnicodeClass ));
  ret.Append( GetAttrib( mask, TypeAttributes.AutoClass    ));

  mask := TypeAttributes.ReservedMask;
  ret.Append( GetAttrib( mask, TypeAttributes.RTSpecialName ));
  ret.Append( GetAttrib( mask, TypeAttributes.HasSecurity   ));

  Result := ret.ToString();
end;

//
// GetIntrinsicAttributes returns a string representing any instrinsic
// (built-in) attributes for the given property
//

function ReflectTreeNode.GetIntrinsicAttributes( pinfo : PropertyInfo    ) : string;

  function HasAttrib( pattr : PropertyAttributes ) : boolean;
  begin
    Result := ( pinfo.Attributes and pattr ) = pattr;
  end;

  function AttribToStr( pattr : PropertyAttributes ) : string;
  begin
    case pattr of
      PropertyAttributes.HasDefault    : Result := 'HasDefault';
      PRopertyAttributes.RTSpecialName : Result := 'RTSpecialName';
      PropertyAttributes.SpecialName   : Result := 'SpecialName';
    end;
  end;

  function TestAttrib( testfor : PropertyAttributes ) : string;
  begin
    Result := '';
    if HasAttrib( testfor ) then
      Result := AttribToStr( testfor );
  end;

begin
  Result := '';
  Result := Result + TestAttrib( PropertyAttributes.HasDefault    );
  Result := Result + TestAttrib( PropertyAttributes.RTSpecialName );
  Result := Result + TestAttrib( PropertyAttributes.SpecialName   );
end;

//
// GetIntrinsicAttributes returns a string representing any instrinsic
// (built-in) attributes for the given field
//

function ReflectTreeNode.GetIntrinsicAttributes( finfo : FieldInfo       ) : string;
begin
  // For even more attributes, see finfo.Attributes
  Result := '';
  if (finfo.IsInitOnly) then
    Result := Result + 'initonly ';

  if (finfo.IsLiteral) then
    Result := Result + 'literal ';

  if (finfo.IsNotSerialized) then
    Result := Result + 'notserialized';

  if (finfo.IsPinvokeImpl) then
    Result := Result + 'pinvoke ';

  if (finfo.IsSpecialName) then
    Result := Result + 'specialname ';
end;

//
// GetIntrinsicAttributes returns a string representing any instrinsic
// (built-in) attributes for the given event
//

function ReflectTreeNode.GetIntrinsicAttributes( einfo : EventInfo       ) : string;
begin
  // More attributes are available in einfo.Attributes
  Result := '';

  if einfo.IsMulticast then
    Result := Result + 'multicast ';

  if einfo.IsSpecialName then
    Result := Result + 'specialname ';
end;

//
// GetIntrinsicAttributes returns a string representing any instrinsic
// (built-in) attributes for the given method
//

function ReflectTreeNode.GetIntrinsicAttributes( mb    : MethodBase ) : string;

  function HasAttrib( testfor : MethodAttributes ) : boolean;
  begin
    Result := (mb.Attributes and testfor) = testfor;
  end;

  function AttribToStr( attr : MethodAttributes ) : string;
  begin
    case attr of
      MethodAttributes.HasSecurity      : Result := 'HasSecurity ';
      MethodAttributes.HideBySig        : Result := 'HideBySig ';
      MethodAttributes.PinvokeImpl      : Result := 'PInvokeImpl ';
      MethodAttributes.RequireSecObject : Result := 'RequireSecObject ';
      MethodAttributes.RTSpecialName    : Result := 'RTSpecialName ';
      MethodAttributes.UnmanagedExport  : Result := 'UnmanagedExport ';
    end;
  end;

  function TestAttrib( testfor : MethodAttributes ) : string;
  begin
    Result := '';
    if HasAttrib( testfor ) then
      Result := AttribToStr( testfor );
  end;

begin
  Result := '';
  Result := Result + TestAttrib( MethodAttributes.HasSecurity      );
  Result := Result + TestAttrib( MethodAttributes.HideBySig        );
  Result := Result + TestAttrib( MethodAttributes.PinvokeImpl      );
  Result := Result + TestAttrib( MethodAttributes.RequireSecObject );
  Result := Result + TestAttrib( MethodAttributes.RTSpecialName    );
  Result := Result + TestAttrib( MethodAttributes.UnmanagedExport  );
end;

//
// IsBorlandNamespace determines if a given type is within the Borland
// namespace.
//

function ReflectTreeNode.IsBorlandNamespace( atype : System.Type ) : boolean;
const
  BORLAND_NS = 'Borland.';

begin
  Result := false;
  if atype = nil then
    Exit;

  // does this fullname start with Borland.
  Result := Pos( BORLAND_NS, atype.FullName ) = 1;
end;

//
// Use this constructor to add a simple label
//

constructor ReflectTreeNode.Create( alabel : string );
begin
  inherited;

  AddNode( nil, nil, nil, nil, nil, nil, nil, nil );
  self.Text := alabel;
end;

//
// Use this constructor to add an AppDomain
//

constructor ReflectTreeNode.Create( anAppDomain : AppDomain );
var
  i : integer;
  assems : array of Assembly;

begin
  inherited Create;
  self.Text := 'Currently loaded assemblies';
  // we're not interested in adding the appdomain but we do want the
  // assemblies that it contains...
  assems := anAppDomain.GetAssemblies();
  for i:=0 to Length(assems)-1 do
  begin
    self.Nodes.Add( ReflectTreeNode.Create( assems[i] ) );
  end;
end;

//
// Use this constructor to add an Assembly
//

constructor ReflectTreeNode.Create( anAssembly : Assembly );
var
  i : integer;
  curMod : Module;

begin
  inherited Create;

  AddNode( anAssembly, nil, nil, nil, nil, nil, nil, nil);
  self.Text := anAssembly.GetName().Name;
  FAttributes := BuildCustomAttributes(anAssembly);
  FOtherInfo  := 'Location  : ' + anAssembly.Location + Environment.NewLine +
                 'CodeBase  : ' + anAssembly.CodeBase + Environment.NewLine +
                 'FullName  : ' + anAssembly.FullName + Environment.NewLine +
                 'GAC?      : ' + BoolToStr(anAssembly.GlobalAssemblyCache,true);

  if assigned(anAssembly.EntryPoint) then
  begin
    FOtherInfo := FOtherInfo + Environment.NewLine +
                 'EntryPoint: ' + anAssembly.EntryPoint.Name;
  end;


  //anAssembly.
  for i:=0 to Length(anAssembly.GetModules())-1 do
  begin
    curMod := anAssembly.GetModules()[i];
    self.Nodes.Add( ReflectTreeNode.Create( curMod ) );
  end;
end;

//
// Use this constructor to add a Module
//

constructor ReflectTreeNode.Create( aModule : Module );
var
  i : integer;
  mi : MethodInfo;
  curType : System.Type;

begin
  inherited Create;

  AddNode( nil, aModule, nil, nil, nil, nil, nil, nil );
  self.Text := aModule.Name;
  FAttributes := BuildCustomAttributes(aModule);
  self.Nodes.Add( ReflectTreeNode.Create('Global Methods') );

  for i:=0 to Length(aModule.GetMethods())-1 do
  begin
    mi := aModule.GetMethods()[i];
    self.Nodes[0].Nodes.Add( ReflectTreeNode.Create( mi ));
  end;

  self.Nodes.Add( ReflectTreeNode.Create('Types'));
  for i:=0 to Length(aModule.GetTypes())-1 do
  begin
    curType := aModule.GetTypes()[i];
    try
      self.Nodes[1].Nodes.Add( ReflectTreeNode.Create( curType ) );
    except
      on EBorlandNamespace do
      begin
        // eat this exception, as we're expecting this when we're
        // filtering out the borland namespace..
      end;
    end;
  end;
end;

// Use this constructor to add a method

constructor ReflectTreeNode.Create( minfo : MethodInfo );
begin
  inherited Create;

  AddNode( nil, nil, nil, minfo, nil, nil, nil, nil );
  self.Text := BuildMethodSignature( minfo );
  FAttributes := BuildIntrinsicAttributes( GetIntrinsicAttributes( minfo )) +
                   Environment.NewLine + BuildCustomAttributes( minfo );
end;

// Use this constructor to add a property

constructor ReflectTreeNode.Create( pinfo : PropertyInfo );
begin
  inherited Create;

  AddNode( nil, nil, nil, nil, nil, pinfo, nil, nil );
  self.Text := BuildPropertyInfo( pinfo );
  FAttributes := BuildIntrinsicAttributes( GetIntrinsicAttributes( pinfo )) +
                   Environment.NewLine + BuildCustomAttributes( pinfo );
end;

// Use this constructor to add a type

constructor ReflectTreeNode.Create( aType : System.Type );
begin
  inherited Create;

  if FHideBorNameSpace and IsBorlandNamespace( aType ) then
    raise EBorlandNamespace.CreateFmt('Borland namespace! %s ',[aType.Fullname]);

  AddNode( nil, nil, aType, nil, nil, nil, nil, nil );
  self.Text := GetType( aType );
  FAttributes := BuildIntrinsicAttributes( GetIntrinsicAttributes( aType )) +
                   Environment.NewLine + BuildCustomAttributes( aType );
  if ((aType.IsClass) or (aType.IsInterface)) then
    BuildClassOrInterfaceDef;
end;

// Use this constructor to add a constructor

constructor ReflectTreeNode.Create( cinfo : ConstructorInfo );
begin
  inherited Create;

  AddNode( nil, nil, nil, nil, cinfo, nil, nil, nil );
  self.Text := BuildConstructor( cinfo );
  FAttributes := BuildIntrinsicAttributes( GetIntrinsicAttributes( cinfo )) +
                   Environment.NewLine + BuildCustomAttributes( cinfo );
end;

// Use this constructor to add an event

constructor ReflectTreeNode.Create( einfo : EventInfo );
begin
  inherited Create;

  AddNode( nil, nil, nil, nil, nil, nil, nil, einfo );
  self.Text := BuildEvent( einfo );
  FAttributes := BuildIntrinsicAttributes( GetIntrinsicAttributes( einfo )) +
                   Environment.NewLine + BuildCustomAttributes( einfo );
end;

// Use this constructor to add a field

constructor ReflectTreeNode.Create( finfo : FieldInfo );
begin
  inherited Create;
    AddNode( nil, nil, nil, nil, nil, nil, finfo, nil );
  Self.Text := BuildField( finfo );
  FAttributes := BuildIntrinsicAttributes( GetIntrinsicAttributes( finfo )) +
                   Environment.NewLine + BuildCustomAttributes( finfo );
end;

end.
