1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Pierre Phaneuf <pp@ludusdesign.com>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either of the GNU General Public License Version 2 or later (the "GPL"),
27 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "nsID.h"
40 : #include "nsCRT.h"
41 : #include "nsReadableUtils.h"
42 : #include "nsIInputStream.h"
43 : #include "nsUnicharInputStream.h"
44 : #include "pratom.h"
45 : #include "nsEnumeratorUtils.h"
46 : #include "nsReadableUtils.h"
47 : #include "nsPrintfCString.h"
48 : #include "nsDependentString.h"
49 :
50 : #define PL_ARENA_CONST_ALIGN_MASK 3
51 : #include "nsPersistentProperties.h"
52 : #include "nsIProperties.h"
53 : #include "nsISupportsArray.h"
54 : #include "nsProperties.h"
55 :
56 : struct PropertyTableEntry : public PLDHashEntryHdr
57 : {
58 : // both of these are arena-allocated
59 : const char *mKey;
60 : const PRUnichar *mValue;
61 : };
62 :
63 : static PRUnichar*
64 308798 : ArenaStrdup(const nsAFlatString& aString, PLArenaPool* aArena)
65 : {
66 : void *mem;
67 : // add one to include the null terminator
68 308798 : PRInt32 len = (aString.Length()+1) * sizeof(PRUnichar);
69 308798 : PL_ARENA_ALLOCATE(mem, aArena, len);
70 308798 : NS_ASSERTION(mem, "Couldn't allocate space!\n");
71 308798 : if (mem) {
72 308798 : memcpy(mem, aString.get(), len);
73 : }
74 308798 : return static_cast<PRUnichar*>(mem);
75 : }
76 :
77 : static char*
78 308798 : ArenaStrdup(const nsAFlatCString& aString, PLArenaPool* aArena)
79 : {
80 : void *mem;
81 : // add one to include the null terminator
82 308798 : PRInt32 len = (aString.Length()+1) * sizeof(char);
83 308798 : PL_ARENA_ALLOCATE(mem, aArena, len);
84 308798 : NS_ASSERTION(mem, "Couldn't allocate space!\n");
85 308798 : if (mem)
86 308798 : memcpy(mem, aString.get(), len);
87 308798 : return static_cast<char*>(mem);
88 : }
89 :
90 : static const struct PLDHashTableOps property_HashTableOps = {
91 : PL_DHashAllocTable,
92 : PL_DHashFreeTable,
93 : PL_DHashStringKey,
94 : PL_DHashMatchStringKey,
95 : PL_DHashMoveEntryStub,
96 : PL_DHashClearEntryStub,
97 : PL_DHashFinalizeStub,
98 : nsnull,
99 : };
100 :
101 : //
102 : // parser stuff
103 : //
104 : enum EParserState {
105 : eParserState_AwaitingKey,
106 : eParserState_Key,
107 : eParserState_AwaitingValue,
108 : eParserState_Value,
109 : eParserState_Comment
110 : };
111 :
112 : enum EParserSpecial {
113 : eParserSpecial_None, // not parsing a special character
114 : eParserSpecial_Escaped, // awaiting a special character
115 : eParserSpecial_Unicode // parsing a \Uxxx value
116 : };
117 :
118 : class nsPropertiesParser
119 2297 : {
120 : public:
121 2297 : nsPropertiesParser(nsIPersistentProperties* aProps) :
122 : mHaveMultiLine(false), mState(eParserState_AwaitingKey),
123 2297 : mProps(aProps) {}
124 :
125 308798 : void FinishValueState(nsAString& aOldValue) {
126 : static const char trimThese[] = " \t";
127 308798 : mKey.Trim(trimThese, false, true);
128 :
129 : // This is really ugly hack but it should be fast
130 : PRUnichar backup_char;
131 308798 : PRUint32 minLength = mMinLength;
132 308798 : if (minLength)
133 : {
134 1184 : backup_char = mValue[minLength-1];
135 1184 : mValue.SetCharAt('x', minLength-1);
136 : }
137 308798 : mValue.Trim(trimThese, false, true);
138 308798 : if (minLength)
139 1184 : mValue.SetCharAt(backup_char, minLength-1);
140 :
141 308798 : mProps->SetStringProperty(NS_ConvertUTF16toUTF8(mKey), mValue, aOldValue);
142 308798 : mSpecialState = eParserSpecial_None;
143 308798 : WaitForKey();
144 308798 : }
145 :
146 2295 : EParserState GetState() { return mState; }
147 :
148 : static NS_METHOD SegmentWriter(nsIUnicharInputStream* aStream,
149 : void* aClosure,
150 : const PRUnichar *aFromSegment,
151 : PRUint32 aToOffset,
152 : PRUint32 aCount,
153 : PRUint32 *aWriteCount);
154 :
155 : nsresult ParseBuffer(const PRUnichar* aBuffer, PRUint32 aBufferLength);
156 :
157 : private:
158 : bool ParseValueCharacter(
159 : PRUnichar c, // character that is just being parsed
160 : const PRUnichar* cur, // pointer to character c in the buffer
161 : const PRUnichar* &tokenStart, // string copying is done in blocks as big as
162 : // possible, tokenStart points to the beginning
163 : // of this block
164 : nsAString& oldValue); // when duplicate property is found, new value
165 : // is stored into hashtable and the old one is
166 : // placed in this variable
167 :
168 386881 : void WaitForKey() {
169 386881 : mState = eParserState_AwaitingKey;
170 386881 : }
171 :
172 308798 : void EnterKeyState() {
173 308798 : mKey.Truncate();
174 308798 : mState = eParserState_Key;
175 308798 : }
176 :
177 308798 : void WaitForValue() {
178 308798 : mState = eParserState_AwaitingValue;
179 308798 : }
180 :
181 308798 : void EnterValueState() {
182 308798 : mValue.Truncate();
183 308798 : mMinLength = 0;
184 308798 : mState = eParserState_Value;
185 308798 : mSpecialState = eParserSpecial_None;
186 308798 : }
187 :
188 78083 : void EnterCommentState() {
189 78083 : mState = eParserState_Comment;
190 78083 : }
191 :
192 : nsAutoString mKey;
193 : nsAutoString mValue;
194 :
195 : PRUint32 mUnicodeValuesRead; // should be 4!
196 : PRUnichar mUnicodeValue; // currently parsed unicode value
197 : bool mHaveMultiLine; // is TRUE when last processed characters form
198 : // any of following sequences:
199 : // - "\\\r"
200 : // - "\\\n"
201 : // - "\\\r\n"
202 : // - any sequence above followed by any
203 : // combination of ' ' and '\t'
204 : bool mMultiLineCanSkipN; // TRUE if "\\\r" was detected
205 : PRUint32 mMinLength; // limit right trimming at the end to not trim
206 : // escaped whitespaces
207 : EParserState mState;
208 : // if we see a '\' then we enter this special state
209 : EParserSpecial mSpecialState;
210 : nsIPersistentProperties* mProps;
211 : };
212 :
213 715299 : inline bool IsWhiteSpace(PRUnichar aChar)
214 : {
215 : return (aChar == ' ') || (aChar == '\t') ||
216 715299 : (aChar == '\r') || (aChar == '\n');
217 : }
218 :
219 395822 : inline bool IsEOL(PRUnichar aChar)
220 : {
221 395822 : return (aChar == '\r') || (aChar == '\n');
222 : }
223 :
224 :
225 9900711 : bool nsPropertiesParser::ParseValueCharacter(
226 : PRUnichar c, const PRUnichar* cur, const PRUnichar* &tokenStart,
227 : nsAString& oldValue)
228 : {
229 9900711 : switch (mSpecialState) {
230 :
231 : // the normal state - look for special characters
232 : case eParserSpecial_None:
233 9897539 : switch (c) {
234 : case '\\':
235 2999 : if (mHaveMultiLine)
236 : // there is nothing to append to mValue yet
237 1 : mHaveMultiLine = false;
238 : else
239 2998 : mValue += Substring(tokenStart, cur);
240 :
241 2999 : mSpecialState = eParserSpecial_Escaped;
242 2999 : break;
243 :
244 : case '\n':
245 : // if we detected multiline and got only "\\\r" ignore next "\n" if any
246 307425 : if (mHaveMultiLine && mMultiLineCanSkipN) {
247 : // but don't allow another '\n' to be skipped
248 1 : mMultiLineCanSkipN = false;
249 : // Now there is nothing to append to the mValue since we are skipping
250 : // whitespaces at the beginning of the new line of the multiline
251 : // property. Set tokenStart properly to ensure that nothing is appended
252 : // if we find regular line-end or the end of the buffer.
253 1 : tokenStart = cur+1;
254 1 : break;
255 : }
256 : // no break
257 :
258 : case '\r':
259 : // we're done! We have a key and value
260 307426 : mValue += Substring(tokenStart, cur);
261 307426 : FinishValueState(oldValue);
262 307426 : mHaveMultiLine = false;
263 307426 : break;
264 :
265 : default:
266 : // there is nothing to do with normal characters,
267 : // but handle multilines correctly
268 9587113 : if (mHaveMultiLine) {
269 11 : if (c == ' ' || c == '\t') {
270 : // don't allow another '\n' to be skipped
271 8 : mMultiLineCanSkipN = false;
272 : // Now there is nothing to append to the mValue since we are skipping
273 : // whitespaces at the beginning of the new line of the multiline
274 : // property. Set tokenStart properly to ensure that nothing is appended
275 : // if we find regular line-end or the end of the buffer.
276 8 : tokenStart = cur+1;
277 8 : break;
278 : }
279 3 : mHaveMultiLine = false;
280 3 : tokenStart = cur;
281 : }
282 9587105 : break; // from switch on (c)
283 : }
284 9897539 : break; // from switch on (mSpecialState)
285 :
286 : // saw a \ character, so parse the character after that
287 : case eParserSpecial_Escaped:
288 : // probably want to start parsing at the next token
289 : // other characters, like 'u' might override this
290 2999 : tokenStart = cur+1;
291 2999 : mSpecialState = eParserSpecial_None;
292 :
293 2999 : switch (c) {
294 :
295 : // the easy characters - \t, \n, and so forth
296 : case 't':
297 3 : mValue += PRUnichar('\t');
298 3 : mMinLength = mValue.Length();
299 3 : break;
300 : case 'n':
301 2946 : mValue += PRUnichar('\n');
302 2946 : mMinLength = mValue.Length();
303 2946 : break;
304 : case 'r':
305 1 : mValue += PRUnichar('\r');
306 1 : mMinLength = mValue.Length();
307 1 : break;
308 : case '\\':
309 0 : mValue += PRUnichar('\\');
310 0 : break;
311 :
312 : // switch to unicode mode!
313 : case 'u':
314 : case 'U':
315 44 : mSpecialState = eParserSpecial_Unicode;
316 44 : mUnicodeValuesRead = 0;
317 44 : mUnicodeValue = 0;
318 44 : break;
319 :
320 : // a \ immediately followed by a newline means we're going multiline
321 : case '\r':
322 : case '\n':
323 4 : mHaveMultiLine = true;
324 4 : mMultiLineCanSkipN = (c == '\r');
325 4 : mSpecialState = eParserSpecial_None;
326 4 : break;
327 :
328 : default:
329 : // don't recognize the character, so just append it
330 1 : mValue += c;
331 1 : break;
332 : }
333 2999 : break;
334 :
335 : // we're in the middle of parsing a 4-character unicode value
336 : // like \u5f39
337 : case eParserSpecial_Unicode:
338 :
339 173 : if(('0' <= c) && (c <= '9'))
340 : mUnicodeValue =
341 157 : (mUnicodeValue << 4) | (c - '0');
342 16 : else if(('a' <= c) && (c <= 'f'))
343 : mUnicodeValue =
344 4 : (mUnicodeValue << 4) | (c - 'a' + 0x0a);
345 12 : else if(('A' <= c) && (c <= 'F'))
346 : mUnicodeValue =
347 10 : (mUnicodeValue << 4) | (c - 'A' + 0x0a);
348 : else {
349 : // non-hex character. Append what we have, and move on.
350 2 : mValue += mUnicodeValue;
351 2 : mMinLength = mValue.Length();
352 2 : mSpecialState = eParserSpecial_None;
353 :
354 : // leave tokenStart at this unknown character, so it gets appended
355 2 : tokenStart = cur;
356 :
357 : // ensure parsing this non-hex character again
358 2 : return false;
359 : }
360 :
361 171 : if (++mUnicodeValuesRead >= 4) {
362 42 : tokenStart = cur+1;
363 42 : mSpecialState = eParserSpecial_None;
364 42 : mValue += mUnicodeValue;
365 42 : mMinLength = mValue.Length();
366 : }
367 :
368 171 : break;
369 : }
370 :
371 9900709 : return true;
372 : }
373 :
374 6772 : NS_METHOD nsPropertiesParser::SegmentWriter(nsIUnicharInputStream* aStream,
375 : void* aClosure,
376 : const PRUnichar *aFromSegment,
377 : PRUint32 aToOffset,
378 : PRUint32 aCount,
379 : PRUint32 *aWriteCount)
380 : {
381 : nsPropertiesParser *parser =
382 6772 : static_cast<nsPropertiesParser *>(aClosure);
383 :
384 6772 : parser->ParseBuffer(aFromSegment, aCount);
385 :
386 6772 : *aWriteCount = aCount;
387 6772 : return NS_OK;
388 : }
389 :
390 6772 : nsresult nsPropertiesParser::ParseBuffer(const PRUnichar* aBuffer,
391 : PRUint32 aBufferLength)
392 : {
393 6772 : const PRUnichar* cur = aBuffer;
394 6772 : const PRUnichar* end = aBuffer + aBufferLength;
395 :
396 : // points to the start/end of the current key or value
397 6772 : const PRUnichar* tokenStart = nsnull;
398 :
399 : // if we're in the middle of parsing a key or value, make sure
400 : // the current token points to the beginning of the current buffer
401 6772 : if (mState == eParserState_Key ||
402 : mState == eParserState_Value) {
403 3885 : tokenStart = aBuffer;
404 : }
405 :
406 13544 : nsAutoString oldValue;
407 :
408 22743452 : while (cur != end) {
409 :
410 22729908 : PRUnichar c = *cur;
411 :
412 22729908 : switch (mState) {
413 : case eParserState_AwaitingKey:
414 398932 : if (c == '#' || c == '!')
415 78083 : EnterCommentState();
416 :
417 320849 : else if (!IsWhiteSpace(c)) {
418 : // not a comment, not whitespace, we must have found a key!
419 308798 : EnterKeyState();
420 308798 : tokenStart = cur;
421 : }
422 398932 : break;
423 :
424 : case eParserState_Key:
425 8658058 : if (c == '=' || c == ':') {
426 308798 : mKey += Substring(tokenStart, cur);
427 308798 : WaitForValue();
428 : }
429 8658058 : break;
430 :
431 : case eParserState_AwaitingValue:
432 395822 : if (IsEOL(c)) {
433 : // no value at all! mimic the normal value-ending
434 1372 : EnterValueState();
435 1372 : FinishValueState(oldValue);
436 : }
437 :
438 : // ignore white space leading up to the value
439 394450 : else if (!IsWhiteSpace(c)) {
440 307426 : tokenStart = cur;
441 307426 : EnterValueState();
442 :
443 : // make sure to handle this first character
444 307426 : if (ParseValueCharacter(c, cur, tokenStart, oldValue))
445 307426 : cur++;
446 : // If the character isn't consumed, don't do cur++ and parse
447 : // the character again. This can happen f.e. for char 'X' in sequence
448 : // "\u00X". This character can be control character and must be
449 : // processed again.
450 307426 : continue;
451 : }
452 88396 : break;
453 :
454 : case eParserState_Value:
455 9593285 : if (ParseValueCharacter(c, cur, tokenStart, oldValue))
456 9593283 : cur++;
457 : // See few lines above for reason of doing this
458 9593285 : continue;
459 :
460 : case eParserState_Comment:
461 : // stay in this state till we hit EOL
462 3683811 : if (c == '\r' || c== '\n')
463 78083 : WaitForKey();
464 3683811 : break;
465 : }
466 :
467 : // finally, advance to the next character
468 12829197 : cur++;
469 : }
470 :
471 : // if we're still parsing the value and are in eParserSpecial_None, then
472 : // append whatever we have..
473 6772 : if (mState == eParserState_Value && tokenStart &&
474 : mSpecialState == eParserSpecial_None) {
475 1990 : mValue += Substring(tokenStart, cur);
476 : }
477 : // if we're still parsing the key, then append whatever we have..
478 4782 : else if (mState == eParserState_Key && tokenStart) {
479 1894 : mKey += Substring(tokenStart, cur);
480 : }
481 :
482 6772 : return NS_OK;
483 : }
484 :
485 2297 : nsPersistentProperties::nsPersistentProperties()
486 2297 : : mIn(nsnull)
487 : {
488 2297 : mSubclass = static_cast<nsIPersistentProperties*>(this);
489 2297 : mTable.ops = nsnull;
490 2297 : PL_INIT_ARENA_POOL(&mArena, "PersistentPropertyArena", 2048);
491 2297 : }
492 :
493 4594 : nsPersistentProperties::~nsPersistentProperties()
494 : {
495 2297 : PL_FinishArenaPool(&mArena);
496 2297 : if (mTable.ops)
497 2297 : PL_DHashTableFinish(&mTable);
498 2297 : }
499 :
500 : nsresult
501 2297 : nsPersistentProperties::Init()
502 : {
503 2297 : if (!PL_DHashTableInit(&mTable, &property_HashTableOps, nsnull,
504 2297 : sizeof(PropertyTableEntry), 20)) {
505 0 : mTable.ops = nsnull;
506 0 : return NS_ERROR_OUT_OF_MEMORY;
507 : }
508 2297 : return NS_OK;
509 : }
510 :
511 : nsresult
512 2297 : nsPersistentProperties::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
513 : {
514 2297 : if (aOuter)
515 0 : return NS_ERROR_NO_AGGREGATION;
516 2297 : nsPersistentProperties* props = new nsPersistentProperties();
517 2297 : if (props == nsnull)
518 0 : return NS_ERROR_OUT_OF_MEMORY;
519 :
520 2297 : NS_ADDREF(props);
521 2297 : nsresult rv = props->Init();
522 2297 : if (NS_SUCCEEDED(rv))
523 2297 : rv = props->QueryInterface(aIID, aResult);
524 :
525 2297 : NS_RELEASE(props);
526 2297 : return rv;
527 : }
528 :
529 20691 : NS_IMPL_THREADSAFE_ISUPPORTS2(nsPersistentProperties, nsIPersistentProperties, nsIProperties)
530 :
531 : NS_IMETHODIMP
532 2297 : nsPersistentProperties::Load(nsIInputStream *aIn)
533 : {
534 2297 : nsresult rv = nsSimpleUnicharStreamFactory::GetInstance()->
535 2297 : CreateInstanceFromUTF8Stream(aIn, getter_AddRefs(mIn));
536 :
537 2297 : if (rv != NS_OK) {
538 0 : NS_WARNING("Error creating UnicharInputStream");
539 0 : return NS_ERROR_FAILURE;
540 : }
541 :
542 4594 : nsPropertiesParser parser(mSubclass);
543 :
544 : PRUint32 nProcessed;
545 : // If this 4096 is changed to some other value, make sure to adjust
546 : // the bug121341.properties test file accordingly.
547 2297 : while (NS_SUCCEEDED(rv = mIn->ReadSegments(nsPropertiesParser::SegmentWriter, &parser, 4096, &nProcessed)) &&
548 : nProcessed != 0);
549 2297 : mIn = nsnull;
550 2297 : if (NS_FAILED(rv))
551 2 : return rv;
552 :
553 : // We may have an unprocessed value at this point
554 : // if the last line did not have a proper line ending.
555 2295 : if (parser.GetState() == eParserState_Value) {
556 0 : nsAutoString oldValue;
557 0 : parser.FinishValueState(oldValue);
558 : }
559 :
560 2295 : return NS_OK;
561 : }
562 :
563 : NS_IMETHODIMP
564 308798 : nsPersistentProperties::SetStringProperty(const nsACString& aKey,
565 : const nsAString& aNewValue,
566 : nsAString& aOldValue)
567 : {
568 617596 : const nsAFlatCString& flatKey = PromiseFlatCString(aKey);
569 : PropertyTableEntry *entry =
570 : static_cast<PropertyTableEntry*>
571 308798 : (PL_DHashTableOperate(&mTable, flatKey.get(), PL_DHASH_ADD));
572 :
573 308798 : if (entry->mKey) {
574 2 : aOldValue = entry->mValue;
575 4 : NS_WARNING(nsPrintfCString(aKey.Length() + 30,
576 : "the property %s already exists\n",
577 2 : flatKey.get()).get());
578 : }
579 : else {
580 308796 : aOldValue.Truncate();
581 : }
582 :
583 308798 : entry->mKey = ArenaStrdup(flatKey, &mArena);
584 308798 : entry->mValue = ArenaStrdup(PromiseFlatString(aNewValue), &mArena);
585 :
586 308798 : return NS_OK;
587 : }
588 :
589 : NS_IMETHODIMP
590 0 : nsPersistentProperties::Save(nsIOutputStream* aOut, const nsACString& aHeader)
591 : {
592 0 : return NS_ERROR_NOT_IMPLEMENTED;
593 : }
594 :
595 : NS_IMETHODIMP
596 0 : nsPersistentProperties::Subclass(nsIPersistentProperties* aSubclass)
597 : {
598 0 : if (aSubclass) {
599 0 : mSubclass = aSubclass;
600 : }
601 :
602 0 : return NS_OK;
603 : }
604 :
605 : NS_IMETHODIMP
606 23416 : nsPersistentProperties::GetStringProperty(const nsACString& aKey,
607 : nsAString& aValue)
608 : {
609 46832 : const nsAFlatCString& flatKey = PromiseFlatCString(aKey);
610 :
611 : PropertyTableEntry *entry =
612 : static_cast<PropertyTableEntry*>
613 23416 : (PL_DHashTableOperate(&mTable, flatKey.get(), PL_DHASH_LOOKUP));
614 :
615 23416 : if (PL_DHASH_ENTRY_IS_FREE(entry))
616 16251 : return NS_ERROR_FAILURE;
617 :
618 7165 : aValue = entry->mValue;
619 7165 : return NS_OK;
620 : }
621 :
622 : static PLDHashOperator
623 0 : AddElemToArray(PLDHashTable* table, PLDHashEntryHdr *hdr,
624 : PRUint32 i, void *arg)
625 : {
626 0 : nsISupportsArray *propArray = (nsISupportsArray *) arg;
627 : PropertyTableEntry* entry =
628 0 : static_cast<PropertyTableEntry*>(hdr);
629 :
630 : nsPropertyElement *element =
631 0 : new nsPropertyElement(nsDependentCString(entry->mKey),
632 0 : nsDependentString(entry->mValue));
633 0 : if (!element)
634 0 : return PL_DHASH_STOP;
635 :
636 0 : propArray->InsertElementAt(element, i);
637 :
638 0 : return PL_DHASH_NEXT;
639 : }
640 :
641 :
642 : NS_IMETHODIMP
643 0 : nsPersistentProperties::Enumerate(nsISimpleEnumerator** aResult)
644 : {
645 0 : nsCOMPtr<nsISupportsArray> propArray;
646 0 : nsresult rv = NS_NewISupportsArray(getter_AddRefs(propArray));
647 0 : if (NS_FAILED(rv))
648 0 : return rv;
649 :
650 : // We know the necessary size; we can avoid growing it while adding elements
651 0 : if (!propArray->SizeTo(mTable.entryCount))
652 0 : return NS_ERROR_OUT_OF_MEMORY;
653 :
654 : // Step through hash entries populating a transient array
655 : PRUint32 n =
656 0 : PL_DHashTableEnumerate(&mTable, AddElemToArray, (void *)propArray);
657 0 : if (n < mTable.entryCount)
658 0 : return NS_ERROR_OUT_OF_MEMORY;
659 :
660 0 : return NS_NewArrayEnumerator(aResult, propArray);
661 : }
662 :
663 : ////////////////////////////////////////////////////////////////////////////////
664 : // XXX Some day we'll unify the nsIPersistentProperties interface with
665 : // nsIProperties, but until now...
666 :
667 : NS_IMETHODIMP
668 0 : nsPersistentProperties::Get(const char* prop, const nsIID & uuid, void* *result)
669 : {
670 0 : return NS_ERROR_NOT_IMPLEMENTED;
671 : }
672 :
673 : NS_IMETHODIMP
674 0 : nsPersistentProperties::Set(const char* prop, nsISupports* value)
675 : {
676 0 : return NS_ERROR_NOT_IMPLEMENTED;
677 : }
678 : NS_IMETHODIMP
679 0 : nsPersistentProperties::Undefine(const char* prop)
680 : {
681 0 : return NS_ERROR_NOT_IMPLEMENTED;
682 : }
683 :
684 : NS_IMETHODIMP
685 0 : nsPersistentProperties::Has(const char* prop, bool *result)
686 : {
687 : PropertyTableEntry *entry =
688 : static_cast<PropertyTableEntry*>
689 0 : (PL_DHashTableOperate(&mTable, prop, PL_DHASH_LOOKUP));
690 :
691 0 : *result = (entry && PL_DHASH_ENTRY_IS_BUSY(entry));
692 :
693 0 : return NS_OK;
694 : }
695 :
696 : NS_IMETHODIMP
697 0 : nsPersistentProperties::GetKeys(PRUint32 *count, char ***keys)
698 : {
699 0 : return NS_ERROR_NOT_IMPLEMENTED;
700 : }
701 :
702 : ////////////////////////////////////////////////////////////////////////////////
703 : // PropertyElement
704 : ////////////////////////////////////////////////////////////////////////////////
705 :
706 : NS_METHOD
707 0 : nsPropertyElement::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
708 : {
709 0 : if (aOuter)
710 0 : return NS_ERROR_NO_AGGREGATION;
711 0 : nsPropertyElement* propElem = new nsPropertyElement();
712 0 : if (propElem == nsnull)
713 0 : return NS_ERROR_OUT_OF_MEMORY;
714 0 : NS_ADDREF(propElem);
715 0 : nsresult rv = propElem->QueryInterface(aIID, aResult);
716 0 : NS_RELEASE(propElem);
717 0 : return rv;
718 : }
719 :
720 0 : NS_IMPL_ISUPPORTS1(nsPropertyElement, nsIPropertyElement)
721 :
722 : NS_IMETHODIMP
723 0 : nsPropertyElement::GetKey(nsACString& aReturnKey)
724 : {
725 0 : aReturnKey = mKey;
726 0 : return NS_OK;
727 : }
728 :
729 : NS_IMETHODIMP
730 0 : nsPropertyElement::GetValue(nsAString& aReturnValue)
731 : {
732 0 : aReturnValue = mValue;
733 0 : return NS_OK;
734 : }
735 :
736 : NS_IMETHODIMP
737 0 : nsPropertyElement::SetKey(const nsACString& aKey)
738 : {
739 0 : mKey = aKey;
740 0 : return NS_OK;
741 : }
742 :
743 : NS_IMETHODIMP
744 0 : nsPropertyElement::SetValue(const nsAString& aValue)
745 : {
746 0 : mValue = aValue;
747 0 : return NS_OK;
748 : }
749 :
750 : ////////////////////////////////////////////////////////////////////////////////
751 :
|