kdecore Library API Documentation

kconfig_compiler.cpp

00001 // -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
00002 /*
00003     This file is part of KDE.
00004 
00005     Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>
00006     Copyright (c) 2003 Waldo Bastian <bastian@kde.org>
00007     Copyright (c) 2003 Zack Rusin <zack@kde.org>
00008 
00009     This library is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU Library General Public
00011     License as published by the Free Software Foundation; either
00012     version 2 of the License, or (at your option) any later version.
00013 
00014     This library is distributed in the hope that it will be useful,
00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017     Library General Public License for more details.
00018 
00019     You should have received a copy of the GNU Library General Public License
00020     along with this library; see the file COPYING.LIB.  If not, write to
00021     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00022     Boston, MA 02111-1307, USA.
00023 */
00024 
00025 #include <qfile.h>
00026 #include <qtextstream.h>
00027 #include <qdom.h>
00028 #include <qregexp.h>
00029 
00030 #include <kaboutdata.h>
00031 #include <kapplication.h>
00032 #include <kdebug.h>
00033 #include <klocale.h>
00034 #include <kcmdlineargs.h>
00035 #include <kglobal.h>
00036 #include <kconfig.h>
00037 #include <ksimpleconfig.h>
00038 #include <kstandarddirs.h>
00039 
00040 static const KCmdLineOptions options[] =
00041 {
00042   { "d", 0, 0 },
00043   { "directory <dir>", I18N_NOOP("Directory to generate files in"), "." },
00044   { "+file.kfcg", I18N_NOOP("Input kcfg XML file."), 0 },
00045   { "+file.kcfgc", I18N_NOOP("Code generation options file."), 0 },
00046   KCmdLineLastOption
00047 };
00048 
00049 
00050 bool globalEnums;
00051 bool itemAccessors;
00052 
00053 class CfgEntry
00054 {
00055   public:
00056     struct Choice
00057     {
00058       QString name;
00059       QString label;
00060       QString whatsThis;
00061     };
00062 
00063     CfgEntry( const QString &group, const QString &type, const QString &key,
00064               const QString &name, const QString &label,
00065               const QString &whatsThis, const QString &code,
00066               const QString &defaultValue, const QValueList<Choice> &choices,
00067               bool hidden )
00068       : mGroup( group ), mType( type ), mKey( key ), mName( name ),
00069         mLabel( label ), mWhatsThis( whatsThis ), mCode( code ),
00070         mDefaultValue( defaultValue ),
00071         mChoices( choices ), mHidden( hidden )
00072     {
00073     }
00074 
00075     void setGroup( const QString &group ) { mGroup = group; }
00076     QString group() const { return mGroup; }
00077 
00078     void setType( const QString &type ) { mType = type; }
00079     QString type() const { return mType; }
00080 
00081     void setKey( const QString &key ) { mKey = key; }
00082     QString key() const { return mKey; }
00083 
00084     void setName( const QString &name ) { mName = name; }
00085     QString name() const { return mName; }
00086 
00087     void setLabel( const QString &label ) { mLabel = label; }
00088     QString label() const { return mLabel; }
00089 
00090     void setWhatsThis( const QString &whatsThis ) { mWhatsThis = whatsThis; }
00091     QString whatsThis() const { return mWhatsThis; }
00092 
00093     void setDefaultValue( const QString &d ) { mDefaultValue = d; }
00094     QString defaultValue() const { return mDefaultValue; }
00095 
00096     void setCode( const QString &d ) { mCode = d; }
00097     QString code() const { return mCode; }
00098 
00099     void setMinValue( const QString &d ) { mMin = d; }
00100     QString minValue() const { return mMin; }
00101 
00102     void setMaxValue( const QString &d ) { mMax = d; }
00103     QString maxValue() const { return mMax; }
00104 
00105     void setParam( const QString &d ) { mParam = d; }
00106     QString param() const { return mParam; }
00107 
00108     void setParamName( const QString &d ) { mParamName = d; }
00109     QString paramName() const { return mParamName; }
00110 
00111     void setParamType( const QString &d ) { mParamType = d; }
00112     QString paramType() const { return mParamType; }
00113 
00114     void setChoices( const QValueList<Choice> &d ) { mChoices = d; }
00115     QValueList<Choice> choices() const { return mChoices; }
00116 
00117     void setParamValues( const QStringList &d ) { mParamValues = d; }
00118     QStringList paramValues() const { return mParamValues; }
00119 
00120     void setParamDefaultValues( const QStringList &d ) { mParamDefaultValues = d; }
00121     QString paramDefaultValue(int i) const { return mParamDefaultValues[i]; }
00122 
00123     void setParamMax( int d ) { mParamMax = d; }
00124     int paramMax() const { return mParamMax; }
00125 
00126     bool hidden() const { return mHidden; }
00127 
00128     void dump() const
00129     {
00130       kdDebug() << "<entry>" << endl;
00131       kdDebug() << "  group: " << mGroup << endl;
00132       kdDebug() << "  type: " << mType << endl;
00133       kdDebug() << "  key: " << mKey << endl;
00134       kdDebug() << "  name: " << mName << endl;
00135       kdDebug() << "  label: " << mLabel << endl;
00136 // whatsthis
00137       kdDebug() << "  code: " << mCode << endl;
00138 //      kdDebug() << "  values: " << mValues.join(":") << endl;
00139       kdDebug() << "  paramvalues: " << mParamValues.join(":") << endl;
00140       kdDebug() << "  default: " << mDefaultValue << endl;
00141       kdDebug() << "  hidden: " << mHidden << endl;
00142       kdDebug() << "  min: " << mMin << endl;
00143       kdDebug() << "  max: " << mMax << endl;
00144       kdDebug() << "</entry>" << endl;
00145     }
00146 
00147   private:
00148     QString mGroup;
00149     QString mType;
00150     QString mKey;
00151     QString mName;
00152     QString mLabel;
00153     QString mWhatsThis;
00154     QString mCode;
00155     QString mDefaultValue;
00156     QString mParam;
00157     QString mParamName;
00158     QString mParamType;
00159     QValueList<Choice> mChoices;
00160     QStringList mParamValues;
00161     QStringList mParamDefaultValues;
00162     int mParamMax;
00163     bool mHidden;
00164     QString mMin;
00165     QString mMax;
00166 };
00167 
00168 
00169 static QString varName(const QString &n)
00170 {
00171   QString result = "m"+n;
00172   result[1] = result[1].upper();
00173   return result;
00174 }
00175 
00176 static QString enumName(const QString &n)
00177 {
00178   QString result = "Enum"+n;
00179   result[4] = result[4].upper();
00180   return result;
00181 }
00182 
00183 static QString setFunction(const QString &n)
00184 {
00185   QString result = "set"+n;
00186   result[3] = result[3].upper();
00187   return result;
00188 }
00189 
00190 
00191 static QString getFunction(const QString &n)
00192 {
00193   QString result = n;
00194   result[0] = result[0].lower();
00195   return result;
00196 }
00197 
00198 
00199 static void addQuotes( QString &s )
00200 {
00201   if ( s.left( 1 ) != "\"" ) s.prepend( "\"" );
00202   if ( s.right( 1 ) != "\"" ) s.append( "\"" );
00203 }
00204 
00205 static QString dumpNode(const QDomNode &node)
00206 {
00207   QString msg;
00208   QTextStream s(&msg, IO_WriteOnly );
00209   node.save(s, 0);
00210 
00211   msg = msg.simplifyWhiteSpace();
00212   if (msg.length() > 40)
00213     return msg.left(37)+"...";
00214   return msg;
00215 }
00216 
00217 static QString filenameOnly(QString path)
00218 {
00219    int i = path.findRev('/');
00220    if (i >= 0)
00221       return path.mid(i+1);
00222    return path;
00223 }
00224 
00225 static void preProcessDefault( QString &defaultValue, const QString &name,
00226                                const QString &type,
00227                                const QValueList<CfgEntry::Choice> &choices,
00228                                QString &code )
00229 {
00230     if ( type == "String" && !defaultValue.isEmpty() ) {
00231       addQuotes( defaultValue );
00232 
00233     } else if ( type == "Path" && !defaultValue.isEmpty() ) {
00234       addQuotes( defaultValue );
00235 
00236     } else if ( type == "StringList" && !defaultValue.isEmpty() ) {
00237       QTextStream cpp( &code, IO_WriteOnly | IO_Append );
00238       if (!code.isEmpty())
00239          cpp << endl;
00240 
00241       cpp << "  QStringList default" << name << ";" << endl;
00242       QStringList defaults = QStringList::split( ",", defaultValue );
00243       QStringList::ConstIterator it;
00244       for( it = defaults.begin(); it != defaults.end(); ++it ) {
00245         cpp << "  default" << name << ".append( \"" << *it << "\" );"
00246             << endl;
00247       }
00248       defaultValue = "default" + name;
00249 
00250     } else if ( type == "Color" && !defaultValue.isEmpty() ) {
00251       QRegExp colorRe("\\d+,\\s*\\d+,\\s*\\d+");
00252       if (colorRe.exactMatch(defaultValue))
00253       {
00254         defaultValue = "QColor( " + defaultValue + " )";
00255       }
00256       else
00257       {
00258         defaultValue = "QColor( \"" + defaultValue + "\" )";
00259       }
00260 
00261     } else if ( type == "Enum" ) {
00262       if ( !globalEnums ) {
00263         QValueList<CfgEntry::Choice>::ConstIterator it;
00264         for( it = choices.begin(); it != choices.end(); ++it ) {
00265           if ( (*it).name == defaultValue ) {
00266             defaultValue.prepend( enumName(name) + "::");
00267             break;
00268           }
00269         }
00270       }
00271 
00272     } else if ( type == "IntList" ) {
00273       QTextStream cpp( &code, IO_WriteOnly | IO_Append );
00274       if (!code.isEmpty())
00275          cpp << endl;
00276 
00277       cpp << "  QValueList<int> default" << name << ";" << endl;
00278       QStringList defaults = QStringList::split( ",", defaultValue );
00279       QStringList::ConstIterator it;
00280       for( it = defaults.begin(); it != defaults.end(); ++it ) {
00281         cpp << "  default" << name << ".append( " << *it << " );"
00282             << endl;
00283       }
00284       defaultValue = "default" + name;
00285     }
00286 }
00287 
00288 
00289 CfgEntry *parseEntry( const QString &group, const QDomElement &element )
00290 {
00291   bool defaultCode = false;
00292   QString type = element.attribute( "type" );
00293   QString name = element.attribute( "name" );
00294   QString key = element.attribute( "key" );
00295   QString hidden = element.attribute( "hidden" );
00296   QString label;
00297   QString whatsThis;
00298   QString defaultValue;
00299   QString code;
00300   QString param;
00301   QString paramName;
00302   QString paramType;
00303   QValueList<CfgEntry::Choice> choices;
00304   QStringList paramValues;
00305   QStringList paramDefaultValues;
00306   QString minValue;
00307   QString maxValue;
00308   int paramMax = 0;
00309 
00310   QDomNode n;
00311   for ( n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
00312     QDomElement e = n.toElement();
00313     QString tag = e.tagName();
00314     if ( tag == "label" ) label = e.text();
00315     else if ( tag == "whatsthis" ) whatsThis = e.text();
00316     else if ( tag == "min" ) minValue = e.text();
00317     else if ( tag == "max" ) maxValue = e.text();
00318     else if ( tag == "code" ) code = e.text();
00319     else if ( tag == "parameter" )
00320     {
00321       param = e.attribute( "name" );
00322       paramType = e.attribute( "type" );
00323       if ( param.isEmpty() ) {
00324         kdError() << "Parameter must have a name: " << dumpNode(e) << endl;
00325         return 0;
00326       }
00327       if ( paramType.isEmpty() ) {
00328         kdError() << "Parameter must have a type: " << dumpNode(e) << endl;
00329         return 0;
00330       }
00331       if ((paramType == "Int") || (paramType == "UInt"))
00332       {
00333          bool ok;
00334          paramMax = e.attribute("max").toInt(&ok);
00335          if (!ok)
00336          {
00337            kdError() << "Integer parameter must have a maximum (e.g. max=\"0\"): " << dumpNode(e) << endl;
00338            return 0;
00339          }
00340       }
00341       else if (paramType == "Enum")
00342       {
00343          QDomNode n2;
00344          for ( n2 = e.firstChild(); !n2.isNull(); n2 = n2.nextSibling() ) {
00345            QDomElement e2 = n2.toElement();
00346            if (e2.tagName() == "values")
00347            {
00348              QDomNode n3;
00349              for ( n3 = e2.firstChild(); !n3.isNull(); n3 = n3.nextSibling() ) {
00350                QDomElement e3 = n3.toElement();
00351                if (e3.tagName() == "value")
00352                {
00353                   paramValues.append( e3.text() );
00354                }
00355              }
00356              break;
00357            }
00358          }
00359          if (paramValues.isEmpty())
00360          {
00361            kdError() << "No values specified for parameter '" << param << "'." << endl;
00362            return 0;
00363          }
00364          paramMax = paramValues.count()-1;
00365       }
00366       else
00367       {
00368         kdError() << "Parameter '" << param << "' has type " << paramType << " but must be of type int, uint or Enum." << endl;
00369         return 0;
00370       }
00371     }
00372     else if ( tag == "default" )
00373     {
00374       if (e.attribute("param").isEmpty())
00375       {
00376         defaultValue = e.text();
00377         if (e.attribute( "code" ) == "true")
00378           defaultCode = true;
00379       }
00380     }
00381     else if ( tag == "choices" ) {
00382       QDomNode n2;
00383       for( n2 = e.firstChild(); !n2.isNull(); n2 = n2.nextSibling() ) {
00384         QDomElement e2 = n2.toElement();
00385         if ( e2.tagName() == "choice" ) {
00386           QDomNode n3;
00387           CfgEntry::Choice choice;
00388           choice.name = e2.attribute( "name" );
00389           if ( choice.name.isEmpty() ) {
00390             kdError() << "Tag <choice> requires attribute 'name'." << endl;
00391           }
00392           for( n3 = e2.firstChild(); !n3.isNull(); n3 = n3.nextSibling() ) {
00393             QDomElement e3 = n3.toElement();
00394             if ( e3.tagName() == "label" ) choice.label = e3.text();
00395             if ( e3.tagName() == "whatsthis" ) choice.whatsThis = e3.text();
00396           }
00397           choices.append( choice );
00398         }
00399       }
00400     }
00401   }
00402 
00403   if ( name.isEmpty() && key.isEmpty() ) {
00404     kdError() << "Entry must have a name or a key: " << dumpNode(element) << endl;
00405     return 0;
00406   }
00407 
00408   if ( key.isEmpty() ) {
00409     key = name;
00410   }
00411 
00412   if ( name.isEmpty() ) {
00413     name = key;
00414     name.replace( " ", "" );
00415   } else if ( name.contains( ' ' ) ) {
00416     kdWarning()<<"Entry '"<<name<<"' contains spaces! <name> elements can't contain speces!"<<endl;
00417     name.remove( ' ' );
00418   }
00419 
00420   if (name.contains("$("))
00421   {
00422     if (param.isEmpty())
00423     {
00424       kdError() << "Name may not be parameterized: " << name << endl;
00425       return 0;
00426     }
00427   }
00428   else
00429   {
00430     if (!param.isEmpty())
00431     {
00432       kdError() << "Name must contain '$(" << param << ")': " << name << endl;
00433       return 0;
00434     }
00435   }
00436 
00437   if ( label.isEmpty() ) {
00438     label = key;
00439   }
00440 
00441   if ( type.isEmpty() ) type = "String"; // XXX : implicit type might be bad
00442 
00443   if (!param.isEmpty())
00444   {
00445     // Adjust name
00446     paramName = name;
00447     name.replace("$("+param+")", QString::null);
00448     // Lookup defaults for indexed entries
00449     for(int i = 0; i <= paramMax; i++)
00450     {
00451       paramDefaultValues.append(QString::null);
00452     }
00453 
00454     QDomNode n;
00455     for ( n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
00456       QDomElement e = n.toElement();
00457       QString tag = e.tagName();
00458       if ( tag == "default" )
00459       {
00460         QString index = e.attribute("param");
00461         if (index.isEmpty())
00462            continue;
00463 
00464         bool ok;
00465         int i = index.toInt(&ok);
00466         if (!ok)
00467         {
00468           i = paramValues.findIndex(index);
00469           if (i == -1)
00470           {
00471             kdError() << "Index '" << index << "' for default value is unknown." << endl;
00472             return 0;
00473           }
00474         }
00475 
00476         if ((i < 0) || (i > paramMax))
00477         {
00478           kdError() << "Index '" << i << "' for default value is out of range [0, "<< paramMax<<"]." << endl;
00479           return 0;
00480         }
00481 
00482         QString tmpDefaultValue = e.text();
00483 
00484         if (e.attribute( "code" ) != "true")
00485            preProcessDefault(tmpDefaultValue, name, type, choices, code);
00486 
00487         paramDefaultValues[i] = tmpDefaultValue;
00488       }
00489     }
00490   }
00491 
00492   if (!defaultCode)
00493   {
00494     preProcessDefault(defaultValue, name, type, choices, code);
00495   }
00496 
00497   CfgEntry *result = new CfgEntry( group, type, key, name, label, whatsThis,
00498                                    code, defaultValue, choices,
00499                                    hidden == "true" );
00500   if (!param.isEmpty())
00501   {
00502     result->setParam(param);
00503     result->setParamName(paramName);
00504     result->setParamType(paramType);
00505     result->setParamValues(paramValues);
00506     result->setParamDefaultValues(paramDefaultValues);
00507     result->setParamMax(paramMax);
00508   }
00509   result->setMinValue(minValue);
00510   result->setMaxValue(maxValue);
00511 
00512   return result;
00513 }
00514 
00518 QString param( const QString &type )
00519 {
00520     if ( type == "String" )           return "const QString &";
00521     else if ( type == "StringList" )  return "const QStringList &";
00522     else if ( type == "Font" )        return "const QFont &";
00523     else if ( type == "Rect" )        return "const QRect &";
00524     else if ( type == "Size" )        return "const QSize &";
00525     else if ( type == "Color" )       return "const QColor &";
00526     else if ( type == "Point" )       return "const QPoint &";
00527     else if ( type == "Int" )         return "int";
00528     else if ( type == "UInt" )        return "uint";
00529     else if ( type == "Bool" )        return "bool";
00530     else if ( type == "Double" )      return "double";
00531     else if ( type == "DateTime" )    return "const QDateTime &";
00532     else if ( type == "Int64" )       return "Q_INT64";
00533     else if ( type == "UInt64" )      return "Q_UINT64";
00534     else if ( type == "IntList" )     return "const QValueList<int> &";
00535     else if ( type == "Enum" )        return "int";
00536     else if ( type == "Path" )        return "const QString &";
00537     else if ( type == "Password" )    return "const QString &";
00538     else {
00539         kdError() <<"kconfig_compiler does not support type \""<< type <<"\""<<endl;
00540         return "QString"; //For now, but an assert would be better
00541     }
00542 }
00543 
00547 QString cppType( const QString &type )
00548 {
00549     if ( type == "String" )           return "QString";
00550     else if ( type == "StringList" )  return "QStringList";
00551     else if ( type == "Font" )        return "QFont";
00552     else if ( type == "Rect" )        return "QRect";
00553     else if ( type == "Size" )        return "QSize";
00554     else if ( type == "Color" )       return "QColor";
00555     else if ( type == "Point" )       return "QPoint";
00556     else if ( type == "Int" )         return "int";
00557     else if ( type == "UInt" )        return "uint";
00558     else if ( type == "Bool" )        return "bool";
00559     else if ( type == "Double" )      return "double";
00560     else if ( type == "DateTime" )    return "QDateTime";
00561     else if ( type == "Int64" )       return "Q_INT64";
00562     else if ( type == "UInt64" )      return "Q_UINT64";
00563     else if ( type == "IntList" )     return "QValueList<int>";
00564     else if ( type == "Enum" )        return "int";
00565     else if ( type == "Path" )        return "QString";
00566     else if ( type == "Password" )    return "QString";
00567     else {
00568         kdError()<<"kconfig_compiler does not support type \""<< type <<"\""<<endl;
00569         return "QString"; //For now, but an assert would be better
00570     }
00571 }
00572 
00573 QString defaultValue( const QString &type )
00574 {
00575     if ( type == "String" )           return "\"\""; // Use empty string, not null string!
00576     else if ( type == "StringList" )  return "QStringList()";
00577     else if ( type == "Font" )        return "KGlobalSettings::generalFont()";
00578     else if ( type == "Rect" )        return "QRect()";
00579     else if ( type == "Size" )        return "QSize()";
00580     else if ( type == "Color" )       return "QColor(128, 128, 128)";
00581     else if ( type == "Point" )       return "QPoint()";
00582     else if ( type == "Int" )         return "0";
00583     else if ( type == "UInt" )        return "0";
00584     else if ( type == "Bool" )        return "false";
00585     else if ( type == "Double" )      return "0.0";
00586     else if ( type == "DateTime" )    return "QDateTime()";
00587     else if ( type == "Int64" )       return "0";
00588     else if ( type == "UInt64" )      return "0";
00589     else if ( type == "IntList" )     return "QValueList<int>()";
00590     else if ( type == "Enum" )        return "0";
00591     else if ( type == "Path" )        return "\"\""; // Use empty string, not null string!
00592     else if ( type == "Password" )    return "\"\""; // Use empty string, not null string!
00593     else {
00594         kdWarning()<<"Error, kconfig_compiler doesn't support the \""<< type <<"\" type!"<<endl;
00595         return "QString"; //For now, but an assert would be better
00596     }
00597 }
00598 
00599 QString itemType( const QString &type )
00600 {
00601   QString t;
00602 
00603   t = type;
00604   t.replace( 0, 1, t.left( 1 ).upper() );
00605 
00606   return t;
00607 }
00608 
00609 static QString itemDeclaration(const CfgEntry *e)
00610 {
00611   if (itemAccessors)
00612      return QString::null;
00613 
00614   return "  KConfigSkeleton::Item"+itemType( e->type() ) +
00615          "  *item" + e->name() + ";\n";
00616 }
00617 
00618 static QString itemVar(const CfgEntry *e)
00619 {
00620   if (itemAccessors)
00621      return varName( e->name() ) + "Item";
00622 
00623   return "item" + e->name();
00624 
00625 }
00626 
00627 QString newItem( const QString &type, const QString &name, const QString &key,
00628                  const QString &defaultValue, const QString &param = QString::null)
00629 {
00630   QString t = "new KConfigSkeleton::Item" + itemType( type ) +
00631               "( currentGroup(), " + key + ", " + varName( name ) + param;
00632   if ( type == "Enum" ) t += ", values" + name;
00633   if ( !defaultValue.isEmpty() ) t += ", " + defaultValue;
00634   t += " );";
00635 
00636   return t;
00637 }
00638 
00639 QString paramString(const QString &s, const CfgEntry *e, int i)
00640 {
00641   QString result = s;
00642   QString needle = "$("+e->param()+")";
00643   if (result.contains(needle))
00644   {
00645     QString tmp;
00646     if (e->paramType() == "Enum")
00647     {
00648       tmp = e->paramValues()[i];
00649     }
00650     else
00651     {
00652       tmp = QString("%1").arg(i);
00653     }
00654 
00655     result.replace(needle, tmp);
00656   }
00657   return result;
00658 }
00659 
00660 QString paramString(const QString &group, const QStringList &parameters)
00661 {
00662   QString paramString = group;
00663   QString arguments;
00664   int i = 1;
00665   for( QStringList::ConstIterator it = parameters.begin();
00666        it != parameters.end(); ++it)
00667   {
00668      if (paramString.contains("$("+*it+")"))
00669      {
00670        QString tmp;
00671        tmp.sprintf("%%%d", i++);
00672        paramString.replace("$("+*it+")", tmp);
00673        arguments += ".arg( mParam"+*it+" )";
00674      }
00675   }
00676   if (arguments.isEmpty())
00677     return "\""+group+"\"";
00678 
00679   return "QString(\""+paramString+"\")"+arguments;
00680 }
00681 
00682 QString userTextsFunctions( CfgEntry *e )
00683 {
00684   QString txt;
00685   if ( !e->label().isEmpty() ) {
00686     txt += "  " + itemVar(e) + "->setLabel( i18n(\"" +
00687            e->label() + "\") );\n";
00688   }
00689   if ( !e->whatsThis().isEmpty() ) {
00690     txt += "  " + itemVar(e) + "->setWhatsThis( i18n(\"" +
00691            e->whatsThis() + "\") );\n";
00692   }
00693   return txt;
00694 }
00695 
00696 int main( int argc, char **argv )
00697 {
00698   KAboutData aboutData( "kconfig_compiler", I18N_NOOP("KDE .kcfg compiler"), "0.3",
00699     I18N_NOOP("KConfig Compiler") , KAboutData::License_LGPL );
00700   aboutData.addAuthor( "Cornelius Schumacher", 0, "schumacher@kde.org" );
00701   aboutData.addAuthor( "Waldo Bastian", 0, "bastian@kde.org" );
00702   aboutData.addAuthor( "Zack Rusin", 0, "zack@kde.org" );
00703 
00704   KCmdLineArgs::init( argc, argv, &aboutData );
00705   KCmdLineArgs::addCmdLineOptions( options );
00706 
00707   KInstance app( &aboutData );
00708 
00709   KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
00710 
00711   if ( args->count() < 2 ) {
00712     kdError() << "Too few arguments." << endl;
00713     return 1;
00714   }
00715   if ( args->count() > 2 ) {
00716     kdError() << "Too many arguments." << endl;
00717     return 1;
00718   }
00719 
00720   QString baseDir = QFile::decodeName(args->getOption("directory"));
00721   if (!baseDir.endsWith("/"))
00722     baseDir.append("/");
00723 
00724   QString inputFilename = args->url( 0 ).path();
00725   QString codegenFilename = args->url( 1 ).path();
00726 
00727   if (!codegenFilename.endsWith(".kcfgc"))
00728   {
00729     kdError() << "Codegen options file must have extension .kcfgc" << endl;
00730     return 1;
00731   }
00732   QString baseName = args->url( 1 ).fileName();
00733   baseName = baseName.left(baseName.length() - 6);
00734 
00735   KSimpleConfig codegenConfig( codegenFilename, true );
00736 
00737   QString nameSpace = codegenConfig.readEntry("NameSpace");
00738   QString className = codegenConfig.readEntry("ClassName");
00739   QString inherits = codegenConfig.readEntry("Inherits");
00740   bool singleton = codegenConfig.readBoolEntry("Singleton", false);
00741   bool staticAccessors = singleton;
00742   bool customAddons = codegenConfig.readBoolEntry("CustomAdditions");
00743   QString memberVariables = codegenConfig.readEntry("MemberVariables");
00744   QStringList headerIncludes = codegenConfig.readListEntry("IncludeFiles");
00745   QStringList mutators = codegenConfig.readListEntry("Mutators");
00746   bool allMutators = false;
00747   if ((mutators.count() == 1) && (mutators[0].lower() == "true"))
00748      allMutators = true;
00749   itemAccessors = codegenConfig.readBoolEntry( "ItemAccessors", false );
00750   bool setUserTexts = codegenConfig.readBoolEntry( "SetUserTexts", false );
00751 
00752   globalEnums = codegenConfig.readBoolEntry( "GlobalEnums", false );
00753 
00754   QFile input( inputFilename );
00755 
00756   QDomDocument doc;
00757   QString errorMsg;
00758   int errorRow;
00759   int errorCol;
00760   if ( !doc.setContent( &input, &errorMsg, &errorRow, &errorCol ) ) {
00761     kdError() << "Unable to load document." << endl;
00762     kdError() << "Parse error in " << args->url( 0 ).fileName() << ", line " << errorRow << ", col " << errorCol << ": " << errorMsg << endl;
00763     return 1;
00764   }
00765 
00766   QDomElement cfgElement = doc.documentElement();
00767 
00768   if ( cfgElement.isNull() ) {
00769     kdError() << "No document in kcfg file" << endl;
00770     return 1;
00771   }
00772 
00773   QString cfgFileName;
00774   bool cfgFileNameArg = false;
00775   QStringList parameters;
00776   QStringList includes;
00777 
00778   QPtrList<CfgEntry> entries;
00779   entries.setAutoDelete( true );
00780 
00781   QDomNode n;
00782   for ( n = cfgElement.firstChild(); !n.isNull(); n = n.nextSibling() ) {
00783     QDomElement e = n.toElement();
00784 
00785     QString tag = e.tagName();
00786 
00787     if ( tag == "include" ) {
00788       QString includeFile = e.text();
00789       if (!includeFile.isEmpty())
00790         includes.append(includeFile);
00791 
00792     } else if ( tag == "kcfgfile" ) {
00793       cfgFileName = e.attribute( "name" );
00794       cfgFileNameArg = e.attribute( "arg" ).lower() == "true";
00795       QDomNode n2;
00796       for( n2 = e.firstChild(); !n2.isNull(); n2 = n2.nextSibling() ) {
00797         QDomElement e2 = n2.toElement();
00798         if ( e2.tagName() == "parameter" ) {
00799           parameters.append( e2.attribute( "name" ) );
00800         }
00801       }
00802 
00803     } else if ( tag == "group" ) {
00804       QString group = e.attribute( "name" );
00805       if ( group.isEmpty() ) {
00806         kdError() << "Group without name" << endl;
00807         return 1;
00808       }
00809       QDomNode n2;
00810       for( n2 = e.firstChild(); !n2.isNull(); n2 = n2.nextSibling() ) {
00811         QDomElement e2 = n2.toElement();
00812         if ( e2.tagName() != "entry" ) continue;
00813         CfgEntry *entry = parseEntry( group, e2 );
00814         if ( entry ) entries.append( entry );
00815         else {
00816           kdError() << "Can't parse entry." << endl;
00817           return 1;
00818         }
00819       }
00820     }
00821   }
00822 
00823   if ( inherits.isEmpty() ) inherits = "KConfigSkeleton";
00824 
00825   if ( className.isEmpty() ) {
00826     kdError() << "Class name missing" << endl;
00827     return 1;
00828   }
00829 
00830   if ( singleton && !parameters.isEmpty() ) {
00831     kdError() << "Singleton class can not have parameters" << endl;
00832     return 1;
00833   }
00834 
00835   if ( singleton && cfgFileNameArg)
00836   {
00837     kdError() << "Singleton class can not use filename as argument." << endl;
00838     return 1;
00839   }
00840 
00841   if ( !cfgFileName.isEmpty() && cfgFileNameArg)
00842   {
00843     kdError() << "Having both a fixed filename and a filename as argument is not possible." << endl;
00844     return 1;
00845   }
00846 
00847   if ( entries.isEmpty() ) {
00848     kdWarning() << "No entries." << endl;
00849   }
00850 
00851 #if 0
00852   CfgEntry *cfg;
00853   for( cfg = entries.first(); cfg; cfg = entries.next() ) {
00854     cfg->dump();
00855   }
00856 #endif
00857 
00858   QString headerFileName = baseName + ".h";
00859   QString implementationFileName = baseName + ".cpp";
00860 
00861   QFile header( baseDir + headerFileName );
00862   if ( !header.open( IO_WriteOnly ) ) {
00863     kdError() << "Can't open '" << headerFileName << "for writing." << endl;
00864     return 1;
00865   }
00866 
00867   QTextStream h( &header );
00868 
00869   h << "// This file is generated by kconfig_compiler from " << args->url(0).fileName() << "." << endl;
00870   h << "// All changes you do to this file will be lost." << endl;
00871 
00872   h << "#ifndef " << ( !nameSpace.isEmpty() ? nameSpace.upper() + "_" : "" )
00873     << className.upper() << "_H" << endl;
00874   h << "#define " << ( !nameSpace.isEmpty() ? nameSpace.upper() + "_" : "" )
00875     << className.upper() << "_H" << endl << endl;
00876 
00877   // Includes
00878   QStringList::ConstIterator it;
00879   for( it = headerIncludes.begin(); it != headerIncludes.end(); ++it ) {
00880     h << "#include <" << *it << ">" << endl;
00881   }
00882 
00883   if ( headerIncludes.count() > 0 ) h << endl;
00884 
00885   h << "#include <kconfigskeleton.h>" << endl << endl;
00886 
00887   if ( !nameSpace.isEmpty() )
00888     h << "namespace " << nameSpace << " {" << endl << endl;
00889 
00890   // Class declaration header
00891   h << "class " << className << " : public " << inherits << endl;
00892   h << "{" << endl;
00893   h << "  public:" << endl;
00894 
00895   // enums
00896   CfgEntry *e;
00897   for( e = entries.first(); e; e = entries.next() ) {
00898     QValueList<CfgEntry::Choice> choices = e->choices();
00899     if ( !choices.isEmpty() ) {
00900       QStringList values;
00901       QValueList<CfgEntry::Choice>::ConstIterator itChoice;
00902       for( itChoice = choices.begin(); itChoice != choices.end(); ++itChoice ) {
00903         values.append( (*itChoice).name );
00904       }
00905       if ( globalEnums ) {
00906         h << "    enum { " << values.join( ", " ) << " };" << endl;
00907       } else {
00908         h << "    class " << enumName(e->name()) << endl;
00909         h << "    {" << endl;
00910         h << "      public:" << endl;
00911         h << "      enum { " << values.join( ", " ) << ", COUNT };" << endl;
00912         h << "    };" << endl;
00913       }
00914     }
00915     QStringList values = e->paramValues();
00916     if ( !values.isEmpty() ) {
00917       h << "    class " << enumName(e->param()) << endl;
00918       h << "    {" << endl;
00919       h << "      public:" << endl;
00920       h << "      enum { " << values.join( ", " ) << ", COUNT };" << endl;
00921       h << "    };" << endl;
00922     }
00923   }
00924 
00925   h << endl;
00926 
00927   // Constructor or singleton accessor
00928   if ( !singleton ) {
00929     h << "    " << className << "(";
00930     if (cfgFileNameArg)
00931        h << " KSharedConfig::Ptr config" << (parameters.isEmpty() ? " " : ", ");
00932     for (QStringList::ConstIterator it = parameters.begin();
00933          it != parameters.end(); ++it)
00934     {
00935        if (it != parameters.begin())
00936          h << ",";
00937        h << " const QString &" << *it;
00938     }
00939     h << " );" << endl;
00940   } else {
00941     h << "    static " << className << " *self();" << endl;
00942   }
00943 
00944   // Destructor
00945   h << "    ~" << className << "();" << endl << endl;
00946 
00947   QString This;
00948   QString Const;
00949   if (staticAccessors)
00950     This = "self()->";
00951   else
00952     Const = " const";
00953 
00954   for( e = entries.first(); e; e = entries.next() ) {
00955     QString n = e->name();
00956     QString t = e->type();
00957 
00958     // Manipulator
00959     if (allMutators || mutators.contains(n))
00960     {
00961       h << "    /**" << endl;
00962       h << "      Set " << e->label() << endl;
00963       h << "    */" << endl;
00964       if (staticAccessors)
00965         h << "    static" << endl;
00966       h << "    void " << setFunction(n) << "( ";
00967       if (!e->param().isEmpty())
00968         h << cppType(e->paramType()) << " i, ";
00969       h << param( t ) << " v )" << endl;
00970       h << "    {" << endl;
00971       h << "      if (!" << This << "isImmutable( \"" << n << "\" ))" << endl;
00972       h << "        " << This << varName(n);
00973       if (!e->param().isEmpty())
00974         h << "[i]";
00975       h << " = v;" << endl;
00976       h << "    }" << endl << endl;
00977     }
00978 
00979     // Accessor
00980     h << "    /**" << endl;
00981     h << "      Get " << e->label() << endl;
00982     h << "    */" << endl;
00983     if (staticAccessors)
00984       h << "    static" << endl;
00985     h << "    " << cppType(t) << " " << getFunction(n) << "(";
00986     if (!e->param().isEmpty())
00987       h << " " << cppType(e->paramType()) <<" i ";
00988     h << ")" << Const << endl;
00989     h << "    {" << endl;
00990     h << "      return " << This << varName(n);
00991     if (!e->param().isEmpty())
00992       h << "[i]";
00993     h << ";" << endl;
00994     h << "    }" << endl;
00995 
00996     // Item accessor
00997     if ( itemAccessors ) {
00998       h << endl;
00999       h << "    /**" << endl;
01000       h << "      Get Item object corresponding to " << n << "()"
01001         << endl;
01002       h << "    */" << endl;
01003       h << "    Item" << itemType( e->type() ) << " *"
01004         << getFunction( n ) << "Item()" << endl;
01005       h << "    {" << endl;
01006       h << "      return " << itemVar(e) << ";" << endl;
01007       h << "    }" << endl;
01008     }
01009 
01010     h << endl;
01011   }
01012 
01013   // Static writeConfig method for singleton
01014   if ( singleton ) {
01015     h << "    static" << endl;
01016     h << "    void writeConfig()" << endl;
01017     h << "    {" << endl;
01018     h << "      static_cast<KConfigSkeleton*>(self())->writeConfig();" << endl;
01019     h << "    }" << endl;
01020   }
01021 
01022   h << "  protected:" << endl;
01023 
01024   // Private constructor for singleton
01025   if ( singleton ) {
01026     h << "    " << className << "();" << endl;
01027     h << "    static " << className << " *mSelf;" << endl << endl;
01028   }
01029 
01030   // Member variables
01031   if ( !memberVariables.isEmpty() && memberVariables != "private" ) {
01032     h << "  " << memberVariables << ":" << endl;
01033   }
01034 
01035   // Class Parameters
01036   for (QStringList::ConstIterator it = parameters.begin();
01037        it != parameters.end(); ++it)
01038   {
01039      h << "    QString mParam" << *it << ";" << endl;
01040   }
01041 
01042   QString group;
01043   for( e = entries.first(); e; e = entries.next() ) {
01044     if ( e->group() != group ) {
01045       group = e->group();
01046       h << endl;
01047       h << "    // " << group << endl;
01048     }
01049     h << "    " << cppType(e->type()) << " " << varName(e->name());
01050     if (!e->param().isEmpty())
01051     {
01052       h << QString("[%1]").arg(e->paramMax()+1);
01053     }
01054     h << ";" << endl;
01055   }
01056 
01057   h << endl << "  private:" << endl;
01058   if ( itemAccessors ) {
01059     for( e = entries.first(); e; e = entries.next() ) {
01060       h << "    Item" << itemType( e->type() ) << " *" << itemVar( e ) << ";" << endl;
01061     }
01062   }
01063 
01064   if (customAddons)
01065   {
01066      h << "    // Include custom additions" << endl;
01067      h << "    #include \"" << filenameOnly(baseName) << "_addons.h\"" <<endl;
01068   }
01069 
01070   h << "};" << endl << endl;
01071 
01072   if ( !nameSpace.isEmpty() ) h << "}" << endl << endl;
01073 
01074   h << "#endif" << endl << endl;
01075 
01076 
01077   header.close();
01078 
01079   QFile implementation( baseDir + implementationFileName );
01080   if ( !implementation.open( IO_WriteOnly ) ) {
01081     kdError() << "Can't open '" << implementationFileName << "for writing."
01082               << endl;
01083     return 1;
01084   }
01085 
01086   QTextStream cpp( &implementation );
01087 
01088 
01089   cpp << "// This file is generated by kconfig_compiler from " << args->url(0).fileName() << "." << endl;
01090   cpp << "// All changes you do to this file will be lost." << endl << endl;
01091 
01092   cpp << "#include \"" << headerFileName << "\"" << endl << endl;
01093 
01094   if ( setUserTexts ) cpp << "#include <klocale.h>" << endl << endl;
01095 
01096   // Includes
01097   for( it = includes.begin(); it != includes.end(); ++it ) {
01098     cpp << "#include <" << *it << ">" << endl;
01099   }
01100 
01101   // Header required by singleton implementation
01102   if ( singleton )
01103     cpp << "#include <kstaticdeleter.h>" << endl << endl;
01104 
01105   if ( !nameSpace.isEmpty() )
01106     cpp << "using namespace " << nameSpace << ";" << endl << endl;
01107 
01108   // Singleton implementation
01109   if ( singleton ) {
01110     cpp << className << " *" << className << "::mSelf = 0;" << endl;
01111     cpp << "static KStaticDeleter<" << className << "> staticDeleter;" << endl << endl;
01112 
01113     cpp << className << " *" << className << "::self()" << endl;
01114     cpp << "{" << endl;
01115     cpp << "  if ( !mSelf ) {" << endl;
01116     cpp << "    staticDeleter.setObject( mSelf, new " << className << "() );" << endl;
01117     cpp << "    mSelf->readConfig();" << endl;
01118     cpp << "  }" << endl << endl;
01119     cpp << "  return mSelf;" << endl;
01120     cpp << "}" << endl << endl;
01121   }
01122 
01123   // Constructor
01124   cpp << className << "::" << className << "( ";
01125   if (cfgFileNameArg)
01126      cpp << " KSharedConfig::Ptr config" << (parameters.isEmpty() ? " " : ", ");
01127   for (QStringList::ConstIterator it = parameters.begin();
01128        it != parameters.end(); ++it)
01129   {
01130      if (it != parameters.begin())
01131        cpp << ",";
01132      cpp << " const QString &" << *it;
01133   }
01134   cpp << " )" << endl;
01135 
01136   cpp << "  : " << inherits << "(";
01137   if ( !cfgFileName.isEmpty() ) cpp << " \"" << cfgFileName << "\" ";
01138   if ( cfgFileNameArg ) cpp << " config ";
01139   cpp << ")" << endl;
01140 
01141   // Store parameters
01142   for (QStringList::ConstIterator it = parameters.begin();
01143        it != parameters.end(); ++it)
01144   {
01145      cpp << "  , mParam" << *it << "(" << *it << ")" << endl;
01146   }
01147 
01148   cpp << "{" << endl;
01149 
01150   // Needed in case the singleton class is used as baseclass for
01151   // another singleton.
01152   if ( singleton )
01153     cpp << "  mSelf = this;" << endl;
01154 
01155   group = QString::null;
01156   for( e = entries.first(); e; e = entries.next() ) {
01157     if ( e->group() != group ) {
01158       if ( !group.isEmpty() ) cpp << endl;
01159       group = e->group();
01160       cpp << "  setCurrentGroup( " << paramString(group, parameters) << " );" << endl << endl;
01161     }
01162 
01163     QString key = paramString(e->key(), parameters);
01164     if ( !e->code().isEmpty())
01165     {
01166       cpp << e->code() << endl;
01167     }
01168     if ( e->type() == "Enum" ) {
01169       cpp << "  QValueList<KConfigSkeleton::ItemEnum::Choice> values"
01170           << e->name() << ";" << endl;
01171       QValueList<CfgEntry::Choice> choices = e->choices();
01172       QValueList<CfgEntry::Choice>::ConstIterator it;
01173       for( it = choices.begin(); it != choices.end(); ++it ) {
01174         cpp << "  {" << endl;
01175         cpp << "    KConfigSkeleton::ItemEnum::Choice choice;" << endl;
01176         cpp << "    choice.name = \"" << (*it).name << "\";" << endl;
01177         if ( setUserTexts ) {
01178           if ( !(*it).label.isEmpty() )
01179             cpp << "    choice.label = i18n(\"" << (*it).label << "\");" << endl;
01180           if ( !(*it).whatsThis.isEmpty() )
01181             cpp << "    choice.whatsThis = i18n(\"" << (*it).whatsThis << "\");" << endl;
01182         }
01183         cpp << "    values" << e->name() << ".append( choice );" << endl;
01184         cpp << "  }" << endl;
01185       }
01186     }
01187     cpp << itemDeclaration(e);
01188     if (e->param().isEmpty())
01189     {
01190       // Normal case
01191       cpp << "  " << itemVar(e) << " = "
01192           << newItem( e->type(), e->name(), key, e->defaultValue() ) << endl;
01193 
01194       if ( !e->minValue().isEmpty() )
01195         cpp << "  " << itemVar(e) << "->setMinValue(" << e->minValue() << ");" << endl;
01196       if ( !e->maxValue().isEmpty() )
01197         cpp << "  " << itemVar(e) << "->setMaxValue(" << e->maxValue() << ");" << endl;
01198 
01199       if ( setUserTexts )
01200         cpp << userTextsFunctions( e );
01201 
01202       cpp << "  addItem( " << itemVar(e);
01203       QString quotedName = e->name();
01204       addQuotes( quotedName );
01205       if ( quotedName != key ) cpp << ", \"" << e->name() << "\"";
01206       cpp << " );" << endl;
01207     }
01208     else
01209     {
01210 // TODO: itemAccessors don't work with parameterized entries
01211 // TODO: itemVar(e) should be an array in that case
01212       // Indexed
01213       for(int i = 0; i <= e->paramMax(); i++)
01214       {
01215         QString defaultStr;
01216         if ( !e->paramDefaultValue(i).isEmpty() )
01217           defaultStr = e->paramDefaultValue(i);
01218         else if ( !e->defaultValue().isEmpty() )
01219           defaultStr = paramString(e->defaultValue(), e, i);
01220         else
01221           defaultStr = defaultValue( e->type() );
01222 
01223         cpp << "  " << itemVar(e) << " = "
01224             << newItem( e->type(), e->name(), paramString(key, e, i), defaultStr, QString("[%1]").arg(i) )
01225             << endl;
01226 
01227         if ( setUserTexts )
01228           cpp << userTextsFunctions( e );
01229 
01230         cpp << "  addItem( " << itemVar( e ) << ", \""
01231             << paramString(e->paramName(), e, i) << "\" );" << endl;
01232       }
01233     }
01234   }
01235 
01236   cpp << "}" << endl << endl;
01237 
01238   // Destructor
01239   cpp << className << "::~" << className << "()" << endl;
01240   cpp << "{" << endl;
01241   if ( singleton ) {
01242     cpp << "  if ( mSelf == this )" << endl;
01243     cpp << "    staticDeleter.setObject( mSelf, 0, false );" << endl;
01244   }
01245   cpp << "}" << endl << endl;
01246 
01247   implementation.close();
01248 }
KDE Logo
This file is part of the documentation for kdecore Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Apr 21 18:42:55 2004 by doxygen 1.3.6-20040222 written by Dimitri van Heesch, © 1997-2003