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