001/*
002 * (C) Copyright 2006-2007 Nuxeo SAS <http://nuxeo.com> and others
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 *
009 * Contributors:
010 *     Jean-Marc Orliaguet, Chalmers
011 *
012 * $Id$
013 */
014
015package org.nuxeo.theme.resources;
016
017import java.net.MalformedURLException;
018import java.net.URL;
019import java.util.ArrayList;
020import java.util.HashMap;
021import java.util.List;
022
023import org.apache.commons.logging.Log;
024import org.apache.commons.logging.LogFactory;
025import org.nuxeo.runtime.api.Framework;
026import org.nuxeo.runtime.services.event.Event;
027import org.nuxeo.runtime.services.event.EventService;
028import org.nuxeo.theme.Manager;
029import org.nuxeo.theme.Registrable;
030import org.nuxeo.theme.themes.ThemeException;
031import org.nuxeo.theme.themes.ThemeManager;
032import org.nuxeo.theme.types.TypeFamily;
033import org.nuxeo.theme.types.TypeRegistry;
034
035public final class ResourceManager implements Registrable {
036
037    private static final Log log = LogFactory.getLog(ResourceManager.class);
038
039    public static final String GLOBAL_RESOURCES_REGISTERED_EVENT = "themeGlobalResourcesRegistered";
040
041    private final HashMap<URL, List<String>> globalCache = new HashMap<URL, List<String>>();
042
043    private final ThreadLocal<List<String>> localCache = new ThreadLocal<List<String>>() {
044        @Override
045        protected List<String> initialValue() {
046            return new ArrayList<String>();
047        }
048    };
049
050    private final TypeRegistry typeRegistry = Manager.getTypeRegistry();
051
052    public void addResource(String name, URL themeUrl) {
053        addResource(name, themeUrl, false);
054    }
055
056    public void addResource(String name, URL themeUrl, boolean local) {
057        if (local) {
058            log.debug("Added local resource: " + name);
059        } else {
060            log.debug("Added theme resource: " + name);
061        }
062        ResourceType resourceType = (ResourceType) typeRegistry.lookup(TypeFamily.RESOURCE, name);
063        if (resourceType != null) {
064            for (String dependency : resourceType.getDependencies()) {
065                log.debug("  Subresource dependency: " + name + " -> " + dependency);
066                addResource(dependency, themeUrl, local);
067            }
068            List<String> scripts;
069            if (local) {
070                scripts = getLocalResources();
071            } else {
072                scripts = getGlobalResourcesFor(themeUrl);
073            }
074            if (!scripts.contains(name)) {
075                scripts.add(name);
076            }
077        } else {
078            log.warn("Resource not found: " + name);
079        }
080    }
081
082    public void flush() {
083        getLocalResources().clear();
084    }
085
086    private List<String> getLocalResources() {
087        return localCache.get();
088    }
089
090    public List<String> getResourcesFor(String themeUrl) {
091        List<String> resources = new ArrayList<String>();
092        resources.addAll(getGlobalResourcesFor(themeUrl));
093        for (String localResource : getLocalResources()) {
094            if (!resources.contains(localResource)) {
095                resources.add(localResource);
096            }
097        }
098        return resources;
099    }
100
101    public List<String> getGlobalResourcesFor(String themeUrl) {
102        try {
103            return getGlobalResourcesFor(new URL(themeUrl));
104        } catch (MalformedURLException e) {
105            log.warn(e);
106            return null;
107        }
108    }
109
110    public synchronized List<String> getGlobalResourcesFor(URL themeUrl) {
111        if (!globalCache.containsKey(themeUrl)) {
112            globalCache.put(themeUrl, new ArrayList<String>());
113            // hook to notify potential listeners that the resources for given
114            // theme url need to be built
115            EventService eventService = Framework.getLocalService(EventService.class);
116            eventService.sendEvent(new Event(ThemeManager.THEME_TOPIC, GLOBAL_RESOURCES_REGISTERED_EVENT, this,
117                    themeUrl));
118        }
119        return globalCache.get(themeUrl);
120    }
121
122    public void clearGlobalCache(String themeName) {
123        List<URL> toRemove = new ArrayList<URL>();
124        for (URL themeUrl : globalCache.keySet()) {
125            String name = ThemeManager.getThemeNameByUrl(themeUrl);
126            if (themeName.equals(name)) {
127                toRemove.add(themeUrl);
128
129            }
130        }
131        for (URL themeUrl : toRemove) {
132            globalCache.remove(themeUrl);
133        }
134    }
135
136    public static byte[] getBinaryBankResource(String resourceBankName, String collectionName, String typeName,
137            String resourceName) throws ThemeException {
138        byte[] data = null;
139        ResourceBank resourceBank = ThemeManager.getResourceBank(resourceBankName);
140        data = resourceBank.getResourceContent(collectionName, typeName, resourceName);
141        if (data == null) {
142            throw new ThemeException("Resource bank content could not be read: " + resourceName);
143        }
144        return data;
145    }
146
147    public static String getBankResource(String resourceBankName, String collectionName, String typeName,
148            String resourceName) throws ThemeException {
149        byte[] data = getBinaryBankResource(resourceBankName, collectionName, typeName, resourceName);
150        if (data == null) {
151            throw new ThemeException("Could not get bank resource: " + resourceName);
152        }
153        return new String(data);
154    }
155
156    @Override
157    public void clear() {
158        globalCache.clear();
159    }
160
161}