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