1. Technology

Your suggestion is on its way!

An email with a link to:

http://delphi.about.com/library/weekly/aa100504a.htm

was emailed to:

Thanks for sharing About.com with others!

The CodeDOM and the Delphi for .NET IDE
The second part of the CodeDOM story explains working with CodeDOM in Delphi code.
 Win prizes by sharing code!
Do you have some Delphi code you want to share? Are you interested in winning a prize for your work?
Delphi Programming Quickies Contest
 More of this Feature
• Part 1: CodeDOM basics
• Part 3: DelphiProvider - a CodeDomProvider for Delphi developers

• DOWNLOAD SAMPLE PROJECTS
 Join the Discussion
"Post your views, comments, questions and doubts to this article."
Discuss!
 Related Resources
• Delphi for .NET topics

Article originally written by Corbin Dunn (Borland R&D Software Engineer).

First part of this article explained what is CodeDOM. The CodeDOM is a collection of classes used to represent source code. Once source code is represented in a CodeDOM, it can then be printed, compiled to an assembly, or compiled to memory and executed.

We are now ready for Delphi code.

Creating a CodeDOM in Delphi Code
Create a CodeDOM is very easy. You first create a CodeCompileUnit (since it contains everything else) and then add a namespace to it. The namespace can then specify what other namespaces (or units) that it uses:

var
  Namespace: CodeNamespace;
begin
  // Create the primary code 
  //unit that contains everything
  FCodeUnit := CodeCompileUnit.Create;
  // Create a namespace and 
  //add it to that unit
  Namespace := CodeNamespace.Create
               ('MyUnitName');
  FCodeUnit.Namespaces.Add(Namespace);
  // Add a few items to 
  //the "uses" clause
  Namespace.Imports.Add(
    CodeNamespaceImport.Create
    ('System.Collections'));
  Namespace.Imports.Add(
    CodeNamespaceImport.Create
    ('System.Data'));
  Namespace.Imports.Add(
     CodeNamespaceImport.Create
     ('System.Xml'));
...

Unfortunately, one of the drawbacks of using the CodeDOM is that it doesn’t know about the Delphi language. There is no way to specify for a CodeNamespaceImport to be in the implementation section’s uses clause.

The next thing you will want to do is add one ore more types to the CodeDOM. The CodeDOM has no knowledge of global procedures or units, so unfortunately there is no way of adding them at this time (although, this may change at some later time).

var
  ...
  MyType: CodeTypeDeclaration;
begin
  ...
  // Create a type and add it to the namespace
  MyType := CodeTypeDeclaration.Create('TMyClass');
  Namespace.Types.Add(MyType);
...

A blank type isn’t very interesting, so you probably should add some members to it:

var
  ...
  MyMethod: CodeMemberMethod;
  MyField: CodeMemberField;
begin
  ...
  // Now add a field to the members in the type
  MyField := CodeMemberField.Create('Integer', 'FInt');
  MyType.Members.Add(MyField);
  // Create method to put in this type
  MyMethod := CodeMemberMethod.Create;
  MyMethod.Name := 'MyMethod';
  MyType.Members.Add(MyMethod);

The real interesting part is adding statements and expressions to the method:

var
  ...
  Statement: CodeStatement;
  LeftExpr, RightExpr: CodeExpression;
  MethodExpr: CodeExpression;
  Target: CodeTypeReferenceExpression;
  VarReference: CodeVariableReferenceExpression;
begin
  ...
  // Create a variable, and assign a value to it.
  Statement := CodeVariableDeclarationStatement.Create
               ('string','LocalStr');

  MyMethod.Statements.Add(Statement);
  // Assign a value to that variable
  LeftExpr := CodeVariableReferenceExpression.Create('LocalStr');
  RightExpr := CodePrimitiveExpression.Create('LocalValue');
  Statement := CodeAssignStatement.Create(LeftExpr, RightExpr);
  MyMethod.Statements.Add(Statement);
  // Write it out to the console
  Target := CodeTypeReferenceExpression.Create
            ('System.Console');
  VarReference := CodeVariableReferenceExpression.Create
                  ('LocalStr');
  MethodExpr := CodeMethodInvokeExpression.Create(
    Target, // The thing we are calling on
    'WriteLine', // The method we are going to call
    [VarReference]); // Parameters
  MyMethod.Statements.Add(MethodExpr);
  ...

Doing anything else is a matter of figuring out the correct CodeDOM object to create and add it in the appropriate place.

Printing a CodeDOM
Now that you have a CodeDOM you probably want to do something with it. Printing it is as simple as selecting the CodeDomProvider for the language of your choice, and printing it out:

var
  Provider: CodeDomProvider;
  Generator: ICodeGenerator;
  GeneratorOptions: CodeGeneratorOptions;
  Writer: TextWriter;
  Builder: StringBuilder;
begin
  // Print the code in delphi
  if FCodeUnit = nil then
    Exit;
 
  Provider := DelphiCodeProvider.Create
  // Create a writer to output to
  Builder := StringBuilder.Create;
  Writer := StringWriter.Create(Builder);

  // And options to control printing
  GeneratorOptions := CodeGeneratorOptions.Create;
  GeneratorOptions.IndentString := '  ';
  GeneratorOptions.BracingStyle := 'C';
  GeneratorOptions.BlankLinesBetweenMembers := True;

  Generator := Provider.CreateGenerator;
  Generator.GenerateCodeFromCompileUnit
            (FCodeUnit, Writer,GeneratorOptions);

  // Get the text that was written out
  TextBox1.Text := Builder.ToString;
end;

This example uses the ICodeGenerator.GenerateCodeFromCompileUnit method to print out the whole CodeCompileUnit. Additionally, you can use some of the other methods on ICodeGenerator to print just expressions, statements, etc.

Compiling and Executing
Another cool thing you can do with a CodeDOM is compile it or execute it. The CodeDomProvider of your choice can give an ICodeCompiler that can be used to compile the source:

var
  Compiler: ICodeCompiler;
  Parameters: CompilerParameters;
  Results: CompilerResults;
begin
  // Create an assembly from the code unit
  Compiler := CSharpCodeProvider.Create.CreateCompiler;
  Parameters := CompilerParameters.Create(
    ['mscorlib.dll', 
     'System.dll', 
     'System.Data.dll'],
    'NewAssembly.dll');
  Results := Compiler.CompileAssemblyFromDom
             (Parameters, FCodeUnit);
  if Results.Errors.Count > 0 then
    MessageBox.Show('Could not compile unit: ' +
      Results.Errors[0].ErrorText);
end;

The reason for using one CodeDomProvider versus another has to do with how your CodeDOM was created. For instance, if you use types that are native to your compiler, such as “Integer” in Delphi or “int” in C#, then you will have to use that compiler to compile your source. Or, if you use CodeSnippetXXX CodeObjects then the snippets are placed directly in the compiled source code, and must be in the same target language. If you stick to fairly safe CodeDOM expressions, and use types found in the CLR, then you should be safe.

For a complete example of all these concepts, take a look at the included project, DelphiDomPrinter.bdsproj.

We now move for the last part of the article: the DelphiProvider.dll - CodeDomProvider for Delphi code.

Next part > DelphiProvider, a CodeDomProvider designed for Delphi developers > Part 1, 2, 3

©2014 About.com. All rights reserved.