You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
398 lines
9.5 KiB
398 lines
9.5 KiB
14 years ago
|
/**********************************************************************
|
||
|
** Copyright (C) 2000-2008 Trolltech ASA. All rights reserved.
|
||
|
**
|
||
|
** This file is part of the Qt Assistant.
|
||
|
**
|
||
|
** This file may be used under the terms of the GNU General
|
||
|
** Public License versions 2.0 or 3.0 as published by the Free
|
||
|
** Software Foundation and appearing in the files LICENSE.GPL2
|
||
|
** and LICENSE.GPL3 included in the packaging of this file.
|
||
|
** Alternatively you may (at your option) use any later version
|
||
|
** of the GNU General Public License if such license has been
|
||
|
** publicly approved by Trolltech ASA (or its successors, if any)
|
||
|
** and the KDE Free Qt Foundation.
|
||
|
**
|
||
|
** Please review the following information to ensure GNU General
|
||
|
** Public Licensing requirements will be met:
|
||
|
** http://trolltech.com/products/qt/licenses/licensing/opensource/.
|
||
|
** If you are unsure which license is appropriate for your use, please
|
||
|
** review the following information:
|
||
|
** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
|
||
|
** or contact the sales department at sales@trolltech.com.
|
||
|
**
|
||
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
||
|
** accordance with the Qt Commercial License Agreement provided with
|
||
|
** the Software.
|
||
|
**
|
||
|
** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
|
||
|
** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
|
||
|
** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
|
||
|
** herein.
|
||
|
**
|
||
|
**********************************************************************/
|
||
|
|
||
|
#include "docuparser.h"
|
||
|
#include "profile.h"
|
||
|
|
||
|
#include <qdir.h>
|
||
|
#include <qfile.h>
|
||
|
#include <qfileinfo.h>
|
||
|
#include <qregexp.h>
|
||
|
#include <qstring.h>
|
||
|
#include <qxml.h>
|
||
|
|
||
|
QDataStream &operator>>( QDataStream &s, ContentItem &ci )
|
||
|
{
|
||
|
s >> ci.title;
|
||
|
s >> ci.reference;
|
||
|
s >> ci.depth;
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
QDataStream &operator<<( QDataStream &s, const ContentItem &ci )
|
||
|
{
|
||
|
s << ci.title;
|
||
|
s << ci.reference;
|
||
|
s << ci.depth;
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
const QString DocuParser::DocumentKey = "/Qt Assistant/" + QString(QT_VERSION_STR) + "/";
|
||
|
|
||
|
DocuParser *DocuParser::createParser( const QString &fileName )
|
||
|
{
|
||
|
QFile file( fileName );
|
||
|
if( !file.open( IO_ReadOnly ) ) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
QString str;
|
||
|
int read = 0;
|
||
|
int maxlen = 1024;
|
||
|
int majVer = 0, minVer = 0, serVer = 0;
|
||
|
static QRegExp re( "assistantconfig +version=\"(\\d)\\.(\\d)\\.(\\d)\"", FALSE );
|
||
|
Q_ASSERT( re.isValid() );
|
||
|
while( read != -1 ) {
|
||
|
read = file.readLine( str, maxlen );
|
||
|
if( re.search( str ) >= 0 ) {
|
||
|
majVer = re.cap( 1 ).toInt();
|
||
|
minVer = re.cap( 2 ).toInt();
|
||
|
serVer = re.cap( 3 ).toInt();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( majVer == 3 && minVer >= 2 )
|
||
|
return new DocuParser320;
|
||
|
|
||
|
return new DocuParser310;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool DocuParser::parse( QFile *file )
|
||
|
{
|
||
|
QXmlInputSource source( file );
|
||
|
QXmlSimpleReader reader;
|
||
|
reader.setContentHandler( this );
|
||
|
reader.setErrorHandler( this );
|
||
|
setFileName( QFileInfo( *file ).absFilePath() );
|
||
|
return reader.parse( source );
|
||
|
}
|
||
|
|
||
|
|
||
|
QString DocuParser::errorProtocol() const
|
||
|
{
|
||
|
return errorProt;
|
||
|
}
|
||
|
|
||
|
|
||
|
QValueList<ContentItem> DocuParser::getContentItems()
|
||
|
{
|
||
|
return contentList;
|
||
|
}
|
||
|
|
||
|
|
||
|
QPtrList<IndexItem> DocuParser::getIndexItems()
|
||
|
{
|
||
|
return indexList;
|
||
|
}
|
||
|
|
||
|
QString DocuParser::absolutify( const QString &name ) const
|
||
|
{
|
||
|
QFileInfo orgPath( name );
|
||
|
if( orgPath.isRelative() )
|
||
|
return QFileInfo( fname ).dirPath() + QDir::separator() + name;
|
||
|
return name;
|
||
|
}
|
||
|
|
||
|
|
||
|
void DocuParser310::addTo( Profile *p )
|
||
|
{
|
||
|
p->addDCFTitle( fname, docTitle );
|
||
|
p->addDCFIcon( docTitle, iconName );
|
||
|
p->addDCFIndexPage( docTitle, conURL );
|
||
|
}
|
||
|
|
||
|
|
||
|
bool DocuParser310::startDocument()
|
||
|
{
|
||
|
state = StateInit;
|
||
|
errorProt = "";
|
||
|
|
||
|
contentRef = "";
|
||
|
indexRef = "";
|
||
|
depth = 0;
|
||
|
contentList.clear();
|
||
|
indexList.clear();
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool DocuParser310::startElement( const QString &, const QString &,
|
||
|
const QString &qname,
|
||
|
const QXmlAttributes &attr )
|
||
|
{
|
||
|
if (qname == "DCF" && state == StateInit) {
|
||
|
state = StateContent;
|
||
|
contentRef = absolutify( attr.value( "ref" ) );
|
||
|
conURL = contentRef;
|
||
|
docTitle = attr.value( "title" );
|
||
|
iconName = absolutify( attr.value( "icon" ) );
|
||
|
contentList.append( ContentItem( docTitle, contentRef, depth ) );
|
||
|
} else if (qname == "section" && (state == StateContent || state == StateSect)) {
|
||
|
state = StateSect;
|
||
|
contentRef = absolutify( attr.value( "ref" ) );
|
||
|
title = attr.value( "title" );
|
||
|
depth++;
|
||
|
contentList.append( ContentItem( title, contentRef, depth ) );
|
||
|
} else if (qname == "keyword" && state == StateSect) {
|
||
|
state = StateKeyword;
|
||
|
indexRef = absolutify( attr.value( "ref" ) );
|
||
|
} else
|
||
|
return FALSE;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
bool DocuParser310::endElement( const QString &nameSpace, const QString &localName,
|
||
|
const QString &qName )
|
||
|
{
|
||
|
switch( state ) {
|
||
|
case StateInit:
|
||
|
break;
|
||
|
case StateContent:
|
||
|
state = StateInit;
|
||
|
break;
|
||
|
case StateSect:
|
||
|
state = --depth ? StateSect : StateContent;
|
||
|
break;
|
||
|
case StateKeyword:
|
||
|
state = StateSect;
|
||
|
break;
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool DocuParser310::characters( const QString& ch )
|
||
|
{
|
||
|
QString str = ch.simplifyWhiteSpace();
|
||
|
if ( str.isEmpty() )
|
||
|
return TRUE;
|
||
|
|
||
|
switch ( state ) {
|
||
|
case StateInit:
|
||
|
case StateContent:
|
||
|
case StateSect:
|
||
|
return FALSE;
|
||
|
break;
|
||
|
case StateKeyword:
|
||
|
indexList.append( new IndexItem( str, indexRef ) );
|
||
|
break;
|
||
|
default:
|
||
|
return FALSE;
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool DocuParser310::fatalError( const QXmlParseException& exception )
|
||
|
{
|
||
|
errorProt += QString( "fatal parsing error: %1 in line %2, column %3\n" )
|
||
|
.arg( exception.message() )
|
||
|
.arg( exception.lineNumber() )
|
||
|
.arg( exception.columnNumber() );
|
||
|
|
||
|
return QXmlDefaultHandler::fatalError( exception );
|
||
|
}
|
||
|
|
||
|
|
||
|
DocuParser320::DocuParser320()
|
||
|
: prof( new Profile )
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
void DocuParser320::addTo( Profile *p )
|
||
|
{
|
||
|
QMap<QString,QString>::ConstIterator it;
|
||
|
|
||
|
for (it = prof->dcfTitles.begin(); it != prof->dcfTitles.end(); ++it)
|
||
|
p->dcfTitles[it.key()] = *it;
|
||
|
|
||
|
for (it = prof->icons.begin(); it != prof->icons.end(); ++it)
|
||
|
p->icons[it.key()] = *it;
|
||
|
|
||
|
for (it = prof->indexPages.begin(); it != prof->indexPages.end(); ++it)
|
||
|
p->indexPages[it.key()] = *it;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool DocuParser320::startDocument()
|
||
|
{
|
||
|
state = StateInit;
|
||
|
errorProt = "";
|
||
|
|
||
|
contentRef = "";
|
||
|
indexRef = "";
|
||
|
depth = 0;
|
||
|
contentList.clear();
|
||
|
indexList.clear();
|
||
|
|
||
|
prof->addDCF( fname );
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
bool DocuParser320::startElement( const QString &, const QString &,
|
||
|
const QString &qname,
|
||
|
const QXmlAttributes &attr )
|
||
|
{
|
||
|
QString lower = qname.lower();
|
||
|
|
||
|
switch( state ) {
|
||
|
|
||
|
case StateInit:
|
||
|
if( lower == "assistantconfig" )
|
||
|
state = StateDocRoot;
|
||
|
break;
|
||
|
|
||
|
case StateDocRoot:
|
||
|
if( lower == "dcf" ) {
|
||
|
state = StateContent;
|
||
|
contentRef = absolutify( attr.value( "ref" ) );
|
||
|
conURL = contentRef;
|
||
|
docTitle = attr.value( "title" );
|
||
|
iconName = absolutify( attr.value( "icon" ) );
|
||
|
contentList.append( ContentItem( docTitle, contentRef, depth ) );
|
||
|
} else if( lower == "profile" ) {
|
||
|
state = StateProfile;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case StateSect:
|
||
|
if ( lower == "keyword" && state == StateSect ) {
|
||
|
state = StateKeyword;
|
||
|
indexRef = absolutify( attr.value( "ref" ) );
|
||
|
break;
|
||
|
} // else if (lower == "section")
|
||
|
case StateContent:
|
||
|
if( lower == "section" ) {
|
||
|
state = StateSect;
|
||
|
contentRef = absolutify( attr.value( "ref" ) );
|
||
|
title = attr.value( "title" );
|
||
|
depth++;
|
||
|
contentList.append( ContentItem( title, contentRef, depth ) );
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case StateProfile:
|
||
|
if( lower == "property" ) {
|
||
|
state = StateProperty;
|
||
|
propertyName = attr.value( "name" );
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case StateProperty:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
bool DocuParser320::endElement( const QString &nameSpace,
|
||
|
const QString &localName,
|
||
|
const QString &qName )
|
||
|
{
|
||
|
switch( state ) {
|
||
|
case StateInit:
|
||
|
break;
|
||
|
case StateDocRoot:
|
||
|
state = StateInit;
|
||
|
break;
|
||
|
case StateProfile:
|
||
|
state = StateDocRoot;
|
||
|
break;
|
||
|
case StateProperty:
|
||
|
state = StateProfile;
|
||
|
if( propertyName.isEmpty() || propertyValue.isEmpty() )
|
||
|
return FALSE;
|
||
|
{
|
||
|
static const QStringList lst = QStringList() << "startpage" << "abouturl"
|
||
|
<< "applicationicon" << "assistantdocs";
|
||
|
if (lst.contains(propertyName))
|
||
|
propertyValue = absolutify( propertyValue );
|
||
|
}
|
||
|
prof->addProperty( propertyName, propertyValue );
|
||
|
break;
|
||
|
case StateContent:
|
||
|
if( !iconName.isEmpty() ) prof->addDCFIcon( docTitle, iconName );
|
||
|
if( contentRef.isEmpty() )
|
||
|
return FALSE;
|
||
|
prof->addDCFIndexPage( docTitle, conURL );
|
||
|
prof->addDCFTitle( fname, docTitle );
|
||
|
state = StateDocRoot;
|
||
|
break;
|
||
|
case StateSect:
|
||
|
state = --depth ? StateSect : StateContent;
|
||
|
break;
|
||
|
case StateKeyword:
|
||
|
state = StateSect;
|
||
|
break;
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
bool DocuParser320::characters( const QString& ch )
|
||
|
{
|
||
|
QString str = ch.simplifyWhiteSpace();
|
||
|
if ( str.isEmpty() )
|
||
|
return TRUE;
|
||
|
|
||
|
switch ( state ) {
|
||
|
case StateInit:
|
||
|
case StateContent:
|
||
|
case StateSect:
|
||
|
return FALSE;
|
||
|
break;
|
||
|
case StateKeyword:
|
||
|
indexList.append( new IndexItem( str, indexRef ) );
|
||
|
break;
|
||
|
case StateProperty:
|
||
|
propertyValue = ch;
|
||
|
break;
|
||
|
default:
|
||
|
return FALSE;
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
bool DocuParser320::fatalError( const QXmlParseException& exception )
|
||
|
{
|
||
|
errorProt += QString( "fatal parsing error: %1 in line %2, column %3\n" )
|
||
|
.arg( exception.message() )
|
||
|
.arg( exception.lineNumber() )
|
||
|
.arg( exception.columnNumber() );
|
||
|
|
||
|
return QXmlDefaultHandler::fatalError( exception );
|
||
|
}
|