Back to index

lightning-sunbird  0.9+nobinonly
nsCharsetConverterManager.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsCOMPtr.h"
00040 #include "nsString.h"
00041 #include "nsReadableUtils.h"
00042 #include "nsUnicharUtils.h"
00043 #include "nsICharsetAlias.h"
00044 #include "nsIServiceManager.h"
00045 #include "nsICategoryManager.h"
00046 #include "nsICharsetConverterManager.h"
00047 #include "nsEncoderDecoderUtils.h"
00048 #include "nsIStringBundle.h"
00049 #include "nsILocaleService.h"
00050 #include "nsUConvDll.h"
00051 #include "prmem.h"
00052 #include "nsCRT.h"
00053 #include "nsVoidArray.h"
00054 #include "nsStringEnumerator.h"
00055 
00056 #include "nsXPCOM.h"
00057 #include "nsISupportsPrimitives.h"
00058 #include "nsICharsetDetector.h"
00059 
00060 // just for CIDs
00061 #include "nsIUnicodeDecodeHelper.h"
00062 #include "nsIUnicodeEncodeHelper.h"
00063 #include "nsCharsetConverterManager.h"
00064 
00065 #ifdef MOZ_USE_NATIVE_UCONV
00066 #include "nsNativeUConvService.h"
00067 #endif
00068 
00069 static NS_DEFINE_CID(kStringBundleServiceCID, NS_STRINGBUNDLESERVICE_CID);
00070 static NS_DEFINE_CID(kCharsetAliasCID, NS_CHARSETALIAS_CID);
00071 
00072 // Pattern of cached, commonly used, single byte decoder
00073 #define NS_1BYTE_CODER_PATTERN "ISO-8859"
00074 #define NS_1BYTE_CODER_PATTERN_LEN 8
00075 
00076 // Class nsCharsetConverterManager [implementation]
00077 
00078 NS_IMPL_THREADSAFE_ISUPPORTS1(nsCharsetConverterManager,
00079                               nsICharsetConverterManager)
00080 
00081 nsCharsetConverterManager::nsCharsetConverterManager() 
00082   :mDataBundle(NULL), mTitleBundle(NULL)
00083 {
00084 #ifdef MOZ_USE_NATIVE_UCONV
00085   mNativeUC = do_GetService(NS_NATIVE_UCONV_SERVICE_CONTRACT_ID);
00086 #endif
00087 }
00088 
00089 nsCharsetConverterManager::~nsCharsetConverterManager() 
00090 {
00091   NS_IF_RELEASE(mDataBundle);
00092   NS_IF_RELEASE(mTitleBundle);
00093 }
00094 
00095 
00096 nsresult nsCharsetConverterManager::RegisterConverterManagerData()
00097 {
00098   nsresult rv;
00099   nsCOMPtr<nsICategoryManager> catman = do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
00100   if (NS_FAILED(rv))
00101     return rv;
00102 
00103   RegisterConverterCategory(catman, NS_TITLE_BUNDLE_CATEGORY,
00104                             "chrome://global/locale/charsetTitles.properties");
00105   RegisterConverterCategory(catman, NS_DATA_BUNDLE_CATEGORY,
00106                             "resource://gre/res/charsetData.properties");
00107 
00108   return NS_OK;
00109 }
00110 
00111 nsresult
00112 nsCharsetConverterManager::RegisterConverterCategory(nsICategoryManager* catman,
00113                                                      const char* aCategory,
00114                                                      const char* aURL)
00115 {
00116   return catman->AddCategoryEntry(aCategory, aURL, "",
00117                                   PR_TRUE, PR_TRUE, nsnull);
00118 }
00119 
00120 nsresult nsCharsetConverterManager::LoadExtensibleBundle(
00121                                     const char* aCategory, 
00122                                     nsIStringBundle ** aResult)
00123 {
00124   nsresult rv = NS_OK;
00125 
00126   nsCOMPtr<nsIStringBundleService> sbServ = 
00127            do_GetService(kStringBundleServiceCID, &rv);
00128   if (NS_FAILED(rv))
00129     return rv;
00130 
00131   return sbServ->CreateExtensibleBundle(aCategory, aResult);
00132 }
00133 
00134 nsresult nsCharsetConverterManager::GetBundleValue(nsIStringBundle * aBundle, 
00135                                                    const char * aName, 
00136                                                    const nsAFlatString& aProp, 
00137                                                    PRUnichar ** aResult)
00138 {
00139   nsAutoString key; 
00140 
00141   key.AssignWithConversion(aName);
00142   ToLowerCase(key); // we lowercase the main comparison key
00143   key.Append(aProp);
00144 
00145   return aBundle->GetStringFromName(key.get(), aResult);
00146 }
00147 
00148 nsresult nsCharsetConverterManager::GetBundleValue(nsIStringBundle * aBundle, 
00149                                                    const char * aName, 
00150                                                    const nsAFlatString& aProp, 
00151                                                    nsAString& aResult)
00152 {
00153   nsresult rv = NS_OK;
00154 
00155   nsXPIDLString value;
00156   rv = GetBundleValue(aBundle, aName, aProp, getter_Copies(value));
00157   if (NS_FAILED(rv))
00158     return rv;
00159 
00160   aResult = value;
00161 
00162   return NS_OK;
00163 }
00164 
00165 
00166 //----------------------------------------------------------------------------//----------------------------------------------------------------------------
00167 // Interface nsICharsetConverterManager [implementation]
00168 
00169 NS_IMETHODIMP
00170 nsCharsetConverterManager::GetUnicodeEncoder(const char * aDest, 
00171                                              nsIUnicodeEncoder ** aResult)
00172 {
00173   // resolve the charset first
00174   nsCAutoString charset;
00175   
00176   // fully qualify to possibly avoid vtable call
00177   nsCharsetConverterManager::GetCharsetAlias(aDest, charset);
00178 
00179   return nsCharsetConverterManager::GetUnicodeEncoderRaw(charset.get(),
00180                                                          aResult);
00181 }
00182 
00183 
00184 NS_IMETHODIMP
00185 nsCharsetConverterManager::GetUnicodeEncoderRaw(const char * aDest, 
00186                                                 nsIUnicodeEncoder ** aResult)
00187 {
00188   *aResult= nsnull;
00189   nsCOMPtr<nsIUnicodeEncoder> encoder;
00190 
00191 #ifdef MOZ_USE_NATIVE_UCONV
00192   if (mNativeUC) {
00193     nsCOMPtr<nsISupports> supports;
00194     mNativeUC->GetNativeConverter("UCS-2", 
00195                                   aDest,
00196                                   getter_AddRefs(supports));
00197 
00198     encoder = do_QueryInterface(supports);
00199 
00200     if (encoder) {
00201       NS_ADDREF(*aResult = encoder);
00202       return NS_OK;
00203     }
00204   }
00205 #endif  
00206   nsresult rv = NS_OK;
00207 
00208   nsCAutoString
00209     contractid(NS_LITERAL_CSTRING(NS_UNICODEENCODER_CONTRACTID_BASE) +
00210                nsDependentCString(aDest));
00211 
00212   // Always create an instance since encoders hold state.
00213   encoder = do_CreateInstance(contractid.get(), &rv);
00214 
00215   if (NS_FAILED(rv))
00216     rv = NS_ERROR_UCONV_NOCONV;
00217   else
00218   {
00219     *aResult = encoder.get();
00220     NS_ADDREF(*aResult);
00221   }
00222   return rv;
00223 }
00224 
00225 NS_IMETHODIMP
00226 nsCharsetConverterManager::GetUnicodeDecoder(const char * aSrc, 
00227                                              nsIUnicodeDecoder ** aResult)
00228 {
00229   // resolve the charset first
00230   nsCAutoString charset;
00231   
00232   // fully qualify to possibly avoid vtable call
00233   nsCharsetConverterManager::GetCharsetAlias(aSrc, charset);
00234 
00235   return nsCharsetConverterManager::GetUnicodeDecoderRaw(charset.get(),
00236                                                          aResult);
00237 }
00238 
00239 NS_IMETHODIMP
00240 nsCharsetConverterManager::GetUnicodeDecoderRaw(const char * aSrc, 
00241                                                 nsIUnicodeDecoder ** aResult)
00242 {
00243   *aResult= nsnull;
00244   nsCOMPtr<nsIUnicodeDecoder> decoder;
00245 
00246 #ifdef MOZ_USE_NATIVE_UCONV
00247   if (mNativeUC) {
00248     nsCOMPtr<nsISupports> supports;
00249     mNativeUC->GetNativeConverter(aSrc,
00250                                   "UCS-2", 
00251                                   getter_AddRefs(supports));
00252     
00253     decoder = do_QueryInterface(supports);
00254 
00255     if (decoder) {
00256       NS_ADDREF(*aResult = decoder);
00257       return NS_OK;
00258     }
00259   }
00260 #endif
00261   nsresult rv = NS_OK;
00262 
00263   NS_NAMED_LITERAL_CSTRING(kUnicodeDecoderContractIDBase,
00264                            NS_UNICODEDECODER_CONTRACTID_BASE);
00265 
00266   nsCAutoString contractid(kUnicodeDecoderContractIDBase +
00267                            nsDependentCString(aSrc));
00268 
00269   if (!strncmp(aSrc,
00270                NS_1BYTE_CODER_PATTERN,
00271                NS_1BYTE_CODER_PATTERN_LEN))
00272   {
00273     // Single byte decoders dont hold state. Optimize by using a service.
00274     decoder = do_GetService(contractid.get(), &rv);
00275   }
00276   else
00277   {
00278     decoder = do_CreateInstance(contractid.get(), &rv);
00279   }
00280   if(NS_FAILED(rv))
00281     rv = NS_ERROR_UCONV_NOCONV;
00282   else
00283   {
00284     *aResult = decoder.get();
00285     NS_ADDREF(*aResult);
00286   }
00287   return rv;
00288 }
00289 
00290 nsresult 
00291 nsCharsetConverterManager::GetList(const nsACString& aCategory,
00292                                    const nsACString& aPrefix,
00293                                    nsIUTF8StringEnumerator** aResult)
00294 {
00295   if (aResult == NULL) 
00296     return NS_ERROR_NULL_POINTER;
00297   *aResult = NULL;
00298 
00299   nsresult rv;
00300   nsCAutoString alias;
00301 
00302   nsCStringArray* array = new nsCStringArray;
00303   if (!array)
00304     return NS_ERROR_OUT_OF_MEMORY;
00305 
00306   nsCOMPtr<nsICategoryManager> catman = do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
00307   if (NS_FAILED(rv))
00308     return rv;
00309   
00310   nsCOMPtr<nsISimpleEnumerator> enumerator;
00311   catman->EnumerateCategory(PromiseFlatCString(aCategory).get(), 
00312                             getter_AddRefs(enumerator));
00313 
00314   PRBool hasMore;
00315   while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) {
00316     nsCOMPtr<nsISupports> supports;
00317     if (NS_FAILED(enumerator->GetNext(getter_AddRefs(supports))))
00318       continue;
00319     
00320     nsCOMPtr<nsISupportsCString> supStr = do_QueryInterface(supports);
00321     if (!supStr)
00322       continue;
00323 
00324     nsCAutoString fullName(aPrefix);
00325     
00326     nsCAutoString name;
00327     if (NS_FAILED(supStr->GetData(name)))
00328       continue;
00329 
00330     fullName += name;
00331     rv = GetCharsetAlias(fullName.get(), alias);
00332     if (NS_FAILED(rv)) 
00333       continue;
00334 
00335     rv = array->AppendCString(alias);
00336   }
00337     
00338   return NS_NewAdoptingUTF8StringEnumerator(aResult, array);
00339 }
00340 
00341 // we should change the interface so that we can just pass back a enumerator!
00342 NS_IMETHODIMP
00343 nsCharsetConverterManager::GetDecoderList(nsIUTF8StringEnumerator ** aResult)
00344 {
00345   return GetList(NS_LITERAL_CSTRING(NS_UNICODEDECODER_NAME),
00346                  EmptyCString(), aResult);
00347 }
00348 
00349 NS_IMETHODIMP
00350 nsCharsetConverterManager::GetEncoderList(nsIUTF8StringEnumerator ** aResult)
00351 {
00352   return GetList(NS_LITERAL_CSTRING(NS_UNICODEENCODER_NAME),
00353                  EmptyCString(), aResult);
00354 }
00355 
00356 NS_IMETHODIMP
00357 nsCharsetConverterManager::GetCharsetDetectorList(nsIUTF8StringEnumerator** aResult)
00358 {
00359   return GetList(NS_LITERAL_CSTRING(NS_CHARSET_DETECTOR_CATEGORY),
00360                  NS_LITERAL_CSTRING("chardet."), aResult);
00361 }
00362 
00363 // XXX Improve the implementation of this method. Right now, it is build on 
00364 // top of the nsCharsetAlias service. We can make the nsCharsetAlias
00365 // better, with its own hash table (not the StringBundle anymore) and
00366 // a nicer file format.
00367 NS_IMETHODIMP
00368 nsCharsetConverterManager::GetCharsetAlias(const char * aCharset, 
00369                                            nsACString& aResult)
00370 {
00371   NS_PRECONDITION(aCharset, "null param");
00372   if (!aCharset)
00373     return NS_ERROR_NULL_POINTER;
00374 
00375   // We try to obtain the preferred name for this charset from the charset 
00376   // aliases. If we don't get it from there, we just use the original string
00377   nsDependentCString charset(aCharset);
00378   nsCOMPtr<nsICharsetAlias> csAlias( do_GetService(kCharsetAliasCID) );
00379   NS_ASSERTION(csAlias, "failed to get the CharsetAlias service");
00380   if (csAlias) {
00381     nsAutoString pref;
00382     nsresult rv = csAlias->GetPreferred(charset, aResult);
00383     if (NS_SUCCEEDED(rv)) {
00384       return (!aResult.IsEmpty()) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
00385     }
00386   }
00387 
00388   aResult = charset;
00389   return NS_OK;
00390 }
00391 
00392 
00393 NS_IMETHODIMP
00394 nsCharsetConverterManager::GetCharsetTitle(const char * aCharset, 
00395                                            nsAString& aResult)
00396 {
00397   if (aCharset == NULL) return NS_ERROR_NULL_POINTER;
00398 
00399   if (mTitleBundle == NULL) {
00400     nsresult rv = LoadExtensibleBundle(NS_TITLE_BUNDLE_CATEGORY, &mTitleBundle);
00401     if (NS_FAILED(rv))
00402       return rv;
00403   }
00404 
00405   return GetBundleValue(mTitleBundle, aCharset, NS_LITERAL_STRING(".title"), aResult);
00406 }
00407 
00408 NS_IMETHODIMP
00409 nsCharsetConverterManager::GetCharsetData(const char * aCharset, 
00410                                           const PRUnichar * aProp,
00411                                           nsAString& aResult)
00412 {
00413   if (aCharset == NULL)
00414     return NS_ERROR_NULL_POINTER;
00415   // aProp can be NULL
00416 
00417   if (mDataBundle == NULL) {
00418     nsresult rv = LoadExtensibleBundle(NS_DATA_BUNDLE_CATEGORY, &mDataBundle);
00419     if (NS_FAILED(rv))
00420       return rv;
00421   }
00422 
00423   return GetBundleValue(mDataBundle, aCharset, nsDependentString(aProp), aResult);
00424 }
00425 
00426 NS_IMETHODIMP
00427 nsCharsetConverterManager::GetCharsetLangGroup(const char * aCharset, 
00428                                                nsIAtom** aResult)
00429 {
00430   // resolve the charset first
00431   nsCAutoString charset;
00432 
00433   nsresult rv = GetCharsetAlias(aCharset, charset);
00434   if (NS_FAILED(rv))
00435     return rv;
00436 
00437   // fully qualify to possibly avoid vtable call
00438   return nsCharsetConverterManager::GetCharsetLangGroupRaw(charset.get(),
00439                                                            aResult);
00440 }
00441 
00442 NS_IMETHODIMP
00443 nsCharsetConverterManager::GetCharsetLangGroupRaw(const char * aCharset, 
00444                                                   nsIAtom** aResult)
00445 {
00446 
00447   *aResult = nsnull;
00448   if (aCharset == NULL)
00449     return NS_ERROR_NULL_POINTER;
00450 
00451   nsresult rv = NS_OK;
00452 
00453   if (mDataBundle == NULL) {
00454     rv = LoadExtensibleBundle(NS_DATA_BUNDLE_CATEGORY, &mDataBundle);
00455     if (NS_FAILED(rv))
00456       return rv;
00457   }
00458 
00459   nsAutoString langGroup;
00460   rv = GetBundleValue(mDataBundle, aCharset, NS_LITERAL_STRING(".LangGroup"), langGroup);
00461 
00462   if (NS_SUCCEEDED(rv))
00463     *aResult = NS_NewAtom(langGroup);
00464 
00465   return rv;
00466 }