/*
* 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 java.util.Map.Entry;
import java.util.Properties;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import javax.el.ELContext;
import javax.el.ExpressionFactory;
import javax.faces.application.Application;
import javax.faces.context.FacesContext;
import org.ajax4jsf.Messages;
import org.richfaces.el.util.ELUtils;
import org.richfaces.util.PropertiesUtil;
/**
* @author Nick Belaevski
*
*/
public abstract class AbstractSkinFactory extends SkinFactory {
private final class SkinBuilder implements Callable<Skin> {
private String skinName;
SkinBuilder(String skinName) {
super();
this.skinName = skinName;
}
public Skin call() throws Exception {
return buildSkin(FacesContext.getCurrentInstance(), skinName);
}
}
/**
* Resource Uri for properties file with default values of skin parameters.
*/
private static final String DEFAULT_SKIN_PATH = "META-INF/skins/%s.skin.properties";
// private static final String[] DEFAULT_SKIN_PATHS = { DEFAULT_SKIN_PATH };
private static final String USER_SKIN_PATH = "%s.skin.properties";
/**
* Path in jar to pre-defined vendor and custom user-defined skins definitions. in this realisation "META-INF/skins/" for
* vendor , "" - user-defined.
*/
private static final String[] SKINS_PATHS = { DEFAULT_SKIN_PATH, USER_SKIN_PATH };
private ConcurrentMap<String, FutureTask<Skin>> skins = new ConcurrentHashMap<String, FutureTask<Skin>>();
protected void processProperties(FacesContext context, Map<Object, Object> properties) {
ELContext elContext = context.getELContext();
// replace all EL-expressions by prepared ValueBinding ?
Application app = context.getApplication();
for (Entry<Object, Object> entry : properties.entrySet()) {
Object propertyObject = entry.getValue();
if (propertyObject instanceof String) {
String property = (String) propertyObject;
if (ELUtils.isValueReference(property)) {
ExpressionFactory expressionFactory = app.getExpressionFactory();
entry.setValue(expressionFactory.createValueExpression(elContext, property, Object.class));
} else {
entry.setValue(property);
}
}
}
}
/**
* Factory method for build skin from properties files. for given skin name, search in classpath all resources with name
* 'name'.skin.properties and append in content to default properties. First, get it from META-INF/skins/ , next - from root
* package. for any place search order determined by {@link java.lang.ClassLoader } realisation.
*
* @param name name for builded skin.
* @param context
* @return skin instance for current name
* @throws SkinNotFoundException - if no skin properies found for name.
*/
protected Skin buildSkin(FacesContext context, String name) throws SkinNotFoundException {
Properties skinParams = loadProperties(name, SKINS_PATHS);
processProperties(context, skinParams);
return new SkinImpl(skinParams, name);
}
/**
* @param name
* @param paths
* @return properties
* @throws SkinNotFoundException
*/
protected Properties loadProperties(String name, String[] paths) throws SkinNotFoundException {
// Get properties for concrete skin.
Properties skinProperties = new Properties();
int loadedPropertiesCount = 0;
for (int i = 0; i < paths.length; i++) {
String skinPropertiesLocation = paths[i].replaceAll("%s", name);
if (PropertiesUtil.loadProperties(skinProperties, skinPropertiesLocation)) {
loadedPropertiesCount++;
}
}
if (loadedPropertiesCount == 0) {
throw new SkinNotFoundException(Messages.getMessage(Messages.SKIN_NOT_FOUND_ERROR, name));
}
return skinProperties;
}
@Override
public Skin getSkin(FacesContext context, String name) {
if (null == name) {
throw new SkinNotFoundException(Messages.getMessage(Messages.NULL_SKIN_NAME_ERROR));
}
FutureTask<Skin> skinFuture = skins.get(name);
if (skinFuture == null) {
FutureTask<Skin> newSkinFuture = new FutureTask<Skin>(new SkinBuilder(name));
skinFuture = skins.putIfAbsent(name, newSkinFuture);
if (skinFuture == null) {
skinFuture = newSkinFuture;
}
}
try {
skinFuture.run();
return skinFuture.get();
} catch (InterruptedException e) {
throw new SkinNotFoundException(Messages.getMessage(Messages.SKIN_NOT_FOUND_ERROR, name), e);
} catch (ExecutionException e) {
throw new SkinNotFoundException(Messages.getMessage(Messages.SKIN_NOT_FOUND_ERROR, name), e);
}
}
}