Back to index

lightning-sunbird  0.9+nobinonly
nsRDFConMemberTestNode.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Chris Waterson <waterson@netscape.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsRDFConMemberTestNode.h"
00040 #include "nsIRDFContainer.h"
00041 #include "nsIRDFContainerUtils.h"
00042 #include "nsRDFCID.h"
00043 #include "nsIServiceManager.h"
00044 #include "nsResourceSet.h"
00045 #include "nsConflictSet.h"
00046 #include "nsString.h"
00047 
00048 #include "prlog.h"
00049 #ifdef PR_LOGGING
00050 #include "nsXULContentUtils.h"
00051 extern PRLogModuleInfo* gXULTemplateLog;
00052 #endif
00053 
00054 nsRDFConMemberTestNode::nsRDFConMemberTestNode(InnerNode* aParent,
00055                                                nsConflictSet& aConflictSet,
00056                                                nsIRDFDataSource* aDataSource,
00057                                                const nsResourceSet& aMembershipProperties,
00058                                                PRInt32 aContainerVariable,
00059                                                PRInt32 aMemberVariable)
00060     : nsRDFTestNode(aParent),
00061       mConflictSet(aConflictSet),
00062       mDataSource(aDataSource),
00063       mMembershipProperties(aMembershipProperties),
00064       mContainerVariable(aContainerVariable),
00065       mMemberVariable(aMemberVariable)
00066 {
00067 #ifdef PR_LOGGING
00068     if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
00069         nsCAutoString props;
00070 
00071         nsResourceSet::ConstIterator last = aMembershipProperties.Last();
00072         nsResourceSet::ConstIterator first = aMembershipProperties.First();
00073         nsResourceSet::ConstIterator iter;
00074 
00075         for (iter = first; iter != last; ++iter) {
00076             if (iter != first)
00077                 props += " ";
00078 
00079             const char* str;
00080             iter->GetValueConst(&str);
00081 
00082             props += str;
00083         }
00084 
00085         PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
00086                ("nsRDFConMemberTestNode[%p]: parent=%p member-props=(%s) container-var=%d member-var=%d",
00087                 this,
00088                 aParent,
00089                 props.get(),
00090                 mContainerVariable,
00091                 mMemberVariable));
00092     }
00093 #endif
00094 }
00095 
00096 nsresult
00097 nsRDFConMemberTestNode::FilterInstantiations(InstantiationSet& aInstantiations, void* aClosure) const
00098 {
00099     // XXX Uh, factor me, please!
00100     nsresult rv;
00101 
00102     nsCOMPtr<nsIRDFContainerUtils> rdfc =
00103         do_GetService("@mozilla.org/rdf/container-utils;1");
00104 
00105     if (! rdfc)
00106         return NS_ERROR_FAILURE;
00107 
00108     InstantiationSet::Iterator last = aInstantiations.Last();
00109     for (InstantiationSet::Iterator inst = aInstantiations.First(); inst != last; ++inst) {
00110         PRBool hasContainerBinding;
00111         Value containerValue;
00112         hasContainerBinding = inst->mAssignments.GetAssignmentFor(mContainerVariable, &containerValue);
00113 
00114         nsCOMPtr<nsIRDFContainer> rdfcontainer;
00115 
00116         if (hasContainerBinding) {
00117             // If we have a container assignment, then see if the
00118             // container is an RDF container (bag, seq, alt), and if
00119             // so, wrap it.
00120             PRBool isRDFContainer;
00121             rv = rdfc->IsContainer(mDataSource,
00122                                    VALUE_TO_IRDFRESOURCE(containerValue),
00123                                    &isRDFContainer);
00124             if (NS_FAILED(rv)) return rv;
00125 
00126             if (isRDFContainer) {
00127                 rdfcontainer = do_CreateInstance("@mozilla.org/rdf/container;1", &rv);
00128                 if (NS_FAILED(rv)) return rv;
00129 
00130                 rv = rdfcontainer->Init(mDataSource, VALUE_TO_IRDFRESOURCE(containerValue));
00131                 if (NS_FAILED(rv)) return rv;
00132             }
00133         }
00134 
00135         PRBool hasMemberBinding;
00136         Value memberValue;
00137         hasMemberBinding = inst->mAssignments.GetAssignmentFor(mMemberVariable, &memberValue);
00138 
00139 #ifdef PR_LOGGING
00140         if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
00141             const char* container = "(unbound)";
00142             if (hasContainerBinding)
00143                 VALUE_TO_IRDFRESOURCE(containerValue)->GetValueConst(&container);
00144 
00145             nsAutoString member(NS_LITERAL_STRING("(unbound)"));
00146             if (hasMemberBinding)
00147                 nsXULContentUtils::GetTextForNode(VALUE_TO_IRDFRESOURCE(memberValue), member);
00148 
00149             PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
00150                    ("nsRDFConMemberTestNode[%p]: FilterInstantiations() container=[%s] member=[%s]",
00151                     this, container, NS_ConvertUCS2toUTF8(member).get()));
00152         }
00153 #endif
00154 
00155         if (hasContainerBinding && hasMemberBinding) {
00156             // it's a consistency check. see if we have a assignment that is consistent
00157             PRBool isconsistent = PR_FALSE;
00158 
00159             if (rdfcontainer) {
00160                 // RDF containers are easy. Just use the container API.
00161                 PRInt32 index;
00162                 rv = rdfcontainer->IndexOf(VALUE_TO_IRDFRESOURCE(memberValue), &index);
00163                 if (NS_FAILED(rv)) return rv;
00164 
00165                 if (index >= 0)
00166                     isconsistent = PR_TRUE;
00167             }
00168 
00169             // XXXwaterson oof. if we *are* an RDF container, why do
00170             // we still need to grovel through all the containment
00171             // properties if the thing we're looking for wasn't there?
00172 
00173             if (! isconsistent) {
00174                 // Othewise, we'll need to grovel through the
00175                 // membership properties to see if we have an
00176                 // assertion that indicates membership.
00177                 for (nsResourceSet::ConstIterator property = mMembershipProperties.First();
00178                      property != mMembershipProperties.Last();
00179                      ++property) {
00180                     PRBool hasAssertion;
00181                     rv = mDataSource->HasAssertion(VALUE_TO_IRDFRESOURCE(containerValue),
00182                                                    *property,
00183                                                    VALUE_TO_IRDFNODE(memberValue),
00184                                                    PR_TRUE,
00185                                                    &hasAssertion);
00186                     if (NS_FAILED(rv)) return rv;
00187 
00188                     if (hasAssertion) {
00189                         // it's consistent. leave it in the set and we'll
00190                         // run it up to our parent.
00191                         isconsistent = PR_TRUE;
00192                         break;
00193                     }
00194                 }
00195             }
00196 
00197             PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
00198                    ("    consistency check => %s", isconsistent ? "passed" : "failed"));
00199 
00200             if (isconsistent) {
00201                 // Add a memory element to our set-of-support.
00202                 Element* element =
00203                     nsRDFConMemberTestNode::Element::Create(mConflictSet.GetPool(),
00204                                                             VALUE_TO_IRDFRESOURCE(containerValue),
00205                                                             VALUE_TO_IRDFNODE(memberValue));
00206 
00207                 if (! element)
00208                     return NS_ERROR_OUT_OF_MEMORY;
00209 
00210                 inst->AddSupportingElement(element);
00211             }
00212             else {
00213                 // it's inconsistent. remove it.
00214                 aInstantiations.Erase(inst--);
00215             }
00216 
00217             // We're done, go on to the next instantiation
00218             continue;
00219         }
00220 
00221         if (hasContainerBinding && rdfcontainer) {
00222             // We've got a container assignment, and the container is
00223             // bound to an RDF container. Add each member as a new
00224             // instantiation.
00225             nsCOMPtr<nsISimpleEnumerator> elements;
00226             rv = rdfcontainer->GetElements(getter_AddRefs(elements));
00227             if (NS_FAILED(rv)) return rv;
00228 
00229             while (1) {
00230                 PRBool hasmore;
00231                 rv = elements->HasMoreElements(&hasmore);
00232                 if (NS_FAILED(rv)) return rv;
00233 
00234                 if (! hasmore)
00235                     break;
00236 
00237                 nsCOMPtr<nsISupports> isupports;
00238                 rv = elements->GetNext(getter_AddRefs(isupports));
00239                 if (NS_FAILED(rv)) return rv;
00240 
00241                 nsCOMPtr<nsIRDFNode> node = do_QueryInterface(isupports);
00242                 if (! node)
00243                     return NS_ERROR_UNEXPECTED;
00244 
00245 #ifdef PR_LOGGING
00246                 if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
00247                     nsAutoString member;
00248                     nsXULContentUtils::GetTextForNode(node, member);
00249 
00250                     PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
00251                            ("    member => %s", NS_ConvertUCS2toUTF8(member).get()));
00252                 }
00253 #endif
00254 
00255                 Instantiation newinst = *inst;
00256                 newinst.AddAssignment(mMemberVariable, Value(node.get()));
00257 
00258                 Element* element =
00259                     nsRDFConMemberTestNode::Element::Create(mConflictSet.GetPool(),
00260                                                             VALUE_TO_IRDFRESOURCE(containerValue),
00261                                                             node);
00262 
00263                 if (! element)
00264                     return NS_ERROR_OUT_OF_MEMORY;
00265 
00266                 newinst.AddSupportingElement(element);
00267 
00268                 aInstantiations.Insert(inst, newinst);
00269             }
00270         }
00271 
00272         if (hasMemberBinding) {
00273             // Oh, this is so nasty. If we have a member assignment, then
00274             // grovel through each one of our inbound arcs to see if
00275             // any of them are ordinal properties (like an RDF
00276             // container might have). If so, walk it backwards to get
00277             // the container we're in.
00278             nsCOMPtr<nsISimpleEnumerator> arcsin;
00279             rv = mDataSource->ArcLabelsIn(VALUE_TO_IRDFNODE(memberValue), getter_AddRefs(arcsin));
00280             if (NS_FAILED(rv)) return rv;
00281 
00282             while (1) {
00283                 nsCOMPtr<nsIRDFResource> property;
00284 
00285                 {
00286                     PRBool hasmore;
00287                     rv = arcsin->HasMoreElements(&hasmore);
00288                     if (NS_FAILED(rv)) return rv;
00289 
00290                     if (! hasmore)
00291                         break;
00292 
00293                     nsCOMPtr<nsISupports> isupports;
00294                     rv = arcsin->GetNext(getter_AddRefs(isupports));
00295                     if (NS_FAILED(rv)) return rv;
00296 
00297                     property = do_QueryInterface(isupports);
00298                     if (! property)
00299                         return NS_ERROR_UNEXPECTED;
00300                 }
00301 
00302                 // Ordinal properties automagically indicate container
00303                 // membership as far as we're concerned. Note that
00304                 // we're *only* concerned with ordinal properties
00305                 // here: the next block will worry about the other
00306                 // membership properties.
00307                 PRBool isordinal;
00308                 rv = rdfc->IsOrdinalProperty(property, &isordinal);
00309                 if (NS_FAILED(rv)) return rv;
00310 
00311                 if (isordinal) {
00312                     // If we get here, we've found a property that
00313                     // indicates container membership leading *into* a
00314                     // member node. Find all the people that point to
00315                     // it, and call them containers.
00316                     nsCOMPtr<nsISimpleEnumerator> sources;
00317                     rv = mDataSource->GetSources(property, VALUE_TO_IRDFNODE(memberValue), PR_TRUE,
00318                                                  getter_AddRefs(sources));
00319                     if (NS_FAILED(rv)) return rv;
00320 
00321                     while (1) {
00322                         PRBool hasmore;
00323                         rv = sources->HasMoreElements(&hasmore);
00324                         if (NS_FAILED(rv)) return rv;
00325 
00326                         if (! hasmore)
00327                             break;
00328 
00329                         nsCOMPtr<nsISupports> isupports;
00330                         rv = sources->GetNext(getter_AddRefs(isupports));
00331                         if (NS_FAILED(rv)) return rv;
00332 
00333                         nsCOMPtr<nsIRDFResource> source = do_QueryInterface(isupports);
00334                         if (! source)
00335                             return NS_ERROR_UNEXPECTED;
00336 
00337 #ifdef PR_LOGGING
00338                         if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
00339                             const char* container;
00340                             source->GetValueConst(&container);
00341 
00342                             PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
00343                                    ("    container => %s", container));
00344                         }
00345 #endif
00346 
00347                         // Add a new instantiation
00348                         Instantiation newinst = *inst;
00349                         newinst.AddAssignment(mContainerVariable, Value(source.get()));
00350 
00351                         Element* element =
00352                             nsRDFConMemberTestNode::Element::Create(mConflictSet.GetPool(),
00353                                                                     source,
00354                                                                     VALUE_TO_IRDFNODE(memberValue));
00355 
00356                         if (! element)
00357                             return NS_ERROR_OUT_OF_MEMORY;
00358 
00359                         newinst.AddSupportingElement(element);
00360 
00361                         aInstantiations.Insert(inst, newinst);
00362                     }
00363                 }
00364             }
00365         }
00366 
00367         if ((hasContainerBinding && ! hasMemberBinding) ||
00368             (! hasContainerBinding && hasMemberBinding)) {
00369             // it's an open ended query on the container or member. go
00370             // through our containment properties to see if anything
00371             // applies.
00372             for (nsResourceSet::ConstIterator property = mMembershipProperties.First();
00373                  property != mMembershipProperties.Last();
00374                  ++property) {
00375                 nsCOMPtr<nsISimpleEnumerator> results;
00376                 if (hasContainerBinding) {
00377                     rv = mDataSource->GetTargets(VALUE_TO_IRDFRESOURCE(containerValue), *property, PR_TRUE,
00378                                                  getter_AddRefs(results));
00379                 }
00380                 else {
00381                     rv = mDataSource->GetSources(*property, VALUE_TO_IRDFNODE(memberValue), PR_TRUE,
00382                                                  getter_AddRefs(results));
00383                 }
00384                 if (NS_FAILED(rv)) return rv;
00385 
00386                 while (1) {
00387                     PRBool hasmore;
00388                     rv = results->HasMoreElements(&hasmore);
00389                     if (NS_FAILED(rv)) return rv;
00390 
00391                     if (! hasmore)
00392                         break;
00393 
00394                     nsCOMPtr<nsISupports> isupports;
00395                     rv = results->GetNext(getter_AddRefs(isupports));
00396                     if (NS_FAILED(rv)) return rv;
00397 
00398                     PRInt32 variable;
00399                     Value value;
00400 
00401                     if (hasContainerBinding) {
00402                         variable = mMemberVariable;
00403 
00404                         nsCOMPtr<nsIRDFNode> member = do_QueryInterface(isupports);
00405                         NS_ASSERTION(member != nsnull, "member is not an nsIRDFNode");
00406                         if (! member) continue;
00407 
00408 #ifdef PR_LOGGING
00409                         if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
00410                             nsAutoString s;
00411                             nsXULContentUtils::GetTextForNode(member, s);
00412 
00413                             PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
00414                                    ("    member => %s", NS_ConvertUCS2toUTF8(s).get()));
00415                         }
00416 #endif
00417 
00418                         value = member.get();
00419                     }
00420                     else {
00421                         variable = mContainerVariable;
00422 
00423                         nsCOMPtr<nsIRDFResource> container = do_QueryInterface(isupports);
00424                         NS_ASSERTION(container != nsnull, "container is not an nsIRDFResource");
00425                         if (! container) continue;
00426 
00427 #ifdef PR_LOGGING
00428                         if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
00429                             const char* s;
00430                             container->GetValueConst(&s);
00431 
00432                             PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
00433                                    ("    container => %s", s));
00434                         }
00435 #endif
00436 
00437                         value = container.get();
00438                     }
00439 
00440                     // Copy the original instantiation, and add it to the
00441                     // instantiation set with the new assignment that we've
00442                     // introduced. Ownership will be transferred to the
00443                     Instantiation newinst = *inst;
00444                     newinst.AddAssignment(variable, value);
00445 
00446                     Element* element;
00447                     if (hasContainerBinding) {
00448                         element =
00449                             nsRDFConMemberTestNode::Element::Create(mConflictSet.GetPool(),
00450                                                                     VALUE_TO_IRDFRESOURCE(containerValue),
00451                                                                     VALUE_TO_IRDFNODE(value));
00452                     }
00453                     else {
00454                         element =
00455                             nsRDFConMemberTestNode::Element::Create(mConflictSet.GetPool(),
00456                                                                     VALUE_TO_IRDFRESOURCE(value),
00457                                                                     VALUE_TO_IRDFNODE(memberValue));
00458                     }
00459 
00460                     if (! element)
00461                         return NS_ERROR_OUT_OF_MEMORY;
00462 
00463                     newinst.AddSupportingElement(element);
00464 
00465                     aInstantiations.Insert(inst, newinst);
00466                 }
00467             }
00468         }
00469 
00470         if (! hasContainerBinding && ! hasMemberBinding) {
00471             // Neither container nor member assignment!
00472             NS_ERROR("can't do open ended queries like that!");
00473             return NS_ERROR_UNEXPECTED;
00474         }
00475 
00476         // finally, remove the "under specified" instantiation.
00477         aInstantiations.Erase(inst--);
00478     }
00479 
00480     return NS_OK;
00481 }
00482 
00483 nsresult
00484 nsRDFConMemberTestNode::GetAncestorVariables(VariableSet& aVariables) const
00485 {
00486     nsresult rv;
00487 
00488     rv = aVariables.Add(mContainerVariable);
00489     if (NS_FAILED(rv)) return rv;
00490 
00491     rv = aVariables.Add(mMemberVariable);
00492     if (NS_FAILED(rv)) return rv;
00493 
00494     return TestNode::GetAncestorVariables(aVariables);
00495 }
00496 
00497 
00498 PRBool
00499 nsRDFConMemberTestNode::CanPropagate(nsIRDFResource* aSource,
00500                                      nsIRDFResource* aProperty,
00501                                      nsIRDFNode* aTarget,
00502                                      Instantiation& aInitialBindings) const
00503 {
00504     nsresult rv;
00505 
00506     PRBool canpropagate = PR_FALSE;
00507 
00508     nsCOMPtr<nsIRDFContainerUtils> rdfc =
00509         do_GetService("@mozilla.org/rdf/container-utils;1");
00510 
00511     if (! rdfc)
00512         return NS_ERROR_FAILURE;
00513 
00514     // We can certainly propagate ordinal properties
00515     rv = rdfc->IsOrdinalProperty(aProperty, &canpropagate);
00516     if (NS_FAILED(rv)) return PR_FALSE;
00517 
00518     if (! canpropagate) {
00519         canpropagate = mMembershipProperties.Contains(aProperty);
00520     }
00521 
00522 #ifdef PR_LOGGING
00523     if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
00524         const char* source;
00525         aSource->GetValueConst(&source);
00526 
00527         const char* property;
00528         aProperty->GetValueConst(&property);
00529 
00530         nsAutoString target;
00531         nsXULContentUtils::GetTextForNode(aTarget, target);
00532 
00533         PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
00534                ("nsRDFConMemberTestNode[%p]: CanPropagate([%s]==[%s]=>[%s]) => %s",
00535                 this, source, property, NS_ConvertUCS2toUTF8(target).get(),
00536                 canpropagate ? "true" : "false"));
00537     }
00538 #endif
00539 
00540     if (canpropagate) {
00541         aInitialBindings.AddAssignment(mContainerVariable, Value(aSource));
00542         aInitialBindings.AddAssignment(mMemberVariable, Value(aTarget));
00543         return PR_TRUE;
00544     }
00545 
00546     return PR_FALSE;
00547 }
00548 
00549 void
00550 nsRDFConMemberTestNode::Retract(nsIRDFResource* aSource,
00551                                 nsIRDFResource* aProperty,
00552                                 nsIRDFNode* aTarget,
00553                                 nsTemplateMatchSet& aFirings,
00554                                 nsTemplateMatchSet& aRetractions) const
00555 {
00556     PRBool canretract = PR_FALSE;
00557 
00558     nsCOMPtr<nsIRDFContainerUtils> rdfc =
00559         do_GetService("@mozilla.org/rdf/container-utils;1");
00560 
00561     if (! rdfc)
00562         return;
00563 
00564     // We can certainly retract ordinal properties
00565     nsresult rv;
00566     rv = rdfc->IsOrdinalProperty(aProperty, &canretract);
00567     if (NS_FAILED(rv)) return;
00568 
00569     if (! canretract) {
00570         canretract = mMembershipProperties.Contains(aProperty);
00571     }
00572 
00573     if (canretract) {
00574         mConflictSet.Remove(Element(aSource, aTarget), aFirings, aRetractions);
00575     }
00576 }