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