001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.search;
016    
017    import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayInputStream;
018    import com.liferay.portal.kernel.util.ArrayUtil;
019    import com.liferay.portal.kernel.util.DateFormatFactoryUtil;
020    import com.liferay.portal.kernel.util.FastDateFormatFactoryUtil;
021    import com.liferay.portal.kernel.util.FileUtil;
022    import com.liferay.portal.kernel.util.GetterUtil;
023    import com.liferay.portal.kernel.util.LocaleUtil;
024    import com.liferay.portal.kernel.util.PropsKeys;
025    import com.liferay.portal.kernel.util.PropsUtil;
026    import com.liferay.portal.kernel.util.SetUtil;
027    import com.liferay.portal.kernel.util.StringBundler;
028    import com.liferay.portal.kernel.util.StringPool;
029    import com.liferay.portal.kernel.util.StringUtil;
030    import com.liferay.portal.kernel.util.Validator;
031    
032    import java.io.File;
033    import java.io.FileInputStream;
034    import java.io.IOException;
035    import java.io.InputStream;
036    
037    import java.text.DateFormat;
038    import java.text.Format;
039    import java.text.ParseException;
040    
041    import java.util.Arrays;
042    import java.util.Date;
043    import java.util.HashMap;
044    import java.util.Locale;
045    import java.util.Map;
046    import java.util.Set;
047    
048    /**
049     * @author Brian Wing Shun Chan
050     * @author Bruno Farache
051     */
052    public class DocumentImpl implements Document {
053    
054            public static String getLocalizedName(Locale locale, String name) {
055                    if (locale == null) {
056                            return name;
057                    }
058    
059                    String languageId = LocaleUtil.toLanguageId(locale);
060    
061                    return getLocalizedName(languageId, name);
062            }
063    
064            public static String getLocalizedName(String languageId, String name) {
065                    return name.concat(StringPool.UNDERLINE).concat(languageId);
066            }
067    
068            public static String getSortableFieldName(String name) {
069                    return name.concat(StringPool.UNDERLINE).concat(_SORTABLE_FIELD_SUFFIX);
070            }
071    
072            public static 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 = StringUtil.toLowerCase(value);
303                    }
304    
305                    Field field = new Field(name, value);
306    
307                    for (String fieldName : Field.UNSCORED_FIELD_NAMES) {
308                            if (StringUtil.equalsIgnoreCase(name, 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(
348                                            entry.getKey(), StringUtil.toLowerCase(value));
349                            }
350    
351                            values = lowerCaseValues;
352                    }
353    
354                    Field field = new Field(name, values);
355    
356                    _fields.put(name, field);
357            }
358    
359            @Override
360            public void addLocalizedText(String name, Map<Locale, String> values) {
361                    if ((values == null) || values.isEmpty()) {
362                            return;
363                    }
364    
365                    Field field = new Field(name, values);
366    
367                    field.setTokenized(true);
368    
369                    _fields.put(name, field);
370            }
371    
372            /**
373             * @deprecated As of 6.1.0
374             */
375            @Override
376            public void addModifiedDate() {
377                    addModifiedDate(new Date());
378            }
379    
380            /**
381             * @deprecated As of 6.1.0
382             */
383            @Override
384            public void addModifiedDate(Date modifiedDate) {
385                    addDate(Field.MODIFIED, modifiedDate);
386            }
387    
388            @Override
389            public void addNumber(String name, double value) {
390                    addNumber(name, String.valueOf(value), Double.class);
391            }
392    
393            @Override
394            public void addNumber(String name, Double value) {
395                    addNumber(name, String.valueOf(value), Double.class);
396            }
397    
398            @Override
399            public void addNumber(String name, double[] values) {
400                    addNumber(name, ArrayUtil.toStringArray(values), Double.class);
401            }
402    
403            @Override
404            public void addNumber(String name, Double[] values) {
405                    addNumber(name, ArrayUtil.toStringArray(values), Double.class);
406            }
407    
408            @Override
409            public void addNumber(String name, float value) {
410                    addNumber(name, String.valueOf(value), Float.class);
411            }
412    
413            @Override
414            public void addNumber(String name, Float value) {
415                    addNumber(name, String.valueOf(value), Float.class);
416            }
417    
418            @Override
419            public void addNumber(String name, float[] values) {
420                    addNumber(name, ArrayUtil.toStringArray(values), Float.class);
421            }
422    
423            @Override
424            public void addNumber(String name, Float[] values) {
425                    addNumber(name, ArrayUtil.toStringArray(values), Float.class);
426            }
427    
428            @Override
429            public void addNumber(String name, int value) {
430                    addNumber(name, String.valueOf(value), Integer.class);
431            }
432    
433            @Override
434            public void addNumber(String name, int[] values) {
435                    addNumber(name, ArrayUtil.toStringArray(values), Integer.class);
436            }
437    
438            @Override
439            public void addNumber(String name, Integer value) {
440                    addNumber(name, String.valueOf(value), Integer.class);
441            }
442    
443            @Override
444            public void addNumber(String name, Integer[] values) {
445                    addNumber(name, ArrayUtil.toStringArray(values), Integer.class);
446            }
447    
448            @Override
449            public void addNumber(String name, long value) {
450                    addNumber(name, String.valueOf(value), Long.class);
451            }
452    
453            @Override
454            public void addNumber(String name, Long value) {
455                    addNumber(name, String.valueOf(value), Long.class);
456            }
457    
458            @Override
459            public void addNumber(String name, long[] values) {
460                    addNumber(name, ArrayUtil.toStringArray(values), Long.class);
461            }
462    
463            @Override
464            public void addNumber(String name, Long[] values) {
465                    addNumber(name, ArrayUtil.toStringArray(values), Long.class);
466            }
467    
468            @Override
469            public void addNumber(String name, String value) {
470                    addNumber(name, value, Long.class);
471            }
472    
473            public void addNumber(
474                    String name, String value, Class<? extends Number> clazz) {
475    
476                    if (Validator.isNull(value)) {
477                            return;
478                    }
479    
480                    addNumber(name, new String[] {value}, clazz);
481            }
482    
483            @Override
484            public void addNumber(String name, String[] values) {
485                    addNumber(name, values, Long.class);
486            }
487    
488            public void addNumber(
489                    String name, String[] values, Class<? extends Number> clazz) {
490    
491                    if (values == null) {
492                            return;
493                    }
494    
495                    String sortableFieldName = getSortableFieldName(name);
496    
497                    Field field = new Field(sortableFieldName, values);
498    
499                    field.setNumeric(true);
500                    field.setNumericClass(clazz);
501    
502                    _fields.put(sortableFieldName, field);
503    
504                    addKeyword(name, values);
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_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    }