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