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