messagingappbase/smilengine/engine/checker/ConformanceChecker.cpp
changeset 0 72b543305e3a
equal deleted inserted replaced
-1:000000000000 0:72b543305e3a
       
     1 /*
       
     2 * Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:  ConformanceChecker implementation
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 
       
    20 
       
    21 // INCLUDE FILES
       
    22 #include "ConformanceChecker.h"
       
    23 
       
    24 #include <gmxmldocument.h>
       
    25 #include <gmxmlelement.h>
       
    26 
       
    27 #include "smilliterals.h"
       
    28 
       
    29 // ============================ MEMBER FUNCTIONS ===============================
       
    30 
       
    31 
       
    32 // -----------------------------------------------------------------------------
       
    33 // CConformanceChecker::NewL
       
    34 // Two-phased constructor.
       
    35 // -----------------------------------------------------------------------------
       
    36 //
       
    37 EXPORT_C
       
    38 CConformanceChecker* CConformanceChecker::NewL()
       
    39     {
       
    40     CConformanceChecker* self = new( ELeave ) CConformanceChecker;
       
    41     return self;
       
    42     }
       
    43 
       
    44     
       
    45 // Destructor
       
    46 CConformanceChecker::~CConformanceChecker()
       
    47     {   
       
    48     }
       
    49 
       
    50 
       
    51 //
       
    52 // ----------------------------------------------------------
       
    53 // CConformanceChecker::Check(CMDXMLDocument* aXmldoc)
       
    54 //
       
    55 // Checks if the DOM tree's content is legacy content or not.
       
    56 // ----------------------------------------------------------
       
    57 //
       
    58 EXPORT_C 
       
    59 TBool CConformanceChecker::Check(CMDXMLDocument* aXmldoc, TMmsSmilVersion aVersion, TInt aFlags)
       
    60 	{
       
    61 	//Initializing the flags according to the CF document version
       
    62 	switch ( aVersion )
       
    63 		{
       
    64 		case EMmsSmil_v2_0:
       
    65 			iFlags = 0;
       
    66 			break;
       
    67 		case EMmsSmil_v3_0: //Not implemented yet
       
    68 			return EFalse;
       
    69 		default:
       
    70 			iFlags = 0;
       
    71 		}
       
    72 	//Combine the implicit flags  with the explicitly specified flags
       
    73 	iFlags=iFlags | aFlags;
       
    74 
       
    75 	if (!aXmldoc) return EFalse;
       
    76 
       
    77 	CMDXMLElement* root = aXmldoc->DocumentElement();	
       
    78 	if (!root) return EFalse;
       
    79 
       
    80 	CMDXMLNode* smil = NULL;
       
    81 	CMDXMLNode* head = NULL;
       
    82 	CMDXMLNode* body = NULL;
       
    83 
       
    84 	CMDXMLNode* node = root->FirstChild();	
       
    85 	while (node && node->NodeName() != KSmilTag) 
       
    86 		{
       
    87 		node=node->NextSibling();
       
    88 		}
       
    89 
       
    90 	if ( node ) smil = node;
       
    91 	else return EFalse;
       
    92 
       
    93 	node = smil->FirstChild();
       
    94 	while (node && node->NodeType() != CMDXMLNode::EElementNode)
       
    95 		{
       
    96 		node = node->NextSibling();
       
    97 		}
       
    98 
       
    99 	if (node && node->NodeName() == KHeadTag)
       
   100 		{
       
   101 		head = node;
       
   102 		if (!CheckHeadStructure(head))
       
   103 			return EFalse;	
       
   104 		//Going forward in the DOM to find next element
       
   105 		node = head->NextSibling();
       
   106 		while (node && node->NodeType() != CMDXMLNode::EElementNode)
       
   107 			{
       
   108 			node = node->NextSibling();
       
   109 			}
       
   110 		}
       
   111 	if (node && node->NodeName() == KBodyTag)
       
   112 		{
       
   113 		body = node;
       
   114 		if (!CheckBodyStructure(body))
       
   115 			return EFalse;
       
   116 		}
       
   117 	else if ( node ) return EFalse; //there is some other element (not head or body in the smil element)
       
   118 	//Checking the head content
       
   119 	if ( head ) 
       
   120 		{
       
   121 		node = head->FirstChild();
       
   122 		}
       
   123 	else 
       
   124 		{
       
   125 		node = NULL;
       
   126 		}
       
   127 
       
   128 	while (node && node->NodeName() != KLayoutTag)
       
   129 		{
       
   130 		node = node->NextSibling();
       
   131 		} 
       
   132 	//Checking the content of the layout
       
   133 	if (!CheckLayoutContent(node))
       
   134 		return EFalse;	
       
   135 	//Checking the body content
       
   136 	if (!CheckBodyContent(body))
       
   137 		return EFalse;
       
   138 
       
   139 	return ETrue;
       
   140 	} //End of Check()
       
   141 
       
   142 //
       
   143 // ----------------------------------------------------------
       
   144 // CConformanceChecker::CheckHeadStructure(CMDXMLNode* aHead)
       
   145 //
       
   146 // Checks the structure of the head element: allowed to contain only layout and meta element
       
   147 // ----------------------------------------------------------
       
   148 //
       
   149 
       
   150 TBool CConformanceChecker::CheckHeadStructure(CMDXMLNode* aHead)
       
   151 	{
       
   152 	//Checking that the head has only layout and meta element
       
   153 	if (!aHead) 
       
   154 		{
       
   155 		return ETrue;
       
   156 		}			
       
   157 	CMDXMLNode* node = aHead->FirstChild();
       
   158 	while (node) 
       
   159 		{
       
   160 		if (node->NodeType() == CMDXMLNode::EElementNode  
       
   161 			&& node->NodeName() != KLayoutTag 
       
   162 			&& node->NodeName() != KMetaTag)
       
   163 			{
       
   164 			return EFalse;
       
   165 			}
       
   166 		else 
       
   167 			{
       
   168 			node = node->NextSibling();
       
   169 			}
       
   170 		}
       
   171 	return ETrue;
       
   172 	}
       
   173 
       
   174 //
       
   175 // ----------------------------------------------------------
       
   176 // CConformanceChecker::CheckBodyStructure(CMDXMLNode* aBody)
       
   177 //
       
   178 // Checks the structure of the body element: allowed to contain only par elements
       
   179 //											(one including seq element right after the body is allowed)
       
   180 // ----------------------------------------------------------
       
   181 //
       
   182 TBool CConformanceChecker::CheckBodyStructure(CMDXMLNode* aBody)
       
   183 	{
       
   184 	//Checking that the body has only par elements
       
   185 	if (!aBody) 
       
   186 		{
       
   187 		return ETrue;
       
   188 		}
       
   189 	CMDXMLNode* node = aBody->FirstChild();	
       
   190 	while (node && node->NodeType() != CMDXMLNode::EElementNode)
       
   191 		{
       
   192 		node = node->NextSibling();
       
   193 		}
       
   194 
       
   195 	//One seq element right after the body is allowed if EAllowSeqTag is set
       
   196 	if ((iFlags & EAllowSeqTag)!=0 && node && node->NodeName() == KSeqTag) 
       
   197 		{
       
   198 		if (node->NextSibling())
       
   199 			{
       
   200 			return EFalse;
       
   201 			}
       
   202 		else
       
   203 			{
       
   204 			node = node->FirstChild();
       
   205 			}
       
   206 		}
       
   207 	while (node)
       
   208 		{
       
   209 		if (node->NodeType() == CMDXMLNode::EElementNode && node->NodeName() != KParTag)
       
   210 			{	
       
   211 			return EFalse;
       
   212 			}
       
   213 		else 
       
   214 			{
       
   215 			node = node->NextSibling();
       
   216 			}
       
   217 		}
       
   218 	return ETrue;
       
   219 	}
       
   220 
       
   221 //
       
   222 // ----------------------------------------------------------
       
   223 // CConformanceChecker::CheckLayoutContent(CMDXMLNode* aLayout)
       
   224 //
       
   225 // Checks the content of the layout element 
       
   226 //			- root-layout and max. 2 region elements are allowed
       
   227 //			- checks the width/height values for root-layout (pixel is allowed)
       
   228 //			- checks the width/height/top/left values for region 
       
   229 //			  if EAllowMixedRegionDimensions is not set no mixed pixel/percent allowed
       
   230 // ----------------------------------------------------------
       
   231 //
       
   232 
       
   233 TBool CConformanceChecker::CheckLayoutContent(CMDXMLNode* aLayout)
       
   234 	{
       
   235 	TInt rl_nr = 0;
       
   236 	TInt r_nr = 0;
       
   237 	if (!aLayout) 
       
   238 		{
       
   239 		return ETrue;
       
   240 		}
       
   241 	CMDXMLNode* node = aLayout->FirstChild();
       
   242 	TBool foundImage = EFalse;
       
   243 	TBool foundText = EFalse;
       
   244 	while (node)
       
   245 		{
       
   246 		if (node->NodeType() == CMDXMLNode::EElementNode)
       
   247 			{
       
   248 			if (node->NodeName() == KRootLayoutTag)
       
   249 				{
       
   250 				if (++rl_nr > 1) 
       
   251 					return EFalse;		
       
   252 				//Check the attributes
       
   253 				CMDXMLElement* elem = static_cast<CMDXMLElement*>(node);
       
   254 				TInt n = elem->NumAttributes();
       
   255 				for (TInt i = 0; i < n; ++i)
       
   256 					{	
       
   257 					TPtrC name, value;
       
   258 					elem->AttributeDetails(i,name,value);
       
   259 					if	( (iFlags & EAllowAllAttributes) == 0 &&
       
   260 						name != KWidthAttr && name != KHeightAttr )
       
   261 						{
       
   262 						return EFalse;
       
   263 						}
       
   264 					if (name == KWidthAttr || name == KHeightAttr) 
       
   265 						{
       
   266 						TBool pixel = EFalse;  // WB!
       
   267 						TBool percent = EFalse;
       
   268 						if (!CheckMixedRegionAttribute(value,percent,pixel))
       
   269 							{
       
   270 							return EFalse;
       
   271 							}
       
   272 						}
       
   273 					}	
       
   274 				}
       
   275 			else if (node->NodeName() == KRegionTag)
       
   276 				{
       
   277 				if (++r_nr > 2) 
       
   278 					{
       
   279 					return EFalse;
       
   280 					}		
       
   281 				//Check the attributes
       
   282 				TBool foundPixel = EFalse;
       
   283 				TBool foundPercent = EFalse;
       
   284 					
       
   285 				CMDXMLElement* elem = static_cast<CMDXMLElement*>(node);
       
   286 				TInt n = elem->NumAttributes();
       
   287 				for (TInt i = 0; i < n; ++i)
       
   288 					{
       
   289 					TPtrC name, value;
       
   290 					elem->AttributeDetails(i,name,value);
       
   291 						
       
   292 					if ((iFlags & EAllowAllAttributes) == 0 && name != KIdAttr 
       
   293 						&& name != KWidthAttr && name != KHeightAttr 
       
   294 						&& name != KFitAttr && name != KLeftAttr && name != KTopAttr)
       
   295 						{
       
   296 						return EFalse;
       
   297 						}
       
   298 					if ((iFlags & EAllowMixedRegionDimensions)==0 && 
       
   299 						(name == KWidthAttr || name == KHeightAttr 
       
   300 						|| name == KTopAttr || name == KLeftAttr) 
       
   301 						&& !CheckMixedRegionAttribute(value,foundPercent,foundPixel))
       
   302 						{			
       
   303 						return EFalse;
       
   304 						}
       
   305 					if ((iFlags & EAllowAnyRegionNames)==0 && name == KIdAttr
       
   306 						&& !CheckRegionNames(value,foundImage,foundText))
       
   307 						{	
       
   308 						return EFalse;
       
   309 						}
       
   310 
       
   311 					}//for
       
   312 				}
       
   313 			else 
       
   314 				{
       
   315 				return EFalse;
       
   316 				}
       
   317 
       
   318 			}
       
   319 		node = node->NextSibling();
       
   320 		}//while
       
   321 	return ETrue;
       
   322 	}
       
   323 
       
   324 //
       
   325 // ----------------------------------------------------------
       
   326 // CConformanceChecker::CheckMixedRegionAttribute 
       
   327 // Checks if the given value is ending with percent or pixel 
       
   328 // and if this is conforming to the values of aPixel, aPercent.
       
   329 // Returns true - if the value is not mixed
       
   330 //		   false - if mixed
       
   331 // ----------------------------------------------------------
       
   332 //
       
   333 TBool CConformanceChecker::CheckMixedRegionAttribute(TDesC& aValue, 
       
   334 													 TBool& aPercent, TBool& aPixel) const
       
   335 	{
       
   336 	TInt valueLength = aValue.Length();
       
   337 	TInt j = 0;
       
   338 	TBool success = ETrue;
       
   339 	while (j < valueLength && static_cast<TChar>(aValue[j]).IsDigit())
       
   340 		{
       
   341 		j++;
       
   342 		}
       
   343 	HBufC* ending = aValue.Right(valueLength-j).Alloc();
       
   344 	if (!ending)
       
   345 		{ 
       
   346 		success = EFalse;
       
   347 		}
       
   348 	else
       
   349 		{
       
   350 		TPtr endingPtr = ending -> Des();
       
   351 		endingPtr.LowerCase();
       
   352 		endingPtr.TrimRight();
       
   353 		if (endingPtr==KPercent)
       
   354 			{
       
   355 			aPercent = ETrue;
       
   356 			if (aPixel)
       
   357 				{
       
   358 				success = EFalse;
       
   359 				}
       
   360 			}
       
   361 		else if (endingPtr==KNullDesC || endingPtr==KPx)
       
   362 			{
       
   363 			aPixel = ETrue;
       
   364 			if (aPercent)
       
   365 				{
       
   366 				success = EFalse;
       
   367 				}
       
   368 			}
       
   369 		else 
       
   370 			success = EFalse; //Cannot be other then pixel or percent 
       
   371 		}
       
   372 	delete ending;
       
   373 	return success;
       
   374 	}
       
   375 
       
   376 
       
   377 //
       
   378 // ----------------------------------------------------------
       
   379 // CConformanceChecker::CheckRegionNames 
       
   380 // Checks if the given value is exactly "Image" or "Text" and if it 
       
   381 // is duplicated (checking the transmitted boolean parameters).
       
   382 // Returns true - if the value is "Image" or "Text" and it's not duplicated
       
   383 //		   false - otherwise
       
   384 // ----------------------------------------------------------
       
   385 //
       
   386 TBool CConformanceChecker::CheckRegionNames(TDesC& aValue, 
       
   387 											TBool& aFoundImage, TBool& aFoundText) const
       
   388 {
       
   389 	if (aValue == KImage)
       
   390 		{
       
   391 		if (aFoundImage)
       
   392 			{
       
   393 			return EFalse;
       
   394 			}
       
   395 		else
       
   396 			{
       
   397 			aFoundImage = ETrue;
       
   398 			}
       
   399 		}
       
   400 	else if (aValue == KText)
       
   401 		{
       
   402 		if (aFoundText)
       
   403 			{
       
   404 			return EFalse;
       
   405 			}
       
   406 		else
       
   407 			{
       
   408 			aFoundText = ETrue;
       
   409 			}
       
   410 		}
       
   411 	else 
       
   412 		{
       
   413 		return EFalse;
       
   414 		}
       
   415 	return ETrue;
       
   416 }
       
   417 
       
   418 //
       
   419 // ----------------------------------------------------------
       
   420 // CConformanceChecker::CheckBodyContent(CMDXMLNode* aBody)
       
   421 //
       
   422 // Checks the content of the body element 
       
   423 // ----------------------------------------------------------
       
   424 //
       
   425 TBool CConformanceChecker::CheckBodyContent(CMDXMLNode* aBody)
       
   426 	{
       
   427 	if (!aBody) 
       
   428 		{
       
   429 		return ETrue;
       
   430 		}
       
   431 	CMDXMLNode* node = aBody->FirstChild();	
       
   432 	//One seq element right after the body is allowed if EAllowSeqTag is set
       
   433 	if ((iFlags & EAllowSeqTag)!=0 && node && node->NodeName() == KSeqTag) 
       
   434 		{
       
   435 		node = node->FirstChild();
       
   436 		}
       
   437 	while (node)
       
   438 		{
       
   439 		if (node->NodeName() == KParTag)
       
   440 			{
       
   441 			//Checking the attributes of par element 
       
   442 			//only dur is allowed, dur='indefinite' illegal
       
   443 			//checking dur is in ms
       
   444 			CMDXMLElement* elem = static_cast<CMDXMLElement*>(node); 
       
   445 			if (elem->NumAttributes() > 1 && (iFlags & EAllowAllAttributes) == 0) 
       
   446 				{
       
   447 				return EFalse;
       
   448 				}
       
   449 			for (TInt i = 0; i < elem->NumAttributes(); ++i)
       
   450 				{
       
   451 				TPtrC name, value;
       
   452 				elem->AttributeDetails(i,name,value);
       
   453 
       
   454 				if (name == KDurAttr && value == KIndefiniteVal)
       
   455 					{
       
   456 					return EFalse;
       
   457 					}
       
   458 				if ((iFlags & EAllowAllAttributes) == 0 && (name != KDurAttr )) 
       
   459 					{
       
   460 					return EFalse;
       
   461 					}
       
   462 				if (name == KDurAttr && (iFlags & EAllowNonMilliseconds) == 0 
       
   463 					&& !IsInMilliseconds(value)) 
       
   464 					{
       
   465 					return EFalse;
       
   466 					}
       
   467 							
       
   468 				}				
       
   469 			if (!CheckParContent(node))
       
   470 				return EFalse;	
       
   471 			}
       
   472 		node = node->NextSibling();
       
   473 		}
       
   474 	return ETrue;
       
   475 	}
       
   476 
       
   477 
       
   478 //
       
   479 // ----------------------------------------------------------
       
   480 // CConformanceChecker::CheckParContent(CMDXMLNode* aPar)
       
   481 //
       
   482 // Checks the content of the par element
       
   483 // ----------------------------------------------------------
       
   484 //
       
   485 
       
   486 TBool CConformanceChecker::CheckParContent(CMDXMLNode* aPar)
       
   487 	{
       
   488 	if (!aPar) 
       
   489 		{
       
   490 		return ETrue;
       
   491 		}
       
   492 	TBool hasImage=EFalse;
       
   493 	TBool hasText=EFalse;
       
   494 	TBool hasAudio=EFalse;
       
   495 	TBool hasRef=EFalse;
       
   496 	TBool hasVideo=EFalse;
       
   497 
       
   498 	RArray<TPtrC> regionNames;	
       
   499 	CMDXMLNode* innernode = aPar->FirstChild();
       
   500 	TBool legacy = ETrue;
       
   501 	while (legacy && innernode)
       
   502 		{ 
       
   503 		if (innernode->NodeType() == CMDXMLNode::EElementNode)
       
   504 			{
       
   505 			//Checking one type of media per par element
       
   506 			if (innernode->NodeName() == KImageTag)
       
   507 				if (hasImage)
       
   508 					{
       
   509 					legacy = EFalse;
       
   510 					}
       
   511 				else 
       
   512 					{
       
   513 					hasImage = ETrue;
       
   514 					}
       
   515 			else if (innernode->NodeName() == KTextTag)
       
   516 				if (hasText)
       
   517 					{
       
   518 					legacy = EFalse;
       
   519 					}
       
   520 				else 
       
   521 					{
       
   522 					hasText = ETrue;
       
   523 					}
       
   524 			else if (innernode->NodeName() == KAudioTag)
       
   525 				if (hasAudio)
       
   526 					{
       
   527 					legacy = EFalse;
       
   528 					}
       
   529 				else 
       
   530 					{
       
   531 					hasAudio = ETrue;
       
   532 					}
       
   533 			else if (innernode->NodeName() == KRefTag)
       
   534 				if (hasRef)
       
   535 					{
       
   536 					legacy = EFalse;
       
   537 					}
       
   538 				else 
       
   539 					{
       
   540 					hasRef = ETrue;
       
   541 					}
       
   542 			else if (innernode->NodeName() == KVideoTag) 
       
   543 				if ((iFlags & EAllowVideoTag) == 0)
       
   544 					{
       
   545 					legacy = EFalse;
       
   546 					}
       
   547 				else
       
   548 					{
       
   549 					if (hasVideo)
       
   550 						{
       
   551 						legacy = EFalse;
       
   552 						}
       
   553 					else 
       
   554 						{
       
   555 						hasVideo=ETrue;
       
   556 						}
       
   557 					}
       
   558 			else legacy = EFalse; //No other elements are allowed then img,text, audio, ref, video  
       
   559 				//Check attributes
       
   560 			CMDXMLElement* elem = static_cast<CMDXMLElement*>(innernode);
       
   561 			TInt n = elem->NumAttributes();
       
   562 			for (TInt i = 0; legacy && (i < n); ++i)
       
   563 				{
       
   564 				TPtrC name, value;
       
   565 				elem->AttributeDetails(i,name,value);
       
   566 				//Check one region is used only once per par element
       
   567 				if (name == KRegionAttr)
       
   568 					{
       
   569 					if ((iFlags & EAllowAnyRegionNames) == 0 
       
   570 						&& value != KImage && value != KText)
       
   571 						{
       
   572 						legacy = EFalse;
       
   573 						}
       
   574 					TInt nr = regionNames.Count();
       
   575 					TInt j = 0;
       
   576 					while (j < nr && legacy)
       
   577 						{
       
   578 						if (regionNames[j]==value)
       
   579 							{
       
   580 							legacy = EFalse;
       
   581 							}
       
   582 						else
       
   583 							{
       
   584 							++j;
       
   585 							}
       
   586 						}
       
   587 					if (legacy) 
       
   588 						{
       
   589 						regionNames.Append(value);
       
   590 						if (regionNames.Count()>2) 
       
   591 							{
       
   592 							legacy = EFalse;
       
   593 							}
       
   594 						}
       
   595 								
       
   596 					}
       
   597 				else if ((iFlags & EAllowAllAttributes) == 0 && name != KSrcAttr 
       
   598 					&& name != KAltAttr && name != KBeginAttr && name != KEndAttr)
       
   599 					{
       
   600 					legacy = EFalse;
       
   601 					}
       
   602 					//checking the src attribute
       
   603 				if (name == KSrcAttr && (iFlags & EAllowNonUsAscii) == 0)
       
   604 					{
       
   605 					TInt valueLength=value.Length();
       
   606 					for (TInt i = 0; legacy && (i < valueLength); i++)
       
   607 						{
       
   608 						TInt v = value[i];
       
   609 						if (v < KUSAsciiMinCode || v > KUSAsciiMaxCode)
       
   610 							{
       
   611 							legacy = EFalse;
       
   612 							}
       
   613 						}
       
   614 					}
       
   615 				//checking begin and end in ms
       
   616 				if ((name == KBeginAttr || name == KEndAttr) 
       
   617 					&& (iFlags & EAllowNonMilliseconds) == 0 
       
   618 					&& !IsInMilliseconds(value))
       
   619 					{
       
   620 						legacy = EFalse;
       
   621 					}
       
   622 				}//for 
       
   623 			}
       
   624 		innernode = innernode->NextSibling();
       
   625 		} //while
       
   626     regionNames.Close();
       
   627 	return legacy;
       
   628 	}
       
   629 
       
   630 //
       
   631 // ----------------------------------------------------------
       
   632 // CConformanceChecker::IsInMilliseconds(TDesC aValue);
       
   633 //
       
   634 // Checks if  aValue is ending with 'ms'
       
   635 // ----------------------------------------------------------
       
   636 //
       
   637 
       
   638 TBool CConformanceChecker::IsInMilliseconds(TDesC& aValue) const
       
   639 	{
       
   640 	TBuf<2> ending;
       
   641 	TInt j = 0;
       
   642 	TInt n = aValue.Length();
       
   643 	TInt trimmedEndingLength = 2;
       
   644 	//Processing till all the white spaces are eliminated from the end
       
   645 	do 
       
   646 	{
       
   647 	j+=2-trimmedEndingLength;
       
   648 	if (n>=j+2)
       
   649 		ending = aValue.Mid(n-2-j,2);
       
   650 	ending.TrimRight();
       
   651 	trimmedEndingLength = ending.Length();
       
   652 	}
       
   653 	while (trimmedEndingLength!=2 && n>j+2);
       
   654 	ending.LowerCase();
       
   655 	if (ending != KMs) return EFalse;
       
   656 	return ETrue;
       
   657 	}
       
   658 
       
   659 //  End of File