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