Back to index

lightning-sunbird  0.9+nobinonly
nsPersistentProperties.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.org 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 "nsID.h"
00040 #include "nsCRT.h"
00041 #include "nsReadableUtils.h"
00042 #include "nsIInputStream.h"
00043 #include "nsIUnicharInputStream.h"
00044 #include "pratom.h"
00045 #include "nsEnumeratorUtils.h"
00046 #include "nsReadableUtils.h"
00047 #include "nsPrintfCString.h"
00048 
00049 #define PL_ARENA_CONST_ALIGN_MASK 3
00050 #include "nsPersistentProperties.h"
00051 #include "nsIProperties.h"
00052 #include "nsProperties.h"
00053 
00054 struct PropertyTableEntry : public PLDHashEntryHdr
00055 {
00056   // both of these are arena-allocated
00057   const char *mKey;
00058   const PRUnichar *mValue;
00059 };
00060 
00061 static PRUnichar*
00062 ArenaStrdup(const nsAFlatString& aString, PLArenaPool* aArena)
00063 {
00064   void *mem;
00065   // add one to include the null terminator
00066   PRInt32 len = (aString.Length()+1) * sizeof(PRUnichar);
00067   PL_ARENA_ALLOCATE(mem, aArena, len);
00068   NS_ASSERTION(mem, "Couldn't allocate space!\n");
00069   if (mem) {
00070     memcpy(mem, aString.get(), len);
00071   }
00072   return NS_STATIC_CAST(PRUnichar*, mem);
00073 }
00074 
00075 static char*
00076 ArenaStrdup(const nsAFlatCString& aString, PLArenaPool* aArena)
00077 {
00078   void *mem;
00079   // add one to include the null terminator
00080   PRInt32 len = (aString.Length()+1) * sizeof(char);
00081   PL_ARENA_ALLOCATE(mem, aArena, len);
00082   NS_ASSERTION(mem, "Couldn't allocate space!\n");
00083   if (mem)
00084     memcpy(mem, aString.get(), len);
00085   return NS_STATIC_CAST(char*, mem);
00086 }
00087 
00088 static const struct PLDHashTableOps property_HashTableOps = {
00089   PL_DHashAllocTable,
00090   PL_DHashFreeTable,
00091   PL_DHashGetKeyStub,
00092   PL_DHashStringKey,
00093   PL_DHashMatchStringKey,
00094   PL_DHashMoveEntryStub,
00095   PL_DHashClearEntryStub,
00096   PL_DHashFinalizeStub,
00097   nsnull,
00098 };
00099 
00100 nsPersistentProperties::nsPersistentProperties()
00101 : mIn(nsnull)
00102 {
00103   mSubclass = NS_STATIC_CAST(nsIPersistentProperties*, this);
00104   mTable.ops = nsnull;
00105   PL_INIT_ARENA_POOL(&mArena, "PersistentPropertyArena", 2048);
00106 }
00107 
00108 nsPersistentProperties::~nsPersistentProperties()
00109 {
00110   PL_FinishArenaPool(&mArena);
00111   if (mTable.ops)
00112     PL_DHashTableFinish(&mTable);
00113 }
00114 
00115 nsresult
00116 nsPersistentProperties::Init()
00117 {
00118   if (!PL_DHashTableInit(&mTable, &property_HashTableOps, nsnull,
00119                          sizeof(PropertyTableEntry), 20)) {
00120     mTable.ops = nsnull;
00121     return NS_ERROR_OUT_OF_MEMORY;
00122   }
00123   return NS_OK;
00124 }
00125 
00126 NS_METHOD
00127 nsPersistentProperties::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
00128 {
00129   if (aOuter)
00130     return NS_ERROR_NO_AGGREGATION;
00131   nsPersistentProperties* props = new nsPersistentProperties();
00132   if (props == nsnull)
00133     return NS_ERROR_OUT_OF_MEMORY;
00134 
00135   NS_ADDREF(props);
00136   nsresult rv = props->Init();
00137   if (NS_SUCCEEDED(rv))
00138     rv = props->QueryInterface(aIID, aResult);
00139 
00140   NS_RELEASE(props);
00141   return rv;
00142 }
00143 
00144 NS_IMPL_THREADSAFE_ISUPPORTS2(nsPersistentProperties, nsIPersistentProperties, nsIProperties)
00145 
00146 NS_IMETHODIMP
00147 nsPersistentProperties::Load(nsIInputStream *aIn)
00148 {
00149   PRInt32  c;
00150   nsresult ret = NS_NewUTF8ConverterStream(&mIn, aIn, 0);
00151 
00152   if (ret != NS_OK) {
00153     NS_WARNING("NS_NewUTF8ConverterStream failed");
00154     return NS_ERROR_FAILURE;
00155   }
00156   c = Read();
00157   while (1) {
00158     c = SkipWhiteSpace(c);
00159     if (c < 0) {
00160       break;
00161     }
00162     else if ((c == '#') || (c == '!')) {
00163       c = SkipLine(c);
00164       continue;
00165     }
00166     else {
00167       nsAutoString key;
00168       while ((c >= 0) && (c != '=') && (c != ':')) {
00169         key.Append(PRUnichar(c));
00170         c = Read();
00171       }
00172       if (c < 0) {
00173         break;
00174       }
00175       static const char trimThese[] = " \t";
00176       key.Trim(trimThese, PR_FALSE, PR_TRUE);
00177       c = Read();
00178       nsAutoString value;
00179       PRUint32 state  = 0;
00180       PRUnichar uchar = 0;
00181       while ((c >= 0) && (c != '\r') && (c != '\n')) {
00182         switch(state) {
00183           case 0:
00184            if (c == '\\') {
00185              c = Read();
00186              switch(c) {
00187                case '\r':
00188                case '\n':
00189                  // Only skip first EOL characters and then next line's
00190                  // whitespace characters. Skipping all EOL characters
00191                  // and all upcoming whitespace is too agressive.
00192                  if (c == '\r')
00193                     c = Read();
00194                  if (c == '\n')
00195                     c = Read();
00196                  while (c == ' ' || c == '\t')
00197                     c = Read();
00198                  continue;
00199                case 'u':
00200                case 'U':
00201                  state = 1;
00202                  uchar=0;
00203                  break;
00204                case 't':
00205                  value.Append(PRUnichar('\t'));
00206                  break;
00207                case 'n':
00208                  value.Append(PRUnichar('\n'));
00209                  break;
00210                case 'r':
00211                  value.Append(PRUnichar('\r'));
00212                  break;
00213                default:
00214                  value.Append((PRUnichar) c);
00215              } // switch(c)
00216            } else {
00217              value.Append((PRUnichar) c);
00218            }
00219            c = Read();
00220            break;
00221          case 1:
00222          case 2:
00223          case 3:
00224          case 4:
00225            if (('0' <= c) && (c <= '9')) {
00226               uchar = (uchar << 4) | (c - '0');
00227               state++;
00228               c = Read();
00229            } else if (('a' <= c) && (c <= 'f')) {
00230               uchar = (uchar << 4) | (c - 'a' + 0x0a);
00231               state++;
00232               c = Read();
00233            } else if (('A' <= c) && (c <= 'F')) {
00234               uchar = (uchar << 4) | (c - 'A' + 0x0a);
00235               state++;
00236               c = Read();
00237            } else {
00238              value.Append((PRUnichar) uchar);
00239              state = 0;
00240            }
00241            break;
00242          case 5:
00243            value.Append((PRUnichar) uchar);
00244            state = 0;
00245         }
00246       }
00247       if (state != 0) {
00248         value.Append((PRUnichar) uchar);
00249         state = 0;
00250       }
00251 
00252       value.Trim(trimThese, PR_TRUE, PR_TRUE);
00253       nsAutoString oldValue;
00254       mSubclass->SetStringProperty(NS_ConvertUCS2toUTF8(key), value, oldValue);
00255     }
00256   }
00257   mIn->Close();
00258   NS_RELEASE(mIn);
00259 
00260   return NS_OK;
00261 }
00262 
00263 NS_IMETHODIMP
00264 nsPersistentProperties::SetStringProperty(const nsACString& aKey,
00265                                           const nsAString& aNewValue,
00266                                           nsAString& aOldValue)
00267 {
00268 #if 0
00269   cout << "will add " << aKey.get() << "=" <<
00270     NS_LossyConvertUCS2ToASCII(aNewValue).get() << endl;
00271 #endif
00272 
00273   const nsAFlatCString&  flatKey = PromiseFlatCString(aKey);
00274   PropertyTableEntry *entry =
00275     NS_STATIC_CAST(PropertyTableEntry*,
00276                    PL_DHashTableOperate(&mTable, flatKey.get(), PL_DHASH_ADD));
00277 
00278   if (entry->mKey) {
00279     aOldValue = entry->mValue;
00280     NS_WARNING(nsPrintfCString(aKey.Length() + 30,
00281                                "the property %s already exists\n",
00282                                flatKey.get()).get());
00283   }
00284 
00285   entry->mKey = ArenaStrdup(flatKey, &mArena);
00286   entry->mValue = ArenaStrdup(PromiseFlatString(aNewValue), &mArena);
00287 
00288   return NS_OK;
00289 }
00290 
00291 NS_IMETHODIMP
00292 nsPersistentProperties::Save(nsIOutputStream* aOut, const nsACString& aHeader)
00293 {
00294   return NS_ERROR_NOT_IMPLEMENTED;
00295 }
00296 
00297 NS_IMETHODIMP
00298 nsPersistentProperties::Subclass(nsIPersistentProperties* aSubclass)
00299 {
00300   if (aSubclass) {
00301     mSubclass = aSubclass;
00302   }
00303 
00304   return NS_OK;
00305 }
00306 
00307 NS_IMETHODIMP
00308 nsPersistentProperties::GetStringProperty(const nsACString& aKey,
00309                                           nsAString& aValue)
00310 {
00311   const nsAFlatCString&  flatKey = PromiseFlatCString(aKey);
00312 
00313   PropertyTableEntry *entry =
00314     NS_STATIC_CAST(PropertyTableEntry*,
00315                    PL_DHashTableOperate(&mTable, flatKey.get(), PL_DHASH_LOOKUP));
00316 
00317   if (PL_DHASH_ENTRY_IS_FREE(entry))
00318     return NS_ERROR_FAILURE;
00319 
00320   aValue = entry->mValue;
00321   return NS_OK;
00322 }
00323 
00324 PR_STATIC_CALLBACK(PLDHashOperator)
00325 AddElemToArray(PLDHashTable* table, PLDHashEntryHdr *hdr,
00326                PRUint32 i, void *arg)
00327 {
00328   nsISupportsArray  *propArray = (nsISupportsArray *) arg;
00329   PropertyTableEntry* entry =
00330     NS_STATIC_CAST(PropertyTableEntry*, hdr);
00331 
00332   nsPropertyElement *element =
00333     new nsPropertyElement(nsDependentCString(entry->mKey),
00334                           nsDependentString(entry->mValue));
00335   if (!element)
00336      return PL_DHASH_STOP;
00337 
00338   propArray->InsertElementAt(element, i);
00339 
00340   return PL_DHASH_NEXT;
00341 }
00342 
00343 
00344 NS_IMETHODIMP
00345 nsPersistentProperties::Enumerate(nsISimpleEnumerator** aResult)
00346 {
00347   nsCOMPtr<nsISupportsArray> propArray;
00348   nsresult rv = NS_NewISupportsArray(getter_AddRefs(propArray));
00349   if (NS_FAILED(rv))
00350     return rv;
00351 
00352   // We know the necessary size; we can avoid growing it while adding elements
00353   if (!propArray->SizeTo(mTable.entryCount))
00354    return NS_ERROR_OUT_OF_MEMORY;
00355 
00356   // Step through hash entries populating a transient array
00357   PRUint32 n =
00358     PL_DHashTableEnumerate(&mTable, AddElemToArray, (void *)propArray);
00359   if (n < mTable.entryCount)
00360       return NS_ERROR_OUT_OF_MEMORY;
00361 
00362   return NS_NewArrayEnumerator(aResult, propArray);
00363 }
00364 
00365 
00366 PRInt32
00367 nsPersistentProperties::Read()
00368 {
00369   PRUnichar  c;
00370   PRUint32  nRead;
00371   nsresult  ret;
00372 
00373   ret = mIn->Read(&c, 1, &nRead);
00374   if (ret == NS_OK && nRead == 1) {
00375     return c;
00376   }
00377 
00378   return -1;
00379 }
00380 
00381 #define IS_WHITE_SPACE(c) \
00382   (((c) == ' ') || ((c) == '\t') || ((c) == '\r') || ((c) == '\n'))
00383 
00384 PRInt32
00385 nsPersistentProperties::SkipWhiteSpace(PRInt32 c)
00386 {
00387   while (IS_WHITE_SPACE(c)) {
00388     c = Read();
00389   }
00390 
00391   return c;
00392 }
00393 
00394 PRInt32
00395 nsPersistentProperties::SkipLine(PRInt32 c)
00396 {
00397   while ((c >= 0) && (c != '\r') && (c != '\n')) {
00398     c = Read();
00399   }
00400   if (c == '\r') {
00401     c = Read();
00402   }
00403   if (c == '\n') {
00404     c = Read();
00405   }
00406 
00407   return c;
00408 }
00409 
00411 // XXX Some day we'll unify the nsIPersistentProperties interface with
00412 // nsIProperties, but until now...
00413 
00414 NS_IMETHODIMP
00415 nsPersistentProperties::Get(const char* prop, const nsIID & uuid, void* *result)
00416 {
00417   return NS_ERROR_NOT_IMPLEMENTED;
00418 }
00419 
00420 NS_IMETHODIMP
00421 nsPersistentProperties::Set(const char* prop, nsISupports* value)
00422 {
00423   return NS_ERROR_NOT_IMPLEMENTED;
00424 }
00425 NS_IMETHODIMP
00426 nsPersistentProperties::Undefine(const char* prop)
00427 {
00428   return NS_ERROR_NOT_IMPLEMENTED;
00429 }
00430 
00431 NS_IMETHODIMP
00432 nsPersistentProperties::Has(const char* prop, PRBool *result)
00433 {
00434   return NS_ERROR_NOT_IMPLEMENTED;
00435 }
00436 
00437 NS_IMETHODIMP
00438 nsPersistentProperties::GetKeys(PRUint32 *count, char ***keys)
00439 {
00440     return NS_ERROR_NOT_IMPLEMENTED;
00441 }
00442 
00444 // PropertyElement
00446 
00447 
00448 NS_METHOD
00449 nsPropertyElement::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
00450 {
00451   if (aOuter)
00452     return NS_ERROR_NO_AGGREGATION;
00453   nsPropertyElement* propElem = new nsPropertyElement();
00454   if (propElem == nsnull)
00455     return NS_ERROR_OUT_OF_MEMORY;
00456   NS_ADDREF(propElem);
00457   nsresult rv = propElem->QueryInterface(aIID, aResult);
00458   NS_RELEASE(propElem);
00459   return rv;
00460 }
00461 
00462 NS_IMPL_ISUPPORTS1(nsPropertyElement, nsIPropertyElement)
00463 
00464 NS_IMETHODIMP
00465 nsPropertyElement::GetKey(nsACString& aReturnKey)
00466 {
00467   aReturnKey = mKey;
00468   return NS_OK;
00469 }
00470 
00471 NS_IMETHODIMP
00472 nsPropertyElement::GetValue(nsAString& aReturnValue)
00473 {
00474   aReturnValue = mValue;
00475   return NS_OK;
00476 }
00477 
00478 NS_IMETHODIMP
00479 nsPropertyElement::SetKey(const nsACString& aKey)
00480 {
00481   mKey = aKey;
00482   return NS_OK;
00483 }
00484 
00485 NS_IMETHODIMP
00486 nsPropertyElement::SetValue(const nsAString& aValue)
00487 {
00488   mValue = aValue;
00489   return NS_OK;
00490 }
00491 
00493