7
mirror of https://gitlab.com/kicad/code/kicad.git synced 2024-11-22 04:25:02 +00:00
kicad/common/io/cadstar/cadstar_archive_parser.cpp

2818 lines
84 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020-2021 Roberto Fernandez Bautista <roberto.fer.bau@gmail.com>
* Copyright (C) 2020-2023 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file cadstar_archive_parser.cpp
* @brief Helper functions and common defines between schematic and PCB Archive files
*/
#include <wx/filename.h>
#include <wx/log.h>
#include <wx/xml/xml.h>
#include <dsnlexer.h>
#include <geometry/shape_poly_set.h>
#include <io/cadstar/cadstar_archive_parser.h>
#include <eda_item.h>
#include <eda_text.h>
#include <macros.h>
#include <progress_reporter.h>
#include <string_utils.h>
#include <trigo.h>
// Ratio derived from CADSTAR default font. See doxygen comment in cadstar_archive_parser.h
const double CADSTAR_ARCHIVE_PARSER::TXT_HEIGHT_RATIO = ( 24.0 - 5.0 ) / 24.0;
// Cadstar fields and their KiCad equivalent
const std::map<CADSTAR_ARCHIVE_PARSER::TEXT_FIELD_NAME, wxString>
CADSTAR_ARCHIVE_PARSER::CADSTAR_TO_KICAD_FIELDS =
{ { TEXT_FIELD_NAME::DESIGN_TITLE, wxT( "DESIGN_TITLE" ) },
{ TEXT_FIELD_NAME::SHORT_JOBNAME, wxT( "SHORT_JOBNAME" ) },
{ TEXT_FIELD_NAME::LONG_JOBNAME, wxT( "LONG_JOBNAME" ) },
{ TEXT_FIELD_NAME::NUM_OF_SHEETS, wxT( "##" ) },
{ TEXT_FIELD_NAME::SHEET_NUMBER, wxT( "#" ) },
{ TEXT_FIELD_NAME::SHEET_NAME, wxT( "SHEETNAME" ) },
{ TEXT_FIELD_NAME::VARIANT_NAME, wxT( "VARIANT_NAME" ) },
{ TEXT_FIELD_NAME::VARIANT_DESCRIPTION, wxT( "VARIANT_DESCRIPTION" ) },
{ TEXT_FIELD_NAME::REG_USER, wxT( "REG_USER" ) },
{ TEXT_FIELD_NAME::COMPANY_NAME, wxT( "COMPANY_NAME" ) },
{ TEXT_FIELD_NAME::CURRENT_USER, wxT( "CURRENT_USER" ) },
{ TEXT_FIELD_NAME::DATE, wxT( "DATE" ) },
{ TEXT_FIELD_NAME::TIME, wxT( "TIME" ) },
{ TEXT_FIELD_NAME::MACHINE_NAME, wxT( "MACHINE_NAME" ) } };
void CADSTAR_ARCHIVE_PARSER::FORMAT::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "FORMAT" ) );
Type = GetXmlAttributeIDString( aNode, 0 );
SomeInt = GetXmlAttributeIDLong( aNode, 1 );
Version = GetXmlAttributeIDLong( aNode, 2 );
}
void CADSTAR_ARCHIVE_PARSER::TIMESTAMP::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "TIMESTAMP" ) );
if( !GetXmlAttributeIDString( aNode, 0 ).ToLong( &Year )
|| !GetXmlAttributeIDString( aNode, 1 ).ToLong( &Month )
|| !GetXmlAttributeIDString( aNode, 2 ).ToLong( &Day )
|| !GetXmlAttributeIDString( aNode, 3 ).ToLong( &Hour )
|| !GetXmlAttributeIDString( aNode, 4 ).ToLong( &Minute )
|| !GetXmlAttributeIDString( aNode, 5 ).ToLong( &Second ) )
THROW_PARSING_IO_ERROR( wxT( "TIMESTAMP" ), wxString::Format( "HEADER" ) );
}
void CADSTAR_ARCHIVE_PARSER::HEADER::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "HEADER" ) );
XNODE* cNode = aNode->GetChildren();
for( ; cNode; cNode = cNode->GetNext() )
{
wxString nodeName = cNode->GetName();
if( nodeName == wxT( "FORMAT" ) )
{
Format.Parse( cNode, aContext );
}
else if( nodeName == wxT( "JOBFILE" ) )
{
JobFile = GetXmlAttributeIDString( cNode, 0 );
}
else if( nodeName == wxT( "JOBTITLE" ) )
{
JobTitle = GetXmlAttributeIDString( cNode, 0 );
}
else if( nodeName == wxT( "GENERATOR" ) )
{
Generator = GetXmlAttributeIDString( cNode, 0 );
}
else if( nodeName == wxT( "RESOLUTION" ) )
{
XNODE* subNode = cNode->GetChildren();
if( ( subNode->GetName() == wxT( "METRIC" ) )
&& ( GetXmlAttributeIDString( subNode, 0 ) == wxT( "HUNDREDTH" ) )
&& ( GetXmlAttributeIDString( subNode, 1 ) == wxT( "MICRON" ) ) )
{
Resolution = RESOLUTION::HUNDREDTH_MICRON;
}
else
{
// TODO Need to find out if there are other possible resolutions. Logically
// there must be other base units that could be used, such as "IMPERIAL INCH"
// or "METRIC MM" but so far none of settings in CADSTAR generated a different
// output resolution to "HUNDREDTH MICRON"
THROW_UNKNOWN_NODE_IO_ERROR( subNode->GetName(), wxT( "HEADER->RESOLUTION" ) );
}
}
else if( nodeName == wxT( "TIMESTAMP" ) )
{
Timestamp.Parse( cNode, aContext );
}
else
{
THROW_UNKNOWN_NODE_IO_ERROR( cNode->GetName(), wxT( "HEADER" ) );
}
}
}
void CADSTAR_ARCHIVE_PARSER::VARIANT::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "VMASTER" ) || aNode->GetName() == wxT( "VARIANT" ) );
ID = GetXmlAttributeIDString( aNode, 0 );
if( aNode->GetName() == wxT( "VMASTER" ) )
{
Name = GetXmlAttributeIDString( aNode, 1 );
Description = GetXmlAttributeIDString( aNode, 2 );
}
else
{
ParentID = GetXmlAttributeIDString( aNode, 1 );
Name = GetXmlAttributeIDString( aNode, 2 );
Description = GetXmlAttributeIDString( aNode, 3 );
}
}
void CADSTAR_ARCHIVE_PARSER::VARIANT_HIERARCHY::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "VHIERARCHY" ) );
XNODE* cNode = aNode->GetChildren();
for( ; cNode; cNode = cNode->GetNext() )
{
if( cNode->GetName() == wxT( "VMASTER" ) || cNode->GetName() == wxT( "VARIANT" ) )
{
VARIANT variant;
variant.Parse( cNode, aContext );
Variants.insert( std::make_pair( variant.ID, variant ) );
}
else
{
THROW_UNKNOWN_NODE_IO_ERROR( cNode->GetName(), cNode->GetName() );
}
}
}
void CADSTAR_ARCHIVE_PARSER::LINECODE::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "LINECODE" ) );
ID = GetXmlAttributeIDString( aNode, 0 );
Name = GetXmlAttributeIDString( aNode, 1 );
if( !GetXmlAttributeIDString( aNode, 2 ).ToLong( &Width ) )
THROW_PARSING_IO_ERROR( wxT( "Line Width" ), wxString::Format( "LINECODE -> %s", Name ) );
XNODE* cNode = aNode->GetChildren();
if( !cNode || cNode->GetName() != wxT( "STYLE" ) )
THROW_UNKNOWN_NODE_IO_ERROR( cNode->GetName(), wxString::Format( "LINECODE -> %s", Name ) );
wxString styleStr = GetXmlAttributeIDString( cNode, 0 );
if( styleStr == wxT( "SOLID" ) )
{
Style = LINESTYLE::SOLID;
}
else if( styleStr == wxT( "DASH" ) )
{
Style = LINESTYLE::DASH;
}
else if( styleStr == wxT( "DASHDOT" ) )
{
Style = LINESTYLE::DASHDOT;
}
else if( styleStr == wxT( "DASHDOTDOT" ) )
{
Style = LINESTYLE::DASHDOTDOT;
}
else if( styleStr == wxT( "DOT" ) )
{
Style = LINESTYLE::DOT;
}
else
{
THROW_UNKNOWN_PARAMETER_IO_ERROR( wxString::Format( "STYLE %s", styleStr ),
wxString::Format( "LINECODE -> %s", Name ) );
}
}
void CADSTAR_ARCHIVE_PARSER::HATCH::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "HATCH" ) );
Step = GetXmlAttributeIDLong( aNode, 0 );
LineWidth = GetXmlAttributeIDLong( aNode, 2 );
XNODE* cNode = aNode->GetChildren();
if( !cNode || cNode->GetName() != wxT( "ORIENT" ) )
THROW_MISSING_NODE_IO_ERROR( wxT( "ORIENT" ), wxT( "HATCH" ) );
OrientAngle = GetXmlAttributeIDLong( cNode, 0 );
}
void CADSTAR_ARCHIVE_PARSER::HATCHCODE::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "HATCHCODE" ) );
ID = GetXmlAttributeIDString( aNode, 0 );
Name = GetXmlAttributeIDString( aNode, 1 );
XNODE* cNode = aNode->GetChildren();
wxString location = wxString::Format( "HATCHCODE -> %s", Name );
for( ; cNode; cNode = cNode->GetNext() )
{
if( cNode->GetName() != wxT( "HATCH" ) )
THROW_UNKNOWN_NODE_IO_ERROR( cNode->GetName(), location );
HATCH hatch;
hatch.Parse( cNode, aContext );
Hatches.push_back( hatch );
}
}
void CADSTAR_ARCHIVE_PARSER::FONT::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "FONT" ) );
Name = GetXmlAttributeIDString( aNode, 0 );
Modifier1 = GetXmlAttributeIDLong( aNode, 1 );
Modifier2 = GetXmlAttributeIDLong( aNode, 2 );
XNODE* cNode = aNode->GetChildren();
for( ; cNode; cNode = cNode->GetNext() )
{
wxString cNodeName = cNode->GetName();
if( cNodeName == wxT( "ITALIC" ) )
Italic = true;
else if( cNodeName == wxT( "KERNING" ) )
KerningPairs = true;
else
THROW_UNKNOWN_NODE_IO_ERROR( cNodeName, aNode->GetName() );
}
}
void CADSTAR_ARCHIVE_PARSER::TEXTCODE::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "TEXTCODE" ) );
ID = GetXmlAttributeIDString( aNode, 0 );
Name = GetXmlAttributeIDString( aNode, 1 );
LineWidth = GetXmlAttributeIDLong( aNode, 2 );
Height = GetXmlAttributeIDLong( aNode, 3 );
Width = GetXmlAttributeIDLong( aNode, 4 );
XNODE* cNode = aNode->GetChildren();
if( cNode )
{
if( cNode->GetName() == wxT( "FONT" ) )
Font.Parse( cNode, aContext );
else
THROW_UNKNOWN_NODE_IO_ERROR( cNode->GetName(), aNode->GetName() );
}
}
void CADSTAR_ARCHIVE_PARSER::ROUTEREASSIGN::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "ROUTEREASSIGN" ) );
LayerID = GetXmlAttributeIDString( aNode, 0 );
OptimalWidth = GetXmlAttributeIDLong( aNode, 1, false );
XNODE* cNode = aNode->GetChildren();
for( ; cNode; cNode = cNode->GetNext() )
{
wxString cNodeName = cNode->GetName();
if( cNodeName == wxT( "NECKWIDTH" ) )
NeckedWidth = GetXmlAttributeIDLong( cNode, 0 );
else if( cNodeName == wxT( "SROUTEWIDTH" ) )
OptimalWidth = GetXmlAttributeIDLong( cNode, 0 );
else if( cNodeName == wxT( "MINWIDTH" ) )
MinWidth = GetXmlAttributeIDLong( cNode, 0 );
else if( cNodeName == wxT( "MAXWIDTH" ) )
MaxWidth = GetXmlAttributeIDLong( cNode, 0 );
else
THROW_UNKNOWN_NODE_IO_ERROR( cNodeName, aNode->GetName() );
}
}
void CADSTAR_ARCHIVE_PARSER::ROUTECODE::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "ROUTECODE" ) );
ID = GetXmlAttributeIDString( aNode, 0 );
Name = GetXmlAttributeIDString( aNode, 1 );
OptimalWidth = GetXmlAttributeIDLong( aNode, 2, false );
XNODE* cNode = aNode->GetChildren();
for( ; cNode; cNode = cNode->GetNext() )
{
wxString cNodeName = cNode->GetName();
if( cNodeName == wxT( "NECKWIDTH" ) )
{
NeckedWidth = GetXmlAttributeIDLong( cNode, 0 );
}
else if( cNodeName == wxT( "SROUTEWIDTH" ) )
{
OptimalWidth = GetXmlAttributeIDLong( cNode, 0 );
}
else if( cNodeName == wxT( "MINWIDTH" ) )
{
MinWidth = GetXmlAttributeIDLong( cNode, 0 );
}
else if( cNodeName == wxT( "MAXWIDTH" ) )
{
MaxWidth = GetXmlAttributeIDLong( cNode, 0 );
}
else if( cNodeName == wxT( "ROUTEREASSIGN" ) )
{
ROUTEREASSIGN routereassign;
routereassign.Parse( cNode, aContext );
RouteReassigns.push_back( routereassign );
}
else
{
THROW_UNKNOWN_NODE_IO_ERROR( cNodeName, aNode->GetName() );
}
}
}
double CADSTAR_ARCHIVE_PARSER::EVALUE::GetDouble()
{
return Base * std::pow( 10.0, Exponent );
}
void CADSTAR_ARCHIVE_PARSER::EVALUE::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "E" ) );
if( ( !GetXmlAttributeIDString( aNode, 0 ).ToLong( &Base ) )
|| ( !GetXmlAttributeIDString( aNode, 1 ).ToLong( &Exponent ) ) )
{
THROW_PARSING_IO_ERROR( wxT( "Base and Exponent" ),
wxString::Format(
"%s->%s", aNode->GetParent()->GetName(), aNode->GetParent()->GetName() ) );
}
}
void CADSTAR_ARCHIVE_PARSER::POINT::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "PT" ) );
x = GetXmlAttributeIDLong( aNode, 0 );
y = GetXmlAttributeIDLong( aNode, 1 );
}
void CADSTAR_ARCHIVE_PARSER::LONGPOINT::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "PT" ) );
x = GetXmlAttributeIDLong( aNode, 0 );
y = GetXmlAttributeIDLong( aNode, 1 );
}
bool CADSTAR_ARCHIVE_PARSER::VERTEX::IsVertex( XNODE* aNode )
{
wxString aNodeName = aNode->GetName();
if( aNodeName == wxT( "PT" ) || aNodeName == wxT( "ACWARC" ) || aNodeName == wxT( "CWARC" )
|| aNodeName == wxT( "CWSEMI" ) || aNodeName == wxT( "ACWSEMI" ) )
{
return true;
}
else
{
return false;
}
}
void CADSTAR_ARCHIVE_PARSER::VERTEX::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( IsVertex( aNode ) );
wxString aNodeName = aNode->GetName();
if( aNodeName == wxT( "PT" ) )
{
Type = VERTEX_TYPE::POINT;
Center.x = UNDEFINED_VALUE;
Center.y = UNDEFINED_VALUE;
End.Parse( aNode, aContext );
}
else if( aNodeName == wxT( "ACWARC" ) || aNodeName == wxT( "CWARC" ) )
{
if( aNodeName == wxT( "ACWARC" ) )
Type = VERTEX_TYPE::ANTICLOCKWISE_ARC;
else
Type = VERTEX_TYPE::CLOCKWISE_ARC;
std::vector<POINT> pts = ParseAllChildPoints( aNode, aContext, true, 2 );
Center = pts[0];
End = pts[1];
}
else if( aNodeName == wxT( "ACWSEMI" ) || aNodeName == wxT( "CWSEMI" ) )
{
if( aNodeName == wxT( "ACWSEMI" ) )
Type = VERTEX_TYPE::ANTICLOCKWISE_SEMICIRCLE;
else
Type = VERTEX_TYPE::CLOCKWISE_SEMICIRCLE;
Center.x = UNDEFINED_VALUE;
Center.y = UNDEFINED_VALUE;
std::vector<POINT> pts = ParseAllChildPoints( aNode, aContext, true, 1 );
End = pts[0];
}
else
{
wxASSERT_MSG( true, wxT( "Unknown VERTEX type" ) );
}
}
void CADSTAR_ARCHIVE_PARSER::VERTEX::AppendToChain( SHAPE_LINE_CHAIN* aChainToAppendTo,
const std::function<VECTOR2I( const VECTOR2I& )> aCadstarToKicadPointCallback,
double aAccuracy ) const
{
if( Type == VERTEX_TYPE::POINT )
{
aChainToAppendTo->Append( aCadstarToKicadPointCallback( End ) );
return;
}
wxCHECK_MSG( aChainToAppendTo->PointCount() > 0, /*void*/,
"Can't append an arc to vertex to an empty chain" );
aChainToAppendTo->Append( BuildArc( aChainToAppendTo->GetPoint( -1 ), aCadstarToKicadPointCallback),
aAccuracy );
}
SHAPE_ARC CADSTAR_ARCHIVE_PARSER::VERTEX::BuildArc( const VECTOR2I& aPrevPoint,
const std::function<VECTOR2I( const VECTOR2I& )> aCadstarToKicadPointCallback ) const
{
wxCHECK_MSG( Type != VERTEX_TYPE::POINT, SHAPE_ARC(),
"Can't build an arc for a straight segment!" );
VECTOR2I startPoint = aPrevPoint;
VECTOR2I endPoint = aCadstarToKicadPointCallback( End );
VECTOR2I centerPoint;
if( Type == VERTEX_TYPE::ANTICLOCKWISE_SEMICIRCLE || Type == VERTEX_TYPE::CLOCKWISE_SEMICIRCLE )
centerPoint = ( startPoint / 2 ) + ( endPoint / 2 );
else
centerPoint = aCadstarToKicadPointCallback( Center );
bool clockwise = Type == VERTEX_TYPE::CLOCKWISE_ARC
|| Type == VERTEX_TYPE::CLOCKWISE_SEMICIRCLE;
// A bit of a hack to figure out if we need to invert clockwise due to the transform
VECTOR2I transform = aCadstarToKicadPointCallback( { 500, 500 } )
- aCadstarToKicadPointCallback( { 0, 0 } );
if( ( transform.x > 0 && transform.y < 0 ) || ( transform.x < 0 && transform.y > 0 ) )
clockwise = !clockwise;
SHAPE_ARC arc;
return arc.ConstructFromStartEndCenter( startPoint, endPoint, centerPoint, clockwise );
}
void CADSTAR_ARCHIVE_PARSER::CUTOUT::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "CUTOUT" ) );
Vertices = ParseAllChildVertices( aNode, aContext, true );
}
bool CADSTAR_ARCHIVE_PARSER::SHAPE::IsShape( XNODE* aNode )
{
wxString aNodeName = aNode->GetName();
if( aNodeName == wxT( "OPENSHAPE" ) || aNodeName == wxT( "OUTLINE" )
|| aNodeName == wxT( "SOLID" ) || aNodeName == wxT( "HATCHED" ) )
{
return true;
}
else
{
return false;
}
}
void CADSTAR_ARCHIVE_PARSER::SHAPE::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( IsShape( aNode ) );
wxString aNodeName = aNode->GetName();
if( aNodeName == wxT( "OPENSHAPE" ) )
{
Type = SHAPE_TYPE::OPENSHAPE;
Vertices = ParseAllChildVertices( aNode, aContext, true );
Cutouts.clear();
HatchCodeID = wxEmptyString;
}
else if( aNodeName == wxT( "OUTLINE" ) )
{
Type = SHAPE_TYPE::OUTLINE;
Vertices = ParseAllChildVertices( aNode, aContext, false );
Cutouts = ParseAllChildCutouts( aNode, aContext, false );
HatchCodeID = wxEmptyString;
}
else if( aNodeName == wxT( "SOLID" ) )
{
Type = SHAPE_TYPE::SOLID;
Vertices = ParseAllChildVertices( aNode, aContext, false );
Cutouts = ParseAllChildCutouts( aNode, aContext, false );
HatchCodeID = wxEmptyString;
}
else if( aNodeName == wxT( "HATCHED" ) )
{
Type = SHAPE_TYPE::HATCHED;
Vertices = ParseAllChildVertices( aNode, aContext, false );
Cutouts = ParseAllChildCutouts( aNode, aContext, false );
HatchCodeID = GetXmlAttributeIDString( aNode, 0 );
}
else
{
wxASSERT_MSG( true, wxT( "Unknown SHAPE type" ) );
}
}
SHAPE_LINE_CHAIN CADSTAR_ARCHIVE_PARSER::SHAPE::OutlineAsChain(
const std::function<VECTOR2I( const VECTOR2I& )> aCadstarToKicadPointCallback,
double aAccuracy ) const
{
SHAPE_LINE_CHAIN outline;
if( Vertices.size() == 0 )
return outline;
for( const auto& vertex : Vertices )
vertex.AppendToChain( &outline, aCadstarToKicadPointCallback, aAccuracy );
if( Type != SHAPE_TYPE::OPENSHAPE )
{
outline.SetClosed( true );
// Append after closing, to ensre first and last point remain the same
outline.Append( outline.CPoint( 0 ), true );
}
return outline;
}
SHAPE_POLY_SET CADSTAR_ARCHIVE_PARSER::SHAPE::ConvertToPolySet(
const std::function<VECTOR2I( const VECTOR2I& )> aCadstarToKicadPointCallback,
double aAccuracy ) const
{
SHAPE_POLY_SET polyset;
wxCHECK( Type != SHAPE_TYPE::OPENSHAPE, polyset ); // We shouldn't convert openshapes to polyset!
polyset.AddOutline( OutlineAsChain( aCadstarToKicadPointCallback, aAccuracy ) );
for( const auto& cutout : Cutouts )
{
SHAPE_LINE_CHAIN hole;
if( cutout.Vertices.size() == 0 )
continue;
for( const auto& cutoutVertex : cutout.Vertices )
cutoutVertex.AppendToChain( &hole, aCadstarToKicadPointCallback, aAccuracy );
hole.SetClosed( true );
// Append after closing, to ensre first and last point remain the same
cutout.Vertices.at( 0 ).AppendToChain( &hole, aCadstarToKicadPointCallback, aAccuracy );
polyset.AddHole( hole );
}
return polyset;
}
CADSTAR_ARCHIVE_PARSER::UNITS CADSTAR_ARCHIVE_PARSER::ParseUnits( XNODE* aNode )
{
wxASSERT( aNode->GetName() == wxT( "UNITS" ) );
wxString unit = GetXmlAttributeIDString( aNode, 0 );
if( unit == wxT( "CENTIMETER" ) )
return UNITS::CENTIMETER;
else if( unit == wxT( "INCH" ) )
return UNITS::INCH;
else if( unit == wxT( "METER" ) )
return UNITS::METER;
else if( unit == wxT( "MICROMETRE" ) )
return UNITS::MICROMETRE;
else if( unit == wxT( "MM" ) )
return UNITS::MM;
else if( unit == wxT( "THOU" ) )
return UNITS::THOU;
else if( unit == wxT( "DESIGN" ) )
return UNITS::DESIGN;
else
THROW_UNKNOWN_PARAMETER_IO_ERROR( unit, wxT( "UNITS" ) );
return UNITS();
}
CADSTAR_ARCHIVE_PARSER::ANGUNITS CADSTAR_ARCHIVE_PARSER::ParseAngunits( XNODE* aNode )
{
wxASSERT( aNode->GetName() == wxT( "ANGUNITS" ) );
wxString angUnitStr = GetXmlAttributeIDString( aNode, 0 );
if( angUnitStr == wxT( "DEGREES" ) )
return ANGUNITS::DEGREES;
else if( angUnitStr == wxT( "RADIANS" ) )
return ANGUNITS::RADIANS;
else
THROW_UNKNOWN_PARAMETER_IO_ERROR( angUnitStr, aNode->GetName() );
return ANGUNITS();
}
bool CADSTAR_ARCHIVE_PARSER::GRID::IsGrid( XNODE* aNode )
{
wxString aNodeName = aNode->GetName();
if( aNodeName == wxT( "FRACTIONALGRID" ) || aNodeName == wxT( "STEPGRID" ) )
return true;
else
return false;
}
void CADSTAR_ARCHIVE_PARSER::GRID::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( IsGrid( aNode ) );
wxString aNodeName = aNode->GetName();
if( aNodeName == wxT( "FRACTIONALGRID" ) )
Type = GRID_TYPE::FRACTIONALGRID;
else if( aNodeName == wxT( "STEPGRID" ) )
Type = GRID_TYPE::STEPGRID;
else
wxASSERT_MSG( true, wxT( "Unknown Grid Type" ) );
Name = GetXmlAttributeIDString( aNode, 0 );
Param1 = GetXmlAttributeIDLong( aNode, 1 );
Param2 = GetXmlAttributeIDLong( aNode, 2 );
}
void CADSTAR_ARCHIVE_PARSER::GRIDS::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "GRIDS" ) );
XNODE* cNode = aNode->GetChildren();
for( ; cNode; cNode = cNode->GetNext() )
{
wxString cNodeName = cNode->GetName();
if( cNodeName == wxT( "WORKINGGRID" ) )
{
XNODE* workingGridNode = cNode->GetChildren();
if( !GRID::IsGrid( workingGridNode ) )
{
THROW_UNKNOWN_NODE_IO_ERROR(
workingGridNode->GetName(), wxT( "GRIDS -> WORKINGGRID" ) );
}
else
{
WorkingGrid.Parse( workingGridNode, aContext );
}
}
else if( cNodeName == wxT( "SCREENGRID" ) )
{
XNODE* screenGridNode = cNode->GetChildren();
if( !GRID::IsGrid( screenGridNode ) )
{
THROW_UNKNOWN_NODE_IO_ERROR(
screenGridNode->GetName(), wxT( "GRIDS -> SCREENGRID" ) );
}
else
{
ScreenGrid.Parse( screenGridNode, aContext );
}
}
else if( GRID::IsGrid( cNode ) )
{
GRID userGrid;
userGrid.Parse( cNode, aContext );
UserGrids.push_back( userGrid );
}
}
}
bool CADSTAR_ARCHIVE_PARSER::SETTINGS::ParseSubNode( XNODE* aChildNode, PARSER_CONTEXT* aContext )
{
wxString cNodeName = aChildNode->GetName();
if( cNodeName == wxT( "UNITS" ) )
{
Units = ParseUnits( aChildNode );
}
else if( cNodeName == wxT( "UNITSPRECISION" ) )
{
UnitDisplPrecision = GetXmlAttributeIDLong( aChildNode, 0 );
}
else if( cNodeName == wxT( "INTERLINEGAP" ) )
{
InterlineGap = GetXmlAttributeIDLong( aChildNode, 0 );
}
else if( cNodeName == wxT( "BARLINEGAP" ) )
{
BarlineGap = GetXmlAttributeIDLong( aChildNode, 0 );
}
else if( cNodeName == wxT( "ALLOWBARTEXT" ) )
{
AllowBarredText = true;
}
else if( cNodeName == wxT( "ANGULARPRECISION" ) )
{
AngularPrecision = GetXmlAttributeIDLong( aChildNode, 0 );
}
else if( cNodeName == wxT( "DESIGNORIGIN" ) )
{
DesignOrigin.Parse( aChildNode->GetChildren(), aContext );
}
else if( cNodeName == wxT( "DESIGNAREA" ) )
{
std::vector<POINT> pts = ParseAllChildPoints( aChildNode, aContext, true, 2 );
DesignArea = std::make_pair( pts[0], pts[1] );
}
else if( cNodeName == wxT( "DESIGNREF" ) )
{
DesignOrigin.Parse( aChildNode->GetChildren(), aContext );
}
else if( cNodeName == wxT( "DESIGNLIMIT" ) )
{
DesignLimit.Parse( aChildNode->GetChildren(), aContext );
}
else if( cNodeName == wxT( "PINNOOFFSET" ) )
{
PinNoOffset = GetXmlAttributeIDLong( aChildNode, 0 );
}
else if( cNodeName == wxT( "PINNOANGLE" ) )
{
PinNoAngle = GetXmlAttributeIDLong( aChildNode, 0 );
}
else
{
return false;
}
return true;
}
void CADSTAR_ARCHIVE_PARSER::SETTINGS::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "SETTINGS" ) );
XNODE* cNode = aNode->GetChildren();
for( ; cNode; cNode = cNode->GetNext() )
{
wxString cNodeName = cNode->GetName();
if( ParseSubNode( cNode, aContext ) )
continue;
else
THROW_UNKNOWN_NODE_IO_ERROR( cNodeName, wxT( "SETTINGS" ) );
}
}
wxString CADSTAR_ARCHIVE_PARSER::ParseTextFields( const wxString& aTextString,
PARSER_CONTEXT* aContext )
{
static const std::map<TEXT_FIELD_NAME, wxString> txtTokens =
{
{ TEXT_FIELD_NAME::DESIGN_TITLE, wxT( "DESIGN TITLE" ) },
{ TEXT_FIELD_NAME::SHORT_JOBNAME, wxT( "SHORT_JOBNAME" ) },
{ TEXT_FIELD_NAME::LONG_JOBNAME, wxT( "LONG_JOBNAME" ) },
{ TEXT_FIELD_NAME::NUM_OF_SHEETS, wxT( "NUM_OF_SHEETS" ) },
{ TEXT_FIELD_NAME::SHEET_NUMBER, wxT( "SHEET_NUMBER" ) },
{ TEXT_FIELD_NAME::SHEET_NAME, wxT( "SHEET_NAME" ) },
{ TEXT_FIELD_NAME::VARIANT_NAME, wxT( "VARIANT_NAME" ) },
{ TEXT_FIELD_NAME::VARIANT_DESCRIPTION, wxT( "VARIANT_DESCRIPTION" ) },
{ TEXT_FIELD_NAME::REG_USER, wxT( "REG_USER" ) },
{ TEXT_FIELD_NAME::COMPANY_NAME, wxT( "COMPANY_NAME" ) },
{ TEXT_FIELD_NAME::CURRENT_USER, wxT( "CURRENT_USER" ) },
{ TEXT_FIELD_NAME::DATE, wxT( "DATE" ) },
{ TEXT_FIELD_NAME::TIME, wxT( "TIME" ) },
{ TEXT_FIELD_NAME::MACHINE_NAME, wxT( "MACHINE_NAME" ) },
{ TEXT_FIELD_NAME::FROM_FILE, wxT( "FROM_FILE" ) },
{ TEXT_FIELD_NAME::DISTANCE, wxT( "DISTANCE" ) },
{ TEXT_FIELD_NAME::UNITS_SHORT, wxT( "UNITS SHORT" ) },
{ TEXT_FIELD_NAME::UNITS_ABBREV, wxT( "UNITS ABBREV" ) },
{ TEXT_FIELD_NAME::UNITS_FULL, wxT( "UNITS FULL" ) },
{ TEXT_FIELD_NAME::HYPERLINK, wxT( "HYPERLINK" ) }
};
wxString remainingStr = aTextString;
wxString returnStr;
while( remainingStr.size() > 0 )
{
//Find the start token
size_t startpos = remainingStr.Find( wxT( "<@" ) );
if( static_cast<int>( startpos ) == wxNOT_FOUND )
{
// No more fields to parse, add to return string
returnStr += remainingStr;
break;
}
if( startpos > 0 )
returnStr += remainingStr.SubString( 0, startpos - 1 );
if( ( startpos + 2 ) >= remainingStr.size() )
break;
remainingStr = remainingStr.Mid( startpos + 2 );
//Find the expected token for the field
TEXT_FIELD_NAME foundField = TEXT_FIELD_NAME::NONE;
for( std::pair<TEXT_FIELD_NAME, wxString> txtF : txtTokens )
{
if( remainingStr.StartsWith( txtF.second ) )
{
foundField = txtF.first;
break;
}
}
if( foundField == TEXT_FIELD_NAME::NONE )
{
// Not a valid field, lets keep looking
returnStr += wxT( "<@" );
continue;
}
//Now lets find the end token
size_t endpos = remainingStr.Find( wxT( "@>" ) );
if( static_cast<int>( endpos ) == wxNOT_FOUND )
{
// The field we found isn't valid as it doesn't have a termination
// Lets append the whole thing as plain text
returnStr += wxT( "<@" ) + remainingStr;
break;
}
size_t valueStart = txtTokens.at( foundField ).size();
wxString fieldValue = remainingStr.SubString( valueStart, endpos - 1 );
wxString address;
if( foundField == TEXT_FIELD_NAME::FROM_FILE || foundField == TEXT_FIELD_NAME::HYPERLINK )
{
// The first character should always be a double quotation mark
wxASSERT_MSG( fieldValue.at( 0 ) == '"', "Expected '\"' as the first character" );
size_t splitPos = fieldValue.find_first_of( '"', 1 );
address = fieldValue.SubString( 1, splitPos - 1 );
if( foundField == TEXT_FIELD_NAME::HYPERLINK )
{
// Assume the last two characters are "@>"
wxASSERT_MSG( remainingStr.EndsWith( wxT( "@>" ) ),
"Expected '@>' at the end of a hyperlink" );
fieldValue = remainingStr.SubString( valueStart + splitPos + 1,
remainingStr.Length() - 3 );
remainingStr = wxEmptyString;
}
else
{
fieldValue = fieldValue.Mid( splitPos + 1 );
}
}
switch( foundField )
{
case TEXT_FIELD_NAME::DESIGN_TITLE:
case TEXT_FIELD_NAME::SHORT_JOBNAME:
case TEXT_FIELD_NAME::LONG_JOBNAME:
case TEXT_FIELD_NAME::VARIANT_NAME:
case TEXT_FIELD_NAME::VARIANT_DESCRIPTION:
case TEXT_FIELD_NAME::REG_USER:
case TEXT_FIELD_NAME::COMPANY_NAME:
case TEXT_FIELD_NAME::CURRENT_USER:
case TEXT_FIELD_NAME::DATE:
case TEXT_FIELD_NAME::TIME:
case TEXT_FIELD_NAME::MACHINE_NAME:
if( aContext->TextFieldToValuesMap.find( foundField )
!= aContext->TextFieldToValuesMap.end() )
{
aContext->InconsistentTextFields.insert( foundField );
}
else
{
aContext->TextFieldToValuesMap.insert( { foundField, fieldValue } );
}
KI_FALLTHROUGH;
case TEXT_FIELD_NAME::NUM_OF_SHEETS:
case TEXT_FIELD_NAME::SHEET_NUMBER:
case TEXT_FIELD_NAME::SHEET_NAME:
returnStr += wxT( "${" ) + CADSTAR_TO_KICAD_FIELDS.at( foundField ) + wxT( "}" );
break;
case TEXT_FIELD_NAME::DISTANCE:
case TEXT_FIELD_NAME::UNITS_SHORT:
case TEXT_FIELD_NAME::UNITS_ABBREV:
case TEXT_FIELD_NAME::UNITS_FULL:
// Just flatten the text for distances
returnStr += fieldValue;
break;
case TEXT_FIELD_NAME::FROM_FILE:
{
wxFileName fn( address );
wxString fieldFmt = wxT( "FROM_FILE_%s_%s" );
wxString fieldName = wxString::Format( fieldFmt, fn.GetName(), fn.GetExt() );
int version = 1;
while(
aContext->FilenamesToTextMap.find( fieldName )
!= aContext->FilenamesToTextMap.end()
&& aContext->FilenamesToTextMap.at( fieldName ) != fieldValue )
{
fieldName = wxString::Format( fieldFmt, fn.GetName(), fn.GetExt() )
+ wxString::Format( wxT( "_%d" ), version++ );
}
aContext->FilenamesToTextMap[fieldName] = fieldValue;
returnStr += wxT( "${" ) + fieldName + wxT( "}" );
}
break;
case TEXT_FIELD_NAME::HYPERLINK:
{
aContext->TextToHyperlinksMap[fieldValue] = address;
returnStr += ParseTextFields( fieldValue, aContext );
}
break;
case TEXT_FIELD_NAME::NONE:
wxFAIL_MSG( "We should have already covered this scenario above" );
break;
}
if( ( endpos + 2 ) >= remainingStr.size() )
break;
remainingStr = remainingStr.Mid( endpos + 2 );
}
return returnStr;
}
CADSTAR_ARCHIVE_PARSER::ALIGNMENT CADSTAR_ARCHIVE_PARSER::ParseAlignment( XNODE* aNode )
{
wxASSERT( aNode->GetName() == wxT( "ALIGN" ) );
wxString alignmentStr = GetXmlAttributeIDString( aNode, 0 );
if( alignmentStr == wxT( "BOTTOMCENTER" ) )
return ALIGNMENT::BOTTOMCENTER;
else if( alignmentStr == wxT( "BOTTOMLEFT" ) )
return ALIGNMENT::BOTTOMLEFT;
else if( alignmentStr == wxT( "BOTTOMRIGHT" ) )
return ALIGNMENT::BOTTOMRIGHT;
else if( alignmentStr == wxT( "CENTERCENTER" ) )
return ALIGNMENT::CENTERCENTER;
else if( alignmentStr == wxT( "CENTERLEFT" ) )
return ALIGNMENT::CENTERLEFT;
else if( alignmentStr == wxT( "CENTERRIGHT" ) )
return ALIGNMENT::CENTERRIGHT;
else if( alignmentStr == wxT( "TOPCENTER" ) )
return ALIGNMENT::TOPCENTER;
else if( alignmentStr == wxT( "TOPLEFT" ) )
return ALIGNMENT::TOPLEFT;
else if( alignmentStr == wxT( "TOPRIGHT" ) )
return ALIGNMENT::TOPRIGHT;
else
THROW_UNKNOWN_PARAMETER_IO_ERROR( alignmentStr, wxT( "ALIGN" ) );
//shouldn't be here but avoids compiler warning
return ALIGNMENT::NO_ALIGNMENT;
}
CADSTAR_ARCHIVE_PARSER::JUSTIFICATION CADSTAR_ARCHIVE_PARSER::ParseJustification( XNODE* aNode )
{
wxASSERT( aNode->GetName() == wxT( "JUSTIFICATION" ) );
wxString justificationStr = GetXmlAttributeIDString( aNode, 0 );
if( justificationStr == wxT( "LEFT" ) )
return JUSTIFICATION::LEFT;
else if( justificationStr == wxT( "RIGHT" ) )
return JUSTIFICATION::RIGHT;
else if( justificationStr == wxT( "CENTER" ) )
return JUSTIFICATION::CENTER;
else
THROW_UNKNOWN_PARAMETER_IO_ERROR( justificationStr, wxT( "JUSTIFICATION" ) );
return JUSTIFICATION::LEFT;
}
CADSTAR_ARCHIVE_PARSER::READABILITY CADSTAR_ARCHIVE_PARSER::ParseReadability( XNODE* aNode )
{
wxASSERT( aNode->GetName() == wxT( "READABILITY" ) );
wxString readabilityStr = GetXmlAttributeIDString( aNode, 0 );
if( readabilityStr == wxT( "BOTTOM_TO_TOP" ) )
return READABILITY::BOTTOM_TO_TOP;
else if( readabilityStr == wxT( "TOP_TO_BOTTOM" ) )
return READABILITY::TOP_TO_BOTTOM;
else
THROW_UNKNOWN_PARAMETER_IO_ERROR( readabilityStr, wxT( "READABILITY" ) );
return READABILITY::BOTTOM_TO_TOP;
}
void CADSTAR_ARCHIVE_PARSER::ATTRIBUTE_LOCATION::ParseIdentifiers( XNODE* aNode,
PARSER_CONTEXT* aContext )
{
TextCodeID = GetXmlAttributeIDString( aNode, 0 );
LayerID = GetXmlAttributeIDString( aNode, 1 );
}
bool CADSTAR_ARCHIVE_PARSER::ATTRIBUTE_LOCATION::ParseSubNode( XNODE* aChildNode,
PARSER_CONTEXT* aContext )
{
wxString cNodeName = aChildNode->GetName();
if( cNodeName == wxT( "PT" ) )
Position.Parse( aChildNode, aContext );
else if( cNodeName == wxT( "ORIENT" ) )
OrientAngle = GetXmlAttributeIDLong( aChildNode, 0 );
else if( cNodeName == wxT( "MIRROR" ) )
Mirror = true;
else if( cNodeName == wxT( "FIX" ) )
Fixed = true;
else if( cNodeName == wxT( "ALIGN" ) )
Alignment = ParseAlignment( aChildNode );
else if( cNodeName == wxT( "JUSTIFICATION" ) )
Justification = ParseJustification( aChildNode );
else
return false;
return true;
}
void CADSTAR_ARCHIVE_PARSER::ATTRIBUTE_LOCATION::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "ATTRLOC" ) );
ParseIdentifiers( aNode, aContext );
//Parse child nodes
XNODE* cNode = aNode->GetChildren();
for( ; cNode; cNode = cNode->GetNext() )
{
if( ParseSubNode( cNode, aContext ) )
continue;
else
THROW_UNKNOWN_NODE_IO_ERROR( cNode->GetName(), wxT( "ATTRLOC" ) );
}
if( Position.x == UNDEFINED_VALUE || Position.y == UNDEFINED_VALUE )
THROW_MISSING_NODE_IO_ERROR( wxT( "PT" ), wxT( "ATTRLOC" ) );
}
void CADSTAR_ARCHIVE_PARSER::ATTRNAME::COLUMNORDER::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "COLUMNORDER" ) );
ID = GetXmlAttributeIDLong( aNode, 0 );
Order = GetXmlAttributeIDLong( aNode, 1 );
CheckNoChildNodes( aNode );
}
void CADSTAR_ARCHIVE_PARSER::ATTRNAME::COLUMNWIDTH::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "COLUMNWIDTH" ) );
ID = GetXmlAttributeIDLong( aNode, 0 );
Width = GetXmlAttributeIDLong( aNode, 1 );
CheckNoChildNodes( aNode );
}
void CADSTAR_ARCHIVE_PARSER::ATTRNAME::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "ATTRNAME" ) );
ID = GetXmlAttributeIDString( aNode, 0 );
Name = GetXmlAttributeIDString( aNode, 1 );
XNODE* cNode = aNode->GetChildren();
wxString location = wxString::Format( "ATTRNAME -> %s", Name );
for( ; cNode; cNode = cNode->GetNext() )
{
wxString cNodeName = cNode->GetName();
if( cNodeName == wxT( "ATTROWNER" ) )
{
wxString attOwnerVal = GetXmlAttributeIDString( cNode, 0 );
if( attOwnerVal == wxT( "ALL_ITEMS" ) )
AttributeOwner = ATTROWNER::ALL_ITEMS;
else if( attOwnerVal == wxT( "AREA" ) )
AttributeOwner = ATTROWNER::AREA;
else if( attOwnerVal == wxT( "BOARD" ) )
AttributeOwner = ATTROWNER::BOARD;
else if( attOwnerVal == wxT( "COMPONENT" ) )
AttributeOwner = ATTROWNER::COMPONENT;
else if( attOwnerVal == wxT( "CONNECTION" ) )
AttributeOwner = ATTROWNER::CONNECTION;
else if( attOwnerVal == wxT( "COPPER" ) )
AttributeOwner = ATTROWNER::COPPER;
else if( attOwnerVal == wxT( "DOCSYMBOL" ) )
AttributeOwner = ATTROWNER::DOCSYMBOL;
else if( attOwnerVal == wxT( "FIGURE" ) )
AttributeOwner = ATTROWNER::FIGURE;
else if( attOwnerVal == wxT( "NET" ) )
AttributeOwner = ATTROWNER::NET;
else if( attOwnerVal == wxT( "NETCLASS" ) )
AttributeOwner = ATTROWNER::NETCLASS;
else if( attOwnerVal == wxT( "PART" ) )
AttributeOwner = ATTROWNER::PART;
else if( attOwnerVal == wxT( "PART_DEFINITION" ) )
AttributeOwner = ATTROWNER::PART_DEFINITION;
else if( attOwnerVal == wxT( "PIN" ) )
AttributeOwner = ATTROWNER::PIN;
else if( attOwnerVal == wxT( "SIGNALREF" ) )
AttributeOwner = ATTROWNER::SIGNALREF;
else if( attOwnerVal == wxT( "SYMBOL" ) )
AttributeOwner = ATTROWNER::SYMBOL;
else if( attOwnerVal == wxT( "SYMDEF" ) )
AttributeOwner = ATTROWNER::SYMDEF;
else if( attOwnerVal == wxT( "TEMPLATE" ) )
AttributeOwner = ATTROWNER::TEMPLATE;
else if( attOwnerVal == wxT( "TESTPOINT" ) )
AttributeOwner = ATTROWNER::TESTPOINT;
else
THROW_UNKNOWN_PARAMETER_IO_ERROR( attOwnerVal, location );
}
else if( cNodeName == wxT( "ATTRUSAGE" ) )
{
wxString attUsageVal = GetXmlAttributeIDString( cNode, 0 );
if( attUsageVal == wxT( "BOTH" ) )
AttributeUsage = ATTRUSAGE::BOTH;
else if( attUsageVal == wxT( "COMPONENT" ) )
AttributeUsage = ATTRUSAGE::COMPONENT;
else if( attUsageVal == wxT( "PART_DEFINITION" ) )
AttributeUsage = ATTRUSAGE::PART_DEFINITION;
else if( attUsageVal == wxT( "PART_LIBRARY" ) )
AttributeUsage = ATTRUSAGE::PART_LIBRARY;
else if( attUsageVal == wxT( "SYMBOL" ) )
AttributeUsage = ATTRUSAGE::SYMBOL;
else
THROW_UNKNOWN_PARAMETER_IO_ERROR( attUsageVal, location );
}
else if( cNodeName == wxT( "NOTRANSFER" ) )
{
NoTransfer = true;
}
else if( cNodeName == wxT( "COLUMNORDER" ) )
{
COLUMNORDER cOrder;
cOrder.Parse( cNode, aContext );
ColumnOrders.push_back( cOrder );
}
else if( cNodeName == wxT( "COLUMNWIDTH" ) )
{
COLUMNWIDTH cWidth;
cWidth.Parse( cNode, aContext );
ColumnWidths.push_back( cWidth );
}
else if( cNodeName == wxT( "COLUMNINVISIBLE" ) )
{
ColumnInvisible = true;
}
else
{
THROW_UNKNOWN_NODE_IO_ERROR( cNodeName, location );
}
}
}
void CADSTAR_ARCHIVE_PARSER::ATTRIBUTE_VALUE::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "ATTR" ) );
AttributeID = GetXmlAttributeIDString( aNode, 0 );
Value = GetXmlAttributeIDString( aNode, 1 );
XNODE* cNode = aNode->GetChildren();
for( ; cNode; cNode = cNode->GetNext() )
{
if( cNode->GetName() == wxT( "READONLY" ) )
{
ReadOnly = true;
}
else if( cNode->GetName() == wxT( "ATTRLOC" ) )
{
AttributeLocation.Parse( cNode, aContext );
HasLocation = true;
}
else
{
THROW_UNKNOWN_NODE_IO_ERROR( cNode->GetName(), wxT( "ATTR" ) );
}
}
}
void CADSTAR_ARCHIVE_PARSER::TEXT_LOCATION::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "TEXTLOC" ) );
wxString attributeStr = GetXmlAttributeIDString( aNode, 0 );
bool attributeIDisSet = false;
if( attributeStr == wxT( "PART_NAME" ) )
{
AttributeID = PART_NAME_ATTRID;
attributeIDisSet = true;
}
else if( attributeStr == wxT( "COMP_NAME" ) )
{
AttributeID = COMPONENT_NAME_ATTRID;
attributeIDisSet = true;
}
else if( attributeStr == wxT( "COMP_NAME2" ) )
{
AttributeID = COMPONENT_NAME_2_ATTRID;
attributeIDisSet = true;
}
else if( attributeStr == wxT( "SYMBOL_NAME" ) )
{
AttributeID = SYMBOL_NAME_ATTRID;
attributeIDisSet = true;
}
else if( attributeStr == wxT( "LINK_ORIGIN" ) )
{
AttributeID = LINK_ORIGIN_ATTRID;
attributeIDisSet = true;
}
else if( attributeStr == wxT( "SIGNALNAME_ORIGIN" ) )
{
AttributeID = SIGNALNAME_ORIGIN_ATTRID;
attributeIDisSet = true;
}
else if( attributeStr == wxT( "ATTRREF" ) )
{
//We will initialise when we parse all child nodes
attributeIDisSet = false;
}
else
{
THROW_UNKNOWN_PARAMETER_IO_ERROR( attributeStr, wxT( "TEXTLOC" ) );
}
TextCodeID = GetXmlAttributeIDString( aNode, 1 );
LayerID = GetXmlAttributeIDString( aNode, 2, false );
//Parse child nodes
XNODE* cNode = aNode->GetChildren();
for( ; cNode; cNode = cNode->GetNext() )
{
wxString cNodeName = cNode->GetName();
if( ParseSubNode( cNode, aContext ) )
{
continue;
}
else if( !attributeIDisSet && cNodeName == wxT( "ATTRREF" ) )
{
AttributeID = GetXmlAttributeIDString( cNode, 0 );
attributeIDisSet = true;
}
else if( cNodeName == wxT( "ORIENT" ) )
{
OrientAngle = GetXmlAttributeIDLong( cNode, 0 );
}
else if( cNodeName == wxT( "MIRROR" ) )
{
Mirror = true;
}
else if( cNodeName == wxT( "FIX" ) )
{
Fixed = true;
}
else if( cNodeName == wxT( "ALIGN" ) )
{
Alignment = ParseAlignment( cNode );
}
else if( cNodeName == wxT( "JUSTIFICATION" ) )
{
Justification = ParseJustification( cNode );
}
else
{
THROW_UNKNOWN_NODE_IO_ERROR( cNodeName, wxT( "TEXTLOC" ) );
}
}
if( Position.x == UNDEFINED_VALUE || Position.y == UNDEFINED_VALUE )
THROW_MISSING_NODE_IO_ERROR( wxT( "PT" ), wxT( "TEXTLOC" ) );
}
void CADSTAR_ARCHIVE_PARSER::CADSTAR_NETCLASS::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "NETCLASS" ) );
ID = GetXmlAttributeIDString( aNode, 0 );
Name = GetXmlAttributeIDString( aNode, 1 );
XNODE* cNode = aNode->GetChildren();
wxString location = wxString::Format( "NETCLASS -> %s", Name );
for( ; cNode; cNode = cNode->GetNext() )
{
wxString cNodeName = cNode->GetName();
if( cNodeName == wxT( "ATTR" ) )
{
ATTRIBUTE_VALUE attribute_val;
attribute_val.Parse( cNode, aContext );
Attributes.push_back( attribute_val );
}
else
{
THROW_UNKNOWN_NODE_IO_ERROR( cNodeName, location );
}
}
}
void CADSTAR_ARCHIVE_PARSER::SPCCLASSNAME::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "SPCCLASSNAME" ) );
ID = GetXmlAttributeIDString( aNode, 0 );
Name = GetXmlAttributeIDString( aNode, 1 );
}
bool CADSTAR_ARCHIVE_PARSER::CODEDEFS::ParseSubNode( XNODE* aChildNode, PARSER_CONTEXT* aContext )
{
wxString nodeName = aChildNode->GetName();
if( nodeName == wxT( "LINECODE" ) )
{
LINECODE linecode;
linecode.Parse( aChildNode, aContext );
LineCodes.insert( std::make_pair( linecode.ID, linecode ) );
}
else if( nodeName == wxT( "HATCHCODE" ) )
{
HATCHCODE hatchcode;
hatchcode.Parse( aChildNode, aContext );
HatchCodes.insert( std::make_pair( hatchcode.ID, hatchcode ) );
}
else if( nodeName == wxT( "TEXTCODE" ) )
{
TEXTCODE textcode;
textcode.Parse( aChildNode, aContext );
TextCodes.insert( std::make_pair( textcode.ID, textcode ) );
}
else if( nodeName == wxT( "ROUTECODE" ) )
{
ROUTECODE routecode;
routecode.Parse( aChildNode, aContext );
RouteCodes.insert( std::make_pair( routecode.ID, routecode ) );
}
else if( nodeName == wxT( "ATTRNAME" ) )
{
ATTRNAME attrname;
attrname.Parse( aChildNode, aContext );
AttributeNames.insert( std::make_pair( attrname.ID, attrname ) );
}
else if( nodeName == wxT( "NETCLASS" ) )
{
CADSTAR_NETCLASS netclass;
netclass.Parse( aChildNode, aContext );
NetClasses.insert( std::make_pair( netclass.ID, netclass ) );
}
else if( nodeName == wxT( "SPCCLASSNAME" ) )
{
SPCCLASSNAME spcclassname;
spcclassname.Parse( aChildNode, aContext );
SpacingClassNames.insert( std::make_pair( spcclassname.ID, spcclassname ) );
}
else
{
return false;
}
return true;
}
CADSTAR_ARCHIVE_PARSER::SWAP_RULE CADSTAR_ARCHIVE_PARSER::ParseSwapRule( XNODE* aNode )
{
wxASSERT( aNode->GetName() == wxT( "SWAPRULE" ) );
SWAP_RULE retval;
wxString swapRuleStr = GetXmlAttributeIDString( aNode, 0 );
if( swapRuleStr == wxT( "NO_SWAP" ) )
retval = SWAP_RULE::NO_SWAP;
else if( swapRuleStr == wxT( "USE_SWAP_LAYER" ) )
retval = SWAP_RULE::USE_SWAP_LAYER;
else
THROW_UNKNOWN_PARAMETER_IO_ERROR( swapRuleStr, wxT( "SWAPRULE" ) );
return retval;
}
void CADSTAR_ARCHIVE_PARSER::REUSEBLOCK::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "REUSEBLOCK" ) );
ID = GetXmlAttributeIDString( aNode, 0 );
Name = GetXmlAttributeIDString( aNode, 1 );
FileName = GetXmlAttributeIDString( aNode, 2 );
XNODE* cNode = aNode->GetChildren();
for( ; cNode; cNode = cNode->GetNext() )
{
wxString cNodeName = cNode->GetName();
if( cNodeName == wxT( "MIRROR" ) )
Mirror = true;
else if( cNodeName == wxT( "ORIENT" ) )
OrientAngle = GetXmlAttributeIDLong( cNode, 0 );
else
THROW_UNKNOWN_NODE_IO_ERROR( cNodeName, wxT( "REUSEBLOCK" ) );
}
}
bool CADSTAR_ARCHIVE_PARSER::REUSEBLOCKREF::IsEmpty()
{
return ReuseBlockID == wxEmptyString && ItemReference == wxEmptyString;
}
void CADSTAR_ARCHIVE_PARSER::REUSEBLOCKREF::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "REUSEBLOCKREF" ) );
ReuseBlockID = GetXmlAttributeIDString( aNode, 0 );
ItemReference = GetXmlAttributeIDString( aNode, 1 );
CheckNoChildNodes( aNode );
}
void CADSTAR_ARCHIVE_PARSER::GROUP::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "GROUP" ) );
ID = GetXmlAttributeIDString( aNode, 0 );
Name = GetXmlAttributeIDString( aNode, 1 );
XNODE* cNode = aNode->GetChildren();
for( ; cNode; cNode = cNode->GetNext() )
{
wxString cNodeName = cNode->GetName();
if( cNodeName == wxT( "FIX" ) )
Fixed = true;
else if( cNodeName == wxT( "TRANSFER" ) )
Transfer = true;
else if( cNodeName == wxT( "GROUPREF" ) )
GroupID = GetXmlAttributeIDString( cNode, 0 );
else if( cNodeName == wxT( "REUSEBLOCKREF" ) )
ReuseBlockRef.Parse( cNode, aContext );
else
THROW_UNKNOWN_NODE_IO_ERROR( cNodeName, wxT( "GROUP" ) );
}
}
void CADSTAR_ARCHIVE_PARSER::FIGURE::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "FIGURE" ) );
ID = GetXmlAttributeIDString( aNode, 0 );
LineCodeID = GetXmlAttributeIDString( aNode, 1 );
LayerID = GetXmlAttributeIDString( aNode, 2 );
XNODE* cNode = aNode->GetChildren();
bool shapeIsInitialised = false; // Stop more than one Shape Object
wxString location = wxString::Format( "Figure %s", ID );
if( !cNode )
THROW_MISSING_NODE_IO_ERROR( wxT( "Shape" ), location );
for( ; cNode; cNode = cNode->GetNext() )
{
wxString cNodeName = cNode->GetName();
if( !shapeIsInitialised && Shape.IsShape( cNode ) )
{
Shape.Parse( cNode, aContext );
shapeIsInitialised = true;
}
else if( cNodeName == wxT( "SWAPRULE" ) )
{
SwapRule = ParseSwapRule( cNode );
}
else if( cNodeName == wxT( "FIX" ) )
{
Fixed = true;
}
else if( cNodeName == wxT( "GROUPREF" ) )
{
GroupID = GetXmlAttributeIDString( cNode, 0 );
}
else if( cNodeName == wxT( "REUSEBLOCKREF" ) )
{
ReuseBlockRef.Parse( cNode, aContext );
}
else if( cNodeName == wxT( "ATTR" ) )
{
ATTRIBUTE_VALUE attr;
attr.Parse( cNode, aContext );
AttributeValues.insert( std::make_pair( attr.AttributeID, attr ) );
}
else
{
THROW_UNKNOWN_NODE_IO_ERROR( cNodeName, location );
}
}
}
void CADSTAR_ARCHIVE_PARSER::TEXT::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
Parse( aNode, aContext, true );
}
void CADSTAR_ARCHIVE_PARSER::TEXT::Parse( XNODE* aNode, PARSER_CONTEXT* aContext,
bool aParseFields )
{
wxASSERT( aNode->GetName() == wxT( "TEXT" ) );
ID = GetXmlAttributeIDString( aNode, 0 );
Text = GetXmlAttributeIDString( aNode, 1 );
if( aParseFields )
Text = ParseTextFields( Text, aContext );
TextCodeID = GetXmlAttributeIDString( aNode, 2 );
LayerID = GetXmlAttributeIDString( aNode, 3 );
XNODE* cNode = aNode->GetChildren();
if( !cNode )
THROW_MISSING_NODE_IO_ERROR( wxT( "PT" ), wxT( "TEXT" ) );
for( ; cNode; cNode = cNode->GetNext() )
{
wxString cNodeName = cNode->GetName();
if( cNodeName == wxT( "PT" ) )
Position.Parse( cNode, aContext );
else if( cNodeName == wxT( "ORIENT" ) )
OrientAngle = GetXmlAttributeIDLong( cNode, 0 );
else if( cNodeName == wxT( "MIRROR" ) )
Mirror = true;
else if( cNodeName == wxT( "FIX" ) )
Fixed = true;
else if( cNodeName == wxT( "SWAPRULE" ) )
SwapRule = ParseSwapRule( cNode );
else if( cNodeName == wxT( "ALIGN" ) )
Alignment = ParseAlignment( cNode );
else if( cNodeName == wxT( "JUSTIFICATION" ) )
Justification = ParseJustification( cNode );
else if( cNodeName == wxT( "GROUPREF" ) )
GroupID = GetXmlAttributeIDString( cNode, 0 );
else if( cNodeName == wxT( "REUSEBLOCKREF" ) )
ReuseBlockRef.Parse( cNode, aContext );
else
THROW_UNKNOWN_NODE_IO_ERROR( cNodeName, wxT( "TEXT" ) );
}
}
wxString CADSTAR_ARCHIVE_PARSER::SYMDEF::BuildLibName() const
{
return generateLibName( ReferenceName, Alternate );
}
void CADSTAR_ARCHIVE_PARSER::SYMDEF::ParseIdentifiers( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "SYMDEF" ) );
ID = GetXmlAttributeIDString( aNode, 0 );
ReferenceName = GetXmlAttributeIDString( aNode, 1 );
Alternate = GetXmlAttributeIDString( aNode, 2 );
}
bool CADSTAR_ARCHIVE_PARSER::SYMDEF::ParseSubNode( XNODE* aChildNode, PARSER_CONTEXT* aContext )
{
wxString cNodeName = aChildNode->GetName();
if( cNodeName == wxT( "PT" ) )
{
Origin.Parse( aChildNode, aContext );
}
else if( cNodeName == wxT( "STUB" ) )
{
Stub = true;
}
else if( cNodeName == wxT( "VERSION" ) )
{
Version = GetXmlAttributeIDLong( aChildNode, 0 );
}
else if( cNodeName == wxT( "FIGURE" ) )
{
FIGURE figure;
figure.Parse( aChildNode, aContext );
Figures.insert( std::make_pair( figure.ID, figure ) );
}
else if( cNodeName == wxT( "TEXT" ) )
{
TEXT txt;
txt.Parse( aChildNode, aContext );
Texts.insert( std::make_pair( txt.ID, txt ) );
}
else if( cNodeName == wxT( "TEXTLOC" ) )
{
TEXT_LOCATION textloc;
textloc.Parse( aChildNode, aContext );
TextLocations.insert( std::make_pair( textloc.AttributeID, textloc ) );
}
else if( cNodeName == wxT( "ATTR" ) )
{
ATTRIBUTE_VALUE attrVal;
attrVal.Parse( aChildNode, aContext );
AttributeValues.insert( std::make_pair( attrVal.AttributeID, attrVal ) );
}
else
{
return false;
}
return true;
}
void CADSTAR_ARCHIVE_PARSER::PART::DEFINITION::GATE::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "GATEDEFINITION" ) );
ID = GetXmlAttributeIDString( aNode, 0 );
Name = GetXmlAttributeIDString( aNode, 1 );
Alternate = GetXmlAttributeIDString( aNode, 2 );
PinCount = GetXmlAttributeIDLong( aNode, 3 );
CheckNoChildNodes( aNode );
}
CADSTAR_PIN_TYPE CADSTAR_ARCHIVE_PARSER::PART::GetPinType( XNODE* aNode )
{
wxASSERT( aNode->GetName() == wxT( "PINTYPE" ) );
wxString pinTypeStr = GetXmlAttributeIDString( aNode, 0 );
std::map<wxString, CADSTAR_PIN_TYPE> pinTypeMap = {
{ wxT( "INPUT" ), CADSTAR_PIN_TYPE::INPUT },
{ wxT( "OUTPUT_OR" ), CADSTAR_PIN_TYPE::OUTPUT_OR },
{ wxT( "OUTPUT_NOT_OR" ), CADSTAR_PIN_TYPE::OUTPUT_NOT_OR },
{ wxT( "OUTPUT_NOT_NORM_OR" ), CADSTAR_PIN_TYPE::OUTPUT_NOT_NORM_OR },
{ wxT( "POWER" ), CADSTAR_PIN_TYPE::POWER },
{ wxT( "GROUND" ), CADSTAR_PIN_TYPE::GROUND },
{ wxT( "TRISTATE_BIDIR" ), CADSTAR_PIN_TYPE::TRISTATE_BIDIR },
{ wxT( "TRISTATE_INPUT" ), CADSTAR_PIN_TYPE::TRISTATE_INPUT },
{ wxT( "TRISTATE_DRIVER" ), CADSTAR_PIN_TYPE::TRISTATE_DRIVER } };
if( pinTypeMap.find( pinTypeStr ) == pinTypeMap.end() )
THROW_UNKNOWN_PARAMETER_IO_ERROR( pinTypeStr, aNode->GetName() );
return pinTypeMap[pinTypeStr];
}
void CADSTAR_ARCHIVE_PARSER::PART::DEFINITION::PIN::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "PARTDEFINITIONPIN" ) );
ID = GetXmlAttributeIDLong( aNode, 0 );
XNODE* cNode = aNode->GetChildren();
for( ; cNode; cNode = cNode->GetNext() )
{
wxString cNodeName = cNode->GetName();
if( cNodeName == wxT( "PINNAME" ) )
{
Name = GetXmlAttributeIDString( cNode, 0 );
}
else if( cNodeName == wxT( "PINLABEL" ) )
{
Label = GetXmlAttributeIDString( cNode, 0 );
}
else if( cNodeName == wxT( "PINSIGNAL" ) )
{
Signal = GetXmlAttributeIDString( cNode, 0 );
}
else if( cNodeName == wxT( "PINTERM" ) )
{
TerminalGate = GetXmlAttributeIDString( cNode, 0 );
TerminalPin = GetXmlAttributeIDLong( cNode, 1 );
}
else if( cNodeName == wxT( "PINTYPE" ) )
{
Type = GetPinType( cNode );
}
else if( cNodeName == wxT( "PINLOAD" ) )
{
Load = GetXmlAttributeIDLong( cNode, 0 );
}
else if( cNodeName == wxT( "PINPOSITION" ) )
{
Position = CADSTAR_PIN_POSITION( GetXmlAttributeIDLong( cNode, 0 ) );
}
else if( cNodeName == wxT( "PINIDENTIFIER" ) )
{
Identifier = GetXmlAttributeIDString( cNode, 0 );
}
else
{
THROW_UNKNOWN_NODE_IO_ERROR( cNodeName, aNode->GetName() );
}
}
}
void CADSTAR_ARCHIVE_PARSER::PART::PART_PIN::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "PARTPIN" ) );
ID = GetXmlAttributeIDLong( aNode, 0 );
XNODE* cNode = aNode->GetChildren();
for( ; cNode; cNode = cNode->GetNext() )
{
wxString cNodeName = cNode->GetName();
if( cNodeName == wxT( "PINNAME" ) )
Name = GetXmlAttributeIDString( cNode, 0 );
else if( cNodeName == wxT( "PINTYPE" ) )
Type = GetPinType( cNode );
else if( cNodeName == wxT( "PINIDENTIFIER" ) )
Identifier = GetXmlAttributeIDString( cNode, 0 );
else
THROW_UNKNOWN_NODE_IO_ERROR( cNodeName, aNode->GetName() );
}
}
void CADSTAR_ARCHIVE_PARSER::PART::DEFINITION::PIN_EQUIVALENCE::Parse( XNODE* aNode,
PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "PINEQUIVALENCE" ) );
wxXmlAttribute* xmlAttribute = aNode->GetAttributes();
for( ; xmlAttribute; xmlAttribute = xmlAttribute->GetNext() )
{
if( !IsValidAttribute( xmlAttribute ) )
continue;
long pinId;
if( !xmlAttribute->GetValue().ToLong( &pinId ) )
THROW_UNKNOWN_PARAMETER_IO_ERROR( xmlAttribute->GetValue(), aNode->GetName() );
PinIDs.push_back( (PART_DEFINITION_PIN_ID) pinId );
}
CheckNoChildNodes( aNode );
}
void CADSTAR_ARCHIVE_PARSER::PART::DEFINITION::SWAP_GATE::Parse( XNODE* aNode,
PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "SWAPGATE" ) );
wxXmlAttribute* xmlAttribute = aNode->GetAttributes();
for( ; xmlAttribute; xmlAttribute = xmlAttribute->GetNext() )
{
if( !IsValidAttribute( xmlAttribute ) )
continue;
long pinId;
if( !xmlAttribute->GetValue().ToLong( &pinId ) )
THROW_UNKNOWN_PARAMETER_IO_ERROR( xmlAttribute->GetValue(), aNode->GetName() );
PinIDs.push_back( (PART_DEFINITION_PIN_ID) pinId );
}
CheckNoChildNodes( aNode );
}
void CADSTAR_ARCHIVE_PARSER::PART::DEFINITION::SWAP_GROUP::Parse( XNODE* aNode,
PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "SWAPGROUP" ) );
GateName = GetXmlAttributeIDString( aNode, 0 );
XNODE* cNode = aNode->GetChildren();
for( ; cNode; cNode = cNode->GetNext() )
{
wxString cNodeName = cNode->GetName();
if( cNodeName == wxT( "EXTERNAL" ) )
{
External = true;
}
else if( cNodeName == wxT( "SWAPGATE" ) )
{
SWAP_GATE swapGate;
swapGate.Parse( cNode, aContext );
SwapGates.push_back( swapGate );
}
else
{
THROW_UNKNOWN_NODE_IO_ERROR( cNodeName, aNode->GetName() );
}
}
}
void CADSTAR_ARCHIVE_PARSER::PART::DEFINITION::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "PARTDEFINITION" ) );
Name = GetXmlAttributeIDString( aNode, 0 );
XNODE* cNode = aNode->GetChildren();
for( ; cNode; cNode = cNode->GetNext() )
{
wxString cNodeName = cNode->GetName();
if( cNodeName == wxT( "HIDEPINNAMES" ) )
{
HidePinNames = true;
}
else if( cNodeName == wxT( "MAXPIN" ) )
{
MaxPinCount = GetXmlAttributeIDLong( cNode, 0 );
}
else if( cNodeName == wxT( "GATEDEFINITION" ) )
{
GATE gate;
gate.Parse( cNode, aContext );
GateSymbols.insert( std::make_pair( gate.ID, gate ) );
}
else if( cNodeName == wxT( "PARTDEFINITIONPIN" ) )
{
PIN pin;
pin.Parse( cNode, aContext );
Pins.insert( std::make_pair( pin.ID, pin ) );
}
else if( cNodeName == wxT( "ATTR" ) )
{
ATTRIBUTE_VALUE attr;
attr.Parse( cNode, aContext );
AttributeValues.insert( std::make_pair( attr.AttributeID, attr ) );
}
else if( cNodeName == wxT( "PINEQUIVALENCE" ) )
{
PIN_EQUIVALENCE pinEq;
pinEq.Parse( cNode, aContext );
PinEquivalences.push_back( pinEq );
}
else if( cNodeName == wxT( "SWAPGROUP" ) )
{
SWAP_GROUP swapGroup;
swapGroup.Parse( cNode, aContext );
SwapGroups.push_back( swapGroup );
}
else
{
THROW_UNKNOWN_NODE_IO_ERROR( cNodeName, aNode->GetName() );
}
}
}
void CADSTAR_ARCHIVE_PARSER::PART::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "PART" ) );
ID = GetXmlAttributeIDString( aNode, 0 );
Name = GetXmlAttributeIDString( aNode, 1 );
XNODE* cNode = aNode->GetChildren();
for( ; cNode; cNode = cNode->GetNext() )
{
wxString cNodeName = cNode->GetName();
if( cNodeName == wxT( "VERSION" ) )
{
Version = GetXmlAttributeIDLong( cNode, 0 );
}
else if( cNodeName == wxT( "HIDEPINNAMES" ) )
{
HidePinNames = true;
}
else if( cNodeName == wxT( "PARTDEFINITION" ) )
{
Definition.Parse( cNode, aContext );
}
else if( cNodeName == wxT( "PARTPIN" ) )
{
PART_PIN pin;
pin.Parse( cNode, aContext );
PartPins.insert( std::make_pair( pin.ID, pin ) );
}
else if( cNodeName == wxT( "ATTR" ) )
{
ATTRIBUTE_VALUE attr;
attr.Parse( cNode, aContext );
AttributeValues.insert( std::make_pair( attr.AttributeID, attr ) );
}
else
{
THROW_UNKNOWN_NODE_IO_ERROR( cNodeName, aNode->GetName() );
}
}
}
void CADSTAR_ARCHIVE_PARSER::PARTS::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "PARTS" ) );
XNODE* cNode = aNode->GetChildren();
for( ; cNode; cNode = cNode->GetNext() )
{
wxString cNodeName = cNode->GetName();
if( cNodeName == wxT( "PART" ) )
{
PART part;
part.Parse( cNode, aContext );
PartDefinitions.insert( std::make_pair( part.ID, part ) );
}
else
{
THROW_UNKNOWN_NODE_IO_ERROR( cNodeName, aNode->GetName() );
}
aContext->CheckPointCallback();
}
}
void CADSTAR_ARCHIVE_PARSER::NET::JUNCTION::ParseIdentifiers( XNODE* aNode,
PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "JPT" ) );
ID = GetXmlAttributeIDString( aNode, 0 );
LayerID = GetXmlAttributeIDString( aNode, 1 );
}
bool CADSTAR_ARCHIVE_PARSER::NET::JUNCTION::ParseSubNode( XNODE* aChildNode,
PARSER_CONTEXT* aContext )
{
wxString cNodeName = aChildNode->GetName();
if( cNodeName == wxT( "PT" ) )
Location.Parse( aChildNode, aContext );
else if( cNodeName == wxT( "FIX" ) )
Fixed = true;
else if( cNodeName == wxT( "GROUPREF" ) )
GroupID = GetXmlAttributeIDString( aChildNode, 0 );
else if( cNodeName == wxT( "REUSEBLOCKREF" ) )
ReuseBlockRef.Parse( aChildNode, aContext );
else
return false;
return true;
}
void CADSTAR_ARCHIVE_PARSER::NET::JUNCTION::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
ParseIdentifiers( aNode, aContext );
XNODE* cNode = aNode->GetChildren();
for( ; cNode; cNode = cNode->GetNext() )
{
if( ParseSubNode( cNode, aContext ) )
continue;
else
THROW_UNKNOWN_NODE_IO_ERROR( cNode->GetName(), aNode->GetName() );
}
}
void CADSTAR_ARCHIVE_PARSER::NET::CONNECTION::ParseIdentifiers( XNODE* aNode,
PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "CONN" ) );
StartNode = GetXmlAttributeIDString( aNode, 0 );
EndNode = GetXmlAttributeIDString( aNode, 1 );
RouteCodeID = GetXmlAttributeIDString( aNode, 2 );
}
bool CADSTAR_ARCHIVE_PARSER::NET::CONNECTION::ParseSubNode( XNODE* aChildNode,
PARSER_CONTEXT* aContext )
{
wxString cNodeName = aChildNode->GetName();
if( cNodeName == wxT( "FIX" ) )
{
Fixed = true;
}
else if( cNodeName == wxT( "HIDDEN" ) )
{
Hidden = true;
}
else if( cNodeName == wxT( "GROUPREF" ) )
{
GroupID = GetXmlAttributeIDString( aChildNode, 0 );
}
else if( cNodeName == wxT( "REUSEBLOCKREF" ) )
{
ReuseBlockRef.Parse( aChildNode, aContext );
}
else if( cNodeName == wxT( "ATTR" ) )
{
ATTRIBUTE_VALUE attrVal;
attrVal.Parse( aChildNode, aContext );
AttributeValues.insert( std::make_pair( attrVal.AttributeID, attrVal ) );
}
else
{
return false;
}
return true;
}
void CADSTAR_ARCHIVE_PARSER::NET::ParseIdentifiers( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "NET" ) );
ID = GetXmlAttributeIDString( aNode, 0 );
}
bool CADSTAR_ARCHIVE_PARSER::NET::ParseSubNode( XNODE* aChildNode, PARSER_CONTEXT* aContext )
{
wxString cNodeName = aChildNode->GetName();
if( cNodeName == wxT( "NETCODE" ) )
{
RouteCodeID = GetXmlAttributeIDString( aChildNode, 0 );
}
else if( cNodeName == wxT( "SIGNAME" ) )
{
Name = GetXmlAttributeIDString( aChildNode, 0 );
}
else if( cNodeName == wxT( "SIGNUM" ) )
{
SignalNum = GetXmlAttributeIDLong( aChildNode, 0 );
}
else if( cNodeName == wxT( "HIGHLIT" ) )
{
Highlight = true;
}
else if( cNodeName == wxT( "JPT" ) )
{
JUNCTION jpt;
jpt.Parse( aChildNode, aContext );
Junctions.insert( std::make_pair( jpt.ID, jpt ) );
}
else if( cNodeName == wxT( "NETCLASSREF" ) )
{
NetClassID = GetXmlAttributeIDString( aChildNode, 0 );
}
else if( cNodeName == wxT( "SPACINGCLASS" ) )
{
SpacingClassID = GetXmlAttributeIDString( aChildNode, 0 );
}
else if( cNodeName == wxT( "ATTR" ) )
{
ATTRIBUTE_VALUE attrVal;
attrVal.Parse( aChildNode, aContext );
AttributeValues.insert( std::make_pair( attrVal.AttributeID, attrVal ) );
}
else
{
return false;
}
return true;
}
void CADSTAR_ARCHIVE_PARSER::DOCUMENTATION_SYMBOL::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "DOCSYMBOL" ) );
ID = GetXmlAttributeIDString( aNode, 0 );
SymdefID = GetXmlAttributeIDString( aNode, 1 );
LayerID = GetXmlAttributeIDString( aNode, 2 );
XNODE* cNode = aNode->GetChildren();
bool originParsed = false;
for( ; cNode; cNode = cNode->GetNext() )
{
wxString cNodeName = cNode->GetName();
if( !originParsed && cNodeName == wxT( "PT" ) )
{
Origin.Parse( cNode, aContext );
originParsed = true;
}
else if( cNodeName == wxT( "GROUPREF" ) )
{
GroupID = GetXmlAttributeIDString( cNode, 0 );
}
else if( cNodeName == wxT( "REUSEBLOCKREF" ) )
{
ReuseBlockRef.Parse( cNode, aContext );
}
else if( cNodeName == wxT( "FIX" ) )
{
Fixed = true;
}
else if( cNodeName == wxT( "MIRROR" ) )
{
Mirror = true;
}
else if( cNodeName == wxT( "READABILITY" ) )
{
Readability = ParseReadability( cNode );
}
else if( cNodeName == wxT( "ORIENT" ) )
{
OrientAngle = GetXmlAttributeIDLong( cNode, 0 );
}
else if( cNodeName == wxT( "ATTR" ) )
{
ATTRIBUTE_VALUE attr;
attr.Parse( cNode, aContext );
AttributeValues.insert( std::make_pair( attr.AttributeID, attr ) );
}
else if( cNodeName == wxT( "SCALE" ) )
{
ScaleRatioNumerator = GetXmlAttributeIDLong( cNode, 0 );
ScaleRatioDenominator = GetXmlAttributeIDLong( cNode, 1 );
}
else
{
THROW_UNKNOWN_NODE_IO_ERROR( cNodeName, aNode->GetName() );
}
}
if( !originParsed )
THROW_MISSING_PARAMETER_IO_ERROR( wxT( "PT" ), aNode->GetName() );
}
void CADSTAR_ARCHIVE_PARSER::DFLTSETTINGS::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "DFLTSETTINGS" ) );
Color = GetXmlAttributeIDString( aNode, 0 );
XNODE* cNode = aNode->GetChildren();
for( ; cNode; cNode = cNode->GetNext() )
{
wxString cNodeName = cNode->GetName();
if( cNodeName == wxT( "INVISIBLE" ) )
{
IsVisible = false;
}
else
{
THROW_UNKNOWN_NODE_IO_ERROR( cNodeName, aNode->GetName() );
}
}
}
void CADSTAR_ARCHIVE_PARSER::ATTRCOL::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "ATTRCOL" ) );
AttributeID = GetXmlAttributeIDString( aNode, 0 );
Color = GetXmlAttributeIDString( aNode, 1 );
XNODE* cNode = aNode->GetChildren();
for( ; cNode; cNode = cNode->GetNext() )
{
wxString cNodeName = cNode->GetName();
if( cNodeName == wxT( "INVISIBLE" ) )
{
IsVisible = false;
}
else if( cNodeName == wxT( "NOTPICKABLE" ) )
{
IsPickable = false;
}
else
{
THROW_UNKNOWN_NODE_IO_ERROR( cNodeName, aNode->GetName() );
}
}
}
void CADSTAR_ARCHIVE_PARSER::ATTRCOLORS::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "ATTRCOLORS" ) );
XNODE* cNode = aNode->GetChildren();
for( ; cNode; cNode = cNode->GetNext() )
{
wxString cNodeName = cNode->GetName();
if( cNodeName == wxT( "DFLTSETTINGS" ) )
{
DefaultSettings.Parse( cNode, aContext );
}
else if( cNodeName == wxT( "ATTRCOL" ) )
{
ATTRCOL attrcol;
attrcol.Parse( cNode, aContext );
AttributeColors.insert( { attrcol.AttributeID, attrcol } );
}
else if( cNodeName == wxT( "INVISIBLE" ) )
{
IsVisible = false;
}
else
{
THROW_UNKNOWN_NODE_IO_ERROR( cNodeName, aNode->GetName() );
}
}
}
void CADSTAR_ARCHIVE_PARSER::PARTNAMECOL::Parse( XNODE* aNode, PARSER_CONTEXT* aContext )
{
wxASSERT( aNode->GetName() == wxT( "PARTNAMECOL" ) );
Color = GetXmlAttributeIDString( aNode, 0 );
XNODE* cNode = aNode->GetChildren();
for( ; cNode; cNode = cNode->GetNext() )
{
wxString cNodeName = cNode->GetName();
if( cNodeName == wxT( "INVISIBLE" ) )
{
IsVisible = false;
}
else if( cNodeName == wxT( "NOTPICKABLE" ) )
{
IsPickable = false;
}
else
{
THROW_UNKNOWN_NODE_IO_ERROR( cNodeName, aNode->GetName() );
}
}
}
void CADSTAR_ARCHIVE_PARSER::InsertAttributeAtEnd( XNODE* aNode, wxString aValue )
{
static const wxString c_numAttributes = wxT( "numAttributes" );
wxString result;
long numAttributes = 0;
if( aNode->GetAttribute( c_numAttributes, &result ) )
{
numAttributes = wxAtol( result );
aNode->DeleteAttribute( c_numAttributes );
++numAttributes;
}
#if wxUSE_UNICODE_WCHAR
std::wstring numAttrStr = std::to_wstring( numAttributes );
#else
std::string numAttrStr = std::to_string( numAttributes );
#endif
aNode->AddAttribute( c_numAttributes, numAttrStr );
wxString paramName = wxT( "attr" );
paramName << numAttrStr;
aNode->AddAttribute( paramName, aValue );
}
XNODE* CADSTAR_ARCHIVE_PARSER::LoadArchiveFile( const wxString& aFileName,
const wxString& aFileTypeIdentifier, PROGRESS_REPORTER* aProgressReporter )
{
KEYWORD emptyKeywords[1] = {};
XNODE* rootNode = nullptr;
XNODE* cNode = nullptr;
XNODE* iNode = nullptr;
int tok;
bool cadstarFileCheckDone = false;
wxString str;
wxCSConv win1252( wxT( "windows-1252" ) );
wxMBConv* conv = &win1252; // Initial testing suggests file encoding to be Windows-1252
// More samples required.
// Open the file and get the file size
FILE* fp = wxFopen( aFileName, wxT( "rt" ) );
if( !fp )
THROW_IO_ERROR( wxString::Format( _( "Cannot open file '%s'" ), aFileName ) );
fseek( fp, 0L, SEEK_END );
long fileSize = ftell( fp );
rewind( fp );
DSNLEXER lexer( emptyKeywords, 0, nullptr, fp, aFileName );
auto currentProgress = [&]() -> double
{
return static_cast<double>( ftell( fp ) ) / fileSize;
};
double previousReportedProgress = -1.0;
while( ( tok = lexer.NextTok() ) != DSN_EOF )
{
if( aProgressReporter && ( currentProgress() - previousReportedProgress ) > 0.01 )
{
if( !aProgressReporter->KeepRefreshing() )
{
delete rootNode;
THROW_IO_ERROR( _( "File import cancelled by user." ) );
}
aProgressReporter->SetCurrentProgress( currentProgress() );
previousReportedProgress = currentProgress();
}
if( tok == DSN_RIGHT )
{
cNode = iNode;
if( cNode )
{
iNode = cNode->GetParent();
}
else
{
//too many closing brackets
delete rootNode;
THROW_IO_ERROR( _( "The selected file is not valid or might be corrupt!" ) );
}
}
else if( tok == DSN_LEFT )
{
tok = lexer.NextTok();
str = wxString( lexer.CurText(), *conv );
cNode = new XNODE( wxXML_ELEMENT_NODE, str );
if( !rootNode )
rootNode = cNode;
if( iNode )
{
//we will add it as attribute as well as child node
InsertAttributeAtEnd( iNode, str );
iNode->AddChild( cNode );
}
else if( !cadstarFileCheckDone )
{
if( cNode->GetName() != aFileTypeIdentifier )
{
delete rootNode;
THROW_IO_ERROR( _( "The selected file is not valid or might be corrupt!" ) );
}
cadstarFileCheckDone = true;
}
iNode = cNode;
}
else if( iNode )
{
str = wxString( lexer.CurText(), *conv );
//Insert even if string is empty
InsertAttributeAtEnd( iNode, str );
}
else
{
//not enough closing brackets
delete rootNode;
THROW_IO_ERROR( _( "The selected file is not valid or might be corrupt!" ) );
}
}
// Not enough closing brackets
if( iNode != nullptr )
{
delete rootNode;
THROW_IO_ERROR( _( "The selected file is not valid or might be corrupt!" ) );
}
// Throw if no data was parsed
if( rootNode )
{
return rootNode;
}
else
{
delete rootNode;
THROW_IO_ERROR( _( "The selected file is not valid or might be corrupt!" ) );
}
return nullptr;
}
bool CADSTAR_ARCHIVE_PARSER::IsValidAttribute( wxXmlAttribute* aAttribute )
{
return aAttribute->GetName() != wxT( "numAttributes" );
}
wxString CADSTAR_ARCHIVE_PARSER::GetXmlAttributeIDString( XNODE* aNode, unsigned int aID,
bool aIsRequired )
{
#if wxUSE_UNICODE_WCHAR
std::wstring idStr = std::to_wstring( aID );
#else
std::string idStr = std::to_string( aID );
#endif
wxString attrName = wxS( "attr" );
attrName << idStr;
wxString retVal;
if( !aNode->GetAttribute( attrName, &retVal ) )
{
if( aIsRequired )
THROW_MISSING_PARAMETER_IO_ERROR( std::to_string( aID ), aNode->GetName() );
else
return wxEmptyString;
}
return retVal;
}
long CADSTAR_ARCHIVE_PARSER::GetXmlAttributeIDLong( XNODE* aNode, unsigned int aID,
bool aIsRequired )
{
long retVal;
bool success = GetXmlAttributeIDString( aNode, aID, aIsRequired ).ToLong( &retVal );
if( !success )
{
if( aIsRequired )
THROW_PARSING_IO_ERROR( std::to_string( aID ), aNode->GetName() );
else
return UNDEFINED_VALUE;
}
return retVal;
}
void CADSTAR_ARCHIVE_PARSER::CheckNoChildNodes( XNODE* aNode )
{
if( aNode && aNode->GetChildren() )
THROW_UNKNOWN_NODE_IO_ERROR( aNode->GetChildren()->GetName(), aNode->GetName() );
}
void CADSTAR_ARCHIVE_PARSER::CheckNoNextNodes( XNODE* aNode )
{
if( aNode && aNode->GetNext() )
THROW_UNKNOWN_NODE_IO_ERROR( aNode->GetNext()->GetName(), aNode->GetParent()->GetName() );
}
void CADSTAR_ARCHIVE_PARSER::ParseChildEValue( XNODE* aNode, PARSER_CONTEXT* aContext,
EVALUE& aValueToParse )
{
if( aNode->GetChildren()->GetName() == wxT( "E" ) )
aValueToParse.Parse( aNode->GetChildren(), aContext );
else
THROW_UNKNOWN_NODE_IO_ERROR( aNode->GetChildren()->GetName(), aNode->GetName() );
}
std::vector<CADSTAR_ARCHIVE_PARSER::POINT> CADSTAR_ARCHIVE_PARSER::ParseAllChildPoints(
XNODE* aNode, PARSER_CONTEXT* aContext, bool aTestAllChildNodes, int aExpectedNumPoints )
{
std::vector<POINT> retVal;
XNODE* cNode = aNode->GetChildren();
for( ; cNode; cNode = cNode->GetNext() )
{
if( cNode->GetName() == wxT( "PT" ) )
{
POINT pt;
//TODO try.. catch + throw again with more detailed error information
pt.Parse( cNode, aContext );
retVal.push_back( pt );
}
else if( aTestAllChildNodes )
{
THROW_UNKNOWN_NODE_IO_ERROR( cNode->GetName(), aNode->GetName() );
}
}
if( aExpectedNumPoints != UNDEFINED_VALUE
&& retVal.size() != static_cast<size_t>( aExpectedNumPoints ) )
{
THROW_IO_ERROR( wxString::Format(
_( "Unexpected number of points in '%s'. Found %d but expected %d." ),
aNode->GetName(), retVal.size(), aExpectedNumPoints ) );
}
return retVal;
}
std::vector<CADSTAR_ARCHIVE_PARSER::VERTEX> CADSTAR_ARCHIVE_PARSER::ParseAllChildVertices(
XNODE* aNode, PARSER_CONTEXT* aContext, bool aTestAllChildNodes )
{
std::vector<VERTEX> retVal;
XNODE* cNode = aNode->GetChildren();
for( ; cNode; cNode = cNode->GetNext() )
{
if( VERTEX::IsVertex( cNode ) )
{
VERTEX vertex;
//TODO try.. catch + throw again with more detailed error information
vertex.Parse( cNode, aContext );
retVal.push_back( vertex );
}
else if( aTestAllChildNodes )
{
THROW_UNKNOWN_NODE_IO_ERROR( cNode->GetName(), aNode->GetName() );
}
}
return retVal;
}
std::vector<CADSTAR_ARCHIVE_PARSER::CUTOUT> CADSTAR_ARCHIVE_PARSER::ParseAllChildCutouts(
XNODE* aNode, PARSER_CONTEXT* aContext, bool aTestAllChildNodes )
{
std::vector<CUTOUT> retVal;
XNODE* cNode = aNode->GetChildren();
for( ; cNode; cNode = cNode->GetNext() )
{
if( cNode->GetName() == wxT( "CUTOUT" ) )
{
CUTOUT cutout;
//TODO try.. catch + throw again with more detailed error information
cutout.Parse( cNode, aContext );
retVal.push_back( cutout );
}
else if( aTestAllChildNodes )
{
THROW_UNKNOWN_NODE_IO_ERROR( cNode->GetName(), aNode->GetName() );
}
}
return retVal;
}
long CADSTAR_ARCHIVE_PARSER::GetNumberOfChildNodes( XNODE* aNode )
{
XNODE* childNodes = aNode->GetChildren();
long retval = 0;
for( ; childNodes; childNodes = childNodes->GetNext() )
retval++;
return retval;
}
long CADSTAR_ARCHIVE_PARSER::GetNumberOfStepsForReporting( XNODE* aRootNode, std::vector<wxString> aSubNodeChildrenToCount )
{
XNODE* level1Node = aRootNode->GetChildren();
long retval = 0;
for( ; level1Node; level1Node = level1Node->GetNext() )
{
for( wxString childNodeName : aSubNodeChildrenToCount )
{
if( level1Node->GetName() == childNodeName )
retval += GetNumberOfChildNodes( level1Node );
}
retval++;
}
return retval;
}
wxString CADSTAR_ARCHIVE_PARSER::HandleTextOverbar( wxString aCadstarString )
{
wxString escapedText = aCadstarString;
escapedText.Replace( wxT( "'" ), wxT( "~" ) );
return ConvertToNewOverbarNotation( escapedText );
}
void CADSTAR_ARCHIVE_PARSER::FixTextPositionNoAlignment( EDA_TEXT* aKiCadTextItem )
{
if( !aKiCadTextItem->GetText().IsEmpty() )
{
VECTOR2I positionOffset( 0, aKiCadTextItem->GetInterline() );
RotatePoint( positionOffset, aKiCadTextItem->GetTextAngle() );
//Count num of additional lines
wxString text = aKiCadTextItem->GetText();
int numExtraLines = text.Replace( "\n", "\n" );
numExtraLines -= text.at( text.size() - 1 ) == '\n'; // Ignore new line character at end
positionOffset.x *= numExtraLines;
positionOffset.y *= numExtraLines;
aKiCadTextItem->Offset( positionOffset );
}
}
wxString CADSTAR_ARCHIVE_PARSER::generateLibName( const wxString& aRefName,
const wxString& aAlternateName )
{
if( aAlternateName.IsEmpty() )
return EscapeString( aRefName, CTX_LIBID );
else
return EscapeString( aRefName + wxT( " (" ) + aAlternateName + wxT( ")" ), CTX_LIBID );
}
void CADSTAR_ARCHIVE_PARSER::checkPoint()
{
if( m_progressReporter )
{
m_progressReporter->AdvanceProgress();
if( !m_progressReporter->KeepRefreshing() )
THROW_IO_ERROR( _( "File import cancelled by user." ) );
}
}