/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2000, 2010 Oracle and/or its affiliates. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ #include "sal/config.h" #include "rtl/strbuf.hxx" #include "rtl/string.hxx" #include "rtl/ustrbuf.hxx" #include "rtl/ustring.hxx" #include "tagtest.hxx" #if OSL_DEBUG_LEVEL > 1 #include #endif #include "gsicheck.hxx" #include "helper.hxx" #define HAS_FLAG( nFlags, nFlag ) ( ( nFlags & nFlag ) != 0 ) #define SET_FLAG( nFlags, nFlag ) ( nFlags |= nFlag ) #define RESET_FLAG( nFlags, nFlag ) ( nFlags &= ~nFlag ) // ~ = Bitweises NOT TokenInfo::TokenInfo( TokenId pnId, sal_Int32 nP, rtl::OUString const & paStr, ParserMessageList &rErrorList ) : bClosed(sal_False) , bCloseTag(sal_False) , bIsBroken(sal_False) , bHasBeenFixed(sal_False) , bDone(sal_False) , aTokenString( paStr ) , nId( pnId ) , nPos(nP) { if ( nId == TAG_COMMONSTART || nId == TAG_COMMONEND ) SplitTag( rErrorList ); } enum tagcheck { TC_START, TC_HAS_TAG_NAME, TC_HAS_PROP_NAME_EQ, TC_HAS_PROP_NAME_EQ_SP, TC_HAS_PROP_NAME_SP, TC_INSIDE_STRING, TC_PROP_FINISHED, TC_CLOSED, TC_CLOSED_SPACE, TC_CLOSETAG, TC_CLOSETAG_HAS_TAG_NAME, TC_FINISHED, TC_ERROR }; /* \< link href = \"text\" name = \"C\" \> START ' ' -> HAS_TAG_NAME START '/' -> CLOSED START '/' -> CLOSETAG - no Portion (starting with /) START '>' -> FINISHED HAS_TAG_NAME '=' -> HAS_PROP_NAME_EQ HAS_TAG_NAME ' ' -> HAS_PROP_NAME_SP HAS_TAG_NAME '/' -> CLOSED HAS_TAG_NAME '>' -> FINISHED HAS_PROP_NAME_SP '=' -> HAS_PROP_NAME_EQ HAS_PROP_NAME_EQ ' ' -> HAS_PROP_NAME_EQ_SP HAS_PROP_NAME_EQ '"' -> INSIDE_STRING HAS_PROP_NAME_EQ_SP '"' -> INSIDE_STRING INSIDE_STRING ' ' -> INSIDE_STRING INSIDE_STRING '=' -> INSIDE_STRING INSIDE_STRING '>' -> INSIDE_STRING INSIDE_STRING '"' -> PROP_FINISHED PROP_FINISHED ' ' -> HAS_TAG_NAME PROP_FINISHED '/' -> CLOSED PROP_FINISHED '>' -> FINISHED CLOSED ' ' -> CLOSED_SPACE CLOSED '>' -> FINISHED CLOSED_SPACE '>' -> FINISHED CLOSETAG ' ' -> CLOSETAG_HAS_TAG_NAME CLOSETAG '>' -> FINISHED CLOSETAG_HAS_TAG_NAME '>' -> FINISHED */ void TokenInfo::SplitTag( ParserMessageList &rErrorList ) { sal_Int32 nLastPos = 2; // skip initial \< sal_Int32 nCheckPos = nLastPos; static char const aDelims[] = " \\=>/"; rtl::OUString aPortion; rtl::OUString aValue; // store the value of a property rtl::OString aName; // store the name of a property/tag sal_Bool bCheckName = sal_False; sal_Bool bCheckEmpty = sal_False; sal_Unicode cDelim; tagcheck aState = TC_START; // skip blanks while ( nLastPos < aTokenString.getLength() && aTokenString[nLastPos] == ' ') nLastPos++; nCheckPos = helper::indexOfAnyAsciiL( aTokenString, RTL_CONSTASCII_STRINGPARAM(aDelims), nLastPos); while ( nCheckPos != -1 && !( aState == TC_FINISHED || aState == TC_ERROR ) ) { aPortion = aTokenString.copy( nLastPos, nCheckPos-nLastPos ); if ( aTokenString[nCheckPos] == '\\' ) nCheckPos++; cDelim = aTokenString[nCheckPos]; nCheckPos++; switch ( aState ) { // START ' ' -> HAS_TAG_NAME // START '/' -> CLOSED // START '>' -> FINISHED case TC_START: aTagName = aPortion; switch ( cDelim ) { case ' ': aState = TC_HAS_TAG_NAME; bCheckName = sal_True; break; case '/': { if (aPortion.isEmpty()) { aState = TC_CLOSETAG; } else { aState = TC_CLOSED; bCheckName = sal_True; } } break; case '>': aState = TC_FINISHED; bCheckName = sal_True; break; default: aState = TC_ERROR; } break; // HAS_TAG_NAME '=' -> HAS_PROP_NAME_EQ // HAS_TAG_NAME ' ' -> HAS_PROP_NAME_SP // HAS_TAG_NAME '/' -> CLOSED // HAS_TAG_NAME '>' -> FINISHED case TC_HAS_TAG_NAME: switch ( cDelim ) { case '=': aState = TC_HAS_PROP_NAME_EQ; bCheckName = sal_True; break; case ' ': aState = TC_HAS_PROP_NAME_SP; bCheckName = sal_True; break; case '/': aState = TC_CLOSED; bCheckEmpty = sal_True; break; case '>': aState = TC_FINISHED; bCheckEmpty = sal_True; break; default: aState = TC_ERROR; } break; // HAS_PROP_NAME_SP '=' -> HAS_PROP_NAME_EQ case TC_HAS_PROP_NAME_SP: switch ( cDelim ) { case '=': aState = TC_HAS_PROP_NAME_EQ; bCheckEmpty = sal_True; break; default: aState = TC_ERROR; } break; // HAS_PROP_NAME_EQ ' ' -> HAS_PROP_NAME_EQ_SP // HAS_PROP_NAME_EQ '"' -> INSIDE_STRING case TC_HAS_PROP_NAME_EQ: switch ( cDelim ) { case ' ': aState = TC_HAS_PROP_NAME_EQ_SP; bCheckEmpty = sal_True; break; case '\"': aState = TC_INSIDE_STRING; bCheckEmpty = sal_True; aValue = rtl::OUString(); break; default: aState = TC_ERROR; } break; // HAS_PROP_NAME_EQ_SP '"' -> INSIDE_STRING case TC_HAS_PROP_NAME_EQ_SP: switch ( cDelim ) { case '\"': aState = TC_INSIDE_STRING; bCheckEmpty = sal_True; aValue = rtl::OUString(); break; default: aState = TC_ERROR; } break; // INSIDE_STRING * -> INSIDE_STRING // INSIDE_STRING '"' -> PROP_FINISHED case TC_INSIDE_STRING: switch ( cDelim ) { case '\"': { aState = TC_PROP_FINISHED; aValue += aPortion; if ( aProperties.find( aName ) == aProperties.end() ) { if ( !IsPropertyValueValid( aName, aValue ) ) { rErrorList.AddError( 25, rtl::OStringBuffer(RTL_CONSTASCII_STRINGPARAM("Property '")).append(aName).append(RTL_CONSTASCII_STRINGPARAM("' has invalid value '")).append(rtl::OUStringToOString(aValue, RTL_TEXTENCODING_UTF8)).append("' ").makeStringAndClear(), *this ); bIsBroken = sal_True; } aProperties[ aName ] = aValue; } else { rErrorList.AddError( 25, rtl::OStringBuffer(RTL_CONSTASCII_STRINGPARAM("Property '")).append(aName).append(RTL_CONSTASCII_STRINGPARAM("' defined twice ")).makeStringAndClear(), *this ); bIsBroken = sal_True; } } break; default: { aState = TC_INSIDE_STRING; aValue += aPortion; aValue += rtl::OUString(cDelim); } } break; // PROP_FINISHED ' ' -> HAS_TAG_NAME // PROP_FINISHED '/' -> CLOSED // PROP_FINISHED '>' -> FINISHED case TC_PROP_FINISHED: switch ( cDelim ) { case ' ': aState = TC_HAS_TAG_NAME; bCheckEmpty = sal_True; break; case '/': aState = TC_CLOSED; bCheckEmpty = sal_True; break; case '>': aState = TC_FINISHED; bCheckEmpty = sal_True; break; default: aState = TC_ERROR; } break; // CLOSED ' ' -> CLOSED_SPACE // CLOSED '>' -> FINISHED case TC_CLOSED: switch ( cDelim ) { case ' ': aState = TC_CLOSED_SPACE; bCheckEmpty = sal_True; bClosed = sal_True; break; case '>': aState = TC_FINISHED; bCheckEmpty = sal_True; break; default: aState = TC_ERROR; } break; // CLOSED_SPACE '>' -> FINISHED case TC_CLOSED_SPACE: switch ( cDelim ) { case '>': aState = TC_FINISHED; bCheckEmpty = sal_True; break; default: aState = TC_ERROR; } break; // CLOSETAG ' ' -> CLOSETAG_HAS_TAG_NAME // CLOSETAG '>' -> FINISHED case TC_CLOSETAG: bCloseTag = sal_True; switch ( cDelim ) { case ' ': aState = TC_CLOSETAG_HAS_TAG_NAME; aTagName = aPortion; bCheckName = sal_True; break; case '>': aState = TC_FINISHED; aTagName = aPortion; bCheckName = sal_True; break; default: aState = TC_ERROR; } break; // CLOSETAG_HAS_TAG_NAME '>' -> FINISHED case TC_CLOSETAG_HAS_TAG_NAME: switch ( cDelim ) { case '>': aState = TC_FINISHED; bCheckEmpty = sal_True; break; default: aState = TC_ERROR; } break; default: rErrorList.AddError( 99, "Internal error Parsing Tag ", *this ); bIsBroken = sal_True; } if ( bCheckName ) { if (aPortion.isEmpty()) { rErrorList.AddError( 25, "Tag/Property name missing ", *this ); bIsBroken = sal_True; } else { aName = rtl::OUStringToOString(aPortion, RTL_TEXTENCODING_UTF8); // "a-zA-Z_-.0-9" sal_Bool bBroken = sal_False; const sal_Char* aBuf = aName.getStr(); for (sal_Int32 nCount = 0 ; !bBroken && nCount < aName.getLength() ; ++nCount) { bBroken = ! ( ( aBuf[nCount] >= 'a' && aBuf[nCount] <= 'z' ) ||( aBuf[nCount] >= 'A' && aBuf[nCount] <= 'Z' ) ||( aBuf[nCount] >= '0' && aBuf[nCount] <= '9' ) ||( aBuf[nCount] == '_' ) ||( aBuf[nCount] == '-' ) ||( aBuf[nCount] == '.' ) ); } if ( bBroken ) { rErrorList.AddError( 25, "Found illegal character in Tag/Property name ", *this ); bIsBroken = sal_True; } } bCheckName = sal_False; } if ( bCheckEmpty ) { if (!aPortion.isEmpty()) { rErrorList.AddError( 25, rtl::OStringBuffer(RTL_CONSTASCII_STRINGPARAM("Found displaced characters '")).append(rtl::OUStringToOString(aPortion, RTL_TEXTENCODING_UTF8)).append(RTL_CONSTASCII_STRINGPARAM("' in Tag ")).makeStringAndClear(), *this ); bIsBroken = sal_True; } bCheckEmpty = sal_False; } nLastPos = nCheckPos; // skip further blanks if ( cDelim == ' ' && aState != TC_INSIDE_STRING ) while ( nLastPos < aTokenString.getLength() && aTokenString[nLastPos] == ' ') nLastPos++; nCheckPos = helper::indexOfAnyAsciiL( aTokenString, RTL_CONSTASCII_STRINGPARAM(aDelims), nLastPos); } if ( aState != TC_FINISHED ) { rErrorList.AddError( 25, "Parsing error in Tag ", *this ); bIsBroken = sal_True; } } sal_Bool TokenInfo::IsPropertyRelevant( const rtl::OString &rName, const rtl::OUString &rValue ) const { if ( aTagName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("alt")) && rName.equalsL(RTL_CONSTASCII_STRINGPARAM("xml-lang")) ) return sal_False; if ( aTagName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("ahelp")) && rName.equalsL(RTL_CONSTASCII_STRINGPARAM("visibility")) && rValue.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("visible")) ) return sal_False; if ( aTagName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("image")) && (rName.equalsL(RTL_CONSTASCII_STRINGPARAM("width")) || rName.equalsL(RTL_CONSTASCII_STRINGPARAM("height"))) ) return sal_False; return sal_True; } sal_Bool TokenInfo::IsPropertyValueValid( const rtl::OString &rName, const rtl::OUString &rValue ) const { /* removed due to i56740 if ( aTagName.EqualsAscii( "switchinline" ) && rName.equalsL(RTL_CONSTASCII_STRINGPARAM("select")) ) { return rValue.EqualsAscii("sys") || rValue.EqualsAscii("appl") || rValue.EqualsAscii("distrib"); } */ if ( aTagName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("caseinline")) && rName.equalsL(RTL_CONSTASCII_STRINGPARAM("select")) ) { return !rValue.isEmpty(); } // we don't know any better so we assume it to be OK return sal_True; } sal_Bool TokenInfo::IsPropertyInvariant( const rtl::OString &rName, const rtl::OUString &rValue ) const { if ( aTagName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("link")) && rName.equalsL(RTL_CONSTASCII_STRINGPARAM("name")) ) return sal_False; if ( aTagName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("link")) && rName.equalsL(RTL_CONSTASCII_STRINGPARAM("href")) ) { // check for external reference return !(rValue.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM("http:")) || rValue.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM("https:")) || rValue.matchIgnoreAsciiCaseAsciiL( RTL_CONSTASCII_STRINGPARAM("ftp:"))); } return sal_True; } sal_Bool TokenInfo::IsPropertyFixable( const rtl::OString &rName ) const { // name everything that is allowed to be fixed automatically here if ( (aTagName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("ahelp")) && rName.equalsL(RTL_CONSTASCII_STRINGPARAM("hid"))) || (aTagName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("link")) && rName.equalsL(RTL_CONSTASCII_STRINGPARAM("href"))) || (aTagName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("alt")) && rName.equalsL(RTL_CONSTASCII_STRINGPARAM("id"))) || (aTagName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("variable")) && rName.equalsL(RTL_CONSTASCII_STRINGPARAM("id"))) || (aTagName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("image")) && rName.equalsL(RTL_CONSTASCII_STRINGPARAM("src"))) || (aTagName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("image")) && rName.equalsL(RTL_CONSTASCII_STRINGPARAM("id")) )) return sal_True; return sal_False; } sal_Bool TokenInfo::MatchesTranslation( TokenInfo& rInfo, sal_Bool bGenErrors, ParserMessageList &rErrorList, sal_Bool bFixTags ) const { // check if tags are equal // check if all existing properties are in the translation as well and // wether they have a matching content (the same in most cases) if ( nId != rInfo.nId ) return sal_False; if ( aTagName != rInfo.aTagName ) return sal_False; // If one of the tags has formating errors already it does make no sense to check here, so return right away if ( bGenErrors && ( bIsBroken || rInfo.bIsBroken ) ) return sal_True; StringHashMap::const_iterator iProp; for( iProp = aProperties.begin() ; iProp != aProperties.end(); ++iProp ) { if ( rInfo.aProperties.find( iProp->first ) != rInfo.aProperties.end() ) { if ( IsPropertyRelevant( iProp->first, iProp->second ) || IsPropertyRelevant( iProp->first, rInfo.aProperties.find( iProp->first )->second ) ) { if ( IsPropertyInvariant( iProp->first, iProp->second ) ) { if ( rInfo.aProperties.find( iProp->first )->second != iProp->second ) { if ( bGenErrors ) { if ( bFixTags && IsPropertyFixable( iProp->first ) ) { rInfo.aProperties.find( iProp->first )->second = iProp->second; rInfo.SetHasBeenFixed(); rErrorList.AddWarning( 25, rtl::OStringBuffer(RTL_CONSTASCII_STRINGPARAM("Property '")).append(iProp->first).append(RTL_CONSTASCII_STRINGPARAM("': FIXED different value in Translation ")).makeStringAndClear(), *this ); } else rErrorList.AddError( 25, rtl::OStringBuffer(RTL_CONSTASCII_STRINGPARAM("Property '")).append(iProp->first).append(RTL_CONSTASCII_STRINGPARAM("': value different in Translation ")).makeStringAndClear(), *this ); } else return sal_False; } } } } else { if ( IsPropertyRelevant( iProp->first, iProp->second ) ) { if ( bGenErrors ) rErrorList.AddError( 25, rtl::OStringBuffer(RTL_CONSTASCII_STRINGPARAM("Property '")).append(iProp->first).append(RTL_CONSTASCII_STRINGPARAM("' missing in Translation ")).makeStringAndClear(), *this ); else return sal_False; } } } for( iProp = rInfo.aProperties.begin() ; iProp != rInfo.aProperties.end(); ++iProp ) { if ( aProperties.find( iProp->first ) == aProperties.end() ) { if ( IsPropertyRelevant( iProp->first, iProp->second ) ) { if ( bGenErrors ) rErrorList.AddError( 25, rtl::OStringBuffer(RTL_CONSTASCII_STRINGPARAM("Extra Property '")).append(iProp->first).append(RTL_CONSTASCII_STRINGPARAM("' in Translation ")).makeStringAndClear(), rInfo ); else return sal_False; } } } // if we reach here eather // the tags match completely or // the tags match but not the properties and we generated errors for that return sal_True; } rtl::OUString TokenInfo::GetTagName() const { return aTagName; } rtl::OUString TokenInfo::MakeTag() const { rtl::OUStringBuffer aRet; aRet.appendAscii("\\<"); if ( bCloseTag ) aRet.appendAscii("/"); aRet.append( GetTagName() ); StringHashMap::const_iterator iProp; for( iProp = aProperties.begin() ; iProp != aProperties.end(); ++iProp ) { aRet.appendAscii(" "); aRet.append( rtl::OStringToOUString( iProp->first, RTL_TEXTENCODING_UTF8 ) ); aRet.appendAscii("=\\\""); aRet.append( iProp->second ); aRet.appendAscii("\\\""); } if ( bClosed ) aRet.appendAscii("/"); aRet.appendAscii("\\>"); return aRet.makeStringAndClear(); } void ParserMessageList::AddError( sal_Int32 nErrorNr, const rtl::OString& rErrorText, const TokenInfo &rTag ) { maList.push_back( new ParserError( nErrorNr, rErrorText, rTag ) ); } void ParserMessageList::AddWarning( sal_Int32 nErrorNr, const rtl::OString& rErrorText, const TokenInfo &rTag ) { maList.push_back( new ParserWarning( nErrorNr, rErrorText, rTag ) ); } sal_Bool ParserMessageList::HasErrors() { for ( size_t i = 0, n = maList.size(); i < n; ++i ) if ( maList[ i ]->IsError() ) return sal_True; return sal_False; } void ParserMessageList::clear() { for ( size_t i = 0, n = maList.size(); i < n; ++i ) delete maList[ i ]; maList.clear(); } struct Tag { rtl::OUString GetName() const { return rtl::OUString::createFromAscii( pName ); }; const char* pName; TokenId nTag; }; static const Tag aKnownTags[] = { /* commenting oldstyle tags // { "<#GROUP_FORMAT>", TAG_GROUP_FORMAT }, { "<#BOLD>", TAG_BOLDON }, { "<#/BOLD>", TAG_BOLDOFF }, { "<#ITALIC>", TAG_ITALICON }, { "<#/ITALIC>", TAG_ITALICOFF }, { "<#UNDER>", TAG_UNDERLINEON }, { "<#/UNDER>", TAG_UNDERLINEOFF }, // { "<#GROUP_NOTALLOWED>", TAG_GROUP_NOTALLOWED }, { "<#HELPID>", TAG_HELPID }, { "<#MODIFY>", TAG_MODIFY }, { "<#REFNR>", TAG_REFNR }, // { "<#GROUP_STRUCTURE>", TAG_GROUP_STRUCTURE }, { "<#NAME>", TAG_NAME }, { "<#HREF>", TAG_HREF }, { "<#AVIS>", TAG_AVIS }, { "<#AHID>", TAG_AHID }, { "<#AEND>", TAG_AEND }, { "<#TITEL>", TAG_TITEL }, { "<#KEY>", TAG_KEY }, { "<#INDEX>", TAG_INDEX }, { "<#REFSTART>", TAG_REFSTART }, { "<#GRAPHIC>", TAG_GRAPHIC }, { "<#NEXTVERSION>", TAG_NEXTVERSION }, // { "<#GROUP_SYSSWITCH>", TAG_GROUP_SYSSWITCH }, { "<#WIN>", TAG_WIN }, { "<#UNIX>", TAG_UNIX }, { "<#MAC>", TAG_MAC }, { "<#OS2>", TAG_OS2 }, // { "<#GROUP_PROGSWITCH>", TAG_GROUP_PROGSWITCH }, { "<#WRITER>", TAG_WRITER }, { "<#CALC>", TAG_CALC }, { "<#DRAW>", TAG_DRAW }, { "<#IMPRESS>", TAG_IMPRESS }, { "<#SCHEDULE>", TAG_SCHEDULE }, { "<#IMAGE>", TAG_IMAGE }, { "<#MATH>", TAG_MATH }, { "<#CHART>", TAG_CHART }, { "<#OFFICE>", TAG_OFFICE }, */ // { "<#TAG_GROUP_META>", TAG_GROUP_META }, { "$[officefullname]", TAG_OFFICEFULLNAME }, { "$[officename]", TAG_OFFICENAME }, { "$[officepath]", TAG_OFFICEPATH }, { "$[officeversion]", TAG_OFFICEVERSION }, { "$[portalname]", TAG_PORTALNAME }, { "$[portalfullname]", TAG_PORTALFULLNAME }, { "$[portalpath]", TAG_PORTALPATH }, { "$[portalversion]", TAG_PORTALVERSION }, { "$[portalshortname]", TAG_PORTALSHORTNAME }, /* commenting oldstyle tags // { "<#TAG_GROUP_SINGLE>", TAG_GROUP_SINGLE }, { "<#REFINSERT>", TAG_REFINSERT }, // { "<#GROUP_MULTI>", TAG_GROUP_MULTI }, { "<#END>", TAG_END }, { "<#ELSE>", TAG_ELSE }, { "<#VERSIONEND>", TAG_VERSIONEND }, { "<#ENDGRAPHIC>", TAG_ENDGRAPHIC },*/ { "", TAG_COMMONSTART }, { "", TAG_COMMONEND }, { "", TAG_NOMORETAGS }, { "", TAG_UNKNOWN_TAG }, }; SimpleParser::SimpleParser() : nPos( 0 ) , aNextTag( TAG_NOMORETAGS, TOK_INVALIDPOS ) { } void SimpleParser::Parse( rtl::OUString const & PaSource ) { aSource = PaSource; nPos = 0; aLastToken = rtl::OUString(); aNextTag = TokenInfo( TAG_NOMORETAGS, TOK_INVALIDPOS ); aTokenList.clear(); }; TokenInfo SimpleParser::GetNextToken( ParserMessageList &rErrorList ) { TokenInfo aResult; sal_Int32 nTokenStartPos = 0; if ( aNextTag.nId != TAG_NOMORETAGS ) { aResult = aNextTag; aNextTag = TokenInfo( TAG_NOMORETAGS, TOK_INVALIDPOS ); } else { aLastToken = GetNextTokenString( rErrorList, nTokenStartPos ); if ( aLastToken.isEmpty() ) return TokenInfo( TAG_NOMORETAGS, TOK_INVALIDPOS ); // do we have a \< ... \> style tag? if (aLastToken.matchAsciiL(RTL_CONSTASCII_STRINGPARAM("\\<"))) { // check for paired \" \" bool bEven = true; sal_Int32 nQuotePos = 0; sal_Int32 nQuotedQuotesPos = aLastToken.indexOfAsciiL(RTL_CONSTASCII_STRINGPARAM("\\\"")); sal_Int32 nQuotedBackPos = aLastToken.indexOfAsciiL( RTL_CONSTASCII_STRINGPARAM("\\\\")); // this is only to kick out quoted backslashes while (nQuotedQuotesPos != -1) { if ( nQuotedBackPos <= nQuotedQuotesPos ) nQuotePos = nQuotedBackPos+2; else { nQuotePos = nQuotedQuotesPos+2; bEven = !bEven; } nQuotedQuotesPos = aLastToken.indexOfAsciiL( RTL_CONSTASCII_STRINGPARAM("\\\""), nQuotePos); nQuotedBackPos = aLastToken.indexOfAsciiL( RTL_CONSTASCII_STRINGPARAM("\\\\"), nQuotePos); // this is only to kick out quoted backslashes } if ( !bEven ) { rErrorList.AddError( 24, "Missing quotes ( \\\" ) in Tag", TokenInfo( TAG_UNKNOWN_TAG, nTokenStartPos, aLastToken ) ); } // check if we have an end-tag or a start-tag sal_Int32 nNonBlankStartPos = 2; while (aLastToken[nNonBlankStartPos] == ' ') nNonBlankStartPos++; if (aLastToken[nNonBlankStartPos] == '/') aResult = TokenInfo( TAG_COMMONEND, nTokenStartPos, aLastToken, rErrorList ); else { aResult = TokenInfo( TAG_COMMONSTART, nTokenStartPos, aLastToken, rErrorList ); sal_Int32 nNonBlankEndPos = aLastToken.getLength() - 3; while (aLastToken[nNonBlankEndPos] == ' ') nNonBlankEndPos--; if (aLastToken[nNonBlankEndPos] == '/') aNextTag = TokenInfo( TAG_COMMONEND, nTokenStartPos, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("\\")), rErrorList ); } } else { sal_Int32 i = 0; while ( aKnownTags[i].nTag != TAG_UNKNOWN_TAG && aLastToken != aKnownTags[i].GetName() ) i++; aResult = TokenInfo( aKnownTags[i].nTag, nTokenStartPos ); } } if ( aResult.nId == TAG_UNKNOWN_TAG ) aResult = TokenInfo( TAG_UNKNOWN_TAG, nTokenStartPos, aLastToken ); aTokenList.insert( aResult ); return aResult; } rtl::OUString SimpleParser::GetNextTokenString( ParserMessageList &rErrorList, sal_Int32 &rTagStartPos ) { sal_Int32 nStyle2StartPos = aSource.indexOfAsciiL( RTL_CONSTASCII_STRINGPARAM("$["), nPos ); sal_Int32 nStyle3StartPos = aSource.indexOfAsciiL( RTL_CONSTASCII_STRINGPARAM("\\<"), nPos); sal_Int32 nStyle4StartPos = aSource.indexOfAsciiL( RTL_CONSTASCII_STRINGPARAM("\\\\"), nPos); // this is only to kick out quoted backslashes rTagStartPos = 0; if (nStyle2StartPos == -1 && nStyle3StartPos == -1) return rtl::OUString(); // no more tokens if ( nStyle4StartPos < nStyle2StartPos && nStyle4StartPos <= nStyle3StartPos ) // <= to make sure \\ is always handled first { // Skip quoted Backslash nPos = nStyle4StartPos +2; return GetNextTokenString( rErrorList, rTagStartPos ); } if ( nStyle2StartPos < nStyle3StartPos ) { // test for $[ ... ] style tokens sal_Int32 nEndPos = aSource.indexOf(']', nStyle2StartPos); if (nEndPos == -1) { // Token is incomplete. Skip start and search for better ones nPos = nStyle2StartPos +2; return GetNextTokenString( rErrorList, rTagStartPos ); } nPos = nEndPos; rTagStartPos = nStyle2StartPos; return aSource.copy(nStyle2StartPos, nEndPos - nStyle2StartPos + 1); } else { // test for \< ... \> style tokens sal_Int32 nEndPos = aSource.indexOfAsciiL( RTL_CONSTASCII_STRINGPARAM("\\>"), nStyle3StartPos); sal_Int32 nQuotedBackPos = aSource.indexOfAsciiL( RTL_CONSTASCII_STRINGPARAM("\\\\"), nStyle3StartPos); // this is only to kick out quoted backslashes while (nQuotedBackPos <= nEndPos && nQuotedBackPos != -1) { nEndPos = aSource.indexOfAsciiL( RTL_CONSTASCII_STRINGPARAM("\\>"), nQuotedBackPos + 2); nQuotedBackPos = aSource.indexOfAsciiL( RTL_CONSTASCII_STRINGPARAM("\\\\"), nQuotedBackPos + 2); // this is only to kick out quoted backslashes } if (nEndPos == -1) { // Token is incomplete. Skip start and search for better ones nPos = nStyle3StartPos +2; rErrorList.AddError( 24, "Tag Start '\\<' without Tag End '\\>'", TokenInfo( TAG_UNKNOWN_TAG, nStyle3StartPos, aSource.copy(nStyle3StartPos - 10, 20) ) ); return GetNextTokenString( rErrorList, rTagStartPos ); } // check for paired quoted " --> \"sometext\" nPos = nEndPos; rTagStartPos = nStyle3StartPos; return aSource.copy(nStyle3StartPos, nEndPos-nStyle3StartPos + 2); } } rtl::OUString SimpleParser::GetLexem( TokenInfo const &aToken ) { if ( !aToken.aTokenString.isEmpty() ) return aToken.aTokenString; else { sal_Int32 i = 0; while ( aKnownTags[i].nTag != TAG_UNKNOWN_TAG && aKnownTags[i].nTag != aToken.nId ) i++; return aKnownTags[i].GetName(); } } TokenParser::TokenParser() : pErrorList( NULL ) {} void TokenParser::Parse( const rtl::OUString &aCode, ParserMessageList* pList ) { pErrorList = pList; //Scanner initialisieren aParser.Parse( aCode ); //erstes Symbol holen aTag = aParser.GetNextToken( *pErrorList ); nPfCaseOptions = 0; nAppCaseOptions = 0; bPfCaseActive = sal_False; bAppCaseActive = sal_False; nActiveRefTypes = 0; //Ausfuehren der Start-Produktion Paragraph(); //Es wurde nicht die ganze Kette abgearbeitet, bisher ist aber //kein Fehler aufgetreten //=> es wurde ein einleitendes Tag vergessen if ( aTag.nId != TAG_NOMORETAGS ) { switch ( aTag.nId ) { case TAG_END: { ParseError( 3, "Extra Tag <#END>. Switch or <#HREF> expected.", aTag ); } break; case TAG_BOLDOFF: { ParseError( 4, "<#BOLD> expected before <#/BOLD>.", aTag ); } break; case TAG_ITALICOFF: { ParseError( 5, "<#ITALIC> expected before <#/ITALIC>.", aTag ); } break; case TAG_UNDERLINEOFF: { ParseError( 17, "<#UNDER> expected before <#/UNDER>.", aTag ); } break; case TAG_AEND: { ParseError( 5, "Extra Tag <#AEND>. <#AVIS> or <#AHID> expected.", aTag ); } break; case TAG_ELSE: { ParseError( 16, "Application-tag or platform-tag expected before <#ELSE>.", aTag ); } break; case TAG_UNKNOWN_TAG: { ParseError( 6, "unknown Tag", aTag ); } break; default: { ParseError( 6, "unexpected Tag", aTag ); } } } pErrorList = NULL; } void TokenParser::Paragraph() { switch ( aTag.nId ) { case TAG_GRAPHIC: case TAG_NEXTVERSION: { TagRef(); Paragraph(); } break; case TAG_AVIS: case TAG_AHID: { TagRef(); Paragraph(); } break; case TAG_HELPID: { SimpleTag(); Paragraph(); } break; case TAG_OFFICEFULLNAME: case TAG_OFFICENAME: case TAG_OFFICEPATH: case TAG_OFFICEVERSION: case TAG_PORTALNAME: case TAG_PORTALFULLNAME: case TAG_PORTALPATH: case TAG_PORTALVERSION: case TAG_PORTALSHORTNAME: { SimpleTag(); Paragraph(); } break; case TAG_REFINSERT: { SimpleTag(); Paragraph(); } break; case TAG_BOLDON: case TAG_ITALICON: case TAG_UNDERLINEON: case TAG_COMMONSTART: { TagPair(); Paragraph(); } break; case TAG_HREF: case TAG_NAME: case TAG_KEY: case TAG_INDEX: case TAG_TITEL: case TAG_REFSTART: { TagRef(); Paragraph(); } break; case TAG_WIN: case TAG_UNIX: case TAG_MAC: //... { if ( ! bPfCaseActive ) { //PfCases duerfen nicht verschachtelt sein: bPfCaseActive = sal_True; PfCase(); //So jetzt kann wieder ein PfCase kommen: bPfCaseActive = sal_False; Paragraph(); } } break; case TAG_WRITER: case TAG_CALC: case TAG_DRAW: case TAG_IMPRESS: case TAG_SCHEDULE: case TAG_IMAGE: case TAG_MATH: case TAG_CHART: case TAG_OFFICE: { if ( !bAppCaseActive ) { //AppCases duerfen nicht verschachtelt sein: bAppCaseActive = sal_True; AppCase(); //jetzt koennen wieder AppCases kommen: bAppCaseActive = sal_False; Paragraph(); } } break; //Case TAG_BOLDOFF, TAG_ITALICOFF, TAG_BUNDERLINE, TAG_END //nichts tun wg. epsilon-Prod. } } void TokenParser::PfCase() { //Produktion: //PfCase -> PfCaseBegin Paragraph (PfCase | PfCaseEnd) PfCaseBegin(); //Jetzt ist eine PfCase-Produktion aktiv: Paragraph(); switch ( aTag.nId ) { case TAG_ELSE: case TAG_END: { CaseEnd(); } break; case TAG_WIN: case TAG_UNIX: case TAG_MAC: //First (PfBegin) { PfCase(); } break; default: ParseError( 8, "<#ELSE> or <#END> or platform-tag expected.", aTag ); } //Die gemerkten Tags wieder loeschen fuer naechstes PfCase: nPfCaseOptions = 0; } void TokenParser::PfCaseBegin() { switch ( aTag.nId ) { case TAG_WIN: case TAG_UNIX: case TAG_MAC: { //Token darf noch nicht vorgekommen sein im //aktuellen Plattform-Case: if ( !HAS_FLAG( nPfCaseOptions, TAG_NOGROUP( aTag.nId ) ) ) { SET_FLAG( nPfCaseOptions, TAG_NOGROUP( aTag.nId ) ); match( aTag, aTag ); } else { ParseError( 9, "Tag defined twice in the same platform-case", aTag ); } } } } void TokenParser::AppCase() { //Produktion: //AppCase -> AppCaseBegin Paragraph (AppCase | AppCaseEnd) AppCaseBegin(); Paragraph(); switch ( aTag.nId ) { case TAG_ELSE: case TAG_END: { CaseEnd(); } break; case TAG_WRITER: case TAG_DRAW: case TAG_CALC: case TAG_IMAGE: case TAG_MATH: case TAG_CHART: case TAG_OFFICE: case TAG_IMPRESS: case TAG_SCHEDULE: //First (AppBegin) { AppCase(); } break; default: ParseError( 1, "<#ELSE> or <#END> or application-case-tag expected.", aTag ); } //Die gemerkten Tags wieder loeschen fuer naechstes AppCase: nAppCaseOptions = 0; } void TokenParser::AppCaseBegin() { switch ( aTag.nId ) { case TAG_WRITER: case TAG_DRAW: case TAG_CALC: case TAG_IMAGE: case TAG_MATH: case TAG_CHART: case TAG_OFFICE: case TAG_IMPRESS: case TAG_SCHEDULE: { //Token darf noch nicht vorgekommen sein im //aktuellen Plattform-Case: if ( !HAS_FLAG( nAppCaseOptions, TAG_NOGROUP( aTag.nId ) ) ) { SET_FLAG( nAppCaseOptions, TAG_NOGROUP( aTag.nId ) ); match( aTag, aTag ); } else { ParseError( 13, "Tag defined twice in the same application-case.", aTag ); } } } } void TokenParser::CaseEnd() { //Produktion: //CaseEnd -> <#ELSE> Paragraph <#END> | <#END> switch ( aTag.nId ) { case TAG_ELSE: { match( aTag, TAG_ELSE ); Paragraph(); match( aTag, TAG_END ); } break; case TAG_END: { match( aTag, TAG_END ); } break; default: ParseError( 2, "<#ELSE> or <#END> expected.", aTag ); } } void TokenParser::SimpleTag() { switch ( aTag.nId ) { case TAG_HELPID: { match( aTag, TAG_HELPID ); } break; case TAG_OFFICEFULLNAME: case TAG_OFFICENAME: case TAG_OFFICEPATH: case TAG_OFFICEVERSION: case TAG_PORTALNAME: case TAG_PORTALFULLNAME: case TAG_PORTALPATH: case TAG_PORTALVERSION: case TAG_PORTALSHORTNAME: case TAG_REFINSERT: { match( aTag, aTag ); } break; default: ParseError( 15, "[<#SimpleTag>] expected.", aTag ); } } void TokenParser::TagPair() { switch ( aTag.nId ) { case TAG_BOLDON: { match( aTag, TAG_BOLDON ); Paragraph(); match( aTag, TAG_BOLDOFF ); } break; case TAG_ITALICON: { match( aTag, TAG_ITALICON ); Paragraph(); match( aTag, TAG_ITALICOFF ); } break; case TAG_UNDERLINEON: { match( aTag, TAG_UNDERLINEON ); Paragraph(); match( aTag, TAG_UNDERLINEOFF ); } break; case TAG_COMMONSTART: { //remember tag so we can give the original tag in case of an error TokenInfo aEndTag( aTag ); aEndTag.nId = TAG_COMMONEND; match( aTag, TAG_COMMONSTART ); Paragraph(); match( aTag, aEndTag ); } break; default: ParseError( 10, "<#BOLD>, <#ITALIC>, <#UNDER> expected.", aTag ); } } void TokenParser::TagRef() { switch ( aTag.nId ) { case TAG_GRAPHIC: case TAG_NEXTVERSION: { if ( !HAS_FLAG( nActiveRefTypes, TAG_NOGROUP( aTag.nId ) ) ) { TokenId aThisToken = aTag.nId; SET_FLAG( nActiveRefTypes, TAG_NOGROUP( aThisToken ) ); match( aTag, aTag ); Paragraph(); if ( aThisToken == TAG_GRAPHIC ) match( aTag, TAG_ENDGRAPHIC ); else match( aTag, TAG_VERSIONEND ); // don't reset since alowed only once per paragraph // RESET_FLAG( nActiveRefTypes, TAG_NOGROUP( aThisToken ) ); } else { ParseError( 11, "Tags <#GRAPHIC>,<#NEXTVERSION> allowed only once per paragraph at", aTag ); } } break; case TAG_AVIS: case TAG_AHID: { if ( !HAS_FLAG( nActiveRefTypes, TAG_NOGROUP( aTag.nId ) ) ) { TokenId aThisToken = aTag.nId; SET_FLAG( nActiveRefTypes, TAG_NOGROUP( aThisToken ) ); match( aTag, aTag ); Paragraph(); match( aTag, TAG_AEND ); RESET_FLAG( nActiveRefTypes, TAG_NOGROUP( aThisToken ) ); } else { ParseError( 11, "Nested <#AHID>,<#AVIS> not allowed.", aTag ); } } break; case TAG_HREF: case TAG_NAME: { } // NOBREAK case TAG_KEY: case TAG_INDEX: case TAG_TITEL: case TAG_REFSTART: { if ( !HAS_FLAG( nActiveRefTypes, TAG_NOGROUP( aTag.nId ) ) ) { TokenId aThisToken = aTag.nId; match( aTag, aTag ); if ( aThisToken != TAG_NAME ) { // TAG_NAME has no TAG_END SET_FLAG( nActiveRefTypes, TAG_NOGROUP( aThisToken ) ); Paragraph(); match( aTag, TAG_END ); RESET_FLAG( nActiveRefTypes, TAG_NOGROUP( aThisToken ) ); } } else { ParseError( 11, "Nested <#HREF>,<#NAME> or <#KEY> not allowed.", aTag ); } } break; default: ParseError( 12, "<#HREF>,<#NAME> or <#KEY> expected.", aTag ); } } sal_Bool TokenParser::match( const TokenInfo &aCurrentToken, const TokenId &aExpectedToken ) { return match( aCurrentToken, TokenInfo( aExpectedToken, TOK_INVALIDPOS ) ); } sal_Bool TokenParser::match( const TokenInfo &aCurrentToken, const TokenInfo &rExpectedToken ) { TokenInfo aExpectedToken( rExpectedToken ); if ( aCurrentToken.nId == aExpectedToken.nId ) { if ( ( aCurrentToken.nId == TAG_COMMONEND && aCurrentToken.GetTagName() == aExpectedToken.GetTagName() ) || aCurrentToken.nId != TAG_COMMONEND ) { aTag = aParser.GetNextToken( *pErrorList ); return sal_True; } } if ( aExpectedToken.nId == TAG_COMMONEND ) { aExpectedToken.aTokenString = rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Close tag for ")) + aExpectedToken.aTokenString; } rtl::OString sTmp(RTL_CONSTASCII_STRINGPARAM("Expected Symbol")); if ( aCurrentToken.nId == TAG_NOMORETAGS ) { ParseError( 7, sTmp, aExpectedToken ); } else { rtl::OStringBuffer aBuf(sTmp); aBuf.append(": "). append(rtl::OUStringToOString(aParser.GetLexem( aExpectedToken ), RTL_TEXTENCODING_UTF8)). append(RTL_CONSTASCII_STRINGPARAM(" near ")); ParseError( 7, aBuf.makeStringAndClear(), aCurrentToken ); } return sal_False; } void TokenParser::ParseError( sal_Int32 nErrNr, const rtl::OString &rErrMsg, const TokenInfo &rTag ) { pErrorList->AddError( nErrNr, rErrMsg, rTag); // Das Fehlerhafte Tag ueberspringen aTag = aParser.GetNextToken( *pErrorList ); } ParserMessage::ParserMessage( sal_Int32 PnErrorNr, const rtl::OString &rPaErrorText, const TokenInfo &rTag ) : nErrorNr( PnErrorNr ) , nTagBegin( 0 ) , nTagLength( 0 ) { rtl::OUString aLexem( SimpleParser::GetLexem( rTag ) ); rtl::OStringBuffer aErrorBuffer(rPaErrorText); aErrorBuffer.append(RTL_CONSTASCII_STRINGPARAM(": ")); aErrorBuffer.append(rtl::OUStringToOString(aLexem, RTL_TEXTENCODING_UTF8)); if ( rTag.nId == TAG_NOMORETAGS ) aErrorBuffer.append(RTL_CONSTASCII_STRINGPARAM(" at end of line ")); else if ( rTag.nPos != TOK_INVALIDPOS ) { aErrorBuffer.append(RTL_CONSTASCII_STRINGPARAM(" at Position ")); aErrorBuffer.append(static_cast(rTag.nPos)); } aErrorText = aErrorBuffer.makeStringAndClear(); nTagBegin = rTag.nPos; nTagLength = aLexem.getLength(); } ParserError::ParserError( sal_Int32 ErrorNr, const rtl::OString &rErrorText, const TokenInfo &rTag ) : ParserMessage( ErrorNr, rErrorText, rTag ) {} ParserWarning::ParserWarning( sal_Int32 ErrorNr, const rtl::OString &rErrorText, const TokenInfo &rTag ) : ParserMessage( ErrorNr, rErrorText, rTag ) {} sal_Bool LingTest::IsTagMandatory( TokenInfo const &aToken, TokenId &aMetaTokens ) { TokenId aTokenId = aToken.nId; TokenId aTokenGroup = TAG_GROUP( aTokenId ); if ( TAG_GROUP_PROGSWITCH == aTokenGroup || TAG_REFINSERT == aTokenId || TAG_REFSTART == aTokenId || TAG_NAME == aTokenId || TAG_HREF == aTokenId || TAG_AVIS == aTokenId || TAG_AHID == aTokenId || TAG_GRAPHIC == aTokenId || TAG_NEXTVERSION == aTokenId || ( TAG_GROUP_META == aTokenGroup && (aMetaTokens & aTokenId) == aTokenId ) ) { if ( TAG_GROUP_META == aTokenGroup ) aMetaTokens |= aTokenId; return sal_True; } else if ( TAG_COMMONSTART == aTokenId || TAG_COMMONEND == aTokenId ) { rtl::OUString aTagName = aToken.GetTagName(); return !(aTagName.equalsIgnoreAsciiCaseAscii( "comment" ) || aTagName.equalsIgnoreAsciiCaseAscii( "bookmark_value" ) || aTagName.equalsIgnoreAsciiCaseAscii( "emph" ) || aTagName.equalsIgnoreAsciiCaseAscii( "item" ) || aTagName.equalsIgnoreAsciiCaseAscii( "br" ) ); } return sal_False; } void LingTest::CheckTags( TokenList &aReference, TokenList &aTestee, sal_Bool bFixTags ) { size_t i=0,j=0; // Clean old Warnings aCompareWarningList.clear(); /* in xml tags, do not require the following tags comment bookmark_value emph item br */ // filter uninteresting Tags TokenId aMetaTokens = 0; for ( i=0 ; i < aReference.size() ; i++ ) { if ( !IsTagMandatory( aReference[ i ], aMetaTokens ) ) aReference[ i ].SetDone(); } aMetaTokens = 0; for ( i=0 ; i < aTestee.size() ; i++ ) { if ( !IsTagMandatory( aTestee[ i ], aMetaTokens ) ) aTestee[ i ].SetDone(); } // remove all matching tags for ( i=0 ; i < aReference.size() ; i++ ) { if ( aReference[ i ].IsDone() ) continue; sal_Bool bTagFound = sal_False; for ( j=0 ; j < aTestee.size() && !bTagFound ; j++ ) { if ( aTestee[ j ].IsDone() ) continue; if ( aReference[ i ].MatchesTranslation( aTestee[ j ], sal_False, aCompareWarningList ) ) { aReference[ i ].SetDone(); aTestee[ j ].SetDone(); bTagFound = sal_True; } } } sal_Bool bCanFix = sal_True; if ( bFixTags ) { // we fix only if its a really simple case sal_Int32 nTagCount = 0; for ( i=0 ; i < aReference.size() ; i++ ) if ( !aReference[ i ].IsDone() ) nTagCount++; if ( nTagCount > 1 ) bCanFix = sal_False; nTagCount = 0; for ( i=0 ; i < aTestee.size() ; i++ ) if ( !aTestee[ i ].IsDone() ) nTagCount++; if ( nTagCount > 1 ) bCanFix = sal_False; } // generate errors for tags that have differing attributes for ( i=0 ; i < aReference.size() ; i++ ) { if ( aReference[ i ].IsDone() ) continue; sal_Bool bTagFound = sal_False; for ( j=0 ; j < aTestee.size() && !bTagFound ; j++ ) { if ( aTestee[ j ].IsDone() ) continue; if ( aReference[ i ].MatchesTranslation( aTestee[ j ], sal_True, aCompareWarningList, bCanFix && bFixTags ) ) { aReference[ i ].SetDone(); aTestee[ j ].SetDone(); bTagFound = sal_True; } } } // list remaining tags as errors for ( i=0 ; i < aReference.size() ; i++ ) { if ( aReference[ i ].IsDone() ) continue; aCompareWarningList.AddError( 20, "Missing Tag in Translation", aReference[ i ] ); } for ( i=0 ; i < aTestee.size() ; i++ ) { if ( aTestee[ i ].IsDone() ) continue; aCompareWarningList.AddError( 21, "Extra Tag in Translation", aTestee[ i ] ); } for ( i=0 ; i < aReference.size() ; i++ ) aReference[ i ].SetDone( sal_False ); for ( i=0 ; i < aTestee.size() ; i++ ) aTestee[ i ].SetDone( sal_False ); } void LingTest::CheckReference( GSILine *aReference ) { aReferenceParser.Parse( aReference->GetUText(), aReference->GetMessageList() ); } void LingTest::CheckTestee( GSILine *aTestee, sal_Bool bHasSourceLine, sal_Bool bFixTags ) { aFixedTestee = aTestee->GetUText(); aTesteeParser.Parse( aFixedTestee, aTestee->GetMessageList() ); if ( bHasSourceLine ) CheckTags( aReferenceParser.GetTokenList(), aTesteeParser.GetTokenList(), bFixTags ); if ( bFixTags ) { TokenList& aTesteeTokens = aTesteeParser.GetTokenList(); sal_Bool bFixesDone = sal_False; // count backwards to allow replacing from right to left int i; for ( i = aTesteeTokens.size() ; i > 0 ; ) { if ( aTesteeTokens[ --i ].HasBeenFixed() ) { bFixesDone = sal_True; aFixedTestee = aFixedTestee.replaceAt( aTesteeTokens[ i ].nPos, aTesteeTokens[ i ].aTokenString.getLength(), aTesteeTokens[ i ].MakeTag() ); } } if ( bFixesDone ) { aTestee->SetUText( aFixedTestee ); aTestee->SetFixed(); } } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */