nsRDFConMemberTestNode.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "nsRDFConMemberTestNode.h"
  6. #include "nsIRDFContainer.h"
  7. #include "nsIRDFContainerUtils.h"
  8. #include "nsRDFCID.h"
  9. #include "nsIServiceManager.h"
  10. #include "nsResourceSet.h"
  11. #include "nsString.h"
  12. #include "nsXULContentUtils.h"
  13. #include "mozilla/Logging.h"
  14. using mozilla::LogLevel;
  15. extern mozilla::LazyLogModule gXULTemplateLog;
  16. nsRDFConMemberTestNode::nsRDFConMemberTestNode(TestNode* aParent,
  17. nsXULTemplateQueryProcessorRDF* aProcessor,
  18. nsIAtom *aContainerVariable,
  19. nsIAtom *aMemberVariable)
  20. : nsRDFTestNode(aParent),
  21. mProcessor(aProcessor),
  22. mContainerVariable(aContainerVariable),
  23. mMemberVariable(aMemberVariable)
  24. {
  25. if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) {
  26. nsAutoCString props;
  27. nsResourceSet& containmentProps = aProcessor->ContainmentProperties();
  28. nsResourceSet::ConstIterator last = containmentProps.Last();
  29. nsResourceSet::ConstIterator first = containmentProps.First();
  30. nsResourceSet::ConstIterator iter;
  31. for (iter = first; iter != last; ++iter) {
  32. if (iter != first)
  33. props += " ";
  34. const char* str;
  35. iter->GetValueConst(&str);
  36. props += str;
  37. }
  38. nsAutoString cvar(NS_LITERAL_STRING("(none)"));
  39. if (mContainerVariable)
  40. mContainerVariable->ToString(cvar);
  41. nsAutoString mvar(NS_LITERAL_STRING("(none)"));
  42. if (mMemberVariable)
  43. mMemberVariable->ToString(mvar);
  44. MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
  45. ("nsRDFConMemberTestNode[%p]: parent=%p member-props=(%s) container-var=%s member-var=%s",
  46. this,
  47. aParent,
  48. props.get(),
  49. NS_ConvertUTF16toUTF8(cvar).get(),
  50. NS_ConvertUTF16toUTF8(mvar).get()));
  51. }
  52. }
  53. nsresult
  54. nsRDFConMemberTestNode::FilterInstantiations(InstantiationSet& aInstantiations,
  55. bool* aCantHandleYet) const
  56. {
  57. // XXX Uh, factor me, please!
  58. nsresult rv;
  59. if (aCantHandleYet)
  60. *aCantHandleYet = false;
  61. nsCOMPtr<nsIRDFContainerUtils> rdfc =
  62. do_GetService("@mozilla.org/rdf/container-utils;1");
  63. if (! rdfc)
  64. return NS_ERROR_FAILURE;
  65. nsIRDFDataSource* ds = mProcessor->GetDataSource();
  66. InstantiationSet::Iterator last = aInstantiations.Last();
  67. for (InstantiationSet::Iterator inst = aInstantiations.First(); inst != last; ++inst) {
  68. bool hasContainerBinding;
  69. nsCOMPtr<nsIRDFNode> containerValue;
  70. hasContainerBinding = inst->mAssignments.GetAssignmentFor(mContainerVariable,
  71. getter_AddRefs(containerValue));
  72. nsCOMPtr<nsIRDFResource> containerRes = do_QueryInterface(containerValue);
  73. nsCOMPtr<nsIRDFContainer> rdfcontainer;
  74. if (hasContainerBinding && containerRes) {
  75. // If we have a container assignment, then see if the
  76. // container is an RDF container (bag, seq, alt), and if
  77. // so, wrap it.
  78. bool isRDFContainer;
  79. rv = rdfc->IsContainer(ds, containerRes, &isRDFContainer);
  80. if (NS_FAILED(rv)) return rv;
  81. if (isRDFContainer) {
  82. rdfcontainer = do_CreateInstance("@mozilla.org/rdf/container;1", &rv);
  83. if (NS_FAILED(rv)) return rv;
  84. rv = rdfcontainer->Init(ds, containerRes);
  85. if (NS_FAILED(rv)) return rv;
  86. }
  87. }
  88. bool hasMemberBinding;
  89. nsCOMPtr<nsIRDFNode> memberValue;
  90. hasMemberBinding = inst->mAssignments.GetAssignmentFor(mMemberVariable,
  91. getter_AddRefs(memberValue));
  92. if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) {
  93. const char* container = "(unbound)";
  94. if (hasContainerBinding)
  95. containerRes->GetValueConst(&container);
  96. nsAutoString member(NS_LITERAL_STRING("(unbound)"));
  97. if (hasMemberBinding)
  98. nsXULContentUtils::GetTextForNode(memberValue, member);
  99. MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
  100. ("nsRDFConMemberTestNode[%p]: FilterInstantiations() container=[%s] member=[%s]",
  101. this, container, NS_ConvertUTF16toUTF8(member).get()));
  102. }
  103. if (hasContainerBinding && hasMemberBinding) {
  104. // it's a consistency check. see if we have a assignment that is consistent
  105. bool isconsistent = false;
  106. if (rdfcontainer) {
  107. // RDF containers are easy. Just use the container API.
  108. int32_t index;
  109. rv = rdfcontainer->IndexOf(memberValue, &index);
  110. if (NS_FAILED(rv)) return rv;
  111. if (index >= 0)
  112. isconsistent = true;
  113. }
  114. // XXXwaterson oof. if we *are* an RDF container, why do
  115. // we still need to grovel through all the containment
  116. // properties if the thing we're looking for wasn't there?
  117. if (! isconsistent) {
  118. // Othewise, we'll need to grovel through the
  119. // membership properties to see if we have an
  120. // assertion that indicates membership.
  121. nsResourceSet& containmentProps = mProcessor->ContainmentProperties();
  122. for (nsResourceSet::ConstIterator property = containmentProps.First();
  123. property != containmentProps.Last();
  124. ++property) {
  125. bool hasAssertion;
  126. rv = ds->HasAssertion(containerRes,
  127. *property,
  128. memberValue,
  129. true,
  130. &hasAssertion);
  131. if (NS_FAILED(rv)) return rv;
  132. if (hasAssertion) {
  133. // it's consistent. leave it in the set and we'll
  134. // run it up to our parent.
  135. isconsistent = true;
  136. break;
  137. }
  138. }
  139. }
  140. MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
  141. (" consistency check => %s", isconsistent ? "passed" : "failed"));
  142. if (isconsistent) {
  143. // Add a memory element to our set-of-support.
  144. Element* element =
  145. new nsRDFConMemberTestNode::Element(containerRes,
  146. memberValue);
  147. inst->AddSupportingElement(element);
  148. }
  149. else {
  150. // it's inconsistent. remove it.
  151. aInstantiations.Erase(inst--);
  152. }
  153. // We're done, go on to the next instantiation
  154. continue;
  155. }
  156. if (hasContainerBinding && rdfcontainer) {
  157. // We've got a container assignment, and the container is
  158. // bound to an RDF container. Add each member as a new
  159. // instantiation.
  160. nsCOMPtr<nsISimpleEnumerator> elements;
  161. rv = rdfcontainer->GetElements(getter_AddRefs(elements));
  162. if (NS_FAILED(rv)) return rv;
  163. while (1) {
  164. bool hasmore;
  165. rv = elements->HasMoreElements(&hasmore);
  166. if (NS_FAILED(rv)) return rv;
  167. if (! hasmore)
  168. break;
  169. nsCOMPtr<nsISupports> isupports;
  170. rv = elements->GetNext(getter_AddRefs(isupports));
  171. if (NS_FAILED(rv)) return rv;
  172. nsCOMPtr<nsIRDFNode> node = do_QueryInterface(isupports);
  173. if (! node)
  174. return NS_ERROR_UNEXPECTED;
  175. if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) {
  176. nsAutoString member;
  177. nsXULContentUtils::GetTextForNode(node, member);
  178. MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
  179. (" member => %s", NS_ConvertUTF16toUTF8(member).get()));
  180. }
  181. Instantiation newinst = *inst;
  182. newinst.AddAssignment(mMemberVariable, node);
  183. Element* element =
  184. new nsRDFConMemberTestNode::Element(containerRes, node);
  185. newinst.AddSupportingElement(element);
  186. aInstantiations.Insert(inst, newinst);
  187. }
  188. }
  189. if (hasMemberBinding) {
  190. // Oh, this is so nasty. If we have a member assignment, then
  191. // grovel through each one of our inbound arcs to see if
  192. // any of them are ordinal properties (like an RDF
  193. // container might have). If so, walk it backwards to get
  194. // the container we're in.
  195. nsCOMPtr<nsISimpleEnumerator> arcsin;
  196. rv = ds->ArcLabelsIn(memberValue, getter_AddRefs(arcsin));
  197. if (NS_FAILED(rv)) return rv;
  198. while (1) {
  199. nsCOMPtr<nsIRDFResource> property;
  200. {
  201. bool hasmore;
  202. rv = arcsin->HasMoreElements(&hasmore);
  203. if (NS_FAILED(rv)) return rv;
  204. if (! hasmore)
  205. break;
  206. nsCOMPtr<nsISupports> isupports;
  207. rv = arcsin->GetNext(getter_AddRefs(isupports));
  208. if (NS_FAILED(rv)) return rv;
  209. property = do_QueryInterface(isupports);
  210. if (! property)
  211. return NS_ERROR_UNEXPECTED;
  212. }
  213. // Ordinal properties automagically indicate container
  214. // membership as far as we're concerned. Note that
  215. // we're *only* concerned with ordinal properties
  216. // here: the next block will worry about the other
  217. // membership properties.
  218. bool isordinal;
  219. rv = rdfc->IsOrdinalProperty(property, &isordinal);
  220. if (NS_FAILED(rv)) return rv;
  221. if (isordinal) {
  222. // If we get here, we've found a property that
  223. // indicates container membership leading *into* a
  224. // member node. Find all the people that point to
  225. // it, and call them containers.
  226. nsCOMPtr<nsISimpleEnumerator> sources;
  227. rv = ds->GetSources(property, memberValue, true,
  228. getter_AddRefs(sources));
  229. if (NS_FAILED(rv)) return rv;
  230. while (1) {
  231. bool hasmore;
  232. rv = sources->HasMoreElements(&hasmore);
  233. if (NS_FAILED(rv)) return rv;
  234. if (! hasmore)
  235. break;
  236. nsCOMPtr<nsISupports> isupports;
  237. rv = sources->GetNext(getter_AddRefs(isupports));
  238. if (NS_FAILED(rv)) return rv;
  239. nsCOMPtr<nsIRDFResource> source = do_QueryInterface(isupports);
  240. if (! source)
  241. return NS_ERROR_UNEXPECTED;
  242. if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) {
  243. const char* container;
  244. source->GetValueConst(&container);
  245. MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
  246. (" container => %s", container));
  247. }
  248. // Add a new instantiation
  249. Instantiation newinst = *inst;
  250. newinst.AddAssignment(mContainerVariable, source);
  251. Element* element =
  252. new nsRDFConMemberTestNode::Element(source,
  253. memberValue);
  254. newinst.AddSupportingElement(element);
  255. aInstantiations.Insert(inst, newinst);
  256. }
  257. }
  258. }
  259. }
  260. if ((hasContainerBinding && ! hasMemberBinding) ||
  261. (! hasContainerBinding && hasMemberBinding)) {
  262. // it's an open ended query on the container or member. go
  263. // through our containment properties to see if anything
  264. // applies.
  265. nsResourceSet& containmentProps = mProcessor->ContainmentProperties();
  266. for (nsResourceSet::ConstIterator property = containmentProps.First();
  267. property != containmentProps.Last();
  268. ++property) {
  269. nsCOMPtr<nsISimpleEnumerator> results;
  270. if (hasContainerBinding) {
  271. rv = ds->GetTargets(containerRes, *property, true,
  272. getter_AddRefs(results));
  273. }
  274. else {
  275. rv = ds->GetSources(*property, memberValue, true,
  276. getter_AddRefs(results));
  277. }
  278. if (NS_FAILED(rv)) return rv;
  279. while (1) {
  280. bool hasmore;
  281. rv = results->HasMoreElements(&hasmore);
  282. if (NS_FAILED(rv)) return rv;
  283. if (! hasmore)
  284. break;
  285. nsCOMPtr<nsISupports> isupports;
  286. rv = results->GetNext(getter_AddRefs(isupports));
  287. if (NS_FAILED(rv)) return rv;
  288. nsIAtom* variable;
  289. nsCOMPtr<nsIRDFNode> value;
  290. nsCOMPtr<nsIRDFResource> valueRes;
  291. if (hasContainerBinding) {
  292. variable = mMemberVariable;
  293. value = do_QueryInterface(isupports);
  294. NS_ASSERTION(value != nullptr, "member is not an nsIRDFNode");
  295. if (! value) continue;
  296. if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) {
  297. nsAutoString s;
  298. nsXULContentUtils::GetTextForNode(value, s);
  299. MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
  300. (" member => %s", NS_ConvertUTF16toUTF8(s).get()));
  301. }
  302. }
  303. else {
  304. variable = mContainerVariable;
  305. valueRes = do_QueryInterface(isupports);
  306. NS_ASSERTION(valueRes != nullptr, "container is not an nsIRDFResource");
  307. if (! valueRes) continue;
  308. value = valueRes;
  309. if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) {
  310. const char* s;
  311. valueRes->GetValueConst(&s);
  312. MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
  313. (" container => %s", s));
  314. }
  315. }
  316. // Copy the original instantiation, and add it to the
  317. // instantiation set with the new assignment that we've
  318. // introduced. Ownership will be transferred to the
  319. Instantiation newinst = *inst;
  320. newinst.AddAssignment(variable, value);
  321. Element* element;
  322. if (hasContainerBinding) {
  323. element =
  324. new nsRDFConMemberTestNode::Element(containerRes, value);
  325. }
  326. else {
  327. element =
  328. new nsRDFConMemberTestNode::Element(valueRes, memberValue);
  329. }
  330. if (! element)
  331. return NS_ERROR_OUT_OF_MEMORY;
  332. newinst.AddSupportingElement(element);
  333. aInstantiations.Insert(inst, newinst);
  334. }
  335. }
  336. }
  337. if (! hasContainerBinding && ! hasMemberBinding) {
  338. // Neither container nor member assignment!
  339. if (!aCantHandleYet) {
  340. nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_MEMBER_UNBOUND);
  341. return NS_ERROR_UNEXPECTED;
  342. }
  343. *aCantHandleYet = true;
  344. return NS_OK;
  345. }
  346. // finally, remove the "under specified" instantiation.
  347. aInstantiations.Erase(inst--);
  348. }
  349. return NS_OK;
  350. }
  351. bool
  352. nsRDFConMemberTestNode::CanPropagate(nsIRDFResource* aSource,
  353. nsIRDFResource* aProperty,
  354. nsIRDFNode* aTarget,
  355. Instantiation& aInitialBindings) const
  356. {
  357. nsresult rv;
  358. bool canpropagate = false;
  359. nsCOMPtr<nsIRDFContainerUtils> rdfc =
  360. do_GetService("@mozilla.org/rdf/container-utils;1");
  361. if (! rdfc)
  362. return false;
  363. // We can certainly propagate ordinal properties
  364. rv = rdfc->IsOrdinalProperty(aProperty, &canpropagate);
  365. if (NS_FAILED(rv)) return false;
  366. if (! canpropagate) {
  367. canpropagate = mProcessor->ContainmentProperties().Contains(aProperty);
  368. }
  369. if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) {
  370. const char* source;
  371. aSource->GetValueConst(&source);
  372. const char* property;
  373. aProperty->GetValueConst(&property);
  374. nsAutoString target;
  375. nsXULContentUtils::GetTextForNode(aTarget, target);
  376. MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
  377. ("nsRDFConMemberTestNode[%p]: CanPropagate([%s]==[%s]=>[%s]) => %s",
  378. this, source, property, NS_ConvertUTF16toUTF8(target).get(),
  379. canpropagate ? "true" : "false"));
  380. }
  381. if (canpropagate) {
  382. aInitialBindings.AddAssignment(mContainerVariable, aSource);
  383. aInitialBindings.AddAssignment(mMemberVariable, aTarget);
  384. return true;
  385. }
  386. return false;
  387. }
  388. void
  389. nsRDFConMemberTestNode::Retract(nsIRDFResource* aSource,
  390. nsIRDFResource* aProperty,
  391. nsIRDFNode* aTarget) const
  392. {
  393. bool canretract = false;
  394. nsCOMPtr<nsIRDFContainerUtils> rdfc =
  395. do_GetService("@mozilla.org/rdf/container-utils;1");
  396. if (! rdfc)
  397. return;
  398. // We can certainly retract ordinal properties
  399. nsresult rv;
  400. rv = rdfc->IsOrdinalProperty(aProperty, &canretract);
  401. if (NS_FAILED(rv)) return;
  402. if (! canretract) {
  403. canretract = mProcessor->ContainmentProperties().Contains(aProperty);
  404. }
  405. if (canretract) {
  406. mProcessor->RetractElement(Element(aSource, aTarget));
  407. }
  408. }