четверг, 20 марта 2014 г.

Баг BlockTableRecord.HasAttributeDefinitions.

На форуме adn-cis.org недавно появилось сообщение о том, что BlockTableRecord.HasAttributeDefinitions возвращает неверное значение для тех записей, из которых определения атрибутов на самом деле были ранее удалены. Наличие данного бага было подтверждено и заявка отправлена в ADN. Соответственно, придётся писать свою, корректную реализацию данного функционала.

По обозначенной выше ссылке проблема обозначалась следующим образом (отредактированная цитата):
1. Создаем определение блока c одним произвольным объектом (например с полилинией).
2. Добавляем определение атрибута в наше 
определение блока.
3. Видим, что свойство BlockTableRecord.HasAttributeDefinitions равно true, как и полагается.
4. Итерацией проходим по объектам нашей BlockTableRecord и видим, что их два: полилиния и определение атрибута.
5. Теперь удаляем ранее добавленное определение атрибута.
6.Итерацией проходим по объектам нашей BlockTableRecord и видим лишь один объект (как и полагается): полилинию.
7. Теперь смотрим свойство BlockTableRecord.HasAttributeDefinitions и видим, что оно равно true вместо ожидаемого false.
Свою версию обозначенного выше функционала реализуем в виде метода расширения, которому присвоим имя HasAttDefs:

   1:  // BlockTableRecordExtentions.cs
   2:  // © Andrey Bushman, 2014
   3:  // Extention methods for the BlockTableRecord class
   4:  using System;
   5:  using System.Collections.Generic;
   6:  using System.Linq;
   7:  using System.Text;
   8:   
   9:  /// В целях переносимости кода и отсутствия в псевдонимах наименований
  10:  /// конкретного САПР, целесообразней формировать псевдонимы в 
  11:  /// нейтральной форме, например: cad, вместо acad, Ap вместо AcAp, Db 
  12:  /// вместо AcDb и т.д. Построенная таким способом система наименований
  13:  /// будет более удобной программисту, портирующему ваш код под другую 
  14:  /// САПР. Ниже приведён вариант определений таких нейтральных псевдонимов
  15:  /// под некоторый набор различных САПР.
  16:  #if AUTOCAD
  17:  using cad = Autodesk.AutoCAD.ApplicationServices.Application;
  18:  using Ap = Autodesk.AutoCAD.ApplicationServices;
  19:  using Db = Autodesk.AutoCAD.DatabaseServices;
  20:  using Ed = Autodesk.AutoCAD.EditorInput;
  21:  using Gm = Autodesk.AutoCAD.Geometry;
  22:  using Rt = Autodesk.AutoCAD.Runtime;
  23:  #elif BRICSCAD
  24:  using cad = Bricscad.ApplicationServices.Application;
  25:  using Ap = Bricscad.ApplicationServices;
  26:  using Db = Teigha.DatabaseServices;
  27:  using Ed = Bricscad.EditorInput;
  28:  using Gm = Teigha.Geometry;
  29:  using Rt = Bricscad.Runtime;
  30:  #elif NANOCAD
  31:  using cad = HostMgd.ApplicationServices.Application;
  32:  using Ap = HostMgd.ApplicationServices;
  33:  using Db = Teigha.DatabaseServices;
  34:  using Ed = HostMgd.EditorInput;
  35:  using Gm = Teigha.Geometry;
  36:  using Rt = Teigha.Runtime; 
  37:  #endif
  38:   
  39:  namespace Bushman.CAD.Extentions {
  40:      /// <summary>
  41:      /// Методы расширения для экземпляров класса BlockTableRecord
  42:      /// </summary>
  43:      public static class BlockTableRecordExtentions {
  44:          /// <summary>
  45:          /// Данный метод проверяет наличие экземпляров <c>Db.AttributeDefinition</c> в
  46:          /// составе объекта <c>BlockTableRecord</c> и представляет собой замену методу
  47:          /// <c>BlockTableRecord.HasAttributeDefinitions</c>, который реализован 
  48:          /// неверно - в виду этого и возникла необходимость написать корректный вариант
  49:          /// реализации. Информация о некорректной работе 
  50:          /// <c>BlockTableRecord.HasAttributeDefinitions</c> была подтверждена и
  51:          /// отправлена в ADN. Подробности на странице 
  52:          /// http://adn-cis.org/forum/index.php?topic=625.0
  53:          /// </summary>
  54:          /// <param name="btr">экземпляр <c>BlockTableRecord</c>, подлежащий проверке.</param>
  55:          /// <returns>true - в составе указанного объекта <c>BlockTableRecord</c>
  56:          /// содержатся элементы <c>Db.AttributeDefinition</c>, иначе - false.</returns>
  57:          public static Boolean HasAttDefs(this Db.BlockTableRecord btr) {
  58:              String name = Rt.RXClass.GetClass(typeof(Db.AttributeDefinition)).Name;
  59:              return btr.Cast<Db.ObjectId>().Any(n => !n.IsNull && n.IsValid
  60:                  && !n.IsErased && !n.IsEffectivelyErased && String.Equals(
  61:                  n.ObjectClass.Name, name, StringComparison.InvariantCulture));
  62:          }
  63:   
  64:  #if DEBUG
  65:          /// <summary>
  66:          /// Команда, демонструрующая некорректную работу 
  67:          /// <c>BlockTableRecord.HasAttributeDefinitions</c>
  68:          /// и корректность работы метода <c>HasAttDefs</c>.
  69:          /// За основу взят код 
  70:          /// http://adn-cis.org/forum/index.php?topic=625.msg2168#msg2168
  71:          /// </summary>
  72:          [Rt.CommandMethod("TestBlock")]
  73:          public static void TestBlock() {
  74:              Ap.Document doc = cad.DocumentManager.MdiActiveDocument;
  75:              Ed.Editor ed = doc.Editor;
  76:              Ed.PromptResult res = ed.GetString("\nType name of block: ");
  77:              if (res.Status != Ed.PromptStatus.OK) return;
  78:              using (Db.Transaction tr = doc.TransactionManager.StartTransaction()) {
  79:                  Db.BlockTable bt = tr.GetObject(doc.Database.BlockTableId,
  80:                      Db.OpenMode.ForRead) as Db.BlockTable;
  81:                  if (bt != null) {
  82:                      if (bt.Has(res.StringResult)) {
  83:                          Db.BlockTableRecord btr = tr.GetObject(bt[res.StringResult],
  84:                              Db.OpenMode.ForRead) as Db.BlockTableRecord;
  85:                          if (btr != null) {
  86:                              ed.WriteMessage("\nAutodesk: Block {0} has{1} attribute definitions.\n",
  87:                                  res.StringResult, btr.HasAttributeDefinitions ? "" : " not");
  88:                              // Мною добавлена эта строка кода:
  89:                              ed.WriteMessage("\nBushman: Block {0} has{1} attribute definitions.\n",
  90:                                  res.StringResult, btr.HasAttDefs() ? "" : " not");
  91:                          }
  92:                      }
  93:                      else {
  94:                          ed.WriteMessage("\nBlock {0} not found", res.StringResult);
  95:                      }
  96:                  }
  97:                  tr.Commit();
  98:              }
  99:          }
 100:  #endif
 101:      }
 102:  }

Последовательно выполняем все шаги, демонстрирующие появление проблемы и запускаем команду TestBlock:


Как видим - наша реализация работает корректно.

Комментариев нет: