    001    // Copyright 2007, 2008, 2010 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
    015    package org.apache.tapestry5.internal.services;
    016
    017    import org.apache.tapestry5.ComponentResources;
    018    import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
    019    import org.apache.tapestry5.ioc.services.SymbolSource;
    020    import org.apache.tapestry5.ioc.services.TypeCoercer;
    021    import org.apache.tapestry5.services.InvalidationListener;
    022    import org.apache.tapestry5.services.MetaDataLocator;
    023
    024    import java.util.Map;
    025
    026    public class MetaDataLocatorImpl implements MetaDataLocator, InvalidationListener
    027    {
    028        private final SymbolSource symbolSource;
    029
    030        private final TypeCoercer typeCoercer;
    031
    032        private final ComponentModelSource modelSource;
    033
    034        private final Map<String, Map<String, String>> defaultsByFolder = CollectionFactory
    035                .newCaseInsensitiveMap();
    036
    037        private final Map<String, String> cache = CollectionFactory.newConcurrentMap();
    038
    039        private interface ValueLocator
    040        {
    041            String valueForKey(String key);
    042        }
    043
    044        public MetaDataLocatorImpl(SymbolSource symbolSource, TypeCoercer typeCoercer,
    045                ComponentModelSource modelSource, Map<String, String> configuration)
    046        {
    047            this.symbolSource = symbolSource;
    048            this.typeCoercer = typeCoercer;
    049            this.modelSource = modelSource;
    050
    051            loadDefaults(configuration);
    052        }
    053
    054        public void objectWasInvalidated()
    055        {
    056            cache.clear();
    057        }
    058
    059        private void loadDefaults(Map<String, String> configuration)
    060        {
    061            for (Map.Entry<String, String> e : configuration.entrySet())
    062            {
    063                String key = e.getKey();
    064
    065                int colonx = key.indexOf(':');
    066
    067                String folderKey = colonx < 0 ? "" : key.substring(0, colonx);
    068
    069                Map<String, String> forFolder = defaultsByFolder.get(folderKey);
    070
    071                if (forFolder == null)
    072                {
    073                    forFolder = CollectionFactory.newCaseInsensitiveMap();
    074                    defaultsByFolder.put(folderKey, forFolder);
    075                }
    076
    077                String defaultKey = colonx < 0 ? key : key.substring(colonx + 1);
    078
    079                forFolder.put(defaultKey, e.getValue());
    080            }
    081        }
    082
    083        public <T> T findMeta(String key, final ComponentResources resources, Class<T> expectedType)
    084        {
    085            String value = getSymbolExpandedValueFromCache(key, resources.getCompleteId() + "/" + key,
    086                    new ValueLocator()
    087                    {
    088                        public String valueForKey(String key)
    089                        {
    090                            return locate(key, resources);
    091                        }
    092                    });
    093
    094            return typeCoercer.coerce(value, expectedType);
    095        }
    096
    097        public <T> T findMeta(String key, final String pageName, Class<T> expectedType)
    098        {
    099            String value = getSymbolExpandedValueFromCache(key, pageName + "/" + key,
    100                    new ValueLocator()
    101                    {
    102                        public String valueForKey(String key)
    103                        {
    104                            String result = modelSource.getPageModel(pageName).getMeta(key);
    105
    106                            return result != null ? result : locateInDefaults(key, pageName);
    107                        }
    108                    });
    109
    110            return typeCoercer.coerce(value, expectedType);
    111        }
    112
    113        private String getSymbolExpandedValueFromCache(String key, String cacheKey,
    114                ValueLocator valueLocator)
    115        {
    116            if (cache.containsKey(cacheKey))
    117                return cache.get(cacheKey);
    118
    119            String value = valueLocator.valueForKey(key);
    120
    121            if (value == null)
    122            {
    123                value = symbolSource.valueForSymbol(key);
    124            }
    125            else
    126            {
    127                value = symbolSource.expandSymbols(value);
    128            }
    129
    130            cache.put(cacheKey, value);
    131
    132            return value;
    133        }
    134
    135        private String locate(String key, ComponentResources resources)
    136        {
    137            ComponentResources cursor = resources;
    138
    139            while (true)
    140            {
    141                String value = cursor.getComponentModel().getMeta(key);
    142
    143                if (value != null)
    144                    return value;
    145
    146                ComponentResources next = cursor.getContainerResources();
    147
    148                if (next == null)
    149                    return locateInDefaults(key, cursor.getPageName());
    150
    151                cursor = next;
    152            }
    153        }
    154
    155        private String locateInDefaults(String key, String pageName)
    156        {
    157
    158            // We're going to peel this apart, slash by slash. Thus for
    159            // "mylib/myfolder/mysubfolder/MyPage" we'll be checking: "mylib/myfolder/mysubfolder",
    160            // then "mylib/myfolder", then "mylib", then "".
    161
    162            String path = pageName;
    163
    164            while (true)
    165            {
    166                int lastSlashx = path.lastIndexOf('/');
    167
    168                String folderKey = lastSlashx < 0 ? "" : path.substring(0, lastSlashx);
    169
    170                Map<String, String> forFolder = defaultsByFolder.get(folderKey);
    171
    172                if (forFolder != null && forFolder.containsKey(key))
    173                    return forFolder.get(key);
    174
    175                if (lastSlashx < 0)
    176                    break;
    177
    178                path = path.substring(0, lastSlashx);
    179            }
    180
    181            // Perhaps from here into the symbol sources? That may come later.
    182
    183            return null;
    184        }
    185    }