/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is the string bundle override service. * * The Initial Developer of the Original Code is * Netscape Communications Corp. * Portions created by the Initial Developer are Copyright (C) 2002 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Alec Flett * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsStringBundleTextOverride.h" #include "nsString.h" #include "nsEscape.h" #include "nsNetUtil.h" #include "nsAppDirectoryServiceDefs.h" static NS_DEFINE_CID(kPersistentPropertiesCID, NS_IPERSISTENTPROPERTIES_CID); // first we need a simple class which wraps a nsIPropertyElement and // cuts out the leading URL from the key class URLPropertyElement : public nsIPropertyElement { public: URLPropertyElement(nsIPropertyElement *aRealElement, PRUint32 aURLLength) : mRealElement(aRealElement), mURLLength(aURLLength) { } virtual ~URLPropertyElement() {} NS_DECL_ISUPPORTS NS_DECL_NSIPROPERTYELEMENT private: nsCOMPtr mRealElement; PRUint32 mURLLength; }; NS_IMPL_ISUPPORTS1(URLPropertyElement, nsIPropertyElement) // we'll tweak the key on the way through, and remove the url prefix NS_IMETHODIMP URLPropertyElement::GetKey(nsACString& aKey) { nsresult rv = mRealElement->GetKey(aKey); if (NS_FAILED(rv)) return rv; // chop off the url aKey.Cut(0, mURLLength); return NS_OK; } // values are unaffected NS_IMETHODIMP URLPropertyElement::GetValue(nsAString& aValue) { return mRealElement->GetValue(aValue); } // setters are kind of strange, hopefully we'll never be called NS_IMETHODIMP URLPropertyElement::SetKey(const nsACString& aKey) { // this is just wrong - ideally you'd take the key, append it to // the url, and set that as the key. However, that would require // us to hold onto a copy of the string, and that's a waste, // considering nobody should ever be calling this. NS_ASSERTION(0, "This makes no sense!"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP URLPropertyElement::SetValue(const nsAString& aValue) { return mRealElement->SetValue(aValue); } // this is a special enumerator which returns only the elements which // are prefixed with a particular url class nsPropertyEnumeratorByURL : public nsISimpleEnumerator { public: nsPropertyEnumeratorByURL(const nsACString& aURL, nsISimpleEnumerator* aOuter) : mOuter(aOuter), mURL(aURL) { // prepare the url once so we can use its value later // persistent properties uses ":" as a delimiter, so escape // that character mURL.ReplaceSubstring(":", "%3A"); // there is always a # between the url and the real key mURL.Append('#'); } NS_DECL_ISUPPORTS NS_DECL_NSISIMPLEENUMERATOR virtual ~nsPropertyEnumeratorByURL() {} private: // actual enumerator of all strings from nsIProperties nsCOMPtr mOuter; // the current element that is valid for this url nsCOMPtr mCurrent; // the url in question, pre-escaped and with the # already in it nsCString mURL; }; // // nsStringBundleTextOverride implementation // NS_IMPL_ISUPPORTS1(nsStringBundleTextOverride, nsIStringBundleOverride) nsresult nsStringBundleTextOverride::Init() { nsresult rv; // check for existence of custom-strings.txt nsCOMPtr customStringsFile; rv = NS_GetSpecialDirectory(NS_APP_CHROME_DIR, getter_AddRefs(customStringsFile)); if (NS_FAILED(rv)) return rv; // bail if not found - this will cause the service creation to // bail as well, and cause this object to go away customStringsFile->AppendNative(NS_LITERAL_CSTRING("custom-strings.txt")); PRBool exists; rv = customStringsFile->Exists(&exists); if (NS_FAILED(rv) || !exists) return NS_ERROR_FAILURE; NS_WARNING("Using custom-strings.txt to override string bundles."); // read in the custom bundle. Keys are in the form // chrome://package/locale/foo.properties:keyname nsCAutoString customStringsURLSpec; rv = NS_GetURLSpecFromFile(customStringsFile, customStringsURLSpec); if (NS_FAILED(rv)) return rv; nsCOMPtr uri; rv = NS_NewURI(getter_AddRefs(uri), customStringsURLSpec); if (NS_FAILED(rv)) return rv; nsCOMPtr in; rv = NS_OpenURI(getter_AddRefs(in), uri); if (NS_FAILED(rv)) return rv; mValues = do_CreateInstance(kPersistentPropertiesCID, &rv); if (NS_FAILED(rv)) return rv; rv = mValues->Load(in); // turn this on to see the contents of custom-strings.txt #ifdef DEBUG_alecf nsCOMPtr enumerator; mValues->Enumerate(getter_AddRefs(enumerator)); NS_ASSERTION(enumerator, "no enumerator!\n"); printf("custom-strings.txt contains:\n"); printf("----------------------------\n"); PRBool hasMore; enumerator->HasMoreElements(&hasMore); do { nsCOMPtr sup; enumerator->GetNext(getter_AddRefs(sup)); nsCOMPtr prop = do_QueryInterface(sup); nsCAutoString key; nsAutoString value; prop->GetKey(key); prop->GetValue(value); printf("%s = '%s'\n", key.get(), NS_ConvertUTF16toUTF8(value).get()); enumerator->HasMoreElements(&hasMore); } while (hasMore); #endif return rv; } NS_IMETHODIMP nsStringBundleTextOverride::GetStringFromName(const nsACString& aURL, const nsACString& key, nsAString& aResult) { // concatenate url#key to get the key to read nsCAutoString combinedURL(aURL + NS_LITERAL_CSTRING("#") + key); // persistent properties uses ":" as a delimiter, so escape that character combinedURL.ReplaceSubstring(":", "%3A"); return mValues->GetStringProperty(combinedURL, aResult); } NS_IMETHODIMP nsStringBundleTextOverride::EnumerateKeysInBundle(const nsACString& aURL, nsISimpleEnumerator** aResult) { // enumerate all strings, and let the enumerator know nsCOMPtr enumerator; mValues->Enumerate(getter_AddRefs(enumerator)); // make the enumerator wrapper and pass it off nsPropertyEnumeratorByURL* propEnum = new nsPropertyEnumeratorByURL(aURL, enumerator); if (!propEnum) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(*aResult = propEnum); return NS_OK; } // // nsPropertyEnumeratorByURL implementation // NS_IMPL_ISUPPORTS1(nsPropertyEnumeratorByURL, nsISimpleEnumerator) NS_IMETHODIMP nsPropertyEnumeratorByURL::GetNext(nsISupports **aResult) { if (!mCurrent) return NS_ERROR_UNEXPECTED; // wrap mCurrent instead of returning it *aResult = new URLPropertyElement(mCurrent, mURL.Length()); NS_ADDREF(*aResult); // release it so we don't return it twice mCurrent = nsnull; return NS_OK; } NS_IMETHODIMP nsPropertyEnumeratorByURL::HasMoreElements(PRBool * aResult) { PRBool hasMore; mOuter->HasMoreElements(&hasMore); while (hasMore) { nsCOMPtr supports; mOuter->GetNext(getter_AddRefs(supports)); mCurrent = do_QueryInterface(supports); if (mCurrent) { nsCAutoString curKey; mCurrent->GetKey(curKey); if (StringBeginsWith(curKey, mURL)) break; } mOuter->HasMoreElements(&hasMore); } if (!hasMore) mCurrent = PR_FALSE; *aResult = mCurrent ? PR_TRUE : PR_FALSE; return NS_OK; }