1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 : *
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 "nsEntityConverter.h"
39 : #include "nsIProperties.h"
40 : #include "nsIServiceManager.h"
41 : #include "nsIComponentManager.h"
42 : #include "nsReadableUtils.h"
43 : #include "nsCRT.h"
44 : #include "nsLiteralString.h"
45 : #include "nsXPIDLString.h"
46 : #include "nsString.h"
47 : #include "nsUnicharUtils.h"
48 : #include "mozilla/Services.h"
49 :
50 : //
51 : // implementation methods
52 : //
53 2 : nsEntityConverter::nsEntityConverter()
54 : : mVersionList(NULL),
55 2 : mVersionListLength(0)
56 : {
57 2 : }
58 :
59 4 : nsEntityConverter::~nsEntityConverter()
60 : {
61 2 : if (NULL != mVersionList) delete [] mVersionList;
62 8 : }
63 :
64 : NS_IMETHODIMP
65 2 : nsEntityConverter::LoadVersionPropertyFile()
66 : {
67 4 : NS_NAMED_LITERAL_CSTRING(url, "resource://gre/res/entityTables/htmlEntityVersions.properties");
68 :
69 : nsCOMPtr<nsIStringBundleService> bundleService =
70 4 : mozilla::services::GetStringBundleService();
71 2 : if (!bundleService)
72 0 : return NS_ERROR_FAILURE;
73 :
74 4 : nsCOMPtr<nsIStringBundle> entities;
75 2 : nsresult rv = bundleService->CreateBundle(url.get(), getter_AddRefs(entities));
76 2 : if (NS_FAILED(rv)) return rv;
77 :
78 : PRInt32 result;
79 :
80 4 : nsAutoString key;
81 4 : nsXPIDLString value;
82 4 : rv = entities->GetStringFromName(NS_LITERAL_STRING("length").get(),
83 4 : getter_Copies(value));
84 2 : NS_ASSERTION(NS_SUCCEEDED(rv),"nsEntityConverter: malformed entity table\n");
85 2 : if (NS_FAILED(rv)) return rv;
86 :
87 2 : mVersionListLength = nsAutoString(value).ToInteger(&result);
88 2 : NS_ASSERTION(32 >= mVersionListLength,"nsEntityConverter: malformed entity table\n");
89 2 : if (32 < mVersionListLength) return NS_ERROR_FAILURE;
90 :
91 4 : mVersionList = new nsEntityVersionList[mVersionListLength];
92 2 : if (!mVersionList) return NS_ERROR_OUT_OF_MEMORY;
93 :
94 12 : for (PRUint32 i = 0; i < mVersionListLength && NS_SUCCEEDED(rv); i++) {
95 10 : key.SetLength(0);
96 10 : key.AppendInt(i+1, 10);
97 10 : rv = entities->GetStringFromName(key.get(), getter_Copies(value));
98 10 : PRUint32 len = value.Length();
99 10 : if (kVERSION_STRING_LEN < len) return NS_ERROR_UNEXPECTED;
100 :
101 10 : memcpy(mVersionList[i].mEntityListName, value.get(), len*sizeof(PRUnichar));
102 10 : mVersionList[i].mEntityListName[len] = 0;
103 10 : mVersionList[i].mVersion = (1 << i);
104 : }
105 :
106 2 : return NS_OK;
107 : }
108 :
109 : already_AddRefed<nsIStringBundle>
110 2 : nsEntityConverter::LoadEntityBundle(PRUint32 version)
111 : {
112 4 : nsCAutoString url(NS_LITERAL_CSTRING("resource://gre/res/entityTables/"));
113 2 : const PRUnichar *versionName = NULL;
114 : nsresult rv;
115 :
116 : nsCOMPtr<nsIStringBundleService> bundleService =
117 4 : do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
118 2 : if (NS_FAILED(rv)) return NULL;
119 :
120 2 : versionName = GetVersionName(version);
121 2 : if (NULL == versionName) return NULL;
122 :
123 : // all property file names are ASCII, like "html40Latin1" so this is safe
124 2 : LossyAppendUTF16toASCII(versionName, url);
125 2 : url.Append(".properties");
126 :
127 : nsIStringBundle* bundle;
128 2 : rv = bundleService->CreateBundle(url.get(), &bundle);
129 2 : if (NS_FAILED(rv)) return NULL;
130 :
131 : // does this addref right?
132 2 : return bundle;
133 : }
134 :
135 : const PRUnichar*
136 2 : nsEntityConverter:: GetVersionName(PRUint32 versionNumber)
137 : {
138 8 : for (PRUint32 i = 0; i < mVersionListLength; i++) {
139 8 : if (versionNumber == mVersionList[i].mVersion)
140 2 : return mVersionList[i].mEntityListName;
141 : }
142 :
143 0 : return NULL;
144 : }
145 :
146 : nsIStringBundle*
147 36 : nsEntityConverter:: GetVersionBundleInstance(PRUint32 versionNumber)
148 : {
149 36 : if (NULL == mVersionList) {
150 : // load the property file which contains available version names
151 : // and generate a list of version/name pair
152 2 : nsresult rv = LoadVersionPropertyFile();
153 2 : if (NS_FAILED(rv)) return NULL;
154 : }
155 :
156 : PRUint32 i;
157 144 : for (i = 0; i < mVersionListLength; i++) {
158 144 : if (versionNumber == mVersionList[i].mVersion) {
159 36 : if (!mVersionList[i].mEntities)
160 : { // not loaded
161 : // load the property file
162 2 : mVersionList[i].mEntities = LoadEntityBundle(versionNumber);
163 2 : NS_ASSERTION(mVersionList[i].mEntities, "LoadEntityBundle failed");
164 : }
165 36 : return mVersionList[i].mEntities.get();
166 : }
167 : }
168 :
169 0 : return NULL;
170 : }
171 :
172 :
173 : //
174 : // nsISupports methods
175 : //
176 16 : NS_IMPL_ISUPPORTS1(nsEntityConverter,nsIEntityConverter)
177 :
178 :
179 : //
180 : // nsIEntityConverter
181 : //
182 : NS_IMETHODIMP
183 0 : nsEntityConverter::ConvertToEntity(PRUnichar character, PRUint32 entityVersion, char **_retval)
184 : {
185 0 : return ConvertUTF32ToEntity((PRUint32)character, entityVersion, _retval);
186 : }
187 :
188 : NS_IMETHODIMP
189 36 : nsEntityConverter::ConvertUTF32ToEntity(PRUint32 character, PRUint32 entityVersion, char **_retval)
190 : {
191 36 : NS_ASSERTION(_retval, "null ptr- _retval");
192 36 : if(nsnull == _retval)
193 0 : return NS_ERROR_NULL_POINTER;
194 36 : *_retval = NULL;
195 :
196 144 : for (PRUint32 mask = 1, mask2 = 0xFFFFFFFFL; (0!=(entityVersion & mask2)); mask<<=1, mask2<<=1) {
197 144 : if (0 == (entityVersion & mask))
198 108 : continue;
199 36 : nsIStringBundle* entities = GetVersionBundleInstance(entityVersion & mask);
200 36 : NS_ASSERTION(entities, "Cannot get the property file");
201 :
202 36 : if (NULL == entities)
203 0 : continue;
204 :
205 72 : nsAutoString key(NS_LITERAL_STRING("entity."));
206 36 : key.AppendInt(character,10);
207 :
208 72 : nsXPIDLString value;
209 36 : nsresult rv = entities->GetStringFromName(key.get(), getter_Copies(value));
210 36 : if (NS_SUCCEEDED(rv)) {
211 36 : *_retval = ToNewCString(value);
212 36 : if(nsnull == *_retval)
213 0 : return NS_ERROR_OUT_OF_MEMORY;
214 : else
215 36 : return NS_OK;
216 : }
217 : }
218 0 : return NS_ERROR_ILLEGAL_VALUE;
219 : }
220 :
221 : NS_IMETHODIMP
222 0 : nsEntityConverter::ConvertToEntities(const PRUnichar *inString, PRUint32 entityVersion, PRUnichar **_retval)
223 : {
224 0 : NS_ASSERTION(inString, "null ptr- inString");
225 0 : NS_ASSERTION(_retval, "null ptr- _retval");
226 0 : if((nsnull == inString) || (nsnull == _retval))
227 0 : return NS_ERROR_NULL_POINTER;
228 0 : *_retval = NULL;
229 :
230 0 : const PRUnichar *entity = NULL;
231 0 : nsString outString;
232 :
233 : // per character look for the entity
234 0 : PRUint32 len = nsCRT::strlen(inString);
235 0 : for (PRUint32 i = 0; i < len; i++) {
236 0 : nsAutoString key(NS_LITERAL_STRING("entity."));
237 0 : if (NS_IS_HIGH_SURROGATE(inString[i]) &&
238 : i + 2 < len &&
239 0 : NS_IS_LOW_SURROGATE(inString[i + 1])) {
240 0 : key.AppendInt(SURROGATE_TO_UCS4(inString[i], inString[i+1]), 10);
241 0 : ++i;
242 : }
243 : else {
244 0 : key.AppendInt(inString[i],10);
245 : }
246 :
247 0 : nsXPIDLString value;
248 :
249 0 : entity = NULL;
250 0 : for (PRUint32 mask = 1, mask2 = 0xFFFFFFFFL; (0!=(entityVersion & mask2)); mask<<=1, mask2<<=1) {
251 0 : if (0 == (entityVersion & mask))
252 0 : continue;
253 0 : nsIStringBundle* entities = GetVersionBundleInstance(entityVersion & mask);
254 0 : NS_ASSERTION(entities, "Cannot get the property file");
255 :
256 0 : if (NULL == entities)
257 0 : continue;
258 :
259 : nsresult rv = entities->GetStringFromName(key.get(),
260 0 : getter_Copies(value));
261 0 : if (NS_SUCCEEDED(rv)) {
262 0 : entity = value.get();
263 0 : break;
264 : }
265 : }
266 0 : if (NULL != entity) {
267 0 : outString.Append(entity);
268 : }
269 : else {
270 0 : outString.Append(&inString[i], 1);
271 : }
272 : }
273 :
274 0 : *_retval = ToNewUnicode(outString);
275 0 : if (NULL == *_retval)
276 0 : return NS_ERROR_OUT_OF_MEMORY;
277 :
278 0 : return NS_OK;
279 : }
|