I would prefer a roadmap like:
1. Design a modular API that fits Trinidad's needs with minimal
compatibility loss.
2. In parallel
1. Start implementing the API defined in 1. as a myfaces
extension, possibily along with a ResourceHandler for JSF 1.2
2. Propose the API to JSF 2.0 EG
3. If the API make it the the spec then
1. Convert the extension to fit the new API (even if target
is 1.2 it should be possible but in a different package)
2. Integrate the API with the corect package naming to the
2.0 branch (obviously)
4. In the API doesn't get in the spec, then create a 2.0 branch of
the skin extension
5. Convert Trinidad to the new extension
6. Integrate the extension in Tomahawk and Tobago
So, for now, I would really really like to get comments from the
community on the API I proposed as well as potential solutions for the
pendings. Otherwise I'll have to propose it in its current form and
that would prevent the API to be reviewed by the MyFaces community and
that would be a shame imho. As I mentioned before, time is running
really short and the fact that 2.0 is in public review phase alone
might prevent the addition of such core API in the spec so the faster
we can design a great API, the more chances there is to see it
standardized.
Regards,
~ Simon
On Mon, Dec 8, 2008 at 4:22 AM, Gerhard Petracek
<[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>> wrote:
hello paul,
ok - let's continue with the case you mentioned.
we could:
- use the same approach of the myfaces shared module also for the
shared skinning code
- provide skinning as an independent jar file (it's easier for
other projects/custom components to re-use it)
regards,
gerhard
2008/12/8 Paul Rivera <[EMAIL PROTECTED]
<mailto:[EMAIL PROTECTED]>>
Hi,
It's good to hear that there's effort done to make skinning
standardized and part of JSF by Simon. Is there anything that
the current skinning project we have at
http://code.google.com/p/myfaces-csi/ can contribute to this?
In the event that Simon's ideas don't get integrated into JSF
2.0, I'd like to bring back the discussion to the skinning
project that we now have in the link mentioned above.
Overall, I think the community agrees with the idea of having
an independent skinning project that is basically derived from
trinidad code. I'd like to hear your opinion on the project
layout described in
http://code.google.com/p/myfaces-csi/wiki/MyfacesSkinsProposal.
What concerns me most here is that the skins project and
trinidad share a lot of classes that it is hard to get a clean
separation. You can check shared-impl for those classes.
Our current layout has a shared module (shared-api,
shared-impl). This module contains code shared by both
skinning and trinidad-core. We can probably move some of the
utility classes into myfaces-commons-utils. But other classes
there might not be such a good fit into myfaces-commons-utils.
We started this project with the most immediate goal of giving
tomahawk skinning support and eventually have trinidad use
this project. We've taken trinidad skinning code and now it
works with tomahawk and trinidad-core. In the event that this
project gets accepted into myfaces, integration with tomahawk
is not a big problem. I am actually more worried with the
changes we have to make in trinidad-core. This is why I'd
like to propose as a roadmap:
- finish the skinning project and make the necessary changes
in trinidad-core without adding new features yet to the
current set that trinidad has. There will be big changes in
trinidad-core that need to be done at the same time.
- once we verify that everything is working, we can then add
the desired new features/changes to the skinning api. In this
phase, we can implement the new features/changes to the api in
small and more manageable increments.
While Simon is in the process of submitting his proposal, I'd
like to work on these issues and anything else you think might
be a problem for the skinning project or the transition with
trinidad.
Best Regards,
Paul Rivera
--- On *Sat, 12/6/08, Gerhard Petracek
/<[EMAIL PROTECTED]
<mailto:[EMAIL PROTECTED]>>/* wrote:
From: Gerhard Petracek <[EMAIL PROTECTED]
<mailto:[EMAIL PROTECTED]>>
Subject: Re: [Skinning] Independent skinning myfaces
subproject
To: "MyFaces Development" <[email protected]
<mailto:[email protected]>>
Date: Saturday, December 6, 2008, 12:42 PM
hello simon,
no it's just because of ie version < 7 like it is
mentioned in the trinidad skinning documentation.
regards,
gerhard
2008/12/6 Simon Lessard <[EMAIL PROTECTED]
<mailto:[EMAIL PROTECTED]>>
Hi Gerhard,
Trinidad already has this and it would be the same
with what I propose. Assume: <tr:tree
styleClass="MyClass"/>
Then switch the selectors to: tr|tree.MyClass
As far as I can tell this is enough to cover every
possibilities without burdening the framework with an
extra attribute. Unless you can come up with a counter
example not involving IE6's shitty CSS support.
~ Simon
On Sat, Dec 6, 2008 at 2:19 PM, Gerhard Petracek
<[EMAIL PROTECTED]
<mailto:[EMAIL PROTECTED]>> wrote:
hello simon,
in my opinion andrew brought up an interesting
question. have you also thought about multiple
selectors for the same component?
i repeat the example i mentioned before:
<tr:tree/> ... uses default selector
<tr:tree selectorId="myId"/> ... uses selector
with "myId" to use a different selector
regards,
gerhard
2008/12/6 Simon Lessard <[EMAIL PROTECTED]
<mailto:[EMAIL PROTECTED]>>
Hi all,
Since I completed the proposition by memory
yesterday I forgot some important parts. Also,
I thought more about some possibilities I
rejected during the night and reconsidered my
those. So here is v2. Among other thing I
integrated skin extends, removed selector
redirection, removed some other PENDING and
simplified 3.1 suggestion since I discarded
redirection:
*Modified classes*
public abstract class Application
{
public String getDefaultSkinFamily();
public void setDefaultSkinFamily(String
skinFamily);
}
public final class FactoryFinder
{
public static final String SKIN_FACTORY;
}
public class UIViewRoot
{
public String getSkinFamily();
public void setSkinFamily(String skinFamily);
}
*Modified methods*
public abstract class ViewHandler
{
/**
* Same as before but also copy skin family.
*/
public abstract UIViewRoot
createView(FacesContext context, String viewId);
}
*New classes*
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 abstract class Skin
{
public abstract Resource
getContentResource(FacesContext context);
public abstract Icon getIcon(FacesContext
context, String iconName);
public abstract Object
getProperty(FacesContext context, String
propertyName);
/**
* Get the ResourceBundle for this skin in
the current locale.
*/
public abstract ResourceBundle
getResourceBundle(FacesContext context);
public abstract SkinNode
getSkinNode(FacesContext context, SkinSelector
selector);
public String getStyleClass(FacesContext
context, SkinSelector selector)
{
SkinNode node = getSkinNode(context,
selector);
return node == null ? null :
node.getStyleClass();
}
}
public class SkinException extends FacesException
{
// Provides all 4 common Exception
constructors
}
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;
}
}
public class SkinInitializationException
extends SkinException
{
// Provides all 4 common Exception
constructors
}
/**
* 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 parameters are not sufficient to
create the new <code>Skin</code>
* instance
*/
public Skin loadSkin(Skin parentSkin,
String resourceBundle, Map<String, String>
parameters);
}
public abstract class SkinNode
{
public abstract String getStyleClass();
public abstract Map<String, Object>
getProperties();
/**
* Gets a server side property of this
node. Server side properties are not visible
by the client.
*/
public Object getProperty(String propertyName)
{
return getProperties().get(propertyName);
}
/**
* Gets a read-only version of the
client-side properties of this node. In HTML,
those represent CSS properties.
*
* It's valid to place those properties in
a SoftRenderece for memory footprint safety.
However, this
* method must be able to reload them if
needed
* those properties should be placed in
memory-aware renference
*/
public abstract Map<String, Object>
getStyleProperties();
public Object getStyleProperty(String
propertyName)
{
return
getStyleProperties().get(propertyName);
}
}
public class SkinSelector implements
Comparable<SkinSelector>
{
/**
* Finds the SkinSelector with the
specified name or return <code>null</code> if
it doesn't exists. This
* method should be called by Renderers
during their initialization in order to cache
the SkinSelector instances
* they'll be using to increase overall
performance of the Skin module.
*/
public static SkinSelector
getInstance(String selectorName);
/**
* Finds the SkinSelector with the
specified name or create a new one if it
doesn't exists and <code>create</code>
* argument is <code>true</code>. This
method should be used mainly by SkinLoader in
order to register a new
* selector.
*/
public static SkinSelector
getInstance(String selectorName, boolean create);
private static int nextOrdinal = 0;
private int ordinal;
private String name;
private SkinSelector(String name)
{
assert name != null;
synchronized (SkinSelector.class)
{
ordinal = nextOrdinal++;
}
this.name <http://this.name> = name;
}
public boolean equals(Object obj)
{
// Sufficient because ordinal is unique
return o == this;
}
public int hashCode()
{
return ordinal();
}
public String name()
{
return name;
}
public int ordinal()
{
return ordinal;
}
}
*New config
*
<application>
<default-skin-family/>
</application>
<skin>
<skin-family/>
<render-kit-id/>
<description/>
<extends>
<skin-family/>
<render-kit-id/>
</extends>
<skin-loader-class/>
<resource-bundle/>
<skin-loader-parameters>
<map-entries/>
</skin-loader-parameters>
</skin>
*
*
PENDING: Should it instead be a
getTranslatedResource instead of
getResourceBundle within the Skin class?
PENDING: Should the skin family and render kit
id passed to the loader or is that irrelevant?
PENDING: With the proposed API, Trinidad
aliases wouldn't be able to be used across
skin additions as it would be aggregated in a
CompositeSkin instance and thus in multiple
different documents, anyone has a better idea
than CompositeSkin for this? Maybe stardadize
aliases somehow and have CompositeSkin
aggregate the content in a single document
instead of delegating to its fragments?
PENDING: Should Skin.getIcon uses an optimized
key as well? Doesn't seem required since there
are much less icons in a page than HTML
elements, thus the gain wouldn't worth the
trouble imho.
PENDING: The Resource API might have to be
revisited in order to recognize the Resource
returned by Skin.getContentResource so that
ResourceHandler can identify Resource requests
for the skin content and thus locate the given
skin, get its Resource insteance and return
the content with the standard algorithm. I
believe a reserved library name could be used
for that, the resource name is an harder issue
though since the skin identifier is a tuple
atm and defining a reserved separator
character would bring limitation to the render
kit ids and skin family values. Someone could
come up with a better suggestion for that maybe?
PENDING: Skin.getStyleProperties could maybe
offer the option to return a mutable Map as an
optional feature, but then would requires the
Skin to
regenerate the style document (CSS). However
this can get hellish with composite skin. The
node would need to have access to its
container Skin and there should be a
SkinModificationEvent/Listener capability
added to the Skin class.
Regards,
~ Simon
On Fri, Dec 5, 2008 at 1:21 PM, Simon Lessard
<[EMAIL PROTECTED]
<mailto:[EMAIL PROTECTED]>> wrote:
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.
--
http://www.irian.at
Your JSF powerhouse -
JSF Consulting, Development and
Courses in English and German
Professional Support for Apache MyFaces
--
http://www.irian.at
Your JSF powerhouse -
JSF Consulting, Development and
Courses in English and German
Professional Support for Apache MyFaces
--
http://www.irian.at
Your JSF powerhouse -
JSF Consulting, Development and
Courses in English and German
Professional Support for Apache MyFaces