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.util.resource;
018
019import java.util.Arrays;
020import java.util.HashSet;
021import java.util.Locale;
022import java.util.Set;
023import java.util.regex.Matcher;
024import java.util.regex.Pattern;
025
026import org.apache.wicket.util.string.Strings;
027
028/**
029 * 
030 * @author Juergen Donnerstag
031 */
032public class ResourceUtils
033{
034        /** The default postfix for minified names (ex: /css/mystyle.min.css) **/
035        public static final String MIN_POSTFIX_DEFAULT = "min";
036        /** The default postfix for minified names (ex: /css/mystyle.min.css) **/
037        public static final String MIN_POSTFIX_DEFAULT_AS_EXTENSION = ".min.";
038        /** Regular expression patter to extract the locale from the filename (ex: de_DE) **/
039        private static final Pattern LOCALE_MIN_PATTERN = Pattern
040                .compile("_([a-z]{2})(_([A-Z]{2})(_([^_\\.]+))?)?(\\.min)?$");
041        /** Stores standard ISO country codes from {@code java.util.Locale} **/
042        private final static Set<String> isoCountries = new HashSet<>(
043                Arrays.asList(Locale.getISOCountries()));
044        /** Stores standard ISO language codes from {@code java.util.Locale} **/
045        private final static Set<String> isoLanguages = new HashSet<>(
046                Arrays.asList(Locale.getISOLanguages()));
047        
048        /**
049         * Return the minified version for a given resource name.
050         * For example '/css/coolTheme.css' becomes '/css/coolTheme.min.css'
051         * 
052         * @param name
053         *                      The original resource name
054         * @param minPostfix
055         *                      The postfix to use for minified name
056         * @return The minified resource name
057         */
058        public static String getMinifiedName(String name, String minPostfix)
059        {
060                String minifiedName;
061                int idxOfExtension = name.lastIndexOf('.');
062                final String dottedPostfix = "." + minPostfix;
063                
064                if (idxOfExtension > -1)
065                {
066                        String extension = name.substring(idxOfExtension);
067                        final String baseName = name.substring(0, name.length() - extension.length() + 1);
068                        if (!dottedPostfix.equals(extension) && !baseName.endsWith(dottedPostfix + "."))
069                        {
070                                minifiedName = baseName + minPostfix + extension;
071                        } else
072                        {
073                                minifiedName = name;
074                        }
075                } else
076                {
077                        minifiedName = name + dottedPostfix;
078                }
079                return minifiedName;
080        }
081        
082        /**
083         * Extract the locale from the filename taking into account possible minimized resource name.
084         * 
085         * E.g. {@code file_us_EN.min.js} will correctly determine a locale of {@code us_EN} by
086         * stripping the {@code .min} from the filename, the filename returned will be
087         * {@code file.min.js}, if you want the {@code .min} to be removed as well, use
088         * {@link #getLocaleFromFilename(String)} instead.
089         * 
090         * @param path
091         *            The file path
092         * @return The updated path, without the locale
093         */
094        public static PathLocale getLocaleFromFilename(String path)
095        {
096                String extension = "";
097
098                final int pos = path.lastIndexOf('.');
099                if (pos != -1)
100                {
101                        extension = path.substring(pos);
102                        path = path.substring(0, pos);
103                }
104
105                String filename = Strings.lastPathComponent(path, '/');
106                Matcher matcher = LOCALE_MIN_PATTERN.matcher(filename);
107                if (matcher.find())
108                {
109                        String language = matcher.group(1);
110                        String country = matcher.group(3);
111                        String variant = matcher.group(5);
112                        String min = matcher.group(6);
113
114                        // did we find a language?
115                        if (language != null)
116                        {
117                                if (isoLanguages.contains(language) == false)
118                                {
119                                        language = null;
120                                        country = null;
121                                        variant = null;
122                                }
123                        }
124
125                        // did we find a country?
126                        if ((language != null) && (country != null))
127                        {
128                                if (isoCountries.contains(country) == false)
129                                {
130                                        country = null;
131                                        variant = null;
132                                }
133                        }
134
135                        if (language != null)
136                        {
137                                int languagePos = path.length() - filename.length() + matcher.start();
138                                String basePath = path.substring(0, languagePos) + (min == null ? "" : min) +
139                                        extension;
140
141                                Locale locale = new Locale(language, country != null ? country : "",
142                                        variant != null ? variant : "");
143
144                                return new PathLocale(basePath, locale);
145                        }
146                } // else skip the whole thing... probably user specific underscores used
147
148                return new PathLocale(path + extension, null);
149        }
150
151        /**
152         * 
153         */
154        public static class PathLocale
155        {
156                /** */
157                public final String path;
158
159                /** */
160                public final Locale locale;
161
162                /**
163                 * @param path
164                 * @param locale
165                 */
166                public PathLocale(final String path, final Locale locale)
167                {
168                        this.path = path;
169                        this.locale = locale;
170                }
171        }
172}