You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shindig.apache.org by jo...@apache.org on 2009/11/02 21:34:20 UTC

svn commit: r832093 [1/3] - in /incubator/shindig/trunk: features/ features/src/main/javascript/features/ features/src/main/javascript/features/analytics/ features/src/main/javascript/features/auth-refresh/ features/src/main/javascript/features/core.au...

Author: johnh
Date: Mon Nov  2 20:34:16 2009
New Revision: 832093

URL: http://svn.apache.org/viewvc?rev=832093&view=rev
Log:
The feature.xml and JS system has served us well, yet various new
requirements/requests have arisen that have stressed the current implementation.
Among them:
* Ability to cleanly represent "code" features and "JS" features in a dependency
tree w/ one another. This allows, for instance, a clean way for "opensocial" to
depend on "security-token" (not yet introduced), which in turn depends on
"locked-domain" to ensure token security.
* Ability to let a gadget opt out of all "core" JavaScript, instead only
including sub-components that are used, to shrink render size.
* Ability to serve JS in a context-sensitive fashion ie. browser-specific code.
* Ability to serve JS in a feature-sensitive fashion ie. only inject certain
code when feature="caja" is enabled/active.
* Ability in development mode to easily serve JS that changes when edits are
made on it.

For these and general code-cleanup purposes, I've rewritten the Feature/JS
inclusion implementation as presented in this CL.

This implementation is functionally 100% backward-compatible with its
predecessor. It is only NOT backward-compatible with existing extensions
(outside of Shindig's code base) that override
JsFeatureLoader/GadgetFeatureRegistry.

With this change, the unit of extension in the features system becomes the
<script ...> line in a feature.xml. The processor for this line is a
FeatureResourceLoader, for which a default implementation is provided. Each
<script> line yields a FeatureResource object, which is in turn used to resolve
content and debugContent by rendering/JS code.

Other components are overridable/subclassable as well, but to date I've yet to
hear a JS/features extension use case that does not fit in the ResourceLoader
model (or, in the current model with dynamically register()'ed features).

An overview of the implementation follows.

FeatureRegistry
- Replaces GadgetFeatureRegistry and JsFeatureLoader
- Performs both register() and getFeaturesXXX() functions
- Does NOT implicitly include "core*" APIs anymore! These are included in the
dependency tree. @see GadgetSpec for more below.
- register(...)
  + Registers one or more features in the system, incorporating them into the
list of known features and building a validated dependency tree with them. If a
circular dependency exists, an error is thrown.
  + Method may be called at ANY time -- the registry is not programmatically
locked. This allows (though this use is not tested!) for dynamic feature
injection at some later point.
- getFeatureResources(...)
  + Queries the feature tree for resources matching the specified context
(GADGET || CONTAINER), which satisfy all the needed dependencies given in the
query. Optionally, fills in unknown/unsupported features from the needed list.
- getFeatures(List<String>)
  + Returns feature strings in dependency order that satisfy the given needed
features.

FeatureResourceLoader
- @Injected into FeatureRegistry, main extension point for feature system.
- Loads a given FeatureResource from a feature.xml <script src="..."/> line
(inline features are handled by FeatureRegistry).
- Key API: load(URI, Map<String,String> attribs)
  + URI is pre-resolved to be absolute by FeatureParser (relative to parent, if
a relative URI in feature.xml)
  + "attribs" allows for arbitrary context-sensitive extensions. Example to come
soon: limiting by feature="..." to include Caja tamings.js per-feature only when
Caja is available.

FeatureParser
- Mostly an implementation detail of FeatureRegistry for now (package-private).
- Parses a String into an intermediary object for further processing.
- Resolves URIs of Resources relative to parent.

GadgetSpec
- If no feature is explicitly <Optional> or <Require>'d starting with "core",
automatically inserts the "core" feature for backward compatibility.
- No way (in this CL) to include "absolutely no core" yet.

Gadget
- Modified how addFeature and removeFeature work. They now manage a
"directlyRequestedDeps" list, which in turn is used by rendering/processing code
for feature resolution.

RenderingGadgetRewriter
- Update injectFeatureLibraries(...) to use new API.
- Hopefully more straightforward: obtain resources requested by the gadget, and
resources specified externally. Subtract latter from the former and inline
those. Extern the rest. No "core" assumptions, as "core" will be included by the
GadgetSpec if needed.

Core features
- Break "core" into core.config, core.json, core.prefs et al.
- "core" now an aggregating feature that has no JS of its own but includes all
sub-libs.

All other features
- Specific assignment of dependencies on core libs.

Other
- feature.xml updates, pom.xml updates for separated core libs
- test updates across the board for all affected classes


Added:
    incubator/shindig/trunk/features/src/main/javascript/features/core.auth/
    incubator/shindig/trunk/features/src/main/javascript/features/core.auth/auth-init.js
      - copied unchanged from r827818, incubator/shindig/trunk/features/src/main/javascript/features/core/auth-init.js
    incubator/shindig/trunk/features/src/main/javascript/features/core.auth/auth.js
      - copied unchanged from r827818, incubator/shindig/trunk/features/src/main/javascript/features/core/auth.js
    incubator/shindig/trunk/features/src/main/javascript/features/core.auth/feature.xml
    incubator/shindig/trunk/features/src/main/javascript/features/core.config/
    incubator/shindig/trunk/features/src/main/javascript/features/core.config/config.js
      - copied unchanged from r827818, incubator/shindig/trunk/features/src/main/javascript/features/core/config.js
    incubator/shindig/trunk/features/src/main/javascript/features/core.config/feature.xml
    incubator/shindig/trunk/features/src/main/javascript/features/core.json/
    incubator/shindig/trunk/features/src/main/javascript/features/core.json/feature.xml
    incubator/shindig/trunk/features/src/main/javascript/features/core.json/json.js
      - copied unchanged from r827818, incubator/shindig/trunk/features/src/main/javascript/features/core/json.js
    incubator/shindig/trunk/features/src/main/javascript/features/core.legacy/
    incubator/shindig/trunk/features/src/main/javascript/features/core.legacy/feature.xml
    incubator/shindig/trunk/features/src/main/javascript/features/core.legacy/legacy.js
      - copied unchanged from r827818, incubator/shindig/trunk/features/src/main/javascript/features/core/legacy.js
    incubator/shindig/trunk/features/src/main/javascript/features/core.log/
    incubator/shindig/trunk/features/src/main/javascript/features/core.log/feature.xml
    incubator/shindig/trunk/features/src/main/javascript/features/core.log/log.js
      - copied unchanged from r827818, incubator/shindig/trunk/features/src/main/javascript/features/core/log.js
    incubator/shindig/trunk/features/src/main/javascript/features/core.prefs/
    incubator/shindig/trunk/features/src/main/javascript/features/core.prefs/feature.xml
    incubator/shindig/trunk/features/src/main/javascript/features/core.prefs/prefs.js
      - copied unchanged from r827818, incubator/shindig/trunk/features/src/main/javascript/features/core/prefs.js
    incubator/shindig/trunk/features/src/main/javascript/features/core.util/
    incubator/shindig/trunk/features/src/main/javascript/features/core.util/feature.xml
    incubator/shindig/trunk/features/src/main/javascript/features/core.util/util.js
      - copied unchanged from r830254, incubator/shindig/trunk/features/src/main/javascript/features/core/util.js
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/features/
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/features/BrowserSpecificFeatureResource.java
      - copied, changed from r827818, incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BrowserSpecificRpcJsFeatureLoader.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/features/FeatureParser.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/features/FeatureRegistry.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/features/FeatureResource.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/features/FeatureResourceLoader.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/features/
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/features/FeatureParserTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/features/FeatureRegistryTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/features/FeatureResourceLoaderTest.java
Removed:
    incubator/shindig/trunk/features/src/main/javascript/features/core/auth-init.js
    incubator/shindig/trunk/features/src/main/javascript/features/core/auth.js
    incubator/shindig/trunk/features/src/main/javascript/features/core/config.js
    incubator/shindig/trunk/features/src/main/javascript/features/core/core.js
    incubator/shindig/trunk/features/src/main/javascript/features/core/json.js
    incubator/shindig/trunk/features/src/main/javascript/features/core/legacy.js
    incubator/shindig/trunk/features/src/main/javascript/features/core/log.js
    incubator/shindig/trunk/features/src/main/javascript/features/core/prefs.js
    incubator/shindig/trunk/features/src/main/javascript/features/core/util.js
    incubator/shindig/trunk/features/src/main/javascript/features/osapi.activities/
    incubator/shindig/trunk/features/src/main/javascript/features/osapi.appdata/
    incubator/shindig/trunk/features/src/main/javascript/features/osapi.base/
    incubator/shindig/trunk/features/src/main/javascript/features/osapi.people/
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BrowserSpecificRpcJsFeatureLoader.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeature.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeatureRegistry.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsFeatureLoader.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsLibrary.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/BrowserSpecificRpcJsFeatureLoaderTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetFeatureRegistryTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/JsFeatureLoaderTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/JsLibraryTest.java
Modified:
    incubator/shindig/trunk/features/pom.xml
    incubator/shindig/trunk/features/src/main/javascript/features/analytics/feature.xml
    incubator/shindig/trunk/features/src/main/javascript/features/auth-refresh/feature.xml
    incubator/shindig/trunk/features/src/main/javascript/features/core.io/feature.xml
    incubator/shindig/trunk/features/src/main/javascript/features/core/feature.xml
    incubator/shindig/trunk/features/src/main/javascript/features/features.txt
    incubator/shindig/trunk/features/src/main/javascript/features/flash/feature.xml
    incubator/shindig/trunk/features/src/main/javascript/features/i18n/feature.xml
    incubator/shindig/trunk/features/src/main/javascript/features/opensocial-0.6/feature.xml
    incubator/shindig/trunk/features/src/main/javascript/features/opensocial-current/feature.xml
    incubator/shindig/trunk/features/src/main/javascript/features/opensocial-data/feature.xml
    incubator/shindig/trunk/features/src/main/javascript/features/opensocial-jsonrpc/feature.xml
    incubator/shindig/trunk/features/src/main/javascript/features/opensocial-reference/feature.xml
    incubator/shindig/trunk/features/src/main/javascript/features/osapi/feature.xml
    incubator/shindig/trunk/features/src/main/javascript/features/rpc/feature.xml
    incubator/shindig/trunk/features/src/main/javascript/features/setprefs/feature.xml
    incubator/shindig/trunk/features/src/main/javascript/features/skins/feature.xml
    incubator/shindig/trunk/features/src/main/javascript/features/views/feature.xml
    incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/Pair.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/DefaultUrlGenerator.java
    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/process/Processor.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/OpenSocialI18NGadgetRewriter.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderingGadgetRewriter.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/JsServlet.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/Feature.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/ModulePrefs.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/Preload.java
    incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/tags/FlashTagHandler.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/DefaultUrlGeneratorTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/HashLockedDomainServiceTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/OpenSocialI18NGadgetRewriterTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/RenderingGadgetRewriterTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/PipelineDataGadgetRewriterTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/TemplateRewriterTest.java
    incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/tags/FlashTagHandlerTest.java

Modified: incubator/shindig/trunk/features/pom.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/pom.xml?rev=832093&r1=832092&r2=832093&view=diff
==============================================================================
--- incubator/shindig/trunk/features/pom.xml (original)
+++ incubator/shindig/trunk/features/pom.xml Mon Nov  2 20:34:16 2009
@@ -99,12 +99,12 @@
                 <source>../../../../src/test/javascript/features/mocks/env.js</source>
                 <source>../../../../src/test/javascript/features/mocks/window.js</source>
                 <source>../../../../src/test/javascript/features/mocks/xhr.js</source>
-                <source>core/config.js</source>
-                <source>core/json.js</source>
-                <source>core/auth.js</source>
-                <source>core/util.js</source>
-                <source>core/prefs.js</source>
-                <source>core/log.js</source>
+                <source>core.config/config.js</source>
+                <source>core.json/json.js</source>
+                <source>core.auth/auth.js</source>
+                <source>core.util/util.js</source>
+                <source>core.prefs/prefs.js</source>
+                <source>core.log/log.js</source>
                 <source>core.io/io.js</source>
                 <source>i18n/currencycodemap.js</source>
                 <source>i18n/datetimeformat.js</source>

Modified: incubator/shindig/trunk/features/src/main/javascript/features/analytics/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/analytics/feature.xml?rev=832093&r1=832092&r2=832093&view=diff
==============================================================================
--- incubator/shindig/trunk/features/src/main/javascript/features/analytics/feature.xml (original)
+++ incubator/shindig/trunk/features/src/main/javascript/features/analytics/feature.xml Mon Nov  2 20:34:16 2009
@@ -18,7 +18,7 @@
 -->
 <feature>
   <name>analytics</name>
-  <dependency>core</dependency>
+  <dependency>core.legacy</dependency>
   <gadget>
     <script src="http://www.google-analytics.com/urchin.js"/>
     <script src="http://www.google.com/ig/lib/libanalytics.js"/>

Modified: incubator/shindig/trunk/features/src/main/javascript/features/auth-refresh/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/auth-refresh/feature.xml?rev=832093&r1=832092&r2=832093&view=diff
==============================================================================
--- incubator/shindig/trunk/features/src/main/javascript/features/auth-refresh/feature.xml (original)
+++ incubator/shindig/trunk/features/src/main/javascript/features/auth-refresh/feature.xml Mon Nov  2 20:34:16 2009
@@ -18,6 +18,7 @@
 -->
 <feature>
   <name>auth-refresh</name>
+  <dependency>core.auth</dependency>
   <dependency>rpc</dependency>
   <gadget>
     <script src="auth-refresh.js"/>

Added: incubator/shindig/trunk/features/src/main/javascript/features/core.auth/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/core.auth/feature.xml?rev=832093&view=auto
==============================================================================
--- incubator/shindig/trunk/features/src/main/javascript/features/core.auth/feature.xml (added)
+++ incubator/shindig/trunk/features/src/main/javascript/features/core.auth/feature.xml Mon Nov  2 20:34:16 2009
@@ -0,0 +1,27 @@
+<?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>core.auth</name>
+  <dependency>core.config</dependency>
+  <dependency>core.util</dependency>
+  <gadget>
+    <script src="auth.js"/>
+    <script src="auth-init.js"/>
+  </gadget>
+</feature>

Added: incubator/shindig/trunk/features/src/main/javascript/features/core.config/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/core.config/feature.xml?rev=832093&view=auto
==============================================================================
--- incubator/shindig/trunk/features/src/main/javascript/features/core.config/feature.xml (added)
+++ incubator/shindig/trunk/features/src/main/javascript/features/core.config/feature.xml Mon Nov  2 20:34:16 2009
@@ -0,0 +1,24 @@
+<?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>core.config</name>
+  <gadget>
+    <script src="config.js"/>
+  </gadget>
+</feature>

Modified: incubator/shindig/trunk/features/src/main/javascript/features/core.io/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/core.io/feature.xml?rev=832093&r1=832092&r2=832093&view=diff
==============================================================================
--- incubator/shindig/trunk/features/src/main/javascript/features/core.io/feature.xml (original)
+++ incubator/shindig/trunk/features/src/main/javascript/features/core.io/feature.xml Mon Nov  2 20:34:16 2009
@@ -28,10 +28,9 @@
       encoded inside of the POST body.
 -->
   <name>core.io</name>
-	<!-- core.io needs an explicit core dependency to ensure that the graph
-	    is resolved in the correct order.
-	-->
-  <dependency>core</dependency>
+  <dependency>core.config</dependency>
+  <dependency>core.json</dependency>
+  <dependency>core.util</dependency>
   <gadget>
     <script src="io.js"/>
     <script src="taming.js"/>

Added: incubator/shindig/trunk/features/src/main/javascript/features/core.json/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/core.json/feature.xml?rev=832093&view=auto
==============================================================================
--- incubator/shindig/trunk/features/src/main/javascript/features/core.json/feature.xml (added)
+++ incubator/shindig/trunk/features/src/main/javascript/features/core.json/feature.xml Mon Nov  2 20:34:16 2009
@@ -0,0 +1,27 @@
+<?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>core.json</name>
+  <gadget>
+    <script src="json.js"/>
+  </gadget>
+  <container>
+    <script src="json.js"/>
+  </container>
+</feature>

Added: incubator/shindig/trunk/features/src/main/javascript/features/core.legacy/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/core.legacy/feature.xml?rev=832093&view=auto
==============================================================================
--- incubator/shindig/trunk/features/src/main/javascript/features/core.legacy/feature.xml (added)
+++ incubator/shindig/trunk/features/src/main/javascript/features/core.legacy/feature.xml Mon Nov  2 20:34:16 2009
@@ -0,0 +1,28 @@
+<?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>core.legacy</name>
+  <dependency>core.json</dependency>
+  <dependency>core.io</dependency>
+  <dependency>core.prefs</dependency>
+  <dependency>core.util</dependency>
+  <gadget>
+    <script src="legacy.js"/>
+  </gadget>
+</feature>

Added: incubator/shindig/trunk/features/src/main/javascript/features/core.log/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/core.log/feature.xml?rev=832093&view=auto
==============================================================================
--- incubator/shindig/trunk/features/src/main/javascript/features/core.log/feature.xml (added)
+++ incubator/shindig/trunk/features/src/main/javascript/features/core.log/feature.xml Mon Nov  2 20:34:16 2009
@@ -0,0 +1,27 @@
+<?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>core.log</name>
+  <gadget>
+    <script src="log.js"/>
+  </gadget>
+  <container>
+    <script src="log.js"/>
+  </container>
+</feature>

Added: incubator/shindig/trunk/features/src/main/javascript/features/core.prefs/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/core.prefs/feature.xml?rev=832093&view=auto
==============================================================================
--- incubator/shindig/trunk/features/src/main/javascript/features/core.prefs/feature.xml (added)
+++ incubator/shindig/trunk/features/src/main/javascript/features/core.prefs/feature.xml Mon Nov  2 20:34:16 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>core.prefs</name>
+  <dependency>core.util</dependency>
+  <gadget>
+    <script src="prefs.js"/>
+  </gadget>
+</feature>

Added: incubator/shindig/trunk/features/src/main/javascript/features/core.util/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/core.util/feature.xml?rev=832093&view=auto
==============================================================================
--- incubator/shindig/trunk/features/src/main/javascript/features/core.util/feature.xml (added)
+++ incubator/shindig/trunk/features/src/main/javascript/features/core.util/feature.xml Mon Nov  2 20:34:16 2009
@@ -0,0 +1,28 @@
+<?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>core.util</name>
+  <dependency>core.config</dependency>
+  <gadget>
+    <script src="util.js"/>
+  </gadget>
+  <container>
+    <script src="util.js"/>
+  </container>
+</feature>

Modified: incubator/shindig/trunk/features/src/main/javascript/features/core/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/core/feature.xml?rev=832093&r1=832092&r2=832093&view=diff
==============================================================================
--- incubator/shindig/trunk/features/src/main/javascript/features/core/feature.xml (original)
+++ incubator/shindig/trunk/features/src/main/javascript/features/core/feature.xml Mon Nov  2 20:34:16 2009
@@ -17,24 +17,19 @@
   specific language governing permissions and limitations under the License.
 -->
 <feature>
+  <!--
+    Core is simply an aggregating feature of all the core.* libraries. It is
+    included in a gadget render that doesn't signal its awareness of "split-core"
+    dependencies (which afford better precision in code inclusion) by including
+    core.* libs.
+  -->
   <name>core</name>
-  <gadget>
-    <script src="core.js"/>
-    <script src="config.js"/>
-    <!-- for gadgets.util.sanitizeHtml -->
-    <script src="res://com/google/caja/plugin/html-sanitizer-minified.js"></script>
-    <script src="util.js"/>
-    <script src="auth.js"/>
-    <script src="auth-init.js"/>
-    <script src="prefs.js"/>
-    <script src="json.js"/>
-    <script src="legacy.js"/>
-    <script src="log.js"/>
-    <script src="taming.js"/>
-  </gadget>
-  <container>
-    <script src="log.js"/>
-    <script src="util.js"/>
-    <script src="json.js"/>
-  </container>
+  <dependency>core.auth</dependency>
+  <dependency>core.config</dependency>
+  <dependency>core.json</dependency>
+  <dependency>core.legacy</dependency>
+  <dependency>core.log</dependency>
+  <dependency>core.io</dependency>
+  <dependency>core.prefs</dependency>
+  <dependency>core.util</dependency>
 </feature>

Modified: incubator/shindig/trunk/features/src/main/javascript/features/features.txt
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/features.txt?rev=832093&r1=832092&r2=832093&view=diff
==============================================================================
--- incubator/shindig/trunk/features/src/main/javascript/features/features.txt (original)
+++ incubator/shindig/trunk/features/src/main/javascript/features/features.txt Mon Nov  2 20:34:16 2009
@@ -22,7 +22,14 @@
 features/auth-refresh/feature.xml
 features/caja/feature.xml
 features/content-rewrite/feature.xml
+features/core.auth/feature.xml
+features/core.config/feature.xml
 features/core.io/feature.xml
+features/core.json/feature.xml
+features/core.legacy/feature.xml
+features/core.log/feature.xml
+features/core.prefs/feature.xml
+features/core.util/feature.xml
 features/core/feature.xml
 features/dynamic-height.util/feature.xml
 features/dynamic-height/feature.xml

Modified: incubator/shindig/trunk/features/src/main/javascript/features/flash/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/flash/feature.xml?rev=832093&r1=832092&r2=832093&view=diff
==============================================================================
--- incubator/shindig/trunk/features/src/main/javascript/features/flash/feature.xml (original)
+++ incubator/shindig/trunk/features/src/main/javascript/features/flash/feature.xml Mon Nov  2 20:34:16 2009
@@ -18,6 +18,7 @@
 -->
 <feature>
   <name>flash</name>
+  <dependency>core.io</dependency>
   <gadget>
     <script src="flash.js"/>
     <script src="taming.js"/>

Modified: incubator/shindig/trunk/features/src/main/javascript/features/i18n/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/i18n/feature.xml?rev=832093&r1=832092&r2=832093&view=diff
==============================================================================
--- incubator/shindig/trunk/features/src/main/javascript/features/i18n/feature.xml (original)
+++ incubator/shindig/trunk/features/src/main/javascript/features/i18n/feature.xml Mon Nov  2 20:34:16 2009
@@ -18,8 +18,6 @@
 -->
 <feature>
   <name>opensocial-i18n</name>
-  <dependency>core</dependency>
-  <dependency>core.io</dependency>
   <gadget>
     <script src="formatting.js"/>
     <script src="datetimeformat.js"/>

Modified: incubator/shindig/trunk/features/src/main/javascript/features/opensocial-0.6/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/opensocial-0.6/feature.xml?rev=832093&r1=832092&r2=832093&view=diff
==============================================================================
--- incubator/shindig/trunk/features/src/main/javascript/features/opensocial-0.6/feature.xml (original)
+++ incubator/shindig/trunk/features/src/main/javascript/features/opensocial-0.6/feature.xml Mon Nov  2 20:34:16 2009
@@ -19,6 +19,9 @@
 -->
 <feature>
   <name>opensocial-0.6</name>
+  <dependency>core.io</dependency>
+  <dependency>core.prefs</dependency>
+  <dependency>core.util</dependency>
   <dependency>opensocial-0.7</dependency>
   <dependency>views</dependency>
   <gadget>

Modified: incubator/shindig/trunk/features/src/main/javascript/features/opensocial-current/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/opensocial-current/feature.xml?rev=832093&r1=832092&r2=832093&view=diff
==============================================================================
--- incubator/shindig/trunk/features/src/main/javascript/features/opensocial-current/feature.xml (original)
+++ incubator/shindig/trunk/features/src/main/javascript/features/opensocial-current/feature.xml Mon Nov  2 20:34:16 2009
@@ -19,6 +19,7 @@
 -->
 <feature>
   <name>opensocial</name>
+  <dependency>core.config</dependency>
   <dependency>opensocial-jsonrpc</dependency>
   <!-- <dependency>caja</dependency> -->
   <!-- Must include the "caja" feature to display samplecontainer -->

Modified: incubator/shindig/trunk/features/src/main/javascript/features/opensocial-data/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/opensocial-data/feature.xml?rev=832093&r1=832092&r2=832093&view=diff
==============================================================================
--- incubator/shindig/trunk/features/src/main/javascript/features/opensocial-data/feature.xml (original)
+++ incubator/shindig/trunk/features/src/main/javascript/features/opensocial-data/feature.xml Mon Nov  2 20:34:16 2009
@@ -19,6 +19,8 @@
 -->
 <feature>
   <name>opensocial-data</name>
+  <dependency>core.io</dependency>
+  <dependency>core.util</dependency>
   <dependency>opensocial-data-context</dependency>
   <dependency>opensocial-0.8</dependency>
   <dependency>xmlutil</dependency>

Modified: incubator/shindig/trunk/features/src/main/javascript/features/opensocial-jsonrpc/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/opensocial-jsonrpc/feature.xml?rev=832093&r1=832092&r2=832093&view=diff
==============================================================================
--- incubator/shindig/trunk/features/src/main/javascript/features/opensocial-jsonrpc/feature.xml (original)
+++ incubator/shindig/trunk/features/src/main/javascript/features/opensocial-jsonrpc/feature.xml Mon Nov  2 20:34:16 2009
@@ -19,6 +19,10 @@
 -->
 <feature>
   <name>opensocial-jsonrpc</name>
+  <dependency>core.prefs</dependency>
+  <dependency>core.io</dependency>
+  <dependency>core.json</dependency>
+  <dependency>core.util</dependency>
   <dependency>opensocial-base</dependency>
   <dependency>rpc</dependency>
   <gadget>   

Modified: incubator/shindig/trunk/features/src/main/javascript/features/opensocial-reference/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/opensocial-reference/feature.xml?rev=832093&r1=832092&r2=832093&view=diff
==============================================================================
--- incubator/shindig/trunk/features/src/main/javascript/features/opensocial-reference/feature.xml (original)
+++ incubator/shindig/trunk/features/src/main/javascript/features/opensocial-reference/feature.xml Mon Nov  2 20:34:16 2009
@@ -19,6 +19,7 @@
 -->
 <feature>
   <name>opensocial-reference</name>
+  <dependency>core.util</dependency>
   <gadget>
     <script src="opensocial.js"/>
     <script src="activity.js"/>

Modified: incubator/shindig/trunk/features/src/main/javascript/features/osapi/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/osapi/feature.xml?rev=832093&r1=832092&r2=832093&view=diff
==============================================================================
--- incubator/shindig/trunk/features/src/main/javascript/features/osapi/feature.xml (original)
+++ incubator/shindig/trunk/features/src/main/javascript/features/osapi/feature.xml Mon Nov  2 20:34:16 2009
@@ -19,7 +19,11 @@
 -->
 <feature>
   <name>osapi</name>
+  <dependency>core.config</dependency>
   <dependency>core.io</dependency>
+  <dependency>core.json</dependency>
+  <dependency>core.log</dependency>
+  <dependency>core.util</dependency>
   <dependency>rpc</dependency>
   <gadget>
     <script src="osapi.js"></script>

Modified: incubator/shindig/trunk/features/src/main/javascript/features/rpc/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/rpc/feature.xml?rev=832093&r1=832092&r2=832093&view=diff
==============================================================================
--- incubator/shindig/trunk/features/src/main/javascript/features/rpc/feature.xml (original)
+++ incubator/shindig/trunk/features/src/main/javascript/features/rpc/feature.xml Mon Nov  2 20:34:16 2009
@@ -28,6 +28,10 @@
 (if true, all calls to rpc.call will use the same wire format as ifpc).
 -->
   <name>rpc</name>
+  <dependency>core.config</dependency>
+  <dependency>core.log</dependency>
+  <dependency>core.json</dependency>
+  <dependency>core.util</dependency>
   <gadget>
     <script src="wpm.transport.js"/>
     <script src="fe.transport.js"/>

Modified: incubator/shindig/trunk/features/src/main/javascript/features/setprefs/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/setprefs/feature.xml?rev=832093&r1=832092&r2=832093&view=diff
==============================================================================
--- incubator/shindig/trunk/features/src/main/javascript/features/setprefs/feature.xml (original)
+++ incubator/shindig/trunk/features/src/main/javascript/features/setprefs/feature.xml Mon Nov  2 20:34:16 2009
@@ -18,6 +18,8 @@
 -->
 <feature>
   <name>setprefs</name>
+  <dependency>core.prefs</dependency>
+  <dependency>core.util</dependency>
   <dependency>rpc</dependency>
   <gadget>
     <script src="setprefs.js"/>

Modified: incubator/shindig/trunk/features/src/main/javascript/features/skins/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/skins/feature.xml?rev=832093&r1=832092&r2=832093&view=diff
==============================================================================
--- incubator/shindig/trunk/features/src/main/javascript/features/skins/feature.xml (original)
+++ incubator/shindig/trunk/features/src/main/javascript/features/skins/feature.xml Mon Nov  2 20:34:16 2009
@@ -18,6 +18,8 @@
 -->
 <feature>
   <name>skins</name>
+  <dependency>core.config</dependency>
+  <dependency>core.util</dependency>
   <gadget>
     <script src="skins.js"/>
     <script src="taming.js"/>

Modified: incubator/shindig/trunk/features/src/main/javascript/features/views/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/views/feature.xml?rev=832093&r1=832092&r2=832093&view=diff
==============================================================================
--- incubator/shindig/trunk/features/src/main/javascript/features/views/feature.xml (original)
+++ incubator/shindig/trunk/features/src/main/javascript/features/views/feature.xml Mon Nov  2 20:34:16 2009
@@ -23,8 +23,10 @@
 
 
 -->
-
   <name>views</name>
+  <dependency>core.config</dependency>
+  <dependency>core.json</dependency>
+  <dependency>core.util</dependency>
   <dependency>rpc</dependency>
   <gadget>
     <script src="views.js"/>

Modified: incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/Pair.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/Pair.java?rev=832093&r1=832092&r2=832093&view=diff
==============================================================================
--- incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/Pair.java (original)
+++ incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/common/Pair.java Mon Nov  2 20:34:16 2009
@@ -28,4 +28,8 @@
     this.one = one;
     this.two = two;
   }
+  
+  public static <T1, T2> Pair<T1, T2> of(T1 one, T2 two) {
+    return new Pair<T1, T2>(one, two);
+  }
 }

Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/DefaultUrlGenerator.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/DefaultUrlGenerator.java?rev=832093&r1=832092&r2=832093&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/DefaultUrlGenerator.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/DefaultUrlGenerator.java Mon Nov  2 20:34:16 2009
@@ -22,6 +22,8 @@
 import org.apache.shindig.common.uri.UriBuilder;
 import org.apache.shindig.common.util.HashUtil;
 import org.apache.shindig.config.ContainerConfig;
+import org.apache.shindig.gadgets.features.FeatureRegistry;
+import org.apache.shindig.gadgets.features.FeatureResource;
 import org.apache.shindig.gadgets.spec.GadgetSpec;
 import org.apache.shindig.gadgets.spec.UserPref;
 import org.apache.shindig.gadgets.spec.View;
@@ -60,7 +62,7 @@
   @Inject
   public DefaultUrlGenerator(ContainerConfig config,
                              LockedDomainService lockedDomainService,
-                             GadgetFeatureRegistry registry) {
+                             FeatureRegistry registry) {
     iframeBaseUris = Maps.newHashMap();
     jsUriTemplates = Maps.newHashMap();
     oauthCallbackUriTemplates = Maps.newHashMap();
@@ -74,10 +76,8 @@
     this.lockedDomainService = lockedDomainService;
 
     StringBuilder jsBuf = new StringBuilder();
-    for (GadgetFeature feature : registry.getAllFeatures()) {
-      for (JsLibrary library : feature.getJsLibraries(null, null)) {
-        jsBuf.append(library.getContent());
-      }
+    for (FeatureResource resource : registry.getAllFeatures()) {
+      jsBuf.append(resource.getContent());
     }
     jsChecksum = HashUtil.checksum(jsBuf.toString().getBytes());
 

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=832093&r1=832092&r2=832093&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 Mon Nov  2 20:34:16 2009
@@ -17,6 +17,7 @@
  */
 package org.apache.shindig.gadgets;
 
+import org.apache.shindig.gadgets.features.FeatureRegistry;
 import org.apache.shindig.gadgets.preload.PreloadedData;
 import org.apache.shindig.gadgets.servlet.ProxyBase;
 import org.apache.shindig.gadgets.spec.GadgetSpec;
@@ -36,13 +37,12 @@
  * of a single gadget request.
  */
 public class Gadget {
-  private GadgetFeatureRegistry gadgetFeatureRegistry;
+  private FeatureRegistry featureRegistry;
   private GadgetContext context;
   private GadgetSpec spec;
   private Collection<PreloadedData> preloads;
   private View currentView;
-  private Set<String> addedFeatures;
-  private Set<String> removedFeatures;
+  private Set<String> directFeatureDeps;
 
   /**
    * @param context The request that the gadget is being processed for.
@@ -60,8 +60,8 @@
    * @param registry The gadget feature registry to use to find dependent
    *                 features.
    */
-  public Gadget setGadgetFeatureRegistry(GadgetFeatureRegistry registry) {
-    this.gadgetFeatureRegistry = registry;
+  public Gadget setGadgetFeatureRegistry(FeatureRegistry registry) {
+    this.featureRegistry = registry;
     return this;
   }
 
@@ -70,6 +70,7 @@
    */
   public Gadget setSpec(GadgetSpec spec) {
     this.spec = spec;
+    this.directFeatureDeps = Sets.newHashSet(spec.getModulePrefs().getFeatures().keySet());
     return this;
   }
 
@@ -96,15 +97,9 @@
   private List<String> allGadgetFeatures;
   public synchronized List<String> getAllFeatures() {
     if (allGadgetFeatures == null) {
-      if (gadgetFeatureRegistry != null) {
-        allGadgetFeatures = Lists.newArrayList();
-        for (GadgetFeature gadgetFeature :
-               gadgetFeatureRegistry.getFeatures(
-                   this.spec.getModulePrefs().getFeatures().keySet())) {
-          allGadgetFeatures.add(gadgetFeature.getName());
-        }
-        // now all features are in reverse order of dependency. So reverse the list.
-        Collections.reverse(allGadgetFeatures);
+      if (featureRegistry != null) {
+        allGadgetFeatures = featureRegistry.getFeatures(
+            Lists.newArrayList(this.spec.getModulePrefs().getFeatures().keySet()));
       } else {
         throw new IllegalStateException(
             "setGadgetFeatureRegistry must be called before Gadget.getAllFeatures()");
@@ -135,38 +130,17 @@
   public LocaleSpec getLocale() {
     return spec.getModulePrefs().getLocale(context.getLocale());
   }
-
   
   public void addFeature(String name) {
-    if (addedFeatures == null) {
-      addedFeatures = Sets.newHashSet();
-    }
-    
-    addedFeatures.add(name);
+    directFeatureDeps.add(name);
   }
   
   public void removeFeature(String name) {
-    if (removedFeatures == null) {
-      removedFeatures = Sets.newHashSet();
-    }
-    
-    removedFeatures.add(name);
+    directFeatureDeps.remove(name);
   }
   
-  public Set<String> getAddedFeatures() {
-    if (addedFeatures == null) {
-      return Collections.emptySet();
-    }
-    
-    return addedFeatures;
-  }
-
-  public Set<String> getRemovedFeatures() {
-    if (removedFeatures == null) {
-      return Collections.emptySet();
-    }
-    
-    return removedFeatures;
+  public Set<String> getDirectFeatureDeps() {
+    return Collections.unmodifiableSet(directFeatureDeps);
   }
 
   /**

Copied: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/features/BrowserSpecificFeatureResource.java (from r827818, incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BrowserSpecificRpcJsFeatureLoader.java)
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/features/BrowserSpecificFeatureResource.java?p2=incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/features/BrowserSpecificFeatureResource.java&p1=incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BrowserSpecificRpcJsFeatureLoader.java&r1=827818&r2=832093&rev=832093&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BrowserSpecificRpcJsFeatureLoader.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/features/BrowserSpecificFeatureResource.java Mon Nov  2 20:34:16 2009
@@ -15,184 +15,263 @@
  * KIND, either express or implied. See the License for the
  * specific language governing permissions and limitations under the License.
  */
-package org.apache.shindig.gadgets;
+package org.apache.shindig.gadgets.features;
 
-import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
 import org.apache.shindig.common.servlet.UserAgent;
-import org.apache.shindig.gadgets.http.HttpFetcher;
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.gadgets.GadgetException;
 
+import java.util.Arrays;
+import java.util.List;
 import java.util.Map;
 
-public class BrowserSpecificRpcJsFeatureLoader extends JsFeatureLoader {
-  private static final String RPC_FEATURE_NAME = "rpc";
-  private Provider<UserAgent> uaProvider;
-  private RpcJsLibrary rpcLib;
-  
-  @Inject
-  BrowserSpecificRpcJsFeatureLoader(HttpFetcher fetcher, Provider<UserAgent> uaProvider) {
-    super(fetcher);
+/**
+ * A FeatureResource that supports being supplied only to certain browsers.
+ * 
+ * This is optional functionality, activated by the browser="..." attribute on
+ * a &lt;script&gt; element. That attribute's value is interpreted as a
+ * comma-separated list of BROWSER-versionKey matchers.
+ * 
+ * BROWSER must match (case-insensitive) the list of UserAgent.Browser enum values
+ * eg. "MSIE" or "FIREFOX".
+ * 
+ * versionKey is OPERATORversionNumber, where OPERATOR may be one of:
+ * ^ - regex
+ * = - exact match
+ * >, >=, <, <= - greater than/less than matches
+ * [no operator] - exact match
+ * 
+ * If no browser="..." attribute is specified, the resource always matches. Otherwise,
+ * if ANY of the browser-versionKey matchers match, the resource matches. In such case,
+ * the delegate FeatureResource's content methods are consulted. Otherwise, "" is returned
+ * for content.
+ * 
+ * Example:
+ * browser="FireFox->=3, MSIE-6.0 would match FireFox 3.x.y (any) and IE 6.0 (only).
+ * 
+ * To activate this capability, you may use the provided Loader class and bind it
+ * as your FeatureResourceLoader implementation; or build your own that wraps its resources
+ * in BrowserSpecificFeatureResource.
+ */
+public class BrowserSpecificFeatureResource implements FeatureResource {
+  private final Provider<UserAgent> uaProvider;
+  private final FeatureResource delegate;
+  private final Map<UserAgent.Browser, List<VersionMatcher>> browserMatch;
+  
+  public BrowserSpecificFeatureResource(
+      Provider<UserAgent> uaProvider, FeatureResource delegate, String browserKey) {
     this.uaProvider = uaProvider;
+    this.delegate = delegate;
+    this.browserMatch = populateBrowserMatchers(browserKey);
   }
   
-  @Override
-  protected JsLibrary createJsLibrary(JsLibrary.Type type, String content, String feature,
-      HttpFetcher fetcher) throws GadgetException {
-    if (feature.equals(RPC_FEATURE_NAME)) {
-      if (rpcLib == null) {
-        synchronized(this) {
-          if (rpcLib == null) {
-            rpcLib = new RpcJsLibrary(uaProvider, type, content);
-            return rpcLib;
-          }
-        }
-      }
-      return new NullJsLibrary(RPC_FEATURE_NAME);
+  public String getContent() {
+    if (browserMatches()) {
+      return delegate.getContent();
+    }
+    return "";
+  }
+
+  public String getDebugContent() {
+    if (browserMatches()) {
+      return delegate.getDebugContent();
     }
-    return super.createJsLibrary(type, content, feature, fetcher);
+    return "";
+  }
+
+  public boolean isExternal() {
+    return delegate.isExternal();
+  }
+
+  public boolean isProxyCacheable() {
+    // If browser-specific (ie. browserMatch has some qualifiers in it), not proxy cacheable
+    // (since the vast majority of browsers don't support Vary: User-Agent, we just say "false")
+    // Otherwise, delegate this call.
+    return browserMatch.size() > 0 ? false : delegate.isProxyCacheable();
   }
   
-  /**
-   * Do-nothing JsLibrary. Workaround for multi-file features
-   * with overrides, ie. rpc. createJsLibrary is called for each
-   * resource in the feature, but we want RpcJsLibrary to
-   * issue all the JS for the request.
-   */
-  protected static class NullJsLibrary extends JsLibrary {
-    public NullJsLibrary(String name) {
-      super(name, Type.FILE, "", "");
+  private boolean browserMatches() {
+    if (browserMatch.size() == 0) {
+      // Not browser-sensitive.
+      return true;
+    }
+    UserAgent ua = uaProvider.get();
+    List<VersionMatcher> versionMatchers = browserMatch.get(ua.getBrowser());
+    for (VersionMatcher matcher : versionMatchers) {
+      if (matcher.matches(ua.getVersion())) return true;
+    }
+    return false;
+  }
+  
+  private static Map<UserAgent.Browser, List<VersionMatcher>> populateBrowserMatchers(
+      String browserKey) {
+    Map<UserAgent.Browser, List<VersionMatcher>> map = Maps.newHashMap();
+    if (browserKey == null || browserKey.length() == 0) {
+      return map;
+    }
+    
+    // Comma-delimited list of <browser>-<versionKey> pairs.
+    String[] entries = browserKey.split(",");
+    for (String entry : entries) {
+      entry = entry.trim();
+      String[] browserAndVersion = entry.split("-");
+      String browser = browserAndVersion[0];
+      String versionKey = browserAndVersion.length == 2 ? browserAndVersion[1] : null;
+      
+      // This may throw an IllegalArgumentException, (properly) indicating a faulty feature.xml
+      UserAgent.Browser browserEnum = UserAgent.Browser.valueOf(browser.toUpperCase());
+      if (!map.containsKey(browserEnum)) {
+        map.put(browserEnum, Lists.<VersionMatcher>newLinkedList());
+      }
+      map.get(browserEnum).add(new VersionMatcher(versionKey));
     }
+    
+    return map;
   }
   
   /**
-   * Custom JsLibrary that emits only rpc.js transport code needed
-   * for a given User-Agent, which it gets from an injected Provider.
+   * Simple FeatureResourceLoader implementation that wraps all resource loads in
+   * a browser-filtering delegator.
    */
-  protected static class RpcJsLibrary extends JsLibrary {
+  public static class Loader extends FeatureResourceLoader {
     private final Provider<UserAgent> uaProvider;
-    private final Map<String, String> rpcJsCode;
-    
-    private static final String RPC_JS_CORE = "rpc.js";
-    private static final String WPM_TX = "wpm.transport.js";
-    private static final String NIX_TX = "nix.transport.js";
-    private static final String FE_TX = "fe.transport.js";
-    private static final String RMR_TX = "rmr.transport.js";
-    private static final String IFPC_TX = "ifpc.transport.js";
-    private static final String ALL_TX = "all-transports";
     
-    private static final String OPTIMIZED_SUFFIX = ":opt";
-    private static final String DEBUG_SUFFIX = ":dbg";
-    
-    protected RpcJsLibrary(Provider<UserAgent> uaProvider, Type type, String filePath) {
-      super(RPC_FEATURE_NAME, type, "", "");
+    @Inject
+    public Loader(Provider<UserAgent> uaProvider) {
       this.uaProvider = uaProvider;
-      
-      // Something of a hack: filePath, without the trailing filename, gives the root
-      // path from which to load the JS.
-      int lastSlash = filePath.lastIndexOf('/');
-      if (lastSlash >= 0) {
-        filePath = filePath.substring(0, lastSlash + 1);
-      }
-      
-      // Load the core and all transport JS.
-      rpcJsCode = Maps.newHashMap();
-      StringBuilder allDbg = new StringBuilder();
-      StringBuilder allOpt = new StringBuilder();
-      for (String rpcPart :
-           ImmutableList.of(RPC_JS_CORE, WPM_TX, NIX_TX, FE_TX, RMR_TX, IFPC_TX)) {
-        StringBuilder opt = new StringBuilder();
-        StringBuilder dbg = new StringBuilder();
-        loadOptimizedAndDebugData(filePath + rpcPart, type, opt, dbg);
-        rpcJsCode.put(rpcPart + OPTIMIZED_SUFFIX, opt.toString());
-        rpcJsCode.put(rpcPart + DEBUG_SUFFIX, dbg.toString());
-        if (!rpcPart.equals(RPC_JS_CORE)) {
-          allOpt.append(opt.toString());
-          allDbg.append(dbg.toString());
-        }
-      }
-      rpcJsCode.put(ALL_TX + OPTIMIZED_SUFFIX, allOpt.toString());
-      rpcJsCode.put(ALL_TX + DEBUG_SUFFIX, allDbg.toString());
     }
     
     @Override
-    public String getContent() {
-      return getRpcContent(OPTIMIZED_SUFFIX);
+    public FeatureResource load(Uri uri, Map<String, String> attribs) throws GadgetException {
+      return new BrowserSpecificFeatureResource(
+          uaProvider, super.load(uri, attribs), attribs.get("browser"));
     }
+  }
+  
+  private static class VersionMatcher {
+    private static final Op[] OPS = {
+      new Op("^") {
+        public boolean match(String in, String key) {
+          return in.matches(key);
+        }
+      },
+      new Op("=") {
+        public boolean match(String in, String key) {
+          return in.equals(key) || num(in).eq(num(key));
+        }
+      },
+      new Op(">") {
+        public boolean match(String in, String key) {
+          return num(in).gt(num(key));
+        }
+      },
+      new Op(">=") {
+        public boolean match(String in, String key) {
+          return in.equals(key) || num(in).eq(num(key)) || num(in).gt(num(key));
+        }
+      },
+      new Op("<") {
+        public boolean match(String in, String key) {
+          return num(in).lt(num(key));
+        }
+      },
+      new Op("<=") {
+        public boolean match(String in, String key) {
+          return in.equals(key) || num(in).eq(num(key)) || num(in).lt(num(key));
+        }
+      },
+    };
     
-    @Override
-    public String getDebugContent() {
-      return getRpcContent(DEBUG_SUFFIX);
+    private final String versionKey;
+    
+    private VersionMatcher(String versionKey) {
+      if (versionKey != null && versionKey.length() != 0) {
+        this.versionKey = versionKey;
+      } else {
+        // No qualifier = match all (shortcut)
+        this.versionKey = null;
+      }
     }
     
-    @Override
-    public boolean isProxyCacheable() {
+    public boolean matches(String version) {
+      if (versionKey == null || versionKey.equals(version)) {
+        // Match-all or exact-string-match.
+        return true;
+      }
+      for (Op op : OPS) {
+        if (op.apply(version, versionKey)) {
+          return true;
+        }
+      }
       return false;
     }
     
-    /**
-     * Does the dirty work of translating UserAgent into transport + rpc core.
-     * @param keySuffix Suffix appended to rpc content key getting debug or opt JS.
-     * @return Context-appropriate rpc.js
-     */
-    String getRpcContent(String keySuffix) {
-      // Send all by default.
-      String txKey = ALL_TX;
-      UserAgent userAgent = uaProvider.get();
-      if (userAgent != null) {
-        double version = userAgent.getVersionNumber();
-        switch(userAgent.getBrowser()) {
-          case MSIE:
-            if (version >= 8) {
-              txKey = WPM_TX;
-            } else if (version >= 6) {
-              txKey = NIX_TX;
-            }
-            break;
-          case FIREFOX:
-            if (version >= 3) {
-              txKey = WPM_TX;
-            } else if (version >= 2) {
-              txKey = FE_TX;
-            }
-            break;
-          case SAFARI:
-            if (version >= 4) {
-              txKey = WPM_TX;
-            } else if (version >= 2) {
-              txKey = RMR_TX;
-            }
-            break;
-          case CHROME:
-            if (version >= 2) {
-              txKey = WPM_TX;
-            } else {
-              txKey = RMR_TX;
-            }
-            break;
-          case OPERA:
-            if (version >= 9) {
-              txKey = WPM_TX;
-            }
-            break;
-          case WEBKIT:
-            if (version >= 29907) {
-              // Webkit nightlies have had window.postMessage since at least 2/1/2008.
-              // TODO Figure out exactly when it was added.
-              txKey = WPM_TX;
-            } else {
-              txKey = RMR_TX;
-            }
-            break;
-          case OTHER:
-            break;
+    private static VersionNumber num(String str) {
+      return new VersionNumber(str);
+    }
+    
+    private static abstract class Op {
+      private final String pfx;
+      
+      private Op(String pfx) {
+        this.pfx = pfx;
+      }
+      
+      private boolean apply(String version, String key) {
+        if (version.startsWith(pfx)) {
+          version = version.substring(pfx.length());
+          return match(version, key);
+        }
+        return false;
+      }
+      
+      public abstract boolean match(String in, String key);
+    }
+    
+    private static class VersionNumber {
+      private final int[] parts;
+      
+      private VersionNumber(String str) {
+        String[] strParts = str.split("\\.");
+        int[] intParts = new int[strParts.length];
+        try {
+          for (int i = 0; i < strParts.length; ++i) {
+            intParts[i] = Integer.parseInt(strParts[i]);
+          }
+        } catch (NumberFormatException e) {
+          intParts = null;
+        }
+        this.parts = intParts;
+      }
+      
+      public boolean eq(VersionNumber other) {
+        return Arrays.equals(this.parts, other.parts);
+      }
+      
+      public boolean lt(VersionNumber other) {
+        for (int i = 0; i < this.parts.length; ++i) {
+          int otherVal = (i < other.parts.length) ? other.parts[i] : 0;  // 0's fill in the rest
+          if (this.parts[i] > otherVal) {
+            return false;
+          }
         }
+        return true;
       }
       
-      // Return the appropriate transport(s) + rpc core.
-      return rpcJsCode.get(txKey + keySuffix) + rpcJsCode.get(RPC_JS_CORE + keySuffix);
+      public boolean gt(VersionNumber other) {
+        for (int i = 0; i < this.parts.length; ++i) {
+          int otherVal = (i < other.parts.length) ? other.parts[i] : 0;  // 0's fill in the rest
+          if (this.parts[i] < otherVal) {
+            return false;
+          }
+        }
+        return true;
+      }
     }
   }
 }

Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/features/FeatureParser.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/features/FeatureParser.java?rev=832093&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/features/FeatureParser.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/features/FeatureParser.java Mon Nov  2 20:34:16 2009
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ */
+package org.apache.shindig.gadgets.features;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.common.xml.XmlException;
+import org.apache.shindig.common.xml.XmlUtil;
+import org.apache.shindig.gadgets.GadgetException;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Parses feature.xml files into an intermediary Java object for further processing.
+ * This is largely an implementation detail of FeatureRegistry.
+ */
+class FeatureParser {
+  public ParsedFeature parse(Uri parent, String xml) throws GadgetException {
+    Element doc;
+    try {
+      doc = XmlUtil.parse(xml);
+    } catch (XmlException e) {
+      throw new GadgetException(GadgetException.Code.MALFORMED_XML_DOCUMENT, e);
+    }
+
+    String name = null;
+    List<String> deps = Lists.newArrayList();
+    List<ParsedFeature.Bundle> bundles = Lists.newArrayList();
+   
+    NodeList children = doc.getChildNodes();
+    for (int i = 0, j = children.getLength(); i < j; ++i) {
+      Node child = children.item(i);
+      if (child.getNodeType() == Node.ELEMENT_NODE) {
+        Element element = (Element)child;
+        if (element.getTagName().equals("name")) {
+          name = element.getTextContent();
+        } else if (element.getTagName().equals("dependency")) {
+          deps.add(element.getTextContent());
+        } else {
+          String type = element.getTagName().toLowerCase();
+          List<ParsedFeature.Resource> resources = Lists.newArrayList();
+          NodeList resourceKids = element.getElementsByTagName("script");
+          for (int x = 0, y = resourceKids.getLength(); x < y; ++x) {
+            Element resourceChild = (Element)resourceKids.item(x);
+            String src = resourceChild.getAttribute("src");
+            String content = resourceChild.getTextContent();
+            resources.add(new ParsedFeature.Resource(
+                src == null || src.length() == 0 ? null :
+                  parent.resolve(FeatureRegistry.getComponentUri(src)),
+                src != null && src.length() != 0 ? null : content,
+                getAttribs(resourceChild)));
+          }
+          bundles.add(new ParsedFeature.Bundle(type, getAttribs(element), resources));
+        }
+      }
+    }
+    
+    return new ParsedFeature(name, deps, bundles);
+  }
+  
+  private Map<String, String> getAttribs(Element element) {
+    Map<String, String> attribs = Maps.newHashMap();
+    NamedNodeMap attribNodes = element.getAttributes();
+    for (int x = 0, y = attribNodes.getLength(); x < y; ++x) {
+      Attr attr = (Attr)attribNodes.item(x);
+      if (!attr.getName().equals("src")) {
+        attribs.put(attr.getName(), attr.getValue());
+      }
+    }
+    return Collections.unmodifiableMap(attribs);
+  }
+  
+  static class ParsedFeature {
+    private final String name;
+    private final List<String> deps;
+    private final List<Bundle> bundles;
+    
+    private ParsedFeature(String name, List<String> deps, List<Bundle> bundles) {
+      this.name = name;
+      this.deps = Collections.unmodifiableList(deps);
+      this.bundles = Collections.unmodifiableList(bundles);
+    }
+    
+    public String getName() {
+      return name;
+    }
+    
+    public List<String> getDeps() {
+      return deps;
+    }
+    
+    public List<Bundle> getBundles() {
+      return bundles;
+    }
+    
+    public static class Bundle {
+      private final String type;
+      private final Map<String, String> attribs;
+      private final List<Resource> resources;
+      
+      private Bundle(String type, Map<String, String> attribs, List<Resource> resources) {
+        this.type = type;
+        this.attribs = attribs;
+        this.resources = resources;
+      }
+      
+      public String getType() {
+        return type;
+      }
+
+      public Map<String, String> getAttribs() {
+        return attribs;
+      }
+      
+      public List<Resource> getResources() {
+        return resources;
+      }
+    }
+    
+    static class Resource {
+      private final Uri source;
+      private final String content;
+      private final Map<String, String> attribs;
+      
+      private Resource(Uri source, String content, Map<String, String> attribs) {
+        this.source = source;
+        this.content = content;
+        this.attribs = Collections.unmodifiableMap(attribs);
+      }
+      
+      public Uri getSource() {
+        return source;
+      }
+      
+      public String getContent() {
+        return content;
+      }
+      
+      public Map<String, String> getAttribs() {
+        return attribs;
+      }
+    }
+  }
+}