Import Plugins

Import behavior is the data-rich half of the combined CSV starter. The plugin registers .csv, identifies supported files cheaply, then translates rows into TonCut projects, materials, pieces, and stock. Delphi and native C++ own that host-facing workflow; the C# project supplies CSV parsing and serialization behind a project-specific native bridge.

Discovery flow

TonCut builds import handlers by calling EnumExtensions(etImport, callback) on every active plugin. In the CSV starter, the same plugin also handles export and therefore answers both

and
.

EnumExtensions

function TCsvImportExportPlugin.EnumExtensions(
  aType: TTcpExtensionType;
  aEnumCb: ITcEnumExtensionsCallback
): TTcpResult;
begin
  if aType in [etImport, etExport] then
    aEnumCb.Invoke(aType, '.csv', 'CSV Import/Export Template');
  Result := cprOK;
end;

The C# managed library does not implement EnumExtensions. The native shim registers extensions and maps host callbacks to any project-specific managed bridge.

File matching

IsMyFile should stay cheap and conservative. In the CSV starter, the matcher checks:

  1. the .csv extension
  2. file readability
  3. the required shared header row

That is enough to make the starter practical without pushing expensive parsing into the ownership path. If your real format needs deeper heuristics later, keep them separate from full import population.

Validate the CSV header

function TCsvImportExportPlugin.ValidateHeader(
  const AFileName: WideString
): TTcpResult;
begin
  if not SameText(ExtractFileExt(AFileName), '.csv') then
    Exit(cprNo);
  if not FileExists(AFileName) then
    Exit(cprFailed);
  if HasRequiredHeader(ParseCsvText(TFile.ReadAllText(AFileName, TEncoding.UTF8))[0]) then
    Exit(cprOK);
  Result := cprNo;
end;

The native shim converts headerIsValid into the appropriate

value; the managed project publishes no direct TonCut callback interface.

Populating data

LoadFile receives host-owned collections. Treat them as the destination for imported data, not scratch buffers.

CollectionCSV starter responsibility
IMaterialsReuse an existing material by title, or create it once when the title is new
IProjectsReuse or create projects by title for FORMAT rows
IFormatsCreate one format per FORMAT row
ISheetsCreate one sheet per SHEET row

The important design rule is that the importer stays close to public host objects like ITonCut, IMaterial, IProject, IFormat, and ISheet. The starter does not depend on hidden helper layers.

LoadFile core pattern

for i := 1 to Length(table) - 1 do
begin
  row := table[i];
  recordType := UpperCase(CellByName(table[0], row, 'RecordType'));

  if recordType = 'FORMAT' then
  begin
    project := EnsureProject(aProjects, CellByName(table[0], row, 'ProjectTitle'));
  end;

  material := EnsureMaterial(
    aMaterials,
    CellByName(table[0], row, 'MaterialTitle'),
    materialKind,
    materialWidth,
    materialThickness
  );

  if recordType = 'FORMAT' then
  begin
    format := aFormats.CreateFormat();
    format.Project := project;
    format.Material := material;
    format.Length := itemLength;
    format.Width := itemWidth;
    format.Quantity := quantity;
    format.Identifier := CellByName(table[0], row, 'Identifier');
    format.Description := CellByName(table[0], row, 'Description');
    aFormats.Add(format);
  end
  else if recordType = 'SHEET' then
  begin
    sheet := aSheets.CreateSheet();
    sheet.Material := material;
    sheet.Length := itemLength;
    sheet.Width := itemWidth;
    sheet.Quantity := Integer(quantity);
    sheet.Identifier := CellByName(table[0], row, 'Identifier');
    sheet.Description := CellByName(table[0], row, 'Description');
    aSheets.Add(sheet);
  end;
end;

The full multi-language SDK examples live under SDK/examples/, and the overview page for them is /sdk.

Was this page helpful?