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