Back to index

lightning-sunbird  0.9+nobinonly
nsTemplateRule.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Chris Waterson <waterson@netscape.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsTemplateRule.h"
00040 #include "nsTemplateMatch.h"
00041 #include "nsRuleNetwork.h"
00042 #include "nsConflictSet.h"
00043 #include "nsVoidArray.h"
00044 
00045 nsTemplateRule::~nsTemplateRule()
00046 {
00047     MOZ_COUNT_DTOR(nsTemplateRule);
00048 
00049     while (mBindings) {
00050         Binding* doomed = mBindings;
00051         mBindings = mBindings->mNext;
00052         delete doomed;
00053     }
00054 }
00055 
00056 
00057 nsresult
00058 nsTemplateRule::GetContent(nsIContent** aResult) const
00059 {
00060     *aResult = mContent.get();
00061     NS_IF_ADDREF(*aResult);
00062     return NS_OK;
00063 }
00064 
00065 PRBool
00066 nsTemplateRule::HasBinding(PRInt32 aSourceVariable,
00067                            nsIRDFResource* aProperty,
00068                            PRInt32 aTargetVariable) const
00069 {
00070     for (Binding* binding = mBindings; binding != nsnull; binding = binding->mNext) {
00071         if ((binding->mSourceVariable == aSourceVariable) &&
00072             (binding->mProperty == aProperty) &&
00073             (binding->mTargetVariable == aTargetVariable))
00074             return PR_TRUE;
00075     }
00076 
00077     return PR_FALSE;
00078 }
00079 
00080 nsresult
00081 nsTemplateRule::AddBinding(PRInt32 aSourceVariable,
00082                            nsIRDFResource* aProperty,
00083                            PRInt32 aTargetVariable)
00084 {
00085     NS_PRECONDITION(aSourceVariable != 0, "no source variable!");
00086     if (! aSourceVariable)
00087         return NS_ERROR_INVALID_ARG;
00088 
00089     NS_PRECONDITION(aProperty != nsnull, "null ptr");
00090     if (! aProperty)
00091         return NS_ERROR_INVALID_ARG;
00092 
00093     NS_PRECONDITION(aTargetVariable != 0, "no target variable!");
00094     if (! aTargetVariable)
00095         return NS_ERROR_INVALID_ARG;
00096 
00097     NS_ASSERTION(! HasBinding(aSourceVariable, aProperty, aTargetVariable),
00098                  "binding added twice");
00099 
00100     Binding* newbinding = new Binding;
00101     if (! newbinding)
00102         return NS_ERROR_OUT_OF_MEMORY;
00103 
00104     newbinding->mSourceVariable = aSourceVariable;
00105     newbinding->mProperty       = aProperty;
00106     newbinding->mTargetVariable = aTargetVariable;
00107     newbinding->mParent         = nsnull;
00108 
00109     Binding* binding = mBindings;
00110     Binding** link = &mBindings;
00111 
00112     // Insert it at the end, unless we detect that an existing
00113     // binding's source is dependent on the newbinding's target.
00114     //
00115     // XXXwaterson this isn't enough to make sure that we get all of
00116     // the dependencies worked out right, but it'll do for now. For
00117     // example, if you have (ab, bc, cd), and insert them in the order
00118     // (cd, ab, bc), you'll get (bc, cd, ab). The good news is, if the
00119     // person uses a natural ordering when writing the XUL, it'll all
00120     // work out ok.
00121     while (binding) {
00122         if (binding->mSourceVariable == newbinding->mTargetVariable) {
00123             binding->mParent = newbinding;
00124             break;
00125         }
00126         else if (binding->mTargetVariable == newbinding->mSourceVariable) {
00127             newbinding->mParent = binding;
00128         }
00129 
00130         link = &binding->mNext;
00131         binding = binding->mNext;
00132     }
00133 
00134     // Insert the newbinding
00135     *link = newbinding;
00136     newbinding->mNext = binding;
00137     return NS_OK;
00138 }
00139 
00140 PRBool
00141 nsTemplateRule::DependsOn(PRInt32 aChildVariable, PRInt32 aParentVariable) const
00142 {
00143     // Determine whether the value for aChildVariable will depend on
00144     // the value for aParentVariable by examining the rule's bindings.
00145     Binding* child = mBindings;
00146     while ((child != nsnull) && (child->mSourceVariable != aChildVariable))
00147         child = child->mNext;
00148 
00149     if (! child)
00150         return PR_FALSE;
00151 
00152     Binding* parent = child->mParent;
00153     while (parent != nsnull) {
00154         if (parent->mSourceVariable == aParentVariable)
00155             return PR_TRUE;
00156 
00157         parent = parent->mParent;
00158     }
00159 
00160     return PR_FALSE;
00161 }
00162 
00163 //----------------------------------------------------------------------
00164 //
00165 // nsTemplateRule
00166 //
00167 
00168 nsresult
00169 nsTemplateRule::InitBindings(nsConflictSet& aConflictSet, nsTemplateMatch* aMatch) const
00170 {
00171     // Initialize a match's binding dependencies, so we can handle
00172     // updates and queries later.
00173 
00174     for (Binding* binding = mBindings; binding != nsnull; binding = binding->mNext) {
00175         // Add a dependency for bindings whose source variable comes
00176         // from one of the <conditions>.
00177         Value sourceValue;
00178         PRBool hasBinding =
00179             aMatch->mInstantiation.mAssignments.GetAssignmentFor(binding->mSourceVariable, &sourceValue);
00180 
00181         if (hasBinding) {
00182             nsIRDFResource* source = VALUE_TO_IRDFRESOURCE(sourceValue);
00183             aMatch->mBindingDependencies.Add(source);
00184             aConflictSet.AddBindingDependency(aMatch, source);
00185         }
00186 
00187         // If this binding is dependant on another binding, then we
00188         // need to eagerly compute its source variable's assignment.
00189         if (binding->mParent) {
00190             Value value;
00191             ComputeAssignmentFor(aConflictSet, aMatch, binding->mSourceVariable, &value);
00192         }
00193     }
00194 
00195     return NS_OK;
00196 }
00197 
00198 nsresult
00199 nsTemplateRule::RecomputeBindings(nsConflictSet& aConflictSet,
00200                                   nsTemplateMatch* aMatch,
00201                                   nsIRDFResource* aSource,
00202                                   nsIRDFResource* aProperty,
00203                                   nsIRDFNode* aOldTarget,
00204                                   nsIRDFNode* aNewTarget,
00205                                   VariableSet& aModifiedVars) const
00206 {
00207     // Given a match with a source, property, old target, and new
00208     // target, compute the minimal changes to the match's bindings.
00209 
00210     // A temporary, mutable collection for holding all of the
00211     // assignments that comprise the current match.
00212     nsAutoVoidArray assignments;
00213 
00214     {
00215         // Collect -all- of the assignments in match into a temporary,
00216         // mutable collection
00217         nsAssignmentSet::ConstIterator last = aMatch->mAssignments.Last();
00218         for (nsAssignmentSet::ConstIterator binding = aMatch->mAssignments.First(); binding != last; ++binding)
00219             assignments.AppendElement(new nsAssignment(*binding));
00220 
00221         // Truncate the match's assignments to only include
00222         // assignments made via condition tests. We'll add back
00223         // assignments as they are recomputed.
00224         aMatch->mAssignments = aMatch->mInstantiation.mAssignments;
00225     }
00226 
00227     PRInt32 i;
00228 
00229     // Iterate through each assignment, looking for the assignment
00230     // whose value corresponds to the source of the assertion that's
00231     // changing.
00232     for (i = 0; i < assignments.Count(); ++i) {
00233         nsAssignment* assignment = NS_STATIC_CAST(nsAssignment*, assignments[i]);
00234         if ((assignment->mValue.GetType() == Value::eISupports) &&
00235             (NS_STATIC_CAST(nsISupports*, assignment->mValue) == aSource)) {
00236 
00237             // ...When we find it, look for binding's whose source
00238             // variable depends on the assignment's variable
00239             for (Binding* binding = mBindings; binding != nsnull; binding = binding->mNext) {
00240                 if ((binding->mSourceVariable != assignment->mVariable) ||
00241                     (binding->mProperty.get() != aProperty))
00242                     continue;
00243 
00244                 // Found one. Now we iterate through the assignments,
00245                 // doing fixup.
00246                 for (PRInt32 j = 0; j < assignments.Count(); ++j) {
00247                     nsAssignment* dependent = NS_STATIC_CAST(nsAssignment*, assignments[j]);
00248                     if (dependent->mVariable == binding->mTargetVariable) {
00249                         // The assignment's variable is the target
00250                         // varible for the binding: we can update it
00251                         // in-place.
00252                         dependent->mValue = Value(aNewTarget);
00253                         aModifiedVars.Add(dependent->mVariable);
00254                     }
00255                     else if (DependsOn(dependent->mVariable, binding->mTargetVariable)) {
00256                         // The assignment's variable depends on the
00257                         // binding's target variable, which is
00258                         // changing. Rip it out.
00259                         nsIRDFResource* target = VALUE_TO_IRDFRESOURCE(dependent->mValue);
00260                         aMatch->mBindingDependencies.Remove(target);
00261                         aConflictSet.RemoveBindingDependency(aMatch, target);
00262 
00263                         delete dependent;
00264                         assignments.RemoveElementAt(j--);
00265 
00266                         aModifiedVars.Add(dependent->mVariable);
00267                     }
00268                     else {
00269                         // The dependent variable is irrelevant. Leave
00270                         // it alone.
00271                     }
00272                 }
00273             }
00274         }
00275     }
00276 
00277     // Now our set of assignments will contain the original
00278     // assignments from the conditions, any unchanged assignments, and
00279     // the single assignment that was updated by iterating through the
00280     // bindings.
00281     //
00282     // Add these assignments *back* to the match (modulo the ones
00283     // already in the conditions).
00284     //
00285     // The values for any dependent assignments that we've ripped out
00286     // will be computed the next time that somebody asks us for them.
00287     for (i = assignments.Count() - 1; i >= 0; --i) {
00288         nsAssignment* assignment = NS_STATIC_CAST(nsAssignment*, assignments[i]);
00289 
00290         // Only add it if it's not already in the match's conditions
00291         if (! aMatch->mInstantiation.mAssignments.HasAssignment(*assignment)) {
00292             aMatch->mAssignments.Add(*assignment);
00293         }
00294 
00295         delete assignment;
00296     }
00297 
00298     return NS_OK;
00299 }
00300 
00301 PRBool
00302 nsTemplateRule::ComputeAssignmentFor(nsConflictSet& aConflictSet,
00303                                      nsTemplateMatch* aMatch,
00304                                      PRInt32 aVariable,
00305                                      Value* aValue) const
00306 {
00307     // Compute the value assignment for an arbitrary variable in a
00308     // match. Potentially fill in dependencies if they haven't been
00309     // resolved yet.
00310     for (Binding* binding = mBindings; binding != nsnull; binding = binding->mNext) {
00311         if (binding->mTargetVariable != aVariable)
00312             continue;
00313 
00314         // Potentially recur to find the value of the source.
00315         //
00316         // XXXwaterson this is sloppy, and could be dealt with more
00317         // directly by following binding->mParent.
00318         Value sourceValue;
00319         PRBool hasSourceAssignment =
00320             aMatch->GetAssignmentFor(aConflictSet, binding->mSourceVariable, &sourceValue);
00321 
00322         if (! hasSourceAssignment)
00323             return PR_FALSE;
00324 
00325         nsCOMPtr<nsIRDFNode> target;
00326 
00327         nsIRDFResource* source = VALUE_TO_IRDFRESOURCE(sourceValue);
00328 
00329         if (source) {
00330             mDataSource->GetTarget(source,
00331                                    binding->mProperty,
00332                                    PR_TRUE,
00333                                    getter_AddRefs(target));
00334 
00335             // Store the assignment in the match so we won't need to
00336             // retrieve it again.
00337             nsAssignment assignment(binding->mTargetVariable, Value(target.get()));
00338             aMatch->mAssignments.Add(assignment);
00339 
00340             // Add a dependency on the source, so we'll recompute the
00341             // assignment if somebody tweaks it.
00342             aMatch->mBindingDependencies.Add(source);
00343             aConflictSet.AddBindingDependency(aMatch, source);
00344         }
00345 
00346         *aValue = target.get();
00347         return PR_TRUE;
00348     }
00349 
00350     return PR_FALSE;
00351 }