/*
* JBoss, Home of Professional Open Source
* Copyright 2013, Red Hat, Inc. and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.richfaces.skin;
import java.util.Map;
import javax.el.ValueExpression;
import javax.faces.FacesException;
import javax.faces.context.FacesContext;
import org.ajax4jsf.Messages;
/**
* Singleton ( in respect as collection of different skins ) for produce instances properties for all used skins.
*
* @author shura (latest modification by $Author: alexsmirnov $)
* @version $Revision: 1.1.2.1 $ $Date: 2007/01/09 18:59:41 $
*/
final class SkinImpl extends AbstractSkin {
private static final Operation RESOLVE = new Operation() {
public Object executeLocal(FacesContext facesContext, SkinImpl skin, String name) {
Object resolvedParameter = skin.getLocalParameter(facesContext, name);
while (resolvedParameter instanceof String) {
String string = (String) resolvedParameter;
if ((string.length() > 0) && (string.charAt(0) == '&')) {
SkinFactory skinFactory = SkinFactory.getInstance(facesContext);
resolvedParameter = skinFactory.getSkin(facesContext).getParameter(facesContext, string.substring(1));
if (resolvedParameter == null) {
throw new FacesException(Messages.getMessage(Messages.SKIN_ILLEGAL_REFERENCE, name));
}
} else {
break;
}
}
return resolvedParameter;
}
@Override
public Object executeBase(FacesContext facesContext, Skin skin, String name) {
return skin.getParameter(facesContext, name);
}
};
private static final Operation CONTAINS = new Operation() {
public Object executeLocal(FacesContext facesContext, SkinImpl skin, String name) {
return skin.localContainsProperty(facesContext, name) ? Boolean.TRUE : Boolean.FALSE;
}
@Override
public Object executeBase(FacesContext facesContext, Skin skin, String name) {
return skin.containsProperty(name);
}
};
private final Map<Object, Object> skinParams;
private static class MutableInteger {
private int value;
public int getAndIncrement() {
return value++;
}
public int getAndDecrement() {
return value--;
}
}
private String name;
/**
* Skin can instantiate only by factory method.
*
* @param skinName
*/
SkinImpl(Map<Object, Object> properties, String name) {
this.skinParams = properties;
this.name = name;
}
private MutableInteger getCounter(FacesContext context) {
Map<Object, Object> attr = context.getAttributes();
MutableInteger counter = (MutableInteger) attr.get(MutableInteger.class);
if (counter == null) {
counter = new MutableInteger();
attr.put(MutableInteger.class, counter);
}
return counter;
}
private abstract static class Operation {
public abstract Object executeLocal(FacesContext facesContext, SkinImpl skin, String name);
public abstract Object executeBase(FacesContext facesContext, Skin skin, String name);
}
protected Map<Object, Object> getSkinParams() {
return skinParams;
}
public Object getParameter(FacesContext context, String name) {
return getValueReference(context, resolveSkinParameter(context, name));
}
public Object getParameter(FacesContext context, String name, Object defaultValue) {
Object value = getValueReference(context, resolveSkinParameter(context, name));
if (null == value) {
value = defaultValue;
}
return value;
}
protected Object getLocalParameter(FacesContext context, String name) {
return getValueReference(context, skinParams.get(name));
}
/**
* Calculate concrete value for property - if it stored as @see ValueBinding , return interpreted value.
*
* @param context
* @param property
* @return
*/
protected Object getValueReference(FacesContext context, Object property) {
if (property instanceof ValueExpression) {
ValueExpression value = (ValueExpression) property;
return value.getValue(context.getELContext());
}
return property;
}
public String toString() {
return this.getClass().getSimpleName() + ": " + skinParams.toString();
}
protected Skin getBaseSkin(FacesContext context) {
String baseSkinName = (String) getLocalParameter(context, Skin.BASE_SKIN);
if (baseSkinName != null) {
SkinFactory skinFactory = SkinFactory.getInstance(context);
return skinFactory.getSkin(context, baseSkinName);
}
return null;
}
protected Object localResolveSkinParameter(FacesContext context, String name) {
return getSkinParams().get(name);
}
protected boolean localContainsProperty(FacesContext context, String name) {
return getSkinParams().containsKey(name);
}
protected Object executeOperation(FacesContext context, Operation operation, String name) {
MutableInteger counter = getCounter(context);
try {
if (counter.getAndIncrement() > 100) {
throw new FacesException(Messages.getMessage(Messages.SKIN_CYCLIC_REFERENCE, name));
}
Object result = operation.executeLocal(context, this, name);
if (result != null) {
return result;
}
Skin baseSkin = getBaseSkin(context);
if (baseSkin != null) {
return operation.executeBase(context, baseSkin, name);
}
} finally {
counter.getAndDecrement();
}
return null;
}
protected boolean containsProperty(FacesContext context, String name) {
return Boolean.TRUE.equals(executeOperation(context, CONTAINS, name));
}
protected Object resolveSkinParameter(FacesContext context, String name) {
return executeOperation(context, RESOLVE, name);
}
public boolean containsProperty(String name) {
FacesContext facesContext = FacesContext.getCurrentInstance();
return containsProperty(facesContext, name);
}
public String getName() {
return name;
}
public int hashCode(FacesContext context) {
int hash = 0;
for (Map.Entry<Object, Object> entry : skinParams.entrySet()) {
String key = (String) entry.getKey();
Object value = entry.getValue();
Object parameter = getValueReference(context, value);
hash = 31 * hash + key.hashCode();
hash = 31 * hash + ((parameter != null) ? parameter.hashCode() : 0);
}
Skin baseSkin = getBaseSkin(context);
if (baseSkin != null) {
hash = 31 * hash + baseSkin.hashCode(context);
}
return hash;
}
}