You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@shindig.apache.org by aw...@apache.org on 2009/02/24 01:03:55 UTC

svn commit: r747227 - in /incubator/shindig/trunk: features/ features/opensocial-base/ features/opensocial-data-context/ features/opensocial-data/ features/opensocial-reference/ java/gadgets/src/main/java/org/apache/shindig/gadgets/ java/gadgets/src/ma...

Author: awiner
Date: Tue Feb 24 00:03:55 2009
New Revision: 747227

URL: http://svn.apache.org/viewvc?rev=747227&view=rev
Log:
SHINDIG-739: Data pipelining (in part)
- Extract DataContext api into an opensocial-data-context feature, depended on by opensocial-data
- If server-side data pipelining succeeds completely, remove the opensocial-data feature and add the opensocial-data-context feature instead
- Rewrite DataContext to hide implementation better, and write test
- Add support for registerListener('*', ...) per spec
- Tweak other opensocial-related tests to work in JSUnit (not relying on 'window' variable)
- Fix name of os:MakeRequest element (now os:HttpRequest per spec)

Added:
    incubator/shindig/trunk/features/opensocial-data-context/
    incubator/shindig/trunk/features/opensocial-data-context/datacontext.js
    incubator/shindig/trunk/features/opensocial-data-context/datacontexttest.js
    incubator/shindig/trunk/features/opensocial-data-context/feature.xml
Modified:
    incubator/shindig/trunk/features/features.txt
    incubator/shindig/trunk/features/opensocial-base/jsonactivitytest.js
    incubator/shindig/trunk/features/opensocial-data/data.js
    incubator/shindig/trunk/features/opensocial-data/feature.xml
    incubator/shindig/trunk/features/opensocial-reference/activitytest.js
    incubator/shindig/trunk/features/opensocial-reference/opensocial.js
    incubator/shindig/trunk/features/pom.xml
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/Gadget.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderingContentRewriter.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/PipelineDataContentRewriter.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/RenderingContentRewriterTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/PipelineDataContentRewriterTest.java

Modified: incubator/shindig/trunk/features/features.txt
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/features.txt?rev=747227&r1=747226&r2=747227&view=diff
==============================================================================
--- incubator/shindig/trunk/features/features.txt (original)
+++ incubator/shindig/trunk/features/features.txt Tue Feb 24 00:03:55 2009
@@ -16,6 +16,7 @@
 features/opensocial-base/feature.xml
 features/opensocial-current/feature.xml
 features/opensocial-data/feature.xml
+features/opensocial-data-context/feature.xml
 features/opensocial-jsonrpc/feature.xml
 features/opensocial-reference/feature.xml
 features/opensocial-rest/feature.xml

Modified: incubator/shindig/trunk/features/opensocial-base/jsonactivitytest.js
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/opensocial-base/jsonactivitytest.js?rev=747227&r1=747226&r2=747227&view=diff
==============================================================================
--- incubator/shindig/trunk/features/opensocial-base/jsonactivitytest.js (original)
+++ incubator/shindig/trunk/features/opensocial-base/jsonactivitytest.js Tue Feb 24 00:03:55 2009
@@ -17,7 +17,6 @@
  */
 
 var gadgets = gadgets || {};
-var opensocial = opensocial || {};
 
 function JsonActivityTest(name) {
   TestCase.call(this, name);

Added: incubator/shindig/trunk/features/opensocial-data-context/datacontext.js
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/opensocial-data-context/datacontext.js?rev=747227&view=auto
==============================================================================
--- incubator/shindig/trunk/features/opensocial-data-context/datacontext.js (added)
+++ incubator/shindig/trunk/features/opensocial-data-context/datacontext.js Tue Feb 24 00:03:55 2009
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ 
+/**
+ * @fileoverview Implements the global implicit data context for containers.
+ */
+
+var opensocial = opensocial || {};
+
+/**
+ * @type {Object} The namespace declaration for this file.
+ */
+opensocial.data = opensocial.data || {};
+
+var osd = opensocial.data;
+
+/**
+ * @type {Object} Global DataContext to contain requested data sets.
+ */
+osd.DataContext = function() {
+  var listeners = [];
+  var dataSets = {};
+
+  /**
+   * Checks if the data for a map of keys is available.
+   * @param {Object<string, ?>} An map of keys to check.
+   * @return {boolean} Data for all the keys is present.
+   */
+  var isDataReady = function(keys) {
+    if (keys['*']) {
+      return true;
+    }
+    
+    for (var key in keys) {
+      if (typeof dataSets[key] === 'undefined') {
+        return false;
+      }
+    }
+    return true;
+  };
+    
+    
+  /**
+   * Fires a listener for a key, but only if the data is ready for other
+   * keys this listener is bound to.
+   * @param {Object} listener The listener object.
+   * @param {string} key The key that this listener is being fired for.
+   */
+  var maybeFireListener = function(listener, key) {
+    if (isDataReady(listener.keys)) {
+      listener.callback(key);
+    }
+  };
+    
+    
+  /**
+   * Scans all active listeners and fires off any callbacks that inserting this
+   * key satisfies.
+   * @param {string} key The key that was updated.
+   * @private
+   */
+  var fireCallbacks = function(key) {
+    for (var i = 0; i < listeners.length; ++i) {
+      var listener = listeners[i];
+      if (listener.keys[key] || listener.keys['*']) {
+        maybeFireListener(listener, key);
+      }
+    }
+  };
+
+
+  return {
+    /**
+     * Map of existing data.  This is used externally by both the
+     * opensocial-data and opensocial-templates feature, hence is
+     * not hidden.
+     */
+    dataSets_ : dataSets,
+    
+    
+    /**
+     * Registers a callback listener for a given set of keys.
+     * @param {string|Array.<string>} keys Key or set of keys to listen on.
+     * @param {Function(string)} callback Function to call when a listener is fired.
+     * TODO: Should return a value that can later be used to return
+     *     a value.
+     */
+    registerListener : function(keys, callback) {
+      var listener = {keys : {}, callback : callback};
+
+      if (typeof keys === 'string') {
+        listener.keys[keys] = true;
+      } else {
+        for (var i = 0; i < keys.length; i++) {
+          listener.keys[keys[i]] = true;
+        }
+      }
+      
+      listeners.push(listener);
+    
+      // Check to see if this one should fire immediately.
+      if (keys !== '*' && isDataReady(listener.keys)) {
+        window.setTimeout(function() {
+          listener.callback()
+        }, 1);
+      }
+    },
+    
+    
+    /**
+     * Retrieve a data set for a given key.
+     * @param {string} key Key for the requested data set.
+     * @return {Object} The data set object.
+     */
+    getDataSet : function(key) {
+      return dataSets[key];
+    },
+    
+    
+    /**
+     * Puts a data set into the global DataContext object. Fires listeners
+     * if they are satisfied by the associated key being inserted.
+     *
+     * Note that if this is passed a ResponseItem object, it will crack it open
+     * and extract the JSON payload of the wrapped API Object. This includes
+     * iterating over an array of API objects and extracting their JSON into a
+     * simple array structure.
+     *
+     * @param {string} key The key to associate with this object.
+     * @param {ResponseItem|Object} obj The data object.
+     */
+    putDataSet : function(key, obj) {
+      var data = obj;
+      if (typeof data === 'undefined' || data === null) {
+        return;
+      }
+    
+      // NOTE: This is ugly, but required since we need to get access
+      // to the JSON/Array payload of API responses.
+      // This will crack the actual API objects and extract their JSON payloads.
+      // TODO: this code block is not described by the spec, and should be removed.
+      // Developers using ResponseItems should call getData() themselves.
+      if (data.getData) {
+       data = data.getData();
+       if (data.array_) {
+         var out = [];
+         for (var i = 0; i < data.array_.length; i++) {
+           out.push(data.array_[i].fields_);
+         }
+         data = out;
+       } else {
+         data = data.fields_ || data;
+       }
+      }
+    
+      dataSets[key] = data;
+      fireCallbacks(key);
+    },    
+  }
+}();
+
+
+/**
+ * Accessor to the shared, global DataContext.
+ */
+osd.getDataContext = function() {
+  return opensocial.data.DataContext;
+};
+

Added: incubator/shindig/trunk/features/opensocial-data-context/datacontexttest.js
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/opensocial-data-context/datacontexttest.js?rev=747227&view=auto
==============================================================================
--- incubator/shindig/trunk/features/opensocial-data-context/datacontexttest.js (added)
+++ incubator/shindig/trunk/features/opensocial-data-context/datacontexttest.js Tue Feb 24 00:03:55 2009
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * @fileoverview
+ *
+ * Unittests for the opensocial-data-context feature.
+ */
+function DataContextTest(name) {
+  TestCase.call(this, name);
+}
+
+DataContextTest.inherits(TestCase);
+
+DataContextTest.prototype.setUp = function() {
+};
+
+DataContextTest.prototype.tearDown = function() {
+  var dataSets = osd.getDataContext().dataSets_;
+  for (var key in dataSets) {
+    if (dataSets.hasOwnProperty(key)) {
+      delete dataSets[key];
+    }
+  }
+};
+
+DataContextTest.prototype.testPutDataSet = function() {
+  var context = osd.getDataContext();
+
+  context.putDataSet('key', 'value');
+  this.assertEquals('value', context.getDataSet('key'));
+  
+  // Test that putting null and undefined don't change the value.
+  // TODO: this seems wrong;  why not support removing data?
+  context.putDataSet('key', null);
+  this.assertEquals('value', context.getDataSet('key'));
+
+  context.putDataSet('key', undefined);
+  this.assertEquals('value', context.getDataSet('key'));
+};
+
+
+/**
+ * Test registerListener()
+ */
+DataContextTest.prototype.testRegisterListener = function() {
+  var context = osd.getDataContext();
+  var listenerCalledWithKey = null;
+  var that = this;
+  var listener = function(key) {
+    listenerCalledWithKey = key;
+    that.assertNotNull(key);
+  };
+  
+  context.registerListener('key', listener);
+  this.assertNull(listenerCalledWithKey);
+  
+  context.putDataSet('other', 1);
+  this.assertNull(listenerCalledWithKey);
+
+  context.putDataSet('key', 2);
+  this.assertEquals('key', listenerCalledWithKey);
+
+  listenerCalledWithKey = null;
+  context.putDataSet('key', 3);
+  this.assertEquals('key', listenerCalledWithKey);
+}
+
+
+
+/**
+ * Test registerListener()
+ */
+DataContextTest.prototype.testRegisterListenerWithArray = function() {
+  var context = osd.getDataContext();
+  var listenerCalledWithKey = null;
+  var that = this;
+  var listener = function(key) {
+    listenerCalledWithKey = key;
+    that.assertNotNull(key);
+  };
+  
+  context.registerListener(['one', 'two'], listener);
+  this.assertNull(listenerCalledWithKey);
+  
+  context.putDataSet('one', 1);
+  this.assertNull(listenerCalledWithKey);
+
+  context.putDataSet('two', 2);
+  this.assertEquals('two', listenerCalledWithKey);
+
+  context.putDataSet('one', 3);
+  this.assertEquals('one', listenerCalledWithKey);
+}
+
+
+/**
+ * Test registerListener() with '*'
+ */
+DataContextTest.prototype.testRegisterListenerWithStar = function() {
+  var context = osd.getDataContext();
+  var listenerCalledWithKey = null;
+  var that = this;
+  var listener = function(key) {
+    listenerCalledWithKey = key;
+    that.assertNotNull(key);
+  };
+  
+  context.registerListener('*', listener);
+  this.assertNull(listenerCalledWithKey);
+  
+  context.putDataSet('one', 1);
+  this.assertEquals('one', listenerCalledWithKey);
+
+  context.putDataSet('two', 2);
+  this.assertEquals('two', listenerCalledWithKey);
+}
\ No newline at end of file

Added: incubator/shindig/trunk/features/opensocial-data-context/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/opensocial-data-context/feature.xml?rev=747227&view=auto
==============================================================================
--- incubator/shindig/trunk/features/opensocial-data-context/feature.xml (added)
+++ incubator/shindig/trunk/features/opensocial-data-context/feature.xml Tue Feb 24 00:03:55 2009
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<feature>
+  <name>opensocial-data-context</name>
+  <gadget>    
+    <script src="datacontext.js"></script>
+  </gadget>
+</feature>

Modified: incubator/shindig/trunk/features/opensocial-data/data.js
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/opensocial-data/data.js?rev=747227&r1=747226&r2=747227&view=diff
==============================================================================
--- incubator/shindig/trunk/features/opensocial-data/data.js (original)
+++ incubator/shindig/trunk/features/opensocial-data/data.js Tue Feb 24 00:03:55 2009
@@ -25,15 +25,6 @@
  *   URL parameter support.
  */
 
-var opensocial = window['opensocial'] || function() {};
-
-/**
- * @type {Object} The namespace declaration for this file.
- */
-opensocial.data = opensocial.data || {};
-
-var osd = opensocial.data;
-
 /**
  * @type {string} The key attribute constant.
  */
@@ -213,109 +204,6 @@
 };
 
 
-/**
- * @type {Object} Global DataContext to contain requested data sets.
- */
-osd.DataContext = {};
-
-osd.DataContext.listeners_ = [];
-
-osd.DataContext.dataSets_ = {};
-
-/**
- * Registers a callback listener for a given set of keys.
- * @param {string|Array.<string>} keys Key or set of keys to listen on.
- * @param {Function(string)} callback Function to call when a listener is fired.
- * TODO: Should return a value that can later be used to return
- *     a value.
- */
-osd.DataContext.registerListener = function(keys, callback) {
-  var listener = {};
-  listener.keys = {};
-
-  if (typeof(keys) == 'object') {
-    for (var i in keys) {
-      listener.keys[keys[i]] = true;
-    }
-  } else {
-    listener.keys[keys] = true;
-  }
-
-  listener.callback = callback;
-  opensocial.data.DataContext.listeners_.push(listener);
-
-  // Check to see if this one should fire immediately.
-  if (osd.DataContext.isDataReady(listener.keys)) {
-    window.setTimeout(function() {
-      listener.callback()
-    }, 1);
-  }
-};
-
-
-/**
- * Retrieve a data set for a given key.
- * @param {string} key Key for the requested data set.
- * @return {Object} The data set object.
- */
-osd.DataContext.getDataSet = function(key) {
-  return opensocial.data.DataContext.dataSets_[key];
-};
-
-
-/**
- * Checks if the data for a map of keys is available.
- * @param {Object<string, ?>} An map of keys to check.
- * @return {boolean} Data for all the keys is present.
- */
-osd.DataContext.isDataReady = function(keys) {
-  for (var key in keys) {
-    if (osd.DataContext.getDataSet(key) == null) {
-      return false;
-    }
-  }
-  return true;
-};
-
-
-/**
- * Puts a data set into the global DataContext object. Fires listeners
- * if they are satisfied by the associated key being inserted.
- *
- * Note that if this is passed a ResponseItem object, it will crack it open
- * and extract the JSON payload of the wrapped API Object. This includes
- * iterating over an array of API objects and extracting their JSON into a
- * simple array structure.
- *
- * @param {string} key The key to associate with this object.
- * @param {ResponseItem|Object} obj The data object.
- */
-osd.DataContext.putDataSet = function(key, obj) {
-  var data = obj;
-  if (typeof(data) == "undefined" || data === null) {
-    return;
-  }
-
-  // NOTE: This is ugly, but required since we need to get access
-  // to the JSON/Array payload of API responses.
-  // This will crack the actual API objects and extract their JSON payloads.
-  if (data.getData) {
-   data = data.getData();
-   if (data.array_) {
-     var out = [];
-     for (var i = 0; i < data.array_.length; i++) {
-       out.push(data.array_[i].fields_);
-     }
-     data = out;
-   } else {
-     data = data.fields_ || data;
-   }
-  }
-
-  opensocial.data.DataContext.dataSets_[key] = data;
-  opensocial.data.DataContext.fireCallbacks_(key);
-};
-
 
 /**
  * Evaluates a JS expression against the DataContext.
@@ -329,44 +217,6 @@
 
 
 /**
- * Fires a listener for a key, but only if the data is ready for other
- * keys this listener is bound to.
- * @param {Object} listener The listener object.
- * @param {string} key The key that this listener is being fired for.
- */
-osd.DataContext.maybeFireListener_ = function(listener, key) {
-  if (osd.DataContext.isDataReady(listener.keys)) {
-    listener.callback(key);
-  }
-};
-
-
-/**
- * Scans all active listeners and fires off any callbacks that inserting this
- * key satisfies.
- * @param {string} key The key that was updated.
- * @private
- */
-osd.DataContext.fireCallbacks_ = function(key) {
-  for (var i = 0; i < opensocial.data.DataContext.listeners_.length; ++i) {
-    var listener = opensocial.data.DataContext.listeners_[i];
-    if (listener.keys[key] != null) {
-      opensocial.data.DataContext.maybeFireListener_(listener, key);
-    }
-  }
-};
-
-
-/**
- * Accessor to the static DataContext object. At a later date multiple
- * DataContexts may be used.
- */
-osd.getDataContext = function() {
-  return opensocial.data.DataContext;
-};
-
-
-/**
  * @type {Object} Map of currently registered RequestDescriptors (by key).
  * @private
  */
@@ -695,7 +545,7 @@
     opensocial.data.addToCurrentAPIRequest(req, descriptor.key);
   });
 
-  osd.registerRequestHandler("os:DataRequest", function(descriptor) {
+  osd.registerRequestHandler("os:HttpRequest", function(descriptor) {
     var href = descriptor.getAttribute('href');
     var format = descriptor.getAttribute('format') || "json";
     var params = {};

Modified: incubator/shindig/trunk/features/opensocial-data/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/opensocial-data/feature.xml?rev=747227&r1=747226&r2=747227&view=diff
==============================================================================
--- incubator/shindig/trunk/features/opensocial-data/feature.xml (original)
+++ incubator/shindig/trunk/features/opensocial-data/feature.xml Tue Feb 24 00:03:55 2009
@@ -19,6 +19,7 @@
 -->
 <feature>
   <name>opensocial-data</name>
+  <dependency>opensocial-data-context</dependency>
   <dependency>opensocial-0.8</dependency>
   <dependency>xmlutil</dependency>
   <gadget>    

Modified: incubator/shindig/trunk/features/opensocial-reference/activitytest.js
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/opensocial-reference/activitytest.js?rev=747227&r1=747226&r2=747227&view=diff
==============================================================================
--- incubator/shindig/trunk/features/opensocial-reference/activitytest.js (original)
+++ incubator/shindig/trunk/features/opensocial-reference/activitytest.js Tue Feb 24 00:03:55 2009
@@ -17,7 +17,6 @@
  */
 
 var gadgets = gadgets || {};
-var opensocial = opensocial || {};
 
 function ActivityTest(name) {
   TestCase.call(this, name);

Modified: incubator/shindig/trunk/features/opensocial-reference/opensocial.js
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/opensocial-reference/opensocial.js?rev=747227&r1=747226&r2=747227&view=diff
==============================================================================
--- incubator/shindig/trunk/features/opensocial-reference/opensocial.js (original)
+++ incubator/shindig/trunk/features/opensocial-reference/opensocial.js Tue Feb 24 00:03:55 2009
@@ -36,7 +36,7 @@
  * @private
  * @constructor (note: a constructor for JsDoc purposes)
  */
-var opensocial = window['opensocial'] || {};
+var opensocial = opensocial || {};
 
 
 /**

Modified: incubator/shindig/trunk/features/pom.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/pom.xml?rev=747227&r1=747226&r2=747227&view=diff
==============================================================================
--- incubator/shindig/trunk/features/pom.xml (original)
+++ incubator/shindig/trunk/features/pom.xml Tue Feb 24 00:03:55 2009
@@ -80,6 +80,7 @@
                 <source>core.io/io.js</source>
                 <source>setprefs/setprefs.js</source>
                 <source>views/views.js</source>
+                <source>opensocial-data-context/datacontext.js</source>
                 <source>opensocial-reference/opensocial.js</source>
                 <source>opensocial-reference/activity.js</source>
                 <source>opensocial-reference/address.js</source>

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/Gadget.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/Gadget.java?rev=747227&r1=747226&r2=747227&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/Gadget.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/Gadget.java Tue Feb 24 00:03:55 2009
@@ -23,6 +23,10 @@
 import org.apache.shindig.gadgets.spec.View;
 
 import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+
+import com.google.common.collect.Sets;
 
 /**
  * Intermediary representation of all state associated with processing
@@ -33,6 +37,9 @@
   private GadgetSpec spec;
   private Collection<PreloadedData> preloads;
   private View currentView;
+  private Set<String> addedFeatures;
+  private Set<String> removedFeatures;
+
   /**
    * @param context The request that the gadget is being processed for.
    */
@@ -91,4 +98,37 @@
   public LocaleSpec getLocale() {
     return spec.getModulePrefs().getLocale(context.getLocale());
   }
+
+  
+  public void addFeature(String name) {
+    if (addedFeatures == null) {
+      addedFeatures = Sets.newHashSet();
+    }
+    
+    addedFeatures.add(name);
+  }
+  
+  public void removeFeature(String name) {
+    if (removedFeatures == null) {
+      removedFeatures = Sets.newHashSet();
+    }
+    
+    removedFeatures.add(name);
+  }
+  
+  public Set<String> getAddedFeatures() {
+    if (addedFeatures == null) {
+      return Collections.<String>emptySet();
+    }
+    
+    return addedFeatures;
+  }
+
+  public Set<String> getRemovedFeatures() {
+    if (removedFeatures == null) {
+      return Collections.<String>emptySet();
+    }
+    
+    return removedFeatures;
+  }
 }
\ No newline at end of file

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderingContentRewriter.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderingContentRewriter.java?rev=747227&r1=747226&r2=747227&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderingContentRewriter.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderingContentRewriter.java Tue Feb 24 00:03:55 2009
@@ -41,7 +41,6 @@
 import org.apache.shindig.gadgets.rewrite.MutableContent;
 import org.apache.shindig.gadgets.rewrite.RewriterResults;
 import org.apache.shindig.gadgets.spec.Feature;
-import org.apache.shindig.gadgets.spec.GadgetSpec;
 import org.apache.shindig.gadgets.spec.LocaleSpec;
 import org.apache.shindig.gadgets.spec.MessageBundle;
 import org.apache.shindig.gadgets.spec.ModulePrefs;
@@ -214,7 +213,6 @@
     // TODO: If there isn't any js in the document, we can skip this. Unfortunately, that means
     // both script tags (easy to detect) and event handlers (much more complex).
     GadgetContext context = gadget.getContext();
-    GadgetSpec spec = gadget.getSpec();
     String forcedLibs = context.getParameter("libs");
 
     // List of libraries we need
@@ -248,7 +246,7 @@
     // Inline any libs that weren't forced. The ugly context switch between inline and external
     // Js is needed to allow both inline and external scripts declared in feature.xml.
     String container = context.getContainer();
-    Collection<GadgetFeature> features = getFeatures(spec, forced);
+    Collection<GadgetFeature> features = getFeatures(gadget, forced);
 
     // Precalculate the maximum length in order to avoid excessive garbage generation.
     int size = 0;
@@ -304,13 +302,16 @@
    * @param forced Forced libraries; added in addition to those found in the spec. Defaults to
    * "core".
    */
-  private Collection<GadgetFeature> getFeatures(GadgetSpec spec, Collection<String> forced)
+  private Collection<GadgetFeature> getFeatures(Gadget gadget, Collection<String> forced)
       throws GadgetException {
-    Map<String, Feature> features = spec.getModulePrefs().getFeatures();
+    Map<String, Feature> features = gadget.getSpec().getModulePrefs().getFeatures();
     Set<String> libs = Sets.newHashSet(features.keySet());
     if (!forced.isEmpty()) {
       libs.addAll(forced);
     }
+    
+    libs.removeAll(gadget.getRemovedFeatures());
+    libs.addAll(gadget.getAddedFeatures());
 
     Set<String> unsupported = Sets.newHashSet();
     Collection<GadgetFeature> feats = featureRegistry.getFeatures(libs, unsupported);

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/PipelineDataContentRewriter.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/PipelineDataContentRewriter.java?rev=747227&r1=747226&r2=747227&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/PipelineDataContentRewriter.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/PipelineDataContentRewriter.java Tue Feb 24 00:03:55 2009
@@ -206,6 +206,19 @@
     head.appendChild(pipelineScript);
     MutableContent.notifyEdit(doc);
     
+    boolean allBatchesCompleted = true;
+    for (PipelineState pipeline : pipelines) {
+      if (pipeline.batch != null) {
+        allBatchesCompleted = false;
+        break;
+      }
+    }
+    
+    if (allBatchesCompleted) {
+      gadget.addFeature("opensocial-data-context");
+      gadget.removeFeature("opensocial-data");
+    }
+    
     return RewriterResults.notCacheable();
   }
   

Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/RenderingContentRewriterTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/RenderingContentRewriterTest.java?rev=747227&r1=747226&r2=747227&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/RenderingContentRewriterTest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/RenderingContentRewriterTest.java Tue Feb 24 00:03:55 2009
@@ -273,6 +273,43 @@
   }
 
   @Test
+  public void featuresNotInjectedWhenRemoved() throws Exception {
+    String gadgetXml =
+      "<Module><ModulePrefs title=''>" +
+      "  <Require feature='foo'/>" +
+      "</ModulePrefs>" +
+      "<Content type='html'/>" +
+      "</Module>";
+
+    Gadget gadget = makeGadgetWithSpec(gadgetXml);
+    gadget.removeFeature("foo");
+
+    featureRegistry.addInline("foo", "foo_content();");
+
+    String rewritten = rewrite(gadget, "");
+
+    assertFalse("Removed script still inlined.", rewritten.contains("foo_content();"));
+  }
+
+  @Test
+  public void featuresInjectedWhenAdded() throws Exception {
+    String gadgetXml =
+      "<Module><ModulePrefs title=''>" +
+      "</ModulePrefs>" +
+      "<Content type='html'/>" +
+      "</Module>";
+
+    Gadget gadget = makeGadgetWithSpec(gadgetXml);
+    gadget.addFeature("foo");
+
+    featureRegistry.addInline("foo", "foo_content();");
+
+    String rewritten = rewrite(gadget, "");
+
+    assertTrue("Added script not inlined.", rewritten.contains("foo_content();"));
+  }
+
+  @Test
   public void mixedExternalAndInline() throws Exception {
     String gadgetXml =
       "<Module><ModulePrefs title=''>" +

Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/PipelineDataContentRewriterTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/PipelineDataContentRewriterTest.java?rev=747227&r1=747226&r2=747227&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/PipelineDataContentRewriterTest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/PipelineDataContentRewriterTest.java Tue Feb 24 00:03:55 2009
@@ -56,6 +56,7 @@
 import java.util.concurrent.Executors;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 
 /**
  * Test of PipelineDataContentRewriter.
@@ -143,6 +144,9 @@
 
     assertTrue(batchCapture.getValue().getSocialPreloads().containsKey("me"));
     assertTrue(batchCapture.getValue().getHttpPreloads().containsKey("json"));
+    
+    assertEquals(ImmutableSet.of("opensocial-data"), gadget.getRemovedFeatures());
+    assertEquals(ImmutableSet.of("opensocial-data-context"), gadget.getAddedFeatures());
 
     control.verify();
   }
@@ -204,6 +208,9 @@
     // Check the evaluated person request
     JSONObject personRequest = (JSONObject) secondBatch.getValue().getSocialPreloads().get("me");
     assertEquals("canonical", personRequest.getJSONObject("params").getJSONArray("userId").get(0));
+
+    assertEquals(ImmutableSet.of("opensocial-data"), gadget.getRemovedFeatures());
+    assertEquals(ImmutableSet.of("opensocial-data-context"), gadget.getAddedFeatures());
   }
 
   @Test
@@ -227,6 +234,9 @@
     // And the os-data elements should be present
     assertTrue("os-data was deleted",
         content.getContent().indexOf("type=\"text/os-data\"") > 0);
+    
+    assertEquals(ImmutableSet.<String>of(), gadget.getRemovedFeatures());
+    assertEquals(ImmutableSet.<String>of(), gadget.getAddedFeatures());
   }
   
   /** Match a batch with the specified count of social and HTTP data items */