[gatein-commits] gatein SVN: r451 - in portal/trunk/component/portal/src/main/java/org/exoplatform/portal: resource and 3 other directories.

do-not-reply at jboss.org do-not-reply at jboss.org
Fri Oct 30 00:29:26 EDT 2009


Author: hoang_to
Date: 2009-10-30 00:29:24 -0400 (Fri, 30 Oct 2009)
New Revision: 451

Added:
   portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/AbstractResourceHandler.java
   portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/Codec.java
   portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/CompositeResourceResolver.java
   portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/CompositeSkin.java
   portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/GateinSkinConfigDeployer.java
   portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/MainResourceResolver.java
   portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/RenderingException.java
   portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/Resource.java
   portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/ResourceRenderer.java
   portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/ResourceResolver.java
   portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/SimpleResourceContext.java
   portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/SimpleSkin.java
   portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/Skin.java
   portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/SkinConfig.java
   portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/SkinConfigDeployer.java
   portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/SkinKey.java
   portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/SkinService.java
   portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/SkinURL.java
   portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/xml/
   portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/xml/AbstractTaskXMLBinding.java
   portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/xml/GateinResource.java
   portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/xml/SkinConfigParser.java
Removed:
   portal/trunk/component/portal/src/main/java/org/exoplatform/portal/skin/
Modified:
   portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/tasks/AbstractSkinTask.java
   portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/tasks/I18nTask.java
   portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/tasks/PortalSkinTask.java
   portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/tasks/PortletSkinTask.java
   portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/tasks/ThemeTask.java
Log:
GTNPORTAL-131: Javascript deployment

Added: portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/AbstractResourceHandler.java
===================================================================
--- portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/AbstractResourceHandler.java	                        (rev 0)
+++ portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/AbstractResourceHandler.java	2009-10-30 04:29:24 UTC (rev 451)
@@ -0,0 +1,38 @@
+/**
+ * Copyright (C) 2009 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.exoplatform.portal.resource;
+
+import org.gatein.wci.WebAppEvent;
+import org.gatein.wci.WebAppListener;
+
+/**
+ * 
+ * Created by eXoPlatform SAS
+ *
+ * Author: Minh Hoang TO - hoang281283 at gmail.com
+ *
+ *      Sep 16, 2009
+ */
+public abstract class AbstractResourceHandler implements WebAppListener
+{
+
+   abstract public void onEvent(WebAppEvent event);
+
+}
\ No newline at end of file

Added: portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/Codec.java
===================================================================
--- portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/Codec.java	                        (rev 0)
+++ portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/Codec.java	2009-10-30 04:29:24 UTC (rev 451)
@@ -0,0 +1,111 @@
+/**
+ * Copyright (C) 2009 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.exoplatform.portal.resource;
+
+import org.exoplatform.commons.utils.CharEncoder;
+import org.exoplatform.commons.utils.CharsetCharEncoder;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+
+/**
+ * A codec for names. It is a modified version of the percent encoding algorithm that translates
+ * underscores to their percent counterpart and slash to underscores. Therefore slash chars are
+ * never seen as the %2F string as it can cause some issues on tomcat when it is used in an URI.
+ *
+ * @author <a href="mailto:julien.viet at exoplatform.com">Julien Viet</a>
+ * @version $Revision$
+ */
+class Codec
+{
+
+   private Codec()
+   {
+   }
+
+   private static final char[][] table = new char[256][];
+
+   static
+   {
+      char[] a = "0123456789ABCDEF".toCharArray();
+      for (int b = 0; b < 256; b++)
+      {
+         int b1 = (b & 0xF0) >> 4;
+         int b2 = b & 0x0F;
+         table[b] = new char[]{a[b1], a[b2]};
+      }
+   }
+
+   public static String decode(String s)
+   {
+      try
+      {
+         s = s.replace("_", "%2F");
+         return URLDecoder.decode(s, "UTF8");
+      }
+      catch (UnsupportedEncodingException e)
+      {
+         throw new Error(e);
+      }
+   }
+
+   public static void encode(Appendable appendable, String s) throws IOException
+   {
+      for (int i = 0; i < s.length(); i++)
+      {
+         char c = s.charAt(i);
+         if (Character.isLetter(c))
+         {
+            appendable.append(c);
+         }
+         else
+         {
+            switch (c)
+            {
+               case 'A' :
+               case '.' :
+               case '-' :
+               case '*' :
+                  appendable.append(c);
+                  break;
+               case ' ' :
+                  appendable.append('+');
+                  break;
+               case '/' :
+                  appendable.append('_');
+                  break;
+               default :
+                  CharEncoder encoder = CharsetCharEncoder.getUTF8();
+                  byte[] bytes = encoder.encode(c);
+                  appendable.append('%');
+                  for (byte b : bytes)
+                  {
+                     for (char cc : table[b])
+                     {
+                        appendable.append(cc);
+                     }
+                  }
+            }
+         }
+      }
+   }
+
+}

Added: portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/CompositeResourceResolver.java
===================================================================
--- portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/CompositeResourceResolver.java	                        (rev 0)
+++ portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/CompositeResourceResolver.java	2009-10-30 04:29:24 UTC (rev 451)
@@ -0,0 +1,80 @@
+/**
+ * Copyright (C) 2009 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.exoplatform.portal.resource;
+
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:julien.viet at exoplatform.com">Julien Viet</a>
+ * @version $Revision$
+ */
+class CompositeResourceResolver implements ResourceResolver
+{
+
+   /** . */
+   private final Map<SkinKey, SkinConfig> skins;
+
+   /** 
+    * The name of the portal container 
+    */
+   private final String portalContainerName;
+
+   public CompositeResourceResolver(String portalContainerName, Map<SkinKey, SkinConfig> skins)
+   {
+      this.portalContainerName = portalContainerName;
+      this.skins = skins;
+   }
+
+   public Resource resolve(String path)
+   {
+      if (path.startsWith("/" + portalContainerName + "/resource/") && path.endsWith(".css"))
+      {
+         final StringBuffer sb = new StringBuffer();
+         String encoded = path.substring(("/" + portalContainerName + "/resource/").length());
+         String blah[] = encoded.split("/");
+         int len = (blah.length >> 1) << 1;
+         for (int i = 0; i < len; i += 2)
+         {
+            String name = Codec.decode(blah[i]);
+            String module = Codec.decode(blah[i + 1]);
+            SkinKey key = new SkinKey(module, name);
+            SkinConfig skin = skins.get(key);
+            if (skin != null)
+            {
+               sb.append("@import url(").append(skin.getCSSPath()).append(");").append("\n");
+            }
+         }
+         return new Resource(path)
+         {
+            @Override
+            public Reader read()
+            {
+               return new StringReader(sb.toString());
+            }
+         };
+      }
+      else
+      {
+         return null;
+      }
+   }
+}

Added: portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/CompositeSkin.java
===================================================================
--- portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/CompositeSkin.java	                        (rev 0)
+++ portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/CompositeSkin.java	2009-10-30 04:29:24 UTC (rev 451)
@@ -0,0 +1,113 @@
+/**
+ * Copyright (C) 2009 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.exoplatform.portal.resource;
+
+import org.exoplatform.commons.utils.PropertyManager;
+import org.exoplatform.services.resources.Orientation;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.TreeMap;
+
+/**
+ * A composite skin.
+ *
+ * @author <a href="mailto:julien.viet at exoplatform.com">Julien Viet</a>
+ * @version $Revision$
+ */
+class CompositeSkin implements Skin
+{
+
+   /** . */
+   private final SkinService service;
+
+   /** . */
+   private final String id;
+
+   /** . */
+   private final String urlPrefix;
+
+   CompositeSkin(SkinService service, Collection<SkinConfig> skins)
+   {
+      TreeMap<String, SkinConfig> urlSkins = new TreeMap<String, SkinConfig>();
+      for (SkinConfig skin : skins)
+      {
+         urlSkins.put(skin.getCSSPath(), skin);
+      }
+
+      //
+      final StringBuilder builder = new StringBuilder();
+      builder.append("/").append(service.portalContainerName).append("/resource");
+
+      //
+      final StringBuilder id = new StringBuilder();
+
+      //
+      try
+      {
+         for (SkinConfig cfg : urlSkins.values())
+         {
+            StringBuilder encodedName = new StringBuilder();
+            Codec.encode(encodedName, cfg.getName());
+            StringBuilder encodedModule = new StringBuilder();
+            Codec.encode(encodedModule, cfg.getModule());
+
+            //
+            id.append(encodedName).append(encodedModule);
+            builder.append("/").append(encodedName).append("/").append(encodedModule);
+         }
+      }
+      catch (IOException e)
+      {
+         throw new Error(e);
+      }
+
+      //
+      this.service = service;
+      this.id = id.toString();
+      this.urlPrefix = builder.toString();
+   }
+
+   public String getId()
+   {
+      return id;
+   }
+
+   public SkinURL createURL()
+   {
+      return new SkinURL()
+      {
+
+         Orientation orientation;
+
+         public void setOrientation(Orientation orientation)
+         {
+            this.orientation = orientation;
+         }
+
+         @Override
+         public String toString()
+         {
+            return urlPrefix + "/" + (PropertyManager.isDevelopping() ? "style" : service.id)
+               + service.getSuffix(orientation);
+         }
+      };
+   }
+}

Added: portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/GateinSkinConfigDeployer.java
===================================================================
--- portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/GateinSkinConfigDeployer.java	                        (rev 0)
+++ portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/GateinSkinConfigDeployer.java	2009-10-30 04:29:24 UTC (rev 451)
@@ -0,0 +1,133 @@
+/**
+ * Copyright (C) 2009 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.exoplatform.portal.resource;
+
+import org.exoplatform.container.PortalContainer;
+import org.exoplatform.container.RootContainer.PortalContainerPostInitTask;
+import org.exoplatform.portal.resource.config.xml.SkinConfigParser;
+import org.exoplatform.services.log.ExoLogger;
+import org.exoplatform.services.log.Log;
+import org.gatein.wci.WebAppEvent;
+import org.gatein.wci.WebAppLifeCycleEvent;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.servlet.ServletContext;
+
+/**
+ * 
+ * Created by eXoPlatform SAS
+ *
+ * Author: Minh Hoang TO - hoang281283 at gmail.com
+ *
+ *      Sep 16, 2009
+ */
+public class GateinSkinConfigDeployer extends AbstractResourceHandler
+{
+
+   private final SkinService skinService;
+
+   private static final String GATEIN_CONFIG_RESOURCE = "/WEB-INF/gatein-resources.xml";
+
+   private static Log LOG = ExoLogger.getExoLogger(GateinSkinConfigDeployer.class);
+
+   /**
+    * The name of the portal container
+    */
+   private final String portalContainerName;
+
+   public GateinSkinConfigDeployer(String portalContainerName, SkinService _skinService)
+   {
+      this.skinService = _skinService;
+      this.portalContainerName = portalContainerName;
+   }
+
+   @Override
+   public void onEvent(WebAppEvent event)
+   {
+      if (event instanceof WebAppLifeCycleEvent)
+      {
+         WebAppLifeCycleEvent waEvent = (WebAppLifeCycleEvent)event;
+         if (waEvent.getType() == WebAppLifeCycleEvent.ADDED)
+         {
+            ServletContext scontext = null;
+            try
+            {
+               scontext = event.getWebApp().getServletContext();
+               InputStream is = scontext.getResourceAsStream(GATEIN_CONFIG_RESOURCE);
+               if (is == null)
+                  return;
+               try
+               {
+                  is.close();
+               }
+               catch (Exception ex)
+               {
+                  // ignore me
+               }
+               final PortalContainerPostInitTask task = new PortalContainerPostInitTask()
+               {
+
+                  public void execute(ServletContext scontext, PortalContainer portalContainer)
+                  {
+                     register(scontext, portalContainer);
+                  }
+               };
+               PortalContainer.addInitTask(scontext, task, portalContainerName);
+            }
+            catch (Exception ex)
+            {
+               LOG.error("An error occurs while registering '" + GATEIN_CONFIG_RESOURCE + "' from the context '"
+                  + (scontext == null ? "unknown" : scontext.getServletContextName()) + "'", ex);
+            }
+         }
+      }
+   }
+
+   private void register(ServletContext scontext, PortalContainer container)
+   {
+      InputStream is = null;
+      try
+      {
+         is = scontext.getResourceAsStream(GATEIN_CONFIG_RESOURCE);
+         SkinConfigParser.processConfigResource(is, skinService, scontext);
+      }
+      catch (Exception ex)
+      {
+         LOG.error("An error occurs while registering '" + GATEIN_CONFIG_RESOURCE + "' from the context '"
+            + (scontext == null ? "unknown" : scontext.getServletContextName()) + "'", ex);
+      }
+      finally
+      {
+         if (is != null)
+         {
+            try
+            {
+               is.close();
+            }
+            catch (IOException e)
+            {
+               // ignore me
+            }
+         }
+      }
+   }
+}

Added: portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/MainResourceResolver.java
===================================================================
--- portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/MainResourceResolver.java	                        (rev 0)
+++ portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/MainResourceResolver.java	2009-10-30 04:29:24 UTC (rev 451)
@@ -0,0 +1,80 @@
+/**
+ * Copyright (C) 2009 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.exoplatform.portal.resource;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.servlet.ServletContext;
+
+/**
+ * @author <a href="mailto:julien.viet at exoplatform.com">Julien Viet</a>
+ * @version $Revision$
+ */
+class MainResourceResolver implements ResourceResolver
+{
+
+   final Map<String, SimpleResourceContext> contexts;
+
+   final CopyOnWriteArrayList<ResourceResolver> resolvers;
+
+   final Map<SkinKey, SkinConfig> skins;
+
+   public MainResourceResolver(String portalContainerName, Map<SkinKey, SkinConfig> skins)
+   {
+      this.skins = skins;
+      this.contexts = new HashMap<String, SimpleResourceContext>();
+      this.resolvers = new CopyOnWriteArrayList<ResourceResolver>();
+
+      //
+      resolvers.add(new CompositeResourceResolver(portalContainerName, skins));
+   }
+
+   SimpleResourceContext registerContext(ServletContext servletContext)
+   {
+      String key = "/" + servletContext.getServletContextName();
+      SimpleResourceContext ctx = contexts.get(key);
+      if (ctx == null)
+      {
+         ctx = new SimpleResourceContext(key, servletContext);
+         contexts.put(ctx.getContextPath(), ctx);
+      }
+      return ctx;
+   }
+
+   public Resource resolve(String path)
+   {
+      for (ResourceResolver resolver : resolvers)
+      {
+         Resource res = resolver.resolve(path);
+         if (res != null)
+         {
+            return res;
+         }
+      }
+
+      //
+      int i1 = path.indexOf("/", 2);
+      String targetedContextPath = path.substring(0, i1);
+      SimpleResourceContext context = contexts.get(targetedContextPath);
+      return context.getResource(path.substring(i1));
+   }
+}

Added: portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/RenderingException.java
===================================================================
--- portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/RenderingException.java	                        (rev 0)
+++ portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/RenderingException.java	2009-10-30 04:29:24 UTC (rev 451)
@@ -0,0 +1,49 @@
+/**
+ * Copyright (C) 2009 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.exoplatform.portal.resource;
+
+/**
+ * Signal an exception during rendering.
+ *
+ * @author <a href="mailto:julien.viet at exoplatform.com">Julien Viet</a>
+ * @version $Revision$
+ */
+public class RenderingException extends Exception
+{
+
+   public RenderingException()
+   {
+   }
+
+   public RenderingException(String message)
+   {
+      super(message);
+   }
+
+   public RenderingException(String message, Throwable cause)
+   {
+      super(message, cause);
+   }
+
+   public RenderingException(Throwable cause)
+   {
+      super(cause);
+   }
+}

Added: portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/Resource.java
===================================================================
--- portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/Resource.java	                        (rev 0)
+++ portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/Resource.java	2009-10-30 04:29:24 UTC (rev 451)
@@ -0,0 +1,85 @@
+/**
+ * Copyright (C) 2009 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.exoplatform.portal.resource;
+
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * Represents a resource.
+ *
+ * @author <a href="mailto:julien.viet at exoplatform.com">Julien Viet</a>
+ * @version $Revision$
+ */
+public abstract class Resource
+{
+
+   private final String contextPath;
+
+   private final String parentPath;
+
+   private final String fileName;
+
+   public Resource(String path)
+   {
+      int index = path.indexOf("/", 2);
+      String relativeCSSPath = path.substring(index);
+      int index2 = relativeCSSPath.lastIndexOf("/") + 1;
+
+      //
+      this.contextPath = path.substring(0, index);
+      this.parentPath = relativeCSSPath.substring(0, index2);
+      this.fileName = relativeCSSPath.substring(index2);
+   }
+
+   public Resource(String contextPath, String parentPath, String fileName)
+   {
+      this.contextPath = contextPath;
+      this.parentPath = parentPath;
+      this.fileName = fileName;
+   }
+
+   public final String getPath()
+   {
+      return getContextPath() + getParentPath() + getFileName();
+   }
+
+   public final String getContextPath()
+   {
+      return contextPath;
+   }
+
+   public final String getParentPath()
+   {
+      return parentPath;
+   }
+
+   public final String getFileName()
+   {
+      return fileName;
+   }
+
+   public final String getResourcePath()
+   {
+      return getParentPath() + getFileName();
+   }
+
+   public abstract Reader read() throws IOException;
+}

Added: portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/ResourceRenderer.java
===================================================================
--- portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/ResourceRenderer.java	                        (rev 0)
+++ portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/ResourceRenderer.java	2009-10-30 04:29:24 UTC (rev 451)
@@ -0,0 +1,47 @@
+/**
+ * Copyright (C) 2009 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.exoplatform.portal.resource;
+
+/**
+ * An interface defining the renderer contract for a resource.
+ *
+ * @author <a href="mailto:julien.viet at exoplatform.com">Julien Viet</a>
+ * @version $Revision$
+ */
+public interface ResourceRenderer
+{
+
+   /**
+    * Returns an appendable for the performing the rendering of the resource.
+    *
+    * @return the appendable
+    */
+   Appendable getAppendable();
+
+   /**
+    * Instruct the renderer about the expiration time in seconds. A non positive value
+    * means that no caching should be performed. The expiration value is relative to the
+    * date of the request.
+    *
+    * @param seconds the value in seconds
+    */
+   void setExpiration(long seconds);
+
+}

Added: portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/ResourceResolver.java
===================================================================
--- portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/ResourceResolver.java	                        (rev 0)
+++ portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/ResourceResolver.java	2009-10-30 04:29:24 UTC (rev 451)
@@ -0,0 +1,39 @@
+/**
+ * Copyright (C) 2009 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.exoplatform.portal.resource;
+
+/**
+ * A resource resolver for char based resources.
+ *
+ * @author <a href="mailto:julien.viet at exoplatform.com">Julien Viet</a>
+ * @version $Revision$
+ */
+public interface ResourceResolver
+{
+
+   /**
+    * Returns a reader for the provided path or null if the resource cannot be resolved.
+    *
+    * @param path the path
+    * @return a reader 
+    */
+   Resource resolve(String path);
+
+}

Added: portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/SimpleResourceContext.java
===================================================================
--- portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/SimpleResourceContext.java	                        (rev 0)
+++ portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/SimpleResourceContext.java	2009-10-30 04:29:24 UTC (rev 451)
@@ -0,0 +1,78 @@
+/**
+ * Copyright (C) 2009 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.exoplatform.portal.resource;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.servlet.ServletContext;
+
+/**
+ * @author <a href="mailto:julien.viet at exoplatform.com">Julien Viet</a>
+ * @version $Revision$
+ */
+class SimpleResourceContext
+{
+
+   private final String contextPath;
+
+   private final ServletContext context;
+
+   public SimpleResourceContext(String contextPath, ServletContext context)
+   {
+      this.contextPath = contextPath;
+      this.context = context;
+   }
+
+   public Resource getResource(String path)
+   {
+      int i2 = path.lastIndexOf("/") + 1;
+      String targetedParentPath = path.substring(0, i2);
+      String targetedFileName = path.substring(i2);
+      try
+      {
+         final URL url = context.getResource(path);
+         if (url != null)
+         {
+            return new Resource(contextPath, targetedParentPath, targetedFileName)
+            {
+               @Override
+               public Reader read() throws IOException
+               {
+                  return new InputStreamReader(url.openStream());
+               }
+            };
+         }
+      }
+      catch (MalformedURLException e)
+      {
+         e.printStackTrace();
+      }
+      return null;
+   }
+
+   public String getContextPath()
+   {
+      return contextPath;
+   }
+}

Added: portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/SimpleSkin.java
===================================================================
--- portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/SimpleSkin.java	                        (rev 0)
+++ portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/SimpleSkin.java	2009-10-30 04:29:24 UTC (rev 451)
@@ -0,0 +1,91 @@
+/**
+ * Copyright (C) 2009 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.exoplatform.portal.resource;
+
+import org.exoplatform.services.resources.Orientation;
+
+/**
+ * An implementation of the skin config.
+ *
+ * Created by The eXo Platform SAS
+ * Jan 19, 2007
+ */
+class SimpleSkin implements SkinConfig
+{
+
+   private final SkinService service_;
+
+   private final String module_;
+
+   private final String name_;
+
+   private final String cssPath_;
+
+   private final String id_;
+
+   public SimpleSkin(SkinService service, String module, String name, String cssPath)
+   {
+      service_ = service;
+      module_ = module;
+      name_ = name;
+      cssPath_ = cssPath;
+      id_ = module.replace('/', '_');
+   }
+
+   public String getId()
+   {
+      return id_;
+   }
+
+   public String getModule()
+   {
+      return module_;
+   }
+
+   public String getCSSPath()
+   {
+      return cssPath_;
+   }
+
+   public String getName()
+   {
+      return name_;
+   }
+
+   public SkinURL createURL()
+   {
+      return new SkinURL()
+      {
+
+         Orientation orientation = null;
+
+         public void setOrientation(Orientation orientation)
+         {
+            this.orientation = orientation;
+         }
+
+         @Override
+         public String toString()
+         {
+            return cssPath_.replaceAll("\\.css$", service_.getSuffix(orientation));
+         }
+      };
+   }
+}
\ No newline at end of file

Added: portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/Skin.java
===================================================================
--- portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/Skin.java	                        (rev 0)
+++ portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/Skin.java	2009-10-30 04:29:24 UTC (rev 451)
@@ -0,0 +1,45 @@
+/**
+ * Copyright (C) 2009 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.exoplatform.portal.resource;
+
+/**
+ * A skin.
+ *
+ * @author <a href="mailto:julien.viet at exoplatform.com">Julien Viet</a>
+ * @version $Revision$
+ */
+public interface Skin
+{
+
+   /**
+    * Returns the skin id.
+    *
+    * @return the skin id
+    */
+   String getId();
+
+   /**
+    * Creates and return a skin URL.
+    *
+    * @return the skin URL
+    */
+   SkinURL createURL();
+
+}

Added: portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/SkinConfig.java
===================================================================
--- portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/SkinConfig.java	                        (rev 0)
+++ portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/SkinConfig.java	2009-10-30 04:29:24 UTC (rev 451)
@@ -0,0 +1,52 @@
+/**
+ * Copyright (C) 2009 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.exoplatform.portal.resource;
+
+/**
+ * Extends a skin with additional information.
+ *
+ * Created by The eXo Platform SAS
+ * Jan 19, 2007  
+ */
+public interface SkinConfig extends Skin
+{
+
+   /**
+    * Returns the skin name
+    *
+    * @return the skin name
+    */
+   String getName();
+
+   /**
+    * Returns the skin module.
+    *
+    * @return the module
+    */
+   String getModule();
+
+   /**
+    * Returns the css path.
+    *
+    * @return the css path
+    */
+   String getCSSPath();
+
+}
\ No newline at end of file

Added: portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/SkinConfigDeployer.java
===================================================================
--- portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/SkinConfigDeployer.java	                        (rev 0)
+++ portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/SkinConfigDeployer.java	2009-10-30 04:29:24 UTC (rev 451)
@@ -0,0 +1,139 @@
+/**
+ * Copyright (C) 2009 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.exoplatform.portal.resource;
+
+import groovy.lang.Binding;
+import groovy.lang.GroovyShell;
+
+import org.exoplatform.container.PortalContainer;
+import org.exoplatform.container.RootContainer.PortalContainerPostInitTask;
+import org.exoplatform.services.log.ExoLogger;
+import org.exoplatform.services.log.Log;
+import org.gatein.wci.WebAppEvent;
+import org.gatein.wci.WebAppLifeCycleEvent;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.servlet.ServletContext;
+
+/**
+ * Created by The eXo Platform SAS
+ * Jan 19, 2007
+ */
+
+public class SkinConfigDeployer extends AbstractResourceHandler
+{
+
+   /**
+    * Logger
+    */
+   private static final Log LOG = ExoLogger.getLogger(SkinConfigDeployer.class);
+
+   /** . */
+   private final SkinService skinService;
+
+   /**
+    * The name of the portal container
+    */
+   private final String portalContainerName;
+
+   public SkinConfigDeployer(String portalContainerName, SkinService skinService)
+   {
+      this.skinService = skinService;
+      this.portalContainerName = portalContainerName;
+   }
+
+   public void onEvent(WebAppEvent event)
+   {
+      if (event instanceof WebAppLifeCycleEvent)
+      {
+         WebAppLifeCycleEvent waEvent = (WebAppLifeCycleEvent)event;
+         if (waEvent.getType() == WebAppLifeCycleEvent.ADDED)
+         {
+            ServletContext scontext = null;
+            try
+            {
+               scontext = event.getWebApp().getServletContext();
+               InputStream is = scontext.getResourceAsStream("/WEB-INF/conf/script/groovy/SkinConfigScript.groovy");
+               if (is == null)
+                  return;
+               try
+               {
+                  is.close();
+               }
+               catch (Exception ex)
+               {
+                  // ignore me
+               }
+               final PortalContainerPostInitTask task = new PortalContainerPostInitTask()
+               {
+
+                  public void execute(ServletContext scontext, PortalContainer portalContainer)
+                  {
+                     register(scontext, portalContainer);
+                  }
+               };
+               PortalContainer.addInitTask(scontext, task, portalContainerName);
+            }
+            catch (Exception ex)
+            {
+               LOG.error("An error occurs while registering 'SkinConfigScript.groovy' from the context '"
+                  + (scontext == null ? "unknown" : scontext.getServletContextName()) + "'", ex);
+            }
+         }
+      }
+   }
+
+   private void register(ServletContext scontext, PortalContainer container)
+   {
+      InputStream is = null;
+      try
+      {
+         is = scontext.getResourceAsStream("/WEB-INF/conf/script/groovy/SkinConfigScript.groovy");
+         Binding binding = new Binding();
+         binding.setVariable("SkinService", skinService);
+         binding.setVariable("ServletContext", scontext);
+         binding.setVariable("ServletContextName", scontext.getServletContextName());
+         binding.setVariable("PortalContainerName", container.getName());
+         GroovyShell shell = new GroovyShell(binding);
+         shell.evaluate(is);
+      }
+      catch (Exception ex)
+      {
+         LOG.error("An error occurs while processing 'SkinConfigScript.groovy' from the context '"
+            + scontext.getServletContextName() + "'", ex);
+      }
+      finally
+      {
+         if (is != null)
+         {
+            try
+            {
+               is.close();
+            }
+            catch (IOException e)
+            {
+               // ignore me
+            }
+         }
+      }
+   }
+}
\ No newline at end of file

Added: portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/SkinKey.java
===================================================================
--- portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/SkinKey.java	                        (rev 0)
+++ portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/SkinKey.java	2009-10-30 04:29:24 UTC (rev 451)
@@ -0,0 +1,98 @@
+/**
+ * Copyright (C) 2009 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.exoplatform.portal.resource;
+
+/**
+ * A key for skin config lookup.
+ *
+ * @author <a href="mailto:julien.viet at exoplatform.com">Julien Viet</a>
+ * @version $Revision$
+ */
+class SkinKey
+{
+
+   private final String module;
+
+   private final String name;
+
+   private final int hashCode;
+
+   /**
+    * Creates a new skin key.
+    *
+    * @param module the skin base
+    * @param name the skin name
+    * @throws IllegalArgumentException if any argument is null
+    */
+   public SkinKey(String module, String name) throws IllegalArgumentException
+   {
+      if (module == null)
+      {
+         throw new IllegalArgumentException("No null base accepted");
+      }
+      if (name == null)
+      {
+         throw new IllegalArgumentException("No null skin name accepted");
+      }
+
+      //
+      this.module = module;
+      this.name = name;
+      this.hashCode = module.hashCode() * 41 + name.hashCode();
+   }
+
+   public String getModule()
+   {
+      return module;
+   }
+
+   public String getName()
+   {
+      return name;
+   }
+
+   public int hashCode()
+   {
+      return hashCode;
+   }
+
+   public boolean equals(Object obj)
+   {
+      if (obj == null)
+      {
+         return false;
+      }
+      if (obj == this)
+      {
+         return true;
+      }
+      if (obj instanceof SkinKey)
+      {
+         SkinKey that = (SkinKey)obj;
+         return that.module.equals(module) && that.name.equals(name);
+      }
+      return false;
+   }
+
+   public String toString()
+   {
+      return "SkinKey[base=" + module + ",name=" + name + "]";
+   }
+}

Added: portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/SkinService.java
===================================================================
--- portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/SkinService.java	                        (rev 0)
+++ portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/SkinService.java	2009-10-30 04:29:24 UTC (rev 451)
@@ -0,0 +1,557 @@
+/**
+ * Copyright (C) 2009 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.exoplatform.portal.resource;
+
+import org.exoplatform.commons.utils.PropertyManager;
+import org.exoplatform.commons.utils.Safe;
+import org.exoplatform.container.ExoContainerContext;
+import org.exoplatform.management.annotations.Managed;
+import org.exoplatform.management.annotations.ManagedDescription;
+import org.exoplatform.management.annotations.ManagedName;
+import org.exoplatform.management.jmx.annotations.NameTemplate;
+import org.exoplatform.management.jmx.annotations.Property;
+import org.exoplatform.services.log.ExoLogger;
+import org.exoplatform.services.log.Log;
+import org.exoplatform.services.resources.Orientation;
+import org.gatein.wci.impl.DefaultServletContainerFactory;
+import org.picocontainer.Startable;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.servlet.ServletContext;
+
+ at Managed
+ at NameTemplate({@Property(key = "view", value = "portal"), @Property(key = "service", value = "management"),
+   @Property(key = "type", value = "skin")})
+ at ManagedDescription("Skin service")
+public class SkinService implements Startable
+{
+
+   protected static Log log = ExoLogger.getLogger("portal.SkinService");
+
+   private static final Map<Orientation, String> suffixMap = new EnumMap<Orientation, String>(Orientation.class);
+
+   static
+   {
+      suffixMap.put(Orientation.LT, "-lt.css");
+      suffixMap.put(Orientation.RT, "-rt.css");
+      suffixMap.put(Orientation.TL, "-lt.css");
+      suffixMap.put(Orientation.TR, "-lt.css");
+   }
+
+   private static final String LEFT_P = "\\(";
+
+   private static final String RIGHT_P = "\\)";
+
+   /** Immutable and therefore thread safe. */
+   private static final Pattern IMPORT_PATTERN =
+      Pattern.compile("(@import\\s+" + "url" + LEFT_P + "['\"]?" + ")([^'\"]+.css)(" + "['\"]?" + RIGHT_P + "\\s*;)");
+
+   /** Immutable and therefore thread safe. */
+   private static final Pattern BACKGROUND_PATTERN =
+      Pattern.compile("(background.*:.*url" + LEFT_P + "['\"]?" + ")([^'\"]+)(" + "['\"]?" + RIGHT_P + ".*;)");
+
+   /** Immutable and therefore thread safe. */
+   private static final Pattern LT = Pattern.compile("/\\*\\s*orientation=lt\\s*\\*/");
+
+   /** Immutable and therefore thread safe. */
+   private static final Pattern RT = Pattern.compile("/\\*\\s*orientation=rt\\s*\\*/");
+
+   /** One month caching. */
+   private static final int ONE_MONTH = 2592000;
+
+   /** One hour caching. */
+   private static final int ONE_HOUR = 3600;
+
+   /** The deployer. */
+   private final AbstractResourceHandler deployer;
+
+   private final Map<SkinKey, SkinConfig> portalSkins_;
+
+   private final Map<SkinKey, SkinConfig> skinConfigs_;
+
+   private final HashSet<String> availableSkins_;
+
+   private final Map<String, String> ltCache;
+
+   private final Map<String, String> rtCache;
+
+   private final Map<String, Set<String>> portletThemes_;
+
+   private final MainResourceResolver mainResolver;
+
+   /**
+     * The name of the portal container 
+     */
+   final String portalContainerName;
+
+   /**
+   * An id used for caching request. The id life cycle is the same than the class instance because
+   * we consider css will change until server is restarted. Of course this only applies for the
+   * developing mode set to false.
+   */
+   final String id = Long.toString(System.currentTimeMillis());
+
+   /** Temporary hack. */
+   //private static final List<String> skinBlackList = Arrays.asList("Vista", "Mac");
+   private static final List<String> skinBlackList = new ArrayList<String>();
+
+   public SkinService(ExoContainerContext context)
+   {
+      portalSkins_ = new LinkedHashMap<SkinKey, SkinConfig>();
+      skinConfigs_ = new LinkedHashMap<SkinKey, SkinConfig>(20);
+      availableSkins_ = new HashSet<String>(5);
+      ltCache = new ConcurrentHashMap<String, String>();
+      rtCache = new ConcurrentHashMap<String, String>();
+      portletThemes_ = new HashMap<String, Set<String>>();
+      portalContainerName = context.getPortalContainerName();
+      mainResolver = new MainResourceResolver(portalContainerName, skinConfigs_);
+      //deployer = new SkinConfigDeployer(portalContainerName, this);
+      deployer = new GateinSkinConfigDeployer(portalContainerName, this);
+   }
+
+   public void addCategoryTheme(String categoryName)
+   {
+      if (!portletThemes_.containsKey(categoryName))
+         portletThemes_.put(categoryName, new HashSet<String>());
+   }
+
+   public void addPortalSkin(String module, String skinName, String cssPath, ServletContext scontext)
+   {
+      addPortalSkin(module, skinName, cssPath, scontext, false);
+   }
+
+   /**
+    * Register the stylesheet for a portal Skin.
+    *
+    * @param module skin module identifier
+    * @param skinName skin name
+    * @param cssPath path uri to the css file. This is relative to the root context, use leading '/'
+    * @param scontext the webapp's {@link javax.servlet.ServletContext}
+    * @param overwrite if any previous skin should be replaced by that one
+    */
+   public void addPortalSkin(String module, String skinName, String cssPath, ServletContext scontext, boolean overwrite)
+   {
+
+      // Triggers a put if absent
+      mainResolver.registerContext(scontext);
+
+      availableSkins_.add(skinName);
+      SkinKey key = new SkinKey(module, skinName);
+      SkinConfig skinConfig = portalSkins_.get(key);
+      if (skinConfig == null || overwrite)
+      {
+         skinConfig = new SimpleSkin(this, module, skinName, cssPath);
+         portalSkins_.put(key, skinConfig);
+      }
+   }
+
+   public void addPortalSkin(String module, String skinName, String cssPath, String cssData)
+   {
+      SkinKey key = new SkinKey(module, skinName);
+      SkinConfig skinConfig = portalSkins_.get(key);
+      if (skinConfig == null)
+      {
+         portalSkins_.put(key, new SimpleSkin(this, module, skinName, cssPath));
+      }
+      ltCache.put(cssPath, cssData);
+      rtCache.put(cssPath, cssData);
+   }
+
+   public void addSkin(String module, String skinName, String cssPath, ServletContext scontext)
+   {
+      addSkin(module, skinName, cssPath, scontext, false);
+   }
+
+   /**
+    * Merge several skins into one single skin.
+    *
+    * @param skins the skins to merge
+    * @return the merged skin
+    */
+   public Skin merge(Collection<SkinConfig> skins)
+   {
+      return new CompositeSkin(this, skins);
+   }
+
+   /**
+    * Add a resource resolver to plug external resolvers.
+    *
+    * @param resolver a resolver to add
+    */
+   public void addResourceResolver(ResourceResolver resolver)
+   {
+      mainResolver.resolvers.addIfAbsent(resolver);
+   }
+
+   public void addSkin(String module, String skinName, String cssPath, ServletContext scontext, boolean overwrite)
+   {
+      // Triggers a put if absent
+      mainResolver.registerContext(scontext);
+
+      availableSkins_.add(skinName);
+      SkinKey key = new SkinKey(module, skinName);
+      SkinConfig skinConfig = skinConfigs_.get(key);
+      if (skinConfig == null || overwrite)
+      {
+         skinConfig = new SimpleSkin(this, module, skinName, cssPath);
+         skinConfigs_.put(key, skinConfig);
+      }
+   }
+
+   public void addSkin(String module, String skinName, String cssPath, String cssData)
+   {
+      //
+      availableSkins_.add(skinName);
+      SkinKey key = new SkinKey(module, skinName);
+      SkinConfig skinConfig = skinConfigs_.get(key);
+      if (skinConfig == null)
+      {
+         skinConfigs_.put(key, new SimpleSkin(this, module, skinName, cssPath));
+      }
+      ltCache.put(cssPath, cssData);
+      rtCache.put(cssPath, cssData);
+   }
+
+   public void addTheme(String categoryName, List<String> themesName)
+   {
+      if (!portletThemes_.containsKey(categoryName))
+         portletThemes_.put(categoryName, new HashSet<String>());
+      Set<String> catThemes = portletThemes_.get(categoryName);
+      for (String theme : themesName)
+         catThemes.add(theme);
+   }
+
+   /**
+    * Get names of all the currently registered skins.
+    * 
+    * @return an unmodifiable Set of the currently registered skins
+    */
+   public Set<String> getAvailableSkinNames()
+   {
+      return availableSkins_;
+   }
+
+   /**
+    * Return the CSS content of the file specified by the given URI.
+    *
+    * @param cssPath path of the css to find
+    * @return the css
+    */
+   public String getCSS(String cssPath)
+   {
+      try
+      {
+         final StringBuilder sb = new StringBuilder();
+         renderCSS(new ResourceRenderer()
+         {
+            public Appendable getAppendable()
+            {
+               return sb;
+            }
+
+            public void setExpiration(long seconds)
+            {
+
+            }
+         }, cssPath);
+         return sb.toString();
+      }
+      catch (IOException e)
+      {
+         log.error("Error while rendering css " + cssPath, e);
+         return null;
+      }
+      catch (RenderingException e)
+      {
+         log.error("Error while rendering css " + cssPath, e);
+         return null;
+      }
+   }
+
+   public void renderCSS(ResourceRenderer renderer, String path) throws RenderingException, IOException
+   {
+      Orientation orientation = Orientation.LT;
+      if (path.endsWith("-lt.css"))
+      {
+         path = path.substring(0, path.length() - "-lt.css".length()) + ".css";
+      }
+      else if (path.endsWith("-rt.css"))
+      {
+         path = path.substring(0, path.length() - "-rt.css".length()) + ".css";
+         orientation = Orientation.RT;
+      }
+
+      // Try cache first
+      if (!PropertyManager.isDevelopping())
+      {
+
+         if (path.startsWith("/" + portalContainerName + "/resource"))
+         {
+            renderer.setExpiration(ONE_MONTH);
+         }
+         else
+         {
+            renderer.setExpiration(ONE_HOUR);
+         }
+
+         //
+         Map<String, String> cache = orientation == Orientation.LT ? ltCache : rtCache;
+         String css = cache.get(path);
+         if (css == null)
+         {
+            StringBuilder sb = new StringBuilder();
+            processCSS(sb, path, orientation, true);
+            css = sb.toString();
+            cache.put(path, css);
+         }
+         renderer.getAppendable().append(css);
+      }
+      else
+      {
+         processCSS(renderer.getAppendable(), path, orientation, false);
+      }
+   }
+
+   public String getMergedCSS(String cssPath)
+   {
+      return ltCache.get(cssPath);
+   }
+
+   public Collection<SkinConfig> getPortalSkins(String skinName)
+   {
+      Set<SkinKey> keys = portalSkins_.keySet();
+      Collection<SkinConfig> portalSkins = new ArrayList<SkinConfig>();
+      for (SkinKey key : keys)
+      {
+         if (key.getName().equals(skinName))
+            portalSkins.add(portalSkins_.get(key));
+      }
+      return portalSkins;
+   }
+
+   public Map<String, Set<String>> getPortletThemes()
+   {
+      return portletThemes_;
+   }
+
+   public SkinConfig getSkin(String module, String skinName)
+   {
+      SkinConfig config = skinConfigs_.get(new SkinKey(module, skinName));
+      if (config == null)
+         skinConfigs_.get(new SkinKey(module, "Default"));
+      return config;
+   }
+
+   public void invalidatePortalSkinCache(String portalName, String skinName)
+   {
+      SkinKey key = new SkinKey(portalName, skinName);
+      skinConfigs_.remove(key);
+   }
+
+   public void remove(String module, String skinName) throws Exception
+   {
+      SkinKey key;
+      if (skinName.length() == 0)
+         key = new SkinKey(module, "Default");
+      else
+         key = new SkinKey(module, skinName);
+      skinConfigs_.remove(key);
+   }
+
+   public int size()
+   {
+      return skinConfigs_.size();
+   }
+
+   private void processCSS(Appendable appendable, String cssPath, Orientation orientation, boolean merge)
+      throws RenderingException, IOException
+   {
+      Resource skin = mainResolver.resolve(cssPath);
+      processCSSRecursively(appendable, merge, skin, orientation);
+   }
+
+   private void processCSSRecursively(Appendable appendable, boolean merge, Resource skin, Orientation orientation)
+      throws RenderingException, IOException
+   {
+
+      // The root URL for the entry
+      String basePath = skin.getContextPath() + skin.getParentPath();
+
+      //
+      String line = "";
+      Reader tmp = skin.read();
+      if (tmp == null)
+      {
+         throw new RenderingException("No skin resolved for path " + skin.getResourcePath());
+      }
+      BufferedReader reader = new BufferedReader(tmp);
+      try
+      {
+         while ((line = reader.readLine()) != null)
+         {
+            Matcher matcher = IMPORT_PATTERN.matcher(line);
+            if (matcher.find())
+            {
+               String includedPath = matcher.group(2);
+               if (includedPath.startsWith("/"))
+               {
+                  if (merge)
+                  {
+                     Resource ssskin = mainResolver.resolve(includedPath);
+                     processCSSRecursively(appendable, merge, ssskin, orientation);
+                  }
+                  else
+                  {
+                     appendable.append(matcher.group(1)).append(
+                        includedPath.substring(0, includedPath.length() - ".css".length())).append(
+                        getSuffix(orientation)).append(matcher.group(3)).append("\n");
+                  }
+               }
+               else
+               {
+                  if (merge)
+                  {
+                     String path = skin.getContextPath() + skin.getParentPath() + includedPath;
+                     Resource ssskin = mainResolver.resolve(path);
+                     processCSSRecursively(appendable, merge, ssskin, orientation);
+                  }
+                  else
+                  {
+                     appendable.append(matcher.group(1));
+                     appendable.append(basePath);
+                     appendable.append(includedPath.substring(0, includedPath.length() - ".css".length()));
+                     appendable.append(getSuffix(orientation));
+                     appendable.append(matcher.group(3));
+                  }
+               }
+            }
+            else
+            {
+               if (orientation == null || wantInclude(line, orientation))
+               {
+                  append(line, basePath, appendable);
+               }
+            }
+         }
+      }
+      finally
+      {
+         Safe.close(reader);
+      }
+   }
+
+   /**
+    * Filter what if it's annotated with the alternative orientation.
+    *
+    * @param line the line to include
+    * @param orientation the orientation
+    * @return true if the line is included
+    */
+   private boolean wantInclude(String line, Orientation orientation)
+   {
+      Pattern orientationPattern = orientation == Orientation.LT ? RT : LT;
+      Matcher matcher2 = orientationPattern.matcher(line);
+      return !matcher2.find();
+   }
+
+   private void append(String line, String basePath, Appendable appendable) throws IOException
+   {
+      // Rewrite background url pattern
+      Matcher matcher = BACKGROUND_PATTERN.matcher(line);
+      if (matcher.find() && !matcher.group(2).startsWith("\"/") && !matcher.group(2).startsWith("'/")
+         && !matcher.group(2).startsWith("/"))
+      {
+         appendable.append(matcher.group(1)).append(basePath).append(matcher.group(2)).append(matcher.group(3)).append(
+            '\n');
+      }
+      else
+      {
+         appendable.append(line).append('\n');
+      }
+   }
+
+   String getSuffix(Orientation orientation)
+   {
+      if (orientation == null)
+      {
+         orientation = Orientation.LT;
+      }
+      return suffixMap.get(orientation);
+   }
+
+   @Managed
+   @ManagedDescription("The list of registered skins identifiers")
+   public String[] getSkinList()
+   {
+      // get all available skin
+      List<String> availableSkin = new ArrayList<String>();
+      for (String skin : availableSkins_)
+      {
+         availableSkin.add(skin);
+      }
+      // sort skin name asc
+      Collections.sort(availableSkin);
+
+      return availableSkin.toArray(new String[availableSkin.size()]);
+   }
+
+   @Managed
+   @ManagedDescription("Reload all skins")
+   public void reloadSkins()
+   {
+      // remove all ltCache, rtCache
+      ltCache.clear();
+      rtCache.clear();
+   }
+
+   @Managed
+   @ManagedDescription("Reload a specified skin")
+   public void reloadSkin(@ManagedDescription("The skin id") @ManagedName("skinId") String skinId)
+   {
+      ltCache.remove(skinId);
+      rtCache.remove(skinId);
+   }
+
+   public void start()
+   {
+      DefaultServletContainerFactory.getInstance().getServletContainer().addWebAppListener(deployer);
+   }
+
+   public void stop()
+   {
+      DefaultServletContainerFactory.getInstance().getServletContainer().removeWebAppListener(deployer);
+   }
+}
\ No newline at end of file

Added: portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/SkinURL.java
===================================================================
--- portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/SkinURL.java	                        (rev 0)
+++ portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/SkinURL.java	2009-10-30 04:29:24 UTC (rev 451)
@@ -0,0 +1,46 @@
+/**
+ * Copyright (C) 2009 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.exoplatform.portal.resource;
+
+import org.exoplatform.services.resources.Orientation;
+
+/**
+ * The URL of a skin.
+ *
+ * @author <a href="mailto:julien.viet at exoplatform.com">Julien Viet</a>
+ * @version $Revision$
+ */
+public interface SkinURL
+{
+
+   /**
+    * Sets the orientation on the skin URL.
+    *
+    * @param orientation the orientation
+    */
+   void setOrientation(Orientation orientation);
+
+   /**
+   * This method is used to compute the path of a CSS.
+   *
+   * @return the CSS path, containing the orientation suffix.
+   */
+   String toString();
+}

Modified: portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/tasks/AbstractSkinTask.java
===================================================================
--- portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/tasks/AbstractSkinTask.java	2009-10-30 04:15:28 UTC (rev 450)
+++ portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/tasks/AbstractSkinTask.java	2009-10-30 04:29:24 UTC (rev 451)
@@ -19,7 +19,7 @@
 
 package org.exoplatform.portal.resource.config.tasks;
 
-import org.exoplatform.portal.skin.SkinService;
+import org.exoplatform.portal.resource.SkinService;
 
 import javax.servlet.ServletContext;
 

Modified: portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/tasks/I18nTask.java
===================================================================
--- portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/tasks/I18nTask.java	2009-10-30 04:15:28 UTC (rev 450)
+++ portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/tasks/I18nTask.java	2009-10-30 04:29:24 UTC (rev 451)
@@ -19,7 +19,7 @@
 
 package org.exoplatform.portal.resource.config.tasks;
 
-import org.exoplatform.portal.skin.SkinService;
+import org.exoplatform.portal.resource.SkinService;
 
 import javax.servlet.ServletContext;
 

Modified: portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/tasks/PortalSkinTask.java
===================================================================
--- portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/tasks/PortalSkinTask.java	2009-10-30 04:15:28 UTC (rev 450)
+++ portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/tasks/PortalSkinTask.java	2009-10-30 04:29:24 UTC (rev 451)
@@ -19,7 +19,7 @@
 
 package org.exoplatform.portal.resource.config.tasks;
 
-import org.exoplatform.portal.skin.SkinService;
+import org.exoplatform.portal.resource.SkinService;
 
 import javax.servlet.ServletContext;
 

Modified: portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/tasks/PortletSkinTask.java
===================================================================
--- portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/tasks/PortletSkinTask.java	2009-10-30 04:15:28 UTC (rev 450)
+++ portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/tasks/PortletSkinTask.java	2009-10-30 04:29:24 UTC (rev 451)
@@ -19,7 +19,7 @@
 
 package org.exoplatform.portal.resource.config.tasks;
 
-import org.exoplatform.portal.skin.SkinService;
+import org.exoplatform.portal.resource.SkinService;
 
 import javax.servlet.ServletContext;
 

Modified: portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/tasks/ThemeTask.java
===================================================================
--- portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/tasks/ThemeTask.java	2009-10-30 04:15:28 UTC (rev 450)
+++ portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/tasks/ThemeTask.java	2009-10-30 04:29:24 UTC (rev 451)
@@ -19,7 +19,7 @@
 
 package org.exoplatform.portal.resource.config.tasks;
 
-import org.exoplatform.portal.skin.SkinService;
+import org.exoplatform.portal.resource.SkinService;
 
 import java.util.ArrayList;
 import java.util.List;

Added: portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/xml/AbstractTaskXMLBinding.java
===================================================================
--- portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/xml/AbstractTaskXMLBinding.java	                        (rev 0)
+++ portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/xml/AbstractTaskXMLBinding.java	2009-10-30 04:29:24 UTC (rev 451)
@@ -0,0 +1,197 @@
+/**
+ * Copyright (C) 2009 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.exoplatform.portal.resource.config.xml;
+
+import org.exoplatform.portal.resource.config.tasks.AbstractSkinTask;
+import org.exoplatform.portal.resource.config.tasks.PortalSkinTask;
+import org.exoplatform.portal.resource.config.tasks.PortletSkinTask;
+import org.exoplatform.portal.resource.config.tasks.ThemeTask;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+/**
+ * 
+ * Created by eXoPlatform SAS
+ *
+ * Author: Minh Hoang TO - hoang281283 at gmail.com
+ *
+ *      Sep 17, 2009
+ */
+public abstract class AbstractTaskXMLBinding
+{
+
+   /** Bind an XML element to a skin task */
+   abstract public AbstractSkinTask xmlToTask(Element element);
+
+   public static class PortalSkinTaskXMLBinding extends AbstractTaskXMLBinding
+   {
+
+      @Override
+      public AbstractSkinTask xmlToTask(Element element)
+      {
+         if (!element.getTagName().equals(GateinResource.PORTAl_SKIN_TAG))
+         {
+            return null;
+         }
+         PortalSkinTask pTask = new PortalSkinTask();
+         bindingCSSPath(pTask, element);
+         bindingSkinName(pTask, element);
+
+         return pTask;
+      }
+
+      private void bindingCSSPath(PortalSkinTask task, Element element)
+      {
+         NodeList nodes = element.getElementsByTagName(GateinResource.CSS_PATH_TAG);
+         if (nodes == null || nodes.getLength() < 1)
+         {
+            return;
+         }
+         String cssPath = nodes.item(0).getFirstChild().getNodeValue();
+         task.setCSSPath(cssPath);
+      }
+
+      private void bindingSkinName(PortalSkinTask task, Element element)
+      {
+         NodeList nodes = element.getElementsByTagName(GateinResource.SKIN_NAME_TAG);
+         if (nodes == null || nodes.getLength() < 1)
+         {
+            return;
+         }
+         String skinName = nodes.item(0).getFirstChild().getNodeValue();
+         task.setSkinName(skinName);
+      }
+
+   }
+
+   public static class ThemeTaskXMLBinding extends AbstractTaskXMLBinding
+   {
+      @Override
+      public AbstractSkinTask xmlToTask(Element element)
+      {
+         if (!element.getTagName().equals(GateinResource.WINDOW_STYLE_TAG))
+         {
+            return null;
+         }
+         ThemeTask tTask = new ThemeTask();
+
+         bindingStyleName(tTask, element);
+         bindingThemeNames(tTask, element);
+
+         return tTask;
+      }
+
+      private void bindingStyleName(ThemeTask task, Element element)
+      {
+         NodeList nodes = element.getElementsByTagName(GateinResource.STYLE_NAME_TAG);
+         if (nodes == null || nodes.getLength() < 1)
+         {
+            return;
+         }
+         String styleName = nodes.item(0).getFirstChild().getNodeValue();
+         task.setStyleName(styleName);
+      }
+
+      private void bindingThemeNames(ThemeTask task, Element element)
+      {
+         NodeList nodes = element.getElementsByTagName(GateinResource.THEME_NAME_TAG);
+         if (nodes == null)
+         {
+            return;
+         }
+         for (int i = nodes.getLength() - 1; i >= 0; i--)
+         {
+            task.addThemeName(nodes.item(i).getFirstChild().getNodeValue());
+         }
+      }
+   }
+
+   public static class PortletSkinTaskXMLBinding extends AbstractTaskXMLBinding
+   {
+      @Override
+      public AbstractSkinTask xmlToTask(Element element)
+      {
+         if (!element.getTagName().equals(GateinResource.PORTLET_SKIN_TAG))
+         {
+            return null;
+         }
+         PortletSkinTask pTask = new PortletSkinTask();
+         bindingApplicationName(pTask, element);
+         bindingPortletName(pTask, element);
+         bindingCSSPath(pTask, element);
+         bindingSkinName(pTask, element);
+         return pTask;
+      }
+
+      private void bindingApplicationName(PortletSkinTask task, Element element)
+      {
+         NodeList nodes = element.getElementsByTagName(GateinResource.APPLICATION_NAME_TAG);
+         if (nodes == null || nodes.getLength() < 1)
+         {
+            return;
+         }
+         String applicationName = nodes.item(0).getFirstChild().getNodeValue();
+         task.setApplicationName(applicationName);
+      }
+
+      private void bindingPortletName(PortletSkinTask task, Element element)
+      {
+         NodeList nodes = element.getElementsByTagName(GateinResource.PORTLET_NAME_TAG);
+         if (nodes == null || nodes.getLength() < 1)
+         {
+            return;
+         }
+         String portletName = nodes.item(0).getFirstChild().getNodeValue();
+         task.setPortletName(portletName);
+      }
+
+      private void bindingCSSPath(PortletSkinTask task, Element element)
+      {
+         NodeList nodes = element.getElementsByTagName(GateinResource.CSS_PATH_TAG);
+         if (nodes == null || nodes.getLength() < 1)
+         {
+            return;
+         }
+         String cssPath = nodes.item(0).getFirstChild().getNodeValue();
+         task.setCSSPath(cssPath);
+      }
+
+      private void bindingSkinName(PortletSkinTask task, Element element)
+      {
+         NodeList nodes = element.getElementsByTagName(GateinResource.SKIN_NAME_TAG);
+         if (nodes == null || nodes.getLength() < 1)
+         {
+            return;
+         }
+         String skinName = nodes.item(0).getFirstChild().getNodeValue();
+         task.setSkinName(skinName);
+      }
+   }
+
+   public static class I18nTaskXMLBinding extends AbstractTaskXMLBinding
+   {
+      @Override
+      public AbstractSkinTask xmlToTask(Element element)
+      {
+         return null;
+      }
+   }
+
+}

Added: portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/xml/GateinResource.java
===================================================================
--- portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/xml/GateinResource.java	                        (rev 0)
+++ portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/xml/GateinResource.java	2009-10-30 04:29:24 UTC (rev 451)
@@ -0,0 +1,54 @@
+/**
+ * Copyright (C) 2009 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.exoplatform.portal.resource.config.xml;
+
+/**
+ * 
+ * Created by eXoPlatform SAS
+ *
+ * Author: Minh Hoang TO - hoang281283 at gmail.com
+ *
+ *      Sep 17, 2009
+ */
+public interface GateinResource
+{
+
+   final public static String SKIN_DEF_TAG = "skin-def";
+
+   final public static String SKIN_NAME_TAG = "skin-name";
+
+   final public static String PORTAl_SKIN_TAG = "portal-skin";
+
+   final public static String PORTLET_SKIN_TAG = "portlet-skin";
+
+   final public static String PORTLET_NAME_TAG = "portlet-name";
+
+   final public static String APPLICATION_NAME_TAG = "application-name";
+
+   final public static String CSS_PATH_TAG = "css-path";
+
+   final public static String WINDOW_STYLE_TAG = "window-style";
+
+   final public static String STYLE_NAME_TAG = "style-name";
+
+   final public static String STYLE_THEME_TAG = "style-theme";
+
+   final public static String THEME_NAME_TAG = "theme-name";
+}

Added: portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/xml/SkinConfigParser.java
===================================================================
--- portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/xml/SkinConfigParser.java	                        (rev 0)
+++ portal/trunk/component/portal/src/main/java/org/exoplatform/portal/resource/config/xml/SkinConfigParser.java	2009-10-30 04:29:24 UTC (rev 451)
@@ -0,0 +1,123 @@
+/**
+ * Copyright (C) 2009 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.exoplatform.portal.resource.config.xml;
+
+import org.exoplatform.portal.resource.SkinService;
+import org.exoplatform.portal.resource.config.tasks.AbstractSkinTask;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+/**
+ * 
+ * Created by eXoPlatform SAS
+ *
+ * Author: Minh Hoang TO - hoang281283 at gmail.com
+ *
+ *      Sep 16, 2009
+ */
+public class SkinConfigParser
+{
+
+   private final static Map<String, AbstractTaskXMLBinding> allBindings = new HashMap<String, AbstractTaskXMLBinding>();
+
+   static
+   {
+      allBindings.put(GateinResource.PORTAl_SKIN_TAG, new AbstractTaskXMLBinding.PortalSkinTaskXMLBinding());
+      allBindings.put(GateinResource.PORTLET_SKIN_TAG, new AbstractTaskXMLBinding.PortletSkinTaskXMLBinding());
+      allBindings.put(GateinResource.WINDOW_STYLE_TAG, new AbstractTaskXMLBinding.ThemeTaskXMLBinding());
+   }
+
+   public static void processConfigResource(InputStream is, SkinService skinService, ServletContext scontext)
+   {
+      List<AbstractSkinTask> allTasks = fetchTasks(is);
+      if (allTasks != null)
+      {
+         for (AbstractSkinTask task : allTasks)
+         {
+            task.execute(skinService, scontext);
+         }
+      }
+   }
+
+   private static List<AbstractSkinTask> fetchTasks(InputStream is)
+   {
+      try
+      {
+         DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+         Document document = docBuilder.parse(is);
+         return fetchTasksFromXMLConfig(document);
+      }
+      catch (Exception ex)
+      {
+         return null;
+      }
+   }
+
+   private static List<AbstractSkinTask> fetchTasksFromXMLConfig(Document document)
+   {
+      List<AbstractSkinTask> tasks = new ArrayList<AbstractSkinTask>();
+      Element docElement = document.getDocumentElement();
+
+      fetchTasksByTagName(GateinResource.PORTAl_SKIN_TAG, docElement, tasks);
+      fetchTasksByTagName(GateinResource.PORTLET_SKIN_TAG, docElement, tasks);
+      fetchTasksByTagName(GateinResource.WINDOW_STYLE_TAG, docElement, tasks);
+
+      return tasks;
+   }
+
+   /**
+    * 
+    * @param tagName
+    * @param rootElement
+    * @param tasks
+    */
+   private static void fetchTasksByTagName(String tagName, Element rootElement, List<AbstractSkinTask> tasks)
+   {
+      AbstractTaskXMLBinding binding = allBindings.get(tagName);
+      //If there is no binding for current tagName, then return
+      if (binding == null)
+      {
+         return;
+      }
+
+      NodeList nodes = rootElement.getElementsByTagName(tagName);
+      AbstractSkinTask task;
+
+      for (int i = nodes.getLength() - 1; i >= 0; i--)
+      {
+         task = binding.xmlToTask((Element)nodes.item(i));
+         if (task != null)
+         {
+            tasks.add(task);
+         }
+      }
+   }
+}



More information about the gatein-commits mailing list