Back to index

lightning-sunbird  0.9+nobinonly
nsMathMLOperators.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is Mozilla MathML Project.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * The University Of Queensland.
00018  * Portions created by the Initial Developer are Copyright (C) 1999
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Roger B. Sidje <rbs@maths.uq.edu.au>
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsCOMPtr.h"
00039 #include "nsString.h"
00040 #include "nsHashtable.h"
00041 #include "nsVoidArray.h"
00042 
00043 #include "nsIComponentManager.h"
00044 #include "nsIPersistentProperties2.h"
00045 #include "nsNetUtil.h"
00046 #include "nsCRT.h"
00047 
00048 #include "nsMathMLOperators.h"
00049 
00050 // operator dictionary entry
00051 struct OperatorData {
00052   OperatorData(void)
00053     : mFlags(0),
00054       mLeftSpace(0.0f),
00055       mRightSpace(0.0f)
00056   {
00057   }
00058 
00059   // member data
00060   nsString        mStr;
00061   nsOperatorFlags mFlags;
00062   float           mLeftSpace;   // unit is em
00063   float           mRightSpace;  // unit is em
00064 };
00065 
00066 /*
00067   The MathML REC says:
00068   "If the operator does not occur in the dictionary with the specified form,
00069   the renderer should use one of the forms which is available there, in the
00070   order of preference: infix, postfix, prefix."
00071 
00072   The following variable will be used to keep track of all possible forms
00073   encountered in the Operator Dictionary.
00074 */
00075 static OperatorData*   gOperatorFound[4];
00076 
00077 static PRInt32         gTableRefCount = 0;
00078 static PRInt32         gOperatorCount = 0;
00079 static OperatorData*   gOperatorArray = nsnull;
00080 static nsHashtable*    gOperatorTable = nsnull;
00081 static nsVoidArray*    gStretchyOperatorArray = nsnull;
00082 static nsStringArray*  gInvariantCharArray = nsnull;
00083 static PRBool          gInitialized   = PR_FALSE;
00084 
00085 static const PRUnichar kNullCh  = PRUnichar('\0');
00086 static const PRUnichar kDashCh  = PRUnichar('#');
00087 static const PRUnichar kEqualCh = PRUnichar('=');
00088 static const PRUnichar kColonCh = PRUnichar(':');
00089 
00090 static const char* const kMathVariant_name[] = {
00091   "normal",
00092   "bold",
00093   "italic",
00094   "bold-italic",
00095   "sans-serif",
00096   "bold-sans-serif",
00097   "sans-serif-italic",
00098   "sans-serif-bold-italic",
00099   "monospace",
00100   "script",
00101   "bold-script",
00102   "fraktur",
00103   "bold-fraktur",
00104   "double-struck"
00105 };
00106 
00107 void
00108 SetProperty(OperatorData* aOperatorData,
00109             nsString      aName,
00110             nsString      aValue)
00111 {
00112   if (!aName.Length() || !aValue.Length())
00113     return;
00114 
00115   // XXX These ones are not kept in the dictionary
00116   // Support for these requires nsString member variables 
00117   // maxsize (default: infinity)
00118   // minsize (default: 1)
00119 
00120   if (aValue.EqualsLiteral("true")) {
00121     // see if we should enable flags with default value=false
00122     if (aName.EqualsLiteral("fence"))
00123       aOperatorData->mFlags |= NS_MATHML_OPERATOR_FENCE;
00124     else if (aName.EqualsLiteral("accent"))
00125       aOperatorData->mFlags |= NS_MATHML_OPERATOR_ACCENT;
00126     else if (aName.EqualsLiteral("largeop"))
00127       aOperatorData->mFlags |= NS_MATHML_OPERATOR_LARGEOP;
00128     else if (aName.EqualsLiteral("separator"))
00129       aOperatorData->mFlags |=  NS_MATHML_OPERATOR_SEPARATOR;
00130     else if (aName.EqualsLiteral("movablelimits"))
00131       aOperatorData->mFlags |= NS_MATHML_OPERATOR_MOVABLELIMITS;
00132   }
00133   else if (aValue.EqualsLiteral("false")) {
00134     // see if we should disable flags with default value=true
00135     if (aName.EqualsLiteral("symmetric"))
00136       aOperatorData->mFlags &= ~NS_MATHML_OPERATOR_SYMMETRIC;
00137   }
00138   else if (aName.EqualsLiteral("stretchy") &&
00139           (1 == aOperatorData->mStr.Length())) {
00140     if (aValue.EqualsLiteral("vertical"))
00141       aOperatorData->mFlags |= NS_MATHML_OPERATOR_STRETCHY_VERT;
00142     else if (aValue.EqualsLiteral("horizontal"))
00143       aOperatorData->mFlags |= NS_MATHML_OPERATOR_STRETCHY_HORIZ;
00144     else return; // invalid value
00145     if (kNotFound == nsMathMLOperators::FindStretchyOperator(aOperatorData->mStr[0])) {
00146       gStretchyOperatorArray->AppendElement(aOperatorData);
00147     }
00148   }
00149   else {
00150     PRInt32 i = 0;
00151     float space = 0.0f;
00152     PRBool isLeftSpace;
00153     if (aName.EqualsLiteral("lspace"))
00154       isLeftSpace = PR_TRUE;
00155     else if (aName.EqualsLiteral("rspace"))
00156       isLeftSpace = PR_FALSE;
00157     else return;  // input is not applicable
00158 
00159     // See if it is a numeric value (unit is assumed to be 'em')
00160     if (nsCRT::IsAsciiDigit(aValue[0])) {
00161       PRInt32 error = 0;
00162       space = aValue.ToFloat(&error);
00163       if (error) return;
00164     }
00165     // See if it is one of the 'namedspace' (ranging 1/18em...7/18em)
00166     else if (aValue.EqualsLiteral("veryverythinmathspace"))  i = 1;
00167     else if (aValue.EqualsLiteral("verythinmathspace"))      i = 2;
00168     else if (aValue.EqualsLiteral("thinmathspace"))          i = 3;
00169     else if (aValue.EqualsLiteral("mediummathspace"))        i = 4;
00170     else if (aValue.EqualsLiteral("thickmathspace"))         i = 5;
00171     else if (aValue.EqualsLiteral("verythickmathspace"))     i = 6;
00172     else if (aValue.EqualsLiteral("veryverythickmathspace")) i = 7;
00173 
00174     if (0 != i) // it was a namedspace value
00175       space = float(i)/float(18);
00176 
00177     if (isLeftSpace)
00178       aOperatorData->mLeftSpace = space;
00179     else
00180       aOperatorData->mRightSpace = space;
00181   }
00182 }
00183 
00184 PRBool
00185 SetOperator(OperatorData*   aOperatorData,
00186             nsOperatorFlags aForm,
00187             const nsCString& aOperator,
00188             nsString&        aAttributes)
00189 
00190 {
00191   // aOperator is in the expanded format \uNNNN\uNNNN ...
00192   // First compress these Unicode points to the internal nsString format
00193   PRInt32 i = 0;
00194   nsAutoString name, value;
00195   PRInt32 len = aOperator.Length();
00196   PRUnichar c = aOperator[i++];
00197   PRUint32 state  = 0;
00198   PRUnichar uchar = 0;
00199   while (i <= len) {
00200     if (0 == state) {
00201       if (c != '\\')
00202         return PR_FALSE;
00203       if (i < len)
00204         c = aOperator[i];
00205       i++;
00206       if (('u' != c) && ('U' != c))
00207         return PR_FALSE;
00208       if (i < len)
00209         c = aOperator[i];
00210       i++;
00211       state++;
00212     }
00213     else {
00214       if (('0' <= c) && (c <= '9'))
00215          uchar = (uchar << 4) | (c - '0');
00216       else if (('a' <= c) && (c <= 'f'))
00217          uchar = (uchar << 4) | (c - 'a' + 0x0a);
00218       else if (('A' <= c) && (c <= 'F'))
00219          uchar = (uchar << 4) | (c - 'A' + 0x0a);
00220       else return PR_FALSE;
00221       if (i < len)
00222         c = aOperator[i];
00223       i++;
00224       state++;
00225       if (5 == state) {
00226         value.Append(uchar);
00227         uchar = 0;
00228         state = 0;
00229       }
00230     }
00231   }
00232   if (0 != state) return PR_FALSE;
00233 
00234   // Quick return when the caller doesn't care about the attributes and just wants
00235   // to know if this is a valid operator (this is the case at the first pass of the
00236   // parsing of the dictionary in InitOperators())
00237   if (!aForm) return PR_TRUE;
00238 
00239   // Add operator to hash table (symmetric="true" by default for all operators)
00240   aOperatorData->mFlags |= aForm | NS_MATHML_OPERATOR_SYMMETRIC;
00241   aOperatorData->mStr.Assign(value);
00242   value.AppendInt(aForm, 10);
00243   nsStringKey key(value);
00244   gOperatorTable->Put(&key, aOperatorData);
00245 
00246 #ifdef NS_DEBUG
00247   NS_LossyConvertUCS2toASCII str(aAttributes);
00248 #endif
00249   // Loop over the space-delimited list of attributes to get the name:value pairs
00250   aAttributes.Append(kNullCh);  // put an extra null at the end
00251   PRUnichar* start = aAttributes.BeginWriting();
00252   PRUnichar* end   = start;
00253   while ((kNullCh != *start) && (kDashCh != *start)) {
00254     name.SetLength(0);
00255     value.SetLength(0);
00256     // skip leading space, the dash amounts to the end of the line
00257     while ((kNullCh!=*start) && (kDashCh!=*start) && nsCRT::IsAsciiSpace(*start)) {
00258       ++start;
00259     }
00260     end = start;
00261     // look for ':' or '='
00262     while ((kNullCh!=*end) && (kDashCh!=*end) && (kColonCh!=*end) && (kEqualCh!=*end)) {
00263       ++end;
00264     }
00265     if ((kColonCh!=*end) && (kEqualCh!=*end)) {
00266 #ifdef NS_DEBUG
00267       printf("Bad MathML operator: %s\n", str.get());
00268 #endif
00269       return PR_TRUE;
00270     }
00271     *end = kNullCh; // end segment here
00272     // this segment is the name
00273     if (start < end) {
00274       name.Assign(start);
00275     }
00276     start = ++end;
00277     // look for space or end of line
00278     while ((kNullCh!=*end) && (kDashCh!=*start) && !nsCRT::IsAsciiSpace(*end)) {
00279       ++end;
00280     }
00281     *end = kNullCh; // end segment here
00282     // this segment is the value
00283     if (start < end) {
00284       value.Assign(start);
00285     }
00286     SetProperty(aOperatorData, name, value);
00287     start = ++end;
00288   }
00289   return PR_TRUE;
00290 }
00291 
00292 nsresult
00293 InitOperators(void)
00294 {
00295   // Load the property file containing the Operator Dictionary
00296   nsresult rv;
00297   nsCOMPtr<nsIPersistentProperties> mathfontProp;
00298   rv = NS_LoadPersistentPropertiesFromURISpec(getter_AddRefs(mathfontProp),
00299        NS_LITERAL_CSTRING("resource://gre/res/fonts/mathfont.properties"));
00300   if NS_FAILED(rv) return rv;
00301 
00302   // Get the list of invariant chars
00303   for (PRInt32 i = 0; i < nsMathMLOperators::eMATHVARIANT_COUNT; ++i) {
00304     nsCAutoString key(NS_LITERAL_CSTRING("mathvariant."));
00305     key.Append(kMathVariant_name[i]);
00306     nsAutoString value;
00307     mathfontProp->GetStringProperty(key, value);
00308     gInvariantCharArray->AppendString(value); // i.e., gInvariantCharArray[i] holds this list
00309   }
00310 
00311   // Parse the Operator Dictionary in two passes.
00312   // The first pass is to count the number of operators; the second pass is to
00313   // allocate the necessary space for them and to add them in the hash table.
00314   for (PRInt32 pass = 1; pass <= 2; pass++) {
00315     OperatorData dummyData;
00316     OperatorData* operatorData = &dummyData;
00317     nsCOMPtr<nsISimpleEnumerator> iterator;
00318     if (NS_SUCCEEDED(mathfontProp->Enumerate(getter_AddRefs(iterator)))) {
00319       PRBool more;
00320       PRInt32 index = 0;
00321       nsCAutoString name;
00322       nsAutoString attributes;
00323       while ((NS_SUCCEEDED(iterator->HasMoreElements(&more))) && more) {
00324         nsCOMPtr<nsIPropertyElement> element;
00325         if (NS_SUCCEEDED(iterator->GetNext(getter_AddRefs(element)))) {
00326           if (NS_SUCCEEDED(element->GetKey(name)) &&
00327               NS_SUCCEEDED(element->GetValue(attributes))) {
00328             // expected key: operator.\uNNNN.{infix,postfix,prefix}
00329             if ((21 <= name.Length()) && (0 == name.Find("operator.\\u"))) {
00330               name.Cut(0, 9); // 9 is the length of "operator.";
00331               PRInt32 len = name.Length();
00332               nsOperatorFlags form = 0;
00333               if (kNotFound != name.RFind(".infix")) {
00334                 form = NS_MATHML_OPERATOR_FORM_INFIX;
00335                 len -= 6;  // 6 is the length of ".infix";
00336               }
00337               else if (kNotFound != name.RFind(".postfix")) {
00338                 form = NS_MATHML_OPERATOR_FORM_POSTFIX;
00339                 len -= 8; // 8 is the length of ".postfix";
00340               }
00341               else if (kNotFound != name.RFind(".prefix")) {
00342                 form = NS_MATHML_OPERATOR_FORM_PREFIX;
00343                 len -= 7; // 7 is the length of ".prefix";
00344               }
00345               else continue; // input is not applicable
00346               name.SetLength(len);
00347               if (2 == pass) { // allocate space and start the storage
00348                 if (!gOperatorArray) {
00349                   if (0 == gOperatorCount) return NS_ERROR_UNEXPECTED;
00350                   gOperatorArray = new OperatorData[gOperatorCount];
00351                   if (!gOperatorArray) return NS_ERROR_OUT_OF_MEMORY;
00352                 }
00353                 operatorData = &gOperatorArray[index];
00354               }
00355               else {
00356                 form = 0; // to quickly return from SetOperator() at pass 1
00357               }
00358               // See if the operator should be retained
00359               if (SetOperator(operatorData, form, name, attributes)) {
00360                 index++;
00361                 if (1 == pass) gOperatorCount = index;
00362               }
00363             }
00364           }
00365         }
00366       }
00367     }
00368   }
00369   return NS_OK;
00370 }
00371 
00372 nsresult
00373 InitGlobals()
00374 {
00375   gInitialized = PR_TRUE;
00376   nsresult rv = NS_ERROR_OUT_OF_MEMORY;
00377   gInvariantCharArray = new nsStringArray();
00378   gStretchyOperatorArray = new nsVoidArray();
00379   if (gInvariantCharArray && gStretchyOperatorArray) {
00380     gOperatorTable = new nsHashtable();
00381     if (gOperatorTable) {
00382       rv = InitOperators();
00383     }
00384   }
00385   if (NS_FAILED(rv)) {
00386     if (gInvariantCharArray) {
00387       delete gInvariantCharArray;
00388       gInvariantCharArray = nsnull;
00389     }
00390     if (gOperatorArray) {
00391       delete[] gOperatorArray;
00392       gOperatorArray = nsnull;
00393     }
00394     if (gStretchyOperatorArray) {
00395       delete gStretchyOperatorArray;
00396       gStretchyOperatorArray = nsnull;
00397     }
00398     if (gOperatorTable) {
00399       delete gOperatorTable;
00400       gOperatorTable = nsnull;
00401     }
00402   }
00403   return rv;
00404 }
00405 
00406 void
00407 nsMathMLOperators::AddRefTable(void)
00408 {
00409   gTableRefCount++;
00410 }
00411 
00412 void
00413 nsMathMLOperators::ReleaseTable(void)
00414 {
00415   if (0 == --gTableRefCount) {
00416     if (gOperatorArray) {
00417       delete[] gOperatorArray;
00418       gOperatorArray = nsnull;
00419     }
00420     if (gStretchyOperatorArray) {
00421       delete gStretchyOperatorArray;
00422       gStretchyOperatorArray = nsnull;
00423     }
00424     if (gOperatorTable) {
00425       delete gOperatorTable;
00426       gOperatorTable = nsnull;
00427     }
00428   }
00429 }
00430 
00431 PRBool
00432 nsMathMLOperators::LookupOperator(const nsString&       aOperator,
00433                                   const nsOperatorFlags aForm,
00434                                   nsOperatorFlags*      aFlags,
00435                                   float*                aLeftSpace,
00436                                   float*                aRightSpace)
00437 {
00438   if (!gInitialized) {
00439     InitGlobals();
00440   }
00441   if (gOperatorTable) {
00442     NS_ASSERTION(aFlags && aLeftSpace && aRightSpace, "bad usage");
00443     NS_ASSERTION(aForm>=0 && aForm<4, "*** invalid call ***");
00444 
00445     OperatorData* found;
00446     PRInt32 form = NS_MATHML_OPERATOR_GET_FORM(aForm);
00447     gOperatorFound[NS_MATHML_OPERATOR_FORM_INFIX] = nsnull;
00448     gOperatorFound[NS_MATHML_OPERATOR_FORM_POSTFIX] = nsnull;
00449     gOperatorFound[NS_MATHML_OPERATOR_FORM_PREFIX] = nsnull;
00450 
00451     nsAutoString key(aOperator);
00452     key.AppendInt(form, 10);
00453     nsStringKey hkey(key);
00454     gOperatorFound[form] = found = (OperatorData*)gOperatorTable->Get(&hkey);
00455 
00456     // If not found, check if the operator exists perhaps in a different form,
00457     // in the order of preference: infix, postfix, prefix
00458     if (!found) {
00459       if (form != NS_MATHML_OPERATOR_FORM_INFIX) {
00460         form = NS_MATHML_OPERATOR_FORM_INFIX;
00461         key.Assign(aOperator);
00462         key.AppendInt(form, 10);
00463         nsStringKey hashkey(key);
00464         gOperatorFound[form] = found = (OperatorData*)gOperatorTable->Get(&hashkey);
00465       }
00466       if (!found) {
00467         if (form != NS_MATHML_OPERATOR_FORM_POSTFIX) {
00468           form = NS_MATHML_OPERATOR_FORM_POSTFIX;
00469           key.Assign(aOperator);
00470           key.AppendInt(form, 10);
00471           nsStringKey hashkey(key);
00472           gOperatorFound[form] = found = (OperatorData*)gOperatorTable->Get(&hashkey);
00473         }
00474         if (!found) {
00475           if (form != NS_MATHML_OPERATOR_FORM_PREFIX) {
00476             form = NS_MATHML_OPERATOR_FORM_PREFIX;
00477             key.Assign(aOperator);
00478             key.AppendInt(form, 10);
00479             nsStringKey hashkey(key);
00480             gOperatorFound[form] = found = (OperatorData*)gOperatorTable->Get(&hashkey);
00481           }
00482         }
00483       }
00484     }
00485     if (found) {
00486       NS_ASSERTION(found->mStr.Equals(aOperator), "bad setup");
00487       *aLeftSpace = found->mLeftSpace;
00488       *aRightSpace = found->mRightSpace;
00489       *aFlags &= ~NS_MATHML_OPERATOR_FORM; // clear the form bits
00490       *aFlags |= found->mFlags; // just add bits without overwriting
00491       return PR_TRUE;
00492     }
00493   }
00494   return PR_FALSE;
00495 }
00496 
00497 void
00498 nsMathMLOperators::LookupOperators(const nsString&       aOperator,
00499                                    nsOperatorFlags*      aFlags,
00500                                    float*                aLeftSpace,
00501                                    float*                aRightSpace)
00502 {
00503   if (!gInitialized) {
00504     InitGlobals();
00505   }
00506 
00507   aFlags[NS_MATHML_OPERATOR_FORM_INFIX] = 0;
00508   aLeftSpace[NS_MATHML_OPERATOR_FORM_INFIX] = 0.0f;
00509   aRightSpace[NS_MATHML_OPERATOR_FORM_INFIX] = 0.0f;
00510 
00511   aFlags[NS_MATHML_OPERATOR_FORM_POSTFIX] = 0;
00512   aLeftSpace[NS_MATHML_OPERATOR_FORM_POSTFIX] = 0.0f;
00513   aRightSpace[NS_MATHML_OPERATOR_FORM_POSTFIX] = 0.0f;
00514 
00515   aFlags[NS_MATHML_OPERATOR_FORM_PREFIX] = 0;
00516   aLeftSpace[NS_MATHML_OPERATOR_FORM_PREFIX] = 0.0f;
00517   aRightSpace[NS_MATHML_OPERATOR_FORM_PREFIX] = 0.0f;
00518 
00519   if (gOperatorTable) {
00520     // a lookup with form=0 will put all the variants in gOperatorFound[]
00521     float dummy;
00522     nsOperatorFlags flags = 0;
00523     LookupOperator(aOperator, /*form=*/0, &flags, &dummy, &dummy);
00524     // if the operator was found, gOperatorFound contains all its variants
00525     OperatorData* found;
00526     found = gOperatorFound[NS_MATHML_OPERATOR_FORM_INFIX];
00527     if (found) {
00528       aFlags[NS_MATHML_OPERATOR_FORM_INFIX] = found->mFlags;
00529       aLeftSpace[NS_MATHML_OPERATOR_FORM_INFIX] = found->mLeftSpace;
00530       aRightSpace[NS_MATHML_OPERATOR_FORM_INFIX] = found->mRightSpace;
00531     }
00532     found = gOperatorFound[NS_MATHML_OPERATOR_FORM_POSTFIX];
00533     if (found) {
00534       aFlags[NS_MATHML_OPERATOR_FORM_POSTFIX] = found->mFlags;
00535       aLeftSpace[NS_MATHML_OPERATOR_FORM_POSTFIX] = found->mLeftSpace;
00536       aRightSpace[NS_MATHML_OPERATOR_FORM_POSTFIX] = found->mRightSpace;
00537     }
00538     found = gOperatorFound[NS_MATHML_OPERATOR_FORM_PREFIX];
00539     if (found) {
00540       aFlags[NS_MATHML_OPERATOR_FORM_PREFIX] = found->mFlags;
00541       aLeftSpace[NS_MATHML_OPERATOR_FORM_PREFIX] = found->mLeftSpace;
00542       aRightSpace[NS_MATHML_OPERATOR_FORM_PREFIX] = found->mRightSpace;
00543     }
00544   }
00545 }
00546 
00547 PRBool
00548 nsMathMLOperators::IsMutableOperator(const nsString& aOperator)
00549 {
00550   if (!gInitialized) {
00551     InitGlobals();
00552   }
00553   // lookup all the variants of the operator and return true if there
00554   // is a variant that is stretchy or largeop
00555   nsOperatorFlags flags[4];
00556   float lspace[4], rspace[4];
00557   nsMathMLOperators::LookupOperators(aOperator, flags, lspace, rspace);
00558   nsOperatorFlags allFlags =
00559     flags[NS_MATHML_OPERATOR_FORM_INFIX] |
00560     flags[NS_MATHML_OPERATOR_FORM_POSTFIX] |
00561     flags[NS_MATHML_OPERATOR_FORM_PREFIX];
00562   return NS_MATHML_OPERATOR_IS_STRETCHY(allFlags) ||
00563          NS_MATHML_OPERATOR_IS_LARGEOP(allFlags);
00564 }
00565 
00566 PRInt32
00567 nsMathMLOperators::CountStretchyOperator()
00568 {
00569   if (!gInitialized) {
00570     InitGlobals();
00571   }
00572   return (gStretchyOperatorArray) ? gStretchyOperatorArray->Count() : 0;
00573 }
00574 
00575 PRInt32
00576 nsMathMLOperators::FindStretchyOperator(PRUnichar aOperator)
00577 {
00578   if (!gInitialized) {
00579     InitGlobals();
00580   }
00581   if (gStretchyOperatorArray) {
00582     for (PRInt32 k = 0; k < gStretchyOperatorArray->Count(); k++) {
00583       OperatorData* data = (OperatorData*)gStretchyOperatorArray->ElementAt(k);
00584       if (data && (aOperator == data->mStr[0])) {
00585         return k;
00586       }
00587     }
00588   }
00589   return kNotFound;
00590 }
00591 
00592 nsStretchDirection
00593 nsMathMLOperators::GetStretchyDirectionAt(PRInt32 aIndex)
00594 {
00595   NS_ASSERTION(gStretchyOperatorArray, "invalid call");
00596   if (gStretchyOperatorArray) {
00597     NS_ASSERTION(aIndex < gStretchyOperatorArray->Count(), "invalid call");
00598     OperatorData* data = (OperatorData*)gStretchyOperatorArray->ElementAt(aIndex);
00599     if (data) {
00600       if (NS_MATHML_OPERATOR_IS_STRETCHY_VERT(data->mFlags))
00601         return NS_STRETCH_DIRECTION_VERTICAL;
00602       else if (NS_MATHML_OPERATOR_IS_STRETCHY_HORIZ(data->mFlags))
00603         return NS_STRETCH_DIRECTION_HORIZONTAL;
00604       NS_ASSERTION(PR_FALSE, "*** bad setup ***");
00605     }
00606   }
00607   return NS_STRETCH_DIRECTION_UNSUPPORTED;
00608 }
00609 
00610 void
00611 nsMathMLOperators::DisableStretchyOperatorAt(PRInt32 aIndex)
00612 {
00613   NS_ASSERTION(gStretchyOperatorArray, "invalid call");
00614   if (gStretchyOperatorArray) {
00615     NS_ASSERTION(aIndex < gStretchyOperatorArray->Count(), "invalid call");
00616     gStretchyOperatorArray->ReplaceElementAt(nsnull, aIndex);
00617   }
00618 }
00619 
00620 PRBool
00621 nsMathMLOperators::LookupInvariantChar(PRUnichar     aChar,
00622                                        eMATHVARIANT* aType)
00623 {
00624   if (!gInitialized) {
00625     InitGlobals();
00626   }
00627   if (aType) *aType = eMATHVARIANT_NONE;
00628   if (gInvariantCharArray) {
00629     for (PRInt32 i = gInvariantCharArray->Count()-1; i >= 0; --i) {
00630       nsString* list = gInvariantCharArray->StringAt(i);
00631       if (kNotFound != list->FindChar(aChar)) {
00632         if (aType) *aType = eMATHVARIANT(i);
00633         return PR_TRUE;
00634       }
00635     }
00636   }
00637   return PR_FALSE;
00638 }