Back to index

lightning-sunbird  0.9+nobinonly
nsRDFXMLSerializer.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org Code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1999
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Chris Waterson <waterson@netscape.com>
00025  *   Axel Hecht <axel@pike.org>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "nsRDFXMLSerializer.h"
00042 
00043 #include "nsIAtom.h"
00044 #include "nsIOutputStream.h"
00045 #include "nsIRDFService.h"
00046 #include "nsIRDFContainerUtils.h"
00047 #include "nsIServiceManager.h"
00048 #include "nsString.h"
00049 #include "nsXPIDLString.h"
00050 #include "nsVoidArray.h"
00051 #include "rdf.h"
00052 #include "rdfutil.h"
00053 
00054 #include "rdfIDataSource.h"
00055 
00056 #include "nsITimelineService.h"
00057 
00058 PRInt32 nsRDFXMLSerializer::gRefCnt = 0;
00059 nsIRDFContainerUtils* nsRDFXMLSerializer::gRDFC;
00060 nsIRDFResource* nsRDFXMLSerializer::kRDF_instanceOf;
00061 nsIRDFResource* nsRDFXMLSerializer::kRDF_type;
00062 nsIRDFResource* nsRDFXMLSerializer::kRDF_nextVal;
00063 nsIRDFResource* nsRDFXMLSerializer::kRDF_Bag;
00064 nsIRDFResource* nsRDFXMLSerializer::kRDF_Seq;
00065 nsIRDFResource* nsRDFXMLSerializer::kRDF_Alt;
00066 
00067 static const char kRDFDescriptionOpen[]      = "  <RDF:Description";
00068 static const char kIDAttr[]                  = " RDF:ID=\"";
00069 static const char kAboutAttr[]               = " RDF:about=\"";
00070 static const char kRDFDescriptionClose[]     = "  </RDF:Description>\n";
00071 static const char kRDFResource1[] = " RDF:resource=\"";
00072 static const char kRDFResource2[] = "\"/>\n";
00073 static const char kRDFParseTypeInteger[] = " NC:parseType=\"Integer\">";
00074 static const char kRDFParseTypeDate[] = " NC:parseType=\"Date\">";
00075 static const char kRDFUnknown[] = "><!-- unknown node type -->";
00076 
00077 NS_IMETHODIMP
00078 nsRDFXMLSerializer::Create(nsISupports* aOuter, REFNSIID aIID, void** aResult)
00079 {
00080     if (aOuter)
00081         return NS_ERROR_NO_AGGREGATION;
00082 
00083     nsCOMPtr<nsIRDFXMLSerializer> result = new nsRDFXMLSerializer();
00084     if (! result)
00085         return NS_ERROR_OUT_OF_MEMORY;
00086     // The serializer object is here, addref gRefCnt so that the
00087     // destructor can safely release it.
00088     gRefCnt++;
00089 
00090     nsresult rv;
00091     rv = result->QueryInterface(aIID, aResult);
00092 
00093     if (NS_FAILED(rv)) return rv;
00094 
00095     if (gRefCnt == 1) do {
00096         nsCOMPtr<nsIRDFService> rdf = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv);
00097         if (NS_FAILED(rv)) break;
00098 
00099         rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "instanceOf"),
00100                               &kRDF_instanceOf);
00101         if (NS_FAILED(rv)) break;
00102 
00103         rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "type"),
00104                               &kRDF_type);
00105         if (NS_FAILED(rv)) break;
00106 
00107         rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"),
00108                               &kRDF_nextVal);
00109         if (NS_FAILED(rv)) break;
00110 
00111         rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Bag"),
00112                               &kRDF_Bag);
00113         if (NS_FAILED(rv)) break;
00114 
00115         rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Seq"),
00116                               &kRDF_Seq);
00117         if (NS_FAILED(rv)) break;
00118 
00119         rv = rdf->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "Alt"),
00120                               &kRDF_Alt);
00121         if (NS_FAILED(rv)) break;
00122 
00123         rv = CallGetService("@mozilla.org/rdf/container-utils;1", &gRDFC);
00124         if (NS_FAILED(rv)) break;
00125     } while (0);
00126 
00127     return rv;
00128 }
00129 
00130 nsRDFXMLSerializer::nsRDFXMLSerializer()
00131 {
00132     MOZ_COUNT_CTOR(nsRDFXMLSerializer);
00133 }
00134 
00135 nsRDFXMLSerializer::~nsRDFXMLSerializer()
00136 {
00137     MOZ_COUNT_DTOR(nsRDFXMLSerializer);
00138 
00139     if (--gRefCnt == 0) {
00140         NS_IF_RELEASE(kRDF_Bag);
00141         NS_IF_RELEASE(kRDF_Seq);
00142         NS_IF_RELEASE(kRDF_Alt);
00143         NS_IF_RELEASE(kRDF_instanceOf);
00144         NS_IF_RELEASE(kRDF_type);
00145         NS_IF_RELEASE(kRDF_nextVal);
00146         NS_IF_RELEASE(gRDFC);
00147     }
00148 }
00149 
00150 NS_IMPL_ISUPPORTS2(nsRDFXMLSerializer, nsIRDFXMLSerializer, nsIRDFXMLSource)
00151 
00152 NS_IMETHODIMP
00153 nsRDFXMLSerializer::Init(nsIRDFDataSource* aDataSource)
00154 {
00155     if (! aDataSource)
00156         return NS_ERROR_NULL_POINTER;
00157 
00158     mDataSource = aDataSource;
00159     mDataSource->GetURI(getter_Copies(mBaseURLSpec));
00160 
00161     // Add the ``RDF'' prefix, by default.
00162     nsCOMPtr<nsIAtom> prefix;
00163 
00164     prefix = do_GetAtom("RDF");
00165     AddNameSpace(prefix, NS_LITERAL_STRING("http://www.w3.org/1999/02/22-rdf-syntax-ns#"));
00166 
00167     prefix = do_GetAtom("NC");
00168     AddNameSpace(prefix, NS_LITERAL_STRING("http://home.netscape.com/NC-rdf#"));
00169 
00170     mQNames.Init();
00171     mPrefixID = 0;
00172 
00173     return NS_OK;
00174 }
00175 
00176 NS_IMETHODIMP
00177 nsRDFXMLSerializer::AddNameSpace(nsIAtom* aPrefix, const nsAString& aURI)
00178 {
00179     nsCOMPtr<nsIAtom> prefix = aPrefix;
00180     if (!prefix) {
00181         // Make up a prefix, we don't want default namespaces, so
00182         // that we can use QNames for elements and attributes alike.
00183         prefix = EnsureNewPrefix();
00184     }
00185     mNameSpaces.Put(aURI, prefix);
00186     return NS_OK;
00187 }
00188 
00189 static nsresult
00190 rdf_BlockingWrite(nsIOutputStream* stream, const char* buf, PRUint32 size)
00191 {
00192     PRUint32 written = 0;
00193     PRUint32 remaining = size;
00194     while (remaining > 0) {
00195         nsresult rv;
00196         PRUint32 cb;
00197 
00198         if (NS_FAILED(rv = stream->Write(buf + written, remaining, &cb)))
00199             return rv;
00200 
00201         written += cb;
00202         remaining -= cb;
00203     }
00204     return NS_OK;
00205 }
00206 
00207 static nsresult
00208 rdf_BlockingWrite(nsIOutputStream* stream, const nsCSubstring& s)
00209 {
00210     return rdf_BlockingWrite(stream, s.BeginReading(), s.Length());
00211 }
00212 
00213 static nsresult
00214 rdf_BlockingWrite(nsIOutputStream* stream, const nsAString& s)
00215 {
00216     NS_ConvertUCS2toUTF8 utf8(s);
00217     return rdf_BlockingWrite(stream, utf8.get(), utf8.Length());
00218 }
00219 
00220 already_AddRefed<nsIAtom>
00221 nsRDFXMLSerializer::EnsureNewPrefix()
00222 {
00223     nsCAutoString qname;
00224     nsCOMPtr<nsIAtom> prefix;
00225     PRBool isNewPrefix;
00226     do {
00227         isNewPrefix = PR_TRUE;
00228         qname.AssignLiteral("NS");
00229         qname.AppendInt(++mPrefixID, 10);
00230         prefix = do_GetAtom(qname);
00231         nsNameSpaceMap::const_iterator iter = mNameSpaces.first();
00232         while (iter != mNameSpaces.last() && isNewPrefix) {
00233             isNewPrefix = (iter->mPrefix != prefix);
00234             ++iter;
00235         } 
00236     } while (!isNewPrefix);
00237     nsIAtom* outPrefix = nsnull;
00238     prefix.swap(outPrefix);
00239     return outPrefix;
00240 }
00241 
00242 // This converts a property resource (like
00243 // "http://www.w3.org/TR/WD-rdf-syntax#Description") into a QName
00244 // ("RDF:Description"), and registers the namespace, if it's made up.
00245 
00246 nsresult
00247 nsRDFXMLSerializer::RegisterQName(nsIRDFResource* aResource)
00248 {
00249     nsCAutoString uri, qname;
00250     aResource->GetValueUTF8(uri);
00251 
00252     nsNameSpaceMap::const_iterator iter = mNameSpaces.GetNameSpaceOf(uri);
00253     if (iter != mNameSpaces.last()) {
00254         NS_ENSURE_TRUE(iter->mPrefix, NS_ERROR_UNEXPECTED);
00255         iter->mPrefix->ToUTF8String(qname);
00256         qname.Append(':');
00257         qname += StringTail(uri, uri.Length() - iter->mURI.Length());
00258         return mQNames.Put(aResource, qname) ? NS_OK : NS_ERROR_FAILURE;
00259     }
00260 
00261     // Okay, so we don't have it in our map. Try to make one up. This
00262     // is very bogus.
00263     PRInt32 i = uri.RFindChar('#'); // first try a '#'
00264     if (i == -1) {
00265         i = uri.RFindChar('/');
00266         if (i == -1) {
00267             // Okay, just punt and assume there is _no_ namespace on
00268             // this thing...
00269             return mQNames.Put(aResource, uri) ? NS_OK : NS_ERROR_FAILURE;
00270         }
00271     }
00272 
00273     // Take whatever is to the right of the '#' or '/' and call it the
00274     // local name, make up a prefix.
00275     nsCOMPtr<nsIAtom> prefix = EnsureNewPrefix();
00276     mNameSpaces.Put(StringHead(uri, i+1), prefix);
00277     prefix->ToUTF8String(qname);
00278     qname.Append(':');
00279     qname += StringTail(uri, uri.Length() - (i + 1));
00280 
00281     return mQNames.Put(aResource, qname) ? NS_OK : NS_ERROR_FAILURE;
00282 }
00283 
00284 nsresult
00285 nsRDFXMLSerializer::GetQName(nsIRDFResource* aResource, nsCString& aQName)
00286 {
00287     return mQNames.Get(aResource, &aQName) ? NS_OK : NS_ERROR_UNEXPECTED;
00288 }
00289 
00290 PRBool
00291 nsRDFXMLSerializer::IsContainerProperty(nsIRDFResource* aProperty)
00292 {
00293     // Return `true' if the property is an internal property related
00294     // to being a container.
00295     if (aProperty == kRDF_instanceOf)
00296         return PR_TRUE;
00297 
00298     if (aProperty == kRDF_nextVal)
00299         return PR_TRUE;
00300 
00301     PRBool isOrdinal = PR_FALSE;
00302     gRDFC->IsOrdinalProperty(aProperty, &isOrdinal);
00303     if (isOrdinal)
00304         return PR_TRUE;
00305 
00306     return PR_FALSE;
00307 } 
00308 
00309 
00310 // convert '&', '<', and '>' into "&amp;", "&lt;", and "&gt", respectively.
00311 static const char amp[] = "&amp;";
00312 static const char lt[] = "&lt;";
00313 static const char gt[] = "&gt;";
00314 static const char quot[] = "&quot;";
00315 
00316 static void
00317 rdf_EscapeAmpersandsAndAngleBrackets(nsCString& s)
00318 {
00319     PRUint32 newLength, origLength;
00320     newLength = origLength = s.Length();
00321 
00322     // Compute the length of the result string.
00323     const char* start = s.BeginReading();
00324     const char* end = s.EndReading();
00325     const char* c = start;
00326     while (c != end) {
00327         switch (*c) {
00328         case '&' :
00329             newLength += sizeof(amp) - 2;
00330             break;
00331         case '<':
00332         case '>':
00333             newLength += sizeof(gt) - 2;
00334             break;
00335         default:
00336             break;
00337         }
00338         ++c;
00339     }
00340     if (newLength == origLength) {
00341         // nothing to escape
00342         return;
00343     }
00344 
00345     // escape the chars from the end back to the front.
00346     s.SetLength(newLength);
00347 
00348     // Buffer might have changed, get the pointers again
00349     start = s.BeginReading(); // begin of string
00350     c = start + origLength - 1; // last char in original string
00351     char* w = s.EndWriting() - 1; // last char in grown buffer
00352     while (c >= start) {
00353         switch (*c) {
00354         case '&' :
00355             w -= 4;
00356             nsCharTraits<char>::copy(w, amp, sizeof(amp) - 1);
00357             break;
00358         case '<':
00359             w -= 3;
00360             nsCharTraits<char>::copy(w, lt, sizeof(lt) - 1);
00361             break;
00362         case '>':
00363             w -= 3;
00364             nsCharTraits<char>::copy(w, gt, sizeof(gt) - 1);
00365             break;
00366         default:
00367             *w = *c;
00368         }
00369         --w;
00370         --c;
00371     }
00372 }
00373 
00374 // convert '"' to "&quot;"
00375 static void
00376 rdf_EscapeQuotes(nsCString& s)
00377 {
00378     PRInt32 i = 0;
00379     while ((i = s.FindChar('"', i)) != -1) {
00380         s.Replace(i, 1, quot, sizeof(quot) - 1);
00381         i += sizeof(quot) - 2;
00382     }
00383 }
00384 
00385 static void
00386 rdf_EscapeAttributeValue(nsCString& s)
00387 {
00388     rdf_EscapeAmpersandsAndAngleBrackets(s);
00389     rdf_EscapeQuotes(s);
00390 }
00391 
00392 
00393 nsresult
00394 nsRDFXMLSerializer::SerializeInlineAssertion(nsIOutputStream* aStream,
00395                                              nsIRDFResource* aResource,
00396                                              nsIRDFResource* aProperty,
00397                                              nsIRDFLiteral* aValue)
00398 {
00399     nsresult rv;
00400     nsCString qname;
00401     rv = GetQName(aProperty, qname);
00402     NS_ENSURE_SUCCESS(rv, rv);
00403 
00404     rv = rdf_BlockingWrite(aStream,
00405                            NS_LITERAL_CSTRING("\n                   "));
00406     if (NS_FAILED(rv)) return rv;
00407 
00408     const PRUnichar* value;
00409     aValue->GetValueConst(&value);
00410     NS_ConvertUTF16toUTF8 s(value);
00411 
00412     rdf_EscapeAttributeValue(s);
00413 
00414     rv = rdf_BlockingWrite(aStream, qname);
00415     if (NS_FAILED(rv)) return rv;
00416     rv = rdf_BlockingWrite(aStream, "=\"", 2);
00417     if (NS_FAILED(rv)) return rv;
00418     s.Append('"');
00419     return rdf_BlockingWrite(aStream, s);
00420 }
00421 
00422 nsresult
00423 nsRDFXMLSerializer::SerializeChildAssertion(nsIOutputStream* aStream,
00424                                             nsIRDFResource* aResource,
00425                                             nsIRDFResource* aProperty,
00426                                             nsIRDFNode* aValue)
00427 {
00428     nsCString qname;
00429     nsresult rv = GetQName(aProperty, qname);
00430     NS_ENSURE_SUCCESS(rv, rv);
00431 
00432     rv = rdf_BlockingWrite(aStream, "    <", 5);
00433     if (NS_FAILED(rv)) return rv;
00434     rv = rdf_BlockingWrite(aStream, qname);
00435     if (NS_FAILED(rv)) return rv;
00436 
00437     nsCOMPtr<nsIRDFResource> resource;
00438     nsCOMPtr<nsIRDFLiteral> literal;
00439     nsCOMPtr<nsIRDFInt> number;
00440     nsCOMPtr<nsIRDFDate> date;
00441 
00442     if ((resource = do_QueryInterface(aValue)) != nsnull) {
00443         nsCAutoString uri;
00444         resource->GetValueUTF8(uri);
00445 
00446         rdf_MakeRelativeRef(mBaseURLSpec, uri);
00447         rdf_EscapeAttributeValue(uri);
00448 
00449         rv = rdf_BlockingWrite(aStream, kRDFResource1,
00450                                sizeof(kRDFResource1) - 1);
00451         if (NS_FAILED(rv)) return rv;
00452         rv = rdf_BlockingWrite(aStream, uri);
00453         if (NS_FAILED(rv)) return rv;
00454         rv = rdf_BlockingWrite(aStream, kRDFResource2,
00455                                sizeof(kRDFResource2) - 1);
00456         if (NS_FAILED(rv)) return rv;
00457 
00458         goto no_close_tag;
00459     }
00460     else if ((literal = do_QueryInterface(aValue)) != nsnull) {
00461         const PRUnichar *value;
00462         literal->GetValueConst(&value);
00463         NS_ConvertUTF16toUTF8 s(value);
00464 
00465         rdf_EscapeAmpersandsAndAngleBrackets(s);
00466 
00467         rv = rdf_BlockingWrite(aStream, ">", 1);
00468         if (NS_FAILED(rv)) return rv;
00469         rv = rdf_BlockingWrite(aStream, s);
00470         if (NS_FAILED(rv)) return rv;
00471     }
00472     else if ((number = do_QueryInterface(aValue)) != nsnull) {
00473         PRInt32 value;
00474         number->GetValue(&value);
00475 
00476         nsCAutoString n;
00477         n.AppendInt(value);
00478 
00479         rv = rdf_BlockingWrite(aStream, kRDFParseTypeInteger, 
00480                                sizeof(kRDFParseTypeInteger) - 1);
00481         if (NS_FAILED(rv)) return rv;
00482         rv = rdf_BlockingWrite(aStream, n);
00483         if (NS_FAILED(rv)) return rv;
00484     }
00485     else if ((date = do_QueryInterface(aValue)) != nsnull) {
00486         PRTime value;
00487         date->GetValue(&value);
00488 
00489         nsCAutoString s;
00490         rdf_FormatDate(value, s);
00491 
00492         rv = rdf_BlockingWrite(aStream, kRDFParseTypeDate, 
00493                                sizeof(kRDFParseTypeDate) - 1);
00494         if (NS_FAILED(rv)) return rv;
00495         rv = rdf_BlockingWrite(aStream, s);
00496         if (NS_FAILED(rv)) return rv;
00497     }
00498     else {
00499         // XXX it doesn't support nsIRDFResource _or_ nsIRDFLiteral???
00500         // We should serialize nsIRDFInt, nsIRDFDate, etc...
00501         NS_WARNING("unknown RDF node type");
00502 
00503         rv = rdf_BlockingWrite(aStream, kRDFUnknown, sizeof(kRDFUnknown) - 1);
00504         if (NS_FAILED(rv)) return rv;
00505     }
00506 
00507     rv = rdf_BlockingWrite(aStream, "</", 2);
00508     if (NS_FAILED(rv)) return rv;
00509     rv = rdf_BlockingWrite(aStream, qname);
00510     if (NS_FAILED(rv)) return rv;
00511     return rdf_BlockingWrite(aStream, ">\n", 2);
00512 
00513  no_close_tag:
00514     return NS_OK;
00515 }
00516 
00517 nsresult
00518 nsRDFXMLSerializer::SerializeProperty(nsIOutputStream* aStream,
00519                                       nsIRDFResource* aResource,
00520                                       nsIRDFResource* aProperty,
00521                                       PRBool aInline,
00522                                       PRInt32* aSkipped)
00523 {
00524     nsresult rv = NS_OK;
00525 
00526     PRInt32 skipped = 0;
00527 
00528     nsCOMPtr<nsISimpleEnumerator> assertions;
00529     mDataSource->GetTargets(aResource, aProperty, PR_TRUE, getter_AddRefs(assertions));
00530     if (! assertions)
00531         return NS_ERROR_FAILURE;
00532 
00533     // Serializing the assertion inline is ok as long as the property has
00534     // only one target value, and it is a literal that doesn't include line
00535     // breaks.
00536     PRBool needsChild = PR_FALSE;
00537 
00538     while (1) {
00539         PRBool hasMore = PR_FALSE;
00540         assertions->HasMoreElements(&hasMore);
00541         if (! hasMore)
00542             break;
00543 
00544         nsCOMPtr<nsISupports> isupports;
00545         assertions->GetNext(getter_AddRefs(isupports));
00546         nsCOMPtr<nsIRDFLiteral> literal = do_QueryInterface(isupports);
00547         needsChild |= (!literal);
00548 
00549         if (!needsChild) {
00550             assertions->HasMoreElements(&needsChild);
00551             if (!needsChild) {
00552                 const PRUnichar* literalVal = nsnull;
00553                 literal->GetValueConst(&literalVal);
00554                 if (literalVal) {
00555                     for (; *literalVal; literalVal++) {
00556                         if (*literalVal == PRUnichar('\n') ||
00557                             *literalVal == PRUnichar('\r')) {
00558                             needsChild = PR_TRUE;
00559                             break;
00560                         }
00561                     }
00562                 }
00563             }
00564         }
00565 
00566         if (aInline && !needsChild) {
00567             rv = SerializeInlineAssertion(aStream, aResource, aProperty, literal);
00568         }
00569         else if (!aInline && needsChild) {
00570             nsCOMPtr<nsIRDFNode> value = do_QueryInterface(isupports);
00571             rv = SerializeChildAssertion(aStream, aResource, aProperty, value);
00572         }
00573         else {
00574             ++skipped;
00575             rv = NS_OK;
00576         }
00577 
00578         if (NS_FAILED(rv))
00579             break;
00580     }
00581 
00582     *aSkipped += skipped;
00583     return rv;
00584 }
00585 
00586 
00587 nsresult
00588 nsRDFXMLSerializer::SerializeDescription(nsIOutputStream* aStream,
00589                                          nsIRDFResource* aResource)
00590 {
00591     nsresult rv;
00592 
00593     PRBool isTypedNode = PR_FALSE;
00594     nsCString typeQName;
00595 
00596     nsCOMPtr<nsIRDFNode> typeNode;
00597     mDataSource->GetTarget(aResource, kRDF_type, PR_TRUE, getter_AddRefs(typeNode));
00598     if (typeNode) {
00599         nsCOMPtr<nsIRDFResource> type = do_QueryInterface(typeNode, &rv);
00600         if (type) {
00601             // Try to get a namespace prefix.  If none is available,
00602             // just treat the description as if it weren't a typed node 
00603             // after all and emit rdf:type as a normal property.  This 
00604             // seems preferable to using a bogus (invented) prefix.
00605             isTypedNode = NS_SUCCEEDED(GetQName(type, typeQName));
00606         }
00607     }
00608 
00609     nsCAutoString uri;
00610     rv = aResource->GetValueUTF8(uri);
00611     if (NS_FAILED(rv)) return rv;
00612 
00613     rdf_MakeRelativeRef(mBaseURLSpec, uri);
00614     rdf_EscapeAttributeValue(uri);
00615 
00616     // Emit an open tag and the subject
00617     if (isTypedNode) {
00618         rv = rdf_BlockingWrite(aStream, NS_LITERAL_STRING("  <"));
00619         if (NS_FAILED(rv)) return rv;
00620         // Watch out for the default namespace!
00621         rv = rdf_BlockingWrite(aStream, typeQName);
00622         if (NS_FAILED(rv)) return rv;
00623     }
00624     else {
00625         rv = rdf_BlockingWrite(aStream, kRDFDescriptionOpen,
00626                                sizeof(kRDFDescriptionOpen) - 1);
00627         if (NS_FAILED(rv)) return rv;
00628     }
00629     if (uri[0] == PRUnichar('#')) {
00630         uri.Cut(0, 1);
00631         rv = rdf_BlockingWrite(aStream, kIDAttr, sizeof(kIDAttr) - 1);
00632     }
00633     else {
00634         rv = rdf_BlockingWrite(aStream, kAboutAttr, sizeof(kAboutAttr) - 1);
00635     }
00636     if (NS_FAILED(rv)) return rv;
00637 
00638     uri.Append('"');
00639     rv = rdf_BlockingWrite(aStream, uri);
00640     if (NS_FAILED(rv)) return rv;
00641 
00642     // Any value that's a literal we can write out as an inline
00643     // attribute on the RDF:Description
00644     nsAutoVoidArray visited;
00645     PRInt32 skipped = 0;
00646 
00647     nsCOMPtr<nsISimpleEnumerator> arcs;
00648     mDataSource->ArcLabelsOut(aResource, getter_AddRefs(arcs));
00649 
00650     if (arcs) {
00651         // Don't re-serialize rdf:type later on
00652         if (isTypedNode)
00653             visited.AppendElement(kRDF_type);
00654 
00655         while (1) {
00656             PRBool hasMore = PR_FALSE;
00657             arcs->HasMoreElements(&hasMore);
00658             if (! hasMore)
00659                 break;
00660 
00661             nsCOMPtr<nsISupports> isupports;
00662             arcs->GetNext(getter_AddRefs(isupports));
00663 
00664             nsCOMPtr<nsIRDFResource> property = do_QueryInterface(isupports);
00665             if (! property)
00666                 continue;
00667 
00668             // Ignore properties that pertain to containers; we may be
00669             // called from SerializeContainer() if the container resource
00670             // has been assigned non-container properties.
00671             if (IsContainerProperty(property))
00672                 continue;
00673 
00674             // Only serialize values for the property once.
00675             if (visited.IndexOf(property.get()) >= 0)
00676                 continue;
00677 
00678             visited.AppendElement(property.get());
00679 
00680             SerializeProperty(aStream, aResource, property, PR_TRUE, &skipped);
00681         }
00682     }
00683 
00684     if (skipped) {
00685         // Close the RDF:Description tag.
00686         rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING(">\n"));
00687         if (NS_FAILED(rv)) return rv;
00688 
00689         // Now write out resources (which might have their own
00690         // substructure) as children.
00691         mDataSource->ArcLabelsOut(aResource, getter_AddRefs(arcs));
00692 
00693         if (arcs) {
00694             // Forget that we've visited anything
00695             visited.Clear();
00696             // ... except for rdf:type
00697             if (isTypedNode)
00698                 visited.AppendElement(kRDF_type);
00699 
00700             while (1) {
00701                 PRBool hasMore = PR_FALSE;
00702                 arcs->HasMoreElements(&hasMore);
00703                 if (! hasMore)
00704                     break;
00705 
00706                 nsCOMPtr<nsISupports> isupports;
00707                 arcs->GetNext(getter_AddRefs(isupports));
00708 
00709                 nsCOMPtr<nsIRDFResource> property = do_QueryInterface(isupports);
00710                 if (! property)
00711                     continue;
00712 
00713                 // Ignore properties that pertain to containers; we may be
00714                 // called from SerializeContainer() if the container
00715                 // resource has been assigned non-container properties.
00716                 if (IsContainerProperty(property))
00717                     continue;
00718 
00719                 // have we already seen this property?  If so, don't write it
00720                 // out again; serialize property will write each instance.
00721                 if (visited.IndexOf(property.get()) >= 0)
00722                     continue;
00723 
00724                 visited.AppendElement(property.get());
00725 
00726                 SerializeProperty(aStream, aResource, property, PR_FALSE, &skipped);
00727             }
00728         }
00729 
00730         // Emit a proper close-tag.
00731         if (isTypedNode) {
00732             rv = rdf_BlockingWrite(aStream,  NS_LITERAL_CSTRING("  </"));
00733             if (NS_FAILED(rv)) return rv;
00734             // Watch out for the default namespace!
00735             rdf_BlockingWrite(aStream, typeQName);
00736             if (NS_FAILED(rv)) return rv;
00737             rdf_BlockingWrite(aStream, ">\n", 2);
00738             if (NS_FAILED(rv)) return rv;
00739         }
00740         else {
00741             rv = rdf_BlockingWrite(aStream, kRDFDescriptionClose,
00742                                    sizeof(kRDFDescriptionClose) - 1);
00743             if (NS_FAILED(rv)) return rv;
00744         }
00745     }
00746     else {
00747         // If we saw _no_ child properties, then we can don't need a
00748         // close-tag.
00749         rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING(" />\n"));
00750         if (NS_FAILED(rv)) return rv;
00751     }
00752 
00753     return NS_OK;
00754 }
00755 
00756 nsresult
00757 nsRDFXMLSerializer::SerializeMember(nsIOutputStream* aStream,
00758                                       nsIRDFResource* aContainer,
00759                                       nsIRDFNode* aMember)
00760 {
00761     // If it's a resource, then output a "<RDF:li RDF:resource=... />"
00762     // tag, because we'll be dumping the resource separately. (We
00763     // iterate thru all the resources in the datasource,
00764     // remember?) Otherwise, output the literal value.
00765 
00766     nsCOMPtr<nsIRDFResource> resource;
00767     nsCOMPtr<nsIRDFLiteral> literal;
00768     nsCOMPtr<nsIRDFInt> number;
00769     nsCOMPtr<nsIRDFDate> date;
00770 
00771 static const char kRDFLIOpen[] = "    <RDF:li";
00772     nsresult rv = rdf_BlockingWrite(aStream, kRDFLIOpen,
00773                                     sizeof(kRDFLIOpen) - 1); 
00774     if (NS_FAILED(rv)) return rv;
00775 
00776     if ((resource = do_QueryInterface(aMember)) != nsnull) {
00777         nsCAutoString uri;
00778         resource->GetValueUTF8(uri);
00779 
00780         rdf_MakeRelativeRef(mBaseURLSpec, uri);
00781         rdf_EscapeAttributeValue(uri);
00782 
00783         rv = rdf_BlockingWrite(aStream, kRDFResource1,
00784                                sizeof(kRDFResource1) - 1);
00785         if (NS_FAILED(rv)) return rv;
00786         rv = rdf_BlockingWrite(aStream, uri);
00787         if (NS_FAILED(rv)) return rv;
00788         rv = rdf_BlockingWrite(aStream, kRDFResource2,
00789                                sizeof(kRDFResource2) - 1);
00790         if (NS_FAILED(rv)) return rv;
00791 
00792         goto no_close_tag;
00793     }
00794     else if ((literal = do_QueryInterface(aMember)) != nsnull) {
00795         const PRUnichar *value;
00796         literal->GetValueConst(&value);
00797 static const char kRDFLIOpenGT[] = ">";
00798         // close the '<RDF:LI' before adding the literal
00799         rv = rdf_BlockingWrite(aStream, kRDFLIOpenGT,
00800                                sizeof(kRDFLIOpenGT) - 1);
00801         if (NS_FAILED(rv)) return rv;
00802 
00803         NS_ConvertUTF16toUTF8 s(value);
00804         rdf_EscapeAmpersandsAndAngleBrackets(s);
00805 
00806         rv = rdf_BlockingWrite(aStream, s);
00807         if (NS_FAILED(rv)) return rv;
00808     }
00809     else if ((number = do_QueryInterface(aMember)) != nsnull) {
00810         PRInt32 value;
00811         number->GetValue(&value);
00812 
00813         nsCAutoString n;
00814         n.AppendInt(value);
00815 
00816         rv = rdf_BlockingWrite(aStream, kRDFParseTypeInteger, 
00817                                sizeof(kRDFParseTypeInteger) - 1);
00818         if (NS_FAILED(rv)) return rv;
00819         rv = rdf_BlockingWrite(aStream, n);
00820         if (NS_FAILED(rv)) return rv;
00821     }
00822     else if ((date = do_QueryInterface(aMember)) != nsnull) {
00823         PRTime value;
00824         date->GetValue(&value);
00825 
00826         nsCAutoString s;
00827         rdf_FormatDate(value, s);
00828 
00829         rv = rdf_BlockingWrite(aStream, kRDFParseTypeDate, 
00830                                sizeof(kRDFParseTypeDate) - 1);
00831         if (NS_FAILED(rv)) return rv;
00832         rv = rdf_BlockingWrite(aStream, s);
00833         if (NS_FAILED(rv)) return rv;
00834     }
00835     else {
00836         // XXX it doesn't support nsIRDFResource _or_ nsIRDFLiteral???
00837         // We should serialize nsIRDFInt, nsIRDFDate, etc...
00838         NS_WARNING("unknown RDF node type");
00839 
00840         rv = rdf_BlockingWrite(aStream, kRDFUnknown, sizeof(kRDFUnknown) - 1);
00841         if (NS_FAILED(rv)) return rv;
00842     }
00843 
00844     {
00845 static const char kRDFLIClose[] = "</RDF:li>\n";
00846         rv = rdf_BlockingWrite(aStream, kRDFLIClose, sizeof(kRDFLIClose) - 1);
00847         if (NS_FAILED(rv)) return rv;
00848     }
00849 
00850  no_close_tag:
00851     return NS_OK;
00852 }
00853 
00854 
00855 nsresult
00856 nsRDFXMLSerializer::SerializeContainer(nsIOutputStream* aStream,
00857                                          nsIRDFResource* aContainer)
00858 {
00859     nsresult rv;
00860     nsCAutoString tag;
00861 
00862     // Decide if it's a sequence, bag, or alternation, and print the
00863     // appropriate tag-open sequence
00864 
00865     if (IsA(mDataSource, aContainer, kRDF_Bag)) {
00866         tag.AssignLiteral("RDF:Bag");
00867     }
00868     else if (IsA(mDataSource, aContainer, kRDF_Seq)) {
00869         tag.AssignLiteral("RDF:Seq");
00870     }
00871     else if (IsA(mDataSource, aContainer, kRDF_Alt)) {
00872         tag.AssignLiteral("RDF:Alt");
00873     }
00874     else {
00875         NS_ASSERTION(PR_FALSE, "huh? this is _not_ a container.");
00876         return NS_ERROR_UNEXPECTED;
00877     }
00878 
00879     rv = rdf_BlockingWrite(aStream, "  <", 3);
00880     if (NS_FAILED(rv)) return rv;
00881     rv = rdf_BlockingWrite(aStream, tag);
00882     if (NS_FAILED(rv)) return rv;
00883 
00884 
00885     // Unfortunately, we always need to print out the identity of the
00886     // resource, even if was constructed "anonymously". We need to do
00887     // this because we never really know who else might be referring
00888     // to it...
00889 
00890     nsCAutoString uri;
00891     if (NS_SUCCEEDED(aContainer->GetValueUTF8(uri))) {
00892         rdf_MakeRelativeRef(mBaseURLSpec, uri);
00893 
00894         rdf_EscapeAttributeValue(uri);
00895 
00896         if (uri.First() == '#') {
00897             // Okay, it's actually identified as an element in the
00898             // current document, not trying to decorate some absolute
00899             // URI. We can use the 'ID=' attribute...
00900 
00901             uri.Cut(0, 1); // chop the '#'
00902             rv = rdf_BlockingWrite(aStream, kIDAttr, sizeof(kIDAttr) - 1);
00903             if (NS_FAILED(rv)) return rv;
00904         }
00905         else {
00906             // We need to cheat and spit out an illegal 'about=' on
00907             // the sequence. 
00908             rv = rdf_BlockingWrite(aStream, kAboutAttr,
00909                                    sizeof(kAboutAttr) - 1);
00910             if (NS_FAILED(rv)) return rv;
00911         }
00912 
00913         rv = rdf_BlockingWrite(aStream, uri);
00914         if (NS_FAILED(rv)) return rv;
00915         rv = rdf_BlockingWrite(aStream, "\"", 1);
00916         if (NS_FAILED(rv)) return rv;
00917     }
00918 
00919     rv = rdf_BlockingWrite(aStream, ">\n", 2);
00920     if (NS_FAILED(rv)) return rv;
00921 
00922     // First iterate through each of the ordinal elements (the RDF/XML
00923     // syntax doesn't allow us to place properties on RDF container
00924     // elements).
00925     nsCOMPtr<nsISimpleEnumerator> elements;
00926     rv = NS_NewContainerEnumerator(mDataSource, aContainer, getter_AddRefs(elements));
00927 
00928     if (NS_SUCCEEDED(rv)) {
00929         while (1) {
00930             PRBool hasMore;
00931             rv = elements->HasMoreElements(&hasMore);
00932             if (NS_FAILED(rv)) break;
00933 
00934             if (! hasMore)
00935                 break;
00936 
00937             nsCOMPtr<nsISupports> isupports;
00938             elements->GetNext(getter_AddRefs(isupports));
00939 
00940             nsCOMPtr<nsIRDFNode> element = do_QueryInterface(isupports);
00941             NS_ASSERTION(element != nsnull, "not an nsIRDFNode");
00942             if (! element)
00943                 continue;
00944 
00945             SerializeMember(aStream, aContainer, element);
00946         }
00947     }
00948 
00949     // close the container tag
00950     rv = rdf_BlockingWrite(aStream, "  </", 4);
00951     if (NS_FAILED(rv)) return rv;
00952     tag.Append(">\n", 2);
00953     rv = rdf_BlockingWrite(aStream, tag);
00954     if (NS_FAILED(rv)) return rv;
00955 
00956     // Now, we iterate through _all_ of the arcs, in case someone has
00957     // applied properties to the bag itself. These'll be placed in a
00958     // separate RDF:Description element.
00959     nsCOMPtr<nsISimpleEnumerator> arcs;
00960     mDataSource->ArcLabelsOut(aContainer, getter_AddRefs(arcs));
00961 
00962     PRBool wroteDescription = PR_FALSE;
00963     while (! wroteDescription) {
00964         PRBool hasMore = PR_FALSE;
00965         rv = arcs->HasMoreElements(&hasMore);
00966         if (NS_FAILED(rv)) break;
00967 
00968         if (! hasMore)
00969             break;
00970 
00971         nsIRDFResource* property;
00972         rv = arcs->GetNext((nsISupports**) &property);
00973         if (NS_FAILED(rv)) break;
00974 
00975         // If it's a membership property, then output a "LI"
00976         // tag. Otherwise, output a property.
00977         if (! IsContainerProperty(property)) {
00978             rv = SerializeDescription(aStream, aContainer);
00979             wroteDescription = PR_TRUE;
00980         }
00981 
00982         NS_RELEASE(property);
00983         if (NS_FAILED(rv))
00984             break;
00985     }
00986 
00987     return NS_OK;
00988 }
00989 
00990 
00991 nsresult
00992 nsRDFXMLSerializer::SerializePrologue(nsIOutputStream* aStream)
00993 {
00994 static const char kXMLVersion[] = "<?xml version=\"1.0\"?>\n";
00995 
00996     nsresult rv;
00997     rv = rdf_BlockingWrite(aStream, kXMLVersion, sizeof(kXMLVersion) - 1);
00998     if (NS_FAILED(rv)) return rv;
00999 
01000     // global name space declarations
01001     rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING("<RDF:RDF "));
01002     if (NS_FAILED(rv)) return rv;
01003 
01004     nsNameSpaceMap::const_iterator first = mNameSpaces.first();
01005     nsNameSpaceMap::const_iterator last = mNameSpaces.last();
01006     for (nsNameSpaceMap::const_iterator entry = first; entry != last; ++entry) {
01007         if (entry != first) {
01008             rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING("\n         "));
01009             if (NS_FAILED(rv)) return rv;
01010         }
01011         rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING("xmlns"));
01012         if (NS_FAILED(rv)) return rv;
01013 
01014         if (entry->mPrefix) {
01015             rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING(":"));
01016             if (NS_FAILED(rv)) return rv;
01017             nsCAutoString prefix;
01018             entry->mPrefix->ToUTF8String(prefix);
01019             rv = rdf_BlockingWrite(aStream, prefix);
01020             if (NS_FAILED(rv)) return rv;
01021         }
01022 
01023         rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING("=\""));
01024         if (NS_FAILED(rv)) return rv;
01025         rv = rdf_BlockingWrite(aStream, entry->mURI);
01026         if (NS_FAILED(rv)) return rv;
01027         rv = rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING("\""));
01028         if (NS_FAILED(rv)) return rv;
01029     }
01030 
01031     return rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING(">\n"));
01032 }
01033 
01034 
01035 nsresult
01036 nsRDFXMLSerializer::SerializeEpilogue(nsIOutputStream* aStream)
01037 {
01038     return rdf_BlockingWrite(aStream, NS_LITERAL_CSTRING("</RDF:RDF>\n"));
01039 }
01040 
01041 class QNameCollector : public rdfITripleVisitor {
01042 public:
01043     NS_DECL_ISUPPORTS
01044     NS_DECL_RDFITRIPLEVISITOR
01045     QNameCollector(nsRDFXMLSerializer* aParent)
01046         : mParent(aParent){}
01047 private:
01048     nsRDFXMLSerializer* mParent;
01049 };
01050 
01051 NS_IMPL_ISUPPORTS1(QNameCollector, rdfITripleVisitor)
01052 nsresult
01053 QNameCollector::Visit(nsIRDFNode* aSubject, nsIRDFResource* aPredicate,
01054                       nsIRDFNode* aObject, PRBool aTruthValue)
01055 {
01056     if (aPredicate == mParent->kRDF_type) {
01057         // try to get a type QName for aObject, should be a resource
01058         nsCOMPtr<nsIRDFResource> resType = do_QueryInterface(aObject);
01059         if (!resType) {
01060             // ignore error
01061             return NS_OK;
01062         }
01063         if (mParent->mQNames.Get(resType, nsnull)) {
01064             return NS_OK;
01065         }
01066         mParent->RegisterQName(resType);
01067         return NS_OK;
01068     }
01069 
01070     if (mParent->mQNames.Get(aPredicate, nsnull)) {
01071         return NS_OK;
01072     }
01073     if (aPredicate == mParent->kRDF_instanceOf ||
01074         aPredicate == mParent->kRDF_nextVal)
01075         return NS_OK;
01076     PRBool isOrdinal = PR_FALSE;
01077     mParent->gRDFC->IsOrdinalProperty(aPredicate, &isOrdinal);
01078     if (isOrdinal)
01079         return NS_OK;
01080 
01081     mParent->RegisterQName(aPredicate);
01082 
01083     return NS_OK;
01084 }
01085     
01086 nsresult
01087 nsRDFXMLSerializer::CollectNamespaces()
01088 {
01089     // Iterate over all Triples to get namespaces for subject resource types
01090     // and Predicates and cache all the QNames we want to use.
01091     nsCOMPtr<rdfITripleVisitor> collector = 
01092         new QNameCollector(this);
01093     nsCOMPtr<rdfIDataSource> ds = do_QueryInterface(mDataSource); // XXX API
01094     NS_ENSURE_TRUE(collector && ds, NS_ERROR_FAILURE);
01095     return ds->VisitAllTriples(collector);
01096 }
01097 
01098 //----------------------------------------------------------------------
01099 
01100 NS_IMETHODIMP
01101 nsRDFXMLSerializer::Serialize(nsIOutputStream* aStream)
01102 {
01103     nsresult rv;
01104     NS_TIMELINE_START_TIMER("rdf/xml-ser");
01105 
01106     rv = CollectNamespaces();
01107     if (NS_FAILED(rv)) return rv;
01108 
01109     nsCOMPtr<nsISimpleEnumerator> resources;
01110     rv = mDataSource->GetAllResources(getter_AddRefs(resources));
01111     if (NS_FAILED(rv)) return rv;
01112 
01113     rv = SerializePrologue(aStream);
01114     if (NS_FAILED(rv))
01115         return rv;
01116 
01117     while (1) {
01118         PRBool hasMore = PR_FALSE;
01119         resources->HasMoreElements(&hasMore);
01120         if (! hasMore)
01121             break;
01122 
01123         nsCOMPtr<nsISupports> isupports;
01124         resources->GetNext(getter_AddRefs(isupports));
01125 
01126         nsCOMPtr<nsIRDFResource> resource = do_QueryInterface(isupports);
01127         if (! resource)
01128             continue;
01129 
01130         if (IsA(mDataSource, resource, kRDF_Bag) ||
01131             IsA(mDataSource, resource, kRDF_Seq) ||
01132             IsA(mDataSource, resource, kRDF_Alt)) {
01133             rv = SerializeContainer(aStream, resource);
01134         }
01135         else {
01136             rv = SerializeDescription(aStream, resource);
01137         }
01138 
01139         if (NS_FAILED(rv))
01140             break;
01141     }
01142 
01143     rv = SerializeEpilogue(aStream);
01144     NS_TIMELINE_STOP_TIMER("rdf/xml-ser");
01145     NS_TIMELINE_MARK("rdf/xml-ser");
01146 
01147     return rv;
01148 }
01149 
01150 
01151 PRBool
01152 nsRDFXMLSerializer::IsA(nsIRDFDataSource* aDataSource, nsIRDFResource* aResource, nsIRDFResource* aType)
01153 {
01154     nsresult rv;
01155 
01156     PRBool result;
01157     rv = aDataSource->HasAssertion(aResource, kRDF_instanceOf, aType, PR_TRUE, &result);
01158     if (NS_FAILED(rv)) return PR_FALSE;
01159 
01160     return result;
01161 }