Moves Eagle XML parser to common, replacing boost with wx.

All E'STRUCTS' are moved to common except for ERULES (which is
specific to pcbnew and needs its internal units), still in
pcbnew/eagle_plugin.{h,cpp}

In order to get rid of another boost dependency, this also changes
the parsing of the XML from Boost.PropertyTree to wxXml.

To replace boost::optional, an OPTIONAL_XML_ATTRIBUTE class has
been implemented. This could be replaced with std::optional when
C++17 is ready.
This commit is contained in:
Alejandro García Montoro 2017-04-07 13:40:34 +02:00 committed by Maciej Suminski
parent 4f2ed1b05f
commit 9cf934ef17
5 changed files with 1646 additions and 1233 deletions

View File

@ -239,6 +239,7 @@ set( COMMON_SRCS
draw_panel.cpp
drawtxt.cpp
dsnlexer.cpp
eagle_parser.cpp
eda_dde.cpp
eda_doc.cpp
eda_pattern_match.cpp

649
common/eagle_parser.cpp Normal file
View File

@ -0,0 +1,649 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 2012-2016 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2017 CERN.
* @author Alejandro García Montoro <alejandro.garciamontoro@gmail.com>
*
* 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 2
* 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <eagle_parser.h>
#include <wx/xml/xml.h>
#include <wx/string.h>
#include <wx/filename.h>
#include <convert_to_biu.h>
using std::string;
// Template specializations below parse wxString to the used types:
// - string
// - double
// - int
// - bool
// - EROT
template<>
string Convert<string>( wxString aValue )
{
return aValue.ToStdString();
}
template <>
double Convert<double>( wxString aValue )
{
double value;
if( aValue.ToDouble( &value ) )
return value;
else
throw XML_PARSER_ERROR( "Conversion to double failed. Original value: '" +
aValue.ToStdString() + "'." );
}
template <>
int Convert<int>( wxString aValue )
{
if( aValue.IsEmpty() )
throw XML_PARSER_ERROR( "Conversion to int failed. Original value is empty." );
return wxAtoi( aValue );
}
template <>
bool Convert<bool>( wxString aValue )
{
if( aValue != "yes" && aValue != "no" )
throw XML_PARSER_ERROR( "Conversion to bool failed. Original value, '" +
aValue.ToStdString() +
"', is neither 'yes' nor 'no'." );
return aValue == "yes";
}
/// parse an Eagle XML "rot" field. Unfortunately the DTD seems not to explain
/// this format very well. [S][M]R<degrees>. Examples: "R90", "MR180", "SR180"
template<>
EROT Convert<EROT>( wxString aRot )
{
EROT value;
value.spin = aRot.find( 'S' ) != aRot.npos;
value.mirror = aRot.find( 'M' ) != aRot.npos;
value.degrees = strtod( aRot.c_str()
+ 1 // skip leading 'R'
+ int( value.spin ) // skip optional leading 'S'
+ int( value.mirror ), // skip optional leading 'M'
NULL );
return value;
}
/**
* Function parseRequiredAttribute
* parsese the aAttribute of the XML node aNode.
* @param aNode is the node whose attribute will be parsed.
* @param aAttribute is the attribute that will be parsed.
* @throw XML_PARSER_ERROR - exception thrown if the required attribute is missing
* @return T - the attributed parsed as the specified type.
*/
template<typename T>
T parseRequiredAttribute( wxXmlNode* aNode, string aAttribute )
{
wxString value;
if( aNode->GetAttribute( aAttribute, &value ) )
return Convert<T>( value );
else
throw XML_PARSER_ERROR( "The required attribute " + aAttribute + " is missing." );
}
/**
* Function parseOptionalAttribute
* parses the aAttribute of the XML node aNode.
* @param aNode is the node whose attribute will be parsed.
* @param aAttribute is the attribute that will be parsed.
* @return OPTIONAL_XML_ATTRIBUTE<T> - an optional XML attribute, parsed as the specified type if
* found.
*/
template<typename T>
OPTIONAL_XML_ATTRIBUTE<T> parseOptionalAttribute( wxXmlNode* aNode, string aAttribute )
{
return OPTIONAL_XML_ATTRIBUTE<T>( aNode->GetAttribute( aAttribute ) );
}
EWIRE::EWIRE( wxXmlNode* aWire )
{
/*
<!ELEMENT wire EMPTY>
<!ATTLIST wire
x1 %Coord; #REQUIRED
y1 %Coord; #REQUIRED
x2 %Coord; #REQUIRED
y2 %Coord; #REQUIRED
width %Dimension; #REQUIRED
layer %Layer; #REQUIRED
extent %Extent; #IMPLIED -- only applicable for airwires --
style %WireStyle; "continuous"
curve %WireCurve; "0"
cap %WireCap; "round" -- only applicable if 'curve' is not zero --
>
*/
x1 = parseRequiredAttribute<double>( aWire, "x1" );
y1 = parseRequiredAttribute<double>( aWire, "y1" );
x2 = parseRequiredAttribute<double>( aWire, "x2" );
y2 = parseRequiredAttribute<double>( aWire, "y2" );
width = parseRequiredAttribute<double>( aWire, "width" );
layer = parseRequiredAttribute<int>( aWire, "layer" );
curve = parseOptionalAttribute<double>( aWire, "curve" );
opt_string s = parseOptionalAttribute<string>( aWire, "style" );
if( s == "continuous" )
style = EWIRE::CONTINUOUS;
else if( s == "longdash" )
style = EWIRE::LONGDASH;
else if( s == "shortdash" )
style = EWIRE::SHORTDASH;
else if( s == "dashdot" )
style = EWIRE::DASHDOT;
s = parseOptionalAttribute<string>( aWire, "cap" );
if( s == "round" )
cap = EWIRE::ROUND;
else if( s == "flat" )
cap = EWIRE::FLAT;
}
EVIA::EVIA( wxXmlNode* aVia )
{
/*
<!ELEMENT via EMPTY>
<!ATTLIST via
x %Coord; #REQUIRED
y %Coord; #REQUIRED
extent %Extent; #REQUIRED
drill %Dimension; #REQUIRED
diameter %Dimension; "0"
shape %ViaShape; "round"
alwaysstop %Bool; "no"
>
*/
x = parseRequiredAttribute<double>( aVia, "x" );
y = parseRequiredAttribute<double>( aVia, "y" );
string ext = parseRequiredAttribute<string>( aVia, "extent" );
sscanf( ext.c_str(), "%d-%d", &layer_front_most, &layer_back_most );
drill = parseRequiredAttribute<double>( aVia, "drill" );
diam = parseOptionalAttribute<double>( aVia, "diameter" );
shape = parseOptionalAttribute<string>( aVia, "shape" );
}
ECIRCLE::ECIRCLE( wxXmlNode* aCircle )
{
/*
<!ELEMENT circle EMPTY>
<!ATTLIST circle
x %Coord; #REQUIRED
y %Coord; #REQUIRED
radius %Coord; #REQUIRED
width %Dimension; #REQUIRED
layer %Layer; #REQUIRED
>
*/
x = parseRequiredAttribute<double>( aCircle, "x" );
y = parseRequiredAttribute<double>( aCircle, "y" );
radius = parseRequiredAttribute<double>( aCircle, "radius" );
width = parseRequiredAttribute<double>( aCircle, "width" );
layer = parseRequiredAttribute<int>( aCircle, "layer" );
}
ERECT::ERECT( wxXmlNode* aRect )
{
/*
<!ELEMENT rectangle EMPTY>
<!ATTLIST rectangle
x1 %Coord; #REQUIRED
y1 %Coord; #REQUIRED
x2 %Coord; #REQUIRED
y2 %Coord; #REQUIRED
layer %Layer; #REQUIRED
rot %Rotation; "R0"
>
*/
x1 = parseRequiredAttribute<double>( aRect, "x1" );
y1 = parseRequiredAttribute<double>( aRect, "y1" );
x2 = parseRequiredAttribute<double>( aRect, "x2" );
y2 = parseRequiredAttribute<double>( aRect, "y2" );
layer = parseRequiredAttribute<int>( aRect, "layer" );
rot = parseOptionalAttribute<EROT>( aRect, "rot" );
}
EATTR::EATTR( wxXmlNode* aTree )
{
/*
<!ELEMENT attribute EMPTY>
<!ATTLIST attribute
name %String; #REQUIRED
value %String; #IMPLIED
x %Coord; #IMPLIED
y %Coord; #IMPLIED
size %Dimension; #IMPLIED
layer %Layer; #IMPLIED
font %TextFont; #IMPLIED
ratio %Int; #IMPLIED
rot %Rotation; "R0"
display %AttributeDisplay; "value" -- only in <element> or <instance> context --
constant %Bool; "no" -- only in <device> context --
>
*/
name = parseRequiredAttribute<string>( aTree, "name" );
value = parseOptionalAttribute<string>( aTree, "value" );
x = parseOptionalAttribute<double>( aTree, "x" );
y = parseOptionalAttribute<double>( aTree, "y" );
size = parseOptionalAttribute<double>( aTree, "size" );
// KiCad cannot currently put a TEXTE_MODULE on a different layer than the MODULE
// Eagle can it seems.
layer = parseOptionalAttribute<int>( aTree, "layer" );
ratio = parseOptionalAttribute<double>( aTree, "ratio" );
rot = parseOptionalAttribute<EROT>( aTree, "rot" );
opt_string stemp = parseOptionalAttribute<string>( aTree, "display" );
// (off | value | name | both)
if( stemp == "off" )
display = EATTR::Off;
else if( stemp == "value" )
display = EATTR::VALUE;
else if( stemp == "name" )
display = EATTR::NAME;
else if( stemp == "both" )
display = EATTR::BOTH;
}
EDIMENSION::EDIMENSION( wxXmlNode* aDimension )
{
/*
<!ELEMENT dimension EMPTY>
<!ATTLIST dimension
x1 %Coord; #REQUIRED
y1 %Coord; #REQUIRED
x2 %Coord; #REQUIRED
y2 %Coord; #REQUIRED
x3 %Coord; #REQUIRED
y3 %Coord; #REQUIRED
layer %Layer; #REQUIRED
dtype %DimensionType; "parallel"
>
*/
x1 = parseRequiredAttribute<double>( aDimension, "x1" );
y1 = parseRequiredAttribute<double>( aDimension, "y1" );
x2 = parseRequiredAttribute<double>( aDimension, "x2" );
y2 = parseRequiredAttribute<double>( aDimension, "y2" );
x3 = parseRequiredAttribute<double>( aDimension, "x3" );
y3 = parseRequiredAttribute<double>( aDimension, "y3" );
layer = parseRequiredAttribute<int>( aDimension, "layer" );
opt_string dimType = parseOptionalAttribute<string>( aDimension, "dtype" );
if( !dimType )
{
// default type is parallel
}
}
ETEXT::ETEXT( wxXmlNode* aText )
{
/*
<!ELEMENT text (#PCDATA)>
<!ATTLIST text
x %Coord; #REQUIRED
y %Coord; #REQUIRED
size %Dimension; #REQUIRED
layer %Layer; #REQUIRED
font %TextFont; "proportional"
ratio %Int; "8"
rot %Rotation; "R0"
align %Align; "bottom-left"
>
*/
text = aText->GetNodeContent();
x = parseRequiredAttribute<double>( aText, "x" );
y = parseRequiredAttribute<double>( aText, "y" );
size = parseRequiredAttribute<double>( aText, "size" );
layer = parseRequiredAttribute<int>( aText, "layer" );
font = parseOptionalAttribute<string>( aText, "font" );
ratio = parseOptionalAttribute<double>( aText, "ratio" );
rot = parseOptionalAttribute<EROT>( aText, "rot" );
opt_string stemp = parseOptionalAttribute<string>( aText, "align" );
// (bottom-left | bottom-center | bottom-right | center-left |
// center | center-right | top-left | top-center | top-right)
if( stemp == "center" )
align = ETEXT::CENTER;
else if( stemp == "center-right" )
align = ETEXT::CENTER_RIGHT;
else if( stemp == "top-left" )
align = ETEXT::TOP_LEFT;
else if( stemp == "top-center" )
align = ETEXT::TOP_CENTER;
else if( stemp == "top-right" )
align = ETEXT::TOP_RIGHT;
else if( stemp == "bottom-left" )
align = ETEXT::BOTTOM_LEFT;
else if( stemp == "bottom-center" )
align = ETEXT::BOTTOM_CENTER;
else if( stemp == "bottom-right" )
align = ETEXT::BOTTOM_RIGHT;
else if( stemp == "center-left" )
align = ETEXT::CENTER_LEFT;
}
EPAD::EPAD( wxXmlNode* aPad )
{
/*
<!ELEMENT pad EMPTY>
<!ATTLIST pad
name %String; #REQUIRED
x %Coord; #REQUIRED
y %Coord; #REQUIRED
drill %Dimension; #REQUIRED
diameter %Dimension; "0"
shape %PadShape; "round"
rot %Rotation; "R0"
stop %Bool; "yes"
thermals %Bool; "yes"
first %Bool; "no"
>
*/
// #REQUIRED says DTD, throw exception if not found
name = parseRequiredAttribute<string>( aPad, "name" );
x = parseRequiredAttribute<double>( aPad, "x" );
y = parseRequiredAttribute<double>( aPad, "y" );
drill = parseRequiredAttribute<double>( aPad, "drill" );
// Optional attributes
diameter = parseOptionalAttribute<double>( aPad, "diameter" );
opt_string s = parseOptionalAttribute<string>( aPad, "shape" );
// (square | round | octagon | long | offset)
if( s == "square" )
shape = EPAD::SQUARE;
else if( s == "round" )
shape = EPAD::ROUND;
else if( s == "octagon" )
shape = EPAD::OCTAGON;
else if( s == "long" )
shape = EPAD::LONG;
else if( s == "offset" )
shape = EPAD::OFFSET;
rot = parseOptionalAttribute<EROT>( aPad, "rot" );
stop = parseOptionalAttribute<bool>( aPad, "stop" );
thermals = parseOptionalAttribute<bool>( aPad, "thermals" );
first = parseOptionalAttribute<bool>( aPad, "first" );
}
ESMD::ESMD( wxXmlNode* aSMD )
{
/*
<!ATTLIST smd
name %String; #REQUIRED
x %Coord; #REQUIRED
y %Coord; #REQUIRED
dx %Dimension; #REQUIRED
dy %Dimension; #REQUIRED
layer %Layer; #REQUIRED
roundness %Int; "0"
rot %Rotation; "R0"
stop %Bool; "yes"
thermals %Bool; "yes"
cream %Bool; "yes"
>
*/
// DTD #REQUIRED, throw exception if not found
name = parseRequiredAttribute<string>( aSMD, "name" );
x = parseRequiredAttribute<double>( aSMD, "x" );
y = parseRequiredAttribute<double>( aSMD, "y" );
dx = parseRequiredAttribute<double>( aSMD, "dx" );
dy = parseRequiredAttribute<double>( aSMD, "dy" );
layer = parseRequiredAttribute<int>( aSMD, "layer" );
roundness = parseOptionalAttribute<int>( aSMD, "roundness" );
rot = parseOptionalAttribute<EROT>( aSMD, "rot" );
thermals = parseOptionalAttribute<bool>( aSMD, "thermals" );
stop = parseOptionalAttribute<bool>( aSMD, "stop" );
thermals = parseOptionalAttribute<bool>( aSMD, "thermals" );
cream = parseOptionalAttribute<bool>( aSMD, "cream" );
}
EVERTEX::EVERTEX( wxXmlNode* aVertex )
{
/*
<!ELEMENT vertex EMPTY>
<!ATTLIST vertex
x %Coord; #REQUIRED
y %Coord; #REQUIRED
curve %WireCurve; "0" -- the curvature from this vertex to the next one --
>
*/
x = parseRequiredAttribute<double>( aVertex, "x" );
y = parseRequiredAttribute<double>( aVertex, "y" );
}
EPOLYGON::EPOLYGON( wxXmlNode* aPolygon )
{
/*
<!ATTLIST polygon
width %Dimension; #REQUIRED
layer %Layer; #REQUIRED
spacing %Dimension; #IMPLIED
pour %PolygonPour; "solid"
isolate %Dimension; #IMPLIED -- only in <signal> or <package> context --
orphans %Bool; "no" -- only in <signal> context --
thermals %Bool; "yes" -- only in <signal> context --
rank %Int; "0" -- 1..6 in <signal> context, 0 or 7 in <package> context --
>
*/
width = parseRequiredAttribute<double>( aPolygon, "width" );
layer = parseRequiredAttribute<int>( aPolygon, "layer" );
spacing = parseOptionalAttribute<double>( aPolygon, "spacing" );
isolate = parseOptionalAttribute<double>( aPolygon, "isolate" );
opt_string s = parseOptionalAttribute<string>( aPolygon, "pour" );
// default pour to solid fill
pour = EPOLYGON::SOLID;
// (solid | hatch | cutout)
if( s == "hatch" )
pour = EPOLYGON::HATCH;
else if( s == "cutout" )
pour = EPOLYGON::CUTOUT;
orphans = parseOptionalAttribute<bool>( aPolygon, "orphans" );
thermals = parseOptionalAttribute<bool>( aPolygon, "thermals" );
rank = parseOptionalAttribute<int>( aPolygon, "rank" );
}
EHOLE::EHOLE( wxXmlNode* aHole )
{
/*
<!ELEMENT hole EMPTY>
<!ATTLIST hole
x %Coord; #REQUIRED
y %Coord; #REQUIRED
drill %Dimension; #REQUIRED
>
*/
// #REQUIRED:
x = parseRequiredAttribute<double>( aHole, "x" );
y = parseRequiredAttribute<double>( aHole, "y" );
drill = parseRequiredAttribute<double>( aHole, "drill" );
}
EELEMENT::EELEMENT( wxXmlNode* aElement )
{
/*
<!ELEMENT element (attribute*, variant*)>
<!ATTLIST element
name %String; #REQUIRED
library %String; #REQUIRED
package %String; #REQUIRED
value %String; #REQUIRED
x %Coord; #REQUIRED
y %Coord; #REQUIRED
locked %Bool; "no"
smashed %Bool; "no"
rot %Rotation; "R0"
>
*/
// #REQUIRED
name = parseRequiredAttribute<string>( aElement, "name" );
library = parseRequiredAttribute<string>( aElement, "library" );
value = parseRequiredAttribute<string>( aElement, "value" );
package = parseRequiredAttribute<string>( aElement, "package" );
ReplaceIllegalFileNameChars( &package );
x = parseRequiredAttribute<double>( aElement, "x" );
y = parseRequiredAttribute<double>( aElement, "y" );
// optional
locked = parseOptionalAttribute<bool>( aElement, "locked" );
smashed = parseOptionalAttribute<bool>( aElement, "smashed" );
rot = parseOptionalAttribute<EROT>( aElement, "rot" );
}
ELAYER::ELAYER( wxXmlNode* aLayer )
{
/*
<!ELEMENT layer EMPTY>
<!ATTLIST layer
number %Layer; #REQUIRED
name %String; #REQUIRED
color %Int; #REQUIRED
fill %Int; #REQUIRED
visible %Bool; "yes"
active %Bool; "yes"
>
*/
number = parseRequiredAttribute<int>( aLayer, "number" );
name = parseRequiredAttribute<string>( aLayer, "name" );
color = parseRequiredAttribute<int>( aLayer, "color" );
fill = 1; // Temporary value.
visible = parseOptionalAttribute<bool>( aLayer, "visible" );
active = parseOptionalAttribute<bool>( aLayer, "active" );
}
NODE_MAP MapChildren( wxXmlNode* currentNode )
{
// Map node_name -> node_pointer
NODE_MAP nodesMap;
// Loop through all children mapping them in nodesMap
currentNode = currentNode->GetChildren();
while( currentNode )
{
// Create a new pair in the map
// key: current node name
// value: current node pointer
nodesMap[currentNode->GetName().ToStdString()] = currentNode;
// Get next child
currentNode = currentNode->GetNext();
}
return nodesMap;
}
string makeKey( const string& aFirst, const string& aSecond )
{
string key = aFirst + '\x02' + aSecond;
return key;
}
unsigned long timeStamp( wxXmlNode* aTree )
{
// in this case from a unique tree memory location
return (unsigned long)(void*) aTree;
}
wxPoint kicad_arc_center( wxPoint start, wxPoint end, double angle )
{
// Eagle give us start and end.
// S_ARC wants start to give the center, and end to give the start.
double dx = end.x - start.x, dy = end.y - start.y;
wxPoint mid = (start + end) / 2;
double dlen = sqrt( dx*dx + dy*dy );
double dist = dlen / ( 2 * tan( DEG2RAD( angle ) / 2 ) );
wxPoint center(
mid.x + dist * ( dy / dlen ),
mid.y - dist * ( dx / dlen )
);
return center;
}

690
include/eagle_parser.h Normal file
View File

@ -0,0 +1,690 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 2012-2016 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2017 CERN.
* @author Alejandro García Montoro <alejandro.garciamontoro@gmail.com>
*
* 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 2
* 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef _EAGLE_PARSER_H_
#define _EAGLE_PARSER_H_
#include <errno.h>
#include <unordered_map>
#include <wx/xml/xml.h>
#include <wx/string.h>
#include <wx/filename.h>
#include <layers_id_colors_and_visibility.h>
#include <convert_to_biu.h>
#include <macros.h>
#include <trigo.h>
#include <kicad_string.h>
using std::string;
class MODULE;
typedef std::unordered_map< string, wxXmlNode* > NODE_MAP;
typedef std::map< string, MODULE* > MODULE_MAP;
/**
* Class XML_PARSER_ERROR
* implements a simple wrapper around runtime_error to isolate the errors thrown by the
* Eagle XML parser.
*/
struct XML_PARSER_ERROR : std::runtime_error
{
/**
* Constructor XML_PARSER_ERROR
* build an XML error by just calling its parent class constructor, std::runtime_error, with
* the passed message.
* @param aMessage is an explanatory error message.
*/
XML_PARSER_ERROR( const string& aMessage ) noexcept :
std::runtime_error( "XML parser failed - " + aMessage )
{}
};
/// segment (element) of our XPATH into the Eagle XML document tree in PTREE form.
struct TRIPLET
{
const char* element;
const char* attribute;
const char* value;
TRIPLET( const char* aElement, const char* aAttribute = "", const char* aValue = "" ) :
element( aElement ),
attribute( aAttribute ),
value( aValue )
{}
};
/**
* Class XPATH
* keeps track of what we are working on within a PTREE.
* Then if an exception is thrown, the place within the tree that gave us
* grief can be reported almost accurately. To minimally impact
* speed, merely assign const char* pointers during the tree walking
* expedition. The const char* pointers must be to C strings residing either in
* the data or code segment (i.e. "compiled in") or within the XML document, but
* not on the stack, since the stack is unwound during the throwing of the
* exception. The XML document will not immediately vanish since we capture
* the xpath (using function Contents()) before the XML document tree (PTREE)
* is destroyed.
*/
class XPATH
{
std::vector<TRIPLET> p;
public:
void push( const char* aPathSegment, const char* aAttribute="" )
{
p.push_back( TRIPLET( aPathSegment, aAttribute ) );
}
void clear() { p.clear(); }
void pop() { p.pop_back(); }
/// modify the last path node's value
void Value( const char* aValue )
{
p.back().value = aValue;
}
/// modify the last path node's attribute
void Attribute( const char* aAttribute )
{
p.back().attribute = aAttribute;
}
/// return the contents of the XPATH as a single string
string Contents()
{
typedef std::vector<TRIPLET>::const_iterator CITER_TRIPLET;
string ret;
for( CITER_TRIPLET it = p.begin(); it != p.end(); ++it )
{
if( it != p.begin() )
ret += '.';
ret += it->element;
if( it->attribute[0] && it->value[0] )
{
ret += '[';
ret += it->attribute;
ret += '=';
ret += it->value;
ret += ']';
}
}
return ret;
}
};
/**
* Function Convert
* converts a wxString to a generic type T.
* @param aValue is a wxString containing the value that will be converted to type T.
* @throw XML_PARSER_ERROR - an exception is thrown if the parsing fails or if the conversion to
* type T is unknown.
*/
template<typename T>
T Convert( wxString aValue )
{
throw XML_PARSER_ERROR( "Conversion failed. Unknown type." );
}
/**
* Class OPTIONAL_XML_ATTRIBUTE
* models an optional XML attribute.
* This was implemented as an alternative to boost::optional. This class should be replaced with a
* simple typedef per type using std::optional when C++17 is published.
*/
template <class T>
class OPTIONAL_XML_ATTRIBUTE
{
private:
/// A boolean indicating if the data is present or not.
bool m_isAvailable;
/// The actual data if m_isAvailable is true; otherwise, garbage.
T m_data;
public:
/**
* Constructor OPTIONAL_XML_ATTRIBUTE
* construct a default OPTIONAL_XML_ATTRIBUTE, whose data is not available.
*/
OPTIONAL_XML_ATTRIBUTE() :
m_isAvailable( false )
{}
/**
* Constructor OPTIONAL_XML_ATTRIBUTE
* @param aData is a wxString containing the value that should be converted to type T. If
* aData is empty, the attribute is understood as unavailable; otherwise, the
* conversion to T is tried.
*/
OPTIONAL_XML_ATTRIBUTE( wxString aData )
{
m_isAvailable = !aData.IsEmpty();
if( m_isAvailable )
Set( aData );
}
/**
* Constructor OPTIONAL_XML_ATTRIBUTE
* @param aData is the value of the XML attribute. If this constructor is called, the
* attribute is available.
*/
OPTIONAL_XML_ATTRIBUTE( T aData ) :
m_isAvailable( true ),
m_data( aData )
{}
/**
* Operator bool
* @return bool - the availability of the attribute.
*/
operator bool() const
{
return m_isAvailable;
}
/**
* Assignment operator
* to a string (optionally) containing the data.
* @param aData is a wxString that should be converted to T. If the string is empty, the
* attribute is set to unavailable.
*/
OPTIONAL_XML_ATTRIBUTE<T>& operator =( wxString aData )
{
m_isAvailable = !aData.IsEmpty();
if( m_isAvailable )
Set( aData );
return *this;
}
/**
* Assignment operator
* to an object of the base type containing the data.
* @param aData is the actual value of the attribute. Calling this assignment, the attribute
* is automatically made available.
*/
OPTIONAL_XML_ATTRIBUTE<T>& operator =( T aData )
{
m_data = aData;
m_isAvailable = true;
return *this;
}
/**
* Equal operator
* to an object of the base type.
* @param aOther is the object of the base type that should be compared with this one.
*/
bool operator ==( const T& aOther ) const
{
return m_isAvailable && ( aOther == m_data );
}
/**
* Function Set
* tries to convert a string to the base type.
* @param aString is the string that will be converted to the base type.
*/
void Set( wxString aString )
{
m_data = Convert<T>( aString );
}
/**
* Function Get
* returns a reference to the value of the attribute assuming it is available.
* @return T& - the value of the attribute.
*/
T& Get()
{
assert( m_isAvailable );
return m_data;
}
/**
* Function CGet
* returns a constant reference to the value of the attribute assuming it is available.
* @return const T& - the value of the attribute.
*/
const T& CGet() const
{
assert( m_isAvailable );
return m_data;
}
/**
* Operator *
* returns a reference to the value of the attribute assuming it is available.
* @return T& - the value of the attribute.
*/
T& operator*()
{
return Get();
}
/**
* Operator *
* returns a constant reference to the value of the attribute assuming it is available.
* @return const T& - the value of the attribute.
*/
const T& operator*() const
{
return CGet();
}
/**
* Operator ->
* returns a pointer to the value of the attribute assuming it is available.
* @return T* - the value of the attribute.
*/
T* operator->()
{
return &Get();
}
/**
* Operator ->
* returns a constant pointer to the value of the attribute assuming it is available.
* @return const T* - the value of the attribute.
*/
const T* operator->() const
{
return &CGet();
}
};
// Pre-declare for typedefs
struct EROT;
typedef OPTIONAL_XML_ATTRIBUTE<string> opt_string;
typedef OPTIONAL_XML_ATTRIBUTE<int> opt_int;
typedef OPTIONAL_XML_ATTRIBUTE<double> opt_double;
typedef OPTIONAL_XML_ATTRIBUTE<bool> opt_bool;
typedef OPTIONAL_XML_ATTRIBUTE<EROT> opt_erot;
// All of the 'E'STRUCTS below merely hold Eagle XML information verbatim, in binary.
// For maintenance and troubleshooting purposes, it was thought that we'd need to
// separate the conversion process into distinct steps. There is no intent to have KiCad
// forms of information in these 'E'STRUCTS. They are only binary forms
// of the Eagle information in the corresponding Eagle XML nodes.
/// Eagle net
struct ENET
{
int netcode;
std::string netname;
ENET( int aNetCode, const std::string& aNetName ) :
netcode( aNetCode ),
netname( aNetName )
{}
ENET() :
netcode( 0 )
{}
};
/// Eagle rotation
struct EROT
{
bool mirror;
bool spin;
double degrees;
EROT() :
mirror( false ),
spin( false ),
degrees( 0 )
{}
EROT( double aDegrees ) :
mirror( false ),
spin( false ),
degrees( aDegrees )
{}
};
/// Eagle wire
struct EWIRE
{
double x1;
double y1;
double x2;
double y2;
double width;
LAYER_NUM layer;
// for style: (continuous | longdash | shortdash | dashdot)
enum {
CONTINUOUS,
LONGDASH,
SHORTDASH,
DASHDOT,
};
opt_int style;
opt_double curve; ///< range is -359.9..359.9
// for cap: (flat | round)
enum {
FLAT,
ROUND,
};
opt_int cap;
EWIRE( wxXmlNode* aWire );
};
/// Eagle via
struct EVIA
{
double x;
double y;
int layer_front_most; /// < extent
int layer_back_most; /// < inclusive
double drill;
opt_double diam;
opt_string shape;
EVIA( wxXmlNode* aVia );
};
/// Eagle circle
struct ECIRCLE
{
double x;
double y;
double radius;
double width;
LAYER_NUM layer;
ECIRCLE( wxXmlNode* aCircle );
};
/// Eagle XML rectangle in binary
struct ERECT
{
double x1;
double y1;
double x2;
double y2;
int layer;
opt_erot rot;
ERECT( wxXmlNode* aRect );
};
/**
* Class EATTR
* parses an Eagle "attribute" XML element. Note that an attribute element
* is different than an XML element attribute. The attribute element is a
* full XML node in and of itself, and has attributes of its own. Blame Eagle.
*/
struct EATTR
{
string name;
opt_string value;
opt_double x;
opt_double y;
opt_double size;
opt_int layer;
opt_double ratio;
opt_erot rot;
enum { // for 'display'
Off,
VALUE,
NAME,
BOTH,
};
opt_int display;
EATTR( wxXmlNode* aTree );
EATTR() {}
};
/// Eagle dimension element
struct EDIMENSION
{
double x1;
double y1;
double x2;
double y2;
double x3;
double y3;
int layer;
opt_string dimensionType;
EDIMENSION( wxXmlNode* aDimension );
};
/// Eagle text element
struct ETEXT
{
string text;
double x;
double y;
double size;
int layer;
opt_string font;
opt_double ratio;
opt_erot rot;
enum { // for align
CENTER,
CENTER_LEFT,
TOP_CENTER,
TOP_LEFT,
TOP_RIGHT,
// opposites are -1 x above, used by code tricks in here
CENTER_RIGHT = -CENTER_LEFT,
BOTTOM_CENTER = -TOP_CENTER,
BOTTOM_LEFT = -TOP_RIGHT,
BOTTOM_RIGHT = -TOP_LEFT,
};
opt_int align;
ETEXT( wxXmlNode* aText );
};
/// Eagle thru hol pad
struct EPAD
{
string name;
double x;
double y;
double drill;
opt_double diameter;
// for shape: (square | round | octagon | long | offset)
enum {
SQUARE,
ROUND,
OCTAGON,
LONG,
OFFSET,
};
opt_int shape;
opt_erot rot;
opt_bool stop;
opt_bool thermals;
opt_bool first;
EPAD( wxXmlNode* aPad );
};
/// Eagle SMD pad
struct ESMD
{
string name;
double x;
double y;
double dx;
double dy;
int layer;
opt_int roundness;
opt_erot rot;
opt_bool stop;
opt_bool thermals;
opt_bool cream;
ESMD( wxXmlNode* aSMD );
};
/// Eagle vertex
struct EVERTEX
{
double x;
double y;
EVERTEX( wxXmlNode* aVertex );
};
/// Eagle polygon, without vertices which are parsed as needed
struct EPOLYGON
{
double width;
int layer;
opt_double spacing;
// KiCad priority is opposite of Eagle rank, that is:
// - Eagle Low rank drawn first
// - KiCad high priority drawn first
// So since Eagle has an upper limit we define this, used for the cases
// where no rank is specified.
static const int max_priority = 6;
enum { // for pour
SOLID,
HATCH,
CUTOUT,
};
int pour;
opt_double isolate;
opt_bool orphans;
opt_bool thermals;
opt_int rank;
EPOLYGON( wxXmlNode* aPolygon );
};
/// Eagle hole element
struct EHOLE
{
double x;
double y;
double drill;
EHOLE( wxXmlNode* aHole );
};
/// Eagle element element
struct EELEMENT
{
string name;
string library;
string package;
string value;
double x;
double y;
opt_bool locked;
opt_bool smashed;
opt_erot rot;
EELEMENT( wxXmlNode* aElement );
};
struct ELAYER
{
int number;
string name;
int color;
int fill;
opt_bool visible;
opt_bool active;
ELAYER( wxXmlNode* aLayer );
};
/**
* Function MapChildren
* provides an easy access to the children of an XML node via their names.
* @param currentNode is a pointer to a wxXmlNode, whose children will be mapped.
* @return NODE_MAP - a map linking the name of each children to the children itself (via a
* wxXmlNode*)
*/
NODE_MAP MapChildren( wxXmlNode* currentNode );
/// Assemble a two part key as a simple concatenation of aFirst and aSecond parts,
/// using a separator.
string makeKey( const string& aFirst, const string& aSecond );
/// Make a unique time stamp
unsigned long timeStamp( wxXmlNode* aTree );
/// Convert an Eagle curve end to a KiCad center for S_ARC
wxPoint kicad_arc_center( wxPoint start, wxPoint end, double angle );
#endif // _EAGLE_PARSER_H_

File diff suppressed because it is too large Load Diff

View File

@ -27,48 +27,54 @@
#include <io_mgr.h>
#include <layers_id_colors_and_visibility.h>
#include <eagle_parser.h>
// forward declaration on ptree template so we can confine use of big boost
// headers to only the implementation *.cpp file.
#include <boost/property_tree/ptree_fwd.hpp>
#include <boost/ptr_container/ptr_map.hpp>
#include <map>
#include <wx/xml/xml.h>
class MODULE;
typedef boost::ptr_map< std::string, MODULE > MODULE_MAP;
typedef std::map< std::string, MODULE* > MODULE_MAP;
typedef std::map< std::string, ENET > NET_MAP;
typedef NET_MAP::const_iterator NET_MAP_CITER;
struct ENET
/// subset of eagle.drawing.board.designrules in the XML document
struct ERULES
{
int netcode;
std::string netname;
int psElongationLong; ///< percent over 100%. 0-> not elongated, 100->twice as wide as is tall
///< Goes into making a scaling factor for "long" pads.
ENET( int aNetCode, const std::string& aNetName ) :
netcode( aNetCode ),
netname( aNetName )
int psElongationOffset; ///< the offset of the hole within the "long" pad.
double rvPadTop; ///< top pad size as percent of drill size
// double rvPadBottom; ///< bottom pad size as percent of drill size
double rlMinPadTop; ///< minimum copper annulus on through hole pads
double rlMaxPadTop; ///< maximum copper annulus on through hole pads
double rvViaOuter; ///< copper annulus is this percent of via hole
double rlMinViaOuter; ///< minimum copper annulus on via
double rlMaxViaOuter; ///< maximum copper annulus on via
double mdWireWire; ///< wire to wire spacing I presume.
ERULES() :
psElongationLong ( 100 ),
psElongationOffset ( 0 ),
rvPadTop ( 0.25 ),
// rvPadBottom ( 0.25 ),
rlMinPadTop ( Mils2iu( 10 ) ),
rlMaxPadTop ( Mils2iu( 20 ) ),
rvViaOuter ( 0.25 ),
rlMinViaOuter ( Mils2iu( 10 ) ),
rlMaxViaOuter ( Mils2iu( 20 ) ),
mdWireWire ( 0 )
{}
ENET() :
netcode( 0 )
{}
void parse( wxXmlNode* aRules );
};
typedef std::map< std::string, ENET > NET_MAP;
typedef NET_MAP::const_iterator NET_MAP_CITER;
typedef boost::property_tree::ptree PTREE;
typedef const PTREE CPTREE;
struct EELEMENT;
class XPATH;
struct ERULES;
struct EATTR;
class TEXTE_MODULE;
/**
* Class EAGLE_PLUGIN
* works with Eagle 6.x XML board files and footprints to implement the
@ -175,11 +181,11 @@ private:
// all these loadXXX() throw IO_ERROR or ptree_error exceptions:
void loadAllSections( CPTREE& aDocument );
void loadDesignRules( CPTREE& aDesignRules );
void loadLayerDefs( CPTREE& aLayers );
void loadPlain( CPTREE& aPlain );
void loadSignals( CPTREE& aSignals );
void loadAllSections( wxXmlNode* aDocument );
void loadDesignRules( wxXmlNode* aDesignRules );
void loadLayerDefs( wxXmlNode* aLayers );
void loadPlain( wxXmlNode* aPlain );
void loadSignals( wxXmlNode* aSignals );
/**
* Function loadLibrary
@ -192,10 +198,10 @@ private:
* we are loading a *.lbr not a *.brd file and the key used in m_templates is to exclude
* the library name.
*/
void loadLibrary( CPTREE& aLib, const std::string* aLibName );
void loadLibrary( wxXmlNode* aLib, const std::string* aLibName );
void loadLibraries( CPTREE& aLibs );
void loadElements( CPTREE& aElements );
void loadLibraries( wxXmlNode* aLibs );
void loadElements( wxXmlNode* aElements );
void orientModuleAndText( MODULE* m, const EELEMENT& e, const EATTR* nameAttr, const EATTR* valueAttr );
void orientModuleText( MODULE* m, const EELEMENT& e, TEXTE_MODULE* txt, const EATTR* a );
@ -216,16 +222,16 @@ private:
* Function makeModule
* creates a MODULE from an Eagle package.
*/
MODULE* makeModule( CPTREE& aPackage, const std::string& aPkgName ) const;
MODULE* makeModule( wxXmlNode* aPackage, const std::string& aPkgName ) const;
void packageWire( MODULE* aModule, CPTREE& aTree ) const;
void packagePad( MODULE* aModule, CPTREE& aTree ) const;
void packageText( MODULE* aModule, CPTREE& aTree ) const;
void packageRectangle( MODULE* aModule, CPTREE& aTree ) const;
void packagePolygon( MODULE* aModule, CPTREE& aTree ) const;
void packageCircle( MODULE* aModule, CPTREE& aTree ) const;
void packageHole( MODULE* aModule, CPTREE& aTree ) const;
void packageSMD( MODULE* aModule, CPTREE& aTree ) const;
void packageWire( MODULE* aModule, wxXmlNode* aTree ) const;
void packagePad( MODULE* aModule, wxXmlNode* aTree ) const;
void packageText( MODULE* aModule, wxXmlNode* aTree ) const;
void packageRectangle( MODULE* aModule, wxXmlNode* aTree ) const;
void packagePolygon( MODULE* aModule, wxXmlNode* aTree ) const;
void packageCircle( MODULE* aModule, wxXmlNode* aTree ) const;
void packageHole( MODULE* aModule, wxXmlNode* aTree ) const;
void packageSMD( MODULE* aModule, wxXmlNode* aTree ) const;
};
#endif // EAGLE_PLUGIN_H_