001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.model;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.util.GetterUtil;
020    import com.liferay.portal.kernel.util.ListUtil;
021    import com.liferay.portal.kernel.util.PropsKeys;
022    import com.liferay.portal.kernel.util.StringPool;
023    import com.liferay.portal.kernel.util.StringUtil;
024    import com.liferay.portal.kernel.util.Tuple;
025    import com.liferay.portal.kernel.util.Validator;
026    import com.liferay.portal.kernel.xml.Document;
027    import com.liferay.portal.kernel.xml.Element;
028    import com.liferay.portal.kernel.xml.SAXReader;
029    import com.liferay.portal.service.ClassNameLocalServiceUtil;
030    import com.liferay.portal.util.PropsUtil;
031    import com.liferay.util.PwdGenerator;
032    
033    import java.io.InputStream;
034    
035    import java.net.URL;
036    
037    import java.util.ArrayList;
038    import java.util.Collections;
039    import java.util.Enumeration;
040    import java.util.HashMap;
041    import java.util.LinkedHashMap;
042    import java.util.List;
043    import java.util.Map;
044    import java.util.Set;
045    import java.util.TreeMap;
046    import java.util.TreeSet;
047    
048    /**
049     * @author Brian Wing Shun Chan
050     * @author Tomas Polesovsky
051     */
052    public class ModelHintsImpl implements ModelHints {
053    
054            public void afterPropertiesSet() {
055                    _hintCollections = new HashMap<String, Map<String, String>>();
056                    _defaultHints = new HashMap<String, Map<String, String>>();
057                    _modelFields = new HashMap<String, Object>();
058                    _models = new TreeSet<String>();
059    
060                    try {
061                            ClassLoader classLoader = getClass().getClassLoader();
062    
063                            String[] configs = StringUtil.split(
064                                    PropsUtil.get(PropsKeys.MODEL_HINTS_CONFIGS));
065    
066                            for (String config : configs) {
067                                    if (config.startsWith("classpath*:")) {
068                                            String name = config.substring("classpath*:".length());
069    
070                                            Enumeration<URL> enu = classLoader.getResources(name);
071    
072                                            if (_log.isDebugEnabled() && !enu.hasMoreElements()) {
073                                                    _log.debug("No resources found for " + name);
074                                            }
075    
076                                            while (enu.hasMoreElements()) {
077                                                    URL url = enu.nextElement();
078    
079                                                    if (_log.isDebugEnabled()) {
080                                                            _log.debug("Loading " + name + " from " + url);
081                                                    }
082    
083                                                    InputStream inputStream = url.openStream();
084    
085                                                    read(classLoader, url.toString(), inputStream);
086                                            }
087                                    }
088                                    else {
089                                            read(classLoader, config);
090                                    }
091                            }
092                    }
093                    catch (Exception e) {
094                            _log.error(e, e);
095                    }
096            }
097    
098            public String buildCustomValidatorName(String validatorName) {
099                    return validatorName.concat(StringPool.UNDERLINE).concat(
100                            PwdGenerator.getPassword(PwdGenerator.KEY3, 4));
101            }
102    
103            public Map<String, String> getDefaultHints(String model) {
104                    return _defaultHints.get(model);
105            }
106    
107            public com.liferay.portal.kernel.xml.Element getFieldsEl(
108                    String model, String field) {
109    
110                    Map<String, Object> fields = (Map<String, Object>)_modelFields.get(
111                            model);
112    
113                    if (fields == null) {
114                            return null;
115                    }
116                    else {
117                            Element fieldsEl = (Element)fields.get(field + _ELEMENTS_SUFFIX);
118    
119                            if (fieldsEl == null) {
120                                    return null;
121                            }
122                            else {
123                                    return fieldsEl;
124                            }
125                    }
126            }
127    
128            public Map<String, String> getHints(String model, String field) {
129                    Map<String, Object> fields =
130                            (Map<String, Object>)_modelFields.get(model);
131    
132                    if (fields == null) {
133                            return null;
134                    }
135                    else {
136                            return (Map<String, String>)fields.get(field + _HINTS_SUFFIX);
137                    }
138            }
139    
140            public List<String> getModels() {
141                    return ListUtil.fromCollection(_models);
142            }
143    
144            public Tuple getSanitizeTuple(String model, String field) {
145                    Map<String, Object> fields = (Map<String, Object>)_modelFields.get(
146                            model);
147    
148                    if (fields == null) {
149                            return null;
150                    }
151                    else {
152                            return (Tuple)fields.get(field + _SANITIZE_SUFFIX);
153                    }
154            }
155    
156            public List<Tuple> getSanitizeTuples(String model) {
157                    Map<String, Object> fields = (Map<String, Object>)_modelFields.get(
158                            model);
159    
160                    if (fields == null) {
161                            return Collections.emptyList();
162                    }
163                    else {
164                            List<Tuple> sanitizeTuples = new ArrayList<Tuple>();
165    
166                            for (Map.Entry<String, Object> entry : fields.entrySet()) {
167                                    String key = entry.getKey();
168    
169                                    if (key.endsWith(_SANITIZE_SUFFIX)) {
170                                            Tuple sanitizeTuple = (Tuple)entry.getValue();
171    
172                                            sanitizeTuples.add(sanitizeTuple);
173                                    }
174                            }
175    
176                            return sanitizeTuples;
177                    }
178            }
179    
180            public String getType(String model, String field) {
181                    Map<String, Object> fields = (Map<String, Object>)_modelFields.get(
182                            model);
183    
184                    if (fields == null) {
185                            return null;
186                    }
187                    else {
188                            return (String)fields.get(field + _TYPE_SUFFIX);
189                    }
190            }
191    
192            public List<Tuple> getValidators(String model, String field) {
193                    Map<String, Object> fields = (Map<String, Object>)_modelFields.get(
194                            model);
195    
196                    if ((fields == null) ||
197                            (fields.get(field + _VALIDATORS_SUFFIX) == null)) {
198    
199                            return null;
200                    }
201                    else {
202                            return (List<Tuple>)fields.get(field + _VALIDATORS_SUFFIX);
203                    }
204            }
205    
206            public boolean isCustomValidator(String validatorName) {
207                    if (validatorName.equals("custom")) {
208                            return true;
209                    }
210    
211                    return false;
212            }
213    
214            public boolean isLocalized(String model, String field) {
215                    Map<String, Object> fields = (Map<String, Object>)_modelFields.get(
216                            model);
217    
218                    if (fields == null) {
219                            return false;
220                    }
221                    else {
222                            Boolean localized = (Boolean)fields.get(
223                                    field + _LOCALIZATION_SUFFIX);
224    
225                            if (localized != null) {
226                                    return localized;
227                            }
228                            else {
229                                    return false;
230                            }
231                    }
232            }
233    
234            public void read(ClassLoader classLoader, String source) throws Exception {
235                    read(classLoader, source, classLoader.getResourceAsStream(source));
236            }
237    
238            public void read(
239                            ClassLoader classLoader, String source, InputStream inputStream)
240                    throws Exception {
241    
242                    if (inputStream == null) {
243                            if (_log.isWarnEnabled()) {
244                                    _log.warn("Cannot load " + source);
245                            }
246    
247                            return;
248                    }
249                    else {
250                            if (_log.isDebugEnabled()) {
251                                    _log.debug("Loading " + source);
252                            }
253                    }
254    
255                    Document document = _saxReader.read(inputStream);
256    
257                    Element rootElement = document.getRootElement();
258    
259                    List<Element> rootElements = rootElement.elements("hint-collection");
260    
261                    for (Element hintCollectionElement : rootElements) {
262                            String name = hintCollectionElement.attributeValue("name");
263    
264                            Map<String, String> hints = _hintCollections.get(name);
265    
266                            if (hints == null) {
267                                    hints = new HashMap<String, String>();
268    
269                                    _hintCollections.put(name, hints);
270                            }
271    
272                            List<Element> hintElements = hintCollectionElement.elements("hint");
273    
274                            for (Element hintElement : hintElements) {
275                                    String hintName = hintElement.attributeValue("name");
276                                    String hintValue = hintElement.getText();
277    
278                                    hints.put(hintName, hintValue);
279                            }
280                    }
281    
282                    rootElements = rootElement.elements("model");
283    
284                    for (Element modelElement : rootElements) {
285                            String name = modelElement.attributeValue("name");
286    
287                            if (classLoader != ModelHintsImpl.class.getClassLoader()) {
288                                    ClassNameLocalServiceUtil.getClassName(name);
289                            }
290    
291                            Map<String, String> defaultHints = new HashMap<String, String>();
292    
293                            _defaultHints.put(name, defaultHints);
294    
295                            Element defaultHintsElement = modelElement.element("default-hints");
296    
297                            if (defaultHintsElement != null) {
298                                    List<Element> hintElements = defaultHintsElement.elements(
299                                            "hint");
300    
301                                    for (Element hintElement : hintElements) {
302                                            String hintName = hintElement.attributeValue("name");
303                                            String hintValue = hintElement.getText();
304    
305                                            defaultHints.put(hintName, hintValue);
306                                    }
307                            }
308    
309                            Map<String, Object> fields = (Map<String, Object>)_modelFields.get(
310                                    name);
311    
312                            if (fields == null) {
313                                    fields = new LinkedHashMap<String, Object>();
314    
315                                    _modelFields.put(name, fields);
316                            }
317    
318                            _models.add(name);
319    
320                            List<Element> modelElements = modelElement.elements("field");
321    
322                            for (Element fieldElement : modelElements) {
323                                    String fieldName = fieldElement.attributeValue("name");
324                                    String fieldType = fieldElement.attributeValue("type");
325                                    boolean fieldLocalized = GetterUtil.getBoolean(
326                                            fieldElement.attributeValue("localized"));
327    
328                                    Map<String, String> fieldHints = new HashMap<String, String>();
329    
330                                    fieldHints.putAll(defaultHints);
331    
332                                    List<Element> fieldElements = fieldElement.elements(
333                                            "hint-collection");
334    
335                                    for (Element hintCollectionElement : fieldElements) {
336                                            Map<String, String> hints = _hintCollections.get(
337                                                    hintCollectionElement.attributeValue("name"));
338    
339                                            fieldHints.putAll(hints);
340                                    }
341    
342                                    fieldElements = fieldElement.elements("hint");
343    
344                                    for (Element hintElement : fieldElements) {
345                                            String hintName = hintElement.attributeValue("name");
346                                            String hintValue = hintElement.getText();
347    
348                                            fieldHints.put(hintName, hintValue);
349                                    }
350    
351                                    Tuple fieldSanitize = null;
352    
353                                    Element sanitizeElement = fieldElement.element("sanitize");
354    
355                                    if (sanitizeElement != null) {
356                                            String contentType = sanitizeElement.attributeValue(
357                                                    "content-type");
358                                            String modes = sanitizeElement.attributeValue("modes");
359    
360                                            fieldSanitize = new Tuple(fieldName, contentType, modes);
361                                    }
362    
363                                    Map<String, Tuple> fieldValidators =
364                                            new TreeMap<String, Tuple>();
365    
366                                    fieldElements = fieldElement.elements("validator");
367    
368                                    for (Element validatorElement : fieldElements) {
369                                            String validatorName = validatorElement.attributeValue(
370                                                    "name");
371    
372                                            if (Validator.isNull(validatorName)) {
373                                                    continue;
374                                            }
375    
376                                            String validatorErrorMessage = GetterUtil.getString(
377                                                    validatorElement.attributeValue("error-message"));
378                                            String validatorValue = GetterUtil.getString(
379                                                    validatorElement.getText());
380                                            boolean customValidator = isCustomValidator(validatorName);
381    
382                                            if (customValidator) {
383                                                    validatorName = buildCustomValidatorName(validatorName);
384                                            }
385    
386                                            Tuple fieldValidator = new Tuple(
387                                                    fieldName, validatorName, validatorErrorMessage,
388                                                    validatorValue, customValidator);
389    
390                                            fieldValidators.put(validatorName, fieldValidator);
391                                    }
392    
393                                    fields.put(fieldName + _ELEMENTS_SUFFIX, fieldElement);
394                                    fields.put(fieldName + _TYPE_SUFFIX, fieldType);
395                                    fields.put(fieldName + _LOCALIZATION_SUFFIX, fieldLocalized);
396                                    fields.put(fieldName + _HINTS_SUFFIX, fieldHints);
397    
398                                    if (fieldSanitize != null) {
399                                            fields.put(fieldName + _SANITIZE_SUFFIX, fieldSanitize);
400                                    }
401    
402                                    if (!fieldValidators.isEmpty()) {
403                                            fields.put(
404                                                    fieldName + _VALIDATORS_SUFFIX,
405                                                    ListUtil.fromMapValues(fieldValidators));
406                                    }
407                            }
408                    }
409            }
410    
411            public void setSAXReader(SAXReader saxReader) {
412                    _saxReader = saxReader;
413            }
414    
415            public String trimString(String model, String field, String value) {
416                    if (value == null) {
417                            return value;
418                    }
419    
420                    Map<String, String> hints = getHints(model, field);
421    
422                    if (hints == null) {
423                            return value;
424                    }
425    
426                    int maxLength = GetterUtil.getInteger(
427                            ModelHintsConstants.TEXT_MAX_LENGTH);
428    
429                    maxLength = GetterUtil.getInteger(hints.get("max-length"), maxLength);
430    
431                    if (value.length() > maxLength) {
432                            return value.substring(0, maxLength);
433                    }
434                    else {
435                            return value;
436                    }
437            }
438    
439            private static final String _ELEMENTS_SUFFIX = "_ELEMENTS";
440    
441            private static final String _HINTS_SUFFIX = "_HINTS";
442    
443            private static final String _LOCALIZATION_SUFFIX = "_LOCALIZATION";
444    
445            private static final String _SANITIZE_SUFFIX = "_SANITIZE_SUFFIX";
446    
447            private static final String _TYPE_SUFFIX = "_TYPE";
448    
449            private static final String _VALIDATORS_SUFFIX = "_VALIDATORS";
450    
451            private static Log _log = LogFactoryUtil.getLog(ModelHintsImpl.class);
452    
453            private Map<String, Map<String, String>> _defaultHints;
454            private Map<String, Map<String, String>> _hintCollections;
455            private Map<String, Object> _modelFields;
456            private Set<String> _models;
457            private SAXReader _saxReader;
458    
459    }