    001// Copyright 2006-2013 The Apache Software Foundation
    002//
    003// Licensed under the Apache License, Version 2.0 (the "License");
    004// you may not use this file except in compliance with the License.
    005// You may obtain a copy of the License at
    006//
    007// http://www.apache.org/licenses/LICENSE-2.0
    008//
    009// Unless required by applicable law or agreed to in writing, software
    010// distributed under the License is distributed on an "AS IS" BASIS,
    011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    012// See the License for the specific language governing permissions and
    013// limitations under the License.
    014
    015package org.apache.tapestry5.internal.services;
    016
    017import java.util.Arrays;
    018import java.util.List;
    019import java.util.Locale;
    020import java.util.concurrent.Callable;
    021
    022import org.apache.tapestry5.commons.Messages;
    023import org.apache.tapestry5.commons.Resource;
    024import org.apache.tapestry5.commons.internal.util.TapestryException;
    025import org.apache.tapestry5.commons.services.InvalidationEventHub;
    026import org.apache.tapestry5.http.TapestryHttpSymbolConstants;
    027import org.apache.tapestry5.ioc.annotations.PostInjection;
    028import org.apache.tapestry5.ioc.annotations.Symbol;
    029import org.apache.tapestry5.ioc.internal.util.URLChangeTracker;
    030import org.apache.tapestry5.ioc.services.ClasspathURLConverter;
    031import org.apache.tapestry5.ioc.services.ThreadLocale;
    032import org.apache.tapestry5.ioc.services.UpdateListener;
    033import org.apache.tapestry5.model.ComponentModel;
    034import org.apache.tapestry5.services.messages.ComponentMessagesSource;
    035import org.apache.tapestry5.services.messages.PropertiesFileParser;
    036import org.apache.tapestry5.services.pageload.ComponentRequestSelectorAnalyzer;
    037import org.apache.tapestry5.services.pageload.ComponentResourceLocator;
    038import org.apache.tapestry5.services.pageload.ComponentResourceSelector;
    039
    040public class ComponentMessagesSourceImpl implements ComponentMessagesSource, UpdateListener
    041{
    042    private final MessagesSourceImpl messagesSource;
    043
    044    private final MessagesBundle appCatalogBundle;
    045
    046    private final ComponentRequestSelectorAnalyzer componentRequestSelectorAnalyzer;
    047
    048    private final ThreadLocale threadLocale;
    049
    050    private class ComponentModelBundle implements MessagesBundle
    051    {
    052        private final ComponentModel model;
    053
    054        public ComponentModelBundle(ComponentModel model)
    055        {
    056            this.model = model;
    057        }
    058
    059        public Resource getBaseResource()
    060        {
    061            return model.getBaseResource();
    062        }
    063
    064        public Object getId()
    065        {
    066            return model.getComponentClassName();
    067        }
    068
    069        public MessagesBundle getParent()
    070        {
    071            ComponentModel parentModel = model.getParentModel();
    072
    073            if (parentModel == null)
    074                return appCatalogBundle;
    075
    076            return new ComponentModelBundle(parentModel);
    077        }
    078    }
    079
    080    public ComponentMessagesSourceImpl(@Symbol(TapestryHttpSymbolConstants.PRODUCTION_MODE)
    081                                       boolean productionMode, List<Resource> appCatalogResources, PropertiesFileParser parser,
    082                                       ComponentResourceLocator resourceLocator, ClasspathURLConverter classpathURLConverter,
    083                                       ComponentRequestSelectorAnalyzer componentRequestSelectorAnalyzer,
    084                                       ThreadLocale threadLocale)
    085    {
    086        this(productionMode, appCatalogResources, resourceLocator, parser, new URLChangeTracker(classpathURLConverter), componentRequestSelectorAnalyzer, threadLocale);
    087    }
    088
    089    ComponentMessagesSourceImpl(boolean productionMode, Resource appCatalogResource,
    090                                ComponentResourceLocator resourceLocator, PropertiesFileParser parser,
    091                                URLChangeTracker tracker, ComponentRequestSelectorAnalyzer componentRequestSelectorAnalyzer,
    092                                ThreadLocale threadLocale)
    093    {
    094        this(productionMode, Arrays.asList(appCatalogResource), resourceLocator, parser, tracker, componentRequestSelectorAnalyzer, threadLocale);
    095    }
    096
    097    ComponentMessagesSourceImpl(boolean productionMode, List<Resource> appCatalogResources,
    098                                ComponentResourceLocator resourceLocator, PropertiesFileParser parser,
    099                                URLChangeTracker tracker, ComponentRequestSelectorAnalyzer componentRequestSelectorAnalyzer,
    100                                ThreadLocale threadLocale)
    101    {
    102        messagesSource = new MessagesSourceImpl(productionMode, productionMode ? null : tracker, resourceLocator,
    103                parser);
    104
    105        appCatalogBundle = createAppCatalogBundle(appCatalogResources);
    106        this.componentRequestSelectorAnalyzer = componentRequestSelectorAnalyzer;
    107        this.threadLocale = threadLocale;
    108    }
    109
    110    @PostInjection
    111    public void setupReload(ReloadHelper reloadHelper)
    112    {
    113        reloadHelper.addReloadCallback(new Runnable()
    114        {
    115            public void run()
    116            {
    117                messagesSource.invalidate();
    118            }
    119        });
    120    }
    121
    122    public void checkForUpdates()
    123    {
    124        messagesSource.checkForUpdates();
    125    }
    126
    127    public Messages getMessages(ComponentModel componentModel, Locale locale)
    128    {
    129       return getMessagesWithForcedLocale(() -> getMessages(componentModel, componentRequestSelectorAnalyzer.buildSelectorForRequest()), locale);
    130    }
    131
    132    private Messages getMessagesWithForcedLocale(Callable<Messages> callable, Locale locale)
    133    {
    134        final Locale original = threadLocale.getLocale();
    135        try
    136        {
    137           threadLocale.setLocale(locale);
    138           return callable.call();
    139        } catch (Exception e) {
    140            throw new TapestryException(e.getMessage(), e);
    141        }
    142        finally {
    143            threadLocale.setLocale(original);
    144        }
    145    }
    146
    147    public Messages getMessages(ComponentModel componentModel, ComponentResourceSelector selector)
    148    {
    149        final MessagesBundle bundle = new ComponentModelBundle(componentModel);
    150        return messagesSource.getMessages(bundle, selector);
    151    }
    152
    153    public Messages getApplicationCatalog(Locale locale)
    154    {
    155        return getMessagesWithForcedLocale(() -> messagesSource.getMessages(appCatalogBundle, componentRequestSelectorAnalyzer.buildSelectorForRequest()), locale);
    156    }
    157
    158    private MessagesBundle createAppCatalogBundle(List<Resource> resources)
    159    {
    160        MessagesBundle current = null;
    161
    162        for (Resource r : resources)
    163        {
    164            current = createMessagesBundle(r, current);
    165        }
    166
    167        return current;
    168    }
    169
    170    private MessagesBundle createMessagesBundle(final Resource resource, final MessagesBundle parent)
    171    {
    172        return new MessagesBundle()
    173        {
    174            public Resource getBaseResource()
    175            {
    176                return resource;
    177            }
    178
    179            public Object getId()
    180            {
    181                return resource.getPath();
    182            }
    183
    184            public MessagesBundle getParent()
    185            {
    186                return parent;
    187            }
    188        };
    189    }
    190
    191    public InvalidationEventHub getInvalidationEventHub()
    192    {
    193        return messagesSource;
    194    }
    195}