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