001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.resource.loader;
018
019import org.apache.wicket.Application;
020import org.apache.wicket.Component;
021import org.apache.wicket.settings.ResourceSettings;
022import org.apache.wicket.util.lang.Args;
023
024import java.util.ArrayList;
025import java.util.Iterator;
026import java.util.List;
027import java.util.Locale;
028import java.util.regex.Matcher;
029import java.util.regex.Pattern;
030
031/**
032 * Creates a nested string resource loader which resolves nested keys.<br>
033 * <br>
034 * Example:
035 * 
036 * <pre>
037 * <code>
038 * List<IStringResourceLoader> loaders = getResourceSettings().getStringResourceLoaders();
039 * // Add more loaders here
040 * NestedStringResourceLoader element = new NestedStringResourceLoader(loaders,Pattern.compile("#\\(([^ ]*?)\\)"));
041 * loaders.clear();
042 * loaders.add(element);
043 * </code>
044 * </pre>
045 * 
046 * @author Sven Meier
047 * @author Tobias Soloschenko
048 *
049 */
050public class NestedStringResourceLoader implements IStringResourceLoader
051{
052        private final Pattern pattern;
053
054        private final List<IStringResourceLoader> loaders;
055
056        private final ResourceSettings resourceSettings;
057
058        /**
059         * Creates a nested string resource loader
060         * 
061         * @param loaders
062         *            the loaders to be added in a chain
063         * @param pattern
064         *            the pattern for nested keys. Example for <b>#(key)</b> is the pattern:
065         *            <b>Pattern.compile("#\\(([^ ]*?)\\)");</b>
066         */
067        public NestedStringResourceLoader(List<IStringResourceLoader> loaders, Pattern pattern)
068        {
069                this.loaders = new ArrayList<>(loaders);
070                this.pattern = Args.notNull(pattern, "pattern");
071                this.resourceSettings = Application.get().getResourceSettings();
072        }
073
074        @Override
075        public String loadStringResource(Component component, String key, Locale locale, String style,
076                String variation)
077        {
078                return loadNestedStringResource(component, key, locale, style, variation);
079        }
080
081        @Override
082        public String loadStringResource(Class<?> clazz, String key, Locale locale, String style,
083                String variation)
084        {
085                return loadNestedStringResource(clazz, key, locale, style, variation);
086        }
087
088        /**
089         * loads nested string resources
090         * 
091         * @param scope
092         *            the scope to find the key
093         * @param key
094         *            the actual key
095         * @param locale
096         *            the locale
097         * @param style
098         *            the style
099         * @param variation
100         *            the variation
101         * @return the load string
102         */
103        private String loadNestedStringResource(Object scope, String key, Locale locale, String style,
104                String variation)
105        {
106                Class<?> clazz = null;
107                Component component = null;
108                if (scope instanceof Component)
109                {
110                        component = (Component)scope;
111                }
112                else
113                {
114                        clazz = (Class<?>)scope;
115                }
116
117                Iterator<IStringResourceLoader> iter = loaders.iterator();
118                String value = null;
119                while (iter.hasNext() && (value == null))
120                {
121                        IStringResourceLoader loader = iter.next();
122                        value = component != null
123                                ? loader.loadStringResource(component, key, locale, style, variation)
124                                : loader.loadStringResource(clazz, key, locale, style, variation);
125                }
126
127                if (value == null)
128                {
129                        return null;
130                }
131                
132                StringBuffer output = new StringBuffer();
133                Matcher matcher = pattern.matcher(value);
134                // Search for other nested keys to replace
135                while (matcher.find())
136                {
137                        String nestedKey = matcher.group(1);
138                        String replacedPlaceHolder = component != null
139                                ? loadNestedStringResource(component, nestedKey, locale, style, variation)
140                                : loadNestedStringResource(clazz, nestedKey, locale, style, variation);
141
142                        if (replacedPlaceHolder == null)
143                        {
144                                return null;
145                        }
146                        matcher.appendReplacement(output, replacedPlaceHolder);
147                }
148                matcher.appendTail(output);
149                return output.toString();
150        }
151}