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 
6 
7 #ifndef nsXULTemplateQueryProcessorRDF_h__
8 #define nsXULTemplateQueryProcessorRDF_h__
9 
10 #include "nsIRDFContainer.h"
11 #include "nsIRDFContainerUtils.h"
12 #include "nsIRDFDataSource.h"
13 #include "nsIRDFObserver.h"
14 #include "nsIRDFService.h"
15 #include "nsIXULTemplateBuilder.h"
16 #include "nsIXULTemplateQueryProcessor.h"
17 #include "nsCollationCID.h"
18 
19 #include "nsResourceSet.h"
20 #include "nsRuleNetwork.h"
21 #include "nsRDFQuery.h"
22 #include "nsRDFBinding.h"
23 #include "nsXULTemplateResultSetRDF.h"
24 #include "nsCOMArray.h"
25 #include "nsString.h"
26 #include "nsClassHashtable.h"
27 #include "nsRefPtrHashtable.h"
28 #include "nsCycleCollectionParticipant.h"
29 #include "mozilla/Attributes.h"
30 
31 #include "mozilla/Logging.h"
32 extern mozilla::LazyLogModule gXULTemplateLog;
33 
34 class nsIContent;
35 class nsXULTemplateResultRDF;
36 
37 /**
38  * An object that generates results from a query on an RDF graph
39  */
40 class nsXULTemplateQueryProcessorRDF final : public nsIXULTemplateQueryProcessor,
41                                              public nsIRDFObserver
42 {
43 public:
44     typedef nsTArray<RefPtr<nsXULTemplateResultRDF> > ResultArray;
45 
46     nsXULTemplateQueryProcessorRDF();
47 
48     nsresult InitGlobals();
49 
50     // nsISupports interface
51     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
52     NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXULTemplateQueryProcessorRDF,
53                                              nsIXULTemplateQueryProcessor)
54 
55     // nsIXULTemplateQueryProcessor interface
56     NS_DECL_NSIXULTEMPLATEQUERYPROCESSOR
57 
58     // nsIRDFObserver interface
59     NS_DECL_NSIRDFOBSERVER
60 
61     /*
62      * Propagate all changes through the rule network when an assertion is
63      * added to the graph, adding any new results.
64      */
65     nsresult
66     Propagate(nsIRDFResource* aSource,
67               nsIRDFResource* aProperty,
68               nsIRDFNode* aTarget);
69 
70     /*
71      * Retract all changes through the rule network when an assertion is
72      * removed from the graph, removing any results that no longer match.
73      */
74     nsresult
75     Retract(nsIRDFResource* aSource,
76             nsIRDFResource* aProperty,
77             nsIRDFNode* aTarget);
78 
79     /*
80      * Synchronize results when the graph changes, updating their bindings.
81      */
82     nsresult
83     SynchronizeAll(nsIRDFResource* aSource,
84                    nsIRDFResource* aProperty,
85                    nsIRDFNode* aOldTarget,
86                    nsIRDFNode* aNewTarget);
87 
88     /*
89      * Return true if a resource is a container
90      */
91     nsresult
92     CheckContainer(nsIRDFResource* aTargetResource,
93                    bool* aIsContainer);
94 
95     /*
96      * Check if a resource does not have any children
97      */
98     nsresult
99     CheckEmpty(nsIRDFResource* aTargetResource,
100                bool* aIsEmpty);
101 
102     /**
103      * Check if a resource is a separator
104      */
105     nsresult
106     CheckIsSeparator(nsIRDFResource* aResource, bool* aIsSeparator);
107 
108     /*
109      * Compute the containment properties which are additional arcs which
110      * indicate that a node is a container, in additional to the RDF container
111      * tests. The computed list is stored in mContainmentProperties
112      */
113     nsresult
114     ComputeContainmentProperties(nsIDOMNode* aRootNode);
115 
116     /**
117      * Compile a query that uses the extended template syntax. The last
118      * compiled node of the query is returned as aLastNode. This node will
119      * have been added to mAllTests which owns the node.
120      */
121     nsresult
122     CompileExtendedQuery(nsRDFQuery* aQuery,
123                          nsIContent* aConditions,
124                          TestNode** aLastNode);
125 
126     /**
127      * Compile a single query child and return the compiled node in aResult.
128      * This node will have been added to mAllTests which owns the node and
129      * set as a child of aParentNode.
130      */
131     virtual nsresult
132     CompileQueryChild(nsIAtom* aTag,
133                       nsRDFQuery* aQuery,
134                       nsIContent* aConditions,
135                       TestNode* aParentNode,
136                       TestNode** aResult);
137 
138     /**
139      * Parse the value of a property test assertion for a condition or a simple
140      * rule based on the parseType attribute into the appropriate literal type.
141      */
142     nsresult ParseLiteral(const nsString& aParseType,
143                           const nsString& aValue,
144                           nsIRDFNode** aResult);
145 
146     /**
147      * Compile a <triple> condition and return the compiled node in aResult.
148      * This node will have been added to mAllTests which owns the node and
149      * set as a child of aParentNode.
150      */
151     nsresult
152     CompileTripleCondition(nsRDFQuery* aQuery,
153                            nsIContent* aCondition,
154                            TestNode* aParentNode,
155                            TestNode** aResult);
156 
157     /**
158      * Compile a <member> condition and return the compiled node in aResult.
159      * This node will have been added to mAllTests which owns the node and
160      * set as a child of aParentNode.
161      */
162     nsresult
163     CompileMemberCondition(nsRDFQuery* aQuery,
164                            nsIContent* aCondition,
165                            TestNode* aParentNode,
166                            TestNode** aResult);
167 
168     /**
169      * Add the default rules shared by all simple queries. This creates
170      * the content start node followed by a member test. The member TestNode
171      * is returned in aChildNode. Both nodes will have been added to mAllTests
172      * which owns the nodes.
173      */
174     nsresult
175     AddDefaultSimpleRules(nsRDFQuery* aQuery,
176                           TestNode** aChildNode);
177 
178     /**
179      * Compile a query that's specified using the simple template
180      * syntax. Each  TestNode is created in a chain, the last compiled node
181      * is returned as aLastNode. All nodes will have been added to mAllTests
182      * which owns the nodes.
183      */
184     nsresult
185     CompileSimpleQuery(nsRDFQuery* aQuery,
186                       nsIContent* aQueryElement,
187                       TestNode** aLastNode);
188 
189     RDFBindingSet*
190     GetBindingsForRule(nsIDOMNode* aRule);
191 
192     /*
193      * Indicate that a result is dependant on a particular resource. When an
194      * assertion is added to or removed from the graph involving that
195      * resource, that result must be recalculated.
196      */
197     void
198     AddBindingDependency(nsXULTemplateResultRDF* aResult,
199                          nsIRDFResource* aResource);
200 
201     /**
202      * Remove a dependency a result has on a particular resource.
203      */
204     void
205     RemoveBindingDependency(nsXULTemplateResultRDF* aResult,
206                             nsIRDFResource* aResource);
207 
208     /**
209      * A memory element is a hash of an RDF triple. One exists for each triple
210      * that was involved in generating a result. This function adds this to a
211      * map, keyed by memory element, when the value is a list of results that
212      * depend on that memory element. When an RDF triple is removed from the
213      * datasource, RetractElement is called, and this map is examined to
214      * determine which results are no longer valid.
215      */
216     nsresult
217     AddMemoryElements(const Instantiation& aInst,
218                       nsXULTemplateResultRDF* aResult);
219 
220     /**
221      * Remove the memory elements associated with a result when the result is
222      * no longer being used.
223      */
224     nsresult
225     RemoveMemoryElements(const Instantiation& aInst,
226                          nsXULTemplateResultRDF* aResult);
227 
228     /**
229      * Remove the results associated with a memory element since the
230      * RDF triple the memory element is a hash of has been removed.
231      */
232     void RetractElement(const MemoryElement& aMemoryElement);
233 
234     /**
235      * Return the index of a result's resource in its RDF container
236      */
237     int32_t
238     GetContainerIndexOf(nsIXULTemplateResult* aResult);
239 
240     /**
241      * Given a result and a predicate to sort on, get the target value of
242      * the triple to use for sorting. The sort predicate is the predicate
243      * with '?sort=true' appended.
244      */
245     nsresult
246     GetSortValue(nsIXULTemplateResult* aResult,
247                  nsIRDFResource* aPredicate,
248                  nsIRDFResource* aSortPredicate,
249                  nsISupports** aResultNode);
250 
GetDataSource()251     nsIRDFDataSource* GetDataSource() { return mDB; }
252 
GetBuilder()253     nsIXULTemplateBuilder* GetBuilder() { return mBuilder; }
254 
ContainmentProperties()255     nsResourceSet& ContainmentProperties() { return mContainmentProperties; }
256 
257     nsresult
258     Log(const char* aOperation,
259         nsIRDFResource* aSource,
260         nsIRDFResource* aProperty,
261         nsIRDFNode* aTarget);
262 
263 #define LOG(_op, _src, _prop, _targ) \
264     Log(_op, _src, _prop, _targ)
265 
266 protected:
267     ~nsXULTemplateQueryProcessorRDF();
268 
269     // We are an observer of the composite datasource. The cycle is
270     // broken when the document is destroyed.
271     nsCOMPtr<nsIRDFDataSource> mDB;
272 
273     // weak reference to the builder, cleared when the document is destroyed
274     nsIXULTemplateBuilder* mBuilder;
275 
276     // true if the query processor has been initialized
277     bool mQueryProcessorRDFInited;
278 
279     // true if results have been generated. Once set, bindings can no longer
280     // be added. If they were, the binding value arrays for results that have
281     // already been generated would be the wrong size
282     bool mGenerationStarted;
283 
284     // nesting level for RDF batch notifications
285     int32_t mUpdateBatchNest;
286 
287     // containment properties that are checked to determine if a resource is
288     // a container
289     nsResourceSet mContainmentProperties;
290 
291     // the end node of the default simple node hierarchy
292     TestNode* mSimpleRuleMemberTest;
293 
294     // the reference variable
295     nsCOMPtr<nsIAtom> mRefVariable;
296 
297     // the last ref that was calculated, used for simple rules
298     nsCOMPtr<nsIXULTemplateResult> mLastRef;
299 
300     /**
301      * A map between nsIRDFNodes that form the left-hand side (the subject) of
302      * a <binding> and an array of nsIXULTemplateResults. When a new assertion
303      * is added to the graph involving a particular rdf node, it is looked up
304      * in this binding map. If it exists, the corresponding results must then
305      * be synchronized.
306      */
307     nsClassHashtable<nsISupportsHashKey, ResultArray> mBindingDependencies;
308 
309     /**
310      * A map between memory elements and an array of nsIXULTemplateResults.
311      * When a triple is unasserted from the graph, the corresponding results
312      * no longer match so they must be removed.
313      */
314     nsClassHashtable<nsUint32HashKey,
315                      nsCOMArray<nsXULTemplateResultRDF> > mMemoryElementToResultMap;
316 
317     // map of the rules to the bindings for those rules.
318     // XXXndeakin this might be better just as an array since there is usually
319     //            ten or fewer rules
320     nsRefPtrHashtable<nsISupportsHashKey, RDFBindingSet> mRuleToBindingsMap;
321 
322     /**
323      * The queries
324      */
325     nsTArray<nsCOMPtr<nsITemplateRDFQuery> > mQueries;
326 
327     /**
328      * All of the RDF tests in the rule network, which are checked when a new
329      * assertion is added to the graph. This is a subset of mAllTests, which
330      * also includes non-RDF tests.
331      */
332     ReteNodeSet mRDFTests;
333 
334     /**
335      * All of the tests in the rule network, owned by this list
336      */
337     ReteNodeSet mAllTests;
338 
339     // pseudo-constants
340     static nsrefcnt gRefCnt;
341 
342 public:
343     static nsIRDFService*            gRDFService;
344     static nsIRDFContainerUtils*     gRDFContainerUtils;
345     static nsIRDFResource*           kNC_BookmarkSeparator;
346     static nsIRDFResource*           kRDF_type;
347 };
348 
349 #endif // nsXULTemplateQueryProcessorRDF_h__
350