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