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