/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck * Copyright (C) 2012-2016 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 2017 CERN. * @author Alejandro GarcĂ­a Montoro * * 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 #include #include #include // Template specializations below parse wxString to the used types: // - string // - double // - int // - bool // - EROT constexpr auto DEFAULT_ALIGNMENT = ETEXT::BOTTOM_LEFT; template<> string Convert( wxString aValue ) { return aValue.ToStdString(); } template <> double Convert( 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( wxString aValue ) { if( aValue.IsEmpty() ) throw XML_PARSER_ERROR( "Conversion to int failed. Original value is empty." ); return wxAtoi( aValue ); } template <> bool Convert( 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. Examples: "R90", "MR180", "SR180" template<> EROT Convert( 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 T parseRequiredAttribute( wxXmlNode* aNode, string aAttribute ) { wxString value; if( aNode->GetAttribute( aAttribute, &value ) ) return Convert( 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 - an optional XML attribute, parsed as the specified type if * found. */ template OPTIONAL_XML_ATTRIBUTE parseOptionalAttribute( wxXmlNode* aNode, string aAttribute ) { return OPTIONAL_XML_ATTRIBUTE( aNode->GetAttribute( aAttribute ) ); } NODE_MAP MapChildren( wxXmlNode* currentNode ) { // Map node_name -> node_pointer NODE_MAP nodesMap; // Loop through all children mapping them in nodesMap if( currentNode ) 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; } time_t moduleTstamp( const string& aName, const string& aValue ) { std::size_t h1 = std::hash{}( aName ); std::size_t h2 = std::hash{}( aValue ); return ((h1 ^ h2 << 1) & 0xffffffff); } string modulePath( const string& aName, const string& aValue ) { std::ostringstream s; s << '/' << std::setfill('0') << std::uppercase << std::hex << std::setw(8) << moduleTstamp( aName, aValue ); return s.str(); } wxPoint kicad_arc_center( const wxPoint& aStart, const wxPoint& aEnd, double aAngle ) { // Eagle give us start and end. // S_ARC wants start to give the center, and end to give the start. double dx = aEnd.x - aStart.x, dy = aEnd.y - aStart.y; wxPoint mid = ( aStart + aEnd ) / 2; double dlen = sqrt( dx*dx + dy*dy ); double dist = dlen / ( 2 * tan( DEG2RAD( aAngle ) / 2 ) ); wxPoint center( mid.x + dist * ( dy / dlen ), mid.y - dist * ( dx / dlen ) ); return center; } int parseAlignment(wxString alignment) { // (bottom-left | bottom-center | bottom-right | center-left | // center | center-right | top-left | top-center | top-right) if( alignment == "center" ) return ETEXT::CENTER; else if( alignment == "center-right" ) return ETEXT::CENTER_RIGHT; else if( alignment == "top-left" ) return ETEXT::TOP_LEFT; else if( alignment == "top-center" ) return ETEXT::TOP_CENTER; else if( alignment == "top-right" ) return ETEXT::TOP_RIGHT; else if( alignment == "bottom-left" ) return ETEXT::BOTTOM_LEFT; else if( alignment == "bottom-center" ) return ETEXT::BOTTOM_CENTER; else if( alignment == "bottom-right" ) return ETEXT::BOTTOM_RIGHT; else if( alignment == "center-left" ) return ETEXT::CENTER_LEFT; return DEFAULT_ALIGNMENT; } EWIRE::EWIRE( wxXmlNode* aWire ) { /* */ x1 = parseRequiredAttribute( aWire, "x1" ); y1 = parseRequiredAttribute( aWire, "y1" ); x2 = parseRequiredAttribute( aWire, "x2" ); y2 = parseRequiredAttribute( aWire, "y2" ); width = parseRequiredAttribute( aWire, "width" ); layer = parseRequiredAttribute( aWire, "layer" ); curve = parseOptionalAttribute( aWire, "curve" ); opt_string s = parseOptionalAttribute( 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( aWire, "cap" ); if( s == "round" ) cap = EWIRE::ROUND; else if( s == "flat" ) cap = EWIRE::FLAT; } EJUNCTION::EJUNCTION( wxXmlNode* aJunction ) { /* */ x = parseRequiredAttribute( aJunction, "x" ); y = parseRequiredAttribute( aJunction, "y" ); } ELABEL::ELABEL( wxXmlNode* aLabel, const wxString& aNetName ) { /* */ x = parseRequiredAttribute( aLabel, "x" ); y = parseRequiredAttribute( aLabel, "y" ); size = parseRequiredAttribute( aLabel, "size" ); layer = parseRequiredAttribute( aLabel, "layer" ); rot = parseOptionalAttribute( aLabel, "rot" ); xref = parseOptionalAttribute( aLabel, "xref" ); netname = aNetName; } EVIA::EVIA( wxXmlNode* aVia ) { /* */ x = parseRequiredAttribute( aVia, "x" ); y = parseRequiredAttribute( aVia, "y" ); string ext = parseRequiredAttribute( aVia, "extent" ); sscanf( ext.c_str(), "%d-%d", &layer_front_most, &layer_back_most ); drill = parseRequiredAttribute( aVia, "drill" ); diam = parseOptionalAttribute( aVia, "diameter" ); shape = parseOptionalAttribute( aVia, "shape" ); } ECIRCLE::ECIRCLE( wxXmlNode* aCircle ) { /* */ x = parseRequiredAttribute( aCircle, "x" ); y = parseRequiredAttribute( aCircle, "y" ); radius = parseRequiredAttribute( aCircle, "radius" ); width = parseRequiredAttribute( aCircle, "width" ); layer = parseRequiredAttribute( aCircle, "layer" ); } ERECT::ERECT( wxXmlNode* aRect ) { /* */ x1 = parseRequiredAttribute( aRect, "x1" ); y1 = parseRequiredAttribute( aRect, "y1" ); x2 = parseRequiredAttribute( aRect, "x2" ); y2 = parseRequiredAttribute( aRect, "y2" ); layer = parseRequiredAttribute( aRect, "layer" ); rot = parseOptionalAttribute( aRect, "rot" ); } EATTR::EATTR( wxXmlNode* aTree ) { /* or context -- constant %Bool; "no" -- only in context -- > */ name = parseRequiredAttribute( aTree, "name" ); value = parseOptionalAttribute( aTree, "value" ); x = parseOptionalAttribute( aTree, "x" ); y = parseOptionalAttribute( aTree, "y" ); size = parseOptionalAttribute( aTree, "size" ); // KiCad cannot currently put a TEXTE_MODULE on a different layer than the MODULE // Eagle can it seems. layer = parseOptionalAttribute( aTree, "layer" ); ratio = parseOptionalAttribute( aTree, "ratio" ); rot = parseOptionalAttribute( aTree, "rot" ); opt_string stemp = parseOptionalAttribute( 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; stemp = parseOptionalAttribute( aTree, "align" ); align = stemp ? parseAlignment( *stemp ) : DEFAULT_ALIGNMENT; } EDIMENSION::EDIMENSION( wxXmlNode* aDimension ) { /* */ x1 = parseRequiredAttribute( aDimension, "x1" ); y1 = parseRequiredAttribute( aDimension, "y1" ); x2 = parseRequiredAttribute( aDimension, "x2" ); y2 = parseRequiredAttribute( aDimension, "y2" ); x3 = parseRequiredAttribute( aDimension, "x3" ); y3 = parseRequiredAttribute( aDimension, "y3" ); layer = parseRequiredAttribute( aDimension, "layer" ); opt_string dimType = parseOptionalAttribute( aDimension, "dtype" ); if( !dimType ) { // default type is parallel } } ETEXT::ETEXT( wxXmlNode* aText ) { /* */ text = aText->GetNodeContent(); x = parseRequiredAttribute( aText, "x" ); y = parseRequiredAttribute( aText, "y" ); size = parseRequiredAttribute( aText, "size" ); layer = parseRequiredAttribute( aText, "layer" ); font = parseOptionalAttribute( aText, "font" ); ratio = parseOptionalAttribute( aText, "ratio" ); rot = parseOptionalAttribute( aText, "rot" ); opt_string stemp = parseOptionalAttribute( aText, "align" ); align = stemp ? parseAlignment( *stemp ) : DEFAULT_ALIGNMENT; } EPAD::EPAD( wxXmlNode* aPad ) { /* */ // #REQUIRED says DTD, throw exception if not found name = parseRequiredAttribute( aPad, "name" ); x = parseRequiredAttribute( aPad, "x" ); y = parseRequiredAttribute( aPad, "y" ); drill = parseRequiredAttribute( aPad, "drill" ); // Optional attributes diameter = parseOptionalAttribute( aPad, "diameter" ); opt_string s = parseOptionalAttribute( 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( aPad, "rot" ); stop = parseOptionalAttribute( aPad, "stop" ); thermals = parseOptionalAttribute( aPad, "thermals" ); first = parseOptionalAttribute( aPad, "first" ); } ESMD::ESMD( wxXmlNode* aSMD ) { /* */ // DTD #REQUIRED, throw exception if not found name = parseRequiredAttribute( aSMD, "name" ); x = parseRequiredAttribute( aSMD, "x" ); y = parseRequiredAttribute( aSMD, "y" ); dx = parseRequiredAttribute( aSMD, "dx" ); dy = parseRequiredAttribute( aSMD, "dy" ); layer = parseRequiredAttribute( aSMD, "layer" ); roundness = parseOptionalAttribute( aSMD, "roundness" ); rot = parseOptionalAttribute( aSMD, "rot" ); thermals = parseOptionalAttribute( aSMD, "thermals" ); stop = parseOptionalAttribute( aSMD, "stop" ); thermals = parseOptionalAttribute( aSMD, "thermals" ); cream = parseOptionalAttribute( aSMD, "cream" ); } EPIN::EPIN( wxXmlNode* aPin ) { /* */ // DTD #REQUIRED, throw exception if not found name = parseRequiredAttribute( aPin, "name" ); x = parseRequiredAttribute( aPin, "x" ); y = parseRequiredAttribute( aPin, "y" ); visible = parseOptionalAttribute( aPin, "visible" ); length = parseOptionalAttribute( aPin, "length" ); direction = parseOptionalAttribute( aPin, "direction" ); function = parseOptionalAttribute( aPin, "function" ); swaplevel = parseOptionalAttribute( aPin, "swaplevel" ); rot = parseOptionalAttribute( aPin, "rot" ); } EVERTEX::EVERTEX( wxXmlNode* aVertex ) { /* */ x = parseRequiredAttribute( aVertex, "x" ); y = parseRequiredAttribute( aVertex, "y" ); } EPOLYGON::EPOLYGON( wxXmlNode* aPolygon ) { /* or context -- orphans %Bool; "no" -- only in context -- thermals %Bool; "yes" -- only in context -- rank %Int; "0" -- 1..6 in context, 0 or 7 in context -- > */ width = parseRequiredAttribute( aPolygon, "width" ); layer = parseRequiredAttribute( aPolygon, "layer" ); spacing = parseOptionalAttribute( aPolygon, "spacing" ); isolate = parseOptionalAttribute( aPolygon, "isolate" ); opt_string s = parseOptionalAttribute( 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( aPolygon, "orphans" ); thermals = parseOptionalAttribute( aPolygon, "thermals" ); rank = parseOptionalAttribute( aPolygon, "rank" ); } EHOLE::EHOLE( wxXmlNode* aHole ) { /* */ // #REQUIRED: x = parseRequiredAttribute( aHole, "x" ); y = parseRequiredAttribute( aHole, "y" ); drill = parseRequiredAttribute( aHole, "drill" ); } EELEMENT::EELEMENT( wxXmlNode* aElement ) { /* */ // #REQUIRED name = parseRequiredAttribute( aElement, "name" ); library = parseRequiredAttribute( aElement, "library" ); value = parseRequiredAttribute( aElement, "value" ); package = parseRequiredAttribute( aElement, "package" ); ReplaceIllegalFileNameChars( &package ); x = parseRequiredAttribute( aElement, "x" ); y = parseRequiredAttribute( aElement, "y" ); // optional locked = parseOptionalAttribute( aElement, "locked" ); smashed = parseOptionalAttribute( aElement, "smashed" ); rot = parseOptionalAttribute( aElement, "rot" ); } ELAYER::ELAYER( wxXmlNode* aLayer ) { /* */ number = parseRequiredAttribute( aLayer, "number" ); name = parseRequiredAttribute( aLayer, "name" ); color = parseRequiredAttribute( aLayer, "color" ); fill = 1; // Temporary value. visible = parseOptionalAttribute( aLayer, "visible" ); active = parseOptionalAttribute( aLayer, "active" ); } EPART::EPART( wxXmlNode* aPart ) { /* * * */ // #REQUIRED name = parseRequiredAttribute( aPart, "name" ); library = parseRequiredAttribute( aPart, "library" ); deviceset = parseRequiredAttribute( aPart, "deviceset" ); device = parseRequiredAttribute( aPart, "device" ); technology = parseOptionalAttribute( aPart, "technology" ); value = parseOptionalAttribute( aPart, "value" ); } EINSTANCE::EINSTANCE( wxXmlNode* aInstance ) { /* * * */ part = parseRequiredAttribute( aInstance, "part" ); gate = parseRequiredAttribute( aInstance, "gate" ); x = parseRequiredAttribute( aInstance, "x" ); y = parseRequiredAttribute( aInstance, "y" ); // optional smashed = parseOptionalAttribute( aInstance, "smashed" ); rot = parseOptionalAttribute( aInstance, "rot" ); } EGATE::EGATE( wxXmlNode* aGate ) { /* * * */ name = parseRequiredAttribute( aGate, "name" ); symbol = parseRequiredAttribute( aGate, "symbol" ); x = parseRequiredAttribute( aGate, "x" ); y = parseRequiredAttribute( aGate, "y" ); opt_string stemp = parseOptionalAttribute( aGate, "addlevel" ); // (off | value | name | both) if( stemp == "must" ) addlevel = EGATE::MUST; else if( stemp == "can" ) addlevel = EGATE::CAN; else if( stemp == "next" ) addlevel = EGATE::NEXT; else if( stemp == "request" ) addlevel = EGATE::REQUEST; else if( stemp == "always" ) addlevel = EGATE::ALWAYS; else addlevel = EGATE::NEXT; } ECONNECT::ECONNECT( wxXmlNode* aConnect ) { /* * * */ gate = parseRequiredAttribute( aConnect, "gate" ); pin = parseRequiredAttribute( aConnect, "pin" ); pad = parseRequiredAttribute( aConnect, "pad" ); //TODO: //int contactroute; }; EDEVICE::EDEVICE( wxXmlNode* aDevice ) { /* */ name = parseRequiredAttribute( aDevice, "name" ); package = parseOptionalAttribute( aDevice, "package" ); NODE_MAP aDeviceChildren = MapChildren(aDevice); wxXmlNode* connectNode = getChildrenNodes(aDeviceChildren, "connects"); while(connectNode){ connects.push_back(ECONNECT(connectNode)); connectNode = connectNode->GetNext(); } }; EDEVICESET::EDEVICESET( wxXmlNode* aDeviceSet ) { /* */ name = parseRequiredAttribute(aDeviceSet, "name"); prefix = parseOptionalAttribute( aDeviceSet, "prefix" ); uservalue = parseOptionalAttribute( aDeviceSet, "uservalue" ); /* Russell: Parsing of devices and gates moved to sch_eagle_plugin.cpp * //TODO: description NODE_MAP aDeviceSetChildren = MapChildren(aDeviceSet); wxXmlNode* deviceNode = getChildrenNodes(aDeviceSetChildren, "device"); while(deviceNode){ devices.push_back(EDEVICE(deviceNode)); deviceNode->GetNext(); } wxXmlNode* gateNode = getChildrenNodes(aDeviceSetChildren, "gate"); while(gateNode){ gates.push_back(EGATE(gateNode)); gateNode->GetNext(); } */ }