1    /*
2     * Copyright 2007 :torweg free software group
3     * 
4     * This program is free software: you can redistribute it and/or modify
5     * it under the terms of the GNU General Public License as published by
6     * the Free Software Foundation, either version 3 of the License, or
7     * (at your option) any later version.
8     * 
9     * This program is distributed in the hope that it will be useful,
10    * but WITHOUT ANY WARRANTY; without even the implied warranty of
11    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    * GNU General Public License for more details.
13    * 
14    * You should have received a copy of the GNU General Public License
15    * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16    *
17    */
18   package org.torweg.pulse.site.content;
19   
20   import java.util.Collections;
21   import java.util.HashMap;
22   import java.util.Locale;
23   import java.util.Map;
24   
25   import org.jdom.Element;
26   import org.slf4j.Logger;
27   import org.slf4j.LoggerFactory;
28   import org.torweg.pulse.configuration.AbstractConfigBean;
29   import org.torweg.pulse.site.content.suffices.SuffixConverter;
30   import org.torweg.pulse.util.LocaleUtils;
31   
32   /**
33    * global configuration for {@code AbstractBasicContent}.
34    * 
35    * @author Thomas Weber
36    * @version $Revision: 1861 $
37    */
38   public class AbstractBasicContentConfig extends AbstractConfigBean {
39   
40       /**
41        * serialVersionUID.
42        */
43       private static final long serialVersionUID = 234949128996236986L;
44   
45       /**
46        * all URL safe characters.
47        */
48       private static final String URL_SAFE_CHARACTERS = "abcdefghijklmnopqrstuvwxyz"
49               + "ABCDEFGHIJKLMNOPQRSTUWVXYZ" + "1234567890-_.";
50   
51       /**
52        * the default substitution character.
53        */
54       private static final String SUBSTITUTION_CHARCTER = "_";
55   
56       /**
57        * the default omittable characters.
58        */
59       private static final String OMIT_CHARACTERS = "!\",;?()[]";
60   
61       /**
62        * the logger.
63        */
64       private static final Logger LOGGER = LoggerFactory
65               .getLogger(AbstractBasicContentConfig.class);
66   
67       /**
68        * the suffix converters.
69        */
70       private final Map<Locale, SuffixConverter> converters = new HashMap<Locale, SuffixConverter>();
71   
72       /**
73        * initialises the {@code ConfigBean}.
74        * 
75        * @param conf
76        *            the config XML
77        * @see org.torweg.pulse.configuration.ConfigBean#init(org.jdom.Element)
78        */
79       public void init(final Element conf) {
80           if (conf.getChild("suffix-converters") != null) {
81               for (Object o : conf.getChild("suffix-converters").getChildren(
82                       "locale")) {
83                   Element localeConfig = (Element) o;
84                   try {
85                       Locale locale = LocaleUtils.localeFromString(localeConfig
86                               .getAttributeValue("name"));
87                       String converter = localeConfig.getAttributeValue("class");
88                       converters.put(locale, (SuffixConverter) Class.forName(
89                               converter).newInstance());
90                       LOGGER.debug("added converter '{}' for locale '{}'.",
91                               converter, locale);
92                   } catch (Exception e) {
93                       LOGGER
94                               .error(
95                                       "Could not instantiate SuffixConverter '{}' for Locale '{}':{}",
96                                       new Object[] {
97                                               localeConfig
98                                                       .getAttributeValue("class"),
99                                               localeConfig
100                                                      .getAttributeValue("name"),
101                                              e.getLocalizedMessage() });
102                  }
103              }
104          } else {
105              LOGGER.warn("no converter configurations found.");
106          }
107      }
108  
109      /**
110       * converts a suffix to a search engine optimised URL containing only URL
111       * safe characters.
112       * 
113       * @param suffix
114       *            the suffix
115       * @param contentLocale
116       *            the locale of the content
117       * @return the converted suffix
118       */
119      public final String convert(final String suffix, final Locale contentLocale) {
120          for (Locale converterLocale : this.converters.keySet()) {
121              LOGGER.trace("checking '{}' against '{}': {}", new Object[] {
122                      converterLocale, contentLocale,
123                      LocaleUtils.matches(converterLocale, contentLocale) });
124              if (LocaleUtils.matches(converterLocale, contentLocale)) {
125                  return postConvert(this.converters.get(converterLocale)
126                          .convert(
127                                  suffix.toLowerCase(new Locale(contentLocale
128                                          .getLanguage()))));
129              }
130          }
131          return postConvert(suffix.toLowerCase(contentLocale));
132      }
133  
134      /**
135       * conversion method which is run after the locale based {@code
136       * SuffixConverter} to ensure that the suffix does only contain URL safe
137       * characters.
138       * 
139       * @param suffix
140       *            the suffix
141       * @return the converted suffix
142       */
143      private String postConvert(final String suffix) {
144          StringBuilder converted = new StringBuilder();
145          for (int i = 0; i < suffix.length(); i++) {
146              String c = suffix.substring(i, i + 1);
147              if (URL_SAFE_CHARACTERS.indexOf(c) > -1) {
148                  converted.append(c);
149              } else if (c.equals(" ")) {
150                  converted.append("-");
151              } else if (OMIT_CHARACTERS.indexOf(c) > -1) {
152                  continue;
153              } else {
154                  converted.append(SUBSTITUTION_CHARCTER);
155              }
156          }
157          return converted.toString();
158      }
159  
160      /**
161       * returns a {@code Map} of the known locales and their corresponding
162       * {@code SuffixConverter}s.
163       * 
164       * @return the converters as an unmodifiable map
165       */
166      protected final Map<Locale, SuffixConverter> getConverters() {
167          return Collections.unmodifiableMap(this.converters);
168      }
169  
170  }
171