1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * 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.
37 * ***** END LICENSE BLOCK *****
40 * This Original Code has been modified by IBM Corporation.
41 * Modifications made by IBM described herein are
42 * Copyright (c) International Business Machines
45 * Modifications to Mozilla code or documentation
46 * identified per MPL Section 3.3
48 * Date Modified by Description of modification
49 * 03/27/2000 IBM Corp. Added PR_CALLBACK for Optlink
53 #include "nsStreamConverterService.h"
54 #include "nsIServiceManager.h"
55 #include "nsIComponentManager.h"
56 #include "nsIComponentRegistrar.h"
58 #include "nsReadableUtils.h"
61 #include "nsIInputStream.h"
62 #include "nsIOutputStream.h"
63 #include "nsIStreamConverter.h"
64 #include "nsICategoryManager.h"
66 #include "nsISupportsPrimitives.h"
67 #include "nsXPIDLString.h"
69 ////////////////////////////////////////////////////////////
70 // nsISupports methods
71 NS_IMPL_ISUPPORTS1(nsStreamConverterService
, nsIStreamConverterService
)
74 ////////////////////////////////////////////////////////////
75 // nsIStreamConverterService methods
77 ////////////////////////////////////////////////////////////
78 // nsStreamConverterService methods
79 nsStreamConverterService::nsStreamConverterService() : mAdjacencyList(nsnull
) {
82 nsStreamConverterService::~nsStreamConverterService() {
83 NS_ASSERTION(mAdjacencyList
, "init wasn't called, or the retval was ignored");
84 delete mAdjacencyList
;
87 // Delete all the entries in the adjacency list
88 static PRBool
DeleteAdjacencyEntry(nsHashKey
*aKey
, void *aData
, void* closure
) {
89 SCTableData
*entry
= (SCTableData
*)aData
;
90 NS_ASSERTION(entry
->key
&& entry
->data
.edges
, "malformed adjacency list entry");
92 delete entry
->data
.edges
;
98 nsStreamConverterService::Init() {
99 mAdjacencyList
= new nsObjectHashtable(nsnull
, nsnull
,
100 DeleteAdjacencyEntry
, nsnull
);
101 if (!mAdjacencyList
) return NS_ERROR_OUT_OF_MEMORY
;
105 // Builds the graph represented as an adjacency list (and built up in
106 // memory using an nsObjectHashtable and nsISupportsArray combination).
108 // :BuildGraph() consults the category manager for all stream converter
109 // CONTRACTIDS then fills the adjacency list with edges.
110 // An edge in this case is comprised of a FROM and TO MIME type combination.
112 // CONTRACTID format:
113 // @mozilla.org/streamconv;1?from=text/html&to=text/plain
114 // XXX curently we only handle a single from and to combo, we should repeat the
115 // XXX registration process for any series of from-to combos.
116 // XXX can use nsTokenizer for this.
120 nsStreamConverterService::BuildGraph() {
124 nsCOMPtr
<nsICategoryManager
> catmgr(do_GetService(NS_CATEGORYMANAGER_CONTRACTID
, &rv
));
125 if (NS_FAILED(rv
)) return rv
;
127 nsCOMPtr
<nsISimpleEnumerator
> entries
;
128 rv
= catmgr
->EnumerateCategory(NS_ISTREAMCONVERTER_KEY
, getter_AddRefs(entries
));
129 if (NS_FAILED(rv
)) return rv
;
131 // go through each entry to build the graph
132 nsCOMPtr
<nsISupportsCString
> entry
;
133 rv
= entries
->GetNext(getter_AddRefs(entry
));
134 while (NS_SUCCEEDED(rv
)) {
136 // get the entry string
137 nsCAutoString entryString
;
138 rv
= entry
->GetData(entryString
);
139 if (NS_FAILED(rv
)) return rv
;
141 // cobble the entry string w/ the converter key to produce a full contractID.
142 nsCAutoString
contractID(NS_ISTREAMCONVERTER_KEY
);
143 contractID
.Append(entryString
);
145 // now we've got the CONTRACTID, let's parse it up.
146 rv
= AddAdjacency(contractID
.get());
147 if (NS_FAILED(rv
)) return rv
;
149 rv
= entries
->GetNext(getter_AddRefs(entry
));
156 // XXX currently you can not add the same adjacency (i.e. you can't have multiple
157 // XXX stream converters registering to handle the same from-to combination. It's
158 // XXX not programatically prohibited, it's just that results are un-predictable
161 nsStreamConverterService::AddAdjacency(const char *aContractID
) {
163 // first parse out the FROM and TO MIME-types.
165 nsCAutoString fromStr
, toStr
;
166 rv
= ParseFromTo(aContractID
, fromStr
, toStr
);
167 if (NS_FAILED(rv
)) return rv
;
169 // Each MIME-type is a vertex in the graph, so first lets make sure
170 // each MIME-type is represented as a key in our hashtable.
172 nsCStringKey
fromKey(fromStr
);
173 SCTableData
*fromEdges
= (SCTableData
*)mAdjacencyList
->Get(&fromKey
);
175 // There is no fromStr vertex, create one.
177 nsCStringKey
*newFromKey
= new nsCStringKey(ToNewCString(fromStr
), fromStr
.Length(), nsCStringKey::OWN
);
178 if (!newFromKey
) return NS_ERROR_OUT_OF_MEMORY
;
180 SCTableData
*data
= new SCTableData(newFromKey
);
183 return NS_ERROR_OUT_OF_MEMORY
;
186 nsCOMArray
<nsIAtom
>* edgeArray
= new nsCOMArray
<nsIAtom
>;
191 return NS_ERROR_OUT_OF_MEMORY
;
193 data
->data
.edges
= edgeArray
;
195 mAdjacencyList
->Put(newFromKey
, data
);
199 nsCStringKey
toKey(toStr
);
200 if (!mAdjacencyList
->Get(&toKey
)) {
201 // There is no toStr vertex, create one.
202 nsCStringKey
*newToKey
= new nsCStringKey(ToNewCString(toStr
), toStr
.Length(), nsCStringKey::OWN
);
203 if (!newToKey
) return NS_ERROR_OUT_OF_MEMORY
;
205 SCTableData
*data
= new SCTableData(newToKey
);
208 return NS_ERROR_OUT_OF_MEMORY
;
211 nsCOMArray
<nsIAtom
>* edgeArray
= new nsCOMArray
<nsIAtom
>;
216 return NS_ERROR_OUT_OF_MEMORY
;
218 data
->data
.edges
= edgeArray
;
219 mAdjacencyList
->Put(newToKey
, data
);
222 // Now we know the FROM and TO types are represented as keys in the hashtable.
223 // Let's "connect" the verticies, making an edge.
225 nsCOMPtr
<nsIAtom
> vertex
= do_GetAtom(toStr
.get());
226 if (!vertex
) return NS_ERROR_OUT_OF_MEMORY
;
228 NS_ASSERTION(fromEdges
, "something wrong in adjacency list construction");
230 return NS_ERROR_FAILURE
;
232 nsCOMArray
<nsIAtom
> *adjacencyList
= fromEdges
->data
.edges
;
233 return adjacencyList
->AppendObject(vertex
) ? NS_OK
: NS_ERROR_FAILURE
;
237 nsStreamConverterService::ParseFromTo(const char *aContractID
, nsCString
&aFromRes
, nsCString
&aToRes
) {
239 nsCAutoString
ContractIDStr(aContractID
);
241 PRInt32 fromLoc
= ContractIDStr
.Find("from=");
242 PRInt32 toLoc
= ContractIDStr
.Find("to=");
243 if (-1 == fromLoc
|| -1 == toLoc
) return NS_ERROR_FAILURE
;
245 fromLoc
= fromLoc
+ 5;
248 nsCAutoString fromStr
, toStr
;
250 ContractIDStr
.Mid(fromStr
, fromLoc
, toLoc
- 4 - fromLoc
);
251 ContractIDStr
.Mid(toStr
, toLoc
, ContractIDStr
.Length() - toLoc
);
253 aFromRes
.Assign(fromStr
);
254 aToRes
.Assign(toStr
);
259 // nsObjectHashtable enumerator functions.
261 // Initializes the BFS state table.
262 static PRBool
InitBFSTable(nsHashKey
*aKey
, void *aData
, void* closure
) {
263 NS_ASSERTION((SCTableData
*)aData
, "no data in the table enumeration");
265 nsHashtable
*BFSTable
= (nsHashtable
*)closure
;
266 if (!BFSTable
) return PR_FALSE
;
268 BFSState
*state
= new BFSState
;
269 if (!state
) return PR_FALSE
;
271 state
->color
= white
;
272 state
->distance
= -1;
273 state
->predecessor
= nsnull
;
275 SCTableData
*data
= new SCTableData(static_cast<nsCStringKey
*>(aKey
));
280 data
->data
.state
= state
;
282 BFSTable
->Put(aKey
, data
);
286 // cleans up the BFS state table
287 static PRBool
DeleteBFSEntry(nsHashKey
*aKey
, void *aData
, void *closure
) {
288 SCTableData
*data
= (SCTableData
*)aData
;
289 BFSState
*state
= data
->data
.state
;
296 class CStreamConvDeallocator
: public nsDequeFunctor
{
298 virtual void* operator()(void* anObject
) {
299 nsCStringKey
*key
= (nsCStringKey
*)anObject
;
305 // walks the graph using a breadth-first-search algorithm which generates a discovered
306 // verticies tree. This tree is then walked up (from destination vertex, to origin vertex)
307 // and each link in the chain is added to an nsStringArray. A direct lookup for the given
308 // CONTRACTID should be made prior to calling this method in an attempt to find a direct
309 // converter rather than walking the graph.
311 nsStreamConverterService::FindConverter(const char *aContractID
, nsCStringArray
**aEdgeList
) {
313 if (!aEdgeList
) return NS_ERROR_NULL_POINTER
;
316 // walk the graph in search of the appropriate converter.
318 PRInt32 vertexCount
= mAdjacencyList
->Count();
319 if (0 >= vertexCount
) return NS_ERROR_FAILURE
;
321 // Create a corresponding color table for each vertex in the graph.
322 nsObjectHashtable
lBFSTable(nsnull
, nsnull
, DeleteBFSEntry
, nsnull
);
323 mAdjacencyList
->Enumerate(InitBFSTable
, &lBFSTable
);
325 NS_ASSERTION(lBFSTable
.Count() == vertexCount
, "strmconv BFS table init problem");
327 // This is our source vertex; our starting point.
328 nsCAutoString fromC
, toC
;
329 rv
= ParseFromTo(aContractID
, fromC
, toC
);
330 if (NS_FAILED(rv
)) return rv
;
332 nsCStringKey
*source
= new nsCStringKey(fromC
.get());
333 if (!source
) return NS_ERROR_OUT_OF_MEMORY
;
335 SCTableData
*data
= (SCTableData
*)lBFSTable
.Get(source
);
338 return NS_ERROR_FAILURE
;
341 BFSState
*state
= data
->data
.state
;
345 CStreamConvDeallocator
*dtorFunc
= new CStreamConvDeallocator();
348 return NS_ERROR_OUT_OF_MEMORY
;
351 nsDeque
grayQ(dtorFunc
);
353 // Now generate the shortest path tree.
355 while (0 < grayQ
.GetSize()) {
356 nsCStringKey
*currentHead
= (nsCStringKey
*)grayQ
.PeekFront();
357 SCTableData
*data2
= (SCTableData
*)mAdjacencyList
->Get(currentHead
);
358 if (!data2
) return NS_ERROR_FAILURE
;
360 nsCOMArray
<nsIAtom
> *edges
= data2
->data
.edges
;
361 NS_ASSERTION(edges
, "something went wrong with BFS strmconv algorithm");
362 if (!edges
) return NS_ERROR_FAILURE
;
364 // Get the state of the current head to calculate the distance of each
365 // reachable vertex in the loop.
366 data2
= (SCTableData
*)lBFSTable
.Get(currentHead
);
367 if (!data2
) return NS_ERROR_FAILURE
;
369 BFSState
*headVertexState
= data2
->data
.state
;
370 NS_ASSERTION(headVertexState
, "problem with the BFS strmconv algorithm");
371 if (!headVertexState
) return NS_ERROR_FAILURE
;
373 PRInt32 edgeCount
= edges
->Count();
375 for (PRInt32 i
= 0; i
< edgeCount
; i
++) {
376 nsIAtom
* curVertexAtom
= edges
->ObjectAt(i
);
377 nsAutoString curVertexStr
;
378 curVertexAtom
->ToString(curVertexStr
);
379 nsCStringKey
*curVertex
= new nsCStringKey(ToNewCString(curVertexStr
),
380 curVertexStr
.Length(), nsCStringKey::OWN
);
381 if (!curVertex
) return NS_ERROR_OUT_OF_MEMORY
;
383 SCTableData
*data3
= (SCTableData
*)lBFSTable
.Get(curVertex
);
386 return NS_ERROR_FAILURE
;
388 BFSState
*curVertexState
= data3
->data
.state
;
389 NS_ASSERTION(curVertexState
, "something went wrong with the BFS strmconv algorithm");
390 if (!curVertexState
) return NS_ERROR_FAILURE
;
392 if (white
== curVertexState
->color
) {
393 curVertexState
->color
= gray
;
394 curVertexState
->distance
= headVertexState
->distance
+ 1;
395 curVertexState
->predecessor
= (nsCStringKey
*)currentHead
->Clone();
396 if (!curVertexState
->predecessor
) {
398 return NS_ERROR_OUT_OF_MEMORY
;
400 grayQ
.Push(curVertex
);
402 delete curVertex
; // if this vertex has already been discovered, we don't want
403 // to leak it. (non-discovered vertex's get cleaned up when
407 headVertexState
->color
= black
;
408 nsCStringKey
*cur
= (nsCStringKey
*)grayQ
.PopFront();
412 // The shortest path (if any) has been generated and is represetned by the chain of
413 // BFSState->predecessor keys. Start at the bottom and work our way up.
415 // first parse out the FROM and TO MIME-types being registered.
417 nsCAutoString fromStr
, toStr
;
418 rv
= ParseFromTo(aContractID
, fromStr
, toStr
);
419 if (NS_FAILED(rv
)) return rv
;
421 // get the root CONTRACTID
422 nsCAutoString
ContractIDPrefix(NS_ISTREAMCONVERTER_KEY
);
423 nsCStringArray
*shortestPath
= new nsCStringArray();
424 if (!shortestPath
) return NS_ERROR_OUT_OF_MEMORY
;
426 nsCStringKey
toMIMEType(toStr
);
427 data
= (SCTableData
*)lBFSTable
.Get(&toMIMEType
);
429 // If this vertex isn't in the BFSTable, then no-one has registered for it,
430 // therefore we can't do the conversion.
432 return NS_ERROR_FAILURE
;
436 BFSState
*curState
= data
->data
.state
;
438 nsCStringKey
*key
= data
->key
;
440 if (fromStr
.Equals(key
->GetString())) {
441 // found it. We're done here.
442 *aEdgeList
= shortestPath
;
446 // reconstruct the CONTRACTID.
447 // Get the predecessor.
448 if (!curState
->predecessor
) break; // no predecessor
449 SCTableData
*predecessorData
= (SCTableData
*)lBFSTable
.Get(curState
->predecessor
);
451 if (!predecessorData
) break; // no predecessor, chain doesn't exist.
453 // build out the CONTRACTID.
454 nsCAutoString
newContractID(ContractIDPrefix
);
455 newContractID
.AppendLiteral("?from=");
457 nsCStringKey
*predecessorKey
= predecessorData
->key
;
458 newContractID
.Append(predecessorKey
->GetString());
460 newContractID
.AppendLiteral("&to=");
461 newContractID
.Append(key
->GetString());
463 // Add this CONTRACTID to the chain.
464 rv
= shortestPath
->AppendCString(newContractID
) ? NS_OK
: NS_ERROR_FAILURE
; // XXX this method incorrectly returns a bool
465 NS_ASSERTION(NS_SUCCEEDED(rv
), "AppendElement failed");
468 data
= predecessorData
;
471 return NS_ERROR_FAILURE
; // couldn't find a stream converter or chain.
475 /////////////////////////////////////////////////////
476 // nsIStreamConverterService methods
478 nsStreamConverterService::CanConvert(const char* aFromType
,
481 nsCOMPtr
<nsIComponentRegistrar
> reg
;
482 nsresult rv
= NS_GetComponentRegistrar(getter_AddRefs(reg
));
486 nsCAutoString contractID
;
487 contractID
.AssignLiteral(NS_ISTREAMCONVERTER_KEY
"?from=");
488 contractID
.Append(aFromType
);
489 contractID
.AppendLiteral("&to=");
490 contractID
.Append(aToType
);
492 // See if we have a direct match
493 rv
= reg
->IsContractIDRegistered(contractID
.get(), _retval
);
499 // Otherwise try the graph.
504 nsCStringArray
*converterChain
= nsnull
;
505 rv
= FindConverter(contractID
.get(), &converterChain
);
506 *_retval
= NS_SUCCEEDED(rv
);
508 delete converterChain
;
513 nsStreamConverterService::Convert(nsIInputStream
*aFromStream
,
514 const char *aFromType
,
516 nsISupports
*aContext
,
517 nsIInputStream
**_retval
) {
518 if (!aFromStream
|| !aFromType
|| !aToType
|| !_retval
) return NS_ERROR_NULL_POINTER
;
521 // first determine whether we can even handle this conversion
522 // build a CONTRACTID
523 nsCAutoString contractID
;
524 contractID
.AssignLiteral(NS_ISTREAMCONVERTER_KEY
"?from=");
525 contractID
.Append(aFromType
);
526 contractID
.AppendLiteral("&to=");
527 contractID
.Append(aToType
);
528 const char *cContractID
= contractID
.get();
530 nsCOMPtr
<nsIStreamConverter
> converter(do_CreateInstance(cContractID
, &rv
));
532 // couldn't go direct, let's try walking the graph of converters.
534 if (NS_FAILED(rv
)) return rv
;
536 nsCStringArray
*converterChain
= nsnull
;
538 rv
= FindConverter(cContractID
, &converterChain
);
540 // can't make this conversion.
541 // XXX should have a more descriptive error code.
542 return NS_ERROR_FAILURE
;
545 PRInt32 edgeCount
= converterChain
->Count();
546 NS_ASSERTION(edgeCount
> 0, "findConverter should have failed");
549 // convert the stream using each edge of the graph as a step.
550 // this is our stream conversion traversal.
551 nsCOMPtr
<nsIInputStream
> dataToConvert
= aFromStream
;
552 nsCOMPtr
<nsIInputStream
> convertedData
;
554 for (PRInt32 i
= edgeCount
-1; i
>= 0; i
--) {
555 nsCString
*contractIDStr
= converterChain
->CStringAt(i
);
556 if (!contractIDStr
) {
557 delete converterChain
;
558 return NS_ERROR_FAILURE
;
560 const char *lContractID
= contractIDStr
->get();
562 converter
= do_CreateInstance(lContractID
, &rv
);
565 delete converterChain
;
569 nsCAutoString fromStr
, toStr
;
570 rv
= ParseFromTo(lContractID
, fromStr
, toStr
);
572 delete converterChain
;
576 rv
= converter
->Convert(dataToConvert
, fromStr
.get(), toStr
.get(), aContext
, getter_AddRefs(convertedData
));
577 dataToConvert
= convertedData
;
579 delete converterChain
;
584 delete converterChain
;
585 *_retval
= convertedData
;
589 // we're going direct.
590 rv
= converter
->Convert(aFromStream
, aFromType
, aToType
, aContext
, _retval
);
598 nsStreamConverterService::AsyncConvertData(const char *aFromType
,
600 nsIStreamListener
*aListener
,
601 nsISupports
*aContext
,
602 nsIStreamListener
**_retval
) {
603 if (!aFromType
|| !aToType
|| !aListener
|| !_retval
) return NS_ERROR_NULL_POINTER
;
607 // first determine whether we can even handle this conversion
608 // build a CONTRACTID
609 nsCAutoString contractID
;
610 contractID
.AssignLiteral(NS_ISTREAMCONVERTER_KEY
"?from=");
611 contractID
.Append(aFromType
);
612 contractID
.AppendLiteral("&to=");
613 contractID
.Append(aToType
);
614 const char *cContractID
= contractID
.get();
616 nsCOMPtr
<nsIStreamConverter
> listener(do_CreateInstance(cContractID
, &rv
));
618 // couldn't go direct, let's try walking the graph of converters.
620 if (NS_FAILED(rv
)) return rv
;
622 nsCStringArray
*converterChain
= nsnull
;
624 rv
= FindConverter(cContractID
, &converterChain
);
626 // can't make this conversion.
627 // XXX should have a more descriptive error code.
628 return NS_ERROR_FAILURE
;
631 // aListener is the listener that wants the final, converted, data.
632 // we initialize finalListener w/ aListener so it gets put at the
633 // tail end of the chain, which in the loop below, means the *first*
634 // converter created.
635 nsCOMPtr
<nsIStreamListener
> finalListener
= aListener
;
637 // convert the stream using each edge of the graph as a step.
638 // this is our stream conversion traversal.
639 PRInt32 edgeCount
= converterChain
->Count();
640 NS_ASSERTION(edgeCount
> 0, "findConverter should have failed");
641 for (int i
= 0; i
< edgeCount
; i
++) {
642 nsCString
*contractIDStr
= converterChain
->CStringAt(i
);
643 if (!contractIDStr
) {
644 delete converterChain
;
645 return NS_ERROR_FAILURE
;
647 const char *lContractID
= contractIDStr
->get();
649 // create the converter for this from/to pair
650 nsCOMPtr
<nsIStreamConverter
> converter(do_CreateInstance(lContractID
));
651 NS_ASSERTION(converter
, "graph construction problem, built a contractid that wasn't registered");
653 nsCAutoString fromStr
, toStr
;
654 rv
= ParseFromTo(lContractID
, fromStr
, toStr
);
656 delete converterChain
;
660 // connect the converter w/ the listener that should get the converted data.
661 rv
= converter
->AsyncConvertData(fromStr
.get(), toStr
.get(), finalListener
, aContext
);
663 delete converterChain
;
667 nsCOMPtr
<nsIStreamListener
> chainListener(do_QueryInterface(converter
, &rv
));
669 delete converterChain
;
673 // the last iteration of this loop will result in finalListener
674 // pointing to the converter that "starts" the conversion chain.
675 // this converter's "from" type is the original "from" type. Prior
676 // to the last iteration, finalListener will continuously be wedged
677 // into the next listener in the chain, then be updated.
678 finalListener
= chainListener
;
680 delete converterChain
;
681 // return the first listener in the chain.
682 *_retval
= finalListener
;
686 // we're going direct.
690 rv
= listener
->AsyncConvertData(aFromType
, aToType
, aListener
, aContext
);
698 NS_NewStreamConv(nsStreamConverterService
** aStreamConv
)
700 NS_PRECONDITION(aStreamConv
!= nsnull
, "null ptr");
701 if (!aStreamConv
) return NS_ERROR_NULL_POINTER
;
703 *aStreamConv
= new nsStreamConverterService();
704 if (!*aStreamConv
) return NS_ERROR_OUT_OF_MEMORY
;
706 NS_ADDREF(*aStreamConv
);
707 nsresult rv
= (*aStreamConv
)->Init();
709 NS_RELEASE(*aStreamConv
);