katesyntaxdocument.cpp
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2001 Joseph Wenninger <jowenn@kde.org> 00003 Copyright (C) 2000 Scott Manson <sdmanson@alltel.net> 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License version 2 as published by the Free Software Foundation. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00017 Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include "katesyntaxdocument.h" 00021 00022 #include <sys/types.h> 00023 #include <sys/stat.h> 00024 #include <unistd.h> 00025 00026 #include <kdebug.h> 00027 #include <kstandarddirs.h> 00028 #include <tdelocale.h> 00029 #include <tdemessagebox.h> 00030 #include <tdeconfig.h> 00031 00032 #include <tqfile.h> 00033 00034 KateSyntaxDocument::KateSyntaxDocument(bool force) 00035 : TQDomDocument() 00036 { 00037 // Let's build the Mode List (katesyntaxhighlightingrc) 00038 setupModeList(force); 00039 } 00040 00041 KateSyntaxDocument::~KateSyntaxDocument() 00042 { 00043 for (uint i=0; i < myModeList.size(); i++) 00044 delete myModeList[i]; 00045 } 00046 00051 bool KateSyntaxDocument::setIdentifier(const TQString& identifier) 00052 { 00053 // if the current file is the same as the new one don't do anything. 00054 if(currentFile != identifier) 00055 { 00056 // let's open the new file 00057 TQFile f( identifier ); 00058 00059 if ( f.open(IO_ReadOnly) ) 00060 { 00061 // Let's parse the contets of the xml file 00062 /* The result of this function should be check for robustness, 00063 a false returned means a parse error */ 00064 TQString errorMsg; 00065 int line, col; 00066 bool success=setContent(&f,&errorMsg,&line,&col); 00067 00068 // Ok, now the current file is the pretended one (identifier) 00069 currentFile = identifier; 00070 00071 // Close the file, is not longer needed 00072 f.close(); 00073 00074 if (!success) 00075 { 00076 KMessageBox::error(0L,i18n("<qt>The error <b>%4</b><br> has been detected in the file %1 at %2/%3</qt>").arg(identifier) 00077 .arg(line).arg(col).arg(i18n("TQXml",errorMsg.utf8()))); 00078 return false; 00079 } 00080 } 00081 else 00082 { 00083 // Oh o, we couldn't open the file. 00084 KMessageBox::error( 0L, i18n("Unable to open %1").arg(identifier) ); 00085 return false; 00086 } 00087 } 00088 return true; 00089 } 00090 00094 bool KateSyntaxDocument::nextGroup( KateSyntaxContextData* data) 00095 { 00096 if(!data) 00097 return false; 00098 00099 // No group yet so go to first child 00100 if (data->currentGroup.isNull()) 00101 { 00102 // Skip over non-elements. So far non-elements are just comments 00103 TQDomNode node = data->parent.firstChild(); 00104 while (node.isComment()) 00105 node = node.nextSibling(); 00106 00107 data->currentGroup = node.toElement(); 00108 } 00109 else 00110 { 00111 // common case, iterate over siblings, skipping comments as we go 00112 TQDomNode node = data->currentGroup.nextSibling(); 00113 while (node.isComment()) 00114 node = node.nextSibling(); 00115 00116 data->currentGroup = node.toElement(); 00117 } 00118 00119 return !data->currentGroup.isNull(); 00120 } 00121 00125 bool KateSyntaxDocument::nextItem( KateSyntaxContextData* data) 00126 { 00127 if(!data) 00128 return false; 00129 00130 if (data->item.isNull()) 00131 { 00132 TQDomNode node = data->currentGroup.firstChild(); 00133 while (node.isComment()) 00134 node = node.nextSibling(); 00135 00136 data->item = node.toElement(); 00137 } 00138 else 00139 { 00140 TQDomNode node = data->item.nextSibling(); 00141 while (node.isComment()) 00142 node = node.nextSibling(); 00143 00144 data->item = node.toElement(); 00145 } 00146 00147 return !data->item.isNull(); 00148 } 00149 00153 TQString KateSyntaxDocument::groupItemData( const KateSyntaxContextData* data, const TQString& name){ 00154 if(!data) 00155 return TQString::null; 00156 00157 // If there's no name just return the tag name of data->item 00158 if ( (!data->item.isNull()) && (name.isEmpty())) 00159 { 00160 return data->item.tagName(); 00161 } 00162 00163 // if name is not empty return the value of the attribute name 00164 if (!data->item.isNull()) 00165 { 00166 return data->item.attribute(name); 00167 } 00168 00169 return TQString::null; 00170 00171 } 00172 00173 TQString KateSyntaxDocument::groupData( const KateSyntaxContextData* data,const TQString& name) 00174 { 00175 if(!data) 00176 return TQString::null; 00177 00178 if (!data->currentGroup.isNull()) 00179 { 00180 return data->currentGroup.attribute(name); 00181 } 00182 else 00183 { 00184 return TQString::null; 00185 } 00186 } 00187 00188 void KateSyntaxDocument::freeGroupInfo( KateSyntaxContextData* data) 00189 { 00190 if (data) 00191 delete data; 00192 } 00193 00194 KateSyntaxContextData* KateSyntaxDocument::getSubItems(KateSyntaxContextData* data) 00195 { 00196 KateSyntaxContextData *retval = new KateSyntaxContextData; 00197 00198 if (data != 0) 00199 { 00200 retval->parent = data->currentGroup; 00201 retval->currentGroup = data->item; 00202 } 00203 00204 return retval; 00205 } 00206 00207 bool KateSyntaxDocument::getElement (TQDomElement &element, const TQString &mainGroupName, const TQString &config) 00208 { 00209 kdDebug(13010) << "Looking for \"" << mainGroupName << "\" -> \"" << config << "\"." << endl; 00210 00211 TQDomNodeList nodes = documentElement().childNodes(); 00212 00213 // Loop over all these child nodes looking for mainGroupName 00214 for (unsigned int i=0; i<nodes.count(); i++) 00215 { 00216 TQDomElement elem = nodes.item(i).toElement(); 00217 if (elem.tagName() == mainGroupName) 00218 { 00219 // Found mainGroupName ... 00220 TQDomNodeList subNodes = elem.childNodes(); 00221 00222 // ... so now loop looking for config 00223 for (unsigned int j=0; j<subNodes.count(); j++) 00224 { 00225 TQDomElement subElem = subNodes.item(j).toElement(); 00226 if (subElem.tagName() == config) 00227 { 00228 // Found it! 00229 element = subElem; 00230 return true; 00231 } 00232 } 00233 00234 kdDebug(13010) << "WARNING: \""<< config <<"\" wasn't found!" << endl; 00235 return false; 00236 } 00237 } 00238 00239 kdDebug(13010) << "WARNING: \""<< mainGroupName <<"\" wasn't found!" << endl; 00240 return false; 00241 } 00242 00247 KateSyntaxContextData* KateSyntaxDocument::getConfig(const TQString& mainGroupName, const TQString &config) 00248 { 00249 TQDomElement element; 00250 if (getElement(element, mainGroupName, config)) 00251 { 00252 KateSyntaxContextData *data = new KateSyntaxContextData; 00253 data->item = element; 00254 return data; 00255 } 00256 return 0; 00257 } 00258 00263 KateSyntaxContextData* KateSyntaxDocument::getGroupInfo(const TQString& mainGroupName, const TQString &group) 00264 { 00265 TQDomElement element; 00266 if (getElement(element, mainGroupName, group+"s")) 00267 { 00268 KateSyntaxContextData *data = new KateSyntaxContextData; 00269 data->parent = element; 00270 return data; 00271 } 00272 return 0; 00273 } 00274 00278 TQStringList& KateSyntaxDocument::finddata(const TQString& mainGroup, const TQString& type, bool clearList) 00279 { 00280 kdDebug(13010)<<"Create a list of keywords \""<<type<<"\" from \""<<mainGroup<<"\"."<<endl; 00281 if (clearList) 00282 m_data.clear(); 00283 00284 for(TQDomNode node = documentElement().firstChild(); !node.isNull(); node = node.nextSibling()) 00285 { 00286 TQDomElement elem = node.toElement(); 00287 if (elem.tagName() == mainGroup) 00288 { 00289 kdDebug(13010)<<"\""<<mainGroup<<"\" found."<<endl; 00290 TQDomNodeList nodelist1 = elem.elementsByTagName("list"); 00291 00292 for (uint l=0; l<nodelist1.count(); l++) 00293 { 00294 if (nodelist1.item(l).toElement().attribute("name") == type) 00295 { 00296 kdDebug(13010)<<"List with attribute name=\""<<type<<"\" found."<<endl; 00297 TQDomNodeList childlist = nodelist1.item(l).toElement().childNodes(); 00298 00299 for (uint i=0; i<childlist.count(); i++) 00300 { 00301 TQString element = childlist.item(i).toElement().text().stripWhiteSpace(); 00302 if (element.isEmpty()) 00303 continue; 00304 #ifndef NDEBUG 00305 if (i<6) 00306 { 00307 kdDebug(13010)<<"\""<<element<<"\" added to the list \""<<type<<"\""<<endl; 00308 } 00309 else if(i==6) 00310 { 00311 kdDebug(13010)<<"... The list continues ..."<<endl; 00312 } 00313 #endif 00314 m_data += element; 00315 } 00316 00317 break; 00318 } 00319 } 00320 break; 00321 } 00322 } 00323 00324 return m_data; 00325 } 00326 00327 // Private 00331 void KateSyntaxDocument::setupModeList (bool force) 00332 { 00333 // If there's something in myModeList the Mode List was already built so, don't do it again 00334 if (!myModeList.isEmpty()) 00335 return; 00336 00337 // We'll store the ModeList in katesyntaxhighlightingrc 00338 TDEConfig config("katesyntaxhighlightingrc", false, false); 00339 00340 // figure our if the kate install is too new 00341 config.setGroup ("General"); 00342 if (config.readNumEntry ("Version") > config.readNumEntry ("CachedVersion")) 00343 { 00344 config.writeEntry ("CachedVersion", config.readNumEntry ("Version")); 00345 force = true; 00346 } 00347 00348 // Let's get a list of all the xml files for hl 00349 TQStringList list = TDEGlobal::dirs()->findAllResources("data","katepart/syntax/*.xml",false,true); 00350 00351 // Let's iterate through the list and build the Mode List 00352 for ( TQStringList::Iterator it = list.begin(); it != list.end(); ++it ) 00353 { 00354 // Each file has a group called: 00355 TQString Group="Cache "+ *it; 00356 00357 // Let's go to this group 00358 config.setGroup(Group); 00359 00360 // stat the file 00361 struct stat sbuf; 00362 memset (&sbuf, 0, sizeof(sbuf)); 00363 stat(TQFile::encodeName(*it), &sbuf); 00364 00365 // If the group exist and we're not forced to read the xml file, let's build myModeList for katesyntax..rc 00366 if (!force && config.hasGroup(Group) && (sbuf.st_mtime == config.readNumEntry("lastModified"))) 00367 { 00368 // Let's make a new KateSyntaxModeListItem to instert in myModeList from the information in katesyntax..rc 00369 KateSyntaxModeListItem *mli=new KateSyntaxModeListItem; 00370 mli->name = config.readEntry("name"); 00371 mli->nameTranslated = i18n("Language",mli->name.utf8()); 00372 mli->section = i18n("Language Section",config.readEntry("section").utf8()); 00373 mli->mimetype = config.readEntry("mimetype"); 00374 mli->extension = config.readEntry("extension"); 00375 mli->version = config.readEntry("version"); 00376 mli->priority = config.readEntry("priority"); 00377 mli->author = config.readEntry("author"); 00378 mli->license = config.readEntry("license"); 00379 mli->hidden = config.readBoolEntry("hidden"); 00380 mli->identifier = *it; 00381 00382 // Apend the item to the list 00383 myModeList.append(mli); 00384 } 00385 else 00386 { 00387 kdDebug (13010) << "UPDATE hl cache for: " << *it << endl; 00388 00389 // We're forced to read the xml files or the mode doesn't exist in the katesyntax...rc 00390 TQFile f(*it); 00391 00392 if (f.open(IO_ReadOnly)) 00393 { 00394 // Ok we opened the file, let's read the contents and close the file 00395 /* the return of setContent should be checked because a false return shows a parsing error */ 00396 TQString errMsg; 00397 int line, col; 00398 00399 bool success = setContent(&f,&errMsg,&line,&col); 00400 00401 f.close(); 00402 00403 if (success) 00404 { 00405 TQDomElement root = documentElement(); 00406 00407 if (!root.isNull()) 00408 { 00409 // If the 'first' tag is language, go on 00410 if (root.tagName()=="language") 00411 { 00412 // let's make the mode list item. 00413 KateSyntaxModeListItem *mli = new KateSyntaxModeListItem; 00414 00415 mli->name = root.attribute("name"); 00416 mli->section = root.attribute("section"); 00417 mli->mimetype = root.attribute("mimetype"); 00418 mli->extension = root.attribute("extensions"); 00419 mli->version = root.attribute("version"); 00420 mli->priority = root.attribute("priority"); 00421 mli->author = root.attribute("author"); 00422 mli->license = root.attribute("license"); 00423 00424 TQString hidden = root.attribute("hidden"); 00425 mli->hidden = (hidden == "true" || hidden == "TRUE"); 00426 00427 mli->identifier = *it; 00428 00429 // Now let's write or overwrite (if force==true) the entry in katesyntax...rc 00430 config.setGroup(Group); 00431 config.writeEntry("name",mli->name); 00432 config.writeEntry("section",mli->section); 00433 config.writeEntry("mimetype",mli->mimetype); 00434 config.writeEntry("extension",mli->extension); 00435 config.writeEntry("version",mli->version); 00436 config.writeEntry("priority",mli->priority); 00437 config.writeEntry("author",mli->author); 00438 config.writeEntry("license",mli->license); 00439 config.writeEntry("hidden",mli->hidden); 00440 00441 // modified time to keep cache in sync 00442 config.writeEntry("lastModified", sbuf.st_mtime); 00443 00444 // Now that the data is in the config file, translate section 00445 mli->section = i18n("Language Section",mli->section.utf8()); 00446 mli->nameTranslated = i18n("Language",mli->name.utf8()); 00447 00448 // Append the new item to the list. 00449 myModeList.append(mli); 00450 } 00451 } 00452 } 00453 else 00454 { 00455 KateSyntaxModeListItem *emli=new KateSyntaxModeListItem; 00456 00457 emli->section=i18n("Errors!"); 00458 emli->mimetype="invalid_file/invalid_file"; 00459 emli->extension="invalid_file.invalid_file"; 00460 emli->version="1."; 00461 emli->name=TQString ("Error: %1").arg(*it); // internal 00462 emli->nameTranslated=i18n("Error: %1").arg(*it); // translated 00463 emli->identifier=(*it); 00464 00465 myModeList.append(emli); 00466 } 00467 } 00468 } 00469 } 00470 00471 // Syncronize with the file katesyntax...rc 00472 config.sync(); 00473 } 00474 00475 // kate: space-indent on; indent-width 2; replace-tabs on;