/*
* Copyright (C) 2011 eXo Platform SAS.
*
* 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.gatein.portal.controller.resource.script;
import org.exoplatform.commons.utils.CompositeReader;
import org.exoplatform.commons.utils.PropertyResolverReader;
import org.exoplatform.web.WebAppController;
import org.exoplatform.web.controller.QualifiedName;
import org.gatein.common.logging.Logger;
import org.gatein.common.logging.LoggerFactory;
import org.gatein.portal.controller.resource.ResourceRequestHandler;
import javax.servlet.ServletContext;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
/**
* @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a>
*/
public abstract class Module
{
/** Our logger. */
private static final Logger log = LoggerFactory.getLogger(Module.class);
/** . */
public static final ResourceBundle.Control CONTROL = new ResourceBundle.Control()
{
@Override
public Locale getFallbackLocale(String baseName, Locale locale)
{
return locale.equals(Locale.ENGLISH) ? null : Locale.ENGLISH;
}
};
/** . */
protected ScriptResource resource;
/** . */
protected final String contextPath;
/** . */
protected int priority;
Module(ScriptResource resource, String contextPath, int priority)
{
this.resource = resource;
this.contextPath = contextPath;
this.priority = priority;
}
public static class Remote extends Module
{
/** . */
private final String uri;
Remote(ScriptResource resource, String contextPath, String uri, int priority)
{
super(resource, contextPath, priority);
//
this.uri = uri;
}
@Override
public boolean isRemote()
{
return true;
}
@Override
public String getURI()
{
return uri;
}
}
public static class Local extends Module
{
/** . */
private final Content[] contents;
/** . */
private final String resourceBundle;
/** . */
private final Map<QualifiedName, String> parameters;
Local(ScriptResource resource, String contextPath, String path, String resourceBundle, int priority)
{
this(resource, contextPath, new Content[] {new Content(path)}, resourceBundle, priority);
}
Local(ScriptResource resource, String contextPath, Content[] contents, String resourceBundle, int priority)
{
super(resource, contextPath, priority);
//
Map<QualifiedName, String> parameters = new HashMap<QualifiedName, String>();
parameters.put(WebAppController.HANDLER_PARAM, "script");
parameters.put(ResourceRequestHandler.RESOURCE_QN, resource.getId().getName());
parameters.put(ResourceRequestHandler.SCOPE_QN, resource.getId().getScope().name());
//
if (contents == null)
{
throw new IllegalArgumentException("contents must be not null");
}
this.contents = contents;
this.parameters = parameters;
this.resourceBundle = resourceBundle;
}
public String getPath()
{
for (Content ct : contents)
{
if (ct.isPath())
{
return ct.getSource();
}
}
return null;
}
public Content[] getContents()
{
return contents;
}
public String getResourceBundle()
{
return resourceBundle;
}
public Map<QualifiedName, String> getParameters()
{
return parameters;
}
@Override
public boolean isRemote()
{
return false;
}
@Override
public String getURI()
{
return contextPath + getPath();
}
/**
*
* @param locale the desired locale, if null, <code>Locale.ENGLISH</code> will be used
* @param scriptLoader the script loader
* @param bundleLoader the bundle loader
* @return a reader for the resource or null if the resource cannot be resolved
*/
public Reader read(Locale locale, ServletContext scriptLoader, ClassLoader bundleLoader)
{
List<Reader> readers = new LinkedList<Reader>();
for (Content content : contents)
{
if (content.isPath())
{
Reader script = getScript(content.getSource(), locale, scriptLoader, bundleLoader);
if (script != null)
{
readers.add(script);
}
else
{
log.warn("File not found: " + content.getSource());
}
}
else
{
readers.add(new StringReader("\n" + content.getSource() + "\n"));
}
}
if (readers.size() > 0)
{
return new CompositeReader(readers);
}
else
{
return null;
}
}
private Reader getScript(String pt, Locale locale, ServletContext scriptLoader, ClassLoader bundleLoader)
{
InputStream in = scriptLoader.getResourceAsStream(pt);
if (in != null)
{
Reader reader = new InputStreamReader(in);
if (resourceBundle != null)
{
if (locale == null)
{
locale = Locale.ENGLISH;
}
//
log.debug("About to load a bundle for locale " + locale + " and bundle " + resourceBundle);
final ResourceBundle bundle = ResourceBundle.getBundle(resourceBundle, locale, bundleLoader, CONTROL);
if (bundle != null)
{
log.debug("Found bundle " + bundle + " for locale " + locale + " and bundle " + resourceBundle);
reader = new PropertyResolverReader(reader)
{
@Override
protected String resolve(String name) throws IOException
{
try
{
String val = bundle.getString(name);
return escapeJavascriptStringLiteral(val);
}
catch (MissingResourceException e)
{
// Need to use logging
log.debug("Could not resolve property " + name + " when filtering JS");
return "";
}
}
};
}
}
return reader;
}
return null;
}
public static class Content
{
private String source;
private boolean isPath;
public Content(String source)
{
this(source, true);
}
public Content(String content, boolean isPath)
{
this.source = content;
this.isPath = isPath;
}
public String getSource()
{
return source;
}
public boolean isPath()
{
return isPath;
}
}
}
public ScriptResource getResource()
{
return resource;
}
public abstract boolean isRemote();
public abstract String getURI();
public String getContextPath()
{
return contextPath;
}
public int getPriority()
{
return priority;
}
/**
* Escape simple and double quotes chars.
*
* @param s the string to escape
* @return the escaped string
*/
private static String escapeJavascriptStringLiteral(String s)
{
StringBuilder sb = new StringBuilder(s.length());
for (int i = 0;i < s.length();i++)
{
char c = s.charAt(i);
if (c == '"')
{
sb.append("\\\"");
}
else if (c == '\'')
{
sb.append("\\\'");
}
else
{
sb.append(c);
}
}
return sb.toString();
}
}