/content/xul/templates/src/nsRuleNetwork.cpp

http://github.com/zpao/v8monkey · C++ · 502 lines · 303 code · 102 blank · 97 comment · 66 complexity · b8a470304092f7ff2a465f1abfe06cf5 MD5 · raw file

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. * http://www.mozilla.org/MPL/
  9. *
  10. * Software distributed under the License is distributed on an "AS IS" basis,
  11. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. * for the specific language governing rights and limitations under the
  13. * License.
  14. *
  15. * The Original Code is mozilla.org code.
  16. *
  17. * The Initial Developer of the Original Code is
  18. * Netscape Communications Corporation.
  19. * Portions created by the Initial Developer are Copyright (C) 2000
  20. * the Initial Developer. All Rights Reserved.
  21. *
  22. * Contributor(s):
  23. * Chris Waterson <waterson@netscape.com>
  24. * Neil Deakin <enndeakin@sympatico.ca>
  25. *
  26. * Alternatively, the contents of this file may be used under the terms of
  27. * either of the GNU General Public License Version 2 or later (the "GPL"),
  28. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  29. * in which case the provisions of the GPL or the LGPL are applicable instead
  30. * of those above. If you wish to allow use of your version of this file only
  31. * under the terms of either the GPL or the LGPL, and not to allow others to
  32. * use your version of this file under the terms of the MPL, indicate your
  33. * decision by deleting the provisions above and replace them with the notice
  34. * and other provisions required by the GPL or the LGPL. If you do not delete
  35. * the provisions above, a recipient may use your version of this file under
  36. * the terms of any one of the MPL, the GPL or the LGPL.
  37. *
  38. * ***** END LICENSE BLOCK ***** */
  39. /*
  40. Implementations for the rule network classes.
  41. To Do.
  42. - Constrain() & Propagate() still feel like they are poorly named.
  43. - As do Instantiation and InstantiationSet.
  44. - Make InstantiationSet share and do copy-on-write.
  45. - Make things iterative, instead of recursive.
  46. */
  47. #include "mozilla/Util.h"
  48. #include "nscore.h"
  49. #include "nsCOMPtr.h"
  50. #include "nsCRT.h"
  51. #include "nsIComponentManager.h"
  52. #include "nsIContent.h"
  53. #include "plhash.h"
  54. #include "nsReadableUtils.h"
  55. #include "prlog.h"
  56. #ifdef PR_LOGGING
  57. extern PRLogModuleInfo* gXULTemplateLog;
  58. #include "nsString.h"
  59. #include "nsUnicharUtils.h"
  60. #include "nsXULContentUtils.h"
  61. #endif
  62. #include "nsRuleNetwork.h"
  63. #include "nsXULTemplateResultSetRDF.h"
  64. #include "nsRDFConMemberTestNode.h"
  65. #include "nsRDFPropertyTestNode.h"
  66. using namespace mozilla;
  67. bool MemoryElement::gPoolInited;
  68. nsFixedSizeAllocator MemoryElement::gPool;
  69. // static
  70. bool
  71. MemoryElement::Init()
  72. {
  73. if (!gPoolInited) {
  74. const size_t bucketsizes[] = {
  75. sizeof (nsRDFConMemberTestNode::Element),
  76. sizeof (nsRDFPropertyTestNode::Element)
  77. };
  78. if (NS_FAILED(gPool.Init("MemoryElement", bucketsizes,
  79. ArrayLength(bucketsizes), 256)))
  80. return false;
  81. gPoolInited = true;
  82. }
  83. return true;
  84. }
  85. //----------------------------------------------------------------------
  86. //
  87. // nsRuleNetwork
  88. //
  89. nsresult
  90. MemoryElementSet::Add(MemoryElement* aElement)
  91. {
  92. for (ConstIterator element = First(); element != Last(); ++element) {
  93. if (*element == *aElement) {
  94. // We've already got this element covered. Since Add()
  95. // assumes ownership, and we aren't going to need this,
  96. // just nuke it.
  97. aElement->Destroy();
  98. return NS_OK;
  99. }
  100. }
  101. List* list = new List;
  102. if (! list)
  103. return NS_ERROR_OUT_OF_MEMORY;
  104. list->mElement = aElement;
  105. list->mRefCnt = 1;
  106. list->mNext = mElements;
  107. mElements = list;
  108. return NS_OK;
  109. }
  110. //----------------------------------------------------------------------
  111. nsresult
  112. nsAssignmentSet::Add(const nsAssignment& aAssignment)
  113. {
  114. NS_PRECONDITION(! HasAssignmentFor(aAssignment.mVariable), "variable already bound");
  115. // XXXndeakin should this just silently fail?
  116. if (HasAssignmentFor(aAssignment.mVariable))
  117. return NS_ERROR_UNEXPECTED;
  118. List* list = new List(aAssignment);
  119. if (! list)
  120. return NS_ERROR_OUT_OF_MEMORY;
  121. list->mRefCnt = 1;
  122. list->mNext = mAssignments;
  123. mAssignments = list;
  124. return NS_OK;
  125. }
  126. PRInt32
  127. nsAssignmentSet::Count() const
  128. {
  129. PRInt32 count = 0;
  130. for (ConstIterator assignment = First(); assignment != Last(); ++assignment)
  131. ++count;
  132. return count;
  133. }
  134. bool
  135. nsAssignmentSet::HasAssignment(nsIAtom* aVariable, nsIRDFNode* aValue) const
  136. {
  137. for (ConstIterator assignment = First(); assignment != Last(); ++assignment) {
  138. if (assignment->mVariable == aVariable && assignment->mValue == aValue)
  139. return true;
  140. }
  141. return false;
  142. }
  143. bool
  144. nsAssignmentSet::HasAssignmentFor(nsIAtom* aVariable) const
  145. {
  146. for (ConstIterator assignment = First(); assignment != Last(); ++assignment) {
  147. if (assignment->mVariable == aVariable)
  148. return true;
  149. }
  150. return false;
  151. }
  152. bool
  153. nsAssignmentSet::GetAssignmentFor(nsIAtom* aVariable, nsIRDFNode** aValue) const
  154. {
  155. for (ConstIterator assignment = First(); assignment != Last(); ++assignment) {
  156. if (assignment->mVariable == aVariable) {
  157. *aValue = assignment->mValue;
  158. NS_IF_ADDREF(*aValue);
  159. return true;
  160. }
  161. }
  162. *aValue = nsnull;
  163. return false;
  164. }
  165. bool
  166. nsAssignmentSet::Equals(const nsAssignmentSet& aSet) const
  167. {
  168. if (aSet.mAssignments == mAssignments)
  169. return true;
  170. // If they have a different number of assignments, then they're different.
  171. if (Count() != aSet.Count())
  172. return false;
  173. // XXX O(n^2)! Ugh!
  174. nsCOMPtr<nsIRDFNode> value;
  175. for (ConstIterator assignment = First(); assignment != Last(); ++assignment) {
  176. if (! aSet.GetAssignmentFor(assignment->mVariable, getter_AddRefs(value)))
  177. return false;
  178. if (assignment->mValue != value)
  179. return false;
  180. }
  181. return true;
  182. }
  183. //----------------------------------------------------------------------
  184. PLHashNumber
  185. Instantiation::Hash(const void* aKey)
  186. {
  187. const Instantiation* inst = static_cast<const Instantiation*>(aKey);
  188. PLHashNumber result = 0;
  189. nsAssignmentSet::ConstIterator last = inst->mAssignments.Last();
  190. for (nsAssignmentSet::ConstIterator assignment = inst->mAssignments.First();
  191. assignment != last; ++assignment)
  192. result ^= assignment->Hash();
  193. return result;
  194. }
  195. PRIntn
  196. Instantiation::Compare(const void* aLeft, const void* aRight)
  197. {
  198. const Instantiation* left = static_cast<const Instantiation*>(aLeft);
  199. const Instantiation* right = static_cast<const Instantiation*>(aRight);
  200. return *left == *right;
  201. }
  202. //----------------------------------------------------------------------
  203. //
  204. // InstantiationSet
  205. //
  206. InstantiationSet::InstantiationSet()
  207. {
  208. mHead.mPrev = mHead.mNext = &mHead;
  209. MOZ_COUNT_CTOR(InstantiationSet);
  210. }
  211. InstantiationSet::InstantiationSet(const InstantiationSet& aInstantiationSet)
  212. {
  213. mHead.mPrev = mHead.mNext = &mHead;
  214. // XXX replace with copy-on-write foo
  215. ConstIterator last = aInstantiationSet.Last();
  216. for (ConstIterator inst = aInstantiationSet.First(); inst != last; ++inst)
  217. Append(*inst);
  218. MOZ_COUNT_CTOR(InstantiationSet);
  219. }
  220. InstantiationSet&
  221. InstantiationSet::operator=(const InstantiationSet& aInstantiationSet)
  222. {
  223. // XXX replace with copy-on-write foo
  224. Clear();
  225. ConstIterator last = aInstantiationSet.Last();
  226. for (ConstIterator inst = aInstantiationSet.First(); inst != last; ++inst)
  227. Append(*inst);
  228. return *this;
  229. }
  230. void
  231. InstantiationSet::Clear()
  232. {
  233. Iterator inst = First();
  234. while (inst != Last())
  235. Erase(inst++);
  236. }
  237. InstantiationSet::Iterator
  238. InstantiationSet::Insert(Iterator aIterator, const Instantiation& aInstantiation)
  239. {
  240. List* newelement = new List();
  241. if (newelement) {
  242. newelement->mInstantiation = aInstantiation;
  243. aIterator.mCurrent->mPrev->mNext = newelement;
  244. newelement->mNext = aIterator.mCurrent;
  245. newelement->mPrev = aIterator.mCurrent->mPrev;
  246. aIterator.mCurrent->mPrev = newelement;
  247. }
  248. return aIterator;
  249. }
  250. InstantiationSet::Iterator
  251. InstantiationSet::Erase(Iterator aIterator)
  252. {
  253. Iterator result = aIterator;
  254. ++result;
  255. aIterator.mCurrent->mNext->mPrev = aIterator.mCurrent->mPrev;
  256. aIterator.mCurrent->mPrev->mNext = aIterator.mCurrent->mNext;
  257. delete aIterator.mCurrent;
  258. return result;
  259. }
  260. bool
  261. InstantiationSet::HasAssignmentFor(nsIAtom* aVariable) const
  262. {
  263. return !Empty() ? First()->mAssignments.HasAssignmentFor(aVariable) : false;
  264. }
  265. //----------------------------------------------------------------------
  266. //
  267. // ReteNode
  268. //
  269. // The basic node in the network.
  270. //
  271. //----------------------------------------------------------------------
  272. //
  273. // TestNode
  274. //
  275. // to do:
  276. // - FilterInstantiations() is poorly named
  277. //
  278. TestNode::TestNode(TestNode* aParent)
  279. : mParent(aParent)
  280. {
  281. }
  282. nsresult
  283. TestNode::Propagate(InstantiationSet& aInstantiations,
  284. bool aIsUpdate, bool& aTakenInstantiations)
  285. {
  286. PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
  287. ("TestNode[%p]: Propagate() begin", this));
  288. aTakenInstantiations = false;
  289. nsresult rv = FilterInstantiations(aInstantiations, nsnull);
  290. if (NS_FAILED(rv))
  291. return rv;
  292. // if there is more than one child, each will need to be supplied with the
  293. // original set of instantiations from this node, so create a copy in this
  294. // case. If there is only one child, optimize and just pass the
  295. // instantiations along to the child without copying
  296. bool shouldCopy = (mKids.Count() > 1);
  297. // See the header file for details about how instantiation ownership works.
  298. if (! aInstantiations.Empty()) {
  299. ReteNodeSet::Iterator last = mKids.Last();
  300. for (ReteNodeSet::Iterator kid = mKids.First(); kid != last; ++kid) {
  301. PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
  302. ("TestNode[%p]: Propagate() passing to child %p", this, kid.operator->()));
  303. // create a copy of the instantiations
  304. if (shouldCopy) {
  305. bool owned = false;
  306. InstantiationSet* instantiations =
  307. new InstantiationSet(aInstantiations);
  308. if (!instantiations)
  309. return NS_ERROR_OUT_OF_MEMORY;
  310. rv = kid->Propagate(*instantiations, aIsUpdate, owned);
  311. if (!owned)
  312. delete instantiations;
  313. if (NS_FAILED(rv))
  314. return rv;
  315. }
  316. else {
  317. rv = kid->Propagate(aInstantiations, aIsUpdate, aTakenInstantiations);
  318. if (NS_FAILED(rv))
  319. return rv;
  320. }
  321. }
  322. }
  323. PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
  324. ("TestNode[%p]: Propagate() end", this));
  325. return NS_OK;
  326. }
  327. nsresult
  328. TestNode::Constrain(InstantiationSet& aInstantiations)
  329. {
  330. nsresult rv;
  331. PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
  332. ("TestNode[%p]: Constrain() begin", this));
  333. // if the cantHandleYet flag is set by FilterInstantiations,
  334. // there isn't enough information yet available to fill in.
  335. // For this, continue the constrain all the way to the top
  336. // and then call FilterInstantiations again afterwards. This
  337. // should fill in any missing information.
  338. bool cantHandleYet = false;
  339. rv = FilterInstantiations(aInstantiations, &cantHandleYet);
  340. if (NS_FAILED(rv)) return rv;
  341. if (mParent && (!aInstantiations.Empty() || cantHandleYet)) {
  342. // if we still have instantiations, or if the instantiations
  343. // could not be filled in yet, then ride 'em on up to the
  344. // parent to narrow them.
  345. PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
  346. ("TestNode[%p]: Constrain() passing to parent %p", this, mParent));
  347. rv = mParent->Constrain(aInstantiations);
  348. if (NS_SUCCEEDED(rv) && cantHandleYet)
  349. rv = FilterInstantiations(aInstantiations, nsnull);
  350. }
  351. else {
  352. PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
  353. ("TestNode[%p]: Constrain() failed", this));
  354. rv = NS_OK;
  355. }
  356. PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
  357. ("TestNode[%p]: Constrain() end", this));
  358. return rv;
  359. }
  360. //----------------------------------------------------------------------
  361. ReteNodeSet::ReteNodeSet()
  362. : mNodes(nsnull), mCount(0), mCapacity(0)
  363. {
  364. }
  365. ReteNodeSet::~ReteNodeSet()
  366. {
  367. Clear();
  368. }
  369. nsresult
  370. ReteNodeSet::Add(ReteNode* aNode)
  371. {
  372. NS_PRECONDITION(aNode != nsnull, "null ptr");
  373. if (! aNode)
  374. return NS_ERROR_NULL_POINTER;
  375. if (mCount >= mCapacity) {
  376. PRInt32 capacity = mCapacity + 4;
  377. ReteNode** nodes = new ReteNode*[capacity];
  378. if (! nodes)
  379. return NS_ERROR_OUT_OF_MEMORY;
  380. for (PRInt32 i = mCount - 1; i >= 0; --i)
  381. nodes[i] = mNodes[i];
  382. delete[] mNodes;
  383. mNodes = nodes;
  384. mCapacity = capacity;
  385. }
  386. mNodes[mCount++] = aNode;
  387. return NS_OK;
  388. }
  389. nsresult
  390. ReteNodeSet::Clear()
  391. {
  392. delete[] mNodes;
  393. mNodes = nsnull;
  394. mCount = mCapacity = 0;
  395. return NS_OK;
  396. }