December 2014

Sun Mon Tue Wed Thu Fri Sat
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      










« Persisting RegAppId data from an external AutoCAD drawing to XML using .NET | Main | Customizing ScriptPro 2.0 to process drawings not present in the AutoCAD editor »

January 31, 2011

Transforming a list of RegAppIds from XML to HTML using XSLT

As raised as a possibility at the end of the last post, I did choose to throw together a quick XSLT stylesheet to generate an HTML report of the XML data created by our XRA command.

To enable this I did make a few changes to our command implementation, which we’ll take a look at first.

Here’s the updated C# code, with new/modified lines in red (and here’s the updated source file):

    1 using Autodesk.AutoCAD.ApplicationServices;

    2 using Autodesk.AutoCAD.DatabaseServices;

    3 using Autodesk.AutoCAD.EditorInput;

    4 using Autodesk.AutoCAD.Runtime;

    5 using System.Collections.Generic;

    6 using System.Text;

    7 using System.Xml;

    8 using System.Xml.Serialization;

    9 using System.IO;

   10 using System;

   11 

   12 namespace XrefRegApps

   13 {

   14   [XmlRoot("RegAppData")]

   15   public class RegAppData

   16   {

   17     // The name of our drawing

   18 

   19     private string _name;

   20 

   21     [XmlAttribute("Name")]

   22     public string Name

   23     {

   24       get { return _name; }

   25       set { _name = value; }

   26     }

   27 

   28     // The location of our drawing

   29 

   30     private string _dwg;

   31 

   32     [XmlAttribute("File")]

   33     public string File

   34     {

   35       get { return _dwg; }

   36       set { _dwg = value; }

   37     }

   38 

   39     private string _dwgVersion;

   40 

   41     [XmlAttribute("DwgVersion")]

   42     public string DwgVersion

   43     {

   44       get { return _dwgVersion; }

   45       set { _dwgVersion = value; }

   46     }

   47 

   48     // The list of our drawing's Registered Application IDs

   49 

   50     private List<string> _regAppIds = new List<string>();

   51 

   52     [XmlArrayItem("RegAppId")]

   53     public List<string> RegAppIds

   54     {

   55       get { return _regAppIds; }

   56       set { _regAppIds = value; }

   57     }

   58 

   59     public void AddRegAppId(string id)

   60     {

   61       if (!_regAppIds.Contains(id))

   62         _regAppIds.Add(id);

   63     }

   64   }

   65 

   66   public class Commands

   67   {

   68     private const string regAppDataXml = "c:\\RegAppData.xml";

   69 

   70     private void SerializeRegAppData(

   71       Dictionary<string, RegAppData> dict

   72     )

   73     {

   74       if (dict.Count > 0)

   75       {

   76         // No direct way to serialize a dictionary to XML,

   77         // so we'll create a list from it and serialize that

   78 

   79         List<RegAppData> regData =

   80           new List<RegAppData>(dict.Count);

   81         foreach (KeyValuePair<string, RegAppData> kv in dict)

   82         {

   83           regData.Add(kv.Value);

   84         }

   85 

   86         // Serialize our list to the specified XML file

   87 

   88         XmlSerializer xs =

   89           new XmlSerializer(typeof(List<RegAppData>));

   90         XmlTextWriter xw =

   91           new XmlTextWriter(regAppDataXml, Encoding.UTF8);

   92         xw.WriteProcessingInstruction(

   93           "xml-stylesheet",

   94           "type=\"text/xsl\" href=\"RegAppData.xslt\""

   95         );

   96         xs.Serialize(xw, regData);

   97         xw.Close();

   98       }

   99     }

  100 

  101     // Read and return the previous Registered Application data

  102     // from our stored XML file

  103 

  104     private Dictionary<string, RegAppData>

  105       DeserializeRegAppData()

  106     {

  107       if (File.Exists(regAppDataXml))

  108       {

  109         XmlSerializer xs =

  110           new XmlSerializer(typeof(List<RegAppData>));

  111         XmlTextReader xr = new XmlTextReader(regAppDataXml);

  112         if (xs.CanDeserialize(xr))

  113         {

  114           // No direct way to serialize a dictionary to XML,

  115           // so we'll read a list and populate a dictionary

  116           // from it

  117 

  118           List<RegAppData> regData =

  119             (List<RegAppData>)xs.Deserialize(xr);

  120           xr.Close();

  121           Dictionary<string, RegAppData> dict =

  122             new Dictionary<string, RegAppData>();

  123           foreach (RegAppData rad in regData)

  124           {

  125             dict.Add(rad.File, rad);

  126           }

  127           return dict;

  128         }

  129       }

  130       return new Dictionary<string, RegAppData>();

  131     }

  132 

  133     [CommandMethod("XRA")]

  134     public void ExternalReferenceRegisteredApps()

  135     {

  136       Document doc =

  137           Application.DocumentManager.MdiActiveDocument;

  138       Database db = doc.Database;

  139       Editor ed = doc.Editor;

  140 

  141       // Prompt the user for the path to a DWG to test

  142 

  143       PromptStringOptions pso =

  144         new PromptStringOptions(

  145           "\nEnter path to root drawing file: "

  146         );

  147       pso.AllowSpaces = true;

  148       PromptResult pr = ed.GetString(pso);

  149       if (pr.Status != PromptStatus.OK)

  150         return;

  151 

  152       if (!File.Exists(pr.StringResult))

  153       {

  154         ed.WriteMessage("\nFile does not exist.");

  155         return;

  156       }

  157 

  158       try

  159       {

  160         // We start by reading any existing RegApp data

  161         // from XML

  162 

  163         Dictionary<string, RegAppData> regData =

  164           DeserializeRegAppData();

  165 

  166         // Load the DWG into a side database

  167 

  168         Database mainDb = new Database(false, true);

  169         using (mainDb)

  170         {

  171           mainDb.ReadDwgFile(

  172             pr.StringResult,

  173             FileOpenMode.OpenForReadAndAllShare,

  174             true,

  175             null

  176           );

  177 

  178           // We need an additional step to resolve Xrefs

  179 

  180           mainDb.ResolveXrefs(false, false);

  181 

  182           // Get the XrefGraph for the specified database

  183 

  184           XrefGraph xg = mainDb.GetHostDwgXrefGraph(true);

  185 

  186           // Loop through the nodes in the graph

  187 

  188           for (int i = 0; i < xg.NumNodes; i++)

  189           {

  190             // Get each node and check its status

  191 

  192             XrefGraphNode xgn = xg.GetXrefNode(i);

  193 

  194             switch (xgn.XrefStatus)

  195             {

  196               // If Un*, print a message

  197 

  198               case XrefStatus.Unresolved:

  199                 ed.WriteMessage(

  200                   "\nUnresolved xref \"{0}\"", xgn.Name

  201                 );

  202                 break;

  203               case XrefStatus.Unloaded:

  204                 ed.WriteMessage(

  205                   "\nUnloaded xref \"{0}\"", xgn.Name

  206                 );

  207                 break;

  208               case XrefStatus.Unreferenced:

  209                 ed.WriteMessage(

  210                   "\nUnreferenced xref \"{0}\"", xgn.Name

  211                 );

  212                 break;

  213               case XrefStatus.Resolved:

  214                 {

  215                   // If Resolved, get the RegAppTable

  216 

  217                   Database xdb = xgn.Database;

  218                   if (xdb != null)

  219                   {

  220                     Transaction tr =

  221                     xdb.TransactionManager.StartTransaction();

  222                     using (tr)

  223                     {

  224                       RegAppTable rat =

  225                         (RegAppTable)tr.GetObject(

  226                           xdb.RegAppTableId,

  227                           OpenMode.ForRead

  228                         );

  229 

  230                       // We'll store our RegApp data in an

  231                       // object

  232 

  233                       RegAppData dwgData = new RegAppData();

  234                       dwgData.Name =

  235                         (i == 0 ?

  236                           Path.GetFileNameWithoutExtension(

  237                             xgn.Name

  238                           ) :

  239                           xgn.Name

  240                         );

  241                       dwgData.File = xdb.Filename;

  242                       dwgData.DwgVersion =

  243                         xdb.OriginalFileVersion.ToString();

  244 

  245                       // Collect the contained names of the

  246                       // RegAppTableRecords (the RegAppIds)

  247                       // in a list

  248 

  249                       foreach (ObjectId id in rat)

  250                       {

  251                         RegAppTableRecord ratr =

  252                           (RegAppTableRecord)tr.GetObject(

  253                             id,

  254                             OpenMode.ForRead

  255                           );

  256                         dwgData.AddRegAppId(ratr.Name);

  257                       }

  258 

  259                       // Store the object in our dictionary,

  260                       // replacing any existing item

  261                       // (keyed off the entire DWG path, not

  262                       // just the name)

  263 

  264                       if (regData.ContainsKey(dwgData.File))

  265                         regData.Remove(dwgData.File);

  266                       regData.Add(dwgData.File, dwgData);

  267 

  268                       // Print the drawing information

  269                       // and the RegAppId count

  270 

  271                       ed.WriteMessage(

  272                         "\nDrawing \"{0}\" (\"{1}\") with " +

  273                         "{2} RegAppIds",

  274                         dwgData.Name,

  275                         dwgData.File,

  276                         dwgData.RegAppIds.Count

  277                       );

  278 

  279                       // Even if only reading, commit the

  280                       // transaction (it's cheaper than

  281                       // aborting)

  282 

  283                       tr.Commit();

  284                     }

  285                   }

  286                   break;

  287                 }

  288             }

  289           }

  290           // Save our data to XML

  291 

  292           SerializeRegAppData(regData);

  293         }

  294       }

  295       catch (System.Exception ex)

  296       {

  297         ed.WriteMessage(

  298           "\nProblem reading/processing \"{0}\": {1}",

  299           pr.StringResult, ex.Message

  300         );

  301       }

  302     }

  303   }

  304 }

The changes are very minor: we’ve added a DwgVersion attribute to our XML, as well as adjusting the save type of RegAppIds from being a simple string attribute to an array of elements (which allows us to determine the number of RegAppIds per drawing more easily).

We've hardcoded the path of the XML file to C:\ (feel free to change this, of course), as well as writing a stylesheet reference directly into the XML to just let the browser take care of the transformation to HTML.

Speaking of the stylesheet, here’s the RegAppData.xslt file to be placed alongside the XML output (currently in C:\, as just mentioned):

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet

  version="1.0"

  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

>

  <xsl:output method="html" indent="yes" version="4.0"/>

 

  <xsl:template match="ArrayOfRegAppData">

    <h2>Registered application IDs per drawing</h2>

    <table>

      <tr>

        <td><em>Drawing name</em></td>

        <td>

          <em>Number of RegAppIds</em>

        </td>

      </tr>

      <xsl:apply-templates select="*"/>

    </table>

  </xsl:template>

 

  <xsl:template match="RegAppData">

    <tr>

      <td>

        <b>

          <xsl:attribute name="Title">

            <xsl:value-of select="@File"/>

            <xsl:text> (</xsl:text>

            <xsl:value-of select="@DwgVersion"/>

            <xsl:text>)</xsl:text>

          </xsl:attribute>

          <xsl:value-of select="@Name"/>

        </b>

      </td>

      <td>

        <xsl:attribute name="Title">

          <xsl:for-each select="RegAppIds/RegAppId">

            <xsl:value-of select="node()"/>

            <xsl:if test="position() != last()">

              <xsl:text>&#x0A;</xsl:text>

            </xsl:if>

          </xsl:for-each>

        </xsl:attribute>

        <center>

          <xsl:value-of select="count(RegAppIds/RegAppId)"/>

        </center>

      </td>

    </tr>

  </xsl:template>

</xsl:stylesheet>

The stylesheet is also quite simple: it creates an HTML file containing a table of two columns – one with the names of our drawings (with the path and version in a tooltip) and the number of RegAppIds found in that drawing (with the specific names in a tooltip). We’ll see that in action a bit.

Running the XRA command just as we did in the last post, we should see this kind of XML output:

<?xml-stylesheet type="text/xsl" href="RegAppData.xslt"?>

<ArrayOfRegAppData

  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xmlns:xsd="http://www.w3.org/2001/XMLSchema">

  <RegAppData

    Name="A-01"

    File="C:\Program Files\Autodesk\AutoCAD 2011\Sample\Sheet Sets\Architectural\A-01.dwg"

    DwgVersion="Current">

    <RegAppIds>

      <RegAppId>ACAD</RegAppId>

      <RegAppId>CONTENTBLOCKDESCRIPTION</RegAppId>

      <RegAppId>CONTENTBLOCKICON</RegAppId>

      <RegAppId>CONTENTTABDATA</RegAppId>

      <RegAppId>CONTENTCURRENTTAB</RegAppId>

      <RegAppId>ACAD_PSEXT</RegAppId>

      <RegAppId>PE_URL</RegAppId>

      <RegAppId>RAK</RegAppId>

      <RegAppId>AcAecLayerStandard</RegAppId>

      <RegAppId>ACAD_DSTYLE_DIMTEXT_FILL</RegAppId>

      <RegAppId>ACAD_EXEMPT_FROM_CAD_STANDARDS</RegAppId>

      <RegAppId>AcDbBlockRepETag</RegAppId>

      <RegAppId>AcDbDynamicBlockTrueName</RegAppId>

      <RegAppId>AcDbDynamicBlockGUID</RegAppId>

      <RegAppId>AcDbBlockRepBTag</RegAppId>

      <RegAppId>ACAD_DSTYLE_DIMEXT_LENGTH</RegAppId>

      <RegAppId>ACAD_DSTYLE_DIMEXT_ENABLED</RegAppId>

      <RegAppId>ACAD_DSTYLE_DIM_LINETYPE</RegAppId>

      <RegAppId>ACAD_DSTYLE_DIM_EXT1_LINETYPE</RegAppId>

      <RegAppId>ACAD_DSTYLE_DIM_EXT2_LINETYPE</RegAppId>

      <RegAppId>ACAD_MLEADERVER</RegAppId>

    </RegAppIds>

  </RegAppData>

  <RegAppData

    Name="Grid Plan"

    File="C:\Program Files\Autodesk\AutoCAD 2011\Sample\Sheet Sets\Architectural\Res\Grid Plan.dwg"

    DwgVersion="AC1021">

    <RegAppIds>

      <RegAppId>ACAD</RegAppId>

      <RegAppId>CONTENTBLOCKDESCRIPTION</RegAppId>

      <RegAppId>CONTENTBLOCKICON</RegAppId>

      <RegAppId>CONTENTTABDATA</RegAppId>

      <RegAppId>CONTENTCURRENTTAB</RegAppId>

      <RegAppId>ACAD_PSEXT</RegAppId>

      <RegAppId>ACAD_DSTYLE_DIMTEXT_FILL</RegAppId>

    </RegAppIds>

  </RegAppData>

  <RegAppData

    Name="Wall Base"

    File="C:\Program Files\Autodesk\AutoCAD 2011\Sample\Sheet Sets\Architectural\Res\Wall Base.dwg"

    DwgVersion="AC1021">

    <RegAppIds>

      <RegAppId>ACAD</RegAppId>

      <RegAppId>CONTENTBLOCKDESCRIPTION</RegAppId>

      <RegAppId>CONTENTBLOCKICON</RegAppId>

      <RegAppId>CONTENTTABDATA</RegAppId>

      <RegAppId>CONTENTCURRENTTAB</RegAppId>

      <RegAppId>RAK</RegAppId>

      <RegAppId>AcAecLayerStandard</RegAppId>

      <RegAppId>ACAD_PSEXT</RegAppId>

      <RegAppId>PE_URL</RegAppId>

      <RegAppId>ACAD_DSTYLE_DIMTEXT_FILL</RegAppId>

      <RegAppId>ACAD_MLEADERVER</RegAppId>

    </RegAppIds>

  </RegAppData>

  <RegAppData

    Name="Master Site Plan"

    File="C:\Program Files\Autodesk\AutoCAD 2011\Sample\Sheet Sets\Civil\Master Site Plan.dwg"

    DwgVersion="AC1021">

    <RegAppIds>

      <RegAppId>ACAD</RegAppId>

      <RegAppId>RAK</RegAppId>

      <RegAppId>DCA_FIGURE_XENT</RegAppId>

      <RegAppId>ADE</RegAppId>

      <RegAppId>ALIGNMENT_DB_APP</RegAppId>

      <RegAppId>SDI_PROFILE</RegAppId>

      <RegAppId>SDSK_POINT</RegAppId>

      <RegAppId>SVPLINE</RegAppId>

      <RegAppId>ADCADD_ZZ</RegAppId>

      <RegAppId>SDSK_PMN</RegAppId>

      <RegAppId>CIVIL_DRAFT</RegAppId>

      <RegAppId>CIVIL_LINE_TABLE</RegAppId>

      <RegAppId>CIVIL_CURVE_TABLE</RegAppId>

      <RegAppId>CIVIL_SPIRAL_TABLE</RegAppId>

      <RegAppId>SDI_XSECTIONS</RegAppId>

      <RegAppId>ACAD_PSEXT</RegAppId>

      <RegAppId>ACAD_MLEADERVER</RegAppId>

      <RegAppId>DCO15</RegAppId>

    </RegAppIds>

  </RegAppData>

  <RegAppData

    Name="PB-BASE"

    File="C:\Program Files\Autodesk\AutoCAD 2011\Sample\Sheet Sets\Civil\PB-BASE.dwg"

    DwgVersion="AC1021">

    <RegAppIds>

      <RegAppId>ACAD</RegAppId>

      <RegAppId>RAK</RegAppId>

      <RegAppId>DCA_FIGURE_XENT</RegAppId>

      <RegAppId>ALIGNMENT_DB_APP</RegAppId>

      <RegAppId>SDI_PROFILE</RegAppId>

      <RegAppId>ACAD_PSEXT</RegAppId>

      <RegAppId>ACAD_MLEADERVER</RegAppId>

    </RegAppIds>

  </RegAppData>

  <RegAppData

    Name="PB-EX41"

    File="C:\Program Files\Autodesk\AutoCAD 2011\Sample\Sheet Sets\Civil\PB-EX41.dwg"

    DwgVersion="AC1021">

    <RegAppIds>

      <RegAppId>ACAD</RegAppId>

      <RegAppId>SDSK_POINT</RegAppId>

      <RegAppId>SVPLINE</RegAppId>

      <RegAppId>ACAD_PSEXT</RegAppId>

      <RegAppId>DCO15</RegAppId>

      <RegAppId>ACAD_MLEADERVER</RegAppId>

    </RegAppIds>

  </RegAppData>

  <RegAppData

    Name="PB-EX61"

    File="C:\Program Files\Autodesk\AutoCAD 2011\Sample\Sheet Sets\Civil\PB-EX61.dwg"

    DwgVersion="AC1021">

    <RegAppIds>

      <RegAppId>ACAD</RegAppId>

      <RegAppId>SVPLINE</RegAppId>

      <RegAppId>ACAD_PSEXT</RegAppId>

      <RegAppId>DCO15</RegAppId>

      <RegAppId>ACAD_MLEADERVER</RegAppId>

    </RegAppIds>

  </RegAppData>

</ArrayOfRegAppData>

Which, when loaded into a browser, should be displayed like this:

Our HTML report on RegAppIds

As we hover over the name of a drawing in which we’re interested, we see its path and drawing version:

With our drawing location and version

And as we hover over the RegAppId count, we see the names of the individual IDs:

And our RegAppIds listed

That’s it for this post. We’ll wrap this series up by making some modifications to ScriptPro 2.0 in the next post.

blog comments powered by Disqus

Feed/Share

10 Random Posts