Hi Simon,
I'm a little unclear on the process. Can you explain? Like did you come up with these ideas and then we all discuss them or is there an Expert Group you will send these to ( you say that at the end) and they will be the primary owner of this project? Is the idea that we would someday take skinning out of Trinidad and it would use these libraries?

Thanks,
Jeanne

Simon Lessard wrote, On 12/5/2008 10:21 AM PT:
I agree with most of these statements, but I would start even higher than that, like defining the base interfaces and access classes.

For instance, I think it's safe to assume that a Skin class or interface should exists. Let assume all interfaces for now as it takes less characters. Now, from where and how should the Skin be available for a given FacesContext. Personally I believe a Skin should be linked to a RenderKit (or a common base RenderKit class for all MyFaces extensions if not in the standard), but let assume work on the API for now for the sake of the discussion.

So for now we have:

public interface Skin
{
    public String getResourceBundle();
}

public abstract class RenderKit
{
    public abstract Skin getSkin(FacesContext context);
}

So far so good, but now that opens some issues that will themselves explode into more:

   1. Should the way the RenderKit locates the current Skin standardized?
   2. How do we define the contract ensuring that the Skin get
      rendered on the page?
   3. What other functionalities should be standarized in the Skin
      interface?

*Issue 1: Current skin localization*
Personally I chose yes to that question since it allow a standardized way to define skins. Let use Trinidad's skin-family notion here, but with the way renderKitId is defined, that's the view root and a factory implementing decorator pattern. So we add

public class UIViewRoot
{
    public String getSkinFamily();
    public void setSkinFamily(String skinFamily);
}

public abstract class SkinFactory implements FacesWrapper<SkinFactory>
{
public abstract void addSkin(String skinFamily, String renderKitId, Skin skin);
    public abstract Skin getSkin(String skinFamily, String renderKitId);
    public abstract Collection<String> getSkinFamilies();
    public SkinFactory getWrapped()
    {
        return null;
    }
}

*Sub-issue 1.1: Default SkinFactory implementation behavior*
Additional Exceptions
public class SkinException extends FacesException
{
    // Provides all 4 common Exception constructors
}

public class SkinInitializationException extends SkinException
{
    // Provides all 4 common Exception constructors
}

Additional interface
/**
* Implementations of this interface are in charge of creating Skin instances in an implementation specific manner.
 */
public interface SkinLoader
{
    /**
* Creates a new <code>Skin</code> instance the specified skinFamily and parameters. There's no garanteed as of what * parameters will be present in the Map argument and the creation should fails if those parameters are not correct
     * for the specific implementation.
     *
* @throws SkinInitializationException if the provided parameter are not enough to create the <code>Skin</code> instance
     */
public Skin loadSkin(String skinFamily, String resourceBundle, Map<String, String> parameters);
}

Addition to faces-config.xml:
<application>
  <default-skin-family/>
</application>
<skin>
  <skin-family/>
  <render-kit-id/>
  <description/>
  <skin-loader-class/>
  <resource-bundle/>
  <skin-loader-parameters>
    <map-entries/>
  </skin-loader-parameters>
</skin>

Note that this solution makes RenderKit.getSkin more an utility method than anything, except if an extension decides to override it (would probably be the casew ith Trinidad, at least to start to use trinidad-config.xml, or later to add .pda or .desktop to the current skinFamily).

Also, the framework should probably provide some implementations of SkinLoader out-of-the-box, most likely ClassSkinLoader expecting a skinClass parameter and a CSSSkinLoader receiving a cssResourcePath parameter. (Trinidad would mostl ikely provide an XSSSkinLoader for example)

*Issue 2: Skin content rendering*
How should rendering be handled? Should it be handled as an added ResourceDependency just before rendering or as an additional contract for the default HtmlRenderer for HtmlHead? The latter sounds better, but how should it be implemented? target would obviously be "head", but then a public Resource Skin.getContentResource() method could be required and this might imply some changes to the ResourceHandler specification so that it can locate the Skin instance in order to be able to serve the Resource's content (effective CSS content) to the agent. I like the Resource version, but I have more thinking to do about how to implement this, maybe involving a reserved resourceIdentifier structure/syntax

*Issue 3: Skin features*
Additional class
public abstract class Icon extends Resource
{
    /**
* Gets the key within the skin's ResourceBundle to the description of this icon. The description can be used * as an alternate text for the icon to improve accessibility for example.
     */
    public abstract String getDescriptionKey();
/**
     * Gets the name of this icon.
     */
    public abstract String getName();

    /**
     * Gets the effective style class of this icon within the skin.
     */
    public abstract String getStyleClass();
}

public interface Skin
{
    public Icon getIcon(String iconName);

    public String getProperty(String propertyKey);

    public String getResourceBundle();

    public String getStyleClass(String selectorKey);
}

*Sub-issue 3.1: style class / property / icon access strategy*
Trinidad uses String keys to access style classes within the skin. That strategy is very flexible, but alas very slow, thanks to String.hashCode() being linear and uncached. So, even if String.intern() is called on all selectors within a given skin, the gain will only be on String.equals, making it O(1) instead of O(n). Even if this is better than nothing, especially it there's a collision, the overall complexity of looking up a selector remains O(n) with the length of the key. So, the overall cost of skinning with this strategy is averageLengthOfKey * averageAmountOfComponentPerPage * averageAmountOfHtmlElementPerComponent (assuming each element gets its own style class which is required for maximum customizability). Another, but a little bit more complex, solution is also possible using a "dynamic enum" alike to PropertyKey in Trinidad. So I'd like something like:

public abstract class SkinKeyPart
{
    public String name();

    public int ordinal();
}

public class Namespace extends SkinKeyPart
{
    /**
     * Find or create the Namespace instance with the specified name.
     */
    public static Namespace getInstance(String name);
}

public class ComponentKey extends SkinKeyPart
{
    /**
     * Find or create the ComponentKey instance with the specified name.
     */
    public static ComponentKey getInstance(String name);
}

public class ComponentPartKey extends SkinKeyPart
{
    /**
* Find or create the ComponentPartKey instance with the specified name.
     */
    public static ComponentPartKey getInstance(String name);
}

public class ComponentStateKey extends SkinKeyPart
{
    /**
* Find or create the ComponentStateKey instance with the specified name.
     */
    public static ComponentPartKey getInstance(String name);
}

public abstract class SkinNode
{
    public abstract String[] getStyleClasses();
    public abstract String getProperty(String propertyName);
    public abstract Icon getIcon(String iconName);
    public abstract SkinNode getSubPart(ComponentPartKey partKey);
}

public interface Skin
{
    /**
* Still exists to get properties global to the whole skin if someone ever need that feature
     */
    public String getProperty(String propertyName);

    /**
     * No change here
     */
    public String getResourceBundle();

    public String getSkinNode(Namespace ns, ComponentKey component);

public String getSkinNode(Namespace ns, ComponentKey component, ComponentStateKey state);

public String getSkinNode(Namespace ns, ComponentKey component, ComponentStateKey... states);
}

That solution has the advantage of being very fast, actually a real O(1) for every node access assuming the Renderer gathers the keys it needs during instanciation. Then the "root" SkinNode could probably be located using a base Renderer class and passed to a custom encoding method (Trinidad's encodeAll for example). Of course, the skin loaders would have to create skin instances structuring the selectors accordingly. A base class for Skin with such feature should probably be provided if this solution is wanted in order to make new implementation of Skin easier to create.

*PENDIND issues:*
PENDING: Should the path leverage the Resource API instead and receive a library-name, library-version, resource-name and resource-version instead?

PENDING: A CompositeSkin class should probably be provided and most likely usable from the configuration, should it be using a specific loader with parameters or have a fully-fledged tag like <composite-skin> for example? The use of such skin implementation would be for users using multiple libraries, each with very different RenderKit implementations. A composite RenderKit class could also be useful here.

PENDING: Should Trinidad's SkinAddition notion be considered? SkinAddition provides a way to add skin elements to an existing skin based on its id. It would be possible to leverage that by using the skin-family/render-kit-id tuple instead. Another option would be to simply have the default SkinFactory implementation to automatically agreggate the Skin instances with the same skin-family and render-kit-id into a CompositeSkin instance.

PENDING: Should additional metadata be added to <renderer> tag to define the supported skin selector for tooling purpose? This point is dependant on the content of the Skin interface and the strategy chosen to access the properties/style class of a given component and/or one of its sub-element.

PENDING: Should the effective CSS properties stay accessible in memory or otherwise from the Skin instance? If so, should they be modifiable? I tend toward no for both for performance issues. Of course this could be also left as an optional operation of the interface, throwing UnsupportedOperationException in most case but at least leaving the possibility for special implementations requiring it. This point also impact issue 2 in some ways.

PENDING: About Icon, it should probably also leverage the Resource API, how should that be implemented?

PENDING: Icon's styleClass, should the instance directly return an effective class or should it rather return a selector that would then be looked up from the Skin? I prefer the latter, but then there's the Stirng issue so maybe the icon would have to return an optimized selector key as mentioned in 3.1

PENDING: Solution to issue 3.1, although very fast and flexible, can be an linkage nightmare. For example, a node for component "inputText" with states "disabled" and "required" should be able to link to the component part node for part "label" of "inputText" without any state defined if needed. The performance gain of 3.1 is great, the implementation complexity isn't. Still, personally I don't have any problem throwing complexity at implementor's head if the users can feel the difference. Another option would be to remove the "state" complexity part and have a predefined style class syntax for those. That solution is what Trinidad does. However, this solution is not as flexible as it doesn't allow to switch Icon dynamically depending on the component's state.

PENDING: Solution 3.1 don't allow state to be applied on the component's sub parts. Most of the time it's not an issue, but we have one such use case with Trinidad's train component where the train itself doesn't have a state, but each of its station (sub-part) can be visited/unvisited and/or selected and/or disabled. If 3.1 is kept, should it be tweaked even more to allow a sub-part state on a per part basis as well? This makes the linkage a nightmare on Elm Street candidate, althoguh adding more potential to the Skin

PENDING: Trinidad supports a "selector redirect" feature that is used with the delegate renderer architecture to have specified selectorKeys transformed into a different one. This is implemented as a Map<String oldName, String newName> redirection map. Should such feature be standardized? If so and 3.1 is adressed then it will have to be well thought about. Also, trinidad support a single of those map applied as a set on the skin instance, I believe it should be a push / pop instead if standardized.

PENDING: Should there be any inheritance/cascading behavior defined by default? I tend toward no here.

PENDING: Should the skin have a getTranslatedResource(FacesContext context, String resourceKey) instead of the getResourceBundle method?


That's about it for now. My main blocker is 3.1 and I'd like to come up with something better for it before presenting it to the EG, same holds true with various links to the Resource API (icons, the skin itself, etc)


Regards,

~ Simon


On Fri, Dec 5, 2008 at 10:33 AM, Andrew Robinson <[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>> wrote:

    > All other frameworks use
    > component attributes for this, but Trinidad puts it in these
    > non-intuitive skinning keys.

    Sorry, had one more comment right after I hit send again. Maybe just
    having default component attributes for a web app would satisfy the
    need. Like a way to say 'partialSumbit' should be defaulted to true on
    all tr:commandLink. Then all the skin properties could be converted to
    attributes and have their defaults set in a common location (like what
    the skin does).

    This would be much more useful and flexible as then page developers
    can make exceptions to the rule.


Reply via email to