Bug 285631, new template builder, r+sr=bz

This commit is contained in:
enndeakin%sympatico.ca 2006-02-13 16:02:08 +00:00
Родитель 0fbe251311
Коммит 5e55605551
55 изменённых файлов: 7956 добавлений и 5105 удалений

Просмотреть файл

@ -356,6 +356,7 @@ GK_ATOM(i, "i")
GK_ATOM(id, "id")
GK_ATOM(_if, "if")
GK_ATOM(iframe, "iframe")
GK_ATOM(ignorecase, "ignorecase")
GK_ATOM(ignorekeys, "ignorekeys")
GK_ATOM(ilayer, "ilayer")
GK_ATOM(image, "image")
@ -464,6 +465,7 @@ GK_ATOM(namespaceAlias, "namespace-alias")
GK_ATOM(namespaceUri, "namespace-uri")
GK_ATOM(NaN, "NaN")
GK_ATOM(nativescrollbar, "nativescrollbar")
GK_ATOM(negate, "negate")
GK_ATOM(never, "never")
GK_ATOM(nextBidi, "NextBidi")
GK_ATOM(no, "no")
@ -611,6 +613,8 @@ GK_ATOM(propagate, "propagate")
GK_ATOM(properties, "properties")
GK_ATOM(property, "property")
GK_ATOM(q, "q")
GK_ATOM(query, "query")
GK_ATOM(queryset, "queryset")
GK_ATOM(radio, "radio")
GK_ATOM(radiogroup, "radiogroup")
GK_ATOM(readonly, "readonly")
@ -777,6 +781,7 @@ GK_ATOM(valign, "valign")
GK_ATOM(value, "value")
GK_ATOM(valueOf, "value-of")
GK_ATOM(valuetype, "valuetype")
GK_ATOM(var, "var")
GK_ATOM(variable, "variable")
GK_ATOM(vbox, "vbox")
GK_ATOM(vcard_name, "vcard_name")
@ -789,6 +794,7 @@ GK_ATOM(vlink, "vlink")
GK_ATOM(vspace, "vspace")
GK_ATOM(wbr, "wbr")
GK_ATOM(when, "when")
GK_ATOM(where, "where")
GK_ATOM(widget, "widget")
GK_ATOM(width, "width")
GK_ATOM(window, "window")

Просмотреть файл

@ -48,6 +48,9 @@ XPIDLSRCS = \
nsIXULSortService.idl \
nsIXULTemplateBuilder.idl \
nsIXULBuilderListener.idl \
nsIXULTemplateQueryProcessor.idl \
nsIXULTemplateResult.idl \
nsIXULTemplateRuleFilter.idl \
$(NULL)
include $(topsrcdir)/config/rules.mk

Просмотреть файл

@ -24,6 +24,7 @@
* Ben Goodger <ben@netscape.com>
* Jan Varga <varga@ku.sk>
* Benjamin Smedberg <bsmedberg@covad.net>
* Neil Deakin <enndeakin@sympatico.ca>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
@ -44,26 +45,148 @@
#include "nsIRDFCompositeDataSource.idl"
#include "nsIRDFResource.idl"
interface nsIAtom;
interface nsIXULBuilderListener;
interface nsIXULTemplateResult;
interface nsIXULTemplateRuleFilter;
interface nsIXULTemplateQueryProcessor;
[ptr] native nsIContent_ptr(nsIContent);
[scriptable, uuid(9da147a7-5854-49e3-a397-22ecdd93e96d)]
/**
* A template builder, given an input source of data, a template, and a
* reference point, generates a list of results from the input, and copies
* part of the template for each result. Templates may generate content
* recursively, using the same template, but with the previous iteration's
* results as the reference point. As an example, for an XML datasource the
* initial reference point would be a specific node in the DOM tree and a
* template might generate a list of all child nodes. For the next iteration,
* those children would be used to generate output for their child nodes and
* so forth.
*
* A template builder is attached to a single DOM node; this node is called
* the root node and is expected to contain a XUL template element as a direct
* child. Different template builders may be specialized in the manner in
* which they generate and display the resulting content from the template.
*
* The structure of a template is as follows:
*
* <rootnode datasources="" ref="">
* <template>
* <queryset>
* <query>
* </query>
* <rule>
* <conditions>...</conditions>
* <bindings>...</bindings>
* <action>...</action>
* </rule>
* </queryset>
* </template>
* </rootnode>
*
* The datasources attribute on the root node is used to identify the source
* of data to be used. The ref attribute is used to specify the reference
* point for the query. Currently, the datasource will either be an
* nsIRDFDataSource or a DOM node. In the future, other datasource types may
* be used.
*
* The <queryset> element contains a single query and one or more <rule>
* elements. There may be more than one <queryset> if multiple queries are
* desired, and this element is optional if only one query is needed -- in
* that case the <query> and <rule>s are allowed to be children of the
* <template> node
*
* The contents of the query are processed by a separate component called a
* query processor. This query processor is expected to use this query to
* generate results when asked by the template builder. The template builder
* then generates output for each result based on the <rule> elements.
*
* This allows the query processor to be specific to a particular kind of
* input data or query syntax, while the template builder remains independent
* of the kind of data being used. Due to this, the query processor will be
* supplied with the datasource and query which the template builder handles
* in an opaque way, while the query processor handles these more
* specifically.
*
* Results implement the nsIXULTemplateResult interface and may be identified
* by an id which must be unique within a given set of query results.
*
* Each query may be accompanied by one or more <rule> elements. These rules
* are evaluated by the template builder for each result produced by the
* query. A rule consists of conditions that cause a rule to be either
* accepted or rejected. The condition syntax allows for common conditional
* handling; additional filtering may be applied by adding a custom filter
* to a rule with the builder's addRuleFilter method.
*
* If a result passes a rule's conditions, this is considered a match, and the
* content within the rule's <action> body is inserted as a sibling of the
* <template>, assuming the template builder creates real DOM content. Only
* one rule will match a result. For a tree builder, for example, the content
* within the action body is used to create the tree rows instead. A matching
* result must have its ruleMatched method called. When a result no longer
* matches, the result's hasBeenRemoved method must be called.
*
* Optionally, the rule may have a <bindings> section which may be used to
* define additional variables to be used within an action body. Each of these
* declared bindings must be supplied to the query processor via its
* addBinding method. The bindings are evaluated after a rule has matched.
*
* Templates may generate content recursively, using the previous iteration's
* results as reference point to invoke the same queries. Since the reference
* point is different, different output will typically be generated.
*
* The reference point nsIXULTemplateResult object for the first iteration is
* determined by calling the query processor's translateRef method using the
* value of the root node's ref attribute. This object may be retrieved later
* via the builder's rootResult property.
*
* For convenience, each reference point as well as all results implement the
* nsIXULTemplateResult interface, allowing the result objects from each
* iteration to be used directly as the reference points for the next
* iteration.
*
* When using multiple queries, each may generate results with the same id.
* More than one of these results may match one of the rules in their
* respective queries, however only the result for the earliest matching query
* in the template becomes the active match and generates output. The
* addResult, removeResult, replaceResult and resultBindingChanged methods may
* be called by the query processor to indicate that the set of valid results
* has changed, such that a different query may match. If a different match
* would become active, the content for the existing match is removed and the
* content for the new match is generated. A query processor is not required
* to provide any support for updating results after they have been generated.
*
* See http://wiki.mozilla.org/XUL:Templates_Plan for details about templates.
*/
[scriptable, uuid(e025ef24-d508-11d9-9633-cd2d4a843228)]
interface nsIXULTemplateBuilder : nsISupports
{
/**
* The ``root'' node in the DOM to which this builder is attached
* The root node in the DOM to which this builder is attached.
*/
readonly attribute nsIDOMElement root;
/**
* The composite datasource that the template builder observes
* and uses to create content
* and uses to create content. This is used only for RDF queries and
* is maintained for backwards compatibility.
*/
readonly attribute nsIRDFCompositeDataSource database;
/**
* Force the template builder to rebuild its content.
* The virtual result representing the starting reference point,
* determined by calling the query processor's translateRef method
* with the root node's ref attribute as an argument.
*/
readonly attribute nsIXULTemplateResult rootResult;
/**
* Force the template builder to rebuild its content. All existing content
* will be removed first. The query processor's done() method will be
* invoked during cleanup, followed by its initializeForBuilding method
* when the content is to be regenerated.
*
*/
void rebuild();
@ -76,6 +199,100 @@ interface nsIXULTemplateBuilder : nsISupports
*/
void refresh();
/**
* Inform the template builder that a new result is available. The builder
* will add this result to the set of results. The query node that the
* new result applies to must be specified using the aQueryNode parameter.
*
* The builder will apply the rules associated with the query to the new
* result, unless a result with the same id from an earlier query
* supersedes it, and the result's RuleMatched method will be called if it
* matches.
*
* @param aResult the result to add
* @param aQueryNode the query that the result applies to
*
* @throws NS_ERROR_NULL_POINTER if aResult or aQueryNode are null
*/
void addResult(in nsIXULTemplateResult aResult, in nsIDOMNode aQueryNode);
/**
* Inform the template builder that a result no longer applies. The builder
* will call the remove content generated for the result, if any. If a different
* query would then match instead, it will become the active match. This
* method will have no effect if the result isn't known to the builder.
*
* @param aResult the result to remove
*
* @throws NS_ERROR_NULL_POINTER if aResult is null
*/
void removeResult(in nsIXULTemplateResult aResult);
/**
* Inform the template builder that one result should be replaced with
* another. Both the old result (aOldResult) and the new result
* (aNewResult) must have the same id. The query node that the new result
* applies to must be specified using the aQueryNode parameter.
*
* This method is expected to have the same effect as calling both
* removeResult for the old result and addResult for the new result.
*
* @param aOldResult the old result
* @param aNewResult the new result
* @param aQueryNode the query that the new result applies to
*
* @throws NS_ERROR_NULL_POINTER if either argument is null, or
* NS_ERROR_INVALID_ARG if the ids don't match
*/
void replaceResult(in nsIXULTemplateResult aOldResult,
in nsIXULTemplateResult aNewResult,
in nsIDOMNode aQueryNode);
/**
* Inform the template builder that one or more of the optional bindings
* for a result has changed. In this case, the rules are not reapplied as
* it is expected that the same rule will still apply. The builder will
* resynchronize any variables that are referenced in the action body.
*
* @param aResult the result to change
*
* @throws NS_ERROR_NULL_POINTER if aResult is null
*/
void resultBindingChanged(in nsIXULTemplateResult aResult);
/**
* Return the result for a given id. Only one such result is returned and
* is always the result with that id associated with the active match.
* This method will return null is there is no result for the id.
*
* @param aId the id to return the result for
*/
nsIXULTemplateResult getResultForId(in AString aId);
/**
* Returns true if the node has content generated for it. This method is
* intended to be called only by the RDF query processor. If aTag is set,
* the content must have a tag name that matches aTag. aTag may be ignored
* for builders that don't generate real DOM content.
*
* @param aNode node to check
* @param aTag tag that must match
*/
boolean hasGeneratedContent(in nsIRDFResource aNode, in nsIAtom aTag);
/**
* Adds a rule filter for a given rule, which may be used for specialized
* rule filtering. Any existing filter on the rule is removed. The default
* conditions specified inside the <rule> tag are applied before the
* rule filter is applied, meaning that the filter may be used to further
* filter out results but not reaccept results that have already been
* rejected.
*
* @param aRule the rule to apply the filter to
* @param aFilter the filter to add
*/
void addRuleFilter(in nsIDOMNode aRule, in nsIXULTemplateRuleFilter aFilter);
/**
* Called to initialize a XUL content builder on a particular root
* element. This element presumably has a ``datasources''
@ -131,7 +348,7 @@ interface nsIXULTreeBuilderObserver : nsISupports
/**
* Called when an item is opened or closed.
*/
void onToggleOpenState (in long index);
void onToggleOpenState (in long index);
/**
* Called when a header is clicked.

Просмотреть файл

@ -0,0 +1,265 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Neil Deakin.
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "domstubs.idl"
#include "nsISupports.idl"
interface nsIAtom;
interface nsISimpleEnumerator;
interface nsIXULTemplateResult;
interface nsIXULTemplateRuleFilter;
interface nsIXULTemplateBuilder;
/**
* A query processor takes a template query and generates results for it given
* a datasource and a reference point. There is a one-to-one relationship
* between a template builder and a query processor. The template builder
* creates the query processor, and there is no other means to retrieve it.
*
* A template query is the contents inside a <query> element within the
* template. The actual syntax is opaque to the template builder and defined
* by a query processor. The query is expected to consist of either text or
* DOM nodes that, when executed by a call to the generateResults method, will
* allow the generation of a list of results.
*
* The template builder will supply two variables, the reference variable and
* the member variable to further indicate what part of the datasource is to
* be examined in addition to the query itself. The reference is always
* a placeholder for the starting point and the member is always a placeholder
* for the end points (the results).
*
* The reference point is important when generating output recursively, as
* the query will be the same for each iteration, however, the reference point
* will differ.
*
* For instance, when examining an XML source, an XML query processor might
* begin at the node referred by the reference variable and end at a list of
* that node's children.
*
* Some queries may not need the reference variable if the syntax or the form
* of the data implies the value. For instance, a datasource that holds a
* table that can only produce one set of results.
*
* The reference variable may be specified in a template by setting the
* "container" attribute on the <template> element to the variable to use. The
* member variable may be specified in a similar way using the "member"
* attribute, or it may be specified in the first <action> body in the
* template as the value of a uri attribute on an element. A breadth-first
* search of the first action is performed to find this element.
*
* If unspecified, the default value of the reference variable is ?uri.
*
* For example, a query might have the following syntax:
*
* (?id, ?name, ?url) from Bookmarks where parentfolder = ?start
*
* This query might generate a result for each bookmark within a given folder.
* The variable ?start would be the reference variable, while the variable ?id
* would be the member variable, since it is the unique value that identifies
* a result. Each result will have the four variables referred to defined for
* it and the values may be retrieved using the result's getBindingFor and
* getBindingObjectFor methods.
*
* The template builder must call initializeForBuilding before the other
* methods, except for translateRef. The builder will then call compileQuery
* for each query in the template to compile the queries. When results need
* to be generated, the builder will call generateResults. The
* initializeForBuilding, compileQuery and addBinding methods may not be
* called after generateResults has been called until the builder indicates
* that the generated output is being removed by calling the done method.
*
* Currently, the datasource supplied to the methods will always be an
* nsIRDFDataSource or a DOM node, and will always be the same one in between
* calls to initializeForBuilding and done.
*/
[scriptable, uuid(11c63d9e-0c0c-444f-b252-f06c546c2ec7)]
interface nsIXULTemplateQueryProcessor : nsISupports
{
/**
* Initialize for query generation. This will be called before the rules are
* processed and whenever the template is rebuilt. This method must be
* called once before any of the other query processor methods except for
* translateRef.
*
* @param aDatasource datasource for the data
* @param aBuilder the template builder
* @param aRootNode the root node the builder is attached to
*
* @throws NS_ERROR_INVALID_ARG if the datasource is not supported or
* NS_ERROR_UNEXPECTED if generateResults has already been called.
*/
void initializeForBuilding(in nsISupports aDatasource,
in nsIXULTemplateBuilder aBuilder,
in nsIDOMNode aRootNode);
/**
* Called when the template builder is being destroyed so that the query
* processor can clean up any state. The query processor should remove as
* much state as possible, such as results or references to the builder.
* This method will also be called when the template is going to be rebuilt.
*/
void done();
/**
* Compile a query from a node. The result of this function will later be
* passed to generateResults for result generation. If null is returned,
* the query will be ignored.
*
* The template builder will call this method once for each query within
* the template, before any results can be generated using generateResults,
* but after initializeForBuilding has been called. This method should not
* be called again for the same query unless the template is rebuilt.
*
* The reference variable may be used by the query processor as a
* placeholder for the reference point, or starting point in the query.
*
* The member variable is determined from the member attribute on the
* template, or from the uri in the first action's rule if that attribute is
* not present. A rule processor may use the member variable as a hint to
* indicate what variable is expected to contain the results.
*
* @param aBuilder the template builder
* @param aQuery <query> node to compile
* @param aRefVariable the reference variable
* @param aMemberVariable the member variable
*
* @returns a compiled query object
*/
nsISupports compileQuery(in nsIXULTemplateBuilder aBuilder,
in nsIDOMNode aQuery,
in nsIAtom aRefVariable,
in nsIAtom aMemberVariable);
/**
* Generate the results of a query and return them in an enumerator. The
* enumerator must contain nsIXULTemplateResult objects. If there are no
* results, an empty enumerator must be returned.
*
* The datasource will be the same as the one passed to the earlier
* initializeForBuilding method. The context reference (aRef) is a reference
* point used when calculating results.
*
* The value of aQuery must be the result of a previous call to compileQuery
* from this query processor. This method may be called multiple times,
* typically with different values for aRef.
*
* @param aDatasource datasource for the data
* @param aRef context reference value used as a starting point
* @param aQuery the compiled query returned from query compilation
*
* @returns an enumerator of nsIXULTemplateResult objects as the results
*
* @throws NS_ERROR_INVALID_ARG if aQuery is invalid
*/
nsISimpleEnumerator generateResults(in nsISupports aDatasource,
in nsIXULTemplateResult aRef,
in nsISupports aQuery);
/**
* Add a variable binding for a particular rule. A binding allows an
* additional variable to be set for a result, outside of those defined
* within the query. These bindings are always optional, in that they will
* never affect the results generated.
*
* This function will never be called after generateResults. Any bindings
* that were added should be applied to each result when the result's
* ruleMatched method is called, since the bindings are different for each
* rule.
*
* The reference aRef may be used to determine the reference when
* calculating the value for the binding, for example when a value should
* depend on the value of another variable.
*
* The syntax of the expression aExpr is defined by the query processor. If
* the syntax is invalid, the binding should be ignored. Only fatal errors
* should be thrown, or NS_ERROR_UNEXPECTED if generateResults has already
* been called.
*
* As an example, if the reference aRef is the variable '?count' which
* holds the value 5, and the expression aExpr is the string '+2', the value
* of the variable aVar would be 7, assuming the query processor considers
* the syntax '+2' to mean add two to the reference.
*
* @param aRuleNode rule to add the binding to
* @param aVar variable that will be bound
* @param aRef variable that holds reference value
* @param aExpr expression used to compute the value to assign
*/
void addBinding(in nsIDOMNode aRuleNode,
in nsIAtom aVar,
in nsIAtom aRef,
in AString aExpr);
/**
* Translate a ref attribute string into a result. This is used as the
* reference point by the template builder when generating the first level
* of content. For recursive generation, the result from the parent
* generation phase will be used directly as the reference so a translation
* is not needed. This allows all levels to be generated using objects that
* all implement the nsIXULTemplateResult interface.
*
* This method may be called before initializeForBuilding, so the
* implementation may use the supplied datasource if it is needed to
* translate the reference.
*
* @param aDatasource datasource for the data
* @param aRefString the ref attribute string
*
* @return the translated ref
*/
nsIXULTemplateResult translateRef(in nsISupports aDatasource,
in AString aRefString);
/**
* Compare two results to determine their order, used when sorting results.
* This method should return -1 when the left result is less than the right,
* 0 if both are equivalent, and 1 if the left is greater than the right.
* The comparison should only consider the values for the specified
* variable.
*
* This method must only be called with results that were created by this
* query processor.
*
* @param aLeft the left result to compare
* @param aRight the right result to compare
* @param aVar variable to compare
*
* @param returns -1 if less, 0 if equal, or 1 if greater
*/
PRInt32 compareResults(in nsIXULTemplateResult aLeft,
in nsIXULTemplateResult aRight,
in nsIAtom aVar);
};

Просмотреть файл

@ -0,0 +1,140 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Neil Deakin.
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
interface nsIAtom;
interface nsIDOMNode;
interface nsIRDFResource;
/**
* A single result generated from a template query. Each result is identified
* by an id, which must be unique within the set of results produced from a
* query. The result may optionally be identified by an RDF resource.
*
* Generally, the result and its id will be able to uniquely identify a node
* in the source data, such as an RDF or XML node. In other contexts, such as
* a database query, a result would represent a particular record.
*
* A result is expected to only be created by a query processor.
*
* Each result also contains a set of variable bindings. The value for a
* particular variable may be retrieved using the getBindingFor and
* getBindingObjectFor methods.
*/
[scriptable, uuid(d035f34f-d8ee-444a-bd46-41163f54ed25)]
interface nsIXULTemplateResult : nsISupports
{
/**
* True if the result represents a container.
*/
readonly attribute boolean isContainer;
/**
* True if the result represents an empty container.
*/
readonly attribute boolean isEmpty;
/**
* True if the template builder may use this result as the reference point
* for additional recursive processing of the template. The template builder
* will reprocess the template using this result as the reference point and
* generate output content that is expected to be inserted as children of the
* output generated for this result. If false, child content is not
* processed. This property identifies only the default handling and may be
* overriden by syntax used in the template.
*/
readonly attribute boolean mayProcessChildren;
/**
* ID of the result. The DOM element created for this result, if any, will
* have its id attribute set to this value. The id must be unique for a
* query.
*/
readonly attribute AString id;
/**
* Resource for the result, which may be null. If set, the resource uri
* must be the same as the ID property.
*/
readonly attribute nsIRDFResource resource;
/**
* Get the string representation of the value of a variable for this
* result. This string will be used in the action body from a template as
* the replacement text. For instance, if the text ?name appears in an
* attribute within the action body, it will be replaced with the result
* of this method. The question mark is considered part of the variable
* name, thus aVar should be ?name and not simply name.
*
* @param aVar the variable to look up
*
* @return the value for the variable or a null string if it has no value
*/
AString getBindingFor(in nsIAtom aVar);
/**
* Get an object value for a variable such as ?name for this result.
*
* This method may return null for a variable, even if getBindingFor returns
* a non-null value for the same variable. This method is provided as a
* convenience when sorting results.
*
* @param aVar the variable to look up
*
* @return the value for the variable or null if it has no value
*/
nsISupports getBindingObjectFor(in nsIAtom aVar);
/**
* Indicate that a particular rule of a query has matched and that output
* will be generated for it. Both the query as compiled by the query
* processor's compileQuery method and the XUL <rule> element are supplied.
* The query must always be one that was compiled by the query processor
* that created this result. The <rule> element must always be a child of
* the <query> element that was used to compile the query.
*
* @param aQuery the query that matched
* @param aRuleNode the rule node that matched
*/
void ruleMatched(in nsISupports aQuery, in nsIDOMNode aRuleNode);
/**
* Indicate that the output for a result has beeen removed and that the
* result is no longer being used by the builder.
*/
void hasBeenRemoved();
};

Просмотреть файл

@ -0,0 +1,67 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Neil Deakin.
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "domstubs.idl"
interface nsISupports;
interface nsIXULTemplateResult;
/**
* A rule filter may be used to add additional filtering of results to a rule.
* The filter is used to further reject results from matching the template's
* rules, beyond what the template syntax can do itself, thus allowing for
* more complex result filtering. The rule filter is applied after the rule
* syntax within the template.
*
* Only one filter may apply to each rule within the template and may be
* assigned using the template builder's addRuleFilter method.
*/
[scriptable, uuid(819cd1ed-8010-42e1-a8b9-778b726a1ff3)]
interface nsIXULTemplateRuleFilter : nsISupports
{
/**
* Evaluate a result and return true if the result is accepted by this
* filter, or false if it is rejected. Accepted results will have output
* generated for them for the rule. Rejected results will not, but they
* may still match another rule.
*
* @param aRef the result to examine
* @param aRule the rule node
*
* @return true if the rule matches
*/
boolean match(in nsIXULTemplateResult aRef, in nsIDOMNode aRule);
};

Просмотреть файл

@ -66,28 +66,27 @@ REQUIRES = xpcom \
$(NULL)
CPPSRCS = \
nsClusterKey.cpp \
nsClusterKeySet.cpp \
nsConflictSet.cpp \
nsContentSupportMap.cpp \
nsContentTagTestNode.cpp \
nsContentTestNode.cpp \
nsInstantiationNode.cpp \
nsTreeRowTestNode.cpp \
nsTreeRows.cpp \
nsRDFConInstanceTestNode.cpp \
nsRDFConMemberTestNode.cpp \
nsRDFPropertyTestNode.cpp \
nsRDFBinding.cpp \
nsRDFQuery.cpp \
nsResourceSet.cpp \
nsRuleNetwork.cpp \
nsTemplateMatch.cpp \
nsTemplateMatchSet.cpp \
nsTemplateRule.cpp \
nsXULContentBuilder.cpp \
nsXULContentUtils.cpp \
nsXULTreeBuilder.cpp \
nsXULSortService.cpp \
nsXULTemplateBuilder.cpp \
nsXULTemplateQueryProcessorRDF.cpp \
nsXULTemplateResultRDF.cpp \
nsXULTemplateResultSetRDF.cpp \
$(NULL)
# we don't want the shared lib, but we want to force the creation of a static lib.

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -36,37 +36,29 @@
*
* ***** END LICENSE BLOCK ***** */
#include "nsConflictSet.h"
#include "nsContentTestNode.h"
#include "nsISupportsArray.h"
#include "nsIXULDocument.h"
#include "nsIRDFResource.h"
#include "nsIAtom.h"
#include "nsIDOMElement.h"
#include "nsXULContentUtils.h"
#include "nsPrintfCString.h"
#include "nsIXULTemplateResult.h"
#include "nsIXULTemplateBuilder.h"
#include "nsXULTemplateQueryProcessorRDF.h"
#include "prlog.h"
#ifdef PR_LOGGING
extern PRLogModuleInfo* gXULTemplateLog;
#endif
PRBool
IsElementInBuilder(nsIContent *aContent, nsIXULTemplateBuilder *aBuilder);
nsContentTestNode::nsContentTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
nsIXULDocument* aDocument,
nsIXULTemplateBuilder* aBuilder,
PRInt32 aContentVariable,
PRInt32 aIdVariable,
nsIAtom* aTag)
: TestNode(aParent),
mConflictSet(aConflictSet),
mDocument(aDocument),
mBuilder(aBuilder),
mContentVariable(aContentVariable),
mIdVariable(aIdVariable),
mTag(aTag)
nsContentTestNode::nsContentTestNode(nsXULTemplateQueryProcessorRDF* aProcessor,
nsIAtom* aRefVariable)
: TestNode(nsnull),
mProcessor(aProcessor),
mDocument(nsnull),
mRefVariable(aRefVariable),
mTag(nsnull)
{
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
@ -74,214 +66,57 @@ nsContentTestNode::nsContentTestNode(InnerNode* aParent,
if (mTag)
mTag->ToString(tag);
nsAutoString refvar(NS_LITERAL_STRING("(none)"));
if (aRefVariable)
aRefVariable->ToString(refvar);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsContentTestNode[%p]: parent=%p content-var=%d id-var=%d tag=%s",
this, aParent, mContentVariable, mIdVariable,
("nsContentTestNode[%p]: ref-var=%s tag=%s",
this, NS_ConvertUTF16toUTF8(refvar).get(),
NS_ConvertUTF16toUTF8(tag).get()));
}
#endif
}
#ifdef PR_LOGGING
static void
ElementToString(nsIContent *aContent, nsString &aResult)
{
aContent->Tag()->ToString(aResult);
AppendASCIItoUTF16(nsPrintfCString(18, "@%p", aContent), aResult);
}
#endif
nsresult
nsContentTestNode::FilterInstantiations(InstantiationSet& aInstantiations, void* aClosure) const
nsContentTestNode::FilterInstantiations(InstantiationSet& aInstantiations) const
{
nsresult rv;
nsCOMPtr<nsISupportsArray> elements;
rv = NS_NewISupportsArray(getter_AddRefs(elements));
if (NS_FAILED(rv)) return rv;
InstantiationSet::Iterator last = aInstantiations.Last();
for (InstantiationSet::Iterator inst = aInstantiations.First(); inst != last; ++inst) {
Value contentValue;
PRBool hasContentBinding = inst->mAssignments.GetAssignmentFor(mContentVariable, &contentValue);
Value idValue;
PRBool hasIdBinding = inst->mAssignments.GetAssignmentFor(mIdVariable, &idValue);
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
nsAutoString content(NS_LITERAL_STRING("(unbound)"));
if (hasContentBinding)
ElementToString(VALUE_TO_ICONTENT(contentValue), content);
const char *id = "(unbound)";
if (hasIdBinding)
VALUE_TO_IRDFRESOURCE(idValue)->GetValueConst(&id);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsContentTestNode[%p]: FilterInstantiations() content=%s id=%s",
this, NS_LossyConvertUTF16toASCII(content).get(), id));
}
#endif
if (hasContentBinding && hasIdBinding) {
// both are bound, consistency check
PRBool consistent = PR_TRUE;
nsIContent* content = VALUE_TO_ICONTENT(contentValue);
if (mTag) {
// If we're supposed to be checking the tag, do it now.
if (content->Tag() != mTag)
consistent = PR_FALSE;
}
if (consistent) {
nsCOMPtr<nsIRDFResource> resource;
nsXULContentUtils::GetElementRefResource(content, getter_AddRefs(resource));
if (resource.get() != VALUE_TO_IRDFRESOURCE(idValue))
consistent = PR_FALSE;
}
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
(" consistency check => %s", consistent ? "passed" : "failed"));
if (consistent) {
Element* element =
nsContentTestNode::Element::Create(mConflictSet.GetPool(),
VALUE_TO_ICONTENT(contentValue));
if (! element)
return NS_ERROR_OUT_OF_MEMORY;
inst->AddSupportingElement(element);
}
else {
aInstantiations.Erase(inst--);
}
}
else if (hasContentBinding) {
// the content node is bound, get its id
PRBool consistent = PR_TRUE;
nsIContent* content = VALUE_TO_ICONTENT(contentValue);
if (mTag) {
// If we're supposed to be checking the tag, do it now.
nsIAtom *tag = content->Tag();
if (tag != mTag) {
consistent = PR_FALSE;
const char *expected, *actual;
mTag->GetUTF8String(&expected);
tag->GetUTF8String(&actual);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
(" => tag mismatch; expected %s, actual %s",
expected,
actual));
}
}
if (consistent) {
nsCOMPtr<nsIRDFResource> resource;
nsXULContentUtils::GetElementRefResource(content, getter_AddRefs(resource));
if (resource) {
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
const char *str;
resource->GetValueConst(&str);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
(" => [%s]", str));
}
#endif
Instantiation newinst = *inst;
newinst.AddAssignment(mIdVariable, Value(resource.get()));
Element* element =
nsContentTestNode::Element::Create(mConflictSet.GetPool(), content);
if (! element)
return NS_ERROR_OUT_OF_MEMORY;
newinst.AddSupportingElement(element);
aInstantiations.Insert(inst, newinst);
}
else {
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
(" => element has no resource"));
}
}
aInstantiations.Erase(inst--);
}
else if (hasIdBinding) {
// the 'id' is bound, find elements in the content tree that match
const char* uri;
VALUE_TO_IRDFRESOURCE(idValue)->GetValueConst(&uri);
mDocument->GetElementsForID(NS_ConvertUTF8toUTF16(uri), elements);
PRUint32 count;
elements->Count(&count);
for (PRInt32 j = PRInt32(count) - 1; j >= 0; --j) {
nsISupports* isupports = elements->ElementAt(j);
nsCOMPtr<nsIContent> content = do_QueryInterface(isupports);
NS_IF_RELEASE(isupports);
if (IsElementInBuilder(content, mBuilder)) {
if (mTag) {
// If we've got a tag, check it to ensure
// we're consistent.
if (content->Tag() != mTag)
continue;
}
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
nsAutoString str;
ElementToString(content, str);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
(" => %s", NS_LossyConvertUTF16toASCII(str).get()));
}
#endif
Instantiation newinst = *inst;
newinst.AddAssignment(mContentVariable, Value(content.get()));
Element* element =
nsContentTestNode::Element::Create(mConflictSet.GetPool(), content);
if (! element)
return NS_ERROR_OUT_OF_MEMORY;
newinst.AddSupportingElement(element);
aInstantiations.Insert(inst, newinst);
}
}
aInstantiations.Erase(inst--);
}
}
return NS_OK;
}
nsresult
nsContentTestNode::GetAncestorVariables(VariableSet& aVariables) const
nsContentTestNode::Constrain(InstantiationSet& aInstantiations)
{
aVariables.Add(mContentVariable);
aVariables.Add(mIdVariable);
// contrain the matches to those that have matched in the template builder
return TestNode::GetAncestorVariables(aVariables);
nsIXULTemplateBuilder* builder = mProcessor->GetBuilder();
if (!builder) {
aInstantiations.Clear();
return NS_OK;
}
nsresult rv;
InstantiationSet::Iterator last = aInstantiations.Last();
for (InstantiationSet::Iterator inst = aInstantiations.First(); inst != last; ++inst) {
nsCOMPtr<nsIRDFNode> refValue;
PRBool hasRefBinding = inst->mAssignments.GetAssignmentFor(mRefVariable,
getter_AddRefs(refValue));
if (hasRefBinding) {
nsCOMPtr<nsIRDFResource> refResource = do_QueryInterface(refValue);
if (refResource) {
PRBool generated;
rv = builder->HasGeneratedContent(refResource, mTag, &generated);
if (NS_FAILED(rv)) return rv;
if (generated)
continue;
}
}
aInstantiations.Erase(inst--);
}
return NS_OK;
}

Просмотреть файл

@ -43,78 +43,37 @@
#include "nsRuleNetwork.h"
#include "nsFixedSizeAllocator.h"
#include "nsIAtom.h"
#include "nsIDOMDocument.h"
class nsIXULTemplateBuilder;
class nsIXULDocument;
class nsConflictSet;
/**
* The nsContentTestNode is always the top node in a query's rule network. It
* exists so that Constrain can filter out resources that aren't part of a
* result.
*/
class nsContentTestNode : public TestNode
{
public:
nsContentTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
nsIXULDocument* aDocument,
nsIXULTemplateBuilder* aBuilder,
PRInt32 aContentVariable,
PRInt32 aIdVariable,
nsIAtom* aTag);
nsContentTestNode(nsXULTemplateQueryProcessorRDF* aProcessor,
nsIAtom* aContentVariable);
virtual nsresult
FilterInstantiations(InstantiationSet& aInstantiations, void* aClosure) const;
FilterInstantiations(InstantiationSet& aInstantiations) const;
virtual nsresult
GetAncestorVariables(VariableSet& aVariables) const;
nsresult
Constrain(InstantiationSet& aInstantiations);
class Element : public MemoryElement {
private:
// Hide so that only Create() and Destroy() can be used to
// allocate and deallocate from the heap
static void* operator new(size_t) CPP_THROW_NEW { return 0; }
static void operator delete(void*, size_t) {}
public:
Element(nsIContent* aContent)
: mContent(aContent) {
MOZ_COUNT_CTOR(nsContentTestNode::Element); }
virtual ~Element() { MOZ_COUNT_DTOR(nsContentTestNode::Element); }
static Element*
Create(nsFixedSizeAllocator& aPool, nsIContent* aContent) {
void* place = aPool.Alloc(sizeof(Element));
return place ? ::new (place) Element(aContent) : nsnull; }
static void
Destroy(nsFixedSizeAllocator& aPool, Element* aElement) {
aElement->~Element();
aPool.Free(aElement, sizeof(*aElement)); }
virtual const char* Type() const {
return "nsContentTestNode::Element"; }
virtual PLHashNumber Hash() const {
return PLHashNumber(NS_PTR_TO_INT32(mContent.get())) >> 2; }
virtual PRBool Equals(const MemoryElement& aElement) const {
if (aElement.Type() == Type()) {
const Element& element = NS_STATIC_CAST(const Element&, aElement);
return mContent == element.mContent;
}
return PR_FALSE; }
virtual MemoryElement* Clone(void* aPool) const {
return Create(*NS_STATIC_CAST(nsFixedSizeAllocator*, aPool), mContent); }
protected:
nsCOMPtr<nsIContent> mContent;
};
void SetTag(nsIAtom* aTag, nsIDOMDocument* aDocument)
{
mTag = aTag;
mDocument = aDocument;
}
protected:
nsConflictSet& mConflictSet;
nsIXULDocument* mDocument; // [WEAK] because we know the document will outlive us
nsIXULTemplateBuilder *mBuilder;
PRInt32 mContentVariable;
PRInt32 mIdVariable;
nsXULTemplateQueryProcessorRDF *mProcessor;
nsIDOMDocument* mDocument;
nsCOMPtr<nsIAtom> mRefVariable;
nsCOMPtr<nsIAtom> mTag;
};

Просмотреть файл

@ -21,6 +21,7 @@
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
* Neil Deakin <enndeakin@sympatico.ca>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
@ -38,64 +39,83 @@
#include "nsInstantiationNode.h"
#include "nsTemplateRule.h"
#include "nsClusterKeySet.h"
#include "nsConflictSet.h"
#include "nsXULTemplateQueryProcessorRDF.h"
#include "prlog.h"
#ifdef PR_LOGGING
extern PRLogModuleInfo* gXULTemplateLog;
#endif
nsInstantiationNode::nsInstantiationNode(nsConflictSet& aConflictSet,
nsTemplateRule* aRule,
nsIRDFDataSource* aDataSource)
: mConflictSet(aConflictSet),
mRule(aRule)
nsInstantiationNode::nsInstantiationNode(nsXULTemplateQueryProcessorRDF* aProcessor,
nsRDFQuery* aQuery)
: mProcessor(aProcessor),
mQuery(aQuery)
{
#ifdef PR_LOGGING
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsInstantiationNode[%p] rule=%p", this, aRule));
("nsInstantiationNode[%p] query=%p", this, aQuery));
#endif
MOZ_COUNT_CTOR(nsInstantiationNode);
}
nsInstantiationNode::~nsInstantiationNode()
{
delete mRule;
MOZ_COUNT_DTOR(nsInstantiationNode);
}
nsresult
nsInstantiationNode::Propagate(const InstantiationSet& aInstantiations, void* aClosure)
nsInstantiationNode::Propagate(InstantiationSet& aInstantiations,
PRBool aIsUpdate, PRBool& aTakenInstantiations)
{
// If we get here, we've matched the rule associated with this
// node. Extend it with any <bindings> that we might have, add it
// to the conflict set, and the set of new <content, member>
// pairs.
nsClusterKeySet* newkeys = NS_STATIC_CAST(nsClusterKeySet*, aClosure);
// In update mode, iterate through the results and call the template
// builder to update them. In non-update mode, cache them in the processor
// to be used during processing. The results are cached in the processor
// so that the simple rules are only computed once. In this situation, all
// data for all queries are calculated at once.
nsresult rv = NS_OK;
InstantiationSet::ConstIterator last = aInstantiations.Last();
for (InstantiationSet::ConstIterator inst = aInstantiations.First(); inst != last; ++inst) {
nsAssignmentSet assignments = inst->mAssignments;
aTakenInstantiations = PR_FALSE;
nsTemplateMatch* match =
nsTemplateMatch::Create(mConflictSet.GetPool(), mRule, *inst, assignments);
if (aIsUpdate) {
// Iterate through newly added keys to determine which rules fired.
//
// XXXwaterson Unfortunately, this could also lead to retractions;
// e.g., (container ?a ^empty false) could become "unmatched". How
// to track those?
nsCOMPtr<nsIDOMNode> querynode;
mQuery->GetQueryNode(getter_AddRefs(querynode));
if (! match)
return NS_ERROR_OUT_OF_MEMORY;
InstantiationSet::ConstIterator last = aInstantiations.Last();
for (InstantiationSet::ConstIterator inst = aInstantiations.First(); inst != last; ++inst) {
nsAssignmentSet assignments = inst->mAssignments;
// Temporarily AddRef() to keep the match alive.
match->AddRef();
nsCOMPtr<nsIRDFNode> node;
assignments.GetAssignmentFor(mQuery->mMemberVariable,
getter_AddRefs(node));
if (node) {
nsCOMPtr<nsIRDFResource> resource = do_QueryInterface(node);
if (resource) {
nsRefPtr<nsXULTemplateResultRDF> nextresult =
new nsXULTemplateResultRDF(mQuery, *inst, resource);
if (! nextresult)
return NS_ERROR_OUT_OF_MEMORY;
mRule->InitBindings(mConflictSet, match);
rv = mProcessor->AddMemoryElements(*inst, nextresult);
if (NS_FAILED(rv))
return rv;
mConflictSet.Add(match);
// Give back our "local" reference. The conflict set will have
// taken what it needs.
match->Release(mConflictSet.GetPool());
newkeys->Add(nsClusterKey(*inst, mRule));
mProcessor->GetBuilder()->AddResult(nextresult, querynode);
}
}
}
}
return NS_OK;
else {
nsresult rv = mQuery->SetCachedResults(mProcessor, aInstantiations);
if (NS_SUCCEEDED(rv))
aTakenInstantiations = PR_TRUE;
}
return rv;
}

Просмотреть файл

@ -21,6 +21,7 @@
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
* Neil Deakin <enndeakin@sympatico.ca>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
@ -40,9 +41,9 @@
#define nsInstantiationNode_h__
#include "nsRuleNetwork.h"
class nsIRDFDataSource;
class nsConflictSet;
class nsTemplateRule;
#include "nsRDFQuery.h"
class nsXULTemplateQueryProcessorRDF;
/**
* A leaf-level node in the rule network. If any instantiations
@ -51,24 +52,19 @@ class nsTemplateRule;
class nsInstantiationNode : public ReteNode
{
public:
nsInstantiationNode(nsConflictSet& aConflictSet,
nsTemplateRule* aRule,
nsIRDFDataSource* aDataSource);
nsInstantiationNode(nsXULTemplateQueryProcessorRDF* aProcessor,
nsRDFQuery* aRule);
~nsInstantiationNode();
// "downward" propagations
virtual nsresult Propagate(const InstantiationSet& aInstantiations, void* aClosure);
virtual nsresult Propagate(InstantiationSet& aInstantiations,
PRBool aIsUpdate, PRBool& aMatched);
protected:
nsConflictSet& mConflictSet;
/**
* The rule that the node instantiates. The instantiation node
* assumes ownership of the rule in its ctor, and will destroy
* the rule in its dtor.
*/
nsTemplateRule* mRule;
nsXULTemplateQueryProcessorRDF* mProcessor;
nsRDFQuery* mQuery;
};
#endif // nsInstantiationNode_h__

Просмотреть файл

@ -0,0 +1,306 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Neil Deakin
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsXULTemplateQueryProcessorRDF.h"
#include "nsXULTemplateResultRDF.h"
#include "nsRDFBinding.h"
#ifdef DEBUG
#include "nsXULContentUtils.h"
#endif
RDFBindingSet::~RDFBindingSet()
{
while (mFirst) {
RDFBinding* doomed = mFirst;
mFirst = mFirst->mNext;
delete doomed;
}
MOZ_COUNT_DTOR(RDFBindingSet);
}
nsresult
RDFBindingSet::AddBinding(nsIAtom* aVar, nsIAtom* aRef, nsIRDFResource* aPredicate)
{
RDFBinding* newbinding = new RDFBinding(aRef, aPredicate, aVar);
if (! newbinding)
return NS_ERROR_OUT_OF_MEMORY;
if (mFirst) {
RDFBinding* binding = mFirst;
while (binding) {
// the binding is dependant on the calculation of a previous binding
if (binding->mSubjectVariable == aVar)
newbinding->mHasDependency = PR_TRUE;
// if the target variable is already used in a binding, ignore it
// since it won't be useful for anything
if (binding->mTargetVariable == aVar) {
delete newbinding;
return NS_OK;
}
// add the binding at the end of the list
if (! binding->mNext) {
binding->mNext = newbinding;
break;
}
binding = binding->mNext;
}
}
else {
mFirst = newbinding;
}
mCount++;
return NS_OK;
}
PRBool
RDFBindingSet::SyncAssignments(nsIRDFResource* aSubject,
nsIRDFResource* aPredicate,
nsIRDFNode* aTarget,
nsIAtom* aMemberVariable,
nsXULTemplateResultRDF* aResult,
nsBindingValues& aBindingValues)
{
NS_ASSERTION(aBindingValues.GetBindingSet() == this,
"nsBindingValues not for this RDFBindingSet");
NS_PRECONDITION(aResult, "Must have result");
PRBool needSync = PR_FALSE;
nsCOMPtr<nsIRDFNode>* valuesArray = aBindingValues.ValuesArray();
if (!valuesArray)
return PR_FALSE;
RDFBinding* binding = mFirst;
PRInt32 count = 0;
// QI for proper comparisons just to be safe
nsCOMPtr<nsIRDFNode> subjectnode = do_QueryInterface(aSubject);
// iterate through the bindings looking for ones that would match the RDF
// nodes that were involved in a change
nsCOMPtr<nsIRDFNode> value;
while (binding) {
if (aPredicate == binding->mPredicate) {
// if the source of the binding is the member variable, optimize
if (binding->mSubjectVariable == aMemberVariable) {
valuesArray[count] = aTarget;
needSync = PR_TRUE;
}
else {
aResult->GetAssignment(binding->mSubjectVariable, getter_AddRefs(value));
if (value == subjectnode) {
valuesArray[count] = aTarget;
needSync = PR_TRUE;
}
}
}
binding = binding->mNext;
count++;
}
return needSync;
}
void
RDFBindingSet::AddDependencies(nsIRDFResource* aSubject,
nsXULTemplateResultRDF* aResult)
{
NS_PRECONDITION(aResult, "Must have result");
// iterate through the bindings and add binding dependencies to the
// processor
nsXULTemplateQueryProcessorRDF* processor = aResult->GetProcessor();
if (! processor)
return;
nsCOMPtr<nsIRDFNode> value;
RDFBinding* binding = mFirst;
while (binding) {
aResult->GetAssignment(binding->mSubjectVariable, getter_AddRefs(value));
nsCOMPtr<nsIRDFResource> valueres = do_QueryInterface(value);
if (valueres)
processor->AddBindingDependency(aResult, valueres);
binding = binding->mNext;
}
}
void
RDFBindingSet::RemoveDependencies(nsIRDFResource* aSubject,
nsXULTemplateResultRDF* aResult)
{
NS_PRECONDITION(aResult, "Must have result");
// iterate through the bindings and remove binding dependencies from the
// processor
nsXULTemplateQueryProcessorRDF* processor = aResult->GetProcessor();
if (! processor)
return;
nsCOMPtr<nsIRDFNode> value;
RDFBinding* binding = mFirst;
while (binding) {
aResult->GetAssignment(binding->mSubjectVariable, getter_AddRefs(value));
nsCOMPtr<nsIRDFResource> valueres = do_QueryInterface(value);
if (valueres)
processor->RemoveBindingDependency(aResult, valueres);
binding = binding->mNext;
}
}
PRInt32
RDFBindingSet::LookupTargetIndex(nsIAtom* aTargetVariable, RDFBinding** aBinding)
{
PRInt32 idx = 0;
RDFBinding* binding = mFirst;
while (binding) {
if (binding->mTargetVariable == aTargetVariable) {
*aBinding = binding;
return idx;
}
idx++;
binding = binding->mNext;
}
return -1;
}
nsBindingValues::~nsBindingValues()
{
ClearBindingSet();
MOZ_COUNT_DTOR(nsBindingValues);
}
void
nsBindingValues::ClearBindingSet()
{
if (mBindings && mValues) {
delete [] mValues;
mValues = nsnull;
}
mBindings = nsnull;
}
nsresult
nsBindingValues::SetBindingSet(RDFBindingSet* aBindings)
{
ClearBindingSet();
PRInt32 count = aBindings->Count();
if (count) {
mValues = new nsCOMPtr<nsIRDFNode>[count];
if (!mValues)
return NS_ERROR_OUT_OF_MEMORY;
mBindings = aBindings;
}
else {
mValues = nsnull;
}
return NS_OK;
}
void
nsBindingValues::GetAssignmentFor(nsXULTemplateResultRDF* aResult,
nsIAtom* aVar,
nsIRDFNode** aValue)
{
*aValue = nsnull;
// assignments are calculated lazily when asked for. The only issue is
// when a binding has no value in the RDF graph, it will be checked again
// every time.
if (mBindings && mValues) {
RDFBinding* binding;
PRInt32 idx = mBindings->LookupTargetIndex(aVar, &binding);
if (idx >= 0) {
*aValue = mValues[idx];
if (*aValue) {
NS_ADDREF(*aValue);
}
else {
nsXULTemplateQueryProcessorRDF* processor = aResult->GetProcessor();
if (! processor)
return;
nsIRDFDataSource* ds = processor->GetDataSource();
if (! ds)
return;
nsCOMPtr<nsIRDFNode> subjectValue;
aResult->GetAssignment(binding->mSubjectVariable,
getter_AddRefs(subjectValue));
NS_ASSERTION(subjectValue, "Value of subject is not set");
if (subjectValue) {
nsCOMPtr<nsIRDFResource> subject = do_QueryInterface(subjectValue);
ds->GetTarget(subject, binding->mPredicate, PR_TRUE, aValue);
if (*aValue)
mValues[idx] = *aValue;
}
}
}
}
}
void
nsBindingValues::RemoveDependencies(nsIRDFResource* aSubject,
nsXULTemplateResultRDF* aResult)
{
if (mBindings)
mBindings->RemoveDependencies(aSubject, aResult);
}

Просмотреть файл

@ -0,0 +1,263 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Neil Deakin
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsRDFBinding_h__
#define nsRDFBinding_h__
#include "nsAutoPtr.h"
#include "nsIAtom.h"
#include "nsIRDFResource.h"
class nsXULTemplateResultRDF;
class nsBindingValues;
/*
* Classes related to storing bindings for RDF handling.
*/
/*
* a <binding> descriptors
*/
class RDFBinding {
public:
nsCOMPtr<nsIAtom> mSubjectVariable;
nsCOMPtr<nsIRDFResource> mPredicate;
nsCOMPtr<nsIAtom> mTargetVariable;
// indicates whether a binding is dependant on the result from a
// previous binding
PRBool mHasDependency;
RDFBinding* mNext;
private:
friend class RDFBindingSet;
RDFBinding(nsIAtom* aSubjectVariable,
nsIRDFResource* aPredicate,
nsIAtom* aTargetVariable)
: mSubjectVariable(aSubjectVariable),
mPredicate(aPredicate),
mTargetVariable(aTargetVariable),
mHasDependency(PR_FALSE),
mNext(nsnull)
{
MOZ_COUNT_CTOR(RDFBinding);
}
~RDFBinding()
{
MOZ_COUNT_DTOR(RDFBinding);
}
};
/*
* a collection of <binding> descriptors. This object is refcounted by
* nsBindingValues objects and the query processor.
*/
class RDFBindingSet
{
protected:
// results hold a reference to a binding set in their nsBindingValues fields
PRInt32 mRefCnt;
// the number of bindings
PRInt32 mCount;
// pointer to the first binding in a linked list
RDFBinding* mFirst;
public:
RDFBindingSet()
: mRefCnt(0),
mCount(0),
mFirst(nsnull)
{
MOZ_COUNT_CTOR(RDFBindingSet);
}
~RDFBindingSet();
PRInt32 AddRef() {
mRefCnt++;
NS_LOG_ADDREF(this, mRefCnt, "RDFBindingSet", sizeof(*this));
return mRefCnt;
}
PRInt32 Release()
{
PRInt32 refcnt = --mRefCnt;
NS_LOG_RELEASE(this, refcnt, "RDFBindingSet");
if (refcnt == 0) delete this;
return refcnt;
}
PRInt32 Count() const { return mCount; }
/*
* Add a binding (aRef -> aPredicate -> aVar) to the set
*/
nsresult
AddBinding(nsIAtom* aVar, nsIAtom* aRef, nsIRDFResource* aPredicate);
/*
* Return true if the binding set contains a binding which would cause
* the result to need resynchronizing for an RDF triple. The member
* variable may be supplied as an optimization since bindings most
* commonly use the member variable as the subject. If aMemberVariable
* is set, aSubject must be the value of the member variable for the
* result. The supplied binding values aBindingValues must be values
* using this binding set (that is aBindingValues->GetBindingSet() == this)
*
* @param aSubject subject of the RDF triple
* @param aPredicate predicate of the RDF triple
* @param aTarget target of the RDF triple
* @param aMemberVariable member variable for the query for the binding
* @param aResult result to synchronize
* @param aBindingValues the values for the bindings for the result
*/
PRBool
SyncAssignments(nsIRDFResource* aSubject,
nsIRDFResource* aPredicate,
nsIRDFNode* aTarget,
nsIAtom* aMemberVariable,
nsXULTemplateResultRDF* aResult,
nsBindingValues& aBindingValues);
/*
* The query processor maintains a map of subjects to an array of results.
* This is used such that when a new assertion is added to the RDF graph,
* the results associated with the subject of that triple may be checked
* to see if their bindings have changed. The AddDependencies method adds
* these subject dependencies to the map.
*/
void
AddDependencies(nsIRDFResource* aSubject,
nsXULTemplateResultRDF* aResult);
/*
* Remove the results from the dependencies map when results are deleted.
*/
void
RemoveDependencies(nsIRDFResource* aSubject,
nsXULTemplateResultRDF* aResult);
/*
* The nsBindingValues classes stores an array of values, one for each
* target symbol that could be set by the bindings in the set.
* LookupTargetIndex determines the index into the array for a given
* target symbol.
*/
PRInt32
LookupTargetIndex(nsIAtom* aTargetVariable, RDFBinding** aBinding);
};
/*
* A set of values of bindings. This object is used once per result.
* This stores a reference to the binding set and an array of node values.
* Since the binding set is used once per query and the values are
* used once per result, we reduce size by only storing the value array's
* length in the binding set. This is possible since the array is always
* a fixed length for a particular binding set.
*
* XXX ndeakin We may want to revisit this later since it makes the code
* more complicated.
*/
class nsBindingValues
{
protected:
// the binding set
nsRefPtr<RDFBindingSet> mBindings;
/*
* A set of values for variable bindings. To look up a binding value,
* scan through the binding set in mBindings for the right target atom.
* Its index will correspond to the index in this array. The size of this
* array is determined by the RDFBindingSet's Count().
*/
nsCOMPtr<nsIRDFNode>* mValues;
public:
nsBindingValues()
: mBindings(nsnull),
mValues(nsnull)
{
MOZ_COUNT_CTOR(nsBindingValues);
}
~nsBindingValues();
/**
* Clear the binding set, to be called when the nsBindingValues is deleted
* or a new binding set is being set.
*/
void ClearBindingSet();
RDFBindingSet* GetBindingSet() { return mBindings; }
/**
* Set the binding set to use. This needs to be called once a rule matches
* since it is then known which bindings will apply.
*/
nsresult SetBindingSet(RDFBindingSet* aBindings);
nsCOMPtr<nsIRDFNode>* ValuesArray() { return mValues; }
/*
* Retrieve the assignment for a particular variable
*/
void
GetAssignmentFor(nsXULTemplateResultRDF* aResult,
nsIAtom* aVar,
nsIRDFNode** aValue);
/*
* Remove depenedencies the bindings have on particular resources
*/
void
RemoveDependencies(nsIRDFResource* aSubject,
nsXULTemplateResultRDF* aResult);
};
#endif // nsRDFBinding_h__

Просмотреть файл

@ -36,7 +36,6 @@
*
* ***** END LICENSE BLOCK ***** */
#include "nsConflictSet.h"
#include "nsIComponentManager.h"
#include "nsIRDFContainer.h"
#include "nsIRDFContainerUtils.h"
@ -44,7 +43,6 @@
#include "nsRDFCID.h"
#include "nsRDFConInstanceTestNode.h"
#include "nsResourceSet.h"
#include "nsString.h"
#include "prlog.h"
#ifdef PR_LOGGING
@ -62,17 +60,13 @@ TestToString(nsRDFConInstanceTestNode::Test aTest) {
}
#endif
nsRDFConInstanceTestNode::nsRDFConInstanceTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
nsIRDFDataSource* aDataSource,
const nsResourceSet& aMembershipProperties,
PRInt32 aContainerVariable,
nsRDFConInstanceTestNode::nsRDFConInstanceTestNode(TestNode* aParent,
nsXULTemplateQueryProcessorRDF* aProcessor,
nsIAtom* aContainerVariable,
Test aContainer,
Test aEmpty)
: nsRDFTestNode(aParent),
mConflictSet(aConflictSet),
mDataSource(aDataSource),
mMembershipProperties(aMembershipProperties),
mProcessor(aProcessor),
mContainerVariable(aContainerVariable),
mContainer(aContainer),
mEmpty(aEmpty)
@ -81,8 +75,9 @@ nsRDFConInstanceTestNode::nsRDFConInstanceTestNode(InnerNode* aParent,
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
nsCAutoString props;
nsResourceSet::ConstIterator last = aMembershipProperties.Last();
nsResourceSet::ConstIterator first = aMembershipProperties.First();
nsResourceSet& containmentProps = aProcessor->ContainmentProperties();
nsResourceSet::ConstIterator last = containmentProps.Last();
nsResourceSet::ConstIterator first = containmentProps.First();
nsResourceSet::ConstIterator iter;
for (iter = first; iter != last; ++iter) {
@ -95,12 +90,16 @@ nsRDFConInstanceTestNode::nsRDFConInstanceTestNode(InnerNode* aParent,
props += str;
}
nsAutoString cvar(NS_LITERAL_STRING("(none)"));
if (mContainerVariable)
mContainerVariable->ToString(cvar);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsRDFConInstanceTestNode[%p]: parent=%p member-props=(%s) container-var=%d container=%s empty=%s",
("nsRDFConInstanceTestNode[%p]: parent=%p member-props=(%s) container-var=%s container=%s empty=%s",
this,
aParent,
props.get(),
mContainerVariable,
NS_ConvertUTF16toUTF8(cvar).get(),
TestToString(aContainer),
TestToString(aEmpty)));
}
@ -108,7 +107,7 @@ nsRDFConInstanceTestNode::nsRDFConInstanceTestNode(InnerNode* aParent,
}
nsresult
nsRDFConInstanceTestNode::FilterInstantiations(InstantiationSet& aInstantiations, void* aClosure) const
nsRDFConInstanceTestNode::FilterInstantiations(InstantiationSet& aInstantiations) const
{
nsresult rv;
@ -118,18 +117,26 @@ nsRDFConInstanceTestNode::FilterInstantiations(InstantiationSet& aInstantiations
if (! rdfc)
return NS_ERROR_FAILURE;
nsIRDFDataSource* ds = mProcessor->GetDataSource();
InstantiationSet::Iterator last = aInstantiations.Last();
for (InstantiationSet::Iterator inst = aInstantiations.First(); inst != last; ++inst) {
Value value;
if (! inst->mAssignments.GetAssignmentFor(mContainerVariable, &value)) {
nsCOMPtr<nsIRDFNode> value;
if (! inst->mAssignments.GetAssignmentFor(mContainerVariable, getter_AddRefs(value))) {
NS_ERROR("can't do unbounded container testing");
return NS_ERROR_UNEXPECTED;
}
nsCOMPtr<nsIRDFResource> valueres = do_QueryInterface(value);
if (! valueres) {
aInstantiations.Erase(inst--);
continue;
}
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
const char* container;
VALUE_TO_IRDFRESOURCE(value)->GetValueConst(&container);
const char* container = "(unbound)";
valueres->GetValueConst(&container);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsRDFConInstanceTestNode[%p]::FilterInstantiations() container=[%s]",
@ -140,7 +147,7 @@ nsRDFConInstanceTestNode::FilterInstantiations(InstantiationSet& aInstantiations
nsCOMPtr<nsIRDFContainer> rdfcontainer;
PRBool isRDFContainer;
rv = rdfc->IsContainer(mDataSource, VALUE_TO_IRDFRESOURCE(value), &isRDFContainer);
rv = rdfc->IsContainer(ds, valueres, &isRDFContainer);
if (NS_FAILED(rv)) return rv;
if (mEmpty != eDontCare || mContainer != eDontCare) {
@ -156,7 +163,7 @@ nsRDFConInstanceTestNode::FilterInstantiations(InstantiationSet& aInstantiations
rdfcontainer = do_CreateInstance("@mozilla.org/rdf/container;1", &rv);
if (NS_FAILED(rv)) return rv;
rv = rdfcontainer->Init(mDataSource, VALUE_TO_IRDFRESOURCE(value));
rv = rdfcontainer->Init(ds, valueres);
if (NS_FAILED(rv)) return rv;
PRInt32 count;
@ -169,13 +176,14 @@ nsRDFConInstanceTestNode::FilterInstantiations(InstantiationSet& aInstantiations
container = eFalse;
// First do the simple check of finding some outward
// arcs; mMembershipProperties should be short, so this can
// arcs; there should be only a few containment arcs, so this can
// save us time from dealing with an iterator later on
for (nsResourceSet::ConstIterator property = mMembershipProperties.First();
property != mMembershipProperties.Last();
nsResourceSet& containmentProps = mProcessor->ContainmentProperties();
for (nsResourceSet::ConstIterator property = containmentProps.First();
property != containmentProps.Last();
++property) {
nsCOMPtr<nsIRDFNode> target;
rv = mDataSource->GetTarget(VALUE_TO_IRDFRESOURCE(value), *property, PR_TRUE, getter_AddRefs(target));
rv = ds->GetTarget(valueres, *property, PR_TRUE, getter_AddRefs(target));
if (NS_FAILED(rv)) return rv;
if (target != nsnull) {
@ -191,7 +199,7 @@ nsRDFConInstanceTestNode::FilterInstantiations(InstantiationSet& aInstantiations
// to check ArcLabelsOut for potential container arcs.
if (container == eFalse && mContainer != eDontCare) {
nsCOMPtr<nsISimpleEnumerator> arcsout;
rv = mDataSource->ArcLabelsOut(VALUE_TO_IRDFRESOURCE(value), getter_AddRefs(arcsout));
rv = ds->ArcLabelsOut(valueres, getter_AddRefs(arcsout));
if (NS_FAILED(rv)) return rv;
while (1) {
@ -211,7 +219,7 @@ nsRDFConInstanceTestNode::FilterInstantiations(InstantiationSet& aInstantiations
if (! property)
return NS_ERROR_UNEXPECTED;
if (mMembershipProperties.Contains(property)) {
if (mProcessor->ContainmentProperties().Contains(property)) {
container = eTrue;
break;
}
@ -232,9 +240,8 @@ nsRDFConInstanceTestNode::FilterInstantiations(InstantiationSet& aInstantiations
((mContainer == eDontCare) && (mEmpty == empty)))
{
Element* element =
nsRDFConInstanceTestNode::Element::Create(mConflictSet.GetPool(),
VALUE_TO_IRDFRESOURCE(value),
container, empty);
nsRDFConInstanceTestNode::Element::Create(mProcessor->GetPool(),
valueres, container, empty);
if (! element)
return NS_ERROR_OUT_OF_MEMORY;
@ -250,17 +257,6 @@ nsRDFConInstanceTestNode::FilterInstantiations(InstantiationSet& aInstantiations
return NS_OK;
}
nsresult
nsRDFConInstanceTestNode::GetAncestorVariables(VariableSet& aVariables) const
{
nsresult rv;
rv = aVariables.Add(mContainerVariable);
if (NS_FAILED(rv)) return rv;
return TestNode::GetAncestorVariables(aVariables);
}
PRBool
nsRDFConInstanceTestNode::CanPropagate(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
@ -282,7 +278,7 @@ nsRDFConInstanceTestNode::CanPropagate(nsIRDFResource* aSource,
if (NS_FAILED(rv)) return PR_FALSE;
if (! canpropagate) {
canpropagate = mMembershipProperties.Contains(aProperty);
canpropagate = mProcessor->ContainmentProperties().Contains(aProperty);
}
#ifdef PR_LOGGING
@ -304,7 +300,7 @@ nsRDFConInstanceTestNode::CanPropagate(nsIRDFResource* aSource,
#endif
if (canpropagate) {
aInitialBindings.AddAssignment(mContainerVariable, Value(aSource));
aInitialBindings.AddAssignment(mContainerVariable, aSource);
return PR_TRUE;
}
@ -314,13 +310,11 @@ nsRDFConInstanceTestNode::CanPropagate(nsIRDFResource* aSource,
void
nsRDFConInstanceTestNode::Retract(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget,
nsTemplateMatchSet& aFirings,
nsTemplateMatchSet& aRetractions) const
nsIRDFNode* aTarget) const
{
// XXXwaterson oof. complicated. figure this out.
if (0) {
mConflictSet.Remove(Element(aSource, mContainer, mEmpty), aFirings, aRetractions);
mProcessor->RetractElement(Element(aSource, mContainer, mEmpty));
}
}

Просмотреть файл

@ -40,12 +40,11 @@
#define nsRDFConInstanceTestNode_h__
#include "nscore.h"
#include "nsRDFTestNode.h"
#include "nsFixedSizeAllocator.h"
#include "nsRDFTestNode.h"
#include "nsIRDFResource.h"
#include "nsIRDFDataSource.h"
class nsConflictSet;
class nsResourceSet;
#include "nsXULTemplateQueryProcessorRDF.h"
/**
* Rule network node that tests if a resource is an RDF container, or
@ -56,17 +55,13 @@ class nsRDFConInstanceTestNode : public nsRDFTestNode
public:
enum Test { eFalse, eTrue, eDontCare };
nsRDFConInstanceTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
nsIRDFDataSource* aDataSource,
const nsResourceSet& aMembershipProperties,
PRInt32 aContainerVariable,
nsRDFConInstanceTestNode(TestNode* aParent,
nsXULTemplateQueryProcessorRDF* aProcessor,
nsIAtom* aContainerVariable,
Test aContainer,
Test aEmpty);
virtual nsresult FilterInstantiations(InstantiationSet& aInstantiations, void* aClosure) const;
virtual nsresult GetAncestorVariables(VariableSet& aVariables) const;
virtual nsresult FilterInstantiations(InstantiationSet& aInstantiations) const;
virtual PRBool
CanPropagate(nsIRDFResource* aSource,
@ -77,9 +72,7 @@ public:
virtual void
Retract(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget,
nsTemplateMatchSet& aFirings,
nsTemplateMatchSet& aRetractions) const;
nsIRDFNode* aTarget) const;
class Element : public MemoryElement {
@ -139,10 +132,8 @@ public:
};
protected:
nsConflictSet& mConflictSet;
nsCOMPtr<nsIRDFDataSource> mDataSource;
const nsResourceSet& mMembershipProperties;
PRInt32 mContainerVariable;
nsXULTemplateQueryProcessorRDF* mProcessor;
nsCOMPtr<nsIAtom> mContainerVariable;
Test mContainer;
Test mEmpty;
};

Просмотреть файл

@ -42,7 +42,6 @@
#include "nsRDFCID.h"
#include "nsIServiceManager.h"
#include "nsResourceSet.h"
#include "nsConflictSet.h"
#include "nsString.h"
#include "prlog.h"
@ -51,16 +50,12 @@
extern PRLogModuleInfo* gXULTemplateLog;
#endif
nsRDFConMemberTestNode::nsRDFConMemberTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
nsIRDFDataSource* aDataSource,
const nsResourceSet& aMembershipProperties,
PRInt32 aContainerVariable,
PRInt32 aMemberVariable)
nsRDFConMemberTestNode::nsRDFConMemberTestNode(TestNode* aParent,
nsXULTemplateQueryProcessorRDF* aProcessor,
nsIAtom *aContainerVariable,
nsIAtom *aMemberVariable)
: nsRDFTestNode(aParent),
mConflictSet(aConflictSet),
mDataSource(aDataSource),
mMembershipProperties(aMembershipProperties),
mProcessor(aProcessor),
mContainerVariable(aContainerVariable),
mMemberVariable(aMemberVariable)
{
@ -68,8 +63,9 @@ nsRDFConMemberTestNode::nsRDFConMemberTestNode(InnerNode* aParent,
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
nsCAutoString props;
nsResourceSet::ConstIterator last = aMembershipProperties.Last();
nsResourceSet::ConstIterator first = aMembershipProperties.First();
nsResourceSet& containmentProps = aProcessor->ContainmentProperties();
nsResourceSet::ConstIterator last = containmentProps.Last();
nsResourceSet::ConstIterator first = containmentProps.First();
nsResourceSet::ConstIterator iter;
for (iter = first; iter != last; ++iter) {
@ -82,19 +78,27 @@ nsRDFConMemberTestNode::nsRDFConMemberTestNode(InnerNode* aParent,
props += str;
}
nsAutoString cvar(NS_LITERAL_STRING("(none)"));
if (mContainerVariable)
mContainerVariable->ToString(cvar);
nsAutoString mvar(NS_LITERAL_STRING("(none)"));
if (mMemberVariable)
mMemberVariable->ToString(mvar);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsRDFConMemberTestNode[%p]: parent=%p member-props=(%s) container-var=%d member-var=%d",
("nsRDFConMemberTestNode[%p]: parent=%p member-props=(%s) container-var=%s member-var=%s",
this,
aParent,
props.get(),
mContainerVariable,
mMemberVariable));
NS_ConvertUTF16toUTF8(cvar).get(),
NS_ConvertUTF16toUTF8(mvar).get()));
}
#endif
}
nsresult
nsRDFConMemberTestNode::FilterInstantiations(InstantiationSet& aInstantiations, void* aClosure) const
nsRDFConMemberTestNode::FilterInstantiations(InstantiationSet& aInstantiations) const
{
// XXX Uh, factor me, please!
nsresult rv;
@ -105,46 +109,50 @@ nsRDFConMemberTestNode::FilterInstantiations(InstantiationSet& aInstantiations,
if (! rdfc)
return NS_ERROR_FAILURE;
nsIRDFDataSource* ds = mProcessor->GetDataSource();
InstantiationSet::Iterator last = aInstantiations.Last();
for (InstantiationSet::Iterator inst = aInstantiations.First(); inst != last; ++inst) {
PRBool hasContainerBinding;
Value containerValue;
hasContainerBinding = inst->mAssignments.GetAssignmentFor(mContainerVariable, &containerValue);
nsCOMPtr<nsIRDFNode> containerValue;
hasContainerBinding = inst->mAssignments.GetAssignmentFor(mContainerVariable,
getter_AddRefs(containerValue));
nsCOMPtr<nsIRDFResource> containerRes = do_QueryInterface(containerValue);
nsCOMPtr<nsIRDFContainer> rdfcontainer;
if (hasContainerBinding) {
if (hasContainerBinding && containerRes) {
// If we have a container assignment, then see if the
// container is an RDF container (bag, seq, alt), and if
// so, wrap it.
PRBool isRDFContainer;
rv = rdfc->IsContainer(mDataSource,
VALUE_TO_IRDFRESOURCE(containerValue),
&isRDFContainer);
rv = rdfc->IsContainer(ds, containerRes, &isRDFContainer);
if (NS_FAILED(rv)) return rv;
if (isRDFContainer) {
rdfcontainer = do_CreateInstance("@mozilla.org/rdf/container;1", &rv);
if (NS_FAILED(rv)) return rv;
rv = rdfcontainer->Init(mDataSource, VALUE_TO_IRDFRESOURCE(containerValue));
rv = rdfcontainer->Init(ds, containerRes);
if (NS_FAILED(rv)) return rv;
}
}
PRBool hasMemberBinding;
Value memberValue;
hasMemberBinding = inst->mAssignments.GetAssignmentFor(mMemberVariable, &memberValue);
nsCOMPtr<nsIRDFNode> memberValue;
hasMemberBinding = inst->mAssignments.GetAssignmentFor(mMemberVariable,
getter_AddRefs(memberValue));
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
const char* container = "(unbound)";
if (hasContainerBinding)
VALUE_TO_IRDFRESOURCE(containerValue)->GetValueConst(&container);
containerRes->GetValueConst(&container);
nsAutoString member(NS_LITERAL_STRING("(unbound)"));
if (hasMemberBinding)
nsXULContentUtils::GetTextForNode(VALUE_TO_IRDFRESOURCE(memberValue), member);
nsXULContentUtils::GetTextForNode(memberValue, member);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsRDFConMemberTestNode[%p]: FilterInstantiations() container=[%s] member=[%s]",
@ -159,7 +167,7 @@ nsRDFConMemberTestNode::FilterInstantiations(InstantiationSet& aInstantiations,
if (rdfcontainer) {
// RDF containers are easy. Just use the container API.
PRInt32 index;
rv = rdfcontainer->IndexOf(VALUE_TO_IRDFRESOURCE(memberValue), &index);
rv = rdfcontainer->IndexOf(memberValue, &index);
if (NS_FAILED(rv)) return rv;
if (index >= 0)
@ -174,15 +182,16 @@ nsRDFConMemberTestNode::FilterInstantiations(InstantiationSet& aInstantiations,
// Othewise, we'll need to grovel through the
// membership properties to see if we have an
// assertion that indicates membership.
for (nsResourceSet::ConstIterator property = mMembershipProperties.First();
property != mMembershipProperties.Last();
nsResourceSet& containmentProps = mProcessor->ContainmentProperties();
for (nsResourceSet::ConstIterator property = containmentProps.First();
property != containmentProps.Last();
++property) {
PRBool hasAssertion;
rv = mDataSource->HasAssertion(VALUE_TO_IRDFRESOURCE(containerValue),
*property,
VALUE_TO_IRDFNODE(memberValue),
PR_TRUE,
&hasAssertion);
rv = ds->HasAssertion(containerRes,
*property,
memberValue,
PR_TRUE,
&hasAssertion);
if (NS_FAILED(rv)) return rv;
if (hasAssertion) {
@ -200,9 +209,9 @@ nsRDFConMemberTestNode::FilterInstantiations(InstantiationSet& aInstantiations,
if (isconsistent) {
// Add a memory element to our set-of-support.
Element* element =
nsRDFConMemberTestNode::Element::Create(mConflictSet.GetPool(),
VALUE_TO_IRDFRESOURCE(containerValue),
VALUE_TO_IRDFNODE(memberValue));
nsRDFConMemberTestNode::Element::Create(mProcessor->GetPool(),
containerRes,
memberValue);
if (! element)
return NS_ERROR_OUT_OF_MEMORY;
@ -253,18 +262,17 @@ nsRDFConMemberTestNode::FilterInstantiations(InstantiationSet& aInstantiations,
#endif
Instantiation newinst = *inst;
newinst.AddAssignment(mMemberVariable, Value(node.get()));
newinst.AddAssignment(mMemberVariable, node);
Element* element =
nsRDFConMemberTestNode::Element::Create(mConflictSet.GetPool(),
VALUE_TO_IRDFRESOURCE(containerValue),
nsRDFConMemberTestNode::Element::Create(mProcessor->GetPool(),
containerRes,
node);
if (! element)
return NS_ERROR_OUT_OF_MEMORY;
newinst.AddSupportingElement(element);
aInstantiations.Insert(inst, newinst);
}
}
@ -276,7 +284,7 @@ nsRDFConMemberTestNode::FilterInstantiations(InstantiationSet& aInstantiations,
// container might have). If so, walk it backwards to get
// the container we're in.
nsCOMPtr<nsISimpleEnumerator> arcsin;
rv = mDataSource->ArcLabelsIn(VALUE_TO_IRDFNODE(memberValue), getter_AddRefs(arcsin));
rv = ds->ArcLabelsIn(memberValue, getter_AddRefs(arcsin));
if (NS_FAILED(rv)) return rv;
while (1) {
@ -314,8 +322,8 @@ nsRDFConMemberTestNode::FilterInstantiations(InstantiationSet& aInstantiations,
// member node. Find all the people that point to
// it, and call them containers.
nsCOMPtr<nsISimpleEnumerator> sources;
rv = mDataSource->GetSources(property, VALUE_TO_IRDFNODE(memberValue), PR_TRUE,
getter_AddRefs(sources));
rv = ds->GetSources(property, memberValue, PR_TRUE,
getter_AddRefs(sources));
if (NS_FAILED(rv)) return rv;
while (1) {
@ -346,12 +354,12 @@ nsRDFConMemberTestNode::FilterInstantiations(InstantiationSet& aInstantiations,
// Add a new instantiation
Instantiation newinst = *inst;
newinst.AddAssignment(mContainerVariable, Value(source.get()));
newinst.AddAssignment(mContainerVariable, source);
Element* element =
nsRDFConMemberTestNode::Element::Create(mConflictSet.GetPool(),
nsRDFConMemberTestNode::Element::Create(mProcessor->GetPool(),
source,
VALUE_TO_IRDFNODE(memberValue));
memberValue);
if (! element)
return NS_ERROR_OUT_OF_MEMORY;
@ -369,17 +377,18 @@ nsRDFConMemberTestNode::FilterInstantiations(InstantiationSet& aInstantiations,
// it's an open ended query on the container or member. go
// through our containment properties to see if anything
// applies.
for (nsResourceSet::ConstIterator property = mMembershipProperties.First();
property != mMembershipProperties.Last();
nsResourceSet& containmentProps = mProcessor->ContainmentProperties();
for (nsResourceSet::ConstIterator property = containmentProps.First();
property != containmentProps.Last();
++property) {
nsCOMPtr<nsISimpleEnumerator> results;
if (hasContainerBinding) {
rv = mDataSource->GetTargets(VALUE_TO_IRDFRESOURCE(containerValue), *property, PR_TRUE,
getter_AddRefs(results));
rv = ds->GetTargets(containerRes, *property, PR_TRUE,
getter_AddRefs(results));
}
else {
rv = mDataSource->GetSources(*property, VALUE_TO_IRDFNODE(memberValue), PR_TRUE,
getter_AddRefs(results));
rv = ds->GetSources(*property, memberValue, PR_TRUE,
getter_AddRefs(results));
}
if (NS_FAILED(rv)) return rv;
@ -395,46 +404,43 @@ nsRDFConMemberTestNode::FilterInstantiations(InstantiationSet& aInstantiations,
rv = results->GetNext(getter_AddRefs(isupports));
if (NS_FAILED(rv)) return rv;
PRInt32 variable;
Value value;
nsIAtom* variable;
nsCOMPtr<nsIRDFNode> value;
nsCOMPtr<nsIRDFResource> valueRes;
if (hasContainerBinding) {
variable = mMemberVariable;
nsCOMPtr<nsIRDFNode> member = do_QueryInterface(isupports);
NS_ASSERTION(member != nsnull, "member is not an nsIRDFNode");
if (! member) continue;
value = do_QueryInterface(isupports);
NS_ASSERTION(value != nsnull, "member is not an nsIRDFNode");
if (! value) continue;
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
nsAutoString s;
nsXULContentUtils::GetTextForNode(member, s);
nsXULContentUtils::GetTextForNode(value, s);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
(" member => %s", NS_ConvertUTF16toUTF8(s).get()));
}
#endif
value = member.get();
}
else {
variable = mContainerVariable;
nsCOMPtr<nsIRDFResource> container = do_QueryInterface(isupports);
NS_ASSERTION(container != nsnull, "container is not an nsIRDFResource");
if (! container) continue;
valueRes = do_QueryInterface(isupports);
NS_ASSERTION(valueRes != nsnull, "container is not an nsIRDFResource");
if (! valueRes) continue;
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
const char* s;
container->GetValueConst(&s);
valueRes->GetValueConst(&s);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
(" container => %s", s));
}
#endif
value = container.get();
}
// Copy the original instantiation, and add it to the
@ -446,15 +452,14 @@ nsRDFConMemberTestNode::FilterInstantiations(InstantiationSet& aInstantiations,
Element* element;
if (hasContainerBinding) {
element =
nsRDFConMemberTestNode::Element::Create(mConflictSet.GetPool(),
VALUE_TO_IRDFRESOURCE(containerValue),
VALUE_TO_IRDFNODE(value));
nsRDFConMemberTestNode::Element::Create(mProcessor->GetPool(),
containerRes,
value);
}
else {
element =
nsRDFConMemberTestNode::Element::Create(mConflictSet.GetPool(),
VALUE_TO_IRDFRESOURCE(value),
VALUE_TO_IRDFNODE(memberValue));
nsRDFConMemberTestNode::Element::Create(mProcessor->GetPool(),
valueRes, memberValue);
}
if (! element)
@ -480,21 +485,6 @@ nsRDFConMemberTestNode::FilterInstantiations(InstantiationSet& aInstantiations,
return NS_OK;
}
nsresult
nsRDFConMemberTestNode::GetAncestorVariables(VariableSet& aVariables) const
{
nsresult rv;
rv = aVariables.Add(mContainerVariable);
if (NS_FAILED(rv)) return rv;
rv = aVariables.Add(mMemberVariable);
if (NS_FAILED(rv)) return rv;
return TestNode::GetAncestorVariables(aVariables);
}
PRBool
nsRDFConMemberTestNode::CanPropagate(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
@ -516,7 +506,7 @@ nsRDFConMemberTestNode::CanPropagate(nsIRDFResource* aSource,
if (NS_FAILED(rv)) return PR_FALSE;
if (! canpropagate) {
canpropagate = mMembershipProperties.Contains(aProperty);
canpropagate = mProcessor->ContainmentProperties().Contains(aProperty);
}
#ifdef PR_LOGGING
@ -538,8 +528,8 @@ nsRDFConMemberTestNode::CanPropagate(nsIRDFResource* aSource,
#endif
if (canpropagate) {
aInitialBindings.AddAssignment(mContainerVariable, Value(aSource));
aInitialBindings.AddAssignment(mMemberVariable, Value(aTarget));
aInitialBindings.AddAssignment(mContainerVariable, aSource);
aInitialBindings.AddAssignment(mMemberVariable, aTarget);
return PR_TRUE;
}
@ -549,9 +539,7 @@ nsRDFConMemberTestNode::CanPropagate(nsIRDFResource* aSource,
void
nsRDFConMemberTestNode::Retract(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget,
nsTemplateMatchSet& aFirings,
nsTemplateMatchSet& aRetractions) const
nsIRDFNode* aTarget) const
{
PRBool canretract = PR_FALSE;
@ -567,10 +555,10 @@ nsRDFConMemberTestNode::Retract(nsIRDFResource* aSource,
if (NS_FAILED(rv)) return;
if (! canretract) {
canretract = mMembershipProperties.Contains(aProperty);
canretract = mProcessor->ContainmentProperties().Contains(aProperty);
}
if (canretract) {
mConflictSet.Remove(Element(aSource, aTarget), aFirings, aRetractions);
mProcessor->RetractElement(Element(aSource, aTarget));
}
}

Просмотреть файл

@ -42,9 +42,7 @@
#include "nscore.h"
#include "nsRDFTestNode.h"
#include "nsIRDFDataSource.h"
#include "nsFixedSizeAllocator.h"
class nsConflictSet;
class nsResourceSet;
#include "nsXULTemplateQueryProcessorRDF.h"
/**
* Rule network node that test if a resource is a member of an RDF
@ -54,16 +52,12 @@ class nsResourceSet;
class nsRDFConMemberTestNode : public nsRDFTestNode
{
public:
nsRDFConMemberTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
nsIRDFDataSource* aDataSource,
const nsResourceSet& aMembershipProperties,
PRInt32 aContainerVariable,
PRInt32 aMemberVariable);
nsRDFConMemberTestNode(TestNode* aParent,
nsXULTemplateQueryProcessorRDF* aProcessor,
nsIAtom* aContainerVariable,
nsIAtom* aMemberVariable);
virtual nsresult FilterInstantiations(InstantiationSet& aInstantiations, void* aClosure) const;
virtual nsresult GetAncestorVariables(VariableSet& aVariables) const;
virtual nsresult FilterInstantiations(InstantiationSet& aInstantiations) const;
virtual PRBool
CanPropagate(nsIRDFResource* aSource,
@ -74,9 +68,7 @@ public:
virtual void
Retract(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget,
nsTemplateMatchSet& aFirings,
nsTemplateMatchSet& aRetractions) const;
nsIRDFNode* aTarget) const;
class Element : public MemoryElement {
protected:
@ -130,11 +122,9 @@ public:
};
protected:
nsConflictSet& mConflictSet;
nsCOMPtr<nsIRDFDataSource> mDataSource;
const nsResourceSet& mMembershipProperties;
PRInt32 mContainerVariable;
PRInt32 mMemberVariable;
nsXULTemplateQueryProcessorRDF* mProcessor;
nsCOMPtr<nsIAtom> mContainerVariable;
nsCOMPtr<nsIAtom> mMemberVariable;
};
#endif // nsRDFConMemberTestNode_h__

Просмотреть файл

@ -37,7 +37,6 @@
* ***** END LICENSE BLOCK ***** */
#include "nsRDFPropertyTestNode.h"
#include "nsConflictSet.h"
#include "nsString.h"
#include "prlog.h"
@ -47,15 +46,13 @@ extern PRLogModuleInfo* gXULTemplateLog;
#include "nsXULContentUtils.h"
#endif
nsRDFPropertyTestNode::nsRDFPropertyTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
nsIRDFDataSource* aDataSource,
PRInt32 aSourceVariable,
nsRDFPropertyTestNode::nsRDFPropertyTestNode(TestNode* aParent,
nsXULTemplateQueryProcessorRDF* aProcessor,
nsIAtom* aSourceVariable,
nsIRDFResource* aProperty,
PRInt32 aTargetVariable)
nsIAtom* aTargetVariable)
: nsRDFTestNode(aParent),
mConflictSet(aConflictSet),
mDataSource(aDataSource),
mProcessor(aProcessor),
mSourceVariable(aSourceVariable),
mSource(nsnull),
mProperty(aProperty),
@ -68,23 +65,29 @@ nsRDFPropertyTestNode::nsRDFPropertyTestNode(InnerNode* aParent,
if (aProperty)
aProperty->GetValueConst(&prop);
nsAutoString svar(NS_LITERAL_STRING("(none)"));
if (mSourceVariable)
mSourceVariable->ToString(svar);
nsAutoString tvar(NS_LITERAL_STRING("(none)"));
if (mTargetVariable)
mTargetVariable->ToString(tvar);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsRDFPropertyTestNode[%p]: parent=%p source=%d property=%s target=%d",
this, aParent, aSourceVariable, prop, aTargetVariable));
("nsRDFPropertyTestNode[%p]: parent=%p source=%s property=%s target=%s",
this, aParent, NS_ConvertUTF16toUTF8(svar).get(), prop, NS_ConvertUTF16toUTF8(tvar).get()));
}
#endif
}
nsRDFPropertyTestNode::nsRDFPropertyTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
nsIRDFDataSource* aDataSource,
nsRDFPropertyTestNode::nsRDFPropertyTestNode(TestNode* aParent,
nsXULTemplateQueryProcessorRDF* aProcessor,
nsIRDFResource* aSource,
nsIRDFResource* aProperty,
PRInt32 aTargetVariable)
nsIAtom* aTargetVariable)
: nsRDFTestNode(aParent),
mConflictSet(aConflictSet),
mDataSource(aDataSource),
mProcessor(aProcessor),
mSourceVariable(0),
mSource(aSource),
mProperty(aProperty),
@ -101,23 +104,25 @@ nsRDFPropertyTestNode::nsRDFPropertyTestNode(InnerNode* aParent,
if (aProperty)
aProperty->GetValueConst(&prop);
nsAutoString tvar(NS_LITERAL_STRING("(none)"));
if (mTargetVariable)
mTargetVariable->ToString(tvar);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsRDFPropertyTestNode[%p]: parent=%p source=%s property=%s target=%d",
this, source, prop, aTargetVariable));
("nsRDFPropertyTestNode[%p]: parent=%p source=%s property=%s target=%s",
this, aParent, source, prop, NS_ConvertUTF16toUTF8(tvar).get()));
}
#endif
}
nsRDFPropertyTestNode::nsRDFPropertyTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
nsIRDFDataSource* aDataSource,
PRInt32 aSourceVariable,
nsRDFPropertyTestNode::nsRDFPropertyTestNode(TestNode* aParent,
nsXULTemplateQueryProcessorRDF* aProcessor,
nsIAtom* aSourceVariable,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget)
: nsRDFTestNode(aParent),
mConflictSet(aConflictSet),
mDataSource(aDataSource),
mProcessor(aProcessor),
mSourceVariable(aSourceVariable),
mSource(nsnull),
mProperty(aProperty),
@ -126,6 +131,10 @@ nsRDFPropertyTestNode::nsRDFPropertyTestNode(InnerNode* aParent,
{
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
nsAutoString svar(NS_LITERAL_STRING("(none)"));
if (mSourceVariable)
mSourceVariable->ToString(svar);
const char* prop = "(null)";
if (aProperty)
aProperty->GetValueConst(&prop);
@ -134,51 +143,57 @@ nsRDFPropertyTestNode::nsRDFPropertyTestNode(InnerNode* aParent,
nsXULContentUtils::GetTextForNode(aTarget, target);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsRDFPropertyTestNode[%p]: parent=%p source=%s property=%s target=%d",
this, aSourceVariable, prop, NS_ConvertUTF16toUTF8(target).get()));
("nsRDFPropertyTestNode[%p]: parent=%p source=%s property=%s target=%s",
this, aParent, NS_ConvertUTF16toUTF8(svar).get(), prop, NS_ConvertUTF16toUTF8(target).get()));
}
#endif
}
nsresult
nsRDFPropertyTestNode::FilterInstantiations(InstantiationSet& aInstantiations, void* aClosure) const
nsRDFPropertyTestNode::FilterInstantiations(InstantiationSet& aInstantiations) const
{
nsresult rv;
nsIRDFDataSource* ds = mProcessor->GetDataSource();
InstantiationSet::Iterator last = aInstantiations.Last();
for (InstantiationSet::Iterator inst = aInstantiations.First(); inst != last; ++inst) {
PRBool hasSourceBinding;
Value sourceValue;
nsCOMPtr<nsIRDFResource> sourceRes;
if (mSource) {
hasSourceBinding = PR_TRUE;
sourceValue = mSource;
sourceRes = mSource;
}
else {
hasSourceBinding = inst->mAssignments.GetAssignmentFor(mSourceVariable, &sourceValue);
nsCOMPtr<nsIRDFNode> sourceValue;
hasSourceBinding = inst->mAssignments.GetAssignmentFor(mSourceVariable,
getter_AddRefs(sourceValue));
sourceRes = do_QueryInterface(sourceValue);
}
PRBool hasTargetBinding;
Value targetValue;
nsCOMPtr<nsIRDFNode> targetValue;
if (mTarget) {
hasTargetBinding = PR_TRUE;
targetValue = mTarget;
}
else {
hasTargetBinding = inst->mAssignments.GetAssignmentFor(mTargetVariable, &targetValue);
hasTargetBinding = inst->mAssignments.GetAssignmentFor(mTargetVariable,
getter_AddRefs(targetValue));
}
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
const char* source = "(unbound)";
if (hasSourceBinding)
VALUE_TO_IRDFRESOURCE(sourceValue)->GetValueConst(&source);
sourceRes->GetValueConst(&source);
nsAutoString target(NS_LITERAL_STRING("(unbound)"));
if (hasTargetBinding)
nsXULContentUtils::GetTextForNode(VALUE_TO_IRDFNODE(targetValue), target);
nsXULContentUtils::GetTextForNode(targetValue, target);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("nsRDFPropertyTestNode[%p]: FilterInstantiations() source=[%s] target=[%s]",
@ -189,11 +204,8 @@ nsRDFPropertyTestNode::FilterInstantiations(InstantiationSet& aInstantiations, v
if (hasSourceBinding && hasTargetBinding) {
// it's a consistency check. see if we have a assignment that is consistent
PRBool hasAssertion;
rv = mDataSource->HasAssertion(VALUE_TO_IRDFRESOURCE(sourceValue),
mProperty,
VALUE_TO_IRDFNODE(targetValue),
PR_TRUE,
&hasAssertion);
rv = ds->HasAssertion(sourceRes, mProperty, targetValue,
PR_TRUE, &hasAssertion);
if (NS_FAILED(rv)) return rv;
#ifdef PR_LOGGING
@ -204,10 +216,10 @@ nsRDFPropertyTestNode::FilterInstantiations(InstantiationSet& aInstantiations, v
if (hasAssertion) {
// it's consistent.
Element* element =
nsRDFPropertyTestNode::Element::Create(mConflictSet.GetPool(),
VALUE_TO_IRDFRESOURCE(sourceValue),
nsRDFPropertyTestNode::Element::Create(mProcessor->GetPool(),
sourceRes,
mProperty,
VALUE_TO_IRDFNODE(targetValue));
targetValue);
if (! element)
return NS_ERROR_OUT_OF_MEMORY;
@ -226,16 +238,16 @@ nsRDFPropertyTestNode::FilterInstantiations(InstantiationSet& aInstantiations, v
// cross-product.
nsCOMPtr<nsISimpleEnumerator> results;
if (hasSourceBinding) {
rv = mDataSource->GetTargets(VALUE_TO_IRDFRESOURCE(sourceValue),
mProperty,
PR_TRUE,
getter_AddRefs(results));
rv = ds->GetTargets(sourceRes,
mProperty,
PR_TRUE,
getter_AddRefs(results));
}
else {
rv = mDataSource->GetSources(mProperty,
VALUE_TO_IRDFNODE(targetValue),
PR_TRUE,
getter_AddRefs(results));
rv = ds->GetSources(mProperty,
targetValue,
PR_TRUE,
getter_AddRefs(results));
if (NS_FAILED(rv)) return rv;
}
@ -251,29 +263,29 @@ nsRDFPropertyTestNode::FilterInstantiations(InstantiationSet& aInstantiations, v
rv = results->GetNext(getter_AddRefs(isupports));
if (NS_FAILED(rv)) return rv;
PRInt32 variable;
Value value;
nsIAtom* variable;
nsCOMPtr<nsIRDFNode> value;
if (hasSourceBinding) {
variable = mTargetVariable;
nsCOMPtr<nsIRDFNode> target = do_QueryInterface(isupports);
NS_ASSERTION(target != nsnull, "target is not an nsIRDFNode");
value = do_QueryInterface(isupports);
NS_ASSERTION(value != nsnull, "target is not an nsIRDFNode");
#ifdef PR_LOGGING
if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
nsAutoString s(NS_LITERAL_STRING("(none found)"));
if (target)
nsXULContentUtils::GetTextForNode(target, s);
if (value)
nsXULContentUtils::GetTextForNode(value, s);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
(" target => %s", NS_ConvertUTF16toUTF8(s).get()));
}
#endif
if (! target) continue;
if (! value) continue;
targetValue = value = target.get();
targetValue = value;
}
else {
variable = mSourceVariable;
@ -294,7 +306,7 @@ nsRDFPropertyTestNode::FilterInstantiations(InstantiationSet& aInstantiations, v
if (! source) continue;
sourceValue = value = source.get();
value = sourceRes = source;
}
// Copy the original instantiation, and add it to the
@ -304,10 +316,10 @@ nsRDFPropertyTestNode::FilterInstantiations(InstantiationSet& aInstantiations, v
newinst.AddAssignment(variable, value);
Element* element =
nsRDFPropertyTestNode::Element::Create(mConflictSet.GetPool(),
VALUE_TO_IRDFRESOURCE(sourceValue),
nsRDFPropertyTestNode::Element::Create(mProcessor->GetPool(),
sourceRes,
mProperty,
VALUE_TO_IRDFNODE(targetValue));
targetValue);
if (! element)
return NS_ERROR_OUT_OF_MEMORY;
@ -330,25 +342,6 @@ nsRDFPropertyTestNode::FilterInstantiations(InstantiationSet& aInstantiations, v
return NS_OK;
}
nsresult
nsRDFPropertyTestNode::GetAncestorVariables(VariableSet& aVariables) const
{
nsresult rv;
if (mSourceVariable) {
rv = aVariables.Add(mSourceVariable);
if (NS_FAILED(rv)) return rv;
}
if (mTargetVariable) {
rv = aVariables.Add(mTargetVariable);
if (NS_FAILED(rv)) return rv;
}
return TestNode::GetAncestorVariables(aVariables);
}
PRBool
nsRDFPropertyTestNode::CanPropagate(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
@ -364,10 +357,10 @@ nsRDFPropertyTestNode::CanPropagate(nsIRDFResource* aSource,
}
else {
if (mSourceVariable)
aInitialBindings.AddAssignment(mSourceVariable, Value(aSource));
aInitialBindings.AddAssignment(mSourceVariable, aSource);
if (mTargetVariable)
aInitialBindings.AddAssignment(mTargetVariable, Value(aTarget));
aInitialBindings.AddAssignment(mTargetVariable, aTarget);
result = PR_TRUE;
}
@ -396,9 +389,7 @@ nsRDFPropertyTestNode::CanPropagate(nsIRDFResource* aSource,
void
nsRDFPropertyTestNode::Retract(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget,
nsTemplateMatchSet& aFirings,
nsTemplateMatchSet& aRetractions) const
nsIRDFNode* aTarget) const
{
if (aProperty == mProperty.get()) {
#ifdef PR_LOGGING
@ -418,7 +409,7 @@ nsRDFPropertyTestNode::Retract(nsIRDFResource* aSource,
}
#endif
mConflictSet.Remove(Element(aSource, aProperty, aTarget), aFirings, aRetractions);
mProcessor->RetractElement(Element(aSource, aProperty, aTarget));
}
}

Просмотреть файл

@ -40,11 +40,10 @@
#define nsRDFPropertyTestNode_h__
#include "nscore.h"
#include "nsFixedSizeAllocator.h"
#include "nsRDFTestNode.h"
#include "nsIRDFDataSource.h"
#include "nsIRDFResource.h"
class nsConflictSet;
#include "nsXULTemplateQueryProcessorRDF.h"
class nsRDFPropertyTestNode : public nsRDFTestNode
{
@ -52,36 +51,31 @@ public:
/**
* Both source and target unbound (?source ^property ?target)
*/
nsRDFPropertyTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
nsIRDFDataSource* aDataSource,
PRInt32 aSourceVariable,
nsRDFPropertyTestNode(TestNode* aParent,
nsXULTemplateQueryProcessorRDF* aProcessor,
nsIAtom* aSourceVariable,
nsIRDFResource* aProperty,
PRInt32 aTargetVariable);
nsIAtom* aTargetVariable);
/**
* Source bound, target unbound (source ^property ?target)
*/
nsRDFPropertyTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
nsIRDFDataSource* aDataSource,
nsRDFPropertyTestNode(TestNode* aParent,
nsXULTemplateQueryProcessorRDF* aProcessor,
nsIRDFResource* aSource,
nsIRDFResource* aProperty,
PRInt32 aTargetVariable);
nsIAtom* aTargetVariable);
/**
* Source unbound, target bound (?source ^property target)
*/
nsRDFPropertyTestNode(InnerNode* aParent,
nsConflictSet& aConflictSet,
nsIRDFDataSource* aDataSource,
PRInt32 aSourceVariable,
nsRDFPropertyTestNode(TestNode* aParent,
nsXULTemplateQueryProcessorRDF* aProcessor,
nsIAtom* aSourceVariable,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget);
virtual nsresult FilterInstantiations(InstantiationSet& aInstantiations, void* aClosure) const;
virtual nsresult GetAncestorVariables(VariableSet& aVariables) const;
virtual nsresult FilterInstantiations(InstantiationSet& aInstantiations) const;
virtual PRBool
CanPropagate(nsIRDFResource* aSource,
@ -92,9 +86,7 @@ public:
virtual void
Retract(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget,
nsTemplateMatchSet& aFirings,
nsTemplateMatchSet& aRetractions) const;
nsIRDFNode* aTarget) const;
class Element : public MemoryElement {
@ -156,12 +148,11 @@ public:
};
protected:
nsConflictSet& mConflictSet;
nsCOMPtr<nsIRDFDataSource> mDataSource;
PRInt32 mSourceVariable;
nsXULTemplateQueryProcessorRDF* mProcessor;
nsCOMPtr<nsIAtom> mSourceVariable;
nsCOMPtr<nsIRDFResource> mSource;
nsCOMPtr<nsIRDFResource> mProperty;
PRInt32 mTargetVariable;
nsCOMPtr<nsIAtom> mTargetVariable;
nsCOMPtr<nsIRDFNode> mTarget;
};

Просмотреть файл

@ -0,0 +1,73 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Neil Deakin
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nscore.h"
#include "nsCOMPtr.h"
#include "nsXULTemplateQueryProcessorRDF.h"
#include "nsRDFQuery.h"
NS_IMPL_ISUPPORTS1(nsRDFQuery, nsITemplateRDFQuery)
void
nsRDFQuery::Finish()
{
// the template builder is going away and the query processor likely as
// well. Clear the reference to avoid calling it.
mProcessor = nsnull;
mCachedResults = nsnull;
}
nsresult
nsRDFQuery::SetCachedResults(nsXULTemplateQueryProcessorRDF* aProcessor,
const InstantiationSet& aInstantiations)
{
mCachedResults = new nsXULTemplateResultSetRDF(aProcessor, this, &aInstantiations);
if (! mCachedResults)
return NS_ERROR_OUT_OF_MEMORY;
return NS_OK;
}
void
nsRDFQuery::UseCachedResults(nsISimpleEnumerator** aResults)
{
*aResults = mCachedResults;
NS_IF_ADDREF(*aResults);
mCachedResults = nsnull;
}

Просмотреть файл

@ -0,0 +1,157 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Neil Deakin
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsRDFQuery_h__
#define nsRDFQuery_h__
#include "nsAutoPtr.h"
#include "nsISimpleEnumerator.h"
#define NS_ITEMPLATERDFQUERY_IID \
{0x8929ff60, 0x1c9c, 0x4d87, \
{ 0xac, 0x02, 0x09, 0x14, 0x15, 0x3b, 0x48, 0xc4 }}
/**
* A compiled query in the RDF query processor. This interface should not be
* used directly outside of the RDF query processor.
*/
class nsITemplateRDFQuery : public nsISupports
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ITEMPLATERDFQUERY_IID)
// return the processor the query was created from
virtual nsXULTemplateQueryProcessorRDF* Processor() = 0; // not addrefed
// return the member variable for the query
virtual nsIAtom* GetMemberVariable() = 0; // not addrefed
// return the <query> node the query was compiled from
virtual void GetQueryNode(nsIDOMNode** aQueryNode) = 0;
// remove any results that are cached by the query
virtual void ClearCachedResults() = 0;
};
class nsRDFQuery : public nsITemplateRDFQuery
{
public:
nsRDFQuery(nsXULTemplateQueryProcessorRDF* aProcessor)
: mProcessor(aProcessor),
mSimple(PR_FALSE),
mRoot(nsnull),
mCachedResults(nsnull)
{ }
~nsRDFQuery() { Finish(); }
NS_DECL_ISUPPORTS
/**
* Retrieve the root node in the rule network
* @return the root node in the rule network
*/
TestNode* GetRoot() { return mRoot; };
void SetRoot(TestNode* aRoot) { mRoot = aRoot; };
void GetQueryNode(nsIDOMNode** aQueryNode)
{
*aQueryNode = mQueryNode;
NS_IF_ADDREF(*aQueryNode);
}
void SetQueryNode(nsIDOMNode* aQueryNode)
{
mQueryNode = aQueryNode;
}
// an optimization is used when several queries all use the simple query
// syntax. Since simple queries can only generate one possible set of
// results, they only need to be calculated once and reused for every
// simple query. The results may be cached in the query for this purpose.
// If successful, this method takes ownership of aInstantiations.
nsresult SetCachedResults(nsXULTemplateQueryProcessorRDF* aProcessor,
const InstantiationSet& aInstantiations);
// grab the cached results, if any, causing the caller to take ownership
// of them. This also has the effect of setting the cached results in this
// nsRDFQuery to null.
void UseCachedResults(nsISimpleEnumerator** aResults);
// clear the cached results
void ClearCachedResults()
{
mCachedResults = nsnull;
}
nsXULTemplateQueryProcessorRDF* Processor() { return mProcessor; }
nsIAtom* GetMemberVariable() { return mMemberVariable; }
PRBool IsSimple() { return mSimple; }
void SetSimple() { mSimple = PR_TRUE; }
// the reference and member variables for the query
nsCOMPtr<nsIAtom> mRefVariable;
nsCOMPtr<nsIAtom> mMemberVariable;
protected:
nsXULTemplateQueryProcessorRDF* mProcessor;
// true if the query is a simple rule (one with a default query)
PRBool mSimple;
/**
* The root node in the network for this query
*/
TestNode *mRoot;
// the <query> node
nsCOMPtr<nsIDOMNode> mQueryNode;
// used for simple rules since their results are all determined in one step
nsCOMPtr<nsISimpleEnumerator> mCachedResults;
void Finish();
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsRDFQuery, NS_ITEMPLATERDFQUERY_IID)
#endif // nsRDFQuery_h__

Просмотреть файл

@ -40,9 +40,9 @@
#define nsRDFTestNode_h__
#include "nsRuleNetwork.h"
class nsIRDFResource;
class nsIRDFNode;
class nsTemplateMatchSet;
/**
* An abstract base class for all of the RDF-related tests. This interface
@ -52,7 +52,7 @@ class nsTemplateMatchSet;
class nsRDFTestNode : public TestNode
{
public:
nsRDFTestNode(InnerNode* aParent)
nsRDFTestNode(TestNode* aParent)
: TestNode(aParent) {}
/**
@ -76,9 +76,7 @@ public:
*/
virtual void Retract(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget,
nsTemplateMatchSet& aFirings,
nsTemplateMatchSet& aRetractions) const = 0;
nsIRDFNode* aTarget) const = 0;
};
#endif // nsRDFTestNode_h__

Просмотреть файл

@ -21,6 +21,7 @@
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
* Neil Deakin <enndeakin@sympatico.ca>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
@ -54,461 +55,27 @@
#include "nsCRT.h"
#include "nsIComponentManager.h"
#include "nsIContent.h"
#include "nsRuleNetwork.h"
#include "plhash.h"
#include "nsReadableUtils.h"
#include "prlog.h"
#ifdef PR_LOGGING
extern PRLogModuleInfo* gXULTemplateLog;
#include "nsString.h"
#include "nsUnicharUtils.h"
#include "nsXULContentUtils.h"
#endif
#include "nsRuleNetwork.h"
#include "nsXULTemplateResultSetRDF.h"
//----------------------------------------------------------------------
//
// nsRuleNetwork
//
static PLDHashNumber PR_CALLBACK
HashEntry(PLDHashTable* aTable, const void* aKey)
{
return nsCRT::HashCode(NS_STATIC_CAST(const PRUnichar*, aKey));
}
static PRBool PR_CALLBACK
MatchEntry(PLDHashTable* aTable, const PLDHashEntryHdr* aEntry, const void* aKey)
{
const nsRuleNetwork::SymtabEntry* entry =
NS_REINTERPRET_CAST(const nsRuleNetwork::SymtabEntry*, aEntry);
return 0 == nsCRT::strcmp(entry->mSymbol, NS_STATIC_CAST(const PRUnichar*, aKey));
}
static void PR_CALLBACK
ClearEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
{
nsRuleNetwork::SymtabEntry* entry =
NS_REINTERPRET_CAST(nsRuleNetwork::SymtabEntry*, aEntry);
nsCRT::free(entry->mSymbol);
PL_DHashClearEntryStub(aTable, aEntry);
}
PLDHashTableOps nsRuleNetwork::gOps = {
PL_DHashAllocTable,
PL_DHashFreeTable,
PL_DHashGetKeyStub,
HashEntry,
MatchEntry,
PL_DHashMoveEntryStub,
ClearEntry,
PL_DHashFinalizeStub
};
void
nsRuleNetwork::Init()
{
mNextVariable = 0;
if (!PL_DHashTableInit(&mSymtab, &gOps, nsnull,
sizeof(SymtabEntry), PL_DHASH_MIN_SIZE))
mSymtab.ops = nsnull;
}
void
nsRuleNetwork::Finish()
{
if (mSymtab.ops)
PL_DHashTableFinish(&mSymtab);
// We "own" the nodes. So it's up to us to delete 'em
for (ReteNodeSet::Iterator node = mNodes.First(); node != mNodes.Last(); ++node)
delete *node;
mNodes.Clear();
mRoot.RemoveAllChildren();
}
//----------------------------------------------------------------------
//
// Value
//
#ifdef DEBUG
/**
* A debug-only implementation that verifies that 1) aValue really
* is an nsISupports, and 2) that it really does support the IID
* that is being asked for.
*/
nsISupports*
value_to_isupports(const nsIID& aIID, const Value& aValue)
{
nsresult rv;
// Need to const_cast aValue because QI() & Release() are not const
nsISupports* isupports = NS_STATIC_CAST(nsISupports*, NS_CONST_CAST(Value&, aValue));
if (isupports) {
nsCOMPtr<nsISupports> dummy;
rv = isupports->QueryInterface(aIID, getter_AddRefs(dummy));
if (NS_FAILED(rv)) {
NS_ERROR("value does not support expected interface");
}
}
return isupports;
}
#endif
Value::Value(const Value& aValue)
: mType(aValue.mType)
{
MOZ_COUNT_CTOR(Value);
switch (mType) {
case eUndefined:
break;
case eISupports:
mISupports = aValue.mISupports;
NS_IF_ADDREF(mISupports);
break;
case eString:
mString = nsCRT::strdup(aValue.mString);
break;
case eInteger:
mInteger = aValue.mInteger;
break;
}
}
Value::Value(nsISupports* aISupports)
: mType(eISupports)
{
MOZ_COUNT_CTOR(Value);
mISupports = aISupports;
NS_IF_ADDREF(mISupports);
}
Value::Value(const PRUnichar* aString)
: mType(eString)
{
MOZ_COUNT_CTOR(Value);
mString = nsCRT::strdup(aString);
}
Value::Value(PRInt32 aInteger)
: mType(eInteger)
{
MOZ_COUNT_CTOR(Value);
mInteger = aInteger;
}
Value&
Value::operator=(const Value& aValue)
{
Clear();
mType = aValue.mType;
switch (mType) {
case eUndefined:
break;
case eISupports:
mISupports = aValue.mISupports;
NS_IF_ADDREF(mISupports);
break;
case eString:
mString = nsCRT::strdup(aValue.mString);
break;
case eInteger:
mInteger = aValue.mInteger;
break;
}
return *this;
}
Value&
Value::operator=(nsISupports* aISupports)
{
Clear();
mType = eISupports;
mISupports = aISupports;
NS_IF_ADDREF(mISupports);
return *this;
}
Value&
Value::operator=(const PRUnichar* aString)
{
Clear();
mType = eString;
mString = nsCRT::strdup(aString);
return *this;
}
Value::~Value()
{
MOZ_COUNT_DTOR(Value);
Clear();
}
void
Value::Clear()
{
switch (mType) {
case eInteger:
case eUndefined:
break;
case eISupports:
NS_IF_RELEASE(mISupports);
break;
case eString:
nsCRT::free(mString);
break;
}
}
PRBool
Value::Equals(const Value& aValue) const
{
if (mType == aValue.mType) {
switch (mType) {
case eUndefined:
return PR_FALSE;
case eISupports:
return mISupports == aValue.mISupports;
case eString:
return nsCRT::strcmp(mString, aValue.mString) == 0;
case eInteger:
return mInteger == aValue.mInteger;
}
}
return PR_FALSE;
}
PRBool
Value::Equals(nsISupports* aISupports) const
{
return (mType == eISupports) && (mISupports == aISupports);
}
PRBool
Value::Equals(const PRUnichar* aString) const
{
return (mType == eString) && (nsCRT::strcmp(aString, mString) == 0);
}
PRBool
Value::Equals(PRInt32 aInteger) const
{
return (mType == eInteger) && (mInteger == aInteger);
}
PLHashNumber
Value::Hash() const
{
PLHashNumber temp = 0;
switch (mType) {
case eUndefined:
break;
case eISupports:
temp = PLHashNumber(NS_PTR_TO_INT32(mISupports)) >> 2; // strip alignment bits
break;
case eString:
{
PRUnichar* p = mString;
PRUnichar c;
while ((c = *p) != 0) {
temp = (temp >> 28) ^ (temp << 4) ^ c;
++p;
}
}
break;
case eInteger:
temp = mInteger;
break;
}
return temp;
}
Value::operator nsISupports*() const
{
NS_ASSERTION(mType == eISupports, "not an nsISupports");
return (mType == eISupports) ? mISupports : 0;
}
Value::operator const PRUnichar*() const
{
NS_ASSERTION(mType == eString, "not a string");
return (mType == eString) ? mString : 0;
}
Value::operator PRInt32() const
{
NS_ASSERTION(mType == eInteger, "not an integer");
return (mType == eInteger) ? mInteger : 0;
}
#ifdef DEBUG
#include "nsIRDFResource.h"
#include "nsIRDFLiteral.h"
#include "nsString.h"
#include "nsPrintfCString.h"
void
Value::ToCString(nsACString& aResult)
{
switch (mType) {
case eUndefined:
aResult = "[(undefined)]";
break;
case eISupports:
do {
nsCOMPtr<nsIRDFResource> res = do_QueryInterface(mISupports);
if (res) {
aResult = "[nsIRDFResource ";
const char* s;
res->GetValueConst(&s);
aResult += s;
aResult += "]";
break;
}
nsCOMPtr<nsIRDFLiteral> lit = do_QueryInterface(mISupports);
if (lit) {
aResult = "[nsIRDFLiteral \"";
const PRUnichar* s;
lit->GetValueConst(&s);
AppendUTF16toUTF8(s, aResult);
aResult += "\"]";
break;
}
aResult = "[nsISupports ";
aResult += nsPrintfCString("%p", mISupports);
aResult += "]";
} while (0);
break;
case eString:
aResult = "[string \"";
AppendUTF16toUTF8(mString, aResult);
aResult += "\"]";
break;
case eInteger:
aResult = "[integer ";
aResult += nsPrintfCString("%d", mInteger);
aResult += "]";
break;
}
}
#endif
//----------------------------------------------------------------------
//
// VariableSet
//
VariableSet::VariableSet()
: mVariables(nsnull), mCount(0), mCapacity(0)
{
}
VariableSet::~VariableSet()
{
delete[] mVariables;
}
nsresult
VariableSet::Add(PRInt32 aVariable)
{
if (Contains(aVariable))
return NS_OK;
if (mCount >= mCapacity) {
PRInt32 capacity = mCapacity + 4;
PRInt32* variables = new PRInt32[capacity];
if (! variables)
return NS_ERROR_OUT_OF_MEMORY;
for (PRInt32 i = mCount - 1; i >= 0; --i)
variables[i] = mVariables[i];
delete[] mVariables;
mVariables = variables;
mCapacity = capacity;
}
mVariables[mCount++] = aVariable;
return NS_OK;
}
nsresult
VariableSet::Remove(PRInt32 aVariable)
{
PRInt32 i = 0;
while (i < mCount) {
if (aVariable == mVariables[i])
break;
++i;
}
if (i >= mCount)
return NS_OK;
--mCount;
while (i < mCount) {
mVariables[i] = mVariables[i + 1];
++i;
}
return NS_OK;
}
PRBool
VariableSet::Contains(PRInt32 aVariable) const
{
for (PRInt32 i = mCount - 1; i >= 0; --i) {
if (aVariable == mVariables[i])
return PR_TRUE;
}
return PR_FALSE;
}
//----------------------------------------------------------------------=
nsresult
MemoryElementSet::Add(MemoryElement* aElement)
{
@ -542,6 +109,8 @@ nsresult
nsAssignmentSet::Add(const nsAssignment& aAssignment)
{
NS_PRECONDITION(! HasAssignmentFor(aAssignment.mVariable), "variable already bound");
// XXXndeakin should this just silently fail?
if (HasAssignmentFor(aAssignment.mVariable))
return NS_ERROR_UNEXPECTED;
@ -569,7 +138,7 @@ nsAssignmentSet::Count() const
}
PRBool
nsAssignmentSet::HasAssignment(PRInt32 aVariable, const Value& aValue) const
nsAssignmentSet::HasAssignment(nsIAtom* aVariable, nsIRDFNode* aValue) const
{
for (ConstIterator assignment = First(); assignment != Last(); ++assignment) {
if (assignment->mVariable == aVariable && assignment->mValue == aValue)
@ -580,7 +149,7 @@ nsAssignmentSet::HasAssignment(PRInt32 aVariable, const Value& aValue) const
}
PRBool
nsAssignmentSet::HasAssignmentFor(PRInt32 aVariable) const
nsAssignmentSet::HasAssignmentFor(nsIAtom* aVariable) const
{
for (ConstIterator assignment = First(); assignment != Last(); ++assignment) {
if (assignment->mVariable == aVariable)
@ -591,15 +160,17 @@ nsAssignmentSet::HasAssignmentFor(PRInt32 aVariable) const
}
PRBool
nsAssignmentSet::GetAssignmentFor(PRInt32 aVariable, Value* aValue) const
nsAssignmentSet::GetAssignmentFor(nsIAtom* aVariable, nsIRDFNode** aValue) const
{
for (ConstIterator assignment = First(); assignment != Last(); ++assignment) {
if (assignment->mVariable == aVariable) {
*aValue = assignment->mValue;
NS_IF_ADDREF(*aValue);
return PR_TRUE;
}
}
*aValue = nsnull;
return PR_FALSE;
}
@ -614,9 +185,9 @@ nsAssignmentSet::Equals(const nsAssignmentSet& aSet) const
return PR_FALSE;
// XXX O(n^2)! Ugh!
nsCOMPtr<nsIRDFNode> value;
for (ConstIterator assignment = First(); assignment != Last(); ++assignment) {
Value value;
if (! aSet.GetAssignmentFor(assignment->mVariable, &value))
if (! aSet.GetAssignmentFor(assignment->mVariable, getter_AddRefs(value)))
return PR_FALSE;
if (assignment->mValue != value)
@ -636,7 +207,8 @@ Instantiation::Hash(const void* aKey)
PLHashNumber result = 0;
nsAssignmentSet::ConstIterator last = inst->mAssignments.Last();
for (nsAssignmentSet::ConstIterator assignment = inst->mAssignments.First(); assignment != last; ++assignment)
for (nsAssignmentSet::ConstIterator assignment = inst->mAssignments.First();
assignment != last; ++assignment)
result ^= assignment->Hash();
return result;
@ -730,7 +302,7 @@ InstantiationSet::Erase(Iterator aIterator)
PRBool
InstantiationSet::HasAssignmentFor(PRInt32 aVariable) const
InstantiationSet::HasAssignmentFor(nsIAtom* aVariable) const
{
return !Empty() ? First()->mAssignments.HasAssignmentFor(aVariable) : PR_FALSE;
}
@ -742,281 +314,6 @@ InstantiationSet::HasAssignmentFor(PRInt32 aVariable) const
// The basic node in the network.
//
//----------------------------------------------------------------------
//
// RootNode
//
nsresult
RootNode::Propagate(const InstantiationSet& aInstantiations, void* aClosure)
{
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("RootNode[%p]: Propagate() begin", this));
ReteNodeSet::Iterator last = mKids.Last();
for (ReteNodeSet::Iterator kid = mKids.First(); kid != last; ++kid)
kid->Propagate(aInstantiations, aClosure);
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("RootNode[%p]: Propagate() end", this));
return NS_OK;
}
nsresult
RootNode::Constrain(InstantiationSet& aInstantiations, void* aClosure)
{
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("RootNode[%p]: Constrain()", this));
return NS_OK;
}
nsresult
RootNode::GetAncestorVariables(VariableSet& aVariables) const
{
return NS_OK;
}
PRBool
RootNode::HasAncestor(const ReteNode* aNode) const
{
return aNode == this;
}
//----------------------------------------------------------------------
//
// JoinNode
//
// A node that performs a join in the network.
//
JoinNode::JoinNode(InnerNode* aLeftParent,
PRInt32 aLeftVariable,
InnerNode* aRightParent,
PRInt32 aRightVariable,
Operator aOperator)
: mLeftParent(aLeftParent),
mLeftVariable(aLeftVariable),
mRightParent(aRightParent),
mRightVariable(aRightVariable),
mOperator(aOperator)
{
}
nsresult
JoinNode::Propagate(const InstantiationSet& aInstantiations, void* aClosure)
{
// the add will have been propagated down from one of the parent
// nodes: either the left or the right. Test the other node for
// matches.
nsresult rv;
PRBool hasLeftAssignment = aInstantiations.HasAssignmentFor(mLeftVariable);
PRBool hasRightAssignment = aInstantiations.HasAssignmentFor(mRightVariable);
NS_ASSERTION(hasLeftAssignment ^ hasRightAssignment, "there isn't exactly one assignment specified");
if (! (hasLeftAssignment ^ hasRightAssignment))
return NS_ERROR_UNEXPECTED;
InstantiationSet instantiations = aInstantiations;
InnerNode* test = hasLeftAssignment ? mRightParent : mLeftParent;
{
// extend the assignments
InstantiationSet::Iterator last = instantiations.Last();
for (InstantiationSet::Iterator inst = instantiations.First(); inst != last; ++inst) {
if (hasLeftAssignment) {
// the left is bound
Value leftValue;
inst->mAssignments.GetAssignmentFor(mLeftVariable, &leftValue);
rv = inst->AddAssignment(mRightVariable, leftValue);
}
else {
// the right is bound
Value rightValue;
inst->mAssignments.GetAssignmentFor(mRightVariable, &rightValue);
rv = inst->AddAssignment(mLeftVariable, rightValue);
}
if (NS_FAILED(rv)) return rv;
}
}
if (! instantiations.Empty()) {
// propagate consistency checking back up the tree
rv = test->Constrain(instantiations, aClosure);
if (NS_FAILED(rv)) return rv;
ReteNodeSet::Iterator last = mKids.Last();
for (ReteNodeSet::Iterator kid = mKids.First(); kid != last; ++kid)
kid->Propagate(instantiations, aClosure);
}
return NS_OK;
}
nsresult
JoinNode::GetNumBound(InnerNode* aAncestor, const InstantiationSet& aInstantiations, PRInt32* aBoundCount)
{
// Compute the number of variables for an ancestor that are bound
// in the current instantiation set.
nsresult rv;
VariableSet vars;
rv = aAncestor->GetAncestorVariables(vars);
if (NS_FAILED(rv)) return rv;
PRInt32 count = 0;
for (PRInt32 i = vars.GetCount() - 1; i >= 0; --i) {
if (aInstantiations.HasAssignmentFor(vars.GetVariableAt(i)))
++count;
}
*aBoundCount = count;
return NS_OK;
}
nsresult
JoinNode::Bind(InstantiationSet& aInstantiations, PRBool* aDidBind)
{
// Try to use the instantiation set to bind the unbound join
// variable. If successful, aDidBind <= PR_TRUE.
nsresult rv;
PRBool hasLeftAssignment = aInstantiations.HasAssignmentFor(mLeftVariable);
PRBool hasRightAssignment = aInstantiations.HasAssignmentFor(mRightVariable);
NS_ASSERTION(! (hasLeftAssignment && hasRightAssignment), "there is more than one assignment specified");
if (hasLeftAssignment && hasRightAssignment)
return NS_ERROR_UNEXPECTED;
if (hasLeftAssignment || hasRightAssignment) {
InstantiationSet::Iterator last = aInstantiations.Last();
for (InstantiationSet::Iterator inst = aInstantiations.First(); inst != last; ++inst) {
if (hasLeftAssignment) {
// the left is bound
Value leftValue;
inst->mAssignments.GetAssignmentFor(mLeftVariable, &leftValue);
rv = inst->AddAssignment(mRightVariable, leftValue);
}
else {
// the right is bound
Value rightValue;
inst->mAssignments.GetAssignmentFor(mRightVariable, &rightValue);
rv = inst->AddAssignment(mLeftVariable, rightValue);
}
if (NS_FAILED(rv)) return rv;
}
*aDidBind = PR_TRUE;
}
else {
*aDidBind = PR_FALSE;
}
return NS_OK;
}
nsresult
JoinNode::Constrain(InstantiationSet& aInstantiations, void* aClosure)
{
if (aInstantiations.Empty())
return NS_OK;
nsresult rv;
PRBool didBind;
rv = Bind(aInstantiations, &didBind);
if (NS_FAILED(rv)) return rv;
PRInt32 numLeftBound;
rv = GetNumBound(mLeftParent, aInstantiations, &numLeftBound);
if (NS_FAILED(rv)) return rv;
PRInt32 numRightBound;
rv = GetNumBound(mRightParent, aInstantiations, &numRightBound);
if (NS_FAILED(rv)) return rv;
InnerNode *first, *last;
if (numLeftBound > numRightBound) {
first = mLeftParent;
last = mRightParent;
}
else {
first = mRightParent;
last = mLeftParent;
}
rv = first->Constrain(aInstantiations, aClosure);
if (NS_FAILED(rv)) return rv;
if (! didBind) {
rv = Bind(aInstantiations, &didBind);
if (NS_FAILED(rv)) return rv;
NS_ASSERTION(didBind, "uh oh, still no assignment");
}
rv = last->Constrain(aInstantiations, aClosure);
if (NS_FAILED(rv)) return rv;
if (! didBind) {
// sort throught the full cross product
NS_NOTYETIMPLEMENTED("write me");
}
return NS_OK;
}
nsresult
JoinNode::GetAncestorVariables(VariableSet& aVariables) const
{
nsresult rv;
rv = mLeftParent->GetAncestorVariables(aVariables);
if (NS_FAILED(rv)) return rv;
rv = mRightParent->GetAncestorVariables(aVariables);
if (NS_FAILED(rv)) return rv;
if (mLeftVariable) {
rv = aVariables.Add(mLeftVariable);
if (NS_FAILED(rv)) return rv;
}
if (mRightVariable) {
rv = aVariables.Add(mRightVariable);
if (NS_FAILED(rv)) return rv;
}
return NS_OK;
}
PRBool
JoinNode::HasAncestor(const ReteNode* aNode) const
{
if (aNode == this) {
return PR_TRUE;
}
else if (mLeftParent->HasAncestor(aNode) || mRightParent->HasAncestor(aNode)) {
return PR_TRUE;
}
else {
return PR_FALSE;
}
}
//----------------------------------------------------------------------
//
// TestNode
@ -1026,31 +323,55 @@ JoinNode::HasAncestor(const ReteNode* aNode) const
//
TestNode::TestNode(InnerNode* aParent)
TestNode::TestNode(TestNode* aParent)
: mParent(aParent)
{
}
nsresult
TestNode::Propagate(const InstantiationSet& aInstantiations, void* aClosure)
TestNode::Propagate(InstantiationSet& aInstantiations,
PRBool aIsUpdate, PRBool& aTakenInstantiations)
{
nsresult rv;
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("TestNode[%p]: Propagate() begin", this));
InstantiationSet instantiations = aInstantiations;
rv = FilterInstantiations(instantiations, aClosure);
if (NS_FAILED(rv)) return rv;
aTakenInstantiations = PR_FALSE;
if (! instantiations.Empty()) {
nsresult rv = FilterInstantiations(aInstantiations);
if (NS_FAILED(rv))
return rv;
// if there is more than one child, each will need to be supplied with the
// original set of instantiations from this node, so create a copy in this
// case. If there is only one child, optimize and just pass the
// instantiations along to the child without copying
PRBool shouldCopy = (mKids.Count() > 1);
// See the header file for details about how instantiation ownership works.
if (! aInstantiations.Empty()) {
ReteNodeSet::Iterator last = mKids.Last();
for (ReteNodeSet::Iterator kid = mKids.First(); kid != last; ++kid) {
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("TestNode[%p]: Propagate() passing to child %p", this, kid.operator->()));
kid->Propagate(instantiations, aClosure);
// create a copy of the instantiations
if (shouldCopy) {
PRBool owned = PR_FALSE;
InstantiationSet* instantiations =
new InstantiationSet(aInstantiations);
if (!instantiations)
return NS_ERROR_OUT_OF_MEMORY;
rv = kid->Propagate(*instantiations, aIsUpdate, owned);
if (!owned)
delete instantiations;
if (NS_FAILED(rv))
return rv;
}
else {
rv = kid->Propagate(aInstantiations, aIsUpdate, aTakenInstantiations);
if (NS_FAILED(rv))
return rv;
}
}
}
@ -1062,24 +383,24 @@ TestNode::Propagate(const InstantiationSet& aInstantiations, void* aClosure)
nsresult
TestNode::Constrain(InstantiationSet& aInstantiations, void* aClosure)
TestNode::Constrain(InstantiationSet& aInstantiations)
{
nsresult rv;
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("TestNode[%p]: Constrain() begin", this));
rv = FilterInstantiations(aInstantiations, aClosure);
rv = FilterInstantiations(aInstantiations);
if (NS_FAILED(rv)) return rv;
if (! aInstantiations.Empty()) {
if (mParent && ! aInstantiations.Empty()) {
// if we still have instantiations, then ride 'em on up to the
// parent to narrow them.
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
("TestNode[%p]: Constrain() passing to parent %p", this, mParent));
rv = mParent->Constrain(aInstantiations, aClosure);
rv = mParent->Constrain(aInstantiations);
}
else {
PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
@ -1095,21 +416,14 @@ TestNode::Constrain(InstantiationSet& aInstantiations, void* aClosure)
}
nsresult
TestNode::GetAncestorVariables(VariableSet& aVariables) const
{
return mParent->GetAncestorVariables(aVariables);
}
PRBool
TestNode::HasAncestor(const ReteNode* aNode) const
{
return aNode == this ? PR_TRUE : mParent->HasAncestor(aNode);
return aNode == this || (mParent && mParent->HasAncestor(aNode));
}
//----------------------------------------------------------------------0
//----------------------------------------------------------------------
ReteNodeSet::ReteNodeSet()
: mNodes(nsnull), mCount(0), mCapacity(0)

Просмотреть файл

@ -21,6 +21,7 @@
*
* Contributor(s):
* Chris Waterson <waterson@netscape.com>
* Neil Deakin <enndeakin@sympatico.ca>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
@ -62,181 +63,33 @@
#define nsRuleNetwork_h__
#include "nsCOMPtr.h"
#include "nsCOMArray.h"
#include "nsIAtom.h"
#include "nsIContent.h"
#include "nsIDOMNode.h"
#include "plhash.h"
#include "pldhash.h"
#include "nsCRT.h"
#include "nsIRDFNode.h"
class nsIRDFResource;
class nsIRDFNode;
class nsXULTemplateResultSetRDF;
class nsXULTemplateQueryProcessorRDF;
//----------------------------------------------------------------------
/**
* A type-safe value that can be bound to a variable in the rule
* network.
*/
class Value {
public:
enum Type { eUndefined, eISupports, eString, eInteger };
protected:
Type mType;
union {
nsISupports* mISupports;
PRUnichar* mString;
PRInt32 mInteger;
};
PRBool Equals(const Value& aValue) const;
PRBool Equals(nsISupports* aISupports) const;
PRBool Equals(const PRUnichar* aString) const;
PRBool Equals(PRInt32 aInteger) const;
void Clear();
public:
Value() : mType(eUndefined) {
MOZ_COUNT_CTOR(Value); }
Value(const Value& aValue);
Value(nsISupports* aISupports);
Value(const PRUnichar* aString);
Value(PRInt32 aInteger);
Value& operator=(const Value& aValue);
Value& operator=(nsISupports* aISupports);
Value& operator=(const PRUnichar* aString);
Value& operator=(PRInt32 aInteger);
~Value();
PRBool operator==(const Value& aValue) const { return Equals(aValue); }
PRBool operator==(nsISupports* aISupports) const { return Equals(aISupports); }
PRBool operator==(const PRUnichar* aString) const { return Equals(aString); }
PRBool operator==(PRInt32 aInteger) const { return Equals(aInteger); }
PRBool operator!=(const Value& aValue) const { return !Equals(aValue); }
PRBool operator!=(nsISupports* aISupports) const { return !Equals(aISupports); }
PRBool operator!=(const PRUnichar* aString) const { return !Equals(aString); }
PRBool operator!=(PRInt32 aInteger) const { return !Equals(aInteger); }
/**
* Get the value's type
* @return the value's type
*/
Type GetType() const { return mType; }
/**
* Treat the Value as an nsISupports. (Note that the result
* is _not_ addref'd.)
* @return the value as an nsISupports, or null if the value is
* not an nsISupports.
*/
operator nsISupports*() const;
/**
* Treat the value as a Unicode string.
* @return the value as a Unicode string, or null if the value
* is not a Unicode string.
*/
operator const PRUnichar*() const;
/**
* Treat the value as an integer.
* @return the value as an integer, or zero if the value is
* not an integer
*/
operator PRInt32() const;
PLHashNumber Hash() const;
#ifdef DEBUG
void ToCString(nsACString& aResult);
#endif
};
#ifdef DEBUG
nsISupports*
value_to_isupports(const nsIID& aIID, const Value& aValue);
# define VALUE_TO_ISUPPORTS(type, v) \
NS_STATIC_CAST(type*, value_to_isupports(NS_GET_IID(type), (v)))
#else
# define VALUE_TO_ISUPPORTS(type, v) \
NS_STATIC_CAST(type*, NS_STATIC_CAST(nsISupports*, (v)))
#endif
// Convenience wrappers for |Value::operator nsISupports*()|. In a
// debug build, they expand to versions that will call QI() and verify
// that the types are kosher. In an optimized build, they'll just cast
// n' go. Rock on!
#define VALUE_TO_IRDFRESOURCE(v) VALUE_TO_ISUPPORTS(nsIRDFResource, (v))
#define VALUE_TO_IRDFNODE(v) VALUE_TO_ISUPPORTS(nsIRDFNode, (v))
#define VALUE_TO_ICONTENT(v) VALUE_TO_ISUPPORTS(nsIContent, (v))
//----------------------------------------------------------------------
/**
* A set of variables
*/
class VariableSet
{
public:
VariableSet();
~VariableSet();
/**
* Add a variable to the set
* @param aVariable the variable to add
* @returns NS_OK, unless something went wrong.
*/
nsresult Add(PRInt32 aVariable);
/**
* Remove a variable from the set
* @param aVariable the variable to remove
* @returns NS_OK, unless something went wrong.
*/
nsresult Remove(PRInt32 aVariable);
/**
* Determine if the set contains a variable
* @param aVariable the variable to test
* @return PR_TRUE if the set contains the variable, PR_FALSE otherwise.
*/
PRBool Contains(PRInt32 aVariable) const;
/**
* Determine the number of variables in the set
* @return the number of variables in the set
*/
PRInt32 GetCount() const { return mCount; }
/**
* Get the <i>i</i>th variable in the set
* @param aIndex the index to retrieve
* @return the <i>i</i>th variable in the set, or -1 if no such
* variable exists.
*/
PRInt32 GetVariableAt(PRInt32 aIndex) const { return mVariables[aIndex]; }
protected:
PRInt32* mVariables;
PRInt32 mCount;
PRInt32 mCapacity;
};
//----------------------------------------------------------------------
/**
* A memory element that supports an instantiation
* A memory element that supports an instantiation. A memory element holds a
* set of nodes involved in an RDF test such as <member> or <triple> test. A
* memory element is created when a specific test matches. The query processor
* maintains a map between the memory elements and the results they eventually
* matched. When an assertion is removed from the graph, this map is consulted
* to determine which results will no longer match.
*/
class MemoryElement {
public:
MemoryElement() {}
virtual ~MemoryElement() {}
MemoryElement() { MOZ_COUNT_CTOR(MemoryElement); }
virtual ~MemoryElement() { MOZ_COUNT_DTOR(MemoryElement); }
virtual const char* Type() const = 0;
virtual PLHashNumber Hash() const = 0;
@ -367,13 +220,13 @@ public:
*/
class nsAssignment {
public:
PRInt32 mVariable;
Value mValue;
nsCOMPtr<nsIAtom> mVariable;
nsCOMPtr<nsIRDFNode> mValue;
nsAssignment() : mVariable(-1), mValue()
nsAssignment() : mValue()
{ MOZ_COUNT_CTOR(nsAssignment); }
nsAssignment(PRInt32 aVariable, const Value& aValue)
nsAssignment(nsIAtom* aVariable, nsIRDFNode* aValue)
: mVariable(aVariable),
mValue(aValue)
{ MOZ_COUNT_CTOR(nsAssignment); }
@ -397,8 +250,9 @@ public:
return mVariable != aAssignment.mVariable || mValue != aAssignment.mValue; }
PLHashNumber Hash() const {
// XXX I have no idea if this hashing function is good or not
return (mValue.Hash() & 0xffff) | (mVariable << 16); }
// XXX I have no idea if this hashing function is good or not // XXX change this
PLHashNumber temp = PLHashNumber(NS_PTR_TO_INT32(mValue.get())) >> 2; // strip alignment bits
return (temp & 0xffff) | ((PRInt32)mVariable.get()); }
};
@ -524,7 +378,7 @@ public:
* @param aValue the value to query
* @return PR_TRUE if aVariable is bound to aValue; PR_FALSE otherwise.
*/
PRBool HasAssignment(PRInt32 aVariable, const Value& aValue) const;
PRBool HasAssignment(nsIAtom* aVariable, nsIRDFNode* aValue) const;
/**
* Determine if the assignment set contains the specified assignment
@ -541,7 +395,7 @@ public:
* @return PR_TRUE if the assignment set has an assignment for the variable,
* PR_FALSE otherwise.
*/
PRBool HasAssignmentFor(PRInt32 aVariable) const;
PRBool HasAssignmentFor(nsIAtom* aVariable) const;
/**
* Retrieve the assignment for the specified variable
@ -551,7 +405,7 @@ public:
* @return PR_TRUE if the variable has an assignment, PR_FALSE
* if there was no assignment for the variable.
*/
PRBool GetAssignmentFor(PRInt32 aVariable, Value* aValue) const;
PRBool GetAssignmentFor(nsIAtom* aVariable, nsIRDFNode** aValue) const;
/**
* Count the number of assignments in the set
@ -574,8 +428,14 @@ public:
//----------------------------------------------------------------------
/**
* A collection of varible-to-value bindings, with the memory elements
* that support those bindings.
* A collection of variable-to-value bindings, with the memory elements
* that support those bindings. Essentially, an instantiation is the
* collection of variables and values assigned to those variables for a single
* result. For each RDF rule in the rule network, each instantiation is
* examined and either extended with additional bindings specified by the RDF
* rule, or removed if the rule doesn't apply (for instance if a node has no
* children). When an instantiation gets to the last node of the rule network,
* which is always an nsInstantiationNode, a result is created for it.
*
* An instantiation object is typically created by "extending" another
* instantiation object. That is, using the copy constructor, and
@ -616,7 +476,7 @@ public:
* @return NS_OK if no errors, NS_ERROR_OUT_OF_MEMORY if there
* is not enough memory to perform the operation
*/
nsresult AddAssignment(PRInt32 aVariable, const Value& aValue) {
nsresult AddAssignment(nsIAtom* aVariable, nsIRDFNode* aValue) {
mAssignments.Add(nsAssignment(aVariable, aValue));
return NS_OK; }
@ -668,6 +528,8 @@ public:
class Iterator;
friend class Iterator;
friend class nsXULTemplateResultSetRDF; // so it can get to the List
protected:
class List {
public:
@ -782,8 +644,7 @@ public:
void Clear();
PRBool HasAssignmentFor(PRInt32 aVariable) const;
PRBool HasAssignmentFor(nsIAtom* aVariable) const;
};
//----------------------------------------------------------------------
@ -813,13 +674,18 @@ public:
* node must recursively call Propagate() on its children. We
* should fix this to make the algorithm interruptable.)
*
* See TestNode::Propagate for details about instantiation set ownership
*
* @param aInstantiations the set of instantiations to propagate
* down through the network.
* @param aClosure any application-specific information that
* needs to be passed through the network.
* @param aIsUpdate true if updating, false for first generation
* @param aTakenInstantiations true if the ownership over aInstantiations
* has been taken from the caller. If false,
* the caller owns it.
* @return NS_OK if no errors occurred.
*/
virtual nsresult Propagate(const InstantiationSet& aInstantiations, void* aClosure) = 0;
virtual nsresult Propagate(InstantiationSet& aInstantiations,
PRBool aIsUpdate, PRBool& aTakenInstantiations) = 0;
};
//----------------------------------------------------------------------
@ -907,6 +773,8 @@ public:
Iterator First() { return Iterator(mNodes); }
Iterator Last() { return Iterator(mNodes + mCount); }
PRInt32 Count() const { return mCount; }
protected:
ReteNode** mNodes;
PRInt32 mCount;
@ -916,12 +784,63 @@ protected:
//----------------------------------------------------------------------
/**
* An abstract base class for an "inner node" in the rule
* network. Adds support for children and "upward" queries.
* A node that applies a test condition to a set of instantiations.
*
* This class provides implementations of Propagate() and Constrain()
* in terms of one simple operation, FilterInstantiations(). A node
* that is a "simple test node" in a rule network should derive from
* this class, and need only implement FilterInstantiations().
*/
class InnerNode : public ReteNode
class TestNode : public ReteNode
{
public:
TestNode(TestNode* aParent);
/**
* Retrieve the test node's parent
* @return the test node's parent
*/
TestNode* GetParent() const { return mParent; }
/**
* Calls FilterInstantiations() on the instantiation set, and if
* the resulting set isn't empty, propagates the new set down to
* each of the test node's children.
*
* Note that the caller of Propagate is responsible for deleting
* aInstantiations if necessary as described below.
*
* Propagate may be called in update or non-update mode as indicated
* by the aIsUpdate argument. Non-update mode is used when initially
* generating results, whereas update mode is used when the datasource
* changes and new results might be available.
*
* The last node in a chain of TestNodes is always an nsInstantiationNode.
* In non-update mode, this nsInstantiationNode will cache the results
* in the query using the SetCachedResults method. The query processor
* takes these cached results and creates a nsXULTemplateResultSetRDF
* which is the enumeration returned to the template builder. This
* nsXULTemplateResultSetRDF owns the instantiations and they will be
* deleted when the nsXULTemplateResultSetRDF goes away.
*
* In update mode, the nsInstantiationNode node will iterate over the
* instantiations itself and callback to the builder to update any matches
* and generated content. If no instantiations match, then the builder
* will never be called.
*
* Thus, the difference between update and non-update modes is that in
* update mode, the results and instantiations have been already handled
* whereas in non-update mode they are expected to be returned in an
* nsXULTemplateResultSetRDF for further processing by the builder.
*
* Regardless, aTakenInstantiations will be set to true if the
* ownership over aInstantiations has been transferred to a result set.
* If set to false, the caller is still responsible for aInstantiations.
* aTakenInstantiations will be set properly even if an error occurs.
*/
virtual nsresult Propagate(InstantiationSet& aInstantiations,
PRBool aIsUpdate, PRBool& aTakenInstantiations);
/**
* This is called by a child node on its parent to allow the
* parent's constraints to apply to the set of instantiations.
@ -938,24 +857,22 @@ public:
*
* @param aInstantiations the set of instantiations that must
* be constrained
* @param aClosure application-specific information that needs to
* be passed through the network.
* @return NS_OK if no errors occurred
*/
virtual nsresult Constrain(InstantiationSet& aInstantiations, void* aClosure) = 0;
virtual nsresult Constrain(InstantiationSet& aInstantiations);
/**
* Retrieve the set of variables that are introduced by this node
* and any of its ancestors. To correctly implement this method, a
* node must add any variables that it introduces to the variable
* set, and then recursively call GetAncestorVariables() on its
* parent (or parents).
* Given a set of instantiations, filter out any that are
* inconsistent with the test node's test, and append
* variable-to-value assignments and memory element support for
* those which do pass the test node's test.
*
* @param aVariables The variable set to which the callee will add
* its variables, and its ancestors variables.
* @return NS_OK if no errors occur.
* @param aInstantiations the set of instantiations to be
* filtered
* @return NS_OK if no errors occurred.
*/
virtual nsresult GetAncestorVariables(VariableSet& aVariables) const = 0;
virtual nsresult FilterInstantiations(InstantiationSet& aInstantiations) const = 0;
//XXX probably better named "ApplyConstraints" or "Discrminiate" or something
/**
* Determine if this node has another node as its direct ancestor.
@ -963,7 +880,7 @@ public:
* @return PR_TRUE if aNode is a direct ancestor of this node, PR_FALSE
* otherwise.
*/
virtual PRBool HasAncestor(const ReteNode* aNode) const = 0;
PRBool HasAncestor(const ReteNode* aNode) const;
/**
* Add another node as a child of this node.
@ -979,236 +896,8 @@ public:
nsresult RemoveAllChildren() { return mKids.Clear(); }
protected:
TestNode* mParent;
ReteNodeSet mKids;
};
//----------------------------------------------------------------------
/**
* The root node in the rule network.
*/
class RootNode : public InnerNode
{
public:
// "downward" propagations
virtual nsresult Propagate(const InstantiationSet& aInstantiations, void* aClosure);
// "upward" propagations
virtual nsresult Constrain(InstantiationSet& aInstantiations, void* aClosure);
virtual nsresult GetAncestorVariables(VariableSet& aVariables) const;
virtual PRBool HasAncestor(const ReteNode* aNode) const;
};
//----------------------------------------------------------------------
/**
* A node that joins to paths from the root node, and binds a
* variable from the left ancestor to a variable in the right
* ancestor.
*/
class JoinNode : public InnerNode
{
public:
enum Operator { eEquality };
JoinNode(InnerNode* aLeftParent,
PRInt32 aLeftVariable,
InnerNode* aRightParent,
PRInt32 aRightVariable,
Operator aOperator);
// "downward" propagations
virtual nsresult Propagate(const InstantiationSet& aInstantiations, void* aClosure);
// "upward" propagations
virtual nsresult Constrain(InstantiationSet& aInstantiations, void* aClosure);
virtual nsresult GetAncestorVariables(VariableSet& aVariables) const;
virtual PRBool HasAncestor(const ReteNode* aNode) const;
protected:
InnerNode* mLeftParent;
PRInt32 mLeftVariable;
InnerNode* mRightParent;
PRInt32 mRightVariable;
Operator mOperator;
static nsresult GetNumBound(InnerNode* aAncestor, const InstantiationSet& aInstantiations, PRInt32* aBoundCount);
nsresult Bind(InstantiationSet& aInstantiations, PRBool* aDidBind);
};
//----------------------------------------------------------------------
/**
* A node that applies a test condition to a set of instantiations.
*
* This class provides implementations of Propagate() and Constrain()
* in terms of one simple operation, FilterInstantiations(). A node
* that is a "simple test node" in a rule network should derive from
* this class, and need only implement FilterInstantiations() and
* GetAncestorVariables().
*/
class TestNode : public InnerNode
{
public:
TestNode(InnerNode* aParent);
/**
* Retrieve the test node's parent
* @return the test node's parent
*/
InnerNode* GetParent() const { return mParent; }
/**
* Calls FilterInstantiations() on the instantiation set, and if
* the resulting set isn't empty, propagates the new set down to
* each of the test node's children.
*/
virtual nsresult Propagate(const InstantiationSet& aInstantiations, void* aClosure);
/**
* Calls FilterInstantiations() on the instantiation set, and if
* the resulting set isn't empty, propagates the new set up to the
* test node's parent.
*/
virtual nsresult Constrain(InstantiationSet& aInstantiations, void* aClosure);
/**
* Given a set of instantiations, filter out any that are
* inconsistent with the test node's test, and append
* variable-to-value assignments and memory element support for
* those which do pass the test node's test.
*
* @param aInstantiations the set of instantiations to be
* filtered
* @param aClosure application-specific data that is to be passed
* through the network.
* @return NS_OK if no errors occurred.
*/
virtual nsresult FilterInstantiations(InstantiationSet& aInstantiations, void* aClosure) const = 0; //XXX probably better named "ApplyConstraints" or "Discrminiate" or something
virtual nsresult GetAncestorVariables(VariableSet& aVariables) const;
virtual PRBool HasAncestor(const ReteNode* aNode) const;
protected:
InnerNode* mParent;
};
//----------------------------------------------------------------------
class nsRuleNetwork
{
public:
struct SymtabEntry {
PLDHashEntryHdr mHdr;
PRUnichar* mSymbol;
PRInt32 mVariable;
};
nsRuleNetwork() { Init(); }
~nsRuleNetwork() { Finish(); }
/**
* Remove all the nodes from the network. The nodes will be
* destroyed
* @return NS_OK if no errors occur
*/
void Clear() { Finish(); Init(); }
/**
* Add a node to the network. The network assumes ownership of the
* node; it will be destroyed when the network is destroyed, or if
* Clear() is called.
*
* @param aNode the node to add to the network
* @return NS_OK if no errors occur
*/
nsresult AddNode(ReteNode* aNode) { return mNodes.Add(aNode); }
/**
* Retrieve the root node in the rule network
* @return the root node in the rule network
*/
RootNode* GetRoot() { return &mRoot; };
/**
* Create an unnamed variable
*/
PRInt32 CreateAnonymousVariable() { return ++mNextVariable; }
/**
* Assign a symbol to a variable
*/
void PutSymbol(const PRUnichar* aSymbol, PRInt32 aVariable) {
if (!mSymtab.ops) return;
NS_PRECONDITION(LookupSymbol(aSymbol) == 0, "symbol already defined");
SymtabEntry* entry =
NS_REINTERPRET_CAST(SymtabEntry*,
PL_DHashTableOperate(&mSymtab,
aSymbol,
PL_DHASH_ADD));
if (entry) {
entry->mSymbol = nsCRT::strdup(aSymbol);
entry->mVariable = aVariable;
} };
/**
* Lookup the variable associated with the symbol
*/
PRInt32 LookupSymbol(const PRUnichar* aSymbol, PRBool aCreate = PR_FALSE) {
if (!mSymtab.ops) return 0;
SymtabEntry* entry =
NS_REINTERPRET_CAST(SymtabEntry*,
PL_DHashTableOperate(&mSymtab,
aSymbol,
PL_DHASH_LOOKUP));
if (PL_DHASH_ENTRY_IS_BUSY(&entry->mHdr))
return entry->mVariable;
PRInt32 result = 0;
if (aCreate) {
result = CreateAnonymousVariable();
PutSymbol(aSymbol, result);
}
return result; }
protected:
/**
* The root node in the network
*/
RootNode mRoot;
/**
* Other nodes in the network
*/
ReteNodeSet mNodes;
void Init();
void Finish();
/**
* Symbol table, mapping symbolic names to variable identifiers
*/
PLDHashTable mSymtab;
/**
* The next available variable identifier
*/
PRInt32 mNextVariable;
static PLDHashTableOps gOps;
};
#endif // nsRuleNetwork_h__

Просмотреть файл

@ -44,14 +44,19 @@ nsTemplateMatch::nsTemplateMatch(const nsTemplateMatch& aMatch) {}
void nsTemplateMatch::operator=(const nsTemplateMatch& aMatch) {}
#endif
PRBool
nsTemplateMatch::GetAssignmentFor(nsConflictSet& aConflictSet, PRInt32 aVariable, Value* aValue)
nsresult
nsTemplateMatch::RuleMatched(nsTemplateQuerySet* aQuerySet,
nsTemplateRule* aRule,
nsIXULTemplateResult* aResult)
{
if (mAssignments.GetAssignmentFor(aVariable, aValue)) {
return PR_TRUE;
}
else {
return mRule->ComputeAssignmentFor(aConflictSet, this, aVariable, aValue);
}
}
mRule = aRule;
nsCOMPtr<nsIDOMNode> rulenode;
aRule->GetRuleNode(getter_AddRefs(rulenode));
if (rulenode) {
nsCOMPtr<nsIDOMNode> querynode = do_QueryInterface(aQuerySet->mQueryNode);
return aResult->RuleMatched(querynode, rulenode);
}
return NS_OK;
}

Просмотреть файл

@ -40,22 +40,28 @@
#define nsTemplateMatch_h__
#include "nsFixedSizeAllocator.h"
#include "nsResourceSet.h"
#include "nsIContent.h"
#include "nsIXULTemplateQueryProcessor.h"
#include "nsIXULTemplateResult.h"
#include "nsRuleNetwork.h"
#include NEW_H
/**
* A "match" is a fully instantiated rule; that is, a complete and
* consistent set of variable-to-value assignments for all the rule's
* condition elements.
* A match object, where each match object is associated with one result.
* There will be one match list for each unique id generated. However, since
* there are multiple querysets and each may generate results with the same
* id, they are all chained together in a linked list, ordered in the same
* order as the respective <queryset> elements they were generated from.
* Only one match is active at a time, but which match is active may change
* as new results are added or removed. When a match is active, content is
* generated for that match.
*
* Each match also contains information about the "optional"
* variable-to-value assignments that can be specified using the
* <bindings> element in a rule.
* Matches are stored and owned by the mMatchToMap hash in the template
* builder.
*/
class nsConflictSet;
class nsTemplateRule;
class nsTemplateQuerySet;
class nsTemplateMatch {
private:
@ -64,25 +70,22 @@ private:
void* operator new(size_t) CPP_THROW_NEW { return 0; }
void operator delete(void*, size_t) {}
protected:
PRInt32 mRefCnt;
nsTemplateMatch(const nsTemplateRule* aRule,
const Instantiation& aInstantiation,
const nsAssignmentSet& aAssignments)
: mRefCnt(0),
mRule(aRule),
mInstantiation(aInstantiation),
mAssignments(aAssignments) {}
public:
nsTemplateMatch(nsTemplateQuerySet* aQuerySet,
nsIXULTemplateResult* aResult)
: mRule(nsnull),
mQuerySet(aQuerySet),
mResult(aResult),
mNext(nsnull) {}
~nsTemplateMatch() {}
static nsTemplateMatch*
Create(nsFixedSizeAllocator& aPool,
const nsTemplateRule* aRule,
const Instantiation& aInstantiation,
const nsAssignmentSet& aAssignments) {
nsTemplateQuerySet* aQuerySet,
nsIXULTemplateResult* aResult) {
void* place = aPool.Alloc(sizeof(nsTemplateMatch));
return place ? ::new (place) nsTemplateMatch(aRule, aInstantiation, aAssignments) : nsnull; }
return place ? ::new (place) nsTemplateMatch(aQuerySet, aResult) : nsnull; }
static void
Destroy(nsFixedSizeAllocator& aPool, nsTemplateMatch* aMatch) {
@ -90,65 +93,44 @@ public:
aPool.Free(aMatch, sizeof(*aMatch)); }
PRBool operator==(const nsTemplateMatch& aMatch) const {
return mRule == aMatch.mRule && mInstantiation == aMatch.mInstantiation; }
return mRule == aMatch.mRule && mResult == aMatch.mResult; }
PRBool operator!=(const nsTemplateMatch& aMatch) const {
return !(*this == aMatch); }
/**
* Get the assignment for the specified variable, computing the
* value using the rule's bindings, if necessary.
* @param aConflictSet
* @param aVariable the variable for which to determine the assignment
* @param aValue an out parameter that receives the value assigned to
* aVariable.
* @return PR_TRUE if aVariable has an assignment, PR_FALSE otherwise.
*/
PRBool GetAssignmentFor(nsConflictSet& aConflictSet, PRInt32 aVariable, Value* aValue);
nsresult RuleMatched(nsTemplateQuerySet* aQuerySet,
nsTemplateRule* aRule,
nsIXULTemplateResult* aResult);
/**
* The rule that this match applies for.
* The rule that this match applies to. If the rule is null, the result
* has not matched a rule and no content has been generated. However, a
* later queryset may still have matched.
*/
const nsTemplateRule* mRule;
nsTemplateRule* mRule; // not owned
/**
* The fully bound instantiation (variable-to-value assignments, with
* memory element support) that match the rule's conditions.
* The queryset for this rule
*/
Instantiation mInstantiation;
nsTemplateQuerySet* mQuerySet; // not owned
/**
* Any additional assignments that apply because of the rule's
* bindings. These are computed lazily.
* The result associated with this match
*/
nsAssignmentSet mAssignments;
nsCOMPtr<nsIXULTemplateResult> mResult;
/**
* The set of resources that the nsTemplateMatch's bindings depend on. Should the
* assertions relating to these resources change, then the rule will
* still match (i.e., this match object is still "good"); however, we
* may need to recompute the assignments that have been made using the
* rule's bindings.
* Matches are stored in a linked list, in priority order. This first
* match that has a rule set (mRule) is the active match and generates
* content. The next match is owned by the builder, which will delete
* template matches when needed.
*/
nsResourceSet mBindingDependencies;
PRInt32 AddRef() {
++mRefCnt;
NS_LOG_ADDREF(this, mRefCnt, "nsTemplateMatch", sizeof(*this));
return mRefCnt; }
PRInt32 Release(nsFixedSizeAllocator& aPool) {
NS_PRECONDITION(mRefCnt > 0, "bad refcnt");
PRInt32 refcnt = --mRefCnt;
NS_LOG_RELEASE(this, mRefCnt, "nsTemplateMatch");
if (refcnt == 0)
Destroy(aPool, this);
return refcnt; }
nsTemplateMatch *mNext;
private:
nsTemplateMatch(const nsTemplateMatch& aMatch); // not to be implemented
void operator=(const nsTemplateMatch& aMatch); // not to be implemented
};
#endif // nsConflictSet_h__
#endif // nsTemplateMatch_h__

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -38,9 +38,264 @@
#include "nsTemplateRule.h"
#include "nsTemplateMatch.h"
#include "nsRuleNetwork.h"
#include "nsConflictSet.h"
#include "nsVoidArray.h"
#include "nsXULContentUtils.h"
#include "nsUnicharUtils.h"
#include "nsReadableUtils.h"
#include "nsICollation.h"
nsTemplateCondition::nsTemplateCondition(nsIAtom* aSourceVariable,
const nsAString& aRelation,
nsIAtom* aTargetVariable,
PRBool aIgnoreCase,
PRBool aNegate)
: mSourceVariable(aSourceVariable),
mTargetVariable(aTargetVariable),
mIgnoreCase(aIgnoreCase),
mNegate(aNegate),
mNext(nsnull)
{
SetRelation(aRelation);
MOZ_COUNT_CTOR(nsTemplateCondition);
}
nsTemplateCondition::nsTemplateCondition(nsIAtom* aSourceVariable,
const nsAString& aRelation,
const nsAString& aTargets,
PRBool aIgnoreCase,
PRBool aNegate,
PRBool aIsMultiple)
: mSourceVariable(aSourceVariable),
mIgnoreCase(aIgnoreCase),
mNegate(aNegate),
mNext(nsnull)
{
SetRelation(aRelation);
if (aIsMultiple) {
PRInt32 start = 0, end = 0;
while ((end = aTargets.FindChar(',',start)) >= 0) {
if (end > start) {
mTargetList.AppendString(Substring(aTargets, start, end - start));
}
start = end + 1;
}
if (start < (PRInt32)aTargets.Length()) {
mTargetList.AppendString(Substring(aTargets, start));
}
}
else {
mTargetList.AppendString(aTargets);
}
MOZ_COUNT_CTOR(nsTemplateCondition);
}
nsTemplateCondition::nsTemplateCondition(const nsAString& aSource,
const nsAString& aRelation,
nsIAtom* aTargetVariable,
PRBool aIgnoreCase,
PRBool aNegate)
: mSource(aSource),
mTargetVariable(aTargetVariable),
mIgnoreCase(aIgnoreCase),
mNegate(aNegate),
mNext(nsnull)
{
SetRelation(aRelation);
MOZ_COUNT_CTOR(nsTemplateCondition);
}
void
nsTemplateCondition::SetRelation(const nsAString& aRelation)
{
if (aRelation.EqualsLiteral("equals") || aRelation.IsEmpty())
mRelation = eEquals;
else if (aRelation.EqualsLiteral("less"))
mRelation = eLess;
else if (aRelation.EqualsLiteral("greater"))
mRelation = eGreater;
else if (aRelation.EqualsLiteral("before"))
mRelation = eBefore;
else if (aRelation.EqualsLiteral("after"))
mRelation = eAfter;
else if (aRelation.EqualsLiteral("startswith"))
mRelation = eStartswith;
else if (aRelation.EqualsLiteral("endswith"))
mRelation = eEndswith;
else if (aRelation.EqualsLiteral("contains"))
mRelation = eContains;
else
mRelation = eUnknown;
}
PRBool
nsTemplateCondition::CheckMatch(nsIXULTemplateResult* aResult)
{
PRBool match = PR_FALSE;
nsAutoString leftString;
if (mSourceVariable)
aResult->GetBindingFor(mSourceVariable, leftString);
else
leftString.Assign(mSource);
if (mTargetVariable) {
nsAutoString rightString;
aResult->GetBindingFor(mTargetVariable, rightString);
match = CheckMatchStrings(leftString, rightString);
}
else {
// iterate over the strings in the target and determine
// whether there is a match.
PRInt32 length = mTargetList.Count();
for (PRInt32 t = 0; t < length; t++) {
match = CheckMatchStrings(leftString, *mTargetList[t]);
// stop once a match is found. In negate mode, stop once a
// target does not match.
if (match != mNegate) break;
}
}
return match;
}
PRBool
nsTemplateCondition::CheckMatchStrings(const nsAString& aLeftString,
const nsAString& aRightString)
{
PRBool match = PR_FALSE;
if (aRightString.IsEmpty()) {
if ((mRelation == eEquals) && aLeftString.IsEmpty())
match = PR_TRUE;
}
else {
switch (mRelation) {
case eEquals:
if (mIgnoreCase)
match = aLeftString.Equals(aRightString,
nsCaseInsensitiveStringComparator());
else
match = aLeftString.Equals(aRightString);
break;
case eLess:
case eGreater:
{
// non-numbers always compare false
PRInt32 err;
PRInt32 leftint = PromiseFlatString(aLeftString).ToInteger(&err);
if (NS_SUCCEEDED(err)) {
PRInt32 rightint = PromiseFlatString(aRightString).ToInteger(&err);
if (NS_SUCCEEDED(err)) {
match = (mRelation == eLess) ? (leftint < rightint) :
(leftint > rightint);
}
}
break;
}
case eBefore:
{
nsICollation* collation = nsXULContentUtils::GetCollation();
if (collation) {
PRInt32 sortOrder;
collation->CompareString((mIgnoreCase ?
nsICollation::kCollationCaseInSensitive :
nsICollation::kCollationCaseSensitive),
aLeftString,
aRightString,
&sortOrder);
match = (sortOrder < 0);
}
else if (mIgnoreCase) {
match = (Compare(aLeftString, aRightString,
nsCaseInsensitiveStringComparator()) < 0);
}
else {
match = (Compare(aLeftString, aRightString) < 0);
}
break;
}
case eAfter:
{
nsICollation* collation = nsXULContentUtils::GetCollation();
if (collation) {
PRInt32 sortOrder;
collation->CompareString((mIgnoreCase ?
nsICollation::kCollationCaseInSensitive :
nsICollation::kCollationCaseSensitive),
aLeftString,
aRightString,
&sortOrder);
match = (sortOrder > 0);
}
else if (mIgnoreCase) {
match = (Compare(aLeftString, aRightString,
nsCaseInsensitiveStringComparator()) > 0);
}
else {
match = (Compare(aLeftString, aRightString) > 0);
}
break;
}
case eStartswith:
if (mIgnoreCase)
match = (StringBeginsWith(aLeftString, aRightString,
nsCaseInsensitiveStringComparator()));
else
match = (StringBeginsWith(aLeftString, aRightString));
break;
case eEndswith:
if (mIgnoreCase)
match = (StringEndsWith(aLeftString, aRightString,
nsCaseInsensitiveStringComparator()));
else
match = (StringEndsWith(aLeftString, aRightString));
break;
case eContains:
{
nsAString::const_iterator start, end;
aLeftString.BeginReading(start);
aLeftString.EndReading(end);
if (mIgnoreCase)
match = CaseInsensitiveFindInReadable(aRightString, start, end);
else
match = FindInReadable(aRightString, start, end);
break;
}
default:
break;
}
}
if (mNegate) match = !match;
return match;
}
nsTemplateRule::nsTemplateRule(nsIContent* aRuleNode,
nsIContent* aAction,
nsTemplateQuerySet* aQuerySet)
: mQuerySet(aQuerySet),
mAction(aAction),
mBindings(nsnull),
mConditions(nsnull)
{
MOZ_COUNT_CTOR(nsTemplateRule);
mRuleNode = do_QueryInterface(aRuleNode);
}
nsTemplateRule::~nsTemplateRule()
{
@ -51,25 +306,72 @@ nsTemplateRule::~nsTemplateRule()
mBindings = mBindings->mNext;
delete doomed;
}
while (mConditions) {
nsTemplateCondition* cdel = mConditions;
mConditions = mConditions->GetNext();
delete cdel;
}
}
nsresult
nsTemplateRule::GetContent(nsIContent** aResult) const
nsTemplateRule::GetAction(nsIContent** aAction) const
{
*aResult = mContent.get();
NS_IF_ADDREF(*aResult);
*aAction = mAction;
NS_IF_ADDREF(*aAction);
return NS_OK;
}
nsresult
nsTemplateRule::GetRuleNode(nsIDOMNode** aRuleNode) const
{
*aRuleNode = mRuleNode;
NS_IF_ADDREF(*aRuleNode);
return NS_OK;
}
void nsTemplateRule::SetCondition(nsTemplateCondition* aCondition)
{
while (mConditions) {
nsTemplateCondition* cdel = mConditions;
mConditions = mConditions->GetNext();
delete cdel;
}
mConditions = aCondition;
}
PRBool
nsTemplateRule::HasBinding(PRInt32 aSourceVariable,
nsIRDFResource* aProperty,
PRInt32 aTargetVariable) const
nsTemplateRule::CheckMatch(nsIXULTemplateResult* aResult) const
{
// check the conditions in the rule first
nsTemplateCondition* condition = mConditions;
while (condition) {
if (!condition->CheckMatch(aResult))
return PR_FALSE;
condition = condition->GetNext();
}
if (mRuleFilter) {
// if a rule filter was set, check it for a match. If an error occurs,
// assume that the match was acceptable
PRBool match;
nsresult rv = mRuleFilter->Match(aResult, mRuleNode, &match);
return NS_FAILED(rv) || match;
}
return PR_TRUE;
}
PRBool
nsTemplateRule::HasBinding(nsIAtom* aSourceVariable,
nsAString& aExpr,
nsIAtom* aTargetVariable) const
{
for (Binding* binding = mBindings; binding != nsnull; binding = binding->mNext) {
if ((binding->mSourceVariable == aSourceVariable) &&
(binding->mProperty == aProperty) &&
(binding->mExpr.Equals(aExpr)) &&
(binding->mTargetVariable == aTargetVariable))
return PR_TRUE;
}
@ -78,23 +380,19 @@ nsTemplateRule::HasBinding(PRInt32 aSourceVariable,
}
nsresult
nsTemplateRule::AddBinding(PRInt32 aSourceVariable,
nsIRDFResource* aProperty,
PRInt32 aTargetVariable)
nsTemplateRule::AddBinding(nsIAtom* aSourceVariable,
nsAString& aExpr,
nsIAtom* aTargetVariable)
{
NS_PRECONDITION(aSourceVariable != 0, "no source variable!");
if (! aSourceVariable)
return NS_ERROR_INVALID_ARG;
NS_PRECONDITION(aProperty != nsnull, "null ptr");
if (! aProperty)
return NS_ERROR_INVALID_ARG;
NS_PRECONDITION(aTargetVariable != 0, "no target variable!");
if (! aTargetVariable)
return NS_ERROR_INVALID_ARG;
NS_ASSERTION(! HasBinding(aSourceVariable, aProperty, aTargetVariable),
NS_ASSERTION(! HasBinding(aSourceVariable, aExpr, aTargetVariable),
"binding added twice");
Binding* newbinding = new Binding;
@ -102,10 +400,11 @@ nsTemplateRule::AddBinding(PRInt32 aSourceVariable,
return NS_ERROR_OUT_OF_MEMORY;
newbinding->mSourceVariable = aSourceVariable;
newbinding->mProperty = aProperty;
newbinding->mTargetVariable = aTargetVariable;
newbinding->mParent = nsnull;
newbinding->mExpr.Assign(aExpr);
Binding* binding = mBindings;
Binding** link = &mBindings;
@ -137,215 +436,18 @@ nsTemplateRule::AddBinding(PRInt32 aSourceVariable,
return NS_OK;
}
PRBool
nsTemplateRule::DependsOn(PRInt32 aChildVariable, PRInt32 aParentVariable) const
{
// Determine whether the value for aChildVariable will depend on
// the value for aParentVariable by examining the rule's bindings.
Binding* child = mBindings;
while ((child != nsnull) && (child->mSourceVariable != aChildVariable))
child = child->mNext;
if (! child)
return PR_FALSE;
Binding* parent = child->mParent;
while (parent != nsnull) {
if (parent->mSourceVariable == aParentVariable)
return PR_TRUE;
parent = parent->mParent;
}
return PR_FALSE;
}
//----------------------------------------------------------------------
//
// nsTemplateRule
//
nsresult
nsTemplateRule::InitBindings(nsConflictSet& aConflictSet, nsTemplateMatch* aMatch) const
nsTemplateRule::AddBindingsToQueryProcessor(nsIXULTemplateQueryProcessor* aProcessor)
{
// Initialize a match's binding dependencies, so we can handle
// updates and queries later.
Binding* binding = mBindings;
for (Binding* binding = mBindings; binding != nsnull; binding = binding->mNext) {
// Add a dependency for bindings whose source variable comes
// from one of the <conditions>.
Value sourceValue;
PRBool hasBinding =
aMatch->mInstantiation.mAssignments.GetAssignmentFor(binding->mSourceVariable, &sourceValue);
while (binding) {
nsresult rv = aProcessor->AddBinding(mRuleNode, binding->mTargetVariable,
binding->mSourceVariable, binding->mExpr);
if (NS_FAILED(rv)) return rv;
if (hasBinding) {
nsIRDFResource* source = VALUE_TO_IRDFRESOURCE(sourceValue);
aMatch->mBindingDependencies.Add(source);
aConflictSet.AddBindingDependency(aMatch, source);
}
// If this binding is dependant on another binding, then we
// need to eagerly compute its source variable's assignment.
if (binding->mParent) {
Value value;
ComputeAssignmentFor(aConflictSet, aMatch, binding->mSourceVariable, &value);
}
binding = binding->mNext;
}
return NS_OK;
}
nsresult
nsTemplateRule::RecomputeBindings(nsConflictSet& aConflictSet,
nsTemplateMatch* aMatch,
nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aOldTarget,
nsIRDFNode* aNewTarget,
VariableSet& aModifiedVars) const
{
// Given a match with a source, property, old target, and new
// target, compute the minimal changes to the match's bindings.
// A temporary, mutable collection for holding all of the
// assignments that comprise the current match.
nsAutoVoidArray assignments;
{
// Collect -all- of the assignments in match into a temporary,
// mutable collection
nsAssignmentSet::ConstIterator last = aMatch->mAssignments.Last();
for (nsAssignmentSet::ConstIterator binding = aMatch->mAssignments.First(); binding != last; ++binding)
assignments.AppendElement(new nsAssignment(*binding));
// Truncate the match's assignments to only include
// assignments made via condition tests. We'll add back
// assignments as they are recomputed.
aMatch->mAssignments = aMatch->mInstantiation.mAssignments;
}
PRInt32 i;
// Iterate through each assignment, looking for the assignment
// whose value corresponds to the source of the assertion that's
// changing.
for (i = 0; i < assignments.Count(); ++i) {
nsAssignment* assignment = NS_STATIC_CAST(nsAssignment*, assignments[i]);
if ((assignment->mValue.GetType() == Value::eISupports) &&
(NS_STATIC_CAST(nsISupports*, assignment->mValue) == aSource)) {
// ...When we find it, look for binding's whose source
// variable depends on the assignment's variable
for (Binding* binding = mBindings; binding != nsnull; binding = binding->mNext) {
if ((binding->mSourceVariable != assignment->mVariable) ||
(binding->mProperty.get() != aProperty))
continue;
// Found one. Now we iterate through the assignments,
// doing fixup.
for (PRInt32 j = 0; j < assignments.Count(); ++j) {
nsAssignment* dependent = NS_STATIC_CAST(nsAssignment*, assignments[j]);
if (dependent->mVariable == binding->mTargetVariable) {
// The assignment's variable is the target
// varible for the binding: we can update it
// in-place.
dependent->mValue = Value(aNewTarget);
aModifiedVars.Add(dependent->mVariable);
}
else if (DependsOn(dependent->mVariable, binding->mTargetVariable)) {
// The assignment's variable depends on the
// binding's target variable, which is
// changing. Rip it out.
nsIRDFResource* target = VALUE_TO_IRDFRESOURCE(dependent->mValue);
aMatch->mBindingDependencies.Remove(target);
aConflictSet.RemoveBindingDependency(aMatch, target);
delete dependent;
assignments.RemoveElementAt(j--);
aModifiedVars.Add(dependent->mVariable);
}
else {
// The dependent variable is irrelevant. Leave
// it alone.
}
}
}
}
}
// Now our set of assignments will contain the original
// assignments from the conditions, any unchanged assignments, and
// the single assignment that was updated by iterating through the
// bindings.
//
// Add these assignments *back* to the match (modulo the ones
// already in the conditions).
//
// The values for any dependent assignments that we've ripped out
// will be computed the next time that somebody asks us for them.
for (i = assignments.Count() - 1; i >= 0; --i) {
nsAssignment* assignment = NS_STATIC_CAST(nsAssignment*, assignments[i]);
// Only add it if it's not already in the match's conditions
if (! aMatch->mInstantiation.mAssignments.HasAssignment(*assignment)) {
aMatch->mAssignments.Add(*assignment);
}
delete assignment;
}
return NS_OK;
}
PRBool
nsTemplateRule::ComputeAssignmentFor(nsConflictSet& aConflictSet,
nsTemplateMatch* aMatch,
PRInt32 aVariable,
Value* aValue) const
{
// Compute the value assignment for an arbitrary variable in a
// match. Potentially fill in dependencies if they haven't been
// resolved yet.
for (Binding* binding = mBindings; binding != nsnull; binding = binding->mNext) {
if (binding->mTargetVariable != aVariable)
continue;
// Potentially recur to find the value of the source.
//
// XXXwaterson this is sloppy, and could be dealt with more
// directly by following binding->mParent.
Value sourceValue;
PRBool hasSourceAssignment =
aMatch->GetAssignmentFor(aConflictSet, binding->mSourceVariable, &sourceValue);
if (! hasSourceAssignment)
return PR_FALSE;
nsCOMPtr<nsIRDFNode> target;
nsIRDFResource* source = VALUE_TO_IRDFRESOURCE(sourceValue);
if (source) {
mDataSource->GetTarget(source,
binding->mProperty,
PR_TRUE,
getter_AddRefs(target));
// Store the assignment in the match so we won't need to
// retrieve it again.
nsAssignment assignment(binding->mTargetVariable, Value(target.get()));
aMatch->mAssignments.Add(assignment);
// Add a dependency on the source, so we'll recompute the
// assignment if somebody tweaks it.
aMatch->mBindingDependencies.Add(source);
aConflictSet.AddBindingDependency(aMatch, source);
}
*aValue = target.get();
return PR_TRUE;
}
return PR_FALSE;
}

Просмотреть файл

@ -40,14 +40,84 @@
#define nsTemplateRule_h__
#include "nsCOMPtr.h"
#include "nsIAtom.h"
#include "nsIRDFDataSource.h"
#include "nsIRDFResource.h"
#include "nsIContent.h"
#include "nsIDOMNode.h"
#include "nsVoidArray.h"
#include "nsString.h"
#include "nsIXULTemplateRuleFilter.h"
class nsConflictSet;
class nsTemplateMatch;
class Value;
class VariableSet;
class nsIXULTemplateQueryProcessor;
class nsTemplateQuerySet;
class nsTemplateCondition
{
public:
// relations that may be used in a rule. They may be negated with the
// negate flag. Less and Greater are used for numeric comparisons and
// Before and After are used for string comparisons. For Less, Greater,
// Before, After, Startswith, Endswith, and Contains, the source is
// conceptually on the left of the relation and the target is on the
// right. For example, if the relation is Contains, that means Match if
// the source contains the target.
enum ConditionRelation {
eUnknown,
eEquals,
eLess,
eGreater,
eBefore,
eAfter,
eStartswith,
eEndswith,
eContains
};
nsTemplateCondition(nsIAtom* aSourceVariable,
const nsAString& aRelation,
nsIAtom* aTargetVariable,
PRBool mIgnoreCase,
PRBool mNegate);
nsTemplateCondition(nsIAtom* aSourceVariable,
const nsAString& aRelation,
const nsAString& aTargets,
PRBool mIgnoreCase,
PRBool mNegate,
PRBool aIsMultiple);
nsTemplateCondition(const nsAString& aSource,
const nsAString& aRelation,
nsIAtom* aTargetVariable,
PRBool mIgnoreCase,
PRBool mNegate);
~nsTemplateCondition() { MOZ_COUNT_DTOR(nsTemplateCondition); }
nsTemplateCondition* GetNext() { return mNext; }
void SetNext(nsTemplateCondition* aNext) { mNext = aNext; }
void SetRelation(const nsAString& aRelation);
PRBool
CheckMatch(nsIXULTemplateResult* aResult);
PRBool
CheckMatchStrings(const nsAString& aLeftString,
const nsAString& aRightString);
protected:
nsCOMPtr<nsIAtom> mSourceVariable;
nsString mSource;
ConditionRelation mRelation;
nsCOMPtr<nsIAtom> mTargetVariable;
nsStringArray mTargetList;
PRPackedBool mIgnoreCase;
PRPackedBool mNegate;
nsTemplateCondition* mNext;
};
/**
* A rule consists of:
@ -64,85 +134,71 @@ class VariableSet;
*
* - Content that should be constructed when the rule is "activated".
*
* - Priority, which helps to determine which rule should be
* considered "active" if several rules "match".
*
*/
class nsTemplateRule
{
public:
nsTemplateRule(nsIRDFDataSource* aDataSource,
nsIContent* aContent,
PRInt32 aPriority)
: mDataSource(aDataSource),
mContent(aContent),
mContainerVariable(0),
mMemberVariable(0),
mPriority(aPriority),
mCount(0),
mCapacity(0),
mBindings(nsnull)
{ MOZ_COUNT_CTOR(nsTemplateRule); }
nsTemplateRule(nsIContent* aRuleNode,
nsIContent* aAction,
nsTemplateQuerySet* aQuerySet);
~nsTemplateRule();
/**
* Return the content node that this rule was constructed from.
* @param aResult an out parameter, which will contain the content node
* that this rule was constructed from
* Return the <action> node that this rule was constructed from, or its
* logical equivalent for shorthand syntaxes. That is, the parent node of
* the content that should be generated for this rule.
* @param aAction an out parameter, which will contain the content node
* that this rule uses to generated content
* @return NS_OK if no errors occur.
*/
nsresult GetContent(nsIContent** aResult) const;
nsresult GetAction(nsIContent** aAction) const;
/**
* Set the variable which should be used as the "container
* variable" in this rule. The container variable will be bound to
* an RDF resource that is the parent of the member resources.
* @param aContainerVariable the variable that should be used as the
* "container variable" in this rule
* Return the <rule> content node that this rule was constructed from.
* @param aResult an out parameter, which will contain the rule node
* @return NS_OK if no errors occur.
*/
void SetContainerVariable(PRInt32 aContainerVariable) {
mContainerVariable = aContainerVariable; }
nsresult GetRuleNode(nsIDOMNode** aResult) const;
void SetVars(nsIAtom* aRefVariable, nsIAtom* aMemberVariable)
{
mRefVariable = aRefVariable;
mMemberVariable = aMemberVariable;
}
void SetRuleFilter(nsIXULTemplateRuleFilter* aRuleFilter)
{
mRuleFilter = aRuleFilter;
}
nsIAtom* GetTag() { return mTag; }
void SetTag(nsIAtom* aTag) { mTag = aTag; }
nsIAtom* GetMemberVariable() { return mMemberVariable; }
/**
* Retrieve the variable that this rule uses as its "container variable".
* @return the variable that this rule uses as its "container variable".
* Set the first condition for the rule. Other conditions are linked
* to it using the condition's SetNext method.
*/
PRInt32 GetContainerVariable() const {
return mContainerVariable; }
void SetCondition(nsTemplateCondition* aConditions);
/**
* Set the variable which should be used as the "member variable"
* in this rule. The member variable will be bound to an RDF
* resource that is the child of the container resource.
* @param aMemberVariable the variable that should be used as the
* "member variable" in this rule.
* Check if the result matches the rule by first looking at the conditions.
* If the results is accepted by the conditions, the rule filter, if any
* was set, is checked. If either check rejects a result, a match cannot
* occur for this rule and result.
*/
void SetMemberVariable(PRInt32 aMemberVariable) {
mMemberVariable = aMemberVariable; }
/**
* Retrieve the variable that this rule uses as its "member variable".
* @return the variable that this rule uses as its "member variable"
*/
PRInt32 GetMemberVariable() const {
return mMemberVariable; }
/**
* Retrieve the "priority" of the rule with respect to the
* other rules in the template
* @return the rule's priority, lower values mean "use this first".
*/
PRInt32 GetPriority() const {
return mPriority; }
PRBool
CheckMatch(nsIXULTemplateResult* aResult) const;
/**
* Determine if the rule has the specified binding
*/
PRBool
HasBinding(PRInt32 aSourceVariable,
nsIRDFResource* aProperty,
PRInt32 aTargetVariable) const;
HasBinding(nsIAtom* aSourceVariable,
nsAString& aExpr,
nsIAtom* aTargetVariable) const;
/**
* Add a binding to the rule. A binding consists of an already-bound
@ -152,100 +208,128 @@ public:
*
* @param aSourceVariable the source variable that will be used in
* the RDF query.
* @param aProperty the RDF property that will be used in the RDF
* query.
* @param aExpr the expression that will be used in the query.
* @param aTargetVariable the variable whose value will be bound
* to the RDF node that is returned when querying the binding
* @return NS_OK if no errors occur.
*/
nsresult AddBinding(PRInt32 aSourceVariable,
nsIRDFResource* aProperty,
PRInt32 aTargetVariable);
nsresult AddBinding(nsIAtom* aSourceVariable,
nsAString& aExpr,
nsIAtom* aTargetVariable);
/**
* Initialize a match by adding necessary binding dependencies to
* the conflict set. This will allow us to properly update the
* match later if a value should change that the match's bindings
* depend on.
* @param aConflictSet the conflict set
* @param aMatch the match we to initialize
* @return NS_OK if no errors occur.
* Inform the query processor of the bindings that are set for a rule.
* This should be called after all the bindings for a rule are compiled.
*/
nsresult InitBindings(nsConflictSet& aConflictSet, nsTemplateMatch* aMatch) const;
/**
* Compute the minimal set of changes to a match's bindings that
* must occur which the specified change is made to the RDF graph.
* @param aConflictSet the conflict set, which we may need to manipulate
* to update the binding dependencies.
* @param aMatch the match for which we must recompute the bindings
* @param aSource the "source" resource in the RDF graph
* @param aProperty the "property" resource in the RDF graph
* @param aOldTarget the old "target" node in the RDF graph
* @param aNewTarget the new "target" node in the RDF graph.
* @param aModifiedVars a VariableSet, into which this routine
* will assign each variable whose value has changed.
* @return NS_OK if no errors occurred.
*/
nsresult RecomputeBindings(nsConflictSet& aConflictSet,
nsTemplateMatch* aMatch,
nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aOldTarget,
nsIRDFNode* aNewTarget,
VariableSet& aModifiedVars) const;
/**
* Compute the value to assign to an arbitrary variable in a
* match. This may require us to work out several dependancies,
* if there are bindings set up for this rule.
* @param aConflictSet the conflict set; if necessary, we may add
* a "binding dependency" to the conflict set, which will allow us
* to correctly recompute the bindings later if they should change.
* @param aMatch the match that provides the "seed" variable assignments,
* which we may need to extend using the rule's bindings.
* @param aVariable the variable for which we are to compute the
* assignment.
* @param aValue an out parameter that will receive the value that
* was assigned to aVariable, if we could find one.
* @return PR_TRUE if an assignment was found for aVariable, PR_FALSE
* otherwise.
*/
PRBool ComputeAssignmentFor(nsConflictSet& aConflictSet,
nsTemplateMatch* aMatch,
PRInt32 aVariable,
Value* aValue) const;
/**
* Determine if one variable depends on another in the rule's
* bindings.
* @param aChild the dependent variable, whose value may
* depend on the assignment of aParent.
* @param aParent the variable whose value aChild is depending on.
* @return PR_TRUE if aChild's assignment depends on the assignment
* for aParent, PR_FALSE otherwise.
*/
PRBool DependsOn(PRInt32 aChild, PRInt32 aParent) const;
nsresult
AddBindingsToQueryProcessor(nsIXULTemplateQueryProcessor* aProcessor);
protected:
nsCOMPtr<nsIRDFDataSource> mDataSource;
nsCOMPtr<nsIContent> mContent;
PRInt32 mContainerVariable;
PRInt32 mMemberVariable;
PRInt32 mPriority;
PRInt32 mCount;
PRInt32 mCapacity;
struct Binding {
PRInt32 mSourceVariable;
nsCOMPtr<nsIRDFResource> mProperty;
PRInt32 mTargetVariable;
nsCOMPtr<nsIAtom> mSourceVariable;
nsCOMPtr<nsIAtom> mTargetVariable;
nsString mExpr;
Binding* mNext;
Binding* mParent;
};
// backreference to the query set which owns this rule
nsTemplateQuerySet* mQuerySet;
// the <rule> node, or the <template> node if there is no <rule>
nsCOMPtr<nsIDOMNode> mRuleNode;
// the <action> node, or, if there is no <action>, the container node
// which contains the content to generate
nsCOMPtr<nsIContent> mAction;
// the rule filter set by the builder's SetRuleFilter function
nsCOMPtr<nsIXULTemplateRuleFilter> mRuleFilter;
// indicates that the rule will only match when generating content
// to be inserted into a container with this tag
nsCOMPtr<nsIAtom> mTag;
// linked-list of the bindings for the rule, owned by the rule.
Binding* mBindings;
nsCOMPtr<nsIAtom> mRefVariable;
nsCOMPtr<nsIAtom> mMemberVariable;
nsTemplateCondition* mConditions; // owned by nsTemplateRule
};
/** nsTemplateQuerySet
*
* A single <queryset> which holds the query node and the rules for it.
* All builders have at least one queryset, which may be created with an
* explicit <queryset> tag or implied if the tag is not used.
*
* These queryset objects are created and owned by the builder in its
* mQuerySets array.
*/
class nsTemplateQuerySet
{
protected:
nsVoidArray mRules; // rules owned by nsTemplateQuerySet
// a number which increments for each successive queryset. It is stored so
// it can be used as an optimization when updating results so that it is
// known where to insert them into a match.
PRInt32 mPriority;
public:
// <query> node
nsCOMPtr<nsIContent> mQueryNode;
// compiled opaque query object returned by the query processor's
// CompileQuery call
nsCOMPtr<nsISupports> mCompiledQuery;
nsTemplateQuerySet(PRInt32 aPriority)
: mPriority(aPriority)
{
MOZ_COUNT_CTOR(nsTemplateQuerySet);
}
~nsTemplateQuerySet()
{
MOZ_COUNT_DTOR(nsTemplateQuerySet);
Clear();
}
PRInt32 Priority() const
{
return mPriority;
}
nsresult AddRule(nsTemplateRule *aChild)
{
if (!mRules.AppendElement(aChild))
return NS_ERROR_OUT_OF_MEMORY;
return NS_OK;
}
PRInt32 RuleCount() const
{
return mRules.Count();
}
nsTemplateRule* GetRuleAt(PRInt32 aIndex)
{
return NS_STATIC_CAST(nsTemplateRule*, mRules[aIndex]);
}
void Clear()
{
for (PRInt32 r = mRules.Count() - 1; r >= 0; r--) {
nsTemplateRule* rule = NS_STATIC_CAST(nsTemplateRule* , mRules[r]);
delete rule;
}
mRules.Clear();
}
};
#endif // nsTemplateRule_h__

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -36,13 +36,12 @@
*
* ***** END LICENSE BLOCK ***** */
#include "nsString.h"
#include "nsTreeRows.h"
#include "nsTemplateMatch.h"
#include "nsTemplateRule.h"
nsTreeRows::Subtree*
nsTreeRows::EnsureSubtreeFor(Subtree* aParent,
PRInt32 aChildIndex)
PRInt32 aChildIndex)
{
Subtree* subtree = GetSubtreeFor(aParent, aChildIndex);
@ -174,20 +173,59 @@ nsTreeRows::operator[](PRInt32 aRow)
}
nsTreeRows::iterator
nsTreeRows::Find(nsConflictSet& aConflictSet, nsIRDFResource* aMember)
nsTreeRows::FindByResource(nsIRDFResource* aResource)
{
// XXX Mmm, scan through the rows one-by-one...
iterator last = Last();
iterator iter;
nsresult rv;
nsAutoString resourceid;
PRBool stringmode = PR_FALSE;
for (iter = First(); iter != last; ++iter) {
if (!stringmode) {
nsCOMPtr<nsIRDFResource> findres;
rv = iter->mMatch->mResult->GetResource(getter_AddRefs(findres));
if (NS_FAILED(rv)) return last;
if (findres == aResource)
break;
if (! findres) {
const char *uri;
aResource->GetValueConst(&uri);
CopyUTF8toUTF16(uri, resourceid);
// set stringmode and fall through
stringmode = PR_TRUE;
}
}
// additional check because previous block could change stringmode
if (stringmode) {
nsAutoString findid;
rv = iter->mMatch->mResult->GetId(findid);
if (NS_FAILED(rv)) return last;
if (resourceid.Equals(findid))
break;
}
}
return iter;
}
nsTreeRows::iterator
nsTreeRows::Find(nsIXULTemplateResult *aResult)
{
// XXX Mmm, scan through the rows one-by-one...
iterator last = Last();
iterator iter;
for (iter = First(); iter != last; ++iter) {
nsTemplateMatch* match = iter->mMatch;
Value val;
match->GetAssignmentFor(aConflictSet, match->mRule->GetMemberVariable(), &val);
if (VALUE_TO_IRDFRESOURCE(val) == aMember)
break;
if (aResult == iter->mMatch->mResult)
break;
}
return iter;

Просмотреть файл

@ -40,10 +40,11 @@
#define nsTreeRows_h__
#include "nsCOMPtr.h"
#include "nsIRDFResource.h"
#include "pldhash.h"
class nsConflictSet;
class nsTemplateMatch;
#include "nsIXULTemplateResult.h"
#include "nsTemplateMatch.h"
#include "nsIRDFResource.h"
/**
* This class maintains the state of the XUL tree builder's
@ -311,17 +312,21 @@ public:
iterator Last();
/**
* Find the row that contains the match with the specified member
* resource.
* Find the row that contains the given resource
*/
iterator Find(nsConflictSet& aConflictSet, nsIRDFResource* aMember);
iterator FindByResource(nsIRDFResource* aResource);
/**
* Find the row that contains the result
*/
iterator Find(nsIXULTemplateResult* aResult);
/**
* Retrieve the ith element in the view
*/
iterator operator[](PRInt32 aIndex);
nsTreeRows() : mRoot(nsnull), mRootResource(nsnull) {}
nsTreeRows() : mRoot(nsnull) {}
~nsTreeRows() {}
/**
@ -431,7 +436,7 @@ public:
mRootResource = aResource; }
/**
* Retrieve hte root resource for the view
* Retrieve the root resource for the view
*/
nsIRDFResource* GetRootResource() {
return mRootResource.get(); }

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -86,16 +86,22 @@
#include "nsIDateTimeFormat.h"
#include "nsDateTimeFormatCID.h"
#include "nsIScriptableDateFormat.h"
#include "nsICollation.h"
#include "nsCollationCID.h"
#include "nsILocale.h"
#include "nsILocaleService.h"
static NS_DEFINE_CID(kDateTimeFormatCID, NS_DATETIMEFORMAT_CID);
static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
static NS_DEFINE_CID(kCollationFactoryCID, NS_COLLATIONFACTORY_CID);
//------------------------------------------------------------------------
nsrefcnt nsXULContentUtils::gRefCnt;
nsIRDFService* nsXULContentUtils::gRDF;
nsIDateTimeFormat* nsXULContentUtils::gFormat;
nsICollation *nsXULContentUtils::gCollation;
#define XUL_RESOURCE(ident, uri) nsIRDFResource* nsXULContentUtils::ident
#define XUL_LITERAL(ident, val) nsIRDFLiteral* nsXULContentUtils::ident
@ -136,6 +142,26 @@ nsXULContentUtils::Init()
if (NS_FAILED(rv)) {
return rv;
}
// get a locale service
nsCOMPtr<nsILocaleService> localeService =
do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsILocale> locale;
rv = localeService->GetApplicationLocale(getter_AddRefs(locale));
if (NS_SUCCEEDED(rv) && locale) {
nsCOMPtr<nsICollationFactory> colFactory =
do_CreateInstance(kCollationFactoryCID);
if (colFactory) {
rv = colFactory->CreateCollation(locale, &gCollation);
NS_ASSERTION(NS_SUCCEEDED(rv),
"couldn't create collation instance");
} else
NS_ERROR("couldn't create instance of collation factory");
} else
NS_ERROR("unable to get application locale");
} else
NS_ERROR("couldn't get locale factory");
}
return NS_OK;
@ -155,6 +181,7 @@ nsXULContentUtils::Finish()
#undef XUL_LITERAL
NS_IF_RELEASE(gFormat);
NS_IF_RELEASE(gCollation);
}
return NS_OK;
@ -215,42 +242,6 @@ nsXULContentUtils::GetElementResource(nsIContent* aElement, nsIRDFResource** aRe
}
nsresult
nsXULContentUtils::GetElementRefResource(nsIContent* aElement, nsIRDFResource** aResult)
{
*aResult = nsnull;
// Perform a reverse mapping from an element in the content model
// to an RDF resource. Check for a "ref" attribute first, then
// fallback on an "id" attribute.
nsresult rv;
PRUnichar buf[128];
nsFixedString uri(buf, NS_ARRAY_LENGTH(buf), 0);
aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::ref, uri);
if (!uri.IsEmpty()) {
// We'll use rdf_MakeAbsolute() to translate this to a URL.
nsCOMPtr<nsIDocument> doc = aElement->GetDocument();
nsIURI *url = doc->GetDocumentURI();
NS_ASSERTION(url != nsnull, "element has no document");
if (! url)
return NS_ERROR_UNEXPECTED;
// N.B. that if this fails (e.g., because necko doesn't grok
// the protocol), uriStr will be untouched.
NS_MakeAbsoluteURI(uri, uri, url);
rv = gRDF->GetUnicodeResource(uri, aResult);
}
else {
rv = GetElementResource(aElement, aResult);
}
return rv;
}
/*
Note: this routine is similar, yet distinctly different from, nsBookmarksService::GetTextForNode
*/
@ -511,5 +502,3 @@ nsXULContentUtils::SetCommandUpdater(nsIDocument* aDocument, nsIContent* aElemen
return NS_OK;
}

Просмотреть файл

@ -59,6 +59,7 @@ class nsIRDFLiteral;
class nsIRDFService;
class nsINameSpaceManager;
class nsIDateTimeFormat;
class nsICollation;
class nsXULContentUtils
{
@ -66,6 +67,7 @@ protected:
static nsrefcnt gRefCnt;
static nsIRDFService* gRDF;
static nsIDateTimeFormat* gFormat;
static nsICollation *gCollation;
static PRBool gDisableXULCache;
@ -93,9 +95,6 @@ public:
static nsresult
GetElementResource(nsIContent* aElement, nsIRDFResource** aResult);
static nsresult
GetElementRefResource(nsIContent* aElement, nsIRDFResource** aResult);
static nsresult
GetTextForNode(nsIRDFNode* aNode, nsAString& aResult);
@ -123,6 +122,12 @@ public:
return gRDF;
}
static nsICollation*
GetCollation()
{
return gCollation;
}
#define XUL_RESOURCE(ident, uri) static nsIRDFResource* ident
#define XUL_LITERAL(ident, val) static nsIRDFLiteral* ident
#include "nsXULResourceList.h"
@ -130,6 +135,4 @@ public:
#undef XUL_LITERAL
};
#endif // nsXULContentUtils_h__

Просмотреть файл

@ -82,7 +82,6 @@
#include "nsIXULSortService.h"
#include "prlog.h"
#include "nsICollation.h"
#include "nsCollationCID.h"
#include "nsLayoutCID.h"
#include "nsIDOMXULElement.h"
#include "nsILocale.h"
@ -95,7 +94,6 @@
////////////////////////////////////////////////////////////////////////
static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
static NS_DEFINE_CID(kCollationFactoryCID, NS_COLLATIONFACTORY_CID);
static NS_DEFINE_CID(kRDFInMemoryDataSourceCID, NS_RDFINMEMORYDATASOURCE_CID);
static NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
@ -149,8 +147,6 @@ protected:
XULSortServiceImpl(void);
virtual ~XULSortServiceImpl(void);
static nsICollation *gCollation;
friend nsresult NS_NewXULSortService(nsIXULSortService** mgr);
private:
@ -193,7 +189,6 @@ public:
PRBool &bothValid, PRInt32 & sortOrder);
};
nsICollation *XULSortServiceImpl::gCollation = nsnull;
nsIRDFService *XULSortServiceImpl::gRDFService = nsnull;
nsIRDFContainerUtils *XULSortServiceImpl::gRDFC = nsnull;
nsrefcnt XULSortServiceImpl::gRefCnt = 0;
@ -220,23 +215,6 @@ XULSortServiceImpl::XULSortServiceImpl(void)
rv = CallGetService(kRDFContainerUtilsCID, &gRDFC);
NS_ASSERTION(NS_SUCCEEDED(rv), "couldn't create rdf container utils");
// get a locale service
nsCOMPtr<nsILocaleService> localeService = do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsILocale> locale;
if (NS_SUCCEEDED(rv = localeService->GetApplicationLocale(getter_AddRefs(locale))) && (locale)) {
nsCOMPtr<nsICollationFactory> colFactory = do_CreateInstance(kCollationFactoryCID);
if (colFactory) {
rv = colFactory->CreateCollation(locale, &gCollation);
NS_ASSERTION(NS_SUCCEEDED(rv), "couldn't create collation instance");
} else
NS_ERROR("couldn't create instance of collation factory");
} else
NS_ERROR("unable to get application locale");
} else
NS_ERROR("couldn't get locale factory");
}
++gRefCnt;
@ -259,8 +237,6 @@ XULSortServiceImpl::~XULSortServiceImpl(void) {
delete kNaturalStr;
kNaturalStr = nsnull;
NS_IF_RELEASE(gCollation);
NS_IF_RELEASE(gRDFService);
NS_IF_RELEASE(gRDFC);
}
@ -462,8 +438,10 @@ XULSortServiceImpl::CompareNodes(nsIRDFNode *cellNode1, PRBool isCollationKey1,
r->GetValue(&rkey);
r->GetLength(&rlen);
bothValid = PR_TRUE;
if (gCollation)
return gCollation->CompareRawSortKey(lkey, llen, rkey, rlen, &sortOrder);
nsICollation* collation = nsXULContentUtils::GetCollation();
if (collation)
return collation->CompareRawSortKey(lkey, llen, rkey, rlen, &sortOrder);
}
}
}
@ -481,18 +459,21 @@ XULSortServiceImpl::CompareNodes(nsIRDFNode *cellNode1, PRBool isCollationKey1,
l->GetValueConst(&luni);
r->GetValueConst(&runi);
bothValid = PR_TRUE;
if (isCollationKey1 && isCollationKey2)
return gCollation->CompareRawSortKey(NS_REINTERPRET_CAST(const PRUint8*, luni),
nsICollation* collation = nsXULContentUtils::GetCollation();
if (collation && isCollationKey1 && isCollationKey2) {
return collation->CompareRawSortKey(NS_REINTERPRET_CAST(const PRUint8*, luni),
nsCRT::strlen(luni)*sizeof(PRUnichar),
NS_REINTERPRET_CAST(const PRUint8*, runi),
nsCRT::strlen(runi)*sizeof(PRUnichar),
&sortOrder);
}
else
{
nsresult rv = NS_ERROR_FAILURE;
nsDependentString lstr(luni), rstr(runi);
if (gCollation)
rv = gCollation->CompareString(nsICollation::kCollationCaseInSensitive, lstr, rstr, &sortOrder);
if (collation)
rv = collation->CompareString(nsICollation::kCollationCaseInSensitive, lstr, rstr, &sortOrder);
if (NS_FAILED(rv))
sortOrder = Compare(lstr, rstr, nsCaseInsensitiveStringComparator());
return NS_OK;

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -24,6 +24,7 @@
* David Hyatt <hyatt@netscape.com>
* Chris Waterson <waterson@netscape.com>
* Pierre Phaneuf <pp@ludusdesign.com>
* Neil Deakin <enndeakin@sympatico.ca>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
@ -44,6 +45,7 @@
#include "nsStubDocumentObserver.h"
#include "nsIScriptSecurityManager.h"
#include "nsIContent.h"
#include "nsIRDFCompositeDataSource.h"
#include "nsIRDFContainer.h"
#include "nsIRDFContainerUtils.h"
@ -52,21 +54,20 @@
#include "nsIRDFService.h"
#include "nsIXULTemplateBuilder.h"
#include "nsConflictSet.h"
#include "nsFixedSizeAllocator.h"
#include "nsResourceSet.h"
#include "nsRuleNetwork.h"
#include "nsVoidArray.h"
#include "nsCOMArray.h"
#include "nsTArray.h"
#include "nsDataHashtable.h"
#include "nsTemplateRule.h"
#include "nsTemplateMatch.h"
#include "nsIXULTemplateQueryProcessor.h"
#include "prlog.h"
#ifdef PR_LOGGING
extern PRLogModuleInfo* gXULTemplateLog;
#endif
class nsClusterKeySet;
class nsTemplateMatch;
class nsTemplateRule;
class nsIXULDocument;
class nsIRDFCompositeDataSource;
@ -75,14 +76,19 @@ class nsIRDFCompositeDataSource;
* set of rules.
*/
class nsXULTemplateBuilder : public nsIXULTemplateBuilder,
public nsStubDocumentObserver,
public nsIRDFObserver
public nsStubDocumentObserver
{
public:
nsXULTemplateBuilder();
virtual ~nsXULTemplateBuilder();
nsresult Init();
nsresult InitGlobals();
/**
* Clear the template builder structures. The aIsFinal flag is set to true
* when the template is going away.
*/
virtual void Uninit(PRBool aIsFinal);
// nsISupports interface
NS_DECL_ISUPPORTS
@ -96,8 +102,10 @@ public:
PRInt32 aModType);
virtual void DocumentWillBeDestroyed(nsIDocument *aDocument);
// nsIRDFObserver interface
NS_DECL_NSIRDFOBSERVER
nsresult
UpdateResult(nsIXULTemplateResult* aOldResult,
nsIXULTemplateResult* aNewResult,
nsIDOMNode* aQueryNode);
nsresult
ComputeContainmentProperties();
@ -105,19 +113,6 @@ public:
static PRBool
IsTemplateElement(nsIContent* aContent);
/**
* Initialize the rule network.
*/
virtual nsresult
InitializeRuleNetwork();
/**
* Initialize the rule network for handling rules that use the
* ``simple'' syntax.
*/
virtual nsresult
InitializeRuleNetworkForSimpleRules(InnerNode** aChildNode) = 0;
virtual nsresult
RebuildAll() = 0; // must be implemented by subclasses
@ -128,60 +123,100 @@ public:
GetTemplateRoot(nsIContent** aResult);
/**
* Compile the template's rules
* Compile the template's queries
*/
nsresult
CompileRules();
CompileQueries();
/**
* Compile a rule that's specified using the extended template
* syntax.
* Compile the template given a <template> in aTemplate. This function
* is called recursively to handle queries inside a queryset. For the
* outer pass, aIsQuerySet will be false, while the inner pass this will
* be true.
*
* aCanUseTemplate will be set to true if the template's queries could be
* compiled, and false otherwise. If false, the entire template is
* invalid.
*
* @param aTemplate <template> to compile
* @param aQuerySet first queryset
* @param aIsQuerySet true if
* @param aPriority the queryset index, incremented when a new one is added
* @param aCanUseTemplate true if template is valid
*/
nsresult
CompileExtendedRule(nsIContent* aRuleElement,
PRInt32 aPriority,
InnerNode* aParentNode);
CompileTemplate(nsIContent* aTemplate,
nsTemplateQuerySet* aQuerySet,
PRBool aIsQuerySet,
PRInt32* aPriority,
PRBool* aCanUseTemplate);
/**
* Compile the <conditions> of a rule that uses the extended
* template syntax.
* Compile a query using the extended syntax. For backwards compatible RDF
* syntax where there is no <query>, the <conditions> becomes the query.
*
* @param aRuleElement <rule> element
* @param aActionElement <action> element
* @param aMemberVariable member variable for the query
* @param aTag tag specified in <content> element
* @param aQuerySet the queryset
*/
nsresult
CompileExtendedQuery(nsIContent* aRuleElement,
nsIContent* aActionElement,
nsIAtom* aMemberVariable,
nsIAtom* aTag,
nsTemplateQuerySet* aQuerySet);
/**
* Determine the ref variable and tag from inside a RDF query.
*/
void DetermineRDFQueryRef(nsIContent* aQueryElement, nsIAtom** tag);
/**
* Determine the member variable from inside an action body. It will be
* the value of the uri attribute on a node.
*/
nsresult
CompileConditions(nsTemplateRule* aRule,
nsIContent* aConditions,
InnerNode* aParentNode,
InnerNode** aLastNode);
DetermineMemberVariable(nsIContent* aActionElement, nsIAtom** aMemberVariable);
/**
* Compile a single condition from an extended template syntax
* rule. Subclasses may override to provide additional,
* subclass-specific condition processing.
* Compile a simple query. A simple query is one that doesn't have a
* <query> and should use a default query which would normally just return
* a list of children of the reference point.
*
* @param aRuleElement the <rule>
* @param aQuerySet the query set
* @param aCanUseTemplate true if the query is valid
*/
virtual nsresult
CompileCondition(nsIAtom* aTag,
nsTemplateRule* aRule,
nsIContent* aConditions,
InnerNode* aParentNode,
TestNode** aResult);
nsresult
CompileSimpleQuery(nsIContent* aRuleElement,
nsTemplateQuerySet* aQuerySet,
PRBool* aCanUseTemplate);
/**
* Compile a <triple> condition
* Compile the <conditions> tag in a rule
*
* @param aRule template rule
* @param aConditions <conditions> element
*/
nsresult
CompileTripleCondition(nsTemplateRule* aRule,
nsIContent* aCondition,
InnerNode* aParentNode,
TestNode** aResult);
CompileConditions(nsTemplateRule* aRule, nsIContent* aConditions);
/**
* Compile a <member> condition
* Compile a <where> tag in a condition. The caller should set
* *aCurrentCondition to null for the first condition. This value will be
* updated to point to the new condition before returning. The conditions
* will be added to the rule aRule by this method.
*
* @param aRule template rule
* @param aCondition <where> element
* @param aCurrentCondition compiled condition
*/
nsresult
CompileMemberCondition(nsTemplateRule* aRule,
nsIContent* aCondition,
InnerNode* aParentNode,
TestNode** aResult);
CompileWhereCondition(nsTemplateRule* aRule,
nsIContent* aCondition,
nsTemplateCondition** aCurrentCondition);
/**
* Compile the <bindings> for an extended template syntax rule.
@ -195,33 +230,6 @@ public:
nsresult
CompileBinding(nsTemplateRule* aRule, nsIContent* aBinding);
/**
* Compile a rule that's specified using the simple template
* syntax.
*/
nsresult
CompileSimpleRule(nsIContent* aRuleElement, PRInt32 aPriorty, InnerNode* aParentNode);
/**
* Can be overridden by subclasses to handle special attribute conditions
* for the simple syntax.
* @return PR_TRUE if the condition was handled
*/
virtual PRBool
CompileSimpleAttributeCondition(PRInt32 aNameSpaceID,
nsIAtom* aAttribute,
const nsAString& aValue,
InnerNode* aParentNode,
TestNode** aResult);
/**
* Parse the value of a property test assertion for a condition or a simple
* rule based on the parseType attribute into the appropriate literal type.
*/
nsresult ParseLiteral(const nsString& aParseType,
const nsString& aValue,
nsIRDFNode** aResult);
/**
* Add automatic bindings for simple rules
*/
@ -233,6 +241,38 @@ public:
const nsAString& aVariable,
void* aClosure);
nsresult
LoadDataSources(nsIDocument* aDoc);
nsresult
InitHTMLTemplateRoot();
/**
* Determine which rule matches a given result. aContainer is used for
* tag matching and is optional for non-content generating builders.
* The returned matched rule is always one of the rules owned by the
* query set aQuerySet.
*
* @param aContainer parent when generated content will be inserted
* @param aResult result to match
* @param aQuerySet query set to examine the rules of
* @param aMatchedRule [out] rule that has matched, or null if any.
*/
nsresult
DetermineMatchedRule(nsIContent* aContainer,
nsIXULTemplateResult* aResult,
nsTemplateQuerySet* aQuerySet,
nsTemplateRule** aMatchedRule);
/**
* Return true if the supplied content or the content generated from the
* result has the given tag.
*/
PRBool
MatchTagForResult(nsIContent* aContent,
nsIXULTemplateResult* aResult,
nsIAtom* aTag);
// XXX sigh, the string template foo doesn't mix with
// operator->*() on egcs-1.1.2, so we'll need to explicitly pass
// "this" and use good ol' fashioned static callbacks.
@ -243,13 +283,7 @@ public:
void* aClosure);
nsresult
LoadDataSources(nsIDocument* aDoc);
nsresult
InitHTMLTemplateRoot();
nsresult
SubstituteText(nsTemplateMatch& aMatch,
SubstituteText(nsIXULTemplateResult* aMatch,
const nsAString& aAttributeValue,
nsAString& aResult);
@ -259,81 +293,71 @@ public:
static void
SubstituteTextReplaceVariable(nsXULTemplateBuilder* aThis, const nsAString& aVariable, void* aClosure);
PRBool
IsAttrImpactedByVars(nsTemplateMatch& aMatch,
const nsAString& aAttributeValue,
const VariableSet& aModifiedVars);
static void
IsVarInSet(nsXULTemplateBuilder* aThis, const nsAString& aVariable, void* aClosure);
nsresult
SynchronizeAll(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aOldTarget,
nsIRDFNode* aNewTarget);
nsresult
Propagate(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget,
nsClusterKeySet& aNewKeys);
nsresult
FireNewlyMatchedRules(const nsClusterKeySet& aNewKeys);
nsresult
Retract(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget);
nsresult
CheckContainer(nsIRDFResource* aTargetResource, PRBool* aIsContainer, PRBool* aIsEmpty);
nsresult
IsSystemPrincipal(nsIPrincipal *principal, PRBool *result);
#ifdef PR_LOGGING
nsresult
Log(const char* aOperation,
nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget);
#define LOG(_op, _src, _prop, _targ) \
Log(_op, _src, _prop, _targ)
#else
#define LOG(_op, _src, _prop, _targ)
#endif
/**
* Convenience method which gets a resource for a result. If a result
* doesn't have a resource set, it will create one from the result's id.
*/
nsresult GetResultResource(nsIXULTemplateResult* aResult,
nsIRDFResource** aResource);
protected:
// We are an observer of the composite datasource. The cycle is
// broken when the document is destroyed.
nsCOMPtr<nsIRDFDataSource> mDB;
nsCOMPtr<nsIRDFCompositeDataSource> mCompDB;
// Circular reference, broken when the document is destroyed.
/**
* Circular reference, broken when the document is destroyed.
*/
nsCOMPtr<nsIContent> mRoot;
nsCOMPtr<nsIRDFDataSource> mCache;
/**
* The root result, translated from the root element's ref
*/
nsCOMPtr<nsIXULTemplateResult> mRootResult;
nsCOMArray<nsIXULBuilderListener> mListeners;
PRInt32 mUpdateBatchNest;
/**
* The query processor which generates results
*/
nsCOMPtr<nsIXULTemplateQueryProcessor> mQueryProcessor;
// For the rule network
nsResourceSet mContainmentProperties;
PRBool mRulesCompiled;
/**
* The list of querysets
*/
nsTArray<nsTemplateQuerySet *> mQuerySets;
/**
* Set to true if the rules have already been compiled
*/
PRBool mQueriesCompiled;
/**
* The default reference and member variables.
*/
nsCOMPtr<nsIAtom> mRefVariable;
nsCOMPtr<nsIAtom> mMemberVariable;
/**
* The match map contains nsTemplateMatch objects, one for each unique
* match found, keyed by the resource for that match. A particular match
* will contain a linked list of all of the matches for that unique result
* id. Only one is active at a time. When a match is retracted, look in
* the match map, remove it, and apply the next valid match in sequence to
* make active.
*/
nsDataHashtable<nsISupportsHashKey, nsTemplateMatch*> mMatchMap;
/**
* Fixed size allocator used to allocate matches
*/
nsFixedSizeAllocator mPool;
public:
nsRuleNetwork mRules;
PRInt32 mContainerVar;
nsString mContainerSymbol;
PRInt32 mMemberVar;
nsString mMemberSymbol;
nsConflictSet mConflictSet;
ReteNodeSet mRDFTests;
nsFixedSizeAllocator& GetPool() { return mPool; }
protected:
// pseudo-constants
@ -344,7 +368,8 @@ protected:
static nsIPrincipal* gSystemPrincipal;
enum {
eDontTestEmpty = (1 << 0)
eDontTestEmpty = (1 << 0),
eDontRecurse = (2 << 0)
};
PRInt32 mFlags;
@ -381,22 +406,41 @@ protected:
IsActivated(nsIRDFResource *aResource);
/**
* Must be implemented by subclasses. Handle replacing aOldMatch
* with aNewMatch. Either aOldMatch or aNewMatch may be null.
* Returns true if content may be generated for a result, or false if it
* cannot, for example, if it would be created inside a closed container.
* Those results will be generated when the container is opened.
* If false is returned, no content should be generated. The insertion
* location may optionally be set for new content, depending on the
* builder being used. This argument is used to optimize the content
* builder so that it doesn't need to determined again when ReplaceMatch
* is called.
*/
virtual PRBool
MayGenerateResult(nsIXULTemplateResult* aOldResult,
void** aLocation) = 0;
/**
* Must be implemented by subclasses. Handle removing the generated
* output for aOldMatch and adding new output for aNewMatch. Either
* aOldMatch or aNewMatch may be null. aContext is the location returned
* from the call to MayGenerateResult.
*/
virtual nsresult
ReplaceMatch(nsIRDFResource* aMember, const nsTemplateMatch* aOldMatch, nsTemplateMatch* aNewMatch) = 0;
ReplaceMatch(nsIXULTemplateResult* aOldResult,
nsTemplateMatch* aNewMatch,
nsTemplateRule* aNewMatchRule,
void *aContext) = 0;
/**
* Must be implemented by subclasses. Handle change in bound
* variable values for aMatch. aModifiedVars contains the set
* variable values for aResult. aModifiedVars contains the set
* of variables that have changed.
* @param aMatch the match for which variable bindings has changed.
* @param aResult the ersult for which variable bindings has changed.
* @param aModifiedVars the set of variables for which the bindings
* have changed.
*/
virtual nsresult
SynchronizeMatch(nsTemplateMatch* aMatch, const VariableSet& aModifiedVars) = 0;
SynchronizeResult(nsIXULTemplateResult* aResult) = 0;
};
#endif // nsXULTemplateBuilder_h__

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,371 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Robert Churchill <rjc@netscape.com>
* David Hyatt <hyatt@netscape.com>
* Chris Waterson <waterson@netscape.com>
* Pierre Phaneuf <pp@ludusdesign.com>
* Neil Deakin <enndeakin@sympatico.ca>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsXULTemplateQueryProcessorRDF_h__
#define nsXULTemplateQueryProcessorRDF_h__
#include "nsIContent.h"
#include "nsIRDFContainer.h"
#include "nsIRDFContainerUtils.h"
#include "nsIRDFDataSource.h"
#include "nsIRDFObserver.h"
#include "nsIRDFService.h"
#include "nsIXULTemplateBuilder.h"
#include "nsIXULTemplateQueryProcessor.h"
#include "nsICollation.h"
#include "nsCollationCID.h"
#include "nsFixedSizeAllocator.h"
#include "nsResourceSet.h"
#include "nsRuleNetwork.h"
#include "nsRDFQuery.h"
#include "nsRDFBinding.h"
#include "nsXULTemplateResultSetRDF.h"
#include "nsVoidArray.h"
#include "nsCOMArray.h"
#include "nsIArray.h"
#include "nsString.h"
#include "nsClassHashtable.h"
#include "nsRefPtrHashtable.h"
#include "prlog.h"
#ifdef PR_LOGGING
extern PRLogModuleInfo* gXULTemplateLog;
#endif
class nsIRDFCompositeDataSource;
class nsXULTemplateResultRDF;
/**
* An object that generates results from a query on an RDF graph
*/
class nsXULTemplateQueryProcessorRDF : public nsIXULTemplateQueryProcessor,
public nsIRDFObserver
{
public:
nsXULTemplateQueryProcessorRDF();
~nsXULTemplateQueryProcessorRDF();
nsresult InitGlobals();
// nsISupports interface
NS_DECL_ISUPPORTS
// nsIXULTemplateQueryProcessor interface
NS_DECL_NSIXULTEMPLATEQUERYPROCESSOR
// nsIRDFObserver interface
NS_DECL_NSIRDFOBSERVER
/*
* Propagate all changes through the rule network when an assertion is
* added to the graph, adding any new results.
*/
nsresult
Propagate(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget);
/*
* Retract all changes through the rule network when an assertion is
* removed from the graph, removing any results that no longer match.
*/
nsresult
Retract(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget);
/*
* Synchronize results when the graph changes, updating their bindings.
*/
nsresult
SynchronizeAll(nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aOldTarget,
nsIRDFNode* aNewTarget);
/*
* Return true if a resource is a container
*/
nsresult
CheckContainer(nsIRDFResource* aTargetResource,
PRBool* aIsContainer);
/*
* Check if a resource does not have any children
*/
nsresult
CheckEmpty(nsIRDFResource* aTargetResource,
PRBool* aIsEmpty);
/*
* Compute the containment properties which are additional arcs which
* indicate that a node is a container, in additional to the RDF container
* tests. The computed list is stored in mContainmentProperties
*/
nsresult
ComputeContainmentProperties(nsIDOMNode* aRootNode);
/**
* Compile a query that uses the extended template syntax. The last
* compiled node of the query is returned as aLastNode. This node will
* have been added to mAllTests which owns the node.
*/
nsresult
CompileExtendedQuery(nsRDFQuery* aQuery,
nsIContent* aConditions,
TestNode** aLastNode);
/**
* Compile a single query child and return the compiled node in aResult.
* This node will have been added to mAllTests which owns the node and
* set as a child of aParentNode.
*/
virtual nsresult
CompileQueryChild(nsIAtom* aTag,
nsRDFQuery* aQuery,
nsIContent* aConditions,
TestNode* aParentNode,
TestNode** aResult);
/**
* Parse the value of a property test assertion for a condition or a simple
* rule based on the parseType attribute into the appropriate literal type.
*/
nsresult ParseLiteral(const nsString& aParseType,
const nsString& aValue,
nsIRDFNode** aResult);
/**
* Compile a <triple> condition and return the compiled node in aResult.
* This node will have been added to mAllTests which owns the node and
* set as a child of aParentNode.
*/
nsresult
CompileTripleCondition(nsRDFQuery* aQuery,
nsIContent* aCondition,
TestNode* aParentNode,
TestNode** aResult);
/**
* Compile a <member> condition and return the compiled node in aResult.
* This node will have been added to mAllTests which owns the node and
* set as a child of aParentNode.
*/
nsresult
CompileMemberCondition(nsRDFQuery* aQuery,
nsIContent* aCondition,
TestNode* aParentNode,
TestNode** aResult);
/**
* Add the default rules shared by all simple queries. This creates
* the content start node followed by a member test. The member TestNode
* is returned in aChildNode. Both nodes will have been added to mAllTests
* which owns the nodes.
*/
nsresult
AddDefaultSimpleRules(nsRDFQuery* aQuery,
TestNode** aChildNode);
/**
* Compile a query that's specified using the simple template
* syntax. Each TestNode is created in a chain, the last compiled node
* is retured as aLastNode. All nodes will have been added to mAllTests
* which owns the nodes.
*/
nsresult
CompileSimpleQuery(nsRDFQuery* aQuery,
nsIContent* aQueryElement,
TestNode** aLastNode);
RDFBindingSet*
GetBindingsForRule(nsIDOMNode* aRule);
/*
* Indicate that a result is dependant on a particular resource. When an
* assertion is added to or removed from the graph involving that
* resource, that result must be recalculated.
*/
nsresult
AddBindingDependency(nsXULTemplateResultRDF* aResult,
nsIRDFResource* aResource);
/**
* Remove a dependency a result has on a particular resource.
*/
nsresult
RemoveBindingDependency(nsXULTemplateResultRDF* aResult,
nsIRDFResource* aResource);
/**
* A memory element is a hash of an RDF triple. One exists for each triple
* that was involved in generating a result. This function adds this to a
* map, keyed by memory element, when the value is a list of results that
* depend on that memory element. When an RDF triple is removed from the
* datasource, RetractElement is called, and this map is examined to
* determine which results are no longer valid.
*/
nsresult
AddMemoryElements(const Instantiation& aInst,
nsXULTemplateResultRDF* aResult);
/**
* Remove the memory elements associated with a result when the result is
* no longer being used.
*/
nsresult
RemoveMemoryElements(const Instantiation& aInst,
nsXULTemplateResultRDF* aResult);
/**
* Remove the results associated with a memory element since the
* RDF triple the memory element is a hash of has been removed.
*/
void RetractElement(const MemoryElement& aMemoryElement);
nsIRDFDataSource* GetDataSource() { return mDB; }
nsIXULTemplateBuilder* GetBuilder() { return mBuilder; }
nsResourceSet& ContainmentProperties() { return mContainmentProperties; }
#ifdef PR_LOGGING
nsresult
Log(const char* aOperation,
nsIRDFResource* aSource,
nsIRDFResource* aProperty,
nsIRDFNode* aTarget);
#define LOG(_op, _src, _prop, _targ) \
Log(_op, _src, _prop, _targ)
#else
#define LOG(_op, _src, _prop, _targ)
#endif
protected:
// We are an observer of the composite datasource. The cycle is
// broken when the document is destroyed.
nsCOMPtr<nsIRDFDataSource> mDB;
// weak reference to the builder, cleared when the document is destroyed
nsIXULTemplateBuilder* mBuilder;
// true if the query processor has been initialized
PRBool mQueryProcessorRDFInited;
// true if results have been generated. Once set, bindings can no longer
// be added. If they were, the binding value arrays for results that have
// already been generated would be the wrong size
PRBool mGenerationStarted;
// nesting level for RDF batch notifications
PRInt32 mUpdateBatchNest;
// containment properties that are checked to determine if a resource is
// a container
nsResourceSet mContainmentProperties;
// the end node of the default simple node hierarchy
TestNode* mSimpleRuleMemberTest;
// fixed size allocator used to allocate rule network structures
nsFixedSizeAllocator mPool;
// the last ref that was calculated, used for simple rules
nsCOMPtr<nsIXULTemplateResult> mLastRef;
/**
* A map between nsIRDFNodes that form the left-hand side (the subject) of
* a <binding> and an array of nsIXULTemplateResults. When a new assertion
* is added to the graph involving a particular rdf node, it is looked up
* in this binding map. If it exists, the corresponding results must then
* be synchronized.
*/
nsClassHashtable<nsISupportsHashKey,
nsCOMArray<nsXULTemplateResultRDF> > mBindingDependencies;
/**
* A map between memory elements and an array of nsIXULTemplateResults.
* When a triple is unasserted from the graph, the corresponding results
* no longer match so they must be removed.
*/
nsClassHashtable<nsUint32HashKey,
nsCOMArray<nsXULTemplateResultRDF> > mMemoryElementToResultMap;
// map of the rules to the bindings for those rules.
// XXXndeakin this might be better just as an array since there is usually
// ten or fewer rules
nsRefPtrHashtable<nsISupportsHashKey, RDFBindingSet> mRuleToBindingsMap;
/**
* The queries
*/
nsCOMArray<nsITemplateRDFQuery> mQueries;
/**
* All of the RDF tests in the rule network, which are checked when a new
* assertion is added to the graph. This is a subset of mAllTests, which
* also includes non-RDF tests.
*/
ReteNodeSet mRDFTests;
/**
* All of the tests in the rule network, owned by this list
*/
ReteNodeSet mAllTests;
// pseudo-constants
static nsrefcnt gRefCnt;
public:
static nsIRDFService* gRDFService;
static nsIRDFContainerUtils* gRDFContainerUtils;
nsFixedSizeAllocator& GetPool() { return mPool; }
};
#endif // nsXULTemplateQueryProcessorRDF_h__

Просмотреть файл

@ -0,0 +1,213 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Neil Deakin
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsXULTemplateResultRDF.h"
#include "nsXULContentUtils.h"
// XXXndeakin for some reason, making this class have classinfo breaks trees.
//#include "nsIDOMClassInfo.h"
NS_IMPL_ISUPPORTS1(nsXULTemplateResultRDF, nsIXULTemplateResult)
nsXULTemplateResultRDF::nsXULTemplateResultRDF(nsIRDFResource* aNode)
: mQuery(nsnull),
mNode(aNode)
{
}
nsXULTemplateResultRDF::nsXULTemplateResultRDF(nsRDFQuery* aQuery,
const Instantiation& aInst,
nsIRDFResource *aNode)
: mQuery(aQuery),
mNode(aNode),
mInst(aInst)
{
}
nsXULTemplateResultRDF::~nsXULTemplateResultRDF()
{
}
NS_IMETHODIMP
nsXULTemplateResultRDF::GetIsContainer(PRBool* aIsContainer)
{
*aIsContainer = PR_FALSE;
if (mNode) {
nsXULTemplateQueryProcessorRDF* processor = GetProcessor();
if (processor)
return processor->CheckContainer(mNode, aIsContainer);
}
return NS_OK;
}
NS_IMETHODIMP
nsXULTemplateResultRDF::GetIsEmpty(PRBool* aIsEmpty)
{
*aIsEmpty = PR_TRUE;
if (mNode) {
nsXULTemplateQueryProcessorRDF* processor = GetProcessor();
if (processor)
return processor->CheckEmpty(mNode, aIsEmpty);
}
return NS_OK;
}
NS_IMETHODIMP
nsXULTemplateResultRDF::GetMayProcessChildren(PRBool* aMayProcessChildren)
{
// RDF always allows recursion
*aMayProcessChildren = PR_TRUE;
return NS_OK;
}
NS_IMETHODIMP
nsXULTemplateResultRDF::GetId(nsAString& aId)
{
if (! mNode)
return NS_ERROR_FAILURE;
const char* uri;
mNode->GetValueConst(&uri);
CopyUTF8toUTF16(uri, aId);
return NS_OK;
}
NS_IMETHODIMP
nsXULTemplateResultRDF::GetResource(nsIRDFResource** aResource)
{
*aResource = mNode;
NS_IF_ADDREF(*aResource);
return NS_OK;
}
NS_IMETHODIMP
nsXULTemplateResultRDF::GetBindingFor(nsIAtom* aVar, nsAString& aValue)
{
nsCOMPtr<nsIRDFNode> val;
GetAssignment(aVar, getter_AddRefs(val));
return nsXULContentUtils::GetTextForNode(val, aValue);
}
NS_IMETHODIMP
nsXULTemplateResultRDF::GetBindingObjectFor(nsIAtom* aVar, nsISupports** aValue)
{
GetAssignment(aVar, (nsIRDFNode **)aValue);
return NS_OK;
}
NS_IMETHODIMP
nsXULTemplateResultRDF::RuleMatched(nsISupports* aQuery, nsIDOMNode* aRuleNode)
{
// when a rule matches, set the bindings that must be used.
nsXULTemplateQueryProcessorRDF* processor = GetProcessor();
if (processor) {
RDFBindingSet* bindings = processor->GetBindingsForRule(aRuleNode);
if (bindings) {
nsresult rv = mBindingValues.SetBindingSet(bindings);
if (NS_FAILED(rv))
return rv;
bindings->AddDependencies(mNode, this);
}
}
return NS_OK;
}
NS_IMETHODIMP
nsXULTemplateResultRDF::HasBeenRemoved()
{
// when a result is no longer used, clean up the dependencies and
// memory elements that refer to it
mBindingValues.RemoveDependencies(mNode, this);
nsXULTemplateQueryProcessorRDF* processor = GetProcessor();
if (processor)
processor->RemoveMemoryElements(mInst, this);
return NS_OK;
}
void
nsXULTemplateResultRDF::GetAssignment(nsIAtom* aVar, nsIRDFNode** aValue)
{
// look up a variable in the assignments map
*aValue = nsnull;
mInst.mAssignments.GetAssignmentFor(aVar, aValue);
// if not found, look up the variable in the bindings
if (! *aValue)
mBindingValues.GetAssignmentFor(this, aVar, aValue);
}
PRBool
nsXULTemplateResultRDF::SyncAssignments(nsIRDFResource* aSubject,
nsIRDFResource* aPredicate,
nsIRDFNode* aTarget)
{
// synchronize the bindings when an assertion is added or removed
RDFBindingSet* bindingset = mBindingValues.GetBindingSet();
if (bindingset) {
return bindingset->SyncAssignments(aSubject, aPredicate, aTarget,
(aSubject == mNode) ? mQuery->GetMemberVariable() : nsnull,
this, mBindingValues);
}
return PR_FALSE;
}
PRBool
nsXULTemplateResultRDF::HasMemoryElement(const MemoryElement& aMemoryElement)
{
MemoryElementSet::ConstIterator last = mInst.mSupport.Last();
for (MemoryElementSet::ConstIterator element = mInst.mSupport.First();
element != last; ++element) {
if ((*element).Equals(aMemoryElement))
return PR_TRUE;
}
return PR_FALSE;
}

Просмотреть файл

@ -0,0 +1,111 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Neil Deakin
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsXULTemplateResultRDF_h__
#define nsXULTemplateResultRDF_h__
#include "nsCOMPtr.h"
#include "nsIRDFResource.h"
#include "nsXULTemplateQueryProcessorRDF.h"
#include "nsRDFQuery.h"
#include "nsRuleNetwork.h"
#include "nsIXULTemplateResult.h"
#include "nsRDFBinding.h"
/**
* A single result of a query on an RDF graph
*/
class nsXULTemplateResultRDF : public nsIXULTemplateResult
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIXULTEMPLATERESULT
nsXULTemplateResultRDF(nsIRDFResource* aNode);
nsXULTemplateResultRDF(nsRDFQuery* aQuery,
const Instantiation& aInst,
nsIRDFResource* aNode);
~nsXULTemplateResultRDF();
nsITemplateRDFQuery* Query() { return mQuery; }
nsXULTemplateQueryProcessorRDF* GetProcessor()
{
return (mQuery ? mQuery->Processor() : nsnull);
}
/**
* Get the value of a variable, first by looking in the assignments and
* then the bindings
*/
void
GetAssignment(nsIAtom* aVar, nsIRDFNode** aValue);
/**
* Synchronize the bindings after a change in the RDF graph. Bindings that
* would be affected will be assigned appropriately based on the change.
*/
PRBool
SyncAssignments(nsIRDFResource* aSubject,
nsIRDFResource* aPredicate,
nsIRDFNode* aTarget);
/**
* Return true if the result has an instantiation involving a particular
* memory element.
*/
PRBool
HasMemoryElement(const MemoryElement& aMemoryElement);
protected:
// query that generated the result
nsCOMPtr<nsITemplateRDFQuery> mQuery;
// resource node
nsCOMPtr<nsIRDFResource> mNode;
// data computed from query
Instantiation mInst;
// extra assignments made by rules (<binding> tags)
nsBindingValues mBindingValues;
};
#endif // nsXULTemplateResultRDF_h__

Просмотреть файл

@ -0,0 +1,114 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Neil Deakin
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsXULTemplateResultSetRDF.h"
#include "nsXULTemplateQueryProcessorRDF.h"
NS_IMPL_ISUPPORTS1(nsXULTemplateResultSetRDF, nsISimpleEnumerator)
NS_IMETHODIMP
nsXULTemplateResultSetRDF::HasMoreElements(PRBool *aResult)
{
*aResult = PR_TRUE;
nsCOMPtr<nsIRDFNode> node;
if (! mInstantiations || ! mQuery) {
*aResult = PR_FALSE;
return NS_OK;
}
if (mCheckedNext) {
if (!mCurrent || mCurrent == &(mInstantiations->mHead))
*aResult = PR_FALSE;
return NS_OK;
}
mCheckedNext = PR_TRUE;
do {
if (mCurrent) {
mCurrent = mCurrent->mNext;
if (mCurrent == &(mInstantiations->mHead)) {
*aResult = PR_FALSE;
return NS_OK;
}
}
else {
*aResult = ! mInstantiations->Empty();
if (*aResult)
mCurrent = mInstantiations->mHead.mNext;
}
// get the value of the member variable. If it is not set, skip
// the result and move on to the next result
if (mCurrent) {
mCurrent->mInstantiation.mAssignments.
GetAssignmentFor(mQuery->mMemberVariable, getter_AddRefs(node));
}
// only resources may be used as results
mResource = do_QueryInterface(node);
} while (! mResource);
return NS_OK;
}
NS_IMETHODIMP
nsXULTemplateResultSetRDF::GetNext(nsISupports **aResult)
{
if (!aResult)
return NS_ERROR_NULL_POINTER;
if (!mCurrent || !mCheckedNext)
return NS_ERROR_FAILURE;
nsRefPtr<nsXULTemplateResultRDF> nextresult =
new nsXULTemplateResultRDF(mQuery, mCurrent->mInstantiation, mResource);
if (!nextresult)
return NS_ERROR_OUT_OF_MEMORY;
// add the supporting memory elements to the processor's map. These are
// used to remove the results when an assertion is removed from the graph
mProcessor->AddMemoryElements(mCurrent->mInstantiation, nextresult);
mCheckedNext = PR_FALSE;
*aResult = nextresult;
NS_ADDREF(*aResult);
return NS_OK;
}

Просмотреть файл

@ -0,0 +1,91 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Neil Deakin
* Portions created by the Initial Developer are Copyright (C) 2005
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsXULTemplateResultSetRDF_h__
#define nsXULTemplateResultSetRDF_h__
#include "nsFixedSizeAllocator.h"
#include "nsISimpleEnumerator.h"
#include "nsRuleNetwork.h"
#include "nsRDFQuery.h"
#include "nsXULTemplateResultRDF.h"
class nsXULTemplateQueryProcessorRDF;
class nsXULTemplateResultRDF;
/**
* An enumerator used to iterate over a set of results.
*/
class nsXULTemplateResultSetRDF : public nsISimpleEnumerator
{
private:
nsXULTemplateQueryProcessorRDF* mProcessor;
nsRDFQuery* mQuery;
const InstantiationSet* mInstantiations;
nsCOMPtr<nsIRDFResource> mResource;
InstantiationSet::List *mCurrent;
PRBool mCheckedNext;
public:
// nsISupports interface
NS_DECL_ISUPPORTS
// nsISimpleEnumerator interface
NS_DECL_NSISIMPLEENUMERATOR
nsXULTemplateResultSetRDF(nsXULTemplateQueryProcessorRDF *aProcessor,
nsRDFQuery* aQuery,
const InstantiationSet* aInstantiations)
: mProcessor(aProcessor),
mQuery(aQuery),
mInstantiations(aInstantiations),
mCurrent(nsnull),
mCheckedNext(PR_FALSE)
{ }
~nsXULTemplateResultSetRDF()
{
delete mInstantiations;
}
};
#endif // nsXULTemplateResultSetRDF_h__

Разница между файлами не показана из-за своего большого размера Загрузить разницу