kicad/common/ptree.cpp

216 lines
5.6 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
* Copyright (C) 2013-2020 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 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
*/
// Something in either <boost/property_tree/ptree.hpp> causes a bunch of compiler
// errors in <wx/msw/winundef.h> version 2.9 on MinGW.
#include <macros.h>
#include <boost/property_tree/ptree.hpp>
#include <cassert>
#include <ptree.h>
typedef PTREE::const_iterator CITER;
typedef PTREE::iterator ITER;
#if defined(DEBUG)
#define D(x) x
#else
#define D(x)
#endif
#define CTL_OMIT_NL (1<<0)
#define CTL_IN_ATTRS (1<<1)
//-----<Scan>------------------------------------------------------------------
/**
* Function scanList
* reads a sexpr list from the input stream into a new node with key
* aLexer->CurText().
*/
inline void scanList( PTREE* aTree, DSNLEXER* aLexer )
{
assert( aLexer->CurTok() == DSN_LEFT );
int tok = aLexer->NextTok();
const char* key = aLexer->CurText();
PTREE* list = &aTree->push_back( PTREE::value_type( key, PTREE() ) )->second;
if( tok != DSN_RIGHT )
{
while( ( tok = aLexer->NextTok() ) != DSN_RIGHT )
{
if( tok == DSN_EOF )
aLexer->Unexpected( DSN_EOF );
Scan( list, aLexer );
}
}
}
inline void scanAtom( PTREE* aTree, const DSNLEXER* aLexer )
{
const char* key = aLexer->CurText();
aTree->push_back( PTREE::value_type( key, PTREE() ) );
}
void Scan( PTREE* aTree, DSNLEXER* aLexer )
{
int tok = aLexer->CurTok();
// conditionally read first token.
if( tok == DSN_NONE )
tok = aLexer->NextTok();
if( tok == DSN_EOF )
{
aLexer->Unexpected( DSN_EOF );
}
if( tok == DSN_LEFT )
{
scanList( aTree, aLexer );
}
else
{
scanAtom( aTree, aLexer );
}
}
//-----<Format>------------------------------------------------------------------
inline bool isAtom( const CPTREE& aTree )
{
return aTree.size() == 0 && aTree.data().size() == 0;
}
inline bool isLast( const CPTREE& aTree, CITER it )
{
CITER next = it;
++next;
return next == aTree.end();
}
inline CITER next( CITER it )
{
CITER n = it;
return ++n;
}
static void formatNode( OUTPUTFORMATTER* out, int aNestLevel, int aCtl,
const std::string& aKey, const CPTREE& aTree );
static void formatList( OUTPUTFORMATTER* out, int aNestLevel, int aCtl, const CPTREE& aTree )
{
for( CITER it = aTree.begin(); it != aTree.end(); ++it )
{
// Processing a tree which was read in with xml_parser?
if( it->first == "<xmlattr>" )
{
formatList( out, aNestLevel, aCtl | CTL_IN_ATTRS, it->second );
continue;
}
int ctl = 0;
if( isLast( aTree, it ) ) // is "it" the last one?
{
//if( !( aCtl & CTL_IN_ATTRS ) )
ctl = CTL_OMIT_NL;
}
else if( isAtom( next( it )->second ) )
{
/* if( !( aCtl & CTL_IN_ATTRS ) ) */
ctl = CTL_OMIT_NL;
}
formatNode( out, aNestLevel+1, ctl, it->first, it->second );
}
}
static void formatNode( OUTPUTFORMATTER* out, int aNestLevel, int aCtl,
const std::string& aKey, const CPTREE& aTree )
{
if( !isAtom( aTree ) ) // is a list, not an atom
{
int ctl = CTL_OMIT_NL;
// aTree is list and its first child is a list
if( aTree.size() && !isAtom( aTree.begin()->second ) && !aTree.data().size() )
ctl = 0;
out->Print( aNestLevel, "(%s%s", out->Quotes( aKey ).c_str(), ctl & CTL_OMIT_NL ? "" : "\n" );
if( aTree.data().size() ) // sexpr format does not use data()
{
out->Print( 0, " %s%s",
out->Quotes( aTree.data() ).c_str(),
aTree.size() ? "\n" : ""
);
}
formatList( out, aNestLevel, aCtl, aTree );
out->Print( 0, ")%s", aCtl & CTL_OMIT_NL ? "" : "\n" );
}
else // is an atom, not a list
{
out->Print( 0, " %s", out->Quotes( aKey ).c_str() );
}
}
void Format( OUTPUTFORMATTER* out, int aNestLevel, int aCtl, const CPTREE& aTree )
{
if( aTree.size() == 1 && !aTree.data().size() )
{
// The topmost node is basically only a container for the document root.
// It anchors the paths which traverse the tree deeper.
CITER it = aTree.begin();
formatNode( out, aNestLevel, aCtl, it->first, it->second );
}
else
{
// This is not expected, neither for sexpr nor xml.
formatNode( out, aNestLevel, aCtl, "", aTree );
}
}