001/*
002 *  Copyright 2010 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.cms.source;
017
018import java.io.IOException;
019import java.net.MalformedURLException;
020import java.util.Map;
021import java.util.regex.Matcher;
022import java.util.regex.Pattern;
023
024import org.apache.avalon.framework.context.Context;
025import org.apache.avalon.framework.context.ContextException;
026import org.apache.avalon.framework.context.Contextualizable;
027import org.apache.avalon.framework.logger.AbstractLogEnabled;
028import org.apache.avalon.framework.service.ServiceException;
029import org.apache.avalon.framework.service.ServiceManager;
030import org.apache.avalon.framework.service.Serviceable;
031import org.apache.cocoon.components.ContextHelper;
032import org.apache.cocoon.environment.Request;
033import org.apache.excalibur.source.Source;
034import org.apache.excalibur.source.SourceFactory;
035
036/**
037 * This factory looks for files in the current skin and fallback in the current plugin dir.
038 * Use: content-view:[view]://path_to_file
039 * Will first look in the skin of the current site in the sub-directory stylesheet/{contenttype} => skins/{skin}/stylesheets/{contenttype}/path_to_file
040 * And if the file does not exist will search in plugin:{currentPluginName}://path_to_file 
041 */
042public class ContentViewSourceFactory extends AbstractLogEnabled implements SourceFactory, Contextualizable, Serviceable
043{
044    // regexp for location : content-view://<ctype>/<view>/<format>.<extension>
045    private static final Pattern __SOURCE_PATTERN = Pattern.compile("^[^:]*+://([^/]+)/view-([^/]*)/([^/]+)\\.([^/]+)$");
046
047    private Context _context;
048    private ContentView _contentView;
049    private ServiceManager _manager;
050    
051    public void service(ServiceManager manager) throws ServiceException
052    {
053        _manager = manager;
054    }
055    
056    @Override
057    public void contextualize(Context context) throws ContextException
058    {
059        _context = context;
060    }
061
062    @Override
063    public Source getSource(String location, Map parameters) throws IOException, MalformedURLException
064    {
065        // lazy initialization to prevent chicken/egg scenario on startup
066        if (_contentView == null)
067        {
068            try
069            {
070                _contentView = (ContentView) _manager.lookup(ContentView.ROLE);
071            }
072            catch (ServiceException e)
073            {
074                throw new IllegalStateException("Exception while getting ContentView", e);
075            }
076        }
077        
078        Matcher m = __SOURCE_PATTERN.matcher(location);
079        if (!m.matches())
080        {
081            throw new MalformedURLException("URI must be like protocol://<ctype>/view-<view>/<format>.<extension>. Location was '" + location + "'");
082        }
083        
084        Request request = ContextHelper.getRequest(_context);
085        String pluginName = request.getParameter("pluginName");
086        if (pluginName == null)
087        {
088            pluginName = (String) request.getAttribute("pluginName");
089        }
090        
091        String contentType = m.group(1);
092        String view = m.group(2);
093        String format = m.group(3);
094        String extension = m.group(4);
095        
096        if (view == null || "".equals(view))
097        {
098            view = "main";
099        }
100        
101        return _contentView.getSource(location, contentType, view, format, pluginName, extension);
102    }
103
104    @Override
105    public void release(Source source)
106    {
107        // empty method
108    }
109}