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/10/17 18:47:47 UTC

svn commit: r1533151 - /myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/provider/BaseSkinProvider.java

Author: jwaldman
Date: Thu Oct 17 16:47:47 2013
New Revision: 1533151

URL: http://svn.apache.org/r1533151
Log:
TRINIDAD-2406 externalize skin repositories by using SkinProvider SPI
Forgot to add the new BaseSkinProvider.java file.
Thanks to Anand V Nath for the patch
reviewed by Prakash Udupa and Jeanne Waldman

Added:
    myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/provider/BaseSkinProvider.java

Added: myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/provider/BaseSkinProvider.java
URL: http://svn.apache.org/viewvc/myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/provider/BaseSkinProvider.java?rev=1533151&view=auto
==============================================================================
--- myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/provider/BaseSkinProvider.java (added)
+++ myfaces/trinidad/trunk/trinidad-impl/src/main/java/org/apache/myfaces/trinidadinternal/skin/provider/BaseSkinProvider.java Thu Oct 17 16:47:47 2013
@@ -0,0 +1,466 @@
+/*
+ * 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.trinidadinternal.skin.provider;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.faces.context.FacesContext;
+
+import org.apache.myfaces.trinidad.logging.TrinidadLogger;
+import org.apache.myfaces.trinidad.skin.Skin;
+import org.apache.myfaces.trinidad.skin.SkinMetadata;
+import org.apache.myfaces.trinidad.skin.SkinProvider;
+import org.apache.myfaces.trinidad.skin.SkinVersion;
+import org.apache.myfaces.trinidadinternal.renderkit.core.xhtml.XhtmlConstants;
+
+/**
+ * This is the common base class for Trinidad SkinProviders. This class abstracts out some common code that is useful
+ * across Trinidad internal SkinProviders. One such example worth mentioning is the code to finding a Skin match
+ * for a given SkinMetadata search criteria.
+ * @See TrinidadBaseSkinProvider
+ * @See TrinidadSkinProvider
+ * @See ExternalSkinProvider
+ */
+public abstract class BaseSkinProvider extends SkinProvider
+{
+  public BaseSkinProvider()
+  {
+    initSkins();
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Skin getSkin(FacesContext context, SkinMetadata skinMetadata)
+  {
+    synchronized (this)
+    {
+      return _getMatchingSkin(context, skinMetadata);
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Collection<SkinMetadata> getSkinMetadata(FacesContext context)
+  {
+    synchronized (this)
+    {
+      return Collections.unmodifiableCollection(_skins.keySet());
+    }
+  }
+
+  /**
+   * add method used to register a skin with the provider.
+   * @param metadata
+   * @param skin
+   * @return Any previously registered skins existing with the same metadata
+   */
+  public Skin addSkin(SkinMetadata metadata, Skin skin)
+  {
+    if (metadata == null || skin == null)
+    {
+      _LOG.warning("CANNOT_ADD_SKIN");
+      return null;
+    }
+
+    if (_LOG.isFine())
+      _LOG.fine(this + " Adding skin to cache: [" + metadata + ", " + skin + "]");
+
+    synchronized (this)
+    {
+      return _skins.put(metadata, skin);
+    }
+  }
+
+  /**
+   * template method to be implemented by subclasses with logic to load a skin
+   * that base class knows as available with the current skin provider being asked
+   * to load the skin
+   * @param context
+   * @param skinMetadata
+   */
+  protected abstract Skin loadAvailableSkin(FacesContext context, SkinMetadata skinMetadata);
+
+  /**
+   * getter for the skins, only used by ExternalSkinProvider.reload to cache and restore the skins if reload fails
+   * @return
+   */
+  protected Map<SkinMetadata, Skin> getSkins()
+  {
+    synchronized (this)
+    {
+      return _skins;
+    }
+  }
+
+  /**
+   * setter for the skins, only used by ExternalSkinProvider.reload to cache and restore the skins if reload fails
+   * @param skins
+   */
+  protected void setSkins(Map<SkinMetadata, Skin> skins)
+  {
+    synchronized (this)
+    {
+      // do not allow a null to be set.
+      if (skins == null)
+        skins = new HashMap<SkinMetadata, Skin>();
+
+      _skins = skins;
+    }
+  }
+
+  /**
+   * initialize a new HashMap for the skins,
+   * used by constructor and ExternalSkinProvider.reload
+   */
+  protected void initSkins()
+  {
+    synchronized (this)
+    {
+      _skins = new HashMap<SkinMetadata, Skin>();
+    }
+  }
+
+  /**
+   * Hook to do any kind of initialization before loading a skin or skin metadata
+   * sub classes can choose to implement
+   * @param context
+   */
+  protected void initialize(FacesContext context)
+  {
+  }
+
+  /**
+   * one point method for searching skins
+   * @param context
+   * @param searchMetadata to search for
+   * @return matching skin
+   */
+  private final Skin _getMatchingSkin(FacesContext context, SkinMetadata searchMetadata)
+  {
+    if (searchMetadata == null)
+      throw new NullPointerException("SkinMetadata passed for search is null");
+
+    // find a skin that is already loaded
+    Skin availableSkin = _skins.get(searchMetadata);
+    if (availableSkin != null)
+      return availableSkin;
+
+    // there is no skin already available, so find a skin which matches the search criteria
+    // verify that either id or family is set for the search
+    if (null == searchMetadata.getId() && null == searchMetadata.getFamily())
+    {
+      if (_LOG.isWarning())
+        _LOG.warning("SP_CANNOT_FIND_SKIN_WITHOUT_FAMILY_OR_ID");
+
+      throw new NullPointerException(_LOG.getMessage("SP_CANNOT_FIND_SKIN_WITHOUT_FAMILY_OR_ID"));
+    }
+
+    initialize(context);
+
+    if (_LOG.isFine())
+      _LOG.fine("SP_FINDING_SKIN_FOR", new Object[]{searchMetadata.toString()} );
+
+    // first check if a skin matching the requirement is present in this provider
+    SkinMetadata availableMetadata = _findSkinMetadata(context, searchMetadata);
+
+    if (availableMetadata == null)
+    {
+      if (_LOG.isFine())
+        _LOG.fine(this + " Cannot find skin in this provider: " + searchMetadata);
+
+      return null;
+    }
+
+    // find a skin that is already loaded
+    // this is different from doing it at the beginning of the method
+    // because the metadata with which the skin is added into _skins
+    // may not have matched completely, since searchMetadata can provide
+    // only certain conditions.
+    availableSkin = _skins.get(availableMetadata);
+    if (availableSkin != null)
+      return availableSkin;
+
+    // now we know that there is a skin available but not loaded
+    // so load the skin
+    availableSkin = loadAvailableSkin(context, availableMetadata);
+    assert (availableSkin != null);
+
+    _skins.put(availableMetadata, availableSkin);
+
+    return availableSkin;
+  }
+
+  /**
+   * find if there is a skin with the search condition supported by the current SkinProvider
+   * @param search
+   * @return
+   */
+  private SkinMetadata _findSkinMetadata(FacesContext context, SkinMetadata search)
+  {
+    SkinMetadata matchingSkinMetadata = null;
+    String skinId = search.getId();
+
+    Collection<SkinMetadata> skinMetadata = getSkinMetadata(context);
+
+    if (skinMetadata == null || skinMetadata.isEmpty())
+      return null;
+
+    // search is with either id or (family, renderkit, version)
+    // we have ensure that either id or family is passed in the search
+    if (skinId != null)
+    {
+      // Id is available then search using ID
+      for (SkinMetadata m : skinMetadata)
+        if (skinId.equals(m.getId()))
+        {
+          matchingSkinMetadata = m;
+        }
+
+      if (matchingSkinMetadata == null)
+      {
+        if (_LOG.isFine())
+        {
+          _LOG.fine("SP_CANNOT_FIND_MATCHING_SKIN_ID", new Object[]{skinId});
+        }
+      }
+    }
+    else
+    {
+      // search using family, renderkit, version
+      String family = search.getFamily();
+
+      // we need atleast the family to go on with the search
+      if (family == null)
+        return null;
+
+      // renderkit id cannot be null, we initialize it to APACHE_TRINIDAD_DESKTOP
+      String renderKit = search.getRenderKitId();
+
+      // version cannot be null as we initialize it to SkinVersion.EMPTY_SKIN_VERSION
+      SkinVersion version = search.getVersion();
+
+      List<SkinMetadata> familyRenderKitMatches = new ArrayList<SkinMetadata>(2);
+
+      // search using family and renderkit id
+      for (SkinMetadata m : skinMetadata)
+        if (family.equalsIgnoreCase(m.getFamily()) && renderKit.equalsIgnoreCase(m.getRenderKitId()))
+          familyRenderKitMatches.add(m);
+
+      if (familyRenderKitMatches.isEmpty())
+      {
+        // if we get here, that means we couldn't find an exact
+        // family/renderKitId match, so return the simple skin
+        // that matches the renderKitId.
+        if (_LOG.isFine())
+        {
+          _LOG.fine("SP_CANNOT_FIND_MATCHING_SKIN", new Object[]{family, renderKit});
+        }
+
+        return null;
+      }
+
+      // at this point we know we have something in the familyRenderKitMatches
+      // which is a list of matching family and renderKitId skins. Now match the version
+      // to find the best matched skin.
+      String versionName =  version.getName();
+      boolean versionIsDefault = version.isDefault();
+      boolean foundMatchingSkin = false;
+
+      // if the user didn't ask for the 'default' version, then look for the exact match
+      if (!versionIsDefault)
+      {
+        matchingSkinMetadata = _findSkinMetadataForVersionName(familyRenderKitMatches, version);
+      }
+
+      // matchingSkinMetadata will be null if an exact version match (family+renderKitId+version) was not found;
+      // or if user was asking for a default version
+      // we can have an exact version match if the user asks for null version, and we find a skin with no
+      // version set.
+      if (matchingSkinMetadata == null || versionIsDefault)
+      {
+        // at this point either user is looking for default skin
+        // or did not find the exact version match specified
+        // so we find the default skin
+        matchingSkinMetadata = _findSkinMetadataWithDefaultVersion(familyRenderKitMatches);
+
+        if (matchingSkinMetadata == null)
+        {
+          // if we fail to find a default skin then
+          // get the latest skin in the matchingSkinList if there is no skin marked default.
+          if (_LOG.isFine())
+            _LOG.fine(this + " did not find default / version skinMetadata. so getting leaf skin");
+
+          matchingSkinMetadata = _findLeafSkinMetadata(familyRenderKitMatches);
+        }
+        else if ((matchingSkinMetadata != null) && versionIsDefault)
+        {
+          // found the default skin the user wanted
+          if (_LOG.isFine())
+            _LOG.fine(this + " found default skinMetadata");
+
+          foundMatchingSkin = true;
+        }
+      }
+
+      // log messages
+      if (foundMatchingSkin)
+      {
+        if (_LOG.isFine())
+          _LOG.fine("GET_SKIN_FOUND_SKIN_VERSION",
+                    new Object[]{family, version, matchingSkinMetadata.getId()});
+      }
+      else if (matchingSkinMetadata != null)
+      {
+        if(_LOG.isFine())
+        {
+          if ("".equals(versionName))
+          {
+            _LOG.fine("GET_SKIN_CANNOT_FIND_NO_VERSION",
+                         new Object[]{family, matchingSkinMetadata.getId()});
+          }
+          else
+          {
+            _LOG.fine("GET_SKIN_CANNOT_FIND_SKIN_VERSION",
+                         new Object[]{family, version, matchingSkinMetadata.getId()});
+          }
+        }
+      }
+
+      if (matchingSkinMetadata == null)
+      {
+        matchingSkinMetadata = familyRenderKitMatches.get(familyRenderKitMatches.size() - 1);
+      }
+    }
+
+
+
+    if (matchingSkinMetadata != null)
+      if (_LOG.isFine())
+        _LOG.fine(this + " found matching metadata: " + matchingSkinMetadata);
+
+    return matchingSkinMetadata;
+  }
+
+  /**
+   * find a skin with version passed
+   * @param skins
+   * @param version
+   * @return
+   */
+  private SkinMetadata _findSkinMetadataForVersionName(Collection<SkinMetadata> skins, SkinVersion version)
+  {
+    if (version == null)
+      throw new IllegalArgumentException("skin version cannot be null");
+
+    if (version.getName() == null || version.getName().isEmpty())
+      return null;
+
+    for (SkinMetadata metadata : skins)
+    {
+      // metadata cannot be null and also version inside it cannot be null
+      if (version.getName().equals(metadata.getVersion().getName()))
+      {
+        if (_LOG.isFine())
+          _LOG.fine("Found version match skinMetadata: " + metadata);
+
+        return metadata;
+      }
+    }
+
+    return null;
+  }
+
+  /**
+   * Latest skin is the one which is last in the family hierarchy
+   * eg: fusion-v1 -> fusion-v2 -> fusion-v3
+   * Among this fusion-v3 is the latest. So we look for a skin that is
+   * not extended by any other skin in the family.
+   * @param skins
+   * @return
+   */
+  private SkinMetadata _findLeafSkinMetadata(Collection<SkinMetadata> skins)
+  {
+    List<SkinMetadata> leafSkinMetadata = new ArrayList<SkinMetadata>();
+    List<String> parentIds = new ArrayList<String>();
+
+    // collect parents skins among the list
+    for (SkinMetadata metadata : skins)
+    {
+      String parentId = metadata.getBaseSkinId();
+      if (parentId != null)
+        parentIds.add(metadata.getBaseSkinId());
+    }
+
+    // find leaf skins, which is not in parent list
+    for (SkinMetadata metadata : skins)
+    {
+      String skinId = metadata.getId();
+      if (skinId != null && !parentIds.contains(skinId))
+      {
+        leafSkinMetadata.add(metadata);
+      }
+    }
+
+    // if there are no leaves, return null
+    // this is a rare case, almost impossible since there will be
+    // at least one skin which is not parent of another
+    // but let us cover the corner case if any
+    if (leafSkinMetadata.isEmpty())
+      return null;
+
+    // if there is one leaf skin return that
+    // if there are many, return the last one among the leaves
+    return leafSkinMetadata.get(leafSkinMetadata.size() - 1);
+  }
+
+  /**
+   * find a skin that has its SkinVersion set to 'default', if it exists.
+   * @param matchingSkinList A list of Skins that we will look through to find the 'default'.
+   * @return Skin with SkinVersion isDefault true, otherwise, null.
+   */
+  private SkinMetadata _findSkinMetadataWithDefaultVersion(Collection<SkinMetadata> matchingSkinList)
+  {
+    for (SkinMetadata metadata : matchingSkinList)
+    {
+      SkinVersion skinVersion = metadata.getVersion();
+
+      if (skinVersion != null && skinVersion.isDefault())
+      {
+        if (_LOG.isFine())
+          _LOG.fine("Found default skinMetadata: " + metadata);
+
+        return metadata;
+      }
+    }
+
+    return null;
+  }
+
+  private Map<SkinMetadata, Skin> _skins;
+  private final static TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(BaseSkinProvider.class);
+}
\ No newline at end of file