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 Communicator client 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 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either of the GNU General Public License Version 2 or later (the "GPL"),
26 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "mozilla/Util.h"
39 :
40 : #include "nsHTMLEntities.h"
41 :
42 :
43 :
44 : #include "nsString.h"
45 : #include "nsCRT.h"
46 : #include "prtypes.h"
47 : #include "pldhash.h"
48 :
49 : using namespace mozilla;
50 :
51 : struct EntityNode {
52 : const char* mStr; // never owns buffer
53 : PRInt32 mUnicode;
54 : };
55 :
56 : struct EntityNodeEntry : public PLDHashEntryHdr
57 : {
58 : const EntityNode* node;
59 : };
60 :
61 : static bool
62 11 : matchNodeString(PLDHashTable*, const PLDHashEntryHdr* aHdr,
63 : const void* key)
64 : {
65 11 : const EntityNodeEntry* entry = static_cast<const EntityNodeEntry*>(aHdr);
66 11 : const char* str = static_cast<const char*>(key);
67 11 : return (nsCRT::strcmp(entry->node->mStr, str) == 0);
68 : }
69 :
70 : static bool
71 1578 : matchNodeUnicode(PLDHashTable*, const PLDHashEntryHdr* aHdr,
72 : const void* key)
73 : {
74 1578 : const EntityNodeEntry* entry = static_cast<const EntityNodeEntry*>(aHdr);
75 1578 : const PRInt32 ucode = NS_PTR_TO_INT32(key);
76 1578 : return (entry->node->mUnicode == ucode);
77 : }
78 :
79 : static PLDHashNumber
80 68117 : hashUnicodeValue(PLDHashTable*, const void* key)
81 : {
82 : // key is actually the unicode value
83 68117 : return PLDHashNumber(NS_PTR_TO_INT32(key));
84 : }
85 :
86 :
87 : static const PLDHashTableOps EntityToUnicodeOps = {
88 : PL_DHashAllocTable,
89 : PL_DHashFreeTable,
90 : PL_DHashStringKey,
91 : matchNodeString,
92 : PL_DHashMoveEntryStub,
93 : PL_DHashClearEntryStub,
94 : PL_DHashFinalizeStub,
95 : nsnull,
96 : };
97 :
98 : static const PLDHashTableOps UnicodeToEntityOps = {
99 : PL_DHashAllocTable,
100 : PL_DHashFreeTable,
101 : hashUnicodeValue,
102 : matchNodeUnicode,
103 : PL_DHashMoveEntryStub,
104 : PL_DHashClearEntryStub,
105 : PL_DHashFinalizeStub,
106 : nsnull,
107 : };
108 :
109 : static PLDHashTable gEntityToUnicode = { 0 };
110 : static PLDHashTable gUnicodeToEntity = { 0 };
111 : static nsrefcnt gTableRefCnt = 0;
112 :
113 : #define HTML_ENTITY(_name, _value) { #_name, _value },
114 : static const EntityNode gEntityArray[] = {
115 : #include "nsHTMLEntityList.h"
116 : };
117 : #undef HTML_ENTITY
118 :
119 : #define NS_HTML_ENTITY_COUNT ((PRInt32)ArrayLength(gEntityArray))
120 :
121 : nsresult
122 263 : nsHTMLEntities::AddRefTable(void)
123 : {
124 263 : if (!gTableRefCnt) {
125 263 : if (!PL_DHashTableInit(&gEntityToUnicode, &EntityToUnicodeOps,
126 : nsnull, sizeof(EntityNodeEntry),
127 263 : PRUint32(NS_HTML_ENTITY_COUNT / 0.75))) {
128 0 : gEntityToUnicode.ops = nsnull;
129 0 : return NS_ERROR_OUT_OF_MEMORY;
130 : }
131 263 : if (!PL_DHashTableInit(&gUnicodeToEntity, &UnicodeToEntityOps,
132 : nsnull, sizeof(EntityNodeEntry),
133 263 : PRUint32(NS_HTML_ENTITY_COUNT / 0.75))) {
134 0 : PL_DHashTableFinish(&gEntityToUnicode);
135 0 : gEntityToUnicode.ops = gUnicodeToEntity.ops = nsnull;
136 0 : return NS_ERROR_OUT_OF_MEMORY;
137 : }
138 68643 : for (const EntityNode *node = gEntityArray,
139 263 : *node_end = ArrayEnd(gEntityArray);
140 : node < node_end; ++node) {
141 :
142 : // add to Entity->Unicode table
143 : EntityNodeEntry* entry =
144 : static_cast<EntityNodeEntry*>
145 : (PL_DHashTableOperate(&gEntityToUnicode,
146 : node->mStr,
147 68117 : PL_DHASH_ADD));
148 68117 : NS_ASSERTION(entry, "Error adding an entry");
149 : // Prefer earlier entries when we have duplication.
150 68117 : if (!entry->node)
151 68117 : entry->node = node;
152 :
153 : // add to Unicode->Entity table
154 : entry = static_cast<EntityNodeEntry*>
155 : (PL_DHashTableOperate(&gUnicodeToEntity,
156 : NS_INT32_TO_PTR(node->mUnicode),
157 68117 : PL_DHASH_ADD));
158 68117 : NS_ASSERTION(entry, "Error adding an entry");
159 : // Prefer earlier entries when we have duplication.
160 68117 : if (!entry->node)
161 66539 : entry->node = node;
162 : }
163 : #ifdef DEBUG
164 263 : PL_DHashMarkTableImmutable(&gUnicodeToEntity);
165 263 : PL_DHashMarkTableImmutable(&gEntityToUnicode);
166 : #endif
167 : }
168 263 : ++gTableRefCnt;
169 263 : return NS_OK;
170 : }
171 :
172 : void
173 263 : nsHTMLEntities::ReleaseTable(void)
174 : {
175 263 : if (--gTableRefCnt != 0)
176 0 : return;
177 :
178 263 : if (gEntityToUnicode.ops) {
179 263 : PL_DHashTableFinish(&gEntityToUnicode);
180 263 : gEntityToUnicode.ops = nsnull;
181 : }
182 263 : if (gUnicodeToEntity.ops) {
183 263 : PL_DHashTableFinish(&gUnicodeToEntity);
184 263 : gUnicodeToEntity.ops = nsnull;
185 : }
186 :
187 : }
188 :
189 : PRInt32
190 11 : nsHTMLEntities::EntityToUnicode(const nsCString& aEntity)
191 : {
192 11 : NS_ASSERTION(gEntityToUnicode.ops, "no lookup table, needs addref");
193 11 : if (!gEntityToUnicode.ops)
194 0 : return -1;
195 :
196 : //this little piece of code exists because entities may or may not have the terminating ';'.
197 : //if we see it, strip if off for this test...
198 :
199 11 : if(';'==aEntity.Last()) {
200 0 : nsCAutoString temp(aEntity);
201 0 : temp.Truncate(aEntity.Length()-1);
202 0 : return EntityToUnicode(temp);
203 : }
204 :
205 : EntityNodeEntry* entry =
206 : static_cast<EntityNodeEntry*>
207 11 : (PL_DHashTableOperate(&gEntityToUnicode, aEntity.get(), PL_DHASH_LOOKUP));
208 :
209 11 : if (!entry || PL_DHASH_ENTRY_IS_FREE(entry))
210 0 : return -1;
211 :
212 11 : return entry->node->mUnicode;
213 : }
214 :
215 :
216 : PRInt32
217 11 : nsHTMLEntities::EntityToUnicode(const nsAString& aEntity) {
218 22 : nsCAutoString theEntity; theEntity.AssignWithConversion(aEntity);
219 11 : if(';'==theEntity.Last()) {
220 11 : theEntity.Truncate(theEntity.Length()-1);
221 : }
222 :
223 11 : return EntityToUnicode(theEntity);
224 : }
225 :
226 :
227 : const char*
228 0 : nsHTMLEntities::UnicodeToEntity(PRInt32 aUnicode)
229 : {
230 0 : NS_ASSERTION(gUnicodeToEntity.ops, "no lookup table, needs addref");
231 : EntityNodeEntry* entry =
232 : static_cast<EntityNodeEntry*>
233 0 : (PL_DHashTableOperate(&gUnicodeToEntity, NS_INT32_TO_PTR(aUnicode), PL_DHASH_LOOKUP));
234 :
235 0 : if (!entry || PL_DHASH_ENTRY_IS_FREE(entry))
236 0 : return nsnull;
237 :
238 0 : return entry->node->mStr;
239 : }
240 :
241 : #ifdef NS_DEBUG
242 : #include <stdio.h>
243 :
244 : class nsTestEntityTable {
245 : public:
246 : nsTestEntityTable() {
247 : PRInt32 value;
248 : nsHTMLEntities::AddRefTable();
249 :
250 : // Make sure we can find everything we are supposed to
251 : for (int i = 0; i < NS_HTML_ENTITY_COUNT; ++i) {
252 : nsAutoString entity; entity.AssignWithConversion(gEntityArray[i].mStr);
253 :
254 : value = nsHTMLEntities::EntityToUnicode(entity);
255 : NS_ASSERTION(value != -1, "can't find entity");
256 : NS_ASSERTION(value == gEntityArray[i].mUnicode, "bad unicode value");
257 :
258 : entity.AssignWithConversion(nsHTMLEntities::UnicodeToEntity(value));
259 : NS_ASSERTION(entity.EqualsASCII(gEntityArray[i].mStr), "bad entity name");
260 : }
261 :
262 : // Make sure we don't find things that aren't there
263 : value = nsHTMLEntities::EntityToUnicode(nsCAutoString("@"));
264 : NS_ASSERTION(value == -1, "found @");
265 : value = nsHTMLEntities::EntityToUnicode(nsCAutoString("zzzzz"));
266 : NS_ASSERTION(value == -1, "found zzzzz");
267 : nsHTMLEntities::ReleaseTable();
268 : }
269 : };
270 : //nsTestEntityTable validateEntityTable;
271 : #endif
272 :
|