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         CfgEntry *entry = parseEntry( group, e2 );
00813         if ( entry ) entries.append( entry );
00814         else {
00815           kdError() << "Can't parse entry." << endl;
00816           return 1;
00817         }
00818       }
00819     }
00820   }
00821 
00822   if ( inherits.isEmpty() ) inherits = "KConfigSkeleton";
00823 
00824   if ( className.isEmpty() ) {
00825     kdError() << "Class name missing" << endl;
00826     return 1;
00827   }
00828 
00829   if ( singleton && !parameters.isEmpty() ) {
00830     kdError() << "Singleton class can not have parameters" << endl;
00831     return 1;
00832   }
00833 
00834   if ( singleton && cfgFileNameArg)
00835   {
00836     kdError() << "Singleton class can not use filename as argument." << endl;
00837     return 1;
00838   }
00839 
00840   if ( !cfgFileName.isEmpty() && cfgFileNameArg)
00841   {
00842     kdError() << "Having both a fixed filename and a filename as argument is not possible." << endl;
00843     return 1;
00844   }
00845 
00846   if ( entries.isEmpty() ) {
00847     kdWarning() << "No entries." << endl;
00848   }
00849 
00850 #if 0
00851   CfgEntry *cfg;
00852   for( cfg = entries.first(); cfg; cfg = entries.next() ) {
00853     cfg->dump();
00854   }
00855 #endif
00856 
00857   QString headerFileName = baseName + ".h";
00858   QString implementationFileName = baseName + ".cpp";
00859 
00860   QFile header( baseDir + headerFileName );
00861   if ( !header.open( IO_WriteOnly ) ) {
00862     kdError() << "Can't open '" << headerFileName << "for writing." << endl;
00863     return 1;
00864   }
00865 
00866   QTextStream h( &header );
00867 
00868   h << "// This file is generated by kconfig_compiler from " << args->url(0).fileName() << "." << endl;
00869   h << "// All changes you do to this file will be lost." << endl;
00870 
00871   h << "#ifndef " << ( !nameSpace.isEmpty() ? nameSpace.upper() + "_" : "" )
00872     << className.upper() << "_H" << endl;
00873   h << "#define " << ( !nameSpace.isEmpty() ? nameSpace.upper() + "_" : "" )
00874     << className.upper() << "_H" << endl << endl;
00875 
00876   // Includes
00877   QStringList::ConstIterator it;
00878   for( it = headerIncludes.begin(); it != headerIncludes.end(); ++it ) {
00879     h << "#include <" << *it << ">" << endl;
00880   }
00881 
00882   if ( headerIncludes.count() > 0 ) h << endl;
00883 
00884   h << "#include <kconfigskeleton.h>" << endl << endl;
00885 
00886   if ( !nameSpace.isEmpty() )
00887     h << "namespace " << nameSpace << " {" << endl << endl;
00888 
00889   // Class declaration header
00890   h << "class " << className << " : public " << inherits << endl;
00891   h << "{" << endl;
00892   h << "  public:" << endl;
00893 
00894   // enums
00895   CfgEntry *e;
00896   for( e = entries.first(); e; e = entries.next() ) {
00897     QValueList<CfgEntry::Choice> choices = e->choices();
00898     if ( !choices.isEmpty() ) {
00899       QStringList values;
00900       QValueList<CfgEntry::Choice>::ConstIterator itChoice;
00901       for( itChoice = choices.begin(); itChoice != choices.end(); ++itChoice ) {
00902         values.append( (*itChoice).name );
00903       }
00904       if ( globalEnums ) {
00905         h << "    enum { " << values.join( ", " ) << " };" << endl;
00906       } else {
00907         h << "    class " << enumName(e->name()) << endl;
00908         h << "    {" << endl;
00909         h << "      public:" << endl;
00910         h << "      enum { " << values.join( ", " ) << ", COUNT };" << endl;
00911         h << "    };" << endl;
00912       }
00913     }
00914     QStringList values = e->paramValues();
00915     if ( !values.isEmpty() ) {
00916       h << "    class " << enumName(e->param()) << endl;
00917       h << "    {" << endl;
00918       h << "      public:" << endl;
00919       h << "      enum { " << values.join( ", " ) << ", COUNT };" << endl;
00920       h << "    };" << endl;
00921     }
00922   }
00923 
00924   h << endl;
00925 
00926   // Constructor or singleton accessor
00927   if ( !singleton ) {
00928     h << "    " << className << "(";
00929     if (cfgFileNameArg)
00930        h << " KSharedConfig::Ptr config" << (parameters.isEmpty() ? " " : ", ");
00931     for (QStringList::ConstIterator it = parameters.begin();
00932          it != parameters.end(); ++it)
00933     {
00934        if (it != parameters.begin())
00935          h << ",";
00936        h << " const QString &" << *it;
00937     }
00938     h << " );" << endl;
00939   } else {
00940     h << "    static " << className << " *self();" << endl;
00941   }
00942 
00943   // Destructor
00944   h << "    ~" << className << "();" << endl << endl;
00945 
00946   QString This;
00947   QString Const;
00948   if (staticAccessors)
00949     This = "self()->";
00950   else
00951     Const = " const";
00952 
00953   for( e = entries.first(); e; e = entries.next() ) {
00954     QString n = e->name();
00955     QString t = e->type();
00956 
00957     // Manipulator
00958     if (allMutators || mutators.contains(n))
00959     {
00960       h << "    /**" << endl;
00961       h << "      Set " << e->label() << endl;
00962       h << "    */" << endl;
00963       if (staticAccessors)
00964         h << "    static" << endl;
00965       h << "    void " << setFunction(n) << "( ";
00966       if (!e->param().isEmpty())
00967         h << cppType(e->paramType()) << " i, ";
00968       h << param( t ) << " v )" << endl;
00969       h << "    {" << endl;
00970       h << "      if (!" << This << "isImmutable( \"" << n << "\" ))" << endl;
00971       h << "        " << This << varName(n);
00972       if (!e->param().isEmpty())
00973         h << "[i]";
00974       h << " = v;" << endl;
00975       h << "    }" << endl << endl;
00976     }
00977 
00978     // Accessor
00979     h << "    /**" << endl;
00980     h << "      Get " << e->label() << endl;
00981     h << "    */" << endl;
00982     if (staticAccessors)
00983       h << "    static" << endl;
00984     h << "    " << cppType(t) << " " << getFunction(n) << "(";
00985     if (!e->param().isEmpty())
00986       h << " " << cppType(e->paramType()) <<" i ";
00987     h << ")" << Const << endl;
00988     h << "    {" << endl;
00989     h << "      return " << This << varName(n);
00990     if (!e->param().isEmpty())
00991       h << "[i]";
00992     h << ";" << endl;
00993     h << "    }" << endl;
00994 
00995     // Item accessor
00996     if ( itemAccessors ) {
00997       h << endl;
00998       h << "    /**" << endl;
00999       h << "      Get Item object corresponding to " << n << "()"
01000         << endl;
01001       h << "    */" << endl;
01002       h << "    Item" << itemType( e->type() ) << " *"
01003         << getFunction( n ) << "Item()" << endl;
01004       h << "    {" << endl;
01005       h << "      return " << itemVar(e) << ";" << endl;
01006       h << "    }" << endl;
01007     }
01008 
01009     h << endl;
01010   }
01011 
01012   // Static writeConfig method for singleton
01013   if ( singleton ) {
01014     h << "    static" << endl;
01015     h << "    void writeConfig()" << endl;
01016     h << "    {" << endl;
01017     h << "      static_cast<KConfigSkeleton*>(self())->writeConfig();" << endl;
01018     h << "    }" << endl;
01019   }
01020 
01021   h << "  protected:" << endl;
01022 
01023   // Private constructor for singleton
01024   if ( singleton ) {
01025     h << "    " << className << "();" << endl;
01026     h << "    static " << className << " *mSelf;" << endl << endl;
01027   }
01028 
01029   // Member variables
01030   if ( !memberVariables.isEmpty() && memberVariables != "private" ) {
01031     h << "  " << memberVariables << ":" << endl;
01032   }
01033 
01034   // Class Parameters
01035   for (QStringList::ConstIterator it = parameters.begin();
01036        it != parameters.end(); ++it)
01037   {
01038      h << "    QString mParam" << *it << ";" << endl;
01039   }
01040 
01041   QString group;
01042   for( e = entries.first(); e; e = entries.next() ) {
01043     if ( e->group() != group ) {
01044       group = e->group();
01045       h << endl;
01046       h << "    // " << group << endl;
01047     }
01048     h << "    " << cppType(e->type()) << " " << varName(e->name());
01049     if (!e->param().isEmpty())
01050     {
01051       h << QString("[%1]").arg(e->paramMax()+1);
01052     }
01053     h << ";" << endl;
01054   }
01055 
01056   h << endl << "  private:" << endl;
01057   if ( itemAccessors ) {
01058     for( e = entries.first(); e; e = entries.next() ) {
01059       h << "    Item" << itemType( e->type() ) << " *" << itemVar( e ) << ";" << endl;
01060     }
01061   }
01062 
01063   if (customAddons)
01064   {
01065      h << "    // Include custom additions" << endl;
01066      h << "    #include \"" << filenameOnly(baseName) << "_addons.h\"" <<endl;
01067   }
01068 
01069   h << "};" << endl << endl;
01070 
01071   if ( !nameSpace.isEmpty() ) h << "}" << endl << endl;
01072 
01073   h << "#endif" << endl;
01074 
01075 
01076   header.close();
01077 
01078   QFile implementation( baseDir + implementationFileName );
01079   if ( !implementation.open( IO_WriteOnly ) ) {
01080     kdError() << "Can't open '" << implementationFileName << "for writing."
01081               << endl;
01082     return 1;
01083   }
01084 
01085   QTextStream cpp( &implementation );
01086 
01087 
01088   cpp << "// This file is generated by kconfig_compiler from " << args->url(0).fileName() << "." << endl;
01089   cpp << "// All changes you do to this file will be lost." << endl << endl;
01090 
01091   cpp << "#include \"" << headerFileName << "\"" << endl << endl;
01092 
01093   if ( setUserTexts ) cpp << "#include <klocale.h>" << endl << endl;
01094 
01095   // Includes
01096   for( it = includes.begin(); it != includes.end(); ++it ) {
01097     cpp << "#include <" << *it << ">" << endl;
01098   }
01099 
01100   // Header required by singleton implementation
01101   if ( singleton )
01102     cpp << "#include <kstaticdeleter.h>" << endl << endl;
01103 
01104   if ( !nameSpace.isEmpty() )
01105     cpp << "using namespace " << nameSpace << ";" << endl << endl;
01106 
01107   // Singleton implementation
01108   if ( singleton ) {
01109     cpp << className << " *" << className << "::mSelf = 0;" << endl;
01110     cpp << "static KStaticDeleter<" << className << "> staticDeleter;" << endl << endl;
01111 
01112     cpp << className << " *" << className << "::self()" << endl;
01113     cpp << "{" << endl;
01114     cpp << "  if ( !mSelf ) {" << endl;
01115     cpp << "    staticDeleter.setObject( mSelf, new " << className << "() );" << endl;
01116     cpp << "    mSelf->readConfig();" << endl;
01117     cpp << "  }" << endl << endl;
01118     cpp << "  return mSelf;" << endl;
01119     cpp << "}" << endl << endl;
01120   }
01121 
01122   // Constructor
01123   cpp << className << "::" << className << "( ";
01124   if (cfgFileNameArg)
01125      cpp << " KSharedConfig::Ptr config" << (parameters.isEmpty() ? " " : ", ");
01126   for (QStringList::ConstIterator it = parameters.begin();
01127        it != parameters.end(); ++it)
01128   {
01129      if (it != parameters.begin())
01130        cpp << ",";
01131      cpp << " const QString &" << *it;
01132   }
01133   cpp << " )" << endl;
01134 
01135   cpp << "  : " << inherits << "(";
01136   if ( !cfgFileName.isEmpty() ) cpp << " \"" << cfgFileName << "\" ";
01137   if ( cfgFileNameArg ) cpp << " config ";
01138   cpp << ")" << endl;
01139 
01140   // Store parameters
01141   for (QStringList::ConstIterator it = parameters.begin();
01142        it != parameters.end(); ++it)
01143   {
01144      cpp << "  , mParam" << *it << "(" << *it << ")" << endl;
01145   }
01146 
01147   cpp << "{" << endl;
01148 
01149   // Needed in case the singleton class is used as baseclass for
01150   // another singleton.
01151   if ( singleton )
01152     cpp << "  mSelf = this;" << endl;
01153 
01154   group = QString::null;
01155   for( e = entries.first(); e; e = entries.next() ) {
01156     if ( e->group() != group ) {
01157       if ( !group.isEmpty() ) cpp << endl;
01158       group = e->group();
01159       cpp << "  setCurrentGroup( " << paramString(group, parameters) << " );" << endl << endl;
01160     }
01161 
01162     QString key = paramString(e->key(), parameters);
01163     if ( !e->code().isEmpty())
01164     {
01165       cpp << e->code() << endl;
01166     }
01167     if ( e->type() == "Enum" ) {
01168       cpp << "  QValueList<KConfigSkeleton::ItemEnum::Choice> values"
01169           << e->name() << ";" << endl;
01170       QValueList<CfgEntry::Choice> choices = e->choices();
01171       QValueList<CfgEntry::Choice>::ConstIterator it;
01172       for( it = choices.begin(); it != choices.end(); ++it ) {
01173         cpp << "  {" << endl;
01174         cpp << "    KConfigSkeleton::ItemEnum::Choice choice;" << endl;
01175         cpp << "    choice.name = \"" << (*it).name << "\";" << endl;
01176         if ( setUserTexts ) {
01177           if ( !(*it).label.isEmpty() )
01178             cpp << "    choice.label = i18n(\"" << (*it).label << "\");" << endl;
01179           if ( !(*it).whatsThis.isEmpty() )
01180             cpp << "    choice.whatsThis = i18n(\"" << (*it).whatsThis << "\");" << endl;
01181         }
01182         cpp << "    values" << e->name() << ".append( choice );" << endl;
01183         cpp << "  }" << endl;
01184       }
01185     }
01186     cpp << itemDeclaration(e);
01187     if (e->param().isEmpty())
01188     {
01189       // Normal case
01190       cpp << "  " << itemVar(e) << " = "
01191           << newItem( e->type(), e->name(), key, e->defaultValue() ) << endl;
01192 
01193       if ( !e->minValue().isEmpty() )
01194         cpp << "  " << itemVar(e) << "->setMinValue(" << e->minValue() << ");" << endl;
01195       if ( !e->maxValue().isEmpty() )
01196         cpp << "  " << itemVar(e) << "->setMaxValue(" << e->maxValue() << ");" << endl;
01197 
01198       if ( setUserTexts )
01199         cpp << userTextsFunctions( e );
01200 
01201       cpp << "  addItem( " << itemVar(e);
01202       QString quotedName = e->name();
01203       addQuotes( quotedName );
01204       if ( quotedName != key ) cpp << ", \"" << e->name() << "\"";
01205       cpp << " );" << endl;
01206     }
01207     else
01208     {
01209 // TODO: itemAccessors don't work with parameterized entries
01210 // TODO: itemVar(e) should be an array in that case
01211       // Indexed
01212       for(int i = 0; i <= e->paramMax(); i++)
01213       {
01214         QString defaultStr;
01215         if ( !e->paramDefaultValue(i).isEmpty() )
01216           defaultStr = e->paramDefaultValue(i);
01217         else if ( !e->defaultValue().isEmpty() )
01218           defaultStr = paramString(e->defaultValue(), e, i);
01219         else
01220           defaultStr = defaultValue( e->type() );
01221 
01222         cpp << "  " << itemVar(e) << " = "
01223             << newItem( e->type(), e->name(), paramString(key, e, i), defaultStr, QString("[%1]").arg(i) )
01224             << endl;
01225 
01226         if ( setUserTexts )
01227           cpp << userTextsFunctions( e );
01228 
01229         cpp << "  addItem( " << itemVar( e ) << ", \""
01230             << paramString(e->paramName(), e, i) << "\" );" << endl;
01231       }
01232     }
01233   }
01234 
01235   cpp << "}" << endl << endl;
01236 
01237   // Destructor
01238   cpp << className << "::~" << className << "()" << endl;
01239   cpp << "{" << endl;
01240   if ( singleton ) {
01241     cpp << "  if ( mSelf == this )" << endl;
01242     cpp << "    staticDeleter.setObject( mSelf, 0, false );" << endl;
01243   }
01244   cpp << "}" << endl << endl;
01245 
01246   implementation.close();
01247 }
KDE Logo
This file is part of the documentation for kdecore Library Version 3.2.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Feb 4 12:33:48 2004 by doxygen 1.2.18 written by Dimitri van Heesch, © 1997-2003