001
014
015 package com.liferay.portal.kernel.search;
016
017 import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayInputStream;
018 import com.liferay.portal.kernel.util.ArrayUtil;
019 import com.liferay.portal.kernel.util.DateFormatFactoryUtil;
020 import com.liferay.portal.kernel.util.FastDateFormatFactoryUtil;
021 import com.liferay.portal.kernel.util.FileUtil;
022 import com.liferay.portal.kernel.util.GetterUtil;
023 import com.liferay.portal.kernel.util.LocaleUtil;
024 import com.liferay.portal.kernel.util.LocalizationUtil;
025 import com.liferay.portal.kernel.util.PropsKeys;
026 import com.liferay.portal.kernel.util.PropsUtil;
027 import com.liferay.portal.kernel.util.SetUtil;
028 import com.liferay.portal.kernel.util.StringBundler;
029 import com.liferay.portal.kernel.util.StringPool;
030 import com.liferay.portal.kernel.util.StringUtil;
031 import com.liferay.portal.kernel.util.Validator;
032
033 import java.io.File;
034 import java.io.FileInputStream;
035 import java.io.IOException;
036 import java.io.InputStream;
037
038 import java.text.DateFormat;
039 import java.text.Format;
040 import java.text.ParseException;
041
042 import java.util.Arrays;
043 import java.util.Collection;
044 import java.util.Date;
045 import java.util.HashMap;
046 import java.util.Locale;
047 import java.util.Map;
048 import java.util.Set;
049
050
054 public class DocumentImpl implements Document {
055
056 public static String getLocalizedName(Locale locale, String name) {
057 if (locale == null) {
058 return name;
059 }
060
061 String languageId = LocaleUtil.toLanguageId(locale);
062
063 return getLocalizedName(languageId, name);
064 }
065
066 public static String getLocalizedName(String languageId, String name) {
067 return LocalizationUtil.getLocalizedName(name, languageId);
068 }
069
070 public static String getSortableFieldName(String name) {
071 return name.concat(StringPool.UNDERLINE).concat(_SORTABLE_FIELD_SUFFIX);
072 }
073
074 public static String getSortFieldName(Sort sort, String scoreFieldName) {
075 if (sort.getType() == Sort.SCORE_TYPE) {
076 return scoreFieldName;
077 }
078
079 String fieldName = sort.getFieldName();
080
081 if (fieldName.endsWith(_SORTABLE_FIELD_SUFFIX)) {
082 return fieldName;
083 }
084
085 String sortFieldName = null;
086
087 if (DocumentImpl.isSortableTextField(fieldName) ||
088 (sort.getType() != Sort.STRING_TYPE)) {
089
090 sortFieldName = DocumentImpl.getSortableFieldName(fieldName);
091 }
092
093 if (Validator.isNull(sortFieldName)) {
094 sortFieldName = scoreFieldName;
095 }
096
097 return sortFieldName;
098 }
099
100 public static boolean isSortableFieldName(String name) {
101 return name.endsWith(_SORTABLE_FIELD_SUFFIX);
102 }
103
104 public static boolean isSortableTextField(String name) {
105 return _defaultSortableTextFields.contains(name);
106 }
107
108 @Override
109 public void add(Field field) {
110 _fields.put(field.getName(), field);
111 }
112
113 @Override
114 public void addDate(String name, Date value) {
115 if (value == null) {
116 return;
117 }
118
119 addDate(name, new Date[] {value});
120 }
121
122 @Override
123 public void addDate(String name, Date[] values) {
124 if (values == null) {
125 return;
126 }
127
128 if (_dateFormat == null) {
129 _dateFormat = FastDateFormatFactoryUtil.getSimpleDateFormat(
130 _INDEX_DATE_FORMAT_PATTERN);
131 }
132
133 String[] dates = new String[values.length];
134 String[] datesTime = new String[values.length];
135
136 for (int i = 0; i < values.length; i++) {
137 dates[i] = _dateFormat.format(values[i]);
138 datesTime[i] = String.valueOf(values[i].getTime());
139 }
140
141 String sortableFieldName = getSortableFieldName(name);
142
143 Field field = createField(sortableFieldName, datesTime);
144
145 field.setNumeric(true);
146 field.setNumericClass(Long.class);
147
148 addKeyword(name, dates);
149 }
150
151 @Override
152 public void addFile(String name, byte[] bytes, String fileExt) {
153 InputStream is = new UnsyncByteArrayInputStream(bytes);
154
155 addFile(name, is, fileExt);
156 }
157
158 @Override
159 public void addFile(String name, File file, String fileExt)
160 throws IOException {
161
162 InputStream is = new FileInputStream(file);
163
164 addFile(name, is, fileExt);
165 }
166
167 @Override
168 public void addFile(String name, InputStream is, String fileExt) {
169 addText(name, FileUtil.extractText(is, fileExt));
170 }
171
172 @Override
173 public void addFile(
174 String name, InputStream is, String fileExt, int maxStringLength) {
175
176 addText(name, FileUtil.extractText(is, fileExt, maxStringLength));
177 }
178
179 @Override
180 public void addKeyword(String name, boolean value) {
181 addKeyword(name, String.valueOf(value));
182 }
183
184 @Override
185 public void addKeyword(String name, Boolean value) {
186 addKeyword(name, String.valueOf(value));
187 }
188
189 @Override
190 public void addKeyword(String name, boolean[] values) {
191 if (values == null) {
192 return;
193 }
194
195 addKeyword(name, ArrayUtil.toStringArray(values));
196 }
197
198 @Override
199 public void addKeyword(String name, Boolean[] values) {
200 if (values == null) {
201 return;
202 }
203
204 addKeyword(name, ArrayUtil.toStringArray(values));
205 }
206
207 @Override
208 public void addKeyword(String name, double value) {
209 addKeyword(name, String.valueOf(value));
210 }
211
212 @Override
213 public void addKeyword(String name, Double value) {
214 addKeyword(name, String.valueOf(value));
215 }
216
217 @Override
218 public void addKeyword(String name, double[] values) {
219 if (values == null) {
220 return;
221 }
222
223 addKeyword(name, ArrayUtil.toStringArray(values));
224 }
225
226 @Override
227 public void addKeyword(String name, Double[] values) {
228 if (values == null) {
229 return;
230 }
231
232 addKeyword(name, ArrayUtil.toStringArray(values));
233 }
234
235 @Override
236 public void addKeyword(String name, float value) {
237 addKeyword(name, String.valueOf(value));
238 }
239
240 @Override
241 public void addKeyword(String name, Float value) {
242 addKeyword(name, String.valueOf(value));
243 }
244
245 @Override
246 public void addKeyword(String name, float[] values) {
247 if (values == null) {
248 return;
249 }
250
251 addKeyword(name, ArrayUtil.toStringArray(values));
252 }
253
254 @Override
255 public void addKeyword(String name, Float[] values) {
256 if (values == null) {
257 return;
258 }
259
260 addKeyword(name, ArrayUtil.toStringArray(values));
261 }
262
263 @Override
264 public void addKeyword(String name, int value) {
265 addKeyword(name, String.valueOf(value));
266 }
267
268 @Override
269 public void addKeyword(String name, int[] values) {
270 if (values == null) {
271 return;
272 }
273
274 addKeyword(name, ArrayUtil.toStringArray(values));
275 }
276
277 @Override
278 public void addKeyword(String name, Integer value) {
279 addKeyword(name, String.valueOf(value));
280 }
281
282 @Override
283 public void addKeyword(String name, Integer[] values) {
284 if (values == null) {
285 return;
286 }
287
288 addKeyword(name, ArrayUtil.toStringArray(values));
289 }
290
291 @Override
292 public void addKeyword(String name, long value) {
293 addKeyword(name, String.valueOf(value));
294 }
295
296 @Override
297 public void addKeyword(String name, Long value) {
298 addKeyword(name, String.valueOf(value));
299 }
300
301 @Override
302 public void addKeyword(String name, long[] values) {
303 if (values == null) {
304 return;
305 }
306
307 addKeyword(name, ArrayUtil.toStringArray(values));
308 }
309
310 @Override
311 public void addKeyword(String name, Long[] values) {
312 if (values == null) {
313 return;
314 }
315
316 addKeyword(name, ArrayUtil.toStringArray(values));
317 }
318
319 @Override
320 public void addKeyword(String name, short value) {
321 addKeyword(name, String.valueOf(value));
322 }
323
324 @Override
325 public void addKeyword(String name, Short value) {
326 addKeyword(name, String.valueOf(value));
327 }
328
329 @Override
330 public void addKeyword(String name, short[] values) {
331 if (values == null) {
332 return;
333 }
334
335 addKeyword(name, ArrayUtil.toStringArray(values));
336 }
337
338 @Override
339 public void addKeyword(String name, Short[] values) {
340 if (values == null) {
341 return;
342 }
343
344 addKeyword(name, ArrayUtil.toStringArray(values));
345 }
346
347 @Override
348 public void addKeyword(String name, String value) {
349 addKeyword(name, value, false);
350 }
351
352 @Override
353 public void addKeyword(String name, String value, boolean lowerCase) {
354 if (lowerCase && Validator.isNotNull(value)) {
355 value = StringUtil.toLowerCase(value);
356 }
357
358 Field field = createField(name, value);
359
360 for (String fieldName : Field.UNSCORED_FIELD_NAMES) {
361 if (StringUtil.equalsIgnoreCase(name, fieldName)) {
362 field.setBoost(0);
363 }
364 }
365 }
366
367 @Override
368 public void addKeyword(String name, String[] values) {
369 if (values == null) {
370 return;
371 }
372
373 createField(name, values);
374 }
375
376 @Override
377 public void addLocalizedKeyword(String name, Map<Locale, String> values) {
378 addLocalizedKeyword(name, values, false);
379 }
380
381 @Override
382 public void addLocalizedKeyword(
383 String name, Map<Locale, String> values, boolean lowerCase) {
384
385 if ((values == null) || values.isEmpty()) {
386 return;
387 }
388
389 if (lowerCase) {
390 Map<Locale, String> lowerCaseValues = new HashMap<Locale, String>(
391 values.size());
392
393 for (Map.Entry<Locale, String> entry : values.entrySet()) {
394 String value = GetterUtil.getString(entry.getValue());
395
396 lowerCaseValues.put(
397 entry.getKey(), StringUtil.toLowerCase(value));
398 }
399
400 values = lowerCaseValues;
401 }
402
403 createField(name, values);
404 }
405
406 @Override
407 public void addLocalizedKeyword(
408 String name, Map<Locale, String> values, boolean lowerCase,
409 boolean sortable) {
410
411 if ((values == null) || values.isEmpty()) {
412 return;
413 }
414
415 if (lowerCase) {
416 Map<Locale, String> lowerCaseValues = new HashMap<Locale, String>(
417 values.size());
418
419 for (Map.Entry<Locale, String> entry : values.entrySet()) {
420 String value = GetterUtil.getString(entry.getValue());
421
422 lowerCaseValues.put(
423 entry.getKey(), StringUtil.toLowerCase(value));
424 }
425
426 values = lowerCaseValues;
427 }
428
429 createField(name, values, sortable);
430 }
431
432 @Override
433 public void addLocalizedText(String name, Map<Locale, String> values) {
434 if ((values == null) || values.isEmpty()) {
435 return;
436 }
437
438 Field field = createField(name, values);
439
440 field.setTokenized(true);
441 }
442
443
446 @Deprecated
447 @Override
448 public void addModifiedDate() {
449 addModifiedDate(new Date());
450 }
451
452
455 @Deprecated
456 @Override
457 public void addModifiedDate(Date modifiedDate) {
458 addDate(Field.MODIFIED, modifiedDate);
459 }
460
461 @Override
462 public void addNumber(String name, double value) {
463 addNumber(name, String.valueOf(value), Double.class);
464 }
465
466 @Override
467 public void addNumber(String name, Double value) {
468 addNumber(name, String.valueOf(value), Double.class);
469 }
470
471 @Override
472 public void addNumber(String name, double[] values) {
473 addNumber(name, ArrayUtil.toStringArray(values), Double.class);
474 }
475
476 @Override
477 public void addNumber(String name, Double[] values) {
478 addNumber(name, ArrayUtil.toStringArray(values), Double.class);
479 }
480
481 @Override
482 public void addNumber(String name, float value) {
483 addNumber(name, String.valueOf(value), Float.class);
484 }
485
486 @Override
487 public void addNumber(String name, Float value) {
488 addNumber(name, String.valueOf(value), Float.class);
489 }
490
491 @Override
492 public void addNumber(String name, float[] values) {
493 addNumber(name, ArrayUtil.toStringArray(values), Float.class);
494 }
495
496 @Override
497 public void addNumber(String name, Float[] values) {
498 addNumber(name, ArrayUtil.toStringArray(values), Float.class);
499 }
500
501 @Override
502 public void addNumber(String name, int value) {
503 addNumber(name, String.valueOf(value), Integer.class);
504 }
505
506 @Override
507 public void addNumber(String name, int[] values) {
508 addNumber(name, ArrayUtil.toStringArray(values), Integer.class);
509 }
510
511 @Override
512 public void addNumber(String name, Integer value) {
513 addNumber(name, String.valueOf(value), Integer.class);
514 }
515
516 @Override
517 public void addNumber(String name, Integer[] values) {
518 addNumber(name, ArrayUtil.toStringArray(values), Integer.class);
519 }
520
521 @Override
522 public void addNumber(String name, long value) {
523 addNumber(name, String.valueOf(value), Long.class);
524 }
525
526 @Override
527 public void addNumber(String name, Long value) {
528 addNumber(name, String.valueOf(value), Long.class);
529 }
530
531 @Override
532 public void addNumber(String name, long[] values) {
533 addNumber(name, ArrayUtil.toStringArray(values), Long.class);
534 }
535
536 @Override
537 public void addNumber(String name, Long[] values) {
538 addNumber(name, ArrayUtil.toStringArray(values), Long.class);
539 }
540
541 @Override
542 public void addNumber(String name, String value) {
543 addNumber(name, value, Long.class);
544 }
545
546 public void addNumber(
547 String name, String value, Class<? extends Number> clazz) {
548
549 if (Validator.isNull(value)) {
550 return;
551 }
552
553 addNumber(name, new String[] {value}, clazz);
554 }
555
556 @Override
557 public void addNumber(String name, String[] values) {
558 addNumber(name, values, Long.class);
559 }
560
561 public void addNumber(
562 String name, String[] values, Class<? extends Number> clazz) {
563
564 if (values == null) {
565 return;
566 }
567
568 String sortableFieldName = getSortableFieldName(name);
569
570 Field field = createField(sortableFieldName, values);
571
572 field.setNumeric(true);
573 field.setNumericClass(clazz);
574
575 addKeyword(name, values);
576 }
577
578 @Override
579 public void addText(String name, String value) {
580 if (Validator.isNull(value)) {
581 return;
582 }
583
584 Field field = createField(name, value);
585
586 field.setTokenized(true);
587
588 if (_sortableTextFields.contains(name)) {
589 String truncatedValue = value;
590
591 if (value.length() > _SORTABLE_TEXT_FIELDS_TRUNCATED_LENGTH) {
592 truncatedValue = value.substring(
593 0, _SORTABLE_TEXT_FIELDS_TRUNCATED_LENGTH);
594 }
595
596 addKeyword(getSortableFieldName(name), truncatedValue, true);
597 }
598 }
599
600 @Override
601 public void addText(String name, String[] values) {
602 if (values == null) {
603 return;
604 }
605
606 Field field = createField(name, values);
607
608 field.setTokenized(true);
609 }
610
611 @Override
612 public void addUID(String portletId, long field1) {
613 addUID(portletId, String.valueOf(field1));
614 }
615
616 @Override
617 public void addUID(String portletId, long field1, String field2) {
618 addUID(portletId, String.valueOf(field1), field2);
619 }
620
621 @Override
622 public void addUID(String portletId, Long field1) {
623 addUID(portletId, field1.longValue());
624 }
625
626 @Override
627 public void addUID(String portletId, Long field1, String field2) {
628 addUID(portletId, field1.longValue(), field2);
629 }
630
631 @Override
632 public void addUID(String portletId, String field1) {
633 addUID(portletId, field1, null);
634 }
635
636 @Override
637 public void addUID(String portletId, String field1, String field2) {
638 addUID(portletId, field1, field2, null);
639 }
640
641 @Override
642 public void addUID(
643 String portletId, String field1, String field2, String field3) {
644
645 addUID(portletId, field1, field2, field3, null);
646 }
647
648 @Override
649 public void addUID(
650 String portletId, String field1, String field2, String field3,
651 String field4) {
652
653 String uid = portletId + _UID_PORTLET + field1;
654
655 if (field2 != null) {
656 uid += _UID_FIELD + field2;
657 }
658
659 if (field3 != null) {
660 uid += _UID_FIELD + field3;
661 }
662
663 if (field4 != null) {
664 uid += _UID_FIELD + field4;
665 }
666
667 addKeyword(Field.UID, uid);
668 }
669
670 @Override
671 public Object clone() {
672 DocumentImpl documentImpl = new DocumentImpl();
673
674 documentImpl.setSortableTextFields(_sortableTextFields);
675
676 return documentImpl;
677 }
678
679 @Override
680 public String get(Locale locale, String name) {
681 if (locale == null) {
682 return get(name);
683 }
684
685 String localizedName = getLocalizedName(locale, name);
686
687 Field field = getField(localizedName);
688
689 if (field == null) {
690 field = getField(name);
691 }
692
693 if (field == null) {
694 return StringPool.BLANK;
695 }
696
697 return field.getValue();
698 }
699
700 @Override
701 public String get(Locale locale, String name, String defaultName) {
702 if (locale == null) {
703 return get(name, defaultName);
704 }
705
706 String localizedName = getLocalizedName(locale, name);
707
708 Field field = getField(localizedName);
709
710 if (field == null) {
711 localizedName = getLocalizedName(locale, defaultName);
712
713 field = getField(localizedName);
714 }
715
716 if (field == null) {
717 return StringPool.BLANK;
718 }
719
720 return field.getValue();
721 }
722
723 @Override
724 public String get(String name) {
725 Field field = getField(name);
726
727 if (field == null) {
728 return StringPool.BLANK;
729 }
730
731 return field.getValue();
732 }
733
734 @Override
735 public String get(String name, String defaultName) {
736 Field field = getField(name);
737
738 if (field == null) {
739 return get(defaultName);
740 }
741
742 return field.getValue();
743 }
744
745 @Override
746 public Date getDate(String name) throws ParseException {
747 DateFormat dateFormat = DateFormatFactoryUtil.getSimpleDateFormat(
748 _INDEX_DATE_FORMAT_PATTERN);
749
750 return dateFormat.parse(get(name));
751 }
752
753 @Override
754 public Field getField(String name) {
755 return doGetField(name, false);
756 }
757
758 @Override
759 public Map<String, Field> getFields() {
760 return _fields;
761 }
762
763 @Override
764 public String getPortletId() {
765 String uid = getUID();
766
767 int pos = uid.indexOf(_UID_PORTLET);
768
769 return uid.substring(0, pos);
770 }
771
772 @Override
773 public String getUID() {
774 Field field = getField(Field.UID);
775
776 if (field == null) {
777 throw new RuntimeException("UID is not set");
778 }
779
780 return field.getValue();
781 }
782
783 @Override
784 public String[] getValues(String name) {
785 Field field = getField(name);
786
787 if (field == null) {
788 return new String[] {StringPool.BLANK};
789 }
790
791 return field.getValues();
792 }
793
794 @Override
795 public boolean hasField(String name) {
796 if (_fields.containsKey(name)) {
797 return true;
798 }
799
800 return false;
801 }
802
803 @Override
804 public boolean isDocumentSortableTextField(String name) {
805 return _sortableTextFields.contains(name);
806 }
807
808 @Override
809 public void remove(String name) {
810 _fields.remove(name);
811 }
812
813 public void setFields(Map<String, Field> fields) {
814 _fields = fields;
815 }
816
817 @Override
818 public void setSortableTextFields(String[] sortableTextFields) {
819 _sortableTextFields = SetUtil.fromArray(sortableTextFields);
820 }
821
822 @Override
823 public String toString() {
824 StringBundler sb = new StringBundler(5 * _fields.size());
825
826 toString(sb, _fields.values());
827
828 return sb.toString();
829 }
830
831 protected Field createField(String name) {
832 return doGetField(name, true);
833 }
834
835 protected Field createField(
836 String name, boolean sortable, String... values) {
837
838 Field field = createField(name);
839
840 field.setSortable(sortable);
841 field.setValues(values);
842
843 return field;
844 }
845
846 protected Field createField(
847 String name, Map<Locale, String> localizedValues) {
848
849 return createField(name, localizedValues, false);
850 }
851
852 protected Field createField(
853 String name, Map<Locale, String> localizedValues, boolean sortable) {
854
855 Field field = createField(name);
856
857 field.setLocalizedValues(localizedValues);
858 field.setSortable(sortable);
859
860 return field;
861 }
862
863 protected Field createField(String name, String... values) {
864 return createField(name, false, values);
865 }
866
867 protected Field doGetField(String name, boolean createIfNew) {
868 Field field = _fields.get(name);
869
870 if ((field == null) && createIfNew) {
871 field = new Field(name);
872
873 _fields.put(name, field);
874 }
875
876 return field;
877 }
878
879 protected void setSortableTextFields(Set<String> sortableTextFields) {
880 _sortableTextFields = sortableTextFields;
881 }
882
883 protected void toString(StringBundler sb, Collection<Field> fields) {
884 sb.append(StringPool.OPEN_CURLY_BRACE);
885
886 boolean firstField = true;
887
888 for (Field field : fields) {
889 if (!firstField) {
890 sb.append(StringPool.COMMA);
891 sb.append(StringPool.SPACE);
892 }
893 else {
894 firstField = false;
895 }
896
897 if (field.hasChildren()) {
898 sb.append(field.getName());
899 sb.append(StringPool.COLON);
900
901 toString(sb, field.getFields());
902 }
903 else {
904 sb.append(field.getName());
905 sb.append(StringPool.EQUAL);
906 sb.append(Arrays.toString(field.getValues()));
907 }
908 }
909
910 sb.append(StringPool.CLOSE_CURLY_BRACE);
911 }
912
913 private static final String _INDEX_DATE_FORMAT_PATTERN = PropsUtil.get(
914 PropsKeys.INDEX_DATE_FORMAT_PATTERN);
915
916 private static final String _SORTABLE_FIELD_SUFFIX = "sortable";
917
918 private static final int _SORTABLE_TEXT_FIELDS_TRUNCATED_LENGTH =
919 GetterUtil.getInteger(
920 PropsUtil.get(
921 PropsKeys.INDEX_SORTABLE_TEXT_FIELDS_TRUNCATED_LENGTH));
922
923 private static final String _UID_FIELD = "_FIELD_";
924
925 private static final String _UID_PORTLET = "_PORTLET_";
926
927 private static Format _dateFormat;
928 private static Set<String> _defaultSortableTextFields = SetUtil.fromArray(
929 PropsUtil.getArray(PropsKeys.INDEX_SORTABLE_TEXT_FIELDS));
930
931 private Map<String, Field> _fields = new HashMap<String, Field>();
932 private Set<String> _sortableTextFields = _defaultSortableTextFields;
933
934 }