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.Collections;
043    import java.util.Date;
044    import java.util.HashMap;
045    import java.util.Locale;
046    import java.util.Map;
047    import java.util.Set;
048    
049    /**
050     * @author Brian Wing Shun Chan
051     * @author Bruno Farache
052     */
053    public class DocumentImpl implements Document {
054    
055            public static String getLocalizedName(Locale locale, String name) {
056                    if (locale == null) {
057                            return name;
058                    }
059    
060                    String languageId = LocaleUtil.toLanguageId(locale);
061    
062                    return getLocalizedName(languageId, name);
063            }
064    
065            public static String getLocalizedName(String languageId, String name) {
066                    return name.concat(StringPool.UNDERLINE).concat(languageId);
067            }
068    
069            public static String getSortableFieldName(String name) {
070                    return name.concat(StringPool.UNDERLINE).concat(_SORTABLE_FIELD_SUFFIX);
071            }
072    
073            public static String getSortFieldName(Sort sort, String scoreFieldName) {
074                    if (sort.getType() == Sort.SCORE_TYPE) {
075                            return scoreFieldName;
076                    }
077    
078                    String fieldName = sort.getFieldName();
079    
080                    if (DocumentImpl.isSortableFieldName(fieldName)) {
081                            return fieldName;
082                    }
083    
084                    if ((sort.getType() == Sort.STRING_TYPE) &&
085                            !DocumentImpl.isSortableTextField(fieldName)) {
086    
087                            return scoreFieldName;
088                    }
089    
090                    return DocumentImpl.getSortableFieldName(fieldName);
091            }
092    
093            public static boolean isSortableFieldName(String name) {
094                    return name.endsWith(_SORTABLE_FIELD_SUFFIX);
095            }
096    
097            public static boolean isSortableTextField(String name) {
098                    return _defaultSortableTextFields.contains(name);
099            }
100    
101            @Override
102            public void add(Field field) {
103                    _fields.put(field.getName(), field);
104            }
105    
106            @Override
107            public void addDate(String name, Date value) {
108                    if (value == null) {
109                            return;
110                    }
111    
112                    addDate(name, new Date[] {value});
113            }
114    
115            @Override
116            public void addDate(String name, Date[] values) {
117                    if (values == null) {
118                            return;
119                    }
120    
121                    String[] dates = new String[values.length];
122                    Long[] datesTime = new Long[values.length];
123    
124                    for (int i = 0; i < values.length; i++) {
125                            dates[i] = _dateFormat.format(values[i]);
126                            datesTime[i] = values[i].getTime();
127                    }
128    
129                    createSortableNumericField(name, false, datesTime);
130    
131                    addKeyword(name, dates);
132            }
133    
134            @Override
135            public void addFile(String name, byte[] bytes, String fileExt) {
136                    InputStream is = new UnsyncByteArrayInputStream(bytes);
137    
138                    addFile(name, is, fileExt);
139            }
140    
141            @Override
142            public void addFile(String name, File file, String fileExt)
143                    throws IOException {
144    
145                    InputStream is = new FileInputStream(file);
146    
147                    addFile(name, is, fileExt);
148            }
149    
150            @Override
151            public void addFile(String name, InputStream is, String fileExt) {
152                    addText(name, FileUtil.extractText(is, fileExt));
153            }
154    
155            @Override
156            public void addFile(
157                    String name, InputStream is, String fileExt, int maxStringLength) {
158    
159                    addText(name, FileUtil.extractText(is, fileExt, maxStringLength));
160            }
161    
162            @Override
163            public void addKeyword(String name, boolean value) {
164                    addKeyword(name, String.valueOf(value));
165            }
166    
167            @Override
168            public void addKeyword(String name, Boolean value) {
169                    addKeyword(name, String.valueOf(value));
170            }
171    
172            @Override
173            public void addKeyword(String name, boolean[] 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, Boolean[] 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, double value) {
192                    addKeyword(name, String.valueOf(value));
193            }
194    
195            @Override
196            public void addKeyword(String name, Double value) {
197                    addKeyword(name, String.valueOf(value));
198            }
199    
200            @Override
201            public void addKeyword(String name, double[] values) {
202                    if (values == null) {
203                            return;
204                    }
205    
206                    addKeyword(name, ArrayUtil.toStringArray(values));
207            }
208    
209            @Override
210            public void addKeyword(String name, Double[] 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, float value) {
220                    addKeyword(name, String.valueOf(value));
221            }
222    
223            @Override
224            public void addKeyword(String name, Float value) {
225                    addKeyword(name, String.valueOf(value));
226            }
227    
228            @Override
229            public void addKeyword(String name, float[] 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, Float[] 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, int value) {
248                    addKeyword(name, String.valueOf(value));
249            }
250    
251            @Override
252            public void addKeyword(String name, int[] values) {
253                    if (values == null) {
254                            return;
255                    }
256    
257                    addKeyword(name, ArrayUtil.toStringArray(values));
258            }
259    
260            @Override
261            public void addKeyword(String name, Integer value) {
262                    addKeyword(name, String.valueOf(value));
263            }
264    
265            @Override
266            public void addKeyword(String name, Integer[] 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, long value) {
276                    addKeyword(name, String.valueOf(value));
277            }
278    
279            @Override
280            public void addKeyword(String name, Long value) {
281                    addKeyword(name, String.valueOf(value));
282            }
283    
284            @Override
285            public void addKeyword(String name, long[] values) {
286                    if (values == null) {
287                            return;
288                    }
289    
290                    addKeyword(name, ArrayUtil.toStringArray(values));
291            }
292    
293            @Override
294            public void addKeyword(String name, Long[] values) {
295                    if (values == null) {
296                            return;
297                    }
298    
299                    addKeyword(name, ArrayUtil.toStringArray(values));
300            }
301    
302            @Override
303            public void addKeyword(String name, short value) {
304                    addKeyword(name, String.valueOf(value));
305            }
306    
307            @Override
308            public void addKeyword(String name, Short value) {
309                    addKeyword(name, String.valueOf(value));
310            }
311    
312            @Override
313            public void addKeyword(String name, short[] values) {
314                    if (values == null) {
315                            return;
316                    }
317    
318                    addKeyword(name, ArrayUtil.toStringArray(values));
319            }
320    
321            @Override
322            public void addKeyword(String name, Short[] values) {
323                    if (values == null) {
324                            return;
325                    }
326    
327                    addKeyword(name, ArrayUtil.toStringArray(values));
328            }
329    
330            @Override
331            public void addKeyword(String name, String value) {
332                    addKeyword(name, value, false);
333            }
334    
335            @Override
336            public void addKeyword(String name, String value, boolean lowerCase) {
337                    createKeywordField(name, value, lowerCase);
338    
339                    createSortableKeywordField(name, value);
340            }
341    
342            @Override
343            public void addKeyword(String name, String[] values) {
344                    if (values == null) {
345                            return;
346                    }
347    
348                    Field field = new Field(name, values);
349    
350                    _fields.put(name, field);
351            }
352    
353            @Override
354            public void addKeywordSortable(String name, Boolean value) {
355                    String valueString = String.valueOf(value);
356    
357                    createKeywordField(name, valueString, false);
358    
359                    createSortableTextField(name, valueString);
360            }
361    
362            @Override
363            public void addKeywordSortable(String name, Boolean[] values) {
364                    if (values == null) {
365                            return;
366                    }
367    
368                    String[] valuesString = ArrayUtil.toStringArray(values);
369    
370                    Field field = new Field(name, valuesString);
371    
372                    _fields.put(name, field);
373    
374                    createSortableTextField(name, valuesString);
375            }
376    
377            @Override
378            public void addKeywordSortable(String name, String value) {
379                    createKeywordField(name, value, false);
380    
381                    createSortableTextField(name, value);
382            }
383    
384            @Override
385            public void addKeywordSortable(String name, String[] values) {
386                    Field field = new Field(name, values);
387    
388                    _fields.put(name, field);
389    
390                    createSortableTextField(name, values);
391            }
392    
393            @Override
394            public void addLocalizedKeyword(String name, Map<Locale, String> values) {
395                    addLocalizedKeyword(name, values, false);
396            }
397    
398            @Override
399            public void addLocalizedKeyword(
400                    String name, Map<Locale, String> values, boolean lowerCase) {
401    
402                    if ((values == null) || values.isEmpty()) {
403                            return;
404                    }
405    
406                    if (lowerCase) {
407                            Map<Locale, String> lowerCaseValues = new HashMap<Locale, String>(
408                                    values.size());
409    
410                            for (Map.Entry<Locale, String> entry : values.entrySet()) {
411                                    String value = GetterUtil.getString(entry.getValue());
412    
413                                    lowerCaseValues.put(
414                                            entry.getKey(), StringUtil.toLowerCase(value));
415                            }
416    
417                            values = lowerCaseValues;
418                    }
419    
420                    Field field = new Field(name, values);
421    
422                    _fields.put(name, field);
423            }
424    
425            @Override
426            public void addLocalizedKeyword(
427                    String name, Map<Locale, String> values, boolean lowerCase,
428                    boolean sortable) {
429    
430                    if ((values == null) || values.isEmpty()) {
431                            return;
432                    }
433    
434                    if (lowerCase) {
435                            Map<Locale, String> lowerCaseValues = new HashMap<Locale, String>(
436                                    values.size());
437    
438                            for (Map.Entry<Locale, String> entry : values.entrySet()) {
439                                    String value = GetterUtil.getString(entry.getValue());
440    
441                                    lowerCaseValues.put(
442                                            entry.getKey(), StringUtil.toLowerCase(value));
443                            }
444    
445                            values = lowerCaseValues;
446                    }
447    
448                    Field field = new Field(name, values);
449    
450                    field.setSortable(sortable);
451    
452                    _fields.put(name, field);
453            }
454    
455            @Override
456            public void addLocalizedText(String name, Map<Locale, String> values) {
457                    if ((values == null) || values.isEmpty()) {
458                            return;
459                    }
460    
461                    Field field = new Field(name, values);
462    
463                    field.setTokenized(true);
464    
465                    _fields.put(name, field);
466            }
467    
468            /**
469             * @deprecated As of 6.1.0
470             */
471            @Override
472            public void addModifiedDate() {
473                    addModifiedDate(new Date());
474            }
475    
476            /**
477             * @deprecated As of 6.1.0
478             */
479            @Override
480            public void addModifiedDate(Date modifiedDate) {
481                    addDate(Field.MODIFIED, modifiedDate);
482            }
483    
484            @Override
485            public void addNumber(String name, double value) {
486                    createNumberField(name, Double.valueOf(value));
487            }
488    
489            @Override
490            public void addNumber(String name, Double value) {
491                    createNumberField(name, value);
492            }
493    
494            @Override
495            public void addNumber(String name, double[] values) {
496                    if (values == null) {
497                            return;
498                    }
499    
500                    createNumberField(name, ArrayUtil.toArray(values));
501            }
502    
503            @Override
504            public void addNumber(String name, Double[] values) {
505                    createNumberField(name, values);
506            }
507    
508            @Override
509            public void addNumber(String name, float value) {
510                    createNumberField(name, Float.valueOf(value));
511            }
512    
513            @Override
514            public void addNumber(String name, Float value) {
515                    createNumberField(name, value);
516            }
517    
518            @Override
519            public void addNumber(String name, float[] values) {
520                    if (values == null) {
521                            return;
522                    }
523    
524                    createNumberField(name, ArrayUtil.toArray(values));
525            }
526    
527            @Override
528            public void addNumber(String name, Float[] values) {
529                    createNumberField(name, values);
530            }
531    
532            @Override
533            public void addNumber(String name, int value) {
534                    createNumberField(name, Integer.valueOf(value));
535            }
536    
537            @Override
538            public void addNumber(String name, int[] values) {
539                    if (values == null) {
540                            return;
541                    }
542    
543                    createNumberField(name, ArrayUtil.toArray(values));
544            }
545    
546            @Override
547            public void addNumber(String name, Integer value) {
548                    createNumberField(name, value);
549            }
550    
551            @Override
552            public void addNumber(String name, Integer[] values) {
553                    createNumberField(name, values);
554            }
555    
556            @Override
557            public void addNumber(String name, long value) {
558                    createNumberField(name, Long.valueOf(value));
559            }
560    
561            @Override
562            public void addNumber(String name, Long value) {
563                    createNumberField(name, value);
564            }
565    
566            @Override
567            public void addNumber(String name, long[] values) {
568                    if (values == null) {
569                            return;
570                    }
571    
572                    createNumberField(name, ArrayUtil.toArray(values));
573            }
574    
575            @Override
576            public void addNumber(String name, Long[] values) {
577                    createNumberField(name, values);
578            }
579    
580            @Override
581            public void addNumber(String name, String value) {
582                    createNumberField(name, Long.valueOf(value));
583            }
584    
585            /**
586             * @deprecated As of 7.0.0
587             */
588            @Deprecated
589            public void addNumber(
590                    String name, String value, Class<? extends Number> clazz) {
591    
592                    if (Validator.isNull(value)) {
593                            return;
594                    }
595    
596                    addNumber(name, new String[] {value}, clazz);
597            }
598    
599            @Override
600            public void addNumber(String name, String[] values) {
601                    if (values == null) {
602                            return;
603                    }
604    
605                    Long[] longs = new Long[values.length];
606    
607                    for (int i = 0; i < values.length; i++) {
608                            longs[i] = Long.valueOf(values[i]);
609                    }
610    
611                    createNumberField(name, longs);
612            }
613    
614            /**
615             * @deprecated As of 7.0.0
616             */
617            @Deprecated
618            public void addNumber(
619                    String name, String[] values, Class<? extends Number> clazz) {
620    
621                    if (values == null) {
622                            return;
623                    }
624    
625                    String sortableFieldName = getSortableFieldName(name);
626    
627                    Field field = new Field(sortableFieldName, values);
628    
629                    field.setNumeric(true);
630                    field.setNumericClass(clazz);
631    
632                    _fields.put(sortableFieldName, field);
633    
634                    addKeyword(name, values);
635            }
636    
637            @Override
638            public void addNumberSortable(String name, Double value) {
639                    createNumberFieldWithTypedSortable(name, value);
640            }
641    
642            @Override
643            public void addNumberSortable(String name, Double[] values) {
644                    createNumberFieldWithTypedSortable(name, values);
645            }
646    
647            @Override
648            public void addNumberSortable(String name, Float value) {
649                    createNumberFieldWithTypedSortable(name, value);
650            }
651    
652            @Override
653            public void addNumberSortable(String name, Float[] values) {
654                    createNumberFieldWithTypedSortable(name, values);
655            }
656    
657            @Override
658            public void addNumberSortable(String name, Integer value) {
659                    createNumberFieldWithTypedSortable(name, value);
660            }
661    
662            @Override
663            public void addNumberSortable(String name, Integer[] values) {
664                    createNumberFieldWithTypedSortable(name, values);
665            }
666    
667            @Override
668            public void addNumberSortable(String name, Long value) {
669                    createNumberFieldWithTypedSortable(name, value);
670            }
671    
672            @Override
673            public void addNumberSortable(String name, Long[] values) {
674                    createNumberFieldWithTypedSortable(name, values);
675            }
676    
677            @Override
678            public void addText(String name, String value) {
679                    if (Validator.isNull(value)) {
680                            return;
681                    }
682    
683                    Field field = new Field(name, value);
684    
685                    field.setTokenized(true);
686    
687                    _fields.put(name, field);
688    
689                    createSortableKeywordField(name, value);
690            }
691    
692            @Override
693            public void addText(String name, String[] values) {
694                    if (values == null) {
695                            return;
696                    }
697    
698                    Field field = new Field(name, values);
699    
700                    field.setTokenized(true);
701    
702                    _fields.put(name, field);
703    
704                    createSortableKeywordField(name, values);
705            }
706    
707            @Override
708            public void addTextSortable(String name, String value) {
709                    if (Validator.isNull(value)) {
710                            return;
711                    }
712    
713                    Field field = new Field(name, value);
714    
715                    field.setTokenized(true);
716    
717                    _fields.put(name, field);
718    
719                    createSortableTextField(name, value);
720            }
721    
722            @Override
723            public void addTextSortable(String name, String[] values) {
724                    if (values == null) {
725                            return;
726                    }
727    
728                    Field field = new Field(name, values);
729    
730                    field.setTokenized(true);
731    
732                    _fields.put(name, field);
733    
734                    createSortableTextField(name, values);
735            }
736    
737            @Override
738            public void addUID(String portletId, long field1) {
739                    addUID(portletId, String.valueOf(field1));
740            }
741    
742            @Override
743            public void addUID(String portletId, long field1, String field2) {
744                    addUID(portletId, String.valueOf(field1), field2);
745            }
746    
747            @Override
748            public void addUID(String portletId, Long field1) {
749                    addUID(portletId, field1.longValue());
750            }
751    
752            @Override
753            public void addUID(String portletId, Long field1, String field2) {
754                    addUID(portletId, field1.longValue(), field2);
755            }
756    
757            @Override
758            public void addUID(String portletId, String field1) {
759                    addUID(portletId, field1, null);
760            }
761    
762            @Override
763            public void addUID(String portletId, String field1, String field2) {
764                    addUID(portletId, field1, field2, null);
765            }
766    
767            @Override
768            public void addUID(
769                    String portletId, String field1, String field2, String field3) {
770    
771                    addUID(portletId, field1, field2, field3, null);
772            }
773    
774            @Override
775            public void addUID(
776                    String portletId, String field1, String field2, String field3,
777                    String field4) {
778    
779                    String uid = portletId + _UID_PORTLET + field1;
780    
781                    if (field2 != null) {
782                            uid += _UID_FIELD + field2;
783                    }
784    
785                    if (field3 != null) {
786                            uid += _UID_FIELD + field3;
787                    }
788    
789                    if (field4 != null) {
790                            uid += _UID_FIELD + field4;
791                    }
792    
793                    addKeyword(Field.UID, uid);
794            }
795    
796            @Override
797            public Object clone() {
798                    DocumentImpl documentImpl = new DocumentImpl();
799    
800                    documentImpl.setSortableTextFields(_sortableTextFields);
801    
802                    return documentImpl;
803            }
804    
805            @Override
806            public String get(Locale locale, String name) {
807                    if (locale == null) {
808                            return get(name);
809                    }
810    
811                    String localizedName = getLocalizedName(locale, name);
812    
813                    Field field = _fields.get(localizedName);
814    
815                    if (field == null) {
816                            field = _fields.get(name);
817                    }
818    
819                    if (field == null) {
820                            return StringPool.BLANK;
821                    }
822    
823                    return field.getValue();
824            }
825    
826            @Override
827            public String get(Locale locale, String name, String defaultName) {
828                    if (locale == null) {
829                            return get(name, defaultName);
830                    }
831    
832                    String localizedName = getLocalizedName(locale, name);
833    
834                    Field field = _fields.get(localizedName);
835    
836                    if (field == null) {
837                            localizedName = getLocalizedName(locale, defaultName);
838    
839                            field = _fields.get(localizedName);
840                    }
841    
842                    if (field == null) {
843                            return StringPool.BLANK;
844                    }
845    
846                    return field.getValue();
847            }
848    
849            @Override
850            public String get(String name) {
851                    Field field = _fields.get(name);
852    
853                    if (field == null) {
854                            return StringPool.BLANK;
855                    }
856    
857                    return field.getValue();
858            }
859    
860            @Override
861            public String get(String name, String defaultName) {
862                    Field field = _fields.get(name);
863    
864                    if (field == null) {
865                            return get(defaultName);
866                    }
867    
868                    return field.getValue();
869            }
870    
871            @Override
872            public Date getDate(String name) throws ParseException {
873                    DateFormat dateFormat = DateFormatFactoryUtil.getSimpleDateFormat(
874                            _INDEX_DATE_FORMAT_PATTERN);
875    
876                    return dateFormat.parse(get(name));
877            }
878    
879            @Override
880            public Field getField(String name) {
881                    return _fields.get(name);
882            }
883    
884            @Override
885            public Map<String, Field> getFields() {
886                    return _fields;
887            }
888    
889            @Override
890            public String getPortletId() {
891                    String uid = getUID();
892    
893                    int pos = uid.indexOf(_UID_PORTLET);
894    
895                    return uid.substring(0, pos);
896            }
897    
898            @Override
899            public String getUID() {
900                    Field field = _fields.get(Field.UID);
901    
902                    if (field == null) {
903                            throw new RuntimeException("UID is not set");
904                    }
905    
906                    return field.getValue();
907            }
908    
909            @Override
910            public String[] getValues(String name) {
911                    Field field = _fields.get(name);
912    
913                    if (field == null) {
914                            return new String[] {StringPool.BLANK};
915                    }
916    
917                    return field.getValues();
918            }
919    
920            @Override
921            public boolean hasField(String name) {
922                    if (_fields.containsKey(name)) {
923                            return true;
924                    }
925    
926                    return false;
927            }
928    
929            @Override
930            public boolean isDocumentSortableTextField(String name) {
931                    return _sortableTextFields.contains(name);
932            }
933    
934            @Override
935            public void remove(String name) {
936                    _fields.remove(name);
937            }
938    
939            public void setFields(Map<String, Field> fields) {
940                    _fields = fields;
941            }
942    
943            @Override
944            public void setSortableTextFields(String[] sortableTextFields) {
945                    _sortableTextFields = SetUtil.fromArray(sortableTextFields);
946            }
947    
948            @Override
949            public String toString() {
950                    StringBundler sb = new StringBundler();
951    
952                    sb.append(StringPool.OPEN_CURLY_BRACE);
953    
954                    boolean firstField = true;
955    
956                    for (Field field : _fields.values()) {
957                            if (!firstField) {
958                                    sb.append(StringPool.COMMA);
959                                    sb.append(StringPool.SPACE);
960                            }
961                            else {
962                                    firstField = false;
963                            }
964    
965                            sb.append(field.getName());
966                            sb.append(StringPool.EQUAL);
967                            sb.append(Arrays.toString(field.getValues()));
968                    }
969    
970                    sb.append(StringPool.CLOSE_CURLY_BRACE);
971    
972                    return sb.toString();
973            }
974    
975            protected void createKeywordField(
976                    String name, String value, boolean lowerCase) {
977    
978                    if (lowerCase && Validator.isNotNull(value)) {
979                            value = StringUtil.toLowerCase(value);
980                    }
981    
982                    Field field = new Field(name, value);
983    
984                    _fields.put(name, field);
985    
986                    for (String fieldName : Field.UNSCORED_FIELD_NAMES) {
987                            if (StringUtil.equalsIgnoreCase(name, fieldName)) {
988                                    field.setBoost(0);
989                            }
990                    }
991            }
992    
993            protected void createNumberField(
994                    String name, boolean typify, Number value) {
995    
996                    if (value == null) {
997                            return;
998                    }
999    
1000                    String valueString = String.valueOf(value);
1001    
1002                    createSortableNumericField(name, typify, valueString, value.getClass());
1003    
1004                    Field field = new Field(name, valueString);
1005    
1006                    _fields.put(name, field);
1007            }
1008    
1009            protected <T extends Number & Comparable<? super T>> void createNumberField(
1010                    String name, boolean typify, T... values) {
1011    
1012                    if (values == null) {
1013                            return;
1014                    }
1015    
1016                    createSortableNumericField(name, typify, values);
1017    
1018                    Field field = new Field(name, ArrayUtil.toStringArray(values));
1019    
1020                    _fields.put(name, field);
1021            }
1022    
1023            protected void createNumberField(String name, Number value) {
1024                    createNumberField(name, false, value);
1025            }
1026    
1027            protected <T extends Number & Comparable<? super T>> void createNumberField(
1028                    String name, T... values) {
1029    
1030                    createNumberField(name, false, values);
1031            }
1032    
1033            protected void createNumberFieldWithTypedSortable(
1034                    String name, Number value) {
1035    
1036                    createNumberField(name, true, value);
1037            }
1038    
1039            protected <T extends Number & Comparable<? super T>> void
1040                    createNumberFieldWithTypedSortable(String name, T... values) {
1041    
1042                    createNumberField(name, true, values);
1043            }
1044    
1045            protected void createSortableKeywordField(String name, String value) {
1046                    if (isDocumentSortableTextField(name)) {
1047                            createSortableTextField(name, value);
1048                    }
1049            }
1050    
1051            protected void createSortableKeywordField(String name, String[] values) {
1052                    if (isDocumentSortableTextField(name)) {
1053                            createSortableTextField(name, values);
1054                    }
1055            }
1056    
1057            protected void createSortableNumericField(
1058                    String name, boolean typify, String value,
1059                    Class<? extends Number> clazz) {
1060    
1061                    if (typify) {
1062                            name = name.concat(StringPool.UNDERLINE).concat("Number");
1063                    }
1064    
1065                    String sortableFieldName = getSortableFieldName(name);
1066    
1067                    Field field = new Field(sortableFieldName, value);
1068    
1069                    _fields.put(sortableFieldName, field);
1070    
1071                    field.setNumeric(true);
1072                    field.setNumericClass(clazz);
1073            }
1074    
1075            protected <T extends Number & Comparable<? super T>> void
1076                    createSortableNumericField(String name, boolean typify, T... values) {
1077    
1078                    if ((values == null) || (values.length == 0)) {
1079                            return;
1080                    }
1081    
1082                    T minValue = Collections.min(Arrays.asList(values));
1083    
1084                    createSortableNumericField(
1085                            name, typify, String.valueOf(minValue), minValue.getClass());
1086            }
1087    
1088            protected void createSortableTextField(String name, String value) {
1089                    String truncatedValue = value;
1090    
1091                    if (value.length() > _SORTABLE_TEXT_FIELDS_TRUNCATED_LENGTH) {
1092                            truncatedValue = value.substring(
1093                                    0, _SORTABLE_TEXT_FIELDS_TRUNCATED_LENGTH);
1094                    }
1095    
1096                    createKeywordField(getSortableFieldName(name), truncatedValue, true);
1097            }
1098    
1099            protected void createSortableTextField(String name, String[] values) {
1100                    if (values.length == 0) {
1101                            return;
1102                    }
1103    
1104                    createSortableTextField(name, Collections.min(Arrays.asList(values)));
1105            }
1106    
1107            protected void setSortableTextFields(Set<String> sortableTextFields) {
1108                    _sortableTextFields = sortableTextFields;
1109            }
1110    
1111            private static final String _INDEX_DATE_FORMAT_PATTERN = PropsUtil.get(
1112                    PropsKeys.INDEX_DATE_FORMAT_PATTERN);
1113    
1114            private static final String _SORTABLE_FIELD_SUFFIX = "sortable";
1115    
1116            private static final int _SORTABLE_TEXT_FIELDS_TRUNCATED_LENGTH =
1117                    GetterUtil.getInteger(
1118                            PropsUtil.get(
1119                                    PropsKeys.INDEX_SORTABLE_TEXT_FIELDS_TRUNCATED_LENGTH));
1120    
1121            private static final String _UID_FIELD = "_FIELD_";
1122    
1123            private static final String _UID_PORTLET = "_PORTLET_";
1124    
1125            private static Format _dateFormat =
1126                    FastDateFormatFactoryUtil.getSimpleDateFormat(
1127                            _INDEX_DATE_FORMAT_PATTERN);
1128            private static Set<String> _defaultSortableTextFields = SetUtil.fromArray(
1129                    PropsUtil.getArray(PropsKeys.INDEX_SORTABLE_TEXT_FIELDS));
1130    
1131            private Map<String, Field> _fields = new HashMap<String, Field>();
1132            private Set<String> _sortableTextFields = _defaultSortableTextFields;
1133    
1134    }