You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by jw...@apache.org on 2013/07/25 18:58:40 UTC

svn commit: r1507065 - in /myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad: change/InsertedComponentFragmentLocator.java change/InsertingComponentFragmentLocator.java util/ClassLoaderUtils.java util/ComponentUtils.java

Author: jwaldman
Date: Thu Jul 25 16:58:39 2013
New Revision: 1507065

URL: http://svn.apache.org/r1507065
Log:
TRINIDAD-2397 Provide API to discover location of document that contains tag definition for a given component 
Thanks to Prakash Udupa for the patch

Added:
    myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/InsertedComponentFragmentLocator.java
    myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/InsertingComponentFragmentLocator.java
Modified:
    myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/ClassLoaderUtils.java
    myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/ComponentUtils.java

Added: myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/InsertedComponentFragmentLocator.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/InsertedComponentFragmentLocator.java?rev=1507065&view=auto
==============================================================================
--- myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/InsertedComponentFragmentLocator.java (added)
+++ myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/InsertedComponentFragmentLocator.java Thu Jul 25 16:58:39 2013
@@ -0,0 +1,64 @@
+/*
+ * 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.myfaces.trinidad.change;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+
+/**
+ * Strategy implemented for <em>tags</em> that insert document fragments, returning the URL of the
+ *  fragment for any top-level UIComponents inserted by the tag. When determining the URL of the
+ *  document that defines the tag corresponding to a target component, clients will call
+ *  <code>getFramentUrlForInsertedComponent</code> for each registered
+ *  <code>InsertedComponentFragmentLocator</code> on each UIComponent starting from the target
+ *  component and upto the UIViewRoot and each registered InsertingComponentFragmentLocator on each
+ *  component from the parent of the target component to the UIViewRoot. If a non-null URL string
+ *  is returned, the walk of the ancestor chain halts and the returned value is considered the
+ *  URL string for the document for the target component. If the registered listeners return
+ *  <code>null</code> for every component in the ancestor chain, the containing URL is assumed to be
+ *  the URL of the enclosing page.
+ *
+ * Implementations of InsertedComponentFragmentLocator are registered using the normal
+ *  Service Provider Interface pattern.  A text file named
+ *  "org.apache.myfaces.trinidad.change.InsertedComponentFragmentLocator" is placed in the
+ *  META-INF/services directory. This file contains the fully qualified class names of all the
+ *  InsertedComponentFragmentLocator strategy to register.
+ *
+ * @see InsertingComponentFragmentLocator
+ */
+public abstract class InsertedComponentFragmentLocator
+{
+  /**
+   * Returns the URL string of the fragment that contains tag corresponding to 
+   *  <code>componentToTest</code>, or <code>null</code> if this InsertedComponentFragmentLocator 
+   *  could not determine the URL.
+   * @param context         The FacesContext instance for current request
+   * @param componentToTest The component to determine the fragment URL for. 
+   *                         <code>componentToTest</code> will be the <code>targetComponent</code>, 
+   *                         or one of its ancestors. EL context will NOT be setup for 
+   *                         <code>componentToTest</code> when this method is called.
+   * @param targetComponent The target component for which we are ultimately trying to determine the 
+   *                         fragment URL. It is assumed that this URL will be same as the URL 
+   *                         obtained for <code>componenToTest</code>
+   */
+  public abstract String getFragmentUrlForInsertedComponent(
+    FacesContext context, 
+    UIComponent  componentToTest, 
+    UIComponent  targetComponent);
+}

Added: myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/InsertingComponentFragmentLocator.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/InsertingComponentFragmentLocator.java?rev=1507065&view=auto
==============================================================================
--- myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/InsertingComponentFragmentLocator.java (added)
+++ myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/change/InsertingComponentFragmentLocator.java Thu Jul 25 16:58:39 2013
@@ -0,0 +1,62 @@
+/*
+ * 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.myfaces.trinidad.change;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+
+/**
+ * Strategy implemented for <em>components</em> that insert document fragments to return the URL of
+ *  the fragment currently inserted by this component. When determining the URL of the
+ *  document that defines the tag corresponding to a target component, clients will call
+ *  <code>getFramentUrlForInsertedComponent</code> for each registered
+ *  <code>InsertedComponentFragmentLocator</code> on each UIComponent starting from the target
+ *  component and upto the UIViewRoot and each registered InsertingComponentFragmentLocator on each
+ *  component from the parent of the target component to the UIViewRoot. If a non-null URL string
+ *  is returned, the walk of the ancestor chain halts and the returned value is considered the
+ *  URL string for the document for the target component. If the registered listeners return
+ *  <code>null</code> for every component in the ancestor chain, the containing URL is assumed to be
+ *  the URL of the enclosing page.
+ *
+ * Implementations of InsertingComponentFragmentLocator are registered using the normal
+ *  Service Provider Interface pattern.  A text file named
+ *  "org.apache.myfaces.trinidad.change.InsertingComponentFragmentLocator" is placed in the
+ *  META-INF/services directory. This file contains the fully qualified class names of all the
+ *  InsertingComponentFragmentLocator strategy to register.
+ *
+ * @see InsertedComponentFragmentLocator
+ */
+public abstract class InsertingComponentFragmentLocator
+{
+  /**
+   * Returns the URL string of the fragment inserted by <code>componentToTest</code>, or 
+   *  <code>null</code> if this InsertingComponentFragmentLocator could not determine the URL.
+   * @param context         The FacesContext instance for current request
+   * @param componentToTest The component that possibly inserted the targetComponent. This component
+   *                         will be used to determine the fragment URL for targetComponent.
+   *                         <code>componentToTest</code> will be an ancestor of the 
+   *                         <code>targetComponent</code>. EL context will NOT be setup for 
+   *                         <code>componentToTest</code> when this method is called.
+   * @param targetComponent The component to determine the fragment URL for
+   */
+  public abstract String getInsertedFragmentUrl(
+    FacesContext context, 
+    UIComponent  componentToTest, 
+    UIComponent  targetComponent);
+}

Modified: myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/ClassLoaderUtils.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/ClassLoaderUtils.java?rev=1507065&r1=1507064&r2=1507065&view=diff
==============================================================================
--- myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/ClassLoaderUtils.java (original)
+++ myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/ClassLoaderUtils.java Thu Jul 25 16:58:39 2013
@@ -19,9 +19,8 @@
 package org.apache.myfaces.trinidad.util;
 
 import java.io.BufferedReader;
-import java.io.InputStream;
 import java.io.IOException;
-
+import java.io.InputStream;
 import java.io.InputStreamReader;
 
 import java.net.URL;
@@ -31,9 +30,10 @@ import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.List;
-
 import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
 
+import org.apache.myfaces.trinidad.context.RequestContext;
 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
 
 /**
@@ -257,7 +257,42 @@ public final class ClassLoaderUtils
   {
     return getServices(service.getName());
   }
+  
+  /**
+   * Gets all the registered services of the supplied type. Once instantiated, the services are
+   *  cached in the application scoped storage, and any further requests to this function will
+   *  get the cached services. It is assumed that the name of the service file and the type of
+   *  the service class is the same.
+   * @param service The class type of the service
+   * @return The list of registered service instances
+   */
+  static <T> List<T> __getCachableServices(Class<T> service)
+  {
+    List<T> services = Collections.emptyList();
+    String serviceName = service.getName();
+    ConcurrentMap<String, Object> concurrentAppMap =
+      RequestContext.getCurrentInstance().getApplicationScopedConcurrentMap();
+    Object serviceObject = concurrentAppMap.get(serviceName);
 
+    if (serviceObject == null)
+    {
+      services = ClassLoaderUtils.getServices(service);
+      Object oldValue = concurrentAppMap.putIfAbsent(serviceName, services);
+      
+      // if a different thread raced us and added one, use that
+      if (oldValue != null)
+      {
+        services = (List<T>) oldValue;
+      }
+    }
+    else
+    {
+      services = (List<T>) serviceObject;
+    }
+    
+    return services;
+  }
+  
   /**
    * Instantiate all available services from a file in /META-INF/services.
    * <P>

Modified: myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/ComponentUtils.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/ComponentUtils.java?rev=1507065&r1=1507064&r2=1507065&view=diff
==============================================================================
--- myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/ComponentUtils.java (original)
+++ myfaces/trinidad/trunk/trinidad-api/src/main/java/org/apache/myfaces/trinidad/util/ComponentUtils.java Thu Jul 25 16:58:39 2013
@@ -19,21 +19,21 @@
 package org.apache.myfaces.trinidad.util;
 
 import java.util.Date;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.TimeZone;
 
 import javax.faces.component.NamingContainer;
 import javax.faces.component.UIComponent;
-
 import javax.faces.component.UIViewRoot;
-
 import javax.faces.component.visit.VisitContext;
-
-import org.apache.myfaces.trinidad.component.UIXComponent;
 import javax.faces.context.FacesContext;
 
+import org.apache.myfaces.trinidad.change.InsertedComponentFragmentLocator;
+import org.apache.myfaces.trinidad.change.InsertingComponentFragmentLocator;
 import org.apache.myfaces.trinidad.component.FlattenedComponent;
+import org.apache.myfaces.trinidad.component.UIXComponent;
 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
 
 /**
@@ -440,6 +440,89 @@ public final class ComponentUtils
   }
   
   /**
+   * Gets the location (context relative path) of the document that contains the tag definition 
+   *  corresponding to the supplied component. This implementation uses the 
+   *  InsertedComponentFragmentLocator and InsertingComponentFragmentLocator strategies to determine 
+   *  the document path for the supplied component.
+   * All registered InsertedComponentFragmentLocator services are first asked for location of the 
+   *  document for the supplied component first. If the locator could not be obtained, then this 
+   *  implementation will walk up the component tree and for every ancestor until the view root, 
+   *  call all the registered InsertedComponentFragmentLocator and InsertingComponentFragmentLocator 
+   *  services to obtain the location of the document. If in this process any non-null value is 
+   *  obtained for the document location, the ancestor processing is halted and the value is 
+   *  returned.
+   *  
+   * @param context   The FacesContext instance for the current request
+   * @param component The component for which the document path is to be determined
+   * @return Location of the document fragment that contains the tag corresponding to the supplied
+   *          component. Returns null if the path cannot be determined.
+   */
+  public static String getDocumentLocationForComponent(
+    FacesContext context,
+    UIComponent component)
+  {
+    UIViewRoot viewRoot = context.getViewRoot();
+    
+    // if we are dealing with view root, look no further
+    if (component == viewRoot)
+    {
+      return viewRoot.getViewId();
+    }
+    
+    String location = null;
+    
+    List<InsertedComponentFragmentLocator> insertedComponentFragmentLocators = 
+      ClassLoaderUtils.__getCachableServices(InsertedComponentFragmentLocator.class);
+    
+    // 1. try to get the path for the supplied component
+    location = _getInsertedComponentLocation(context,
+                                             insertedComponentFragmentLocators,
+                                             component,
+                                             component);
+    
+    // 2. try to get the path for the supplied component using its ancestors
+    if (location == null)
+    {
+      List<InsertingComponentFragmentLocator> insertingComponentFragmentLocators = 
+        ClassLoaderUtils.__getCachableServices(InsertingComponentFragmentLocator.class);
+      
+      UIComponent currAncestor = component.getParent();
+      
+      while (currAncestor != null)
+      {
+        if (currAncestor == viewRoot)
+        {
+          return viewRoot.getViewId();
+        }
+        
+        // 2a. try to get the location where the tag for ancestor is defined
+        location = _getInsertedComponentLocation(context,
+                                                 insertedComponentFragmentLocators,
+                                                 currAncestor,
+                                                 component);
+
+        if (location == null)
+        {
+          // 2b. try to get the location of the fragment that the ancestor could have inserted
+          location = _getInsertingComponentLocation(context,
+                                                   insertingComponentFragmentLocators,
+                                                   currAncestor,
+                                                   component);
+          
+          if (location != null)
+          {
+            break;
+          }
+        }
+        
+        currAncestor = currAncestor.getParent();
+      }
+    }
+    
+    return location;
+  }
+  
+  /**
    * Gets the scoped identifier for the target component. The scoping will be
    * within a subtree rooted by the supplied base component. If the supplied
    * base component were to be the view root, the returned id will be the 
@@ -566,6 +649,58 @@ public final class ComponentUtils
     return null;
   }
 
+  /**
+   * Iterates through all the registered InsertedComponentFragmentLocator services and gets
+   *  the location of the fragment document where the tag corresponding to the supplied 
+   *  componentToTest is defined. Returns null if the location cannot be determined.
+   */
+  private static String _getInsertedComponentLocation(
+    FacesContext context,
+    List<InsertedComponentFragmentLocator> insertedComponentFragmentLocators,
+    UIComponent componentToTest,
+    UIComponent targetComponent)
+  {
+    for (InsertedComponentFragmentLocator 
+           insertedComponentFragmentLocator :  insertedComponentFragmentLocators)
+    {
+      String location = 
+        insertedComponentFragmentLocator.getFragmentUrlForInsertedComponent(context,
+                                                                            componentToTest,
+                                                                            targetComponent);
+      if (location != null)
+      {
+        return location;
+      }
+    }
+    return null;
+  }
+  
+  /**
+   * Iterates through all the registered InsertingComponentFragmentLocator services and gets
+   *  the location of the fragment document that the supplied componentToTest could have possibly
+   *  inserted. Returns null if the location cannot be determined.
+   */
+  private static String _getInsertingComponentLocation(
+    FacesContext context,
+    List<InsertingComponentFragmentLocator> insertingComponentFragmentLocators,
+    UIComponent componentToTest,
+    UIComponent targetComponent)
+  {
+    for (InsertingComponentFragmentLocator 
+           insertingComponentFragmentLocator :  insertingComponentFragmentLocators)
+    {
+      String location = 
+        insertingComponentFragmentLocator.getInsertedFragmentUrl(context,
+                                                                 componentToTest,
+                                                                 targetComponent);
+      if (location != null)
+      {
+        return location;
+      }
+    }
+    return null;
+  }
+  
   private static boolean _isFlattening(FacesContext context, UIComponent component)
   {
     return ((component instanceof FlattenedComponent) &&